Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ npm start
| preStepsCount | number | 最初のタスクの前の空白を指定します。 |
| locale | string | 月名の言語を指定します。利用可能な形式: ISO 639-2, Java Locale。 |
| rtl | boolean | rtl モードを設定します。 |
| workHoursPerDay | number | 実績正規化で使用する 1 日あたりの稼働時間(時間単位)。未指定時は業務時間帯から算出されます。 |
| workdayStartTime | string | 実績正規化で使用する業務開始時刻("HH:mm")。未指定・不正時は "09:00" を使用します。 |
| workdayEndTime | string | 実績正規化で使用する業務終了時刻("HH:mm")。未指定・不正時は "18:00" を使用します。 |
| calendar | [CalendarConfig](#calendarconfig) | 稼働日計算と日付表示のカレンダー設定を指定します。未指定の場合は従来の動作を維持します(オプトイン式)。 |

### CalendarConfig
Expand Down
45 changes: 37 additions & 8 deletions src/components/gantt/gantt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { HorizontalScroll } from "../other/horizontal-scroll";
import { removeHiddenTasks, sortTasks } from "../../helpers/other-helper";
import { DEFAULT_VISIBLE_FIELDS } from "../../helpers/task-helper";
import { normalizeCalendarConfig } from "../../helpers/calendar-helper";
import { normalizeActuals } from "../../helpers/actuals-helper";
import styles from "./gantt.module.css";

const DEFAULT_TASK_LIST_WIDTH = 450;
Expand All @@ -49,6 +50,9 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
preStepsCount = 1,
locale = "en-GB",
calendar,
workHoursPerDay,
workdayStartTime,
workdayEndTime,
barFill = 60,
barCornerRadius = 3,
barProgressColor = "#a3a3ff",
Expand Down Expand Up @@ -91,6 +95,19 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
() => (calendar ? normalizeCalendarConfig(calendar) : undefined),
[calendar]
);
const actualsOptions = useMemo(
() => ({
calendarConfig,
workHoursPerDay,
workdayStartTime,
workdayEndTime,
}),
[calendarConfig, workHoursPerDay, workdayStartTime, workdayEndTime]
);
const normalizedTasks = useMemo(
() => tasks.map(task => normalizeActuals(task, actualsOptions)),
[tasks, actualsOptions]
);

const wrapperRef = useRef<HTMLDivElement>(null);
const taskListRef = useRef<HTMLDivElement>(null);
Expand All @@ -104,7 +121,11 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
const supportsPointerEvents =
typeof window !== "undefined" && "PointerEvent" in window;
const [dateSetup, setDateSetup] = useState<DateSetup>(() => {
const [startDate, endDate] = ganttDateRange(tasks, viewMode, preStepsCount);
const [startDate, endDate] = ganttDateRange(
normalizedTasks,
viewMode,
preStepsCount
);
return { viewMode, dates: seedDates(startDate, endDate, viewMode) };
});
const [currentViewDate, setCurrentViewDate] = useState<Date | undefined>(
Expand Down Expand Up @@ -142,9 +163,9 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
useEffect(() => {
let filteredTasks: Task[];
if (onExpanderClick) {
filteredTasks = removeHiddenTasks(tasks);
filteredTasks = removeHiddenTasks(normalizedTasks);
} else {
filteredTasks = tasks;
filteredTasks = normalizedTasks;
}
filteredTasks = filteredTasks.sort(sortTasks);
const [startDate, endDate] = ganttDateRange(
Expand Down Expand Up @@ -183,7 +204,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
)
);
}, [
tasks,
normalizedTasks,
viewMode,
preStepsCount,
rowHeight,
Expand Down Expand Up @@ -306,9 +327,9 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
if (ganttHeight) {
setSvgContainerHeight(ganttHeight + headerHeight);
} else {
setSvgContainerHeight(tasks.length * rowHeight + headerHeight);
setSvgContainerHeight(normalizedTasks.length * rowHeight + headerHeight);
}
}, [ganttHeight, tasks, headerHeight, rowHeight]);
}, [ganttHeight, normalizedTasks, headerHeight, rowHeight]);

useEffect(() => {
return () => {
Expand Down Expand Up @@ -389,7 +410,14 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
}
window.removeEventListener("resize", updateLeftScroller);
};
}, [tasks, fontFamily, fontSize, listCellWidth, taskListBodyRef, visibleFields]);
}, [
normalizedTasks,
fontFamily,
fontSize,
listCellWidth,
taskListBodyRef,
visibleFields,
]);

const handleScrollY = (event: SyntheticEvent<HTMLDivElement>) => {
if (scrollY !== event.currentTarget.scrollTop && !ignoreScrollLeftRef.current) {
Expand Down Expand Up @@ -583,7 +611,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
const gridProps: GridProps = {
columnWidth,
svgWidth,
tasks: tasks,
tasks: normalizedTasks,
rowHeight,
dates: dateSetup.dates,
todayColor,
Expand Down Expand Up @@ -651,6 +679,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
onCellCommit,
effortDisplayUnit,
enableColumnDrag,
actualsOptions,
};
return (
<div>
Expand Down
67 changes: 65 additions & 2 deletions src/components/task-list/task-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ import {
Task,
VisibleField,
} from "../../types/public-types";
import {
ActualsNormalizeOptions,
normalizeActuals,
} from "../../helpers/actuals-helper";
import {
formatDate,
parseDateFromInput,
sanitizeEffortInput,
} from "../../helpers/task-helper";
import { OverlayEditor } from "./overlay-editor";

export type EditingTrigger = "dblclick" | "enter" | "key";
Expand Down Expand Up @@ -50,6 +59,7 @@ export type TaskListProps = {
visibleFields: VisibleField[];
effortDisplayUnit: EffortUnit;
tasks: Task[];
actualsOptions?: ActualsNormalizeOptions;
taskListRef: React.RefObject<HTMLDivElement>;
headerContainerRef?: React.RefObject<HTMLDivElement>;
bodyContainerRef?: React.RefObject<HTMLDivElement>;
Expand Down Expand Up @@ -116,6 +126,7 @@ export const TaskList: React.FC<TaskListProps> = ({
onUpdateTask,
onCellCommit,
effortDisplayUnit,
actualsOptions,
enableColumnDrag = true,
onHorizontalScroll,
}) => {
Expand Down Expand Up @@ -265,6 +276,54 @@ export const TaskList: React.FC<TaskListProps> = ({
}
const rowId = editingState.rowId;
const columnId = editingState.columnId;
const task = tasks.find(row => row.id === rowId);
const resolveActualsCommit = () => {
if (!task) {
return null;
}
if (columnId !== "start" && columnId !== "end" && columnId !== "actualEffort") {
return null;
}
const parsedValue =
columnId === "actualEffort"
? sanitizeEffortInput(value)
: parseDateFromInput(value);
if (parsedValue === undefined) {
return null;
}
const invalidEndForRecalc = new Date("invalid");
const draftTask = {
...task,
[columnId]: parsedValue,
...(columnId === "actualEffort"
? { end: invalidEndForRecalc }
: {}),
} as Task;
Comment thread
LevelCapTech marked this conversation as resolved.
Outdated
const normalized = normalizeActuals(draftTask, actualsOptions ?? {});
const updatedFields: Partial<Task> = {};
if (normalized.start.getTime() !== task.start.getTime()) {
updatedFields.start = normalized.start;
}
if (normalized.end.getTime() !== task.end.getTime()) {
updatedFields.end = normalized.end;
Comment thread
LevelCapTech marked this conversation as resolved.
Outdated
}
if (normalized.actualEffort !== task.actualEffort) {
updatedFields.actualEffort = normalized.actualEffort;
}
const normalizedValue =
columnId === "actualEffort"
? normalized.actualEffort !== undefined
? `${normalized.actualEffort}`
: value
: columnId === "start"
? formatDate(normalized.start)
: formatDate(normalized.end);
return {
normalizedValue,
updatedFields: Object.keys(updatedFields).length > 0 ? updatedFields : null,
};
};
const actualsCommit = resolveActualsCommit();
setEditingState(prev => {
if (
prev.mode !== "editing" ||
Expand All @@ -277,7 +336,11 @@ export const TaskList: React.FC<TaskListProps> = ({
return { ...prev, pending: true, errorMessage: null };
});
try {
await onCellCommit({ rowId, columnId, value, trigger });
const commitValue = actualsCommit?.normalizedValue ?? value;
await onCellCommit({ rowId, columnId, value: commitValue, trigger });
if (actualsCommit?.updatedFields && onUpdateTask) {
onUpdateTask(rowId, actualsCommit.updatedFields);
}
if (!mountedRef.current) {
return;
}
Expand Down Expand Up @@ -324,7 +387,7 @@ export const TaskList: React.FC<TaskListProps> = ({
});
}
},
[editingState, onCellCommit]
[actualsOptions, editingState, onCellCommit, onUpdateTask, tasks]
);

const selectCell = useCallback((rowId: string, columnId: VisibleField) => {
Expand Down
Loading