選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

137 行
4.0 KiB

  1. "use client";
  2. import Card from "@mui/material/Card";
  3. import CardContent from "@mui/material/CardContent";
  4. import Typography from "@mui/material/Typography";
  5. import { useTranslation } from "react-i18next";
  6. import Button from "@mui/material/Button";
  7. import React, { useCallback, useMemo, useState } from "react";
  8. import CardActions from "@mui/material/CardActions";
  9. import RestartAlt from "@mui/icons-material/RestartAlt";
  10. import {
  11. Alert,
  12. FormControl,
  13. Grid,
  14. InputLabel,
  15. MenuItem,
  16. Select,
  17. SelectChangeEvent,
  18. Stack,
  19. } from "@mui/material";
  20. import { Task, TaskGroup } from "@/app/api/tasks";
  21. import uniqBy from "lodash/uniqBy";
  22. import { moneyFormatter } from "@/app/utils/formatUtil";
  23. import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
  24. import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
  25. import dayjs from "dayjs";
  26. interface Props {
  27. tasks: Task[];
  28. }
  29. const ResourceMilestone: React.FC<Props> = ({ tasks }) => {
  30. const { t } = useTranslation();
  31. const taskGroups = useMemo(() => {
  32. return uniqBy(
  33. tasks.map((task) => task.taskGroup),
  34. "id",
  35. );
  36. }, [tasks]);
  37. const [currentTaskGroupId, setCurrentTaskGroupId] = useState(
  38. taskGroups[0].id,
  39. );
  40. const onSelectTaskGroup = useCallback(
  41. (event: SelectChangeEvent<TaskGroup["id"]>) => {
  42. const id = event.target.value;
  43. setCurrentTaskGroupId(typeof id === "string" ? parseInt(id) : id);
  44. },
  45. [],
  46. );
  47. return (
  48. <>
  49. <Card>
  50. <CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
  51. <FormControl>
  52. <InputLabel>{t("Task Stage")}</InputLabel>
  53. <Select
  54. label={t("Task Stage")}
  55. onChange={onSelectTaskGroup}
  56. value={currentTaskGroupId}
  57. >
  58. {taskGroups.map((taskGroup) => (
  59. <MenuItem key={taskGroup.id} value={taskGroup.id}>
  60. {taskGroup.name}
  61. </MenuItem>
  62. ))}
  63. </Select>
  64. </FormControl>
  65. <Typography variant="overline" display="block" marginBlockEnd={1}>
  66. {t("Resource")}
  67. </Typography>
  68. <Typography variant="overline" display="block" marginBlockEnd={1}>
  69. {t("Milestone")}
  70. </Typography>
  71. <LocalizationProvider dateAdapter={AdapterDayjs}>
  72. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  73. <Grid item xs>
  74. <FormControl fullWidth>
  75. <DatePicker
  76. label={t("Stage Start Date")}
  77. defaultValue={dayjs()}
  78. />
  79. </FormControl>
  80. </Grid>
  81. <Grid item xs>
  82. <FormControl fullWidth>
  83. <DatePicker
  84. label={t("Stage End Date")}
  85. defaultValue={dayjs()}
  86. />
  87. </FormControl>
  88. </Grid>
  89. </Grid>
  90. </LocalizationProvider>
  91. <CardActions sx={{ justifyContent: "flex-end" }}>
  92. <Button variant="text" startIcon={<RestartAlt />}>
  93. {t("Reset")}
  94. </Button>
  95. </CardActions>
  96. </CardContent>
  97. </Card>
  98. <Card>
  99. <CardContent>
  100. <Stack direction="row" justifyContent="space-between">
  101. <Typography variant="h6">{t("Project Total Fee")}</Typography>
  102. <Typography>{moneyFormatter.format(80000)}</Typography>
  103. </Stack>
  104. </CardContent>
  105. </Card>
  106. </>
  107. );
  108. };
  109. const NoTaskState: React.FC = () => {
  110. const { t } = useTranslation();
  111. return (
  112. <Card>
  113. <CardContent>
  114. <Alert severity="error">
  115. {t('Please add some tasks in "Project Task Setup" first!')}
  116. </Alert>
  117. </CardContent>
  118. </Card>
  119. );
  120. };
  121. const ResourceMilestoneWrapper: React.FC<Props> = (props) => {
  122. if (props.tasks.length === 0) {
  123. return <NoTaskState />;
  124. }
  125. return <ResourceMilestone {...props} />;
  126. };
  127. export default ResourceMilestoneWrapper;