feat: P6 모바일 셀프 조회 PWA 스캐폴드 (ga-mobile-pwa)
설계사 본인 정산명세서/출금/공지를 모바일에서 조회하는 PWA 신규 모듈. 데스크탑 admin UI(ga-frontend)와 별도. 백엔드 무변경 — 기존 ga-api(8082) 재사용. 토큰 키도 mobile.accessToken 으로 분리. 스택: - Vite + React 18 + TS + antd-mobile@5 - vite-plugin-pwa (manifest + service worker autoUpdate) - React Query + Zustand (persist) + react-router-dom - 포트 3001, /api → 8082 dev 프록시 화면 5종 (NavBar + List + PullToRefresh + TabBar): - /login — POST /api/auth/login + GET /api/auth/me - / — 홈 (요약 카드 + 메뉴, 로그아웃) - /statement — GET /api/statements?agentId=… (본인 명세서) - /withdraw — GET /api/withdraw-requests?agentId=… (본인 출금이력) - /notice — GET /api/notices/active PWA: - manifest: name/icon/theme/standalone, ko, / - /api/** NetworkFirst 5s 타임아웃 / 5분 캐시 (오프라인 시 직전 응답) - 아이콘은 SVG placeholder (192/512) — 본 배포 시 PNG 권장 검증: - npm install 407 packages - tsc --noEmit PASS - npm run build PASS — manifest.webmanifest + sw.js + workbox-*.js 자동 생성, precache 8 entries 528KB, 번들 gzip 173KB - vite dev :3001 LISTENING + ga-api :8082 정상 - /api/statements total=112, /api/withdraw-requests total=3, /api/notices/active 0건, /api/auth/me 정상 잔여 (스캐폴드 이후): - 백엔드 /api/mobile/me/* 전용 endpoint (서버측 본인 필터 강제) - 출금 신청 작성 폼, 명세서 상세, 본인 알림(user_notification) - 토큰 만료 자동 refresh, 푸시 알림 (FCM) - 실 아이콘 PNG, manualChunks 분리 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import { Card, List, NavBar, Space, Tag } from 'antd-mobile';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAuthStore } from '@/stores/authStore';
|
||||
import { statementApi } from '@/api/statement';
|
||||
import { withdrawApi } from '@/api/withdraw';
|
||||
import { noticeApi } from '@/api/notice';
|
||||
|
||||
export default function Home() {
|
||||
const navigate = useNavigate();
|
||||
const profile = useAuthStore((s) => s.profile);
|
||||
const clear = useAuthStore((s) => s.clear);
|
||||
const agentId = profile?.agentId;
|
||||
|
||||
const statements = useQuery({
|
||||
queryKey: ['home.statements', agentId],
|
||||
enabled: !!agentId,
|
||||
queryFn: () => statementApi.list({ agentId, pageNum: 1, pageSize: 1 }),
|
||||
});
|
||||
const withdraws = useQuery({
|
||||
queryKey: ['home.withdraws', agentId],
|
||||
enabled: !!agentId,
|
||||
queryFn: () => withdrawApi.list({ agentId, pageNum: 1, pageSize: 1 }),
|
||||
});
|
||||
const notices = useQuery({
|
||||
queryKey: ['home.notices'],
|
||||
queryFn: () => noticeApi.active(),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<NavBar back={null} right={<a onClick={() => { clear(); navigate('/login'); }} style={{ fontSize: 14 }}>로그아웃</a>}>
|
||||
홈
|
||||
</NavBar>
|
||||
<div style={{ padding: 16 }}>
|
||||
<Card>
|
||||
<div style={{ fontSize: 18, fontWeight: 700 }}>
|
||||
{profile?.userName ?? profile?.loginId ?? '사용자'} 님
|
||||
</div>
|
||||
<div style={{ color: '#888', fontSize: 13, marginTop: 4 }}>
|
||||
{profile?.agentName ? `${profile.agentName} · ${profile.orgName ?? ''}` : '설계사 정보 없음'}
|
||||
</div>
|
||||
{profile?.roleCodes && (
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Space wrap>
|
||||
{profile.roleCodes.map((r) => <Tag key={r} color="primary">{r}</Tag>)}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<List header="요약">
|
||||
<List.Item
|
||||
extra={statements.isLoading ? '...' : `총 ${statements.data?.total ?? 0}건`}
|
||||
onClick={() => navigate('/statement')}
|
||||
clickable
|
||||
>
|
||||
정산 명세서
|
||||
</List.Item>
|
||||
<List.Item
|
||||
extra={withdraws.isLoading ? '...' : `총 ${withdraws.data?.total ?? 0}건`}
|
||||
onClick={() => navigate('/withdraw')}
|
||||
clickable
|
||||
>
|
||||
출금 신청
|
||||
</List.Item>
|
||||
<List.Item
|
||||
extra={notices.isLoading ? '...' : `${notices.data?.length ?? 0}건`}
|
||||
onClick={() => navigate('/notice')}
|
||||
clickable
|
||||
>
|
||||
공지사항
|
||||
</List.Item>
|
||||
</List>
|
||||
</div>
|
||||
|
||||
{!agentId && (
|
||||
<div style={{ marginTop: 24, fontSize: 12, color: '#f96', textAlign: 'center' }}>
|
||||
본 계정에 연결된 설계사(agent)가 없습니다. 일부 기능이 제한될 수 있습니다.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user