diff --git a/package.json b/package.json index 1bf2185..8e1148f 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "check:types": "tsc", "check:format": "biome check .", "check:unused": "knip", - "check-all": "yarn check:types && yarn check:format && yarn check:unused" + "check-all": "yarn check:types && yarn check:format && yarn check:unused", + "fix": "yarn biome check --write ." }, "dependencies": { "@radix-ui/react-alert-dialog": "^1.1.15", diff --git a/src/layouts/RootLayout.tsx b/src/layouts/RootLayout.tsx index 4f55f61..50c5bec 100644 --- a/src/layouts/RootLayout.tsx +++ b/src/layouts/RootLayout.tsx @@ -7,7 +7,7 @@ export default function RootLayout() {
diff --git a/src/routes.ts b/src/routes.ts index a96015d..45e76d4 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -1,6 +1,7 @@ import { createBrowserRouter } from 'react-router'; import RootLayout from './layouts/RootLayout'; import Event from './routes/Event'; +import Guests from './routes/Guests'; import Home from './routes/Home'; import Login from './routes/Login'; import RegisterChoice from './routes/RegisterChoice'; @@ -22,6 +23,9 @@ export const router = createBrowserRouter([ { path: '/event/:id', Component: RootLayout, - children: [{ index: true, Component: Event }], + children: [ + { index: true, Component: Event }, + { path: 'guests', Component: Guests }, + ], }, ]); diff --git a/src/routes/Event.tsx b/src/routes/Event.tsx index 1777841..a88704c 100644 --- a/src/routes/Event.tsx +++ b/src/routes/Event.tsx @@ -124,16 +124,14 @@ export default function Event() { }, [id]); const handleDelete = () => { - if (confirm('정말 이 일정을 삭제하시겠습니까?')) { - // 삭제 API 필요 - console.info('Deleting event...'); - navigate('/'); - } + // 삭제 API 필요 + console.info('Deleting event...'); + toast.error('일정이 삭제되었습니다.'); + navigate('/'); }; const handleCopyLink = () => { navigator.clipboard.writeText(joinLink); - // sonner 사용: 아주 간결합니다. toast.success('링크가 복사되었습니다!', { description: '참여자에게 주소를 공유해 보세요.', }); @@ -142,69 +140,70 @@ export default function Event() { if (!schedule) return null; return ( -
- {/* 1. 상단 네비게이션*/} -
- -

- {schedule.title} -

+
+ {/* 1. 상단 네비게이션 */} +
+
+ +

+ {schedule.title} +

- - - - - - navigate('edit')} - className="cursor-pointer" - > - 일정 수정하기 - + + + + + + navigate('edit')} + className="cursor-pointer" + > + 일정 수정하기 + - {/* 삭제 버튼은 AlertDialog와 연결 */} - - -
- 일정 삭제하기 -
-
- - - - 정말 일정을 삭제하시겠습니까? - - - 삭제된 일정은 복구할 수 없으며, 모든 참여 정보가 함께 - 사라집니다. - - - - 취소 - - 삭제 - - - -
-
-
+ + +
+ 일정 삭제하기 +
+
+ + + + 정말 일정을 삭제하시겠습니까? + + + 삭제된 일정은 복구할 수 없으며, 모든 참여 정보가 함께 + 사라집니다. + + + + 취소 + + 삭제 + + + +
+
+
+
{/* 2. 메인 콘텐츠*/} -
+
{/* 일정 정보 (왼쪽 정렬) */}

@@ -272,7 +271,7 @@ export default function Event() { +

+ 참여자 명단({guests.length}) +

+
+
+ + {/* 2. 참여자 리스트 */} +
+ {guests.map((guest) => ( +
+
+ + + + {guest.name.slice(0, 2)} + + +
+ + {guest.name} + + {guest.email} +
+
+ + {/* 강제취소 버튼 */} + + + + + + + + {guest.name} 님의 신청을 취소하시겠습니까? + + + 취소 후 원복이 어렵습니다. 취소 메일이 참여자에게 + 전송됩니다. + + + + 신청 유지하기 + + handleCancelGuest(guest.registration_id, guest.name) + } + className="bg-red-600 hover:bg-red-700" + > + 취소하기 + + + + +
+ ))} +
+
+ ); +} diff --git a/src/routes/Login.tsx b/src/routes/Login.tsx index 9769160..9aeec8d 100644 --- a/src/routes/Login.tsx +++ b/src/routes/Login.tsx @@ -14,104 +14,106 @@ export default function Login() { }; return ( -
-
-

로그인

-

- 계정이 없으신가요?{' '} - - 회원가입하러 가기 - -

-
+
+
+
+

로그인

+

+ 계정이 없으신가요?{' '} + + 회원가입하러 가기 + +

+
-
-
-
- setUsername(e.target.value)} - /> + +
+
+ setUsername(e.target.value)} + /> +
+
+ setPassword(e.target.value)} + /> +
+
- setPassword(e.target.value)} - /> +
-
+ -
- -
- - -
-
-
-
-
-
- - 또는 소셜 계정으로 로그인 - +
+
+
+
+
+
+ + 또는 소셜 계정으로 로그인 + +
-
-
diff --git a/src/routes/RegisterChoice.tsx b/src/routes/RegisterChoice.tsx index 2bc5244..e7ec36f 100644 --- a/src/routes/RegisterChoice.tsx +++ b/src/routes/RegisterChoice.tsx @@ -5,71 +5,75 @@ export default function RegisterChoice() { const navigate = useNavigate(); return ( -
-
-

- 회원가입 -

-

- 모이샤와 함께 모임 활동을 시작해 보세요! -

-
- -
- - - - - - - - +
+
+
+

+ 회원가입 +

+

+ 모이샤와 함께 모임 활동을 시작해 보세요! +

+
- - - - - -
+
+ + + + + + + + -
- -
- 또는 + +
+
+ +
+
+ + 또는 + +
-
- + +
); } diff --git a/src/routes/RegisterForm.tsx b/src/routes/RegisterForm.tsx index 4a6db63..1e5f39f 100644 --- a/src/routes/RegisterForm.tsx +++ b/src/routes/RegisterForm.tsx @@ -82,164 +82,166 @@ export default function RegisterForm() { const errorTextStyle = 'mt-1 text-xs text-red-500 font-medium'; return ( -
-

- 회원 정보 입력 -

- -
-
-
- {previewUrl ? ( - Preview +
+

+ 회원 정보 입력 +

+ + +
+
+ {previewUrl ? ( + Preview + ) : ( + 사진 없음 + )} +
+ +
+ +
+ + setName(e.target.value)} + placeholder="이름을 입력하세요" + /> + + {showErrors && !name && ( +

이름을 입력해주세요.

+ )} +
+ +
+ + 0 && !validations.isEmailValid + ? 'border-red-400 focus:ring-red-100' + : 'border-gray-300 focus:ring-blue-500' + }`} + value={email} + onChange={(e) => setEmail(e.target.value)} + placeholder="example@moisha.com" + /> + + {showErrors && !email && ( +

이메일을 입력해주세요.

+ )} + {email.length > 0 && !validations.isEmailValid && ( +

+ 유효한 이메일 형식을 입력해주세요. +

+ )} +
+ +
+ + 0 && !isPasswordValid + ? 'border-red-400 focus:ring-red-100' + : 'border-gray-300 focus:ring-blue-500' + }`} + value={password} + onChange={(e) => setPassword(e.target.value)} + placeholder="8자 이상, 숫자, 특수문자 포함" + /> + + {showErrors && !password && ( +

비밀번호를 입력해주세요.

+ )} + {password.length > 0 && ( +
    +
  • + {validations.password.isLongEnough ? '✓' : '○'} 8자 이상 +
  • +
  • + {validations.password.hasNumber ? '✓' : '○'} 숫자 포함 +
  • +
  • + {validations.password.hasSpecial ? '✓' : '○'} 특수문자 포함 +
  • +
)}
-
+ +
+ + +
+ +
); }