diff --git a/src/app/(content-layout)/[groupId]/editgroup/_editgroup/DeleteGroupButton.tsx b/src/app/(content-layout)/[groupId]/editgroup/_editgroup/DeleteGroupButton.tsx index 023d78a8..9878f298 100644 --- a/src/app/(content-layout)/[groupId]/editgroup/_editgroup/DeleteGroupButton.tsx +++ b/src/app/(content-layout)/[groupId]/editgroup/_editgroup/DeleteGroupButton.tsx @@ -8,12 +8,12 @@ import BouncingDots from '@/components/common/loading/BouncingDots'; import useModalContext from '@/components/common/modal/core/useModalContext'; import TrashCan from '@/assets/TrashCan'; import { deleteGroup } from '../action'; - -const DELETE_MODAL_ID = 'delete-group'; +import { useUser } from '@/contexts/UserContext'; export default function DeleteGroupButton({ groupId }: { groupId: number }) { const [isPending, setIsPending] = useState(false); const { openModal } = useModalContext(); + const { fetchUser } = useUser(); const router = useRouter(); const handleDeleteGroup = () => { @@ -21,6 +21,7 @@ export default function DeleteGroupButton({ groupId }: { groupId: number }) { deleteGroup(groupId) .then(() => { + fetchUser(); Toast.success('팀 삭제 성공'); router.push('/'); }) @@ -28,17 +29,19 @@ export default function DeleteGroupButton({ groupId }: { groupId: number }) { .finally(() => setIsPending(false)); }; + const deleteModalId = `delete-group-${groupId}`; + return ( <> : '삭제하기'} onConfirm={handleDeleteGroup} diff --git a/src/app/(content-layout)/[groupId]/tasklist/_tasklist/components/Skeleton.tsx b/src/app/(content-layout)/[groupId]/tasklist/_tasklist/components/Skeleton.tsx index c58e60bf..4c27706d 100644 --- a/src/app/(content-layout)/[groupId]/tasklist/_tasklist/components/Skeleton.tsx +++ b/src/app/(content-layout)/[groupId]/tasklist/_tasklist/components/Skeleton.tsx @@ -1,12 +1,12 @@ export function DateSkeleton() { return (
-
+
-
-
+
+
-
+
); } @@ -16,7 +16,7 @@ export function TaskListsSkeleton() {
    {[...Array(4)].map((_, i) => ( -
  1. +
  2. ))}
@@ -29,7 +29,7 @@ export function TaskSkeleton() {
    {[...Array(5)].map((_, i) => ( -
  1. +
  2. ))}
diff --git a/src/app/(content-layout)/[groupId]/tasklist/_tasklist/components/Tasks.tsx b/src/app/(content-layout)/[groupId]/tasklist/_tasklist/components/Tasks.tsx index a36c5c8c..987533dc 100644 --- a/src/app/(content-layout)/[groupId]/tasklist/_tasklist/components/Tasks.tsx +++ b/src/app/(content-layout)/[groupId]/tasklist/_tasklist/components/Tasks.tsx @@ -54,7 +54,7 @@ export default function Tasks({ groupId, tasks, currentTaskList }: Props) { ) : ( -
+

아직 할 일이 없습니다.
할 일을 추가 해보세요. diff --git a/src/app/(content-layout)/articles/ArticlesPageClient.tsx b/src/app/(content-layout)/articles/ArticlesPageClient.tsx index 82af1101..e05569f9 100644 --- a/src/app/(content-layout)/articles/ArticlesPageClient.tsx +++ b/src/app/(content-layout)/articles/ArticlesPageClient.tsx @@ -185,7 +185,7 @@ export default function ArticlesPageClient() { key={article.id} {...article} title={ - article.title.length > 30 ? article.title.slice(0, 30) + '...' : article.title + article.title.length > 50 ? article.title.slice(0, 50) + '...' : article.title } /> ))} @@ -203,7 +203,7 @@ export default function ArticlesPageClient() { diff --git a/src/app/(content-layout)/articles/[articleId]/_articleId/components/DetailArticleInfo.tsx b/src/app/(content-layout)/articles/[articleId]/_articleId/components/DetailArticleInfo.tsx index 1e7914d4..35ba9d41 100644 --- a/src/app/(content-layout)/articles/[articleId]/_articleId/components/DetailArticleInfo.tsx +++ b/src/app/(content-layout)/articles/[articleId]/_articleId/components/DetailArticleInfo.tsx @@ -43,7 +43,7 @@ export default function DetailArticleInfo({ detail }: { detail: GetArticleDetail

{detail.image && detail.image !== DEFAULT_IMAGE && (
- article-image + article-image
)}
diff --git a/src/app/(content-layout)/articles/_articles/components/Card.tsx b/src/app/(content-layout)/articles/_articles/components/Card.tsx index a9f2963b..8113d153 100644 --- a/src/app/(content-layout)/articles/_articles/components/Card.tsx +++ b/src/app/(content-layout)/articles/_articles/components/Card.tsx @@ -14,7 +14,7 @@ export default function Card(props: Article) {
-

{title}

+

{title}

{image && image !== DEFAULT_IMAGE && ( diff --git a/src/app/(content-layout)/articles/_articles/components/Skeleton.tsx b/src/app/(content-layout)/articles/_articles/components/Skeleton.tsx index ea1bb25a..deddd261 100644 --- a/src/app/(content-layout)/articles/_articles/components/Skeleton.tsx +++ b/src/app/(content-layout)/articles/_articles/components/Skeleton.tsx @@ -2,9 +2,9 @@ export function BestCardSkeleton() { return (
-
-
-
+
+
+
); @@ -15,7 +15,7 @@ export function CardSkeleton() {
{Array.from({ length: 6 }).map((_, i) => ( -
+
))}
diff --git a/src/app/(content-layout)/myhistory/page.tsx b/src/app/(content-layout)/myhistory/page.tsx index 9143d10c..9f54aca1 100644 --- a/src/app/(content-layout)/myhistory/page.tsx +++ b/src/app/(content-layout)/myhistory/page.tsx @@ -20,7 +20,7 @@ export default async function MyHistoryPage() { {historyTasks.tasksDone.length > 0 ? ( ) : ( -
+

아직 히스토리가 없습니다.

)} @@ -28,7 +28,7 @@ export default async function MyHistoryPage() { ); } catch { return ( -
+

데이터를 불러오는데 실패했습니다.

); diff --git a/src/app/(content-layout)/nogroup/loading.tsx b/src/app/(content-layout)/nogroup/loading.tsx deleted file mode 100644 index 96662241..00000000 --- a/src/app/(content-layout)/nogroup/loading.tsx +++ /dev/null @@ -1,17 +0,0 @@ -export default function Loading() { - return ( -
-
-
-
-
-
-
-
-
-
-
-
-
- ); -} diff --git a/src/app/(form-layout)/joingroup/page.tsx b/src/app/(form-layout)/joingroup/page.tsx index d9061dd3..76f28e67 100644 --- a/src/app/(form-layout)/joingroup/page.tsx +++ b/src/app/(form-layout)/joingroup/page.tsx @@ -6,15 +6,16 @@ import axiosClient from '@/lib/axiosClient'; import Button from '@/components/common/Button'; import FormField from '@/components/common/formField'; import { Toast } from '@/components/common/Toastify'; +import { useUser } from '@/contexts/UserContext'; export default function JoinGroup() { const [inviteLink, setInviteLink] = useState(''); const router = useRouter(); + const { email: userEmail } = useUser(); const handleJoin = async (e: React.FormEvent) => { e.preventDefault(); const token = inviteLink.trim(); - const userEmail = localStorage.getItem('userEmail') ?? ''; try { const response = await axiosClient.post('/groups/accept-invitation', { userEmail, diff --git a/src/app/(form-layout)/signup/_signup/SignupForm.tsx b/src/app/(form-layout)/signup/_signup/SignupForm.tsx index 3554c2c3..6700cb95 100644 --- a/src/app/(form-layout)/signup/_signup/SignupForm.tsx +++ b/src/app/(form-layout)/signup/_signup/SignupForm.tsx @@ -205,9 +205,14 @@ export default function SignupForm() { isFailure={field.isFailure} errorMessage={field.errorMessage} value={formData[field.name as keyof typeof formData]} - onChange={(e: ChangeEvent) => - setFieldValue(field.name as keyof typeof formData, e.target.value) - } + onChange={(e: ChangeEvent) => { + const key = field.name as keyof typeof formData; + setFieldValue(key, e.target.value); + + if (key === 'email' || key === 'nickname') { + setDuplicateError((prev) => ({ ...prev, [key]: false })); + } + }} placeholder={field.placeholder} rightSlot={field.rightSlot} /> diff --git a/src/app/_home/StartButton.tsx b/src/app/_home/StartButton.tsx index ddd82271..91ab535b 100644 --- a/src/app/_home/StartButton.tsx +++ b/src/app/_home/StartButton.tsx @@ -27,7 +27,10 @@ export default function StartButton({ } else if (memberships.length === 0) { determinedHref = PATHS.NOGROUP; } else if (memberships[0].group.id) { - determinedHref = PATHS.getGroupPath(memberships[0].group.id); + const sortedGroups = memberships + .map((m) => m.group) + .toSorted((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + determinedHref = PATHS.getGroupPath(sortedGroups[0].id); } else { determinedHref = PATHS.LOGIN; } diff --git a/src/app/mypage/MypageClient.tsx b/src/app/mypage/MypageClient.tsx index dd9d51f9..2ee7a466 100644 --- a/src/app/mypage/MypageClient.tsx +++ b/src/app/mypage/MypageClient.tsx @@ -2,7 +2,7 @@ import Image from 'next/image'; import { useState, useEffect } from 'react'; -import { updateUserNickname } from './_mypage/action'; +import { updateUserNickname, verifyPassword } from './_mypage/action'; import ProfileImageUploader from './_mypage/ProfileImageUploader'; import NicknameField from './_mypage/NicknameField'; import PasswordField from './_mypage/PasswordField'; @@ -79,7 +79,16 @@ export default function MyPageClient() { openModal('change-password')} + onClick={async () => { + try { + const res = await verifyPassword(email!, password); + if (res?.accessToken) { + openModal('change-password'); + } + } catch (e) { + Toast.error('비밀번호 인증 실패'); + } + }} />

팀 이름은 회사명이나 모임 이름 등으로 설정하면 좋아요. diff --git a/src/components/manage-group/useManageGroup.ts b/src/components/manage-group/useManageGroup.ts index e8b374f2..8bfc2ba6 100644 --- a/src/components/manage-group/useManageGroup.ts +++ b/src/components/manage-group/useManageGroup.ts @@ -30,6 +30,7 @@ export default function useManageGroup({ }) { const [group, setGroup] = useState(groupData ?? INITIAL_GROUP_VALUE); const [isSubmit, setIsSubmit] = useState(false); + const [isPending, setIsPending] = useState(false); const { fetchUser } = useUser(); const router = useRouter(); @@ -85,6 +86,7 @@ export default function useManageGroup({ const handleManageGroupSubmit = (e: React.FormEvent) => { e.preventDefault(); + setIsSubmit(true); if (!isManageTeamFormValid) return; @@ -92,8 +94,7 @@ export default function useManageGroup({ Toast.info('변경 사항이 없습니다.'); return; } - - setIsSubmit(true); + setIsPending(true); axiosClient .request({ @@ -105,14 +106,20 @@ export default function useManageGroup({ }, }) .then((result) => { - router.push(`/${result.data.id}`); fetchUser(); + + setTimeout(() => { + router.push(`/${result.data.id}`); + }, 500); }) .catch(() => { const action = isEdit ? '수정' : '생성'; Toast.error(`팀 ${action} 실패`); }) - .finally(() => setIsSubmit(false)); + .finally(() => { + setIsPending(true); + setIsSubmit(false); + }); }; return { @@ -120,6 +127,7 @@ export default function useManageGroup({ isNameFailure, isImageEmpty, isSubmit, + isPending, imageErrorMessage, nameErrorMessage, handleNameChange, diff --git a/src/components/manage-task-item/useManageTaskItem.ts b/src/components/manage-task-item/useManageTaskItem.ts index a5d08c79..d48a0d60 100644 --- a/src/components/manage-task-item/useManageTaskItem.ts +++ b/src/components/manage-task-item/useManageTaskItem.ts @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { format, getDate, startOfDay } from 'date-fns'; +import { format, getDate, startOfDay, differenceInMinutes } from 'date-fns'; import { Frequency } from '@/app/(content-layout)/[groupId]/tasklist/_tasklist/types/task-type'; import generateTime from './time-table'; import { TaskItemProps, TaskItem, Time } from './type'; @@ -41,14 +41,30 @@ export default function useManageTaskItem({ const [weekDays, setWeekDays] = useState([]); const [isPending, setIsPending] = useState(false); - const initialSelectedTime = (): Time => { - if (!task?.startDate) return { period: '오전', time: am[0] }; - - const date = new Date(task?.startDate); + const getNearestTime = (date: Date): Time => { const hours = date.getHours(); - const period = hours < 12 ? '오전' : '오후'; - const time = format(date, 'hh:mm'); - return { period, time }; + const isAM = hours < 12; + const period = isAM ? '오전' : '오후'; + const list = isAM ? am : pm; + + const formattedTime = list.map((time) => { + const [h, m] = time.split(':').map(Number); + const candidate = new Date(date).setHours(h, m); + + return { + time, + diff: Math.abs(differenceInMinutes(candidate, date)), + }; + }); + + const nearest = formattedTime.reduce((prev, curr) => (curr.diff < prev.diff ? curr : prev)); + + return { period, time: nearest.time }; + }; + + const initialSelectedTime = (): Time => { + const date = task?.startDate ? new Date(task.startDate) : new Date(); + return getNearestTime(date); }; const [selectedTime, setSelectedTime] = useState