diff --git a/client/cms/.sisyphus/drafts/checkin_logic.md b/client/cms/.sisyphus/drafts/checkin_logic.md new file mode 100644 index 0000000..606a434 --- /dev/null +++ b/client/cms/.sisyphus/drafts/checkin_logic.md @@ -0,0 +1,32 @@ +# Draft: Implement Check-in Logic + +## Requirements (User) +- **Input**: 6-digit number from scanner. +- **Action**: Call `/event/checkin/submit` (`postEventCheckinSubmit`). +- **Feedback**: Toaster (success/failure) using `sonner`. + +## Research Questions +1. [Resolved] API Client: `postEventCheckinSubmit` exists. +2. [Pending] API Parameters: Need to verify `PostEventCheckinSubmitData`. +3. [Resolved] Toaster Library: `sonner` (`toast.success`, `toast.error`). + +## Technical Decisions +- **Logic Placement**: `CheckinScannerNavContainer`. +- **State Management**: `useMutation` from `@tanstack/react-query`. +- **Validation**: Regex `^\d{6}$` for 6-digit number. +- **Error Handling**: `onError` in mutation -> `toast.error`. +- **Success Handling**: `onSuccess` in mutation -> `toast.success`. + +## Code Snippets +```typescript +import { useMutation } from '@tanstack/react-query'; +import { postEventCheckinSubmit } from '@/client/sdk.gen'; +import { toast } from 'sonner'; + +// In container +const { mutate } = useMutation({ + mutationFn: (code: string) => postEventCheckinSubmit({ body: { code } }), + onSuccess: () => toast.success('签到成功'), + onError: () => toast.error('签到失败'), +}); +``` diff --git a/client/cms/.sisyphus/notepads/implement-checkin-logic/learnings.md b/client/cms/.sisyphus/notepads/implement-checkin-logic/learnings.md new file mode 100644 index 0000000..f5a2ace --- /dev/null +++ b/client/cms/.sisyphus/notepads/implement-checkin-logic/learnings.md @@ -0,0 +1,9 @@ +### useCheckinSubmit Hook +- Created `src/hooks/data/useCheckinSubmit.ts` using `postEventCheckinSubmitMutation` from `@/client/@tanstack/react-query.gen`. +- Integrated `sonner` for success and error toasts. +- Followed the pattern from `useJoinEvent.ts`. + +### CheckinScannerNav Implementation +- `CheckinScannerNavView` validation logic implemented with regex `^\d{6}$`. +- `CheckinScannerNavContainer` connects the hook to the view. +- Type checking passed with `bun tsc -b`. diff --git a/client/cms/.sisyphus/plans/implement-checkin-logic.md b/client/cms/.sisyphus/plans/implement-checkin-logic.md new file mode 100644 index 0000000..ef14632 --- /dev/null +++ b/client/cms/.sisyphus/plans/implement-checkin-logic.md @@ -0,0 +1,204 @@ +# Plan: Implement Check-in Logic + +## TL;DR + +> **Quick Summary**: Connect the scanner to the backend check-in API. When a 6-digit code is scanned, submit it to `/event/checkin/submit`. Show success/error toasts. +> +> **Deliverables**: +> - Updated `CheckinScannerNavContainer` with mutation logic. +> - Integration with `sonner` for user feedback. +> - Proper parameter mapping (`checkin_code`). +> +> **Estimated Effort**: Short +> **Parallel Execution**: NO - sequential implementation. +> **Critical Path**: Implement Mutation → Update View Integration + +--- + +## Context + +### Original Request +"扫码器扫到的如果是6位数字,使用/event/checkin/submit接口进行签到,成功/失败都弹出toaster提示。" + +### Interview Summary +**Key Discussions**: +- **API**: `postEventCheckinSubmit` is the correct client function. +- **Parameters**: API expects `checkin_code` in the body. +- **Input**: "6位数字" implies regex validation `^\d{6}$`. +- **Feedback**: Use `sonner` (`toast.success`, `toast.error`). + +**Metis Review Findings**: +- **Critical Fix**: Ensure parameter name is `checkin_code`, not `code`. +- **UX**: Disable scanning while `isPending` to prevent double submissions. +- **Error Handling**: Use generic "签到失败" for errors unless specific message available. + +--- + +## Work Objectives + +### Core Objective +Make the check-in scanner functional by connecting it to the backend. + +### Concrete Deliverables +- `src/components/checkin/checkin-scanner-nav.container.tsx`: Updated with `useMutation`. +- `src/components/checkin/checkin-scanner-nav.view.tsx`: Updated to receive `isPending` prop and handle scan events. + +### Definition of Done +- [ ] Scanning "123456" calls API with `{"checkin_code": "123456"}`. +- [ ] Success response shows "签到成功" toast. +- [ ] Error response shows "签到失败" toast. +- [ ] Scanner ignores non-6-digit inputs. +- [ ] Scanner pauses/ignores input while API is pending. + +### Must Have +- Regex validation: `^\d{6}$`. +- `checkin_code` parameter mapping. +- Toaster feedback. + +### Must NOT Have (Guardrails) +- Do NOT change the existing permission logic in the container. +- Do NOT remove the Dialog wrapping. + +--- + +## Verification Strategy (MANDATORY) + +> **UNIVERSAL RULE: ZERO HUMAN INTERVENTION** +> ALL tasks in this plan MUST be verifiable WITHOUT any human action. + +### Test Decision +- **Infrastructure exists**: YES (Playwright). +- **Automated tests**: YES (Playwright API mocking). + +### Agent-Executed QA Scenarios (MANDATORY) + +**Scenario 1: Successful Check-in** +- **Tool**: Playwright +- **Steps**: + 1. Mock `/event/checkin/submit` to return 200 OK. + 2. Simulate scan event with "123456". + 3. Assert API called with correct body. + 4. Assert "签到成功" toast visible. + +**Scenario 2: Failed Check-in** +- **Tool**: Playwright +- **Steps**: + 1. Mock `/event/checkin/submit` to return 400 Bad Request. + 2. Simulate scan event with "123456". + 3. Assert "签到失败" toast visible. + +**Scenario 3: Invalid Input** +- **Tool**: Playwright (if view exposes logic) or Unit Test +- **Steps**: + 1. Simulate scan event with "ABC". + 2. Assert API NOT called. + +--- + +## Execution Strategy + +### Parallel Execution Waves + +``` +Wave 1: +├── Task 1: Create Data Hook +└── Task 2: Implement Container & View Logic +``` + +--- + +## TODOs + +- [x] 1. Create Data Hook + + **What to do**: + - Create `src/hooks/data/useCheckinSubmit.ts`. + - Import `useMutation` from `@tanstack/react-query`. + - Import `postEventCheckinSubmitMutation` from `@/client/@tanstack/react-query.gen`. + - Import `toast` from `sonner`. + - Export `useCheckinSubmit` hook that returns the mutation. + - Use `...postEventCheckinSubmitMutation()` pattern. + - On success: `toast.success('签到成功')`. + - On error: `toast.error('签到失败')`. + + **Recommended Agent Profile**: + - **Category**: `quick` + - **Skills**: [`frontend-ui-ux`] + + **Parallelization**: + - **Can Run In Parallel**: YES + - **Parallel Group**: Wave 1 + + **References**: + - `src/hooks/data/useJoinEvent.ts` (Pattern reference) + - `src/client/@tanstack/react-query.gen.ts` + + **Acceptance Criteria**: + - [ ] Hook uses `@hey-api` pattern. + - [ ] Toasts are configured. + +- [x] 2. Implement Container & View Logic + + **What to do**: + - Update `src/components/checkin/checkin-scanner-nav.container.tsx`: + - Import `useCheckinSubmit` from `@/hooks/data/useCheckinSubmit`. + - Use the hook to get `mutate` and `isPending`. + - Pass `handleScan` (wrapper calling mutate with `{ body: { checkin_code: code } }`) and `isPending` to View. + - Update `src/components/checkin/checkin-scanner-nav.view.tsx`: + - Accept `onScan` and `isPending` props. + - Inside internal `handleScan`, check regex `^\d{6}$`. + - If valid and !isPending, call prop `onScan`. + + **Recommended Agent Profile**: + - **Category**: `visual-engineering` + - **Skills**: [`frontend-ui-ux`] + + **Parallelization**: + - **Can Run In Parallel**: YES + - **Parallel Group**: Wave 1 + - **References**: + - `src/hooks/data/useCheckinSubmit.ts` (Dependency) + + **Acceptance Criteria**: + - [ ] Container uses the new hook. + - [ ] View logic validates regex. + +- [ ] 2. Update Playwright Verification + + **What to do**: + - Update `tests/checkin-scanner.spec.ts`. + - Add test case for successful check-in (mock API success). + - Add test case for failed check-in (mock API failure). + - Verify toaster appearance. + + **Recommended Agent Profile**: + - **Category**: `quick` + - **Skills**: [`playwright`] + + **Parallelization**: + - **Can Run In Parallel**: NO + - **Parallel Group**: Wave 1 + - **Blocked By**: Task 1 + + **References**: + - `tests/checkin-scanner.spec.ts` + + **Acceptance Criteria**: + - [ ] Tests pass. + + **Agent-Executed QA Scenarios**: + ``` + Scenario: Run Updated Tests + Tool: Bash + Steps: + 1. npx playwright test tests/checkin-scanner.spec.ts + ``` + +--- + +## Success Criteria + +### Final Checklist +- [ ] API integration complete. +- [ ] Regex validation matches `^\d{6}$`. +- [ ] User feedback (toasts) functional. diff --git a/client/cms/src/components/checkin/checkin-scanner-nav.container.tsx b/client/cms/src/components/checkin/checkin-scanner-nav.container.tsx index 1e1e821..955557f 100644 --- a/client/cms/src/components/checkin/checkin-scanner-nav.container.tsx +++ b/client/cms/src/components/checkin/checkin-scanner-nav.container.tsx @@ -1,12 +1,19 @@ import { useUserInfo } from '@/hooks/data/useUserInfo'; +import { useCheckinSubmit } from '@/hooks/data/useCheckinSubmit'; import { CheckinScannerNavView } from './checkin-scanner-nav.view'; export function CheckinScannerNavContainer() { const { data } = useUserInfo(); + const { mutate, isPending } = useCheckinSubmit(); if ((data.data?.permission_level ?? 0) <= 20) { return null; } - return ; + return ( + mutate({ body: { checkin_code: code } })} + isPending={isPending} + /> + ); } diff --git a/client/cms/src/components/checkin/checkin-scanner-nav.view.tsx b/client/cms/src/components/checkin/checkin-scanner-nav.view.tsx index dee5b18..0e46a72 100644 --- a/client/cms/src/components/checkin/checkin-scanner-nav.view.tsx +++ b/client/cms/src/components/checkin/checkin-scanner-nav.view.tsx @@ -4,11 +4,22 @@ import { Dialog, DialogTrigger } from '@/components/ui/dialog'; import { SidebarMenuButton, SidebarMenuItem } from '@/components/ui/sidebar'; import { CheckinScannerDialogView } from './checkin-scanner.dialog.view'; -export function CheckinScannerNavView() { +interface CheckinScannerNavViewProps { + onScan: (code: string) => void; + isPending: boolean; +} + +export function CheckinScannerNavView({ onScan, isPending }: CheckinScannerNavViewProps) { const [open, setOpen] = useState(false); const handleScan = (value: string) => { - console.log('Scanned:', value); + if (isPending) return; + + if (!/^\d{6}$/.test(value)) { + return; + } + + onScan(value); setOpen(false); }; diff --git a/client/cms/src/hooks/data/useCheckinSubmit.ts b/client/cms/src/hooks/data/useCheckinSubmit.ts new file mode 100644 index 0000000..e894680 --- /dev/null +++ b/client/cms/src/hooks/data/useCheckinSubmit.ts @@ -0,0 +1,15 @@ +import { useMutation } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import { postEventCheckinSubmitMutation } from '@/client/@tanstack/react-query.gen'; + +export function useCheckinSubmit() { + return useMutation({ + ...postEventCheckinSubmitMutation(), + onSuccess: () => { + toast.success('签到成功'); + }, + onError: () => { + toast.error('签到失败'); + }, + }); +}