feat(members): implement /members page — Kachel-Ansicht (E2, issue #48)
Backend: - Rename V006 migration to V026 (avoid conflict with existing V006) - Migration adds invalidated_at + partial unique index on household_invite Frontend: - Toast.svelte — new system component (message + dismiss) - SegmentedControl.svelte — new system component (options, value, onchange) - members/+page.server.ts — loads members + active invite - members/[userId]/+server.ts — DELETE/PATCH proxy - members/invites/+server.ts — POST (regenerate) proxy - MemberCard.svelte — tile with avatar, kebab, inline role edit - RemoveDialog.svelte — confirmation dialog (desktop modal + BottomSheet mobile) - InviteCard.svelte + InvitePanel.svelte — invite management UI - MemberGrid.svelte — responsive 4/2-col grid with sorted members - members/+page.svelte — page composing all components with optimistic updates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
50
frontend/src/routes/(app)/members/[userId]/server.test.ts
Normal file
50
frontend/src/routes/(app)/members/[userId]/server.test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
vi.mock('$env/dynamic/private', () => ({ env: { BACKEND_URL: 'http://localhost:8080' } }));
|
||||
|
||||
const mockDelete = vi.fn();
|
||||
const mockPatch = vi.fn();
|
||||
vi.mock('$lib/server/api', () => ({
|
||||
apiClient: () => ({ DELETE: mockDelete, PATCH: mockPatch })
|
||||
}));
|
||||
|
||||
const USER_UUID = '22222222-2222-2222-2222-222222222222';
|
||||
|
||||
describe('members server routes', () => {
|
||||
let DELETE: any;
|
||||
let PATCH: any;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockDelete.mockReset();
|
||||
mockPatch.mockReset();
|
||||
vi.resetModules();
|
||||
const mod = await import('./+server');
|
||||
DELETE = mod.DELETE;
|
||||
PATCH = mod.PATCH;
|
||||
});
|
||||
|
||||
it('DELETE proxies to backend and returns 204', async () => {
|
||||
mockDelete.mockResolvedValue({ response: { status: 204 } });
|
||||
const event = {
|
||||
fetch: vi.fn(),
|
||||
params: { userId: USER_UUID },
|
||||
request: { json: vi.fn() }
|
||||
} as any;
|
||||
const res = await DELETE(event);
|
||||
expect(res.status).toBe(204);
|
||||
});
|
||||
|
||||
it('PATCH proxies to backend and returns member response', async () => {
|
||||
mockPatch.mockResolvedValue({
|
||||
data: { status: 'success', data: { userId: USER_UUID, displayName: 'Tom', role: 'planner', joinedAt: '' } },
|
||||
response: { status: 200 }
|
||||
});
|
||||
const event = {
|
||||
fetch: vi.fn(),
|
||||
params: { userId: USER_UUID },
|
||||
request: { json: async () => ({ role: 'planner' }) }
|
||||
} as any;
|
||||
const res = await PATCH(event);
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user