Task categories edition
This commit is contained in:
parent
5f0cad970e
commit
b0bffe6fb8
|
@ -1,4 +1,5 @@
|
||||||
import { uuidV4 } from "../util/uuid"
|
import { uuidV4 } from "../util/uuid"
|
||||||
|
import { defaults } from "./params";
|
||||||
|
|
||||||
export type TaskID = string
|
export type TaskID = string
|
||||||
|
|
||||||
|
@ -24,14 +25,22 @@ export interface TaskCategory {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newTask(label: string, category: TaskCategoryID): Task {
|
export function newTask(label: string, category: TaskCategoryID): Task {
|
||||||
return {
|
return {
|
||||||
id: uuidV4(),
|
id: uuidV4(),
|
||||||
label,
|
label,
|
||||||
category,
|
category,
|
||||||
estimations: {
|
estimations: {
|
||||||
[EstimationConfidence.Optimistic]: 0,
|
[EstimationConfidence.Optimistic]: 0,
|
||||||
[EstimationConfidence.Likely]: 0,
|
[EstimationConfidence.Likely]: 0,
|
||||||
[EstimationConfidence.Pessimistic]: 0,
|
[EstimationConfidence.Pessimistic]: 0,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTaskCategory(): TaskCategory {
|
||||||
|
return {
|
||||||
|
id: uuidV4(),
|
||||||
|
costPerTimeUnit: defaults.costPerTimeUnit,
|
||||||
|
label: ""
|
||||||
|
};
|
||||||
}
|
}
|
|
@ -1,70 +1,113 @@
|
||||||
import { FunctionalComponent, h } from "preact";
|
import { FunctionalComponent, h } from "preact";
|
||||||
import { Project } from "../../models/project";
|
import { Project } from "../../models/project";
|
||||||
import style from './style.module.css';
|
import style from './style.module.css';
|
||||||
import { projectReducer, ProjectReducerActions, updateTaskCategoryCost, updateTaskCategoryLabel, removeTaskCategory } from "../../hooks/use-project-reducer";
|
import { ProjectReducerActions, updateTaskCategoryCost, updateTaskCategoryLabel, removeTaskCategory, addTaskCategory } from "../../hooks/use-project-reducer";
|
||||||
import EditableText from "../../components/editable-text";
|
import EditableText from "../../components/editable-text";
|
||||||
import { TaskCategoryID } from "../../models/task";
|
import { TaskCategoryID, createTaskCategory } from "../../models/task";
|
||||||
import ProjectTimeUnit from "../../components/project-time-unit";
|
|
||||||
import { getCurrency, getTaskCategoryCost } from "../../models/params";
|
import { getCurrency, getTaskCategoryCost } from "../../models/params";
|
||||||
|
import { useState } from "preact/hooks";
|
||||||
|
|
||||||
export interface TaskCategoriesTableProps {
|
export interface TaskCategoriesTableProps {
|
||||||
project: Project
|
project: Project
|
||||||
dispatch: (action: ProjectReducerActions) => void
|
dispatch: (action: ProjectReducerActions) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskCategoriesTable: FunctionalComponent<TaskCategoriesTableProps> = ({ project, dispatch }) => {
|
const TaskCategoriesTable: FunctionalComponent<TaskCategoriesTableProps> = ({ project, dispatch }) => {
|
||||||
const onTaskCategoryRemove = (categoryId: TaskCategoryID) => {
|
const [ newTaskCategory, setNewTaskCategory ] = useState(createTaskCategory());
|
||||||
dispatch(removeTaskCategory(categoryId));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onTaskCategoryLabelChange = (categoryId: TaskCategoryID, value: string) => {
|
const onTaskCategoryRemove = (categoryId: TaskCategoryID) => {
|
||||||
dispatch(updateTaskCategoryLabel(categoryId, value));
|
dispatch(removeTaskCategory(categoryId));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTaskCategoryCostChange = (categoryId: TaskCategoryID, value: string) => {
|
const onTaskCategoryLabelChange = (categoryId: TaskCategoryID, value: string) => {
|
||||||
const cost = parseFloat(value);
|
dispatch(updateTaskCategoryLabel(categoryId, value));
|
||||||
dispatch(updateTaskCategoryCost(categoryId, cost));
|
};
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const onTaskCategoryCostChange = (categoryId: TaskCategoryID, value: string) => {
|
||||||
<div class="table-container">
|
const cost = parseFloat(value);
|
||||||
<label class="label">Catégories de tâche</label>
|
dispatch(updateTaskCategoryCost(categoryId, cost));
|
||||||
<table class={`table is-bordered is-striped" ${style.middleTable}`}>
|
};
|
||||||
<thead>
|
|
||||||
<tr>
|
const onNewTaskCategoryCostChange = (evt: Event) => {
|
||||||
<th class={`${style.noBorder} is-narrow`}></th>
|
const costPerTimeUnit = parseFloat((evt.currentTarget as HTMLInputElement).value);
|
||||||
<th>Catégorie</th>
|
setNewTaskCategory(newTaskCategory => ({ ...newTaskCategory, costPerTimeUnit }));
|
||||||
<th>Coût par unité de temps</th>
|
};
|
||||||
</tr>
|
|
||||||
</thead>
|
const onNewTaskCategoryLabelChange = (evt: Event) => {
|
||||||
<tbody>
|
const label = (evt.currentTarget as HTMLInputElement).value;
|
||||||
{
|
setNewTaskCategory(newTaskCategory => ({ ...newTaskCategory, label }));
|
||||||
Object.values(project.params.taskCategories).map(tc => {
|
};
|
||||||
return (
|
|
||||||
<tr key={`task-category-${tc.id}`}>
|
const onNewTaskCategoryAddClick = (evt: Event) => {
|
||||||
<td>
|
dispatch(addTaskCategory(newTaskCategory));
|
||||||
<button
|
setNewTaskCategory(createTaskCategory());
|
||||||
onClick={onTaskCategoryRemove.bind(null, tc.id)}
|
};
|
||||||
class="button is-danger is-small is-outlined">
|
|
||||||
🗑️
|
return (
|
||||||
</button>
|
<div class="table-container">
|
||||||
</td>
|
<label class="label">Catégories de tâche</label>
|
||||||
<td><EditableText value={tc.label}
|
<table class={`table is-bordered is-striped" ${style.middleTable}`}>
|
||||||
onChange={onTaskCategoryLabelChange.bind(null, tc.id)} />
|
<thead>
|
||||||
</td>
|
<tr>
|
||||||
<td>
|
<th class={`${style.noBorder} is-narrow`}></th>
|
||||||
<EditableText value={`${getTaskCategoryCost(tc)}`}
|
<th>Catégorie</th>
|
||||||
render={value=> (<span>{value} {getCurrency(project)}</span>)}
|
<th>Coût par unité de temps</th>
|
||||||
onChange={onTaskCategoryCostChange.bind(null, tc.id)} />
|
</tr>
|
||||||
</td>
|
</thead>
|
||||||
</tr>
|
<tbody>
|
||||||
);
|
{
|
||||||
})
|
Object.values(project.params.taskCategories).map(tc => {
|
||||||
}
|
return (
|
||||||
</tbody>
|
<tr key={`task-category-${tc.id}`}>
|
||||||
</table>
|
<td>
|
||||||
</div>
|
<button
|
||||||
);
|
onClick={onTaskCategoryRemove.bind(null, tc.id)}
|
||||||
|
class="button is-danger is-small is-outlined">
|
||||||
|
🗑️
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<EditableText value={tc.label}
|
||||||
|
onChange={onTaskCategoryLabelChange.bind(null, tc.id)} />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<EditableText value={`${getTaskCategoryCost(tc)}`}
|
||||||
|
render={value=> (<span>{value} {getCurrency(project)}</span>)}
|
||||||
|
onChange={onTaskCategoryCostChange.bind(null, tc.id)} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td class={`${style.noBorder}`}></td>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<p class="control is-expanded">
|
||||||
|
<input class="input" type="text" placeholder="Nouvelle catégorie"
|
||||||
|
value={newTaskCategory.label} onChange={onNewTaskCategoryLabelChange} />
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<input class="input" type="number"
|
||||||
|
value={newTaskCategory.costPerTimeUnit} onChange={onNewTaskCategoryCostChange} />
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<a class="button is-static">{getCurrency(project)}</a>
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<a class="button is-primary" onClick={onNewTaskCategoryAddClick}>
|
||||||
|
Ajouter
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TaskCategoriesTable;
|
export default TaskCategoriesTable;
|
||||||
|
|
Loading…
Reference in New Issue