113 lines
4.3 KiB
TypeScript
113 lines
4.3 KiB
TypeScript
import React, { FunctionComponent, useState, MouseEvent, ChangeEvent } from "react";
|
||
import { Project } from "../../models/project";
|
||
import style from './style.module.css';
|
||
import { ProjectReducerActions, updateTaskCategoryCost, updateTaskCategoryLabel, removeTaskCategory, addTaskCategory } from "../../hooks/use-project-reducer";
|
||
import EditableText from "../../components/editable-text";
|
||
import { TaskCategoryID, createTaskCategory } from "../../models/task";
|
||
import { getCurrency, getTaskCategoryCost } from "../../models/params";
|
||
|
||
export interface TaskCategoriesTableProps {
|
||
project: Project
|
||
dispatch: (action: ProjectReducerActions) => void
|
||
}
|
||
|
||
const TaskCategoriesTable: FunctionComponent<TaskCategoriesTableProps> = ({ project, dispatch }) => {
|
||
const [ newTaskCategory, setNewTaskCategory ] = useState(createTaskCategory());
|
||
|
||
const onTaskCategoryRemove = (categoryId: TaskCategoryID) => {
|
||
dispatch(removeTaskCategory(categoryId));
|
||
};
|
||
|
||
const onTaskCategoryLabelChange = (categoryId: TaskCategoryID, value: string) => {
|
||
dispatch(updateTaskCategoryLabel(categoryId, value));
|
||
};
|
||
|
||
const onTaskCategoryCostChange = (categoryId: TaskCategoryID, value: string) => {
|
||
const cost = parseFloat(value);
|
||
dispatch(updateTaskCategoryCost(categoryId, cost));
|
||
};
|
||
|
||
const onNewTaskCategoryCostChange = (evt: ChangeEvent) => {
|
||
const costPerTimeUnit = parseFloat((evt.currentTarget as HTMLInputElement).value);
|
||
setNewTaskCategory(newTaskCategory => ({ ...newTaskCategory, costPerTimeUnit }));
|
||
};
|
||
|
||
const onNewTaskCategoryLabelChange = (evt: ChangeEvent) => {
|
||
const label = (evt.currentTarget as HTMLInputElement).value;
|
||
setNewTaskCategory(newTaskCategory => ({ ...newTaskCategory, label }));
|
||
};
|
||
|
||
const onNewTaskCategoryAddClick = (evt: MouseEvent) => {
|
||
dispatch(addTaskCategory(newTaskCategory));
|
||
setNewTaskCategory(createTaskCategory());
|
||
};
|
||
|
||
return (
|
||
<div className="table-container">
|
||
<label className="label is-size-5">Catégories de tâche</label>
|
||
<table className={`table is-bordered is-striped" ${style.middleTable}`}>
|
||
<thead>
|
||
<tr>
|
||
<th className={`${style.noBorder} is-narrow`}></th>
|
||
<th>Catégorie</th>
|
||
<th>Coût par unité de temps</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{
|
||
Object.values(project.params.taskCategories).map(tc => {
|
||
return (
|
||
<tr key={`task-category-${tc.id}`}>
|
||
<td>
|
||
<button
|
||
onClick={onTaskCategoryRemove.bind(null, tc.id)}
|
||
className="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 className={`${style.noBorder}`}></td>
|
||
<td colSpan={2}>
|
||
<div className="field has-addons">
|
||
<p className="control is-expanded">
|
||
<input className="input" type="text" placeholder="Nouvelle catégorie"
|
||
value={newTaskCategory.label} onChange={onNewTaskCategoryLabelChange} />
|
||
</p>
|
||
<p className="control">
|
||
<input className="input" type="number"
|
||
value={newTaskCategory.costPerTimeUnit} onChange={onNewTaskCategoryCostChange} />
|
||
</p>
|
||
<p className="control">
|
||
<a className="button is-static">{getCurrency(project)}</a>
|
||
</p>
|
||
<p className="control">
|
||
<a className="button is-primary" onClick={onNewTaskCategoryAddClick}>
|
||
Ajouter
|
||
</a>
|
||
</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default TaskCategoriesTable;
|