diff --git a/src/Components/DeleteGoalModal/DeleteGoalModal.tsx b/src/Components/DeleteGoalModal/DeleteGoalModal.tsx index 71b2df0..ca681b9 100644 --- a/src/Components/DeleteGoalModal/DeleteGoalModal.tsx +++ b/src/Components/DeleteGoalModal/DeleteGoalModal.tsx @@ -37,9 +37,8 @@ export default function DeleteGoalModal({ goalId }: { goalId: string }) { return ( <> setOpen(true)} - sx={{ cursor: "pointer" }} + sx={{ cursor: "pointer", fontSize: "23px" }} /> + + + + + ); +} diff --git a/src/Components/GoalModal/CopyGoalButton.tsx b/src/Components/GoalModal/CopyGoalButton.tsx new file mode 100644 index 0000000..042fff6 --- /dev/null +++ b/src/Components/GoalModal/CopyGoalButton.tsx @@ -0,0 +1,29 @@ +import LibraryAddOutlinedIcon from "@mui/icons-material/LibraryAddOutlined"; +import { useState } from "react"; +import CreateGoalModal from "./CreateGoalModal"; + +export default function CopyModalButton({ + deadline, + goalText, +}: { + deadline: string; + goalText: string; +}) { + const [open, setOpen] = useState(false); + + return ( + <> + setOpen(true)} + sx={{ cursor: "pointer", fontSize: "23px" }} + /> + + + + ); +} diff --git a/src/Components/GoalModal/CreateGoalModal.tsx b/src/Components/GoalModal/CreateGoalModal.tsx new file mode 100644 index 0000000..3472a55 --- /dev/null +++ b/src/Components/GoalModal/CreateGoalModal.tsx @@ -0,0 +1,159 @@ +"use client"; +import { showSnackBar } from "@/Components/SnackBar/SnackBar"; +import { Goal } from "@/types/types"; +import { createGoal, handleCreateGoalError } from "@/utils/API/Goal/createGoal"; +import { useUser } from "@/utils/UserContext"; +import SendIcon from "@mui/icons-material/Send"; +import { + Button, + DialogContent, + DialogTitle, + Input, + Modal, + ModalDialog, + Stack, + Typography, +} from "@mui/joy"; +import React, { useEffect, useState } from "react"; + +export default function CreateGoalModal({ + open, + setOpen, + defaultText, + defaultDeadline, +}: { + open: boolean; + setOpen: React.Dispatch>; + defaultText?: string; + defaultDeadline?: string; +}) { + const [text, setText] = useState(""); + const [dueDate, setDueDate] = useState(""); + + const { user } = useUser(); + + useEffect(() => { + if (defaultText) { + setText(defaultText); + } + if (defaultDeadline) { + const convertedDate = new Date(defaultDeadline); + const localDate = new Date( + convertedDate.getTime() - convertedDate.getTimezoneOffset() * 60000 + ); + localDate.setDate(localDate.getDate() + 1); // 1日後にする + setDueDate(localDate.toISOString().slice(0, 16)); + } + }, [defaultText, defaultDeadline]); + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + // 過去の時間が入力されている場合 + if (new Date(dueDate).getTime() < Date.now()) { + showSnackBar({ + message: "過去の時間を指定することはできません", + type: "warning", + }); + return; + } + + const postData: Goal = { + userId: user?.userId as string, + text: text, + deadline: new Date(dueDate), + }; + + try { + const data = await createGoal(postData); + console.log("Success:", data); + + showSnackBar({ + message: "目標を作成しました", + type: "success", + }); + + setText(""); + setDueDate(""); + setOpen(false); + } catch (error: unknown) { + console.error("Error creating goal:", error); + const message = handleCreateGoalError(error); + showSnackBar({ + message, + type: "warning", + }); + } + }; + + // 以下のJoy UIによるエラーを無効化 + // Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release. Error Component Stack + try { + const consoleError = console.error; + console.error = (...args) => { + if (args[0]?.includes("Accessing element.ref was removed")) { + return; + } + consoleError(...args); + }; + } catch { + console.error("Failed to disable Joy UI error"); + } + + return ( + setOpen(false)} + keepMounted + disablePortal={false} + > + + 目標を作成 + 達成したい内容と期限を入力してください +
+ + setText(e.target.value)} + required + /> + setDueDate(e.target.value)} + required + /> + + 期限が1時間以内の目標は削除できなくなります + + + + + + +
+
+
+ ); +} diff --git a/src/Components/GoalModal/GoalModal.tsx b/src/Components/GoalModal/GoalModal.tsx deleted file mode 100644 index 272a99e..0000000 --- a/src/Components/GoalModal/GoalModal.tsx +++ /dev/null @@ -1,168 +0,0 @@ -"use client"; -import { showSnackBar } from "@/Components/SnackBar/SnackBar"; -import { Goal } from "@/types/types"; -import { createGoal, handleCreateGoalError } from "@/utils/API/Goal/createGoal"; -import { useUser } from "@/utils/UserContext"; -import AddIcon from "@mui/icons-material/Add"; -import SendIcon from "@mui/icons-material/Send"; -import { - Button, - DialogContent, - DialogTitle, - Input, - Modal, - ModalDialog, - Stack, - Typography, -} from "@mui/joy"; -import Box from "@mui/material/Box"; -import Fab from "@mui/material/Fab"; -import React, { useState } from "react"; - -export default function GoalModal() { - const [open, setOpen] = useState(false); - const [text, setText] = useState(""); - const [dueDate, setDueDate] = useState(""); - const { user } = useUser(); - - const handleSubmit = async (event: React.FormEvent) => { - event.preventDefault(); - - // 過去の時間が入力されている場合 - if (new Date(dueDate).getTime() < Date.now()) { - showSnackBar({ - message: "過去の時間を指定することはできません", - type: "warning", - }); - return; - } - - const postData: Goal = { - userId: user?.userId as string, - text: text, - deadline: new Date(dueDate), - }; - - try { - const data = await createGoal(postData); - console.log("Success:", data); - - showSnackBar({ - message: "目標を作成しました", - type: "success", - }); - - setText(""); - setDueDate(""); - setOpen(false); - } catch (error: unknown) { - console.error("Error creating goal:", error); - const message = handleCreateGoalError(error); - showSnackBar({ - message, - type: "warning", - }); - } - }; - - // 以下のJoy UIによるエラーを無効化 - // Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release. Error Component Stack - try { - const consoleError = console.error; - console.error = (...args) => { - if (args[0]?.includes("Accessing element.ref was removed")) { - return; - } - consoleError(...args); - }; - } catch { - console.error("Failed to disable Joy UI error"); - } - - return ( - <> - :not(style)": { m: 1 }, - display: "flex", - flexDirection: "row-reverse", - position: "fixed", - bottom: "90px", - width: "100%", - maxWidth: "600px", - zIndex: 1000, - }} - > - setOpen(true)} - disabled={ - !user || user?.loginType === "Guest" || !user?.isMailVerified - } - > - - - - - setOpen(false)} - keepMounted - disablePortal - > - - 目標を作成 - 達成したい内容と期限を入力してください -
- - setText(e.target.value)} - required - /> - setDueDate(e.target.value)} - required - /> - - 期限が1時間以内の目標は削除できなくなります - - - - - - -
-
-
- - ); -} diff --git a/src/Components/GoalModal/GoalModalButton.tsx b/src/Components/GoalModal/GoalModalButton.tsx new file mode 100644 index 0000000..f345e56 --- /dev/null +++ b/src/Components/GoalModal/GoalModalButton.tsx @@ -0,0 +1,44 @@ +import { useUser } from "@/utils/UserContext"; +import AddIcon from "@mui/icons-material/Add"; +import Box from "@mui/material/Box"; +import Fab from "@mui/material/Fab"; +import { useState } from "react"; +import CreateGoalModal from "./CreateGoalModal"; + +export default function GoalModalButton() { + const [open, setOpen] = useState(false); + + const { user } = useUser(); + + return ( + <> + :not(style)": { m: 1 }, + display: "flex", + flexDirection: "row-reverse", + position: "fixed", + bottom: "90px", + width: "100%", + maxWidth: "600px", + zIndex: 1000, + }} + > + setOpen(true)} + disabled={ + !user || user?.loginType === "Guest" || !user?.isMailVerified + } + > + + + + + + + ); +} diff --git a/src/Components/PostModal/PostModal.tsx b/src/Components/PostModal/PostModal.tsx index 65ca544..769e369 100644 --- a/src/Components/PostModal/PostModal.tsx +++ b/src/Components/PostModal/PostModal.tsx @@ -24,7 +24,13 @@ import Typography from "@mui/material/Typography"; import { styled } from "@mui/material/styles"; import React, { ChangeEvent, useState } from "react"; -export default function PostModal({ goalId }: { goalId: string }) { +export default function PostModal({ + goalId, + setIsSubmitted, +}: { + goalId: string; + setIsSubmitted: React.Dispatch>; +}) { const [open, setOpen] = useState(false); const [text, setText] = useState(""); const [image, setImage] = useState(null); @@ -104,6 +110,7 @@ export default function PostModal({ goalId }: { goalId: string }) { message: "投稿しました", type: "success", }); + setIsSubmitted(true); setImage(null); setText(""); diff --git a/src/Components/Progress/Progress.tsx b/src/Components/Progress/Progress.tsx index ba31dce..29fa70c 100644 --- a/src/Components/Progress/Progress.tsx +++ b/src/Components/Progress/Progress.tsx @@ -15,6 +15,8 @@ import { Divider } from "@mui/material"; import { ReactNode, useEffect, useState } from "react"; import DeleteGoalModal from "../DeleteGoalModal/DeleteGoalModal"; import DeletePostModal from "../DeletePostModal/DeletePostModal"; +import CopyGoalAfterPostButton from "../GoalModal/CopyGoalAfterPostButton"; +import CopyModalButton from "../GoalModal/CopyGoalButton"; import PostModal from "../PostModal/PostModal"; const successPostIndicatorStyle = { @@ -97,18 +99,40 @@ export default function Progress({ <> {allResults.map((result) => { const userName = userNames[result.userId] || "Loading..."; + + if (!user) { + return null; + } + if (result.type === "success") { - return successStep( - result as SuccessResult, - userName, - user as UserData + return ( + ); } if (result.type === "failed") { - return failedStep(result as GoalWithId, userName, user as UserData); + return ( + + ); } if (result.type === "pending") { - return pendingStep(result as GoalWithId, userName, user as UserData); + return ( + + ); } return null; })} @@ -116,11 +140,15 @@ export default function Progress({ ); } -const successStep = ( - result: SuccessResult, - userName: string, - user: UserData -) => { +const SuccessStep = ({ + result, + userName, + user, +}: { + result: SuccessResult; + userName: string; + user: UserData; +}) => { return ( { +const FailedStep = ({ + result, + userName, + user, +}: { + result: GoalWithId; + userName: string; + user: UserData; +}) => { return ( { ); }; -const pendingStep = (result: GoalWithId, userName: string, user: UserData) => { +const PendingStep = ({ + result, + userName, + user, +}: { + result: GoalWithId; + userName: string; + user: UserData; +}) => { + const [isSubmitted, setIsSubmitted] = useState(false); + return ( { userId={result.userId} user={user} /> - {/* 自分の作成した目標の場合のみ投稿可能にする */} - {result.userId === user?.userId && } + {isSubmitted ? ( + // 投稿したら同じ目標で明日にも作成できるボタンを表示する + + ) : ( + // 自分の作成した目標の場合のみ投稿可能にする + result.userId === user?.userId && ( + + ) + )} ); @@ -291,10 +347,13 @@ const GoalCard = ({ {formatStringToDate(deadline)}までに - {/* 期限の1時間以内、もしくは自分の目標ではない場合は削除できないようにする */} - {!isWithinOneHour && userId === user?.userId && ( - - )} +
+ + {/* 期限の1時間以内、もしくは自分の目標ではない場合は削除できないようにする */} + {!isWithinOneHour && userId === user?.userId && ( + + )} +
{goalText} diff --git a/src/app/mycontent/page.tsx b/src/app/mycontent/page.tsx index e12f8ce..8cac74d 100644 --- a/src/app/mycontent/page.tsx +++ b/src/app/mycontent/page.tsx @@ -1,6 +1,6 @@ "use client"; import DashBoard from "@/Components/DashBoard/DashBoard"; -import GoalModal from "@/Components/GoalModal/GoalModal"; +import GoalModalButton from "@/Components/GoalModal/GoalModalButton"; import { useUser } from "@/utils/UserContext"; import { styled } from "@mui/material/styles"; import ToggleButton from "@mui/material/ToggleButton"; @@ -55,7 +55,7 @@ export default function MyContent() { )} - + ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index f112b9f..2460720 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,12 +1,12 @@ "use client"; import DashBoard from "@/Components/DashBoard/DashBoard"; -import GoalModal from "@/Components/GoalModal/GoalModal"; +import GoalModalButton from "@/Components/GoalModal/GoalModalButton"; export default function Top() { return ( <> - + ); }