From 88041bb2827981b9a3b2a85a624195167146e2e4 Mon Sep 17 00:00:00 2001 From: MYONG JAEWI <78201530+Jaymyong66@users.noreply.github.com> Date: Sun, 10 May 2026 19:54:06 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Revert=20"fix:=20Portal=20=EB=A7=88?= =?UTF-8?q?=EC=8A=A4=ED=84=B0=EA=B3=84=EC=95=BD=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20Dashboard=20=EB=93=9C=EB=A1=AD=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=EA=B3=BC=20=EB=8F=99=EC=9D=BC=ED=95=9C=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=ED=86=B5=EC=9D=BC"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 770087b8bb9586bfd4b3296bde37f0dacfaf8032. --- .../layout/MasterAgreementDropdown.tsx | 2 +- frontend/src/hooks/useMyPolicies.ts | 3 - frontend/src/pages/PortalPage.tsx | 72 ++++++++++++------- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/frontend/src/components/layout/MasterAgreementDropdown.tsx b/frontend/src/components/layout/MasterAgreementDropdown.tsx index f5e066cd..f800d5cd 100644 --- a/frontend/src/components/layout/MasterAgreementDropdown.tsx +++ b/frontend/src/components/layout/MasterAgreementDropdown.tsx @@ -86,7 +86,7 @@ export function MasterAgreementDropdown() { )} {policies.map(p => ( ))} {masterAgreementPDA && !policies.some(p => p.pda === masterAgreementPDA) && ( diff --git a/frontend/src/hooks/useMyPolicies.ts b/frontend/src/hooks/useMyPolicies.ts index cfee1e15..4a4dc293 100644 --- a/frontend/src/hooks/useMyPolicies.ts +++ b/frontend/src/hooks/useMyPolicies.ts @@ -17,7 +17,6 @@ export interface MyPolicySummary { statusLabel: string; roles: MyPolicyRole[]; track: 'A' | 'B'; - coverageEndTs?: number; /** Track B only fields */ flightNo?: string; route?: string; @@ -34,7 +33,6 @@ interface BackendMasterAgreementFull { reinsurer: string; reinsurer_confirmed: boolean; reinsurer_effective_bps: number; - coverage_end_ts: number; participants: Array<{ insurer: string; share_bps: number; @@ -110,7 +108,6 @@ export function useMyPolicies() { statusLabel: mp.status_label, roles, track: 'A', - coverageEndTs: mp.coverage_end_ts, }); } } diff --git a/frontend/src/pages/PortalPage.tsx b/frontend/src/pages/PortalPage.tsx index 14901237..4b58d8a6 100644 --- a/frontend/src/pages/PortalPage.tsx +++ b/frontend/src/pages/PortalPage.tsx @@ -6,11 +6,11 @@ import { useWallet } from '@solana/wallet-adapter-react'; import { useTranslation } from 'react-i18next'; import { PageShell } from '@/components/layout/PageShell'; import { PortalHeader } from '@/components/layout/PortalHeader'; - +import { Tag, Mono } from '@/components/common'; import { useParticipantRole } from '@/hooks/useParticipantRole'; import { useMyPolicies, type MyPolicySummary } from '@/hooks/useMyPolicies'; import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'; -import { MasterAgreementStatus, POLICY_STATE_LABELS } from '@/lib/idl/open_parametric'; +import { MasterAgreementStatus, POLICY_STATE_LABELS, PolicyState } from '@/lib/idl/open_parametric'; import { LeaderPortal } from './portal/LeaderPortal'; import { ParticipantPortal } from './portal/ParticipantPortal'; import { ReinPortal } from './portal/ReinPortal'; @@ -71,6 +71,13 @@ const PolicyCard = styled.div` } `; +const ROLE_COLORS: Record = { + leader: '#9945FF', + partA: '#22C55E', + partB: '#F59E0B', + rein: '#38BDF8', +}; + const STATUS_LABELS: Record = { [MasterAgreementStatus.Draft]: 'Draft', [MasterAgreementStatus.PendingConfirm]: 'Pending', @@ -79,40 +86,55 @@ const STATUS_LABELS: Record = { [MasterAgreementStatus.Cancelled]: 'Cancelled', }; - -const PORTAL_ROLE_LABEL: Record = { - leader: '리더사', - partA: '참여사', - partB: '참여사', - rein: '재보험사', +const STATUS_COLORS: Record = { + [MasterAgreementStatus.Draft]: '#94A3B8', + [MasterAgreementStatus.PendingConfirm]: '#F59E0B', + [MasterAgreementStatus.Active]: '#22C55E', + [MasterAgreementStatus.Closed]: '#64748B', + [MasterAgreementStatus.Cancelled]: '#EF4444', }; -function formatDate(ts: number): string { - return new Date(ts * 1000).toLocaleDateString('ko-KR', { - year: '2-digit', month: '2-digit', day: '2-digit', - }); -} +const TRACK_B_STATUS_COLORS: Record = { + [PolicyState.Draft]: '#94A3B8', + [PolicyState.Open]: '#38BDF8', + [PolicyState.Funded]: '#F59E0B', + [PolicyState.Active]: '#22C55E', + [PolicyState.Claimable]: '#EF4444', + [PolicyState.Approved]: '#9945FF', + [PolicyState.Settled]: '#64748B', + [PolicyState.Expired]: '#475569', +}; function PolicyListItem({ policy, onClick }: { policy: MyPolicySummary; onClick: () => void }) { + const { t } = useTranslation(); const isTrackB = policy.track === 'B'; + const statusColor = isTrackB + ? (TRACK_B_STATUS_COLORS[policy.status] || '#94A3B8') + : (STATUS_COLORS[policy.status] || '#94A3B8'); const statusLabel = isTrackB ? (POLICY_STATE_LABELS[policy.status] || 'Unknown') : (STATUS_LABELS[policy.status] || 'Unknown'); - const primaryRole = policy.roles[0]; - const roleLabel = primaryRole ? (PORTAL_ROLE_LABEL[primaryRole.role] || primaryRole.role) : ''; - const dateStr = policy.coverageEndTs ? formatDate(policy.coverageEndTs) : ''; - const label = [ - `${policy.pda.slice(0, 8)}...`, - statusLabel, - roleLabel, - dateStr, - ].filter(Boolean).join(' · '); return ( - - {label} - +
+ {policy.roles.map(r => ( + + {t(`portal.role.${r.role}`, r.role)} + + ))} +
+ + {isTrackB ? `Policy #${policy.masterId}` : `Master #${policy.masterId}`} + + + {isTrackB && policy.flightNo + ? `${policy.flightNo} · ${policy.route}` + : `${policy.pda.slice(0, 12)}...${policy.pda.slice(-8)}`} + +
+
+ {statusLabel}
); } From 87ec6c8ead7b79906c58fec7785bc47f573b4979 Mon Sep 17 00:00:00 2001 From: MYONG JAEWI <78201530+Jaymyong66@users.noreply.github.com> Date: Sun, 10 May 2026 19:57:31 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Revert=20"fix:=20=EB=8C=80=EC=8B=9C?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=EB=A7=88=EC=8A=A4=ED=84=B0=EA=B3=84?= =?UTF-8?q?=EC=95=BD=20=EB=93=9C=EB=A1=AD=EB=8B=A4=EC=9A=B4=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=EB=AA=85=20PDA=20=E2=86=92=20Master=20#N=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=ED=86=B5=EC=9D=BC"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6aa939a084f1feb4442a3d34dcc43084308aed6e. --- frontend/src/components/layout/MasterAgreementDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/layout/MasterAgreementDropdown.tsx b/frontend/src/components/layout/MasterAgreementDropdown.tsx index f800d5cd..f5e066cd 100644 --- a/frontend/src/components/layout/MasterAgreementDropdown.tsx +++ b/frontend/src/components/layout/MasterAgreementDropdown.tsx @@ -86,7 +86,7 @@ export function MasterAgreementDropdown() { )} {policies.map(p => ( ))} {masterAgreementPDA && !policies.some(p => p.pda === masterAgreementPDA) && ( From eb35f61cab40f5c3811af39223ca62b634fc5c8b Mon Sep 17 00:00:00 2001 From: MYONG JAEWI <78201530+Jaymyong66@users.noreply.github.com> Date: Sun, 10 May 2026 21:43:05 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20Portal=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EB=A7=88=EC=8A=A4=ED=84=B0=EA=B3=84=EC=95=BD=20select=EB=A5=BC?= =?UTF-8?q?=20Dashboard=20=EB=93=9C=EB=A1=AD=EB=8B=A4=EC=9A=B4=EA=B3=BC=20?= =?UTF-8?q?=EB=8F=99=EC=9D=BC=20=ED=8F=AC=EB=A7=B7=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존 Master #N 단일 표시에서 PDA · status · role · date 4단 표시로 변경 useMyPolicies에 coverageEndTs 필드 추가하여 만기일 노출 Co-Authored-By: Claude Sonnet 4.6 --- .../src/components/layout/PortalHeader.tsx | 55 +++++++++++++++---- frontend/src/hooks/useMyPolicies.ts | 4 ++ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/layout/PortalHeader.tsx b/frontend/src/components/layout/PortalHeader.tsx index 8f2dd918..748bdc9d 100644 --- a/frontend/src/components/layout/PortalHeader.tsx +++ b/frontend/src/components/layout/PortalHeader.tsx @@ -5,8 +5,9 @@ import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'; import { BaseHeader } from './BaseHeader'; import { Mono } from '@/components/common'; import { useThemeModeContext } from '@/context/ThemeModeContext'; -import { useMyPolicies } from '@/hooks/useMyPolicies'; +import { useMyPolicies, type MyPolicySummary } from '@/hooks/useMyPolicies'; import type { ParticipantInfo } from '@/hooks/useParticipantRole'; +import { MasterAgreementStatus } from '@/lib/idl/open_parametric'; const Controls = styled.div` display: flex; @@ -18,18 +19,18 @@ const PolicySelect = styled.select` background: ${p => p.theme.colors.card}; border: 1px solid ${p => p.theme.colors.border}; color: ${p => p.theme.colors.text}; - font-family: ${p => p.theme.fonts.sans}; - font-size: 11px; + font-family: ${p => p.theme.fonts.mono}; + font-size: 10px; font-weight: 600; padding: 5px 24px 5px 9px; - border-radius: ${p => p.theme.radii.sm}; + border-radius: 7px; outline: none; cursor: pointer; appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2394A3B8' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 8px center; - max-width: 160px; + max-width: 220px; `; const ThemeToggle = styled.button` @@ -143,6 +144,29 @@ const ROLE_COLORS: Record = { rein: '#38BDF8', }; +const POLICY_ROLE_LABEL: Record = { + leader: '리더사', + partA: '참여사', + partB: '참여사', + rein: '재보험사', +}; + +function policyStatusLabel(p: MyPolicySummary): string { + if (p.track === 'B') return p.statusLabel || 'Unknown'; + if (p.status === MasterAgreementStatus.Active) return 'Active'; + if (p.status === MasterAgreementStatus.PendingConfirm) return 'Pending'; + if (p.status === MasterAgreementStatus.Closed) return 'Closed'; + if (p.status === MasterAgreementStatus.Cancelled) return 'Cancelled'; + return 'Draft'; +} + +function formatPolicyDate(ts?: number): string { + if (!ts) return ''; + return new Date(ts * 1000).toLocaleDateString('ko-KR', { + year: '2-digit', month: '2-digit', day: '2-digit', + }); +} + interface PortalHeaderProps { role: 'leader' | 'participant' | 'rein' | null; masterPDA: string | null; @@ -178,11 +202,22 @@ export function PortalHeader({ role, masterPDA, roles, hideBottomBar = false, pa {policies.length > 0 && ( - {policies.map(p => ( - - ))} + {policies.map(p => { + const role = p.roles[0]?.role; + const roleLabel = role ? (POLICY_ROLE_LABEL[role] || '') : ''; + const dateStr = formatPolicyDate(p.coverageEndTs); + const parts = [ + `${p.pda.slice(0, 8)}...`, + policyStatusLabel(p), + roleLabel, + dateStr, + ].filter(Boolean); + return ( + + ); + })} )} diff --git a/frontend/src/hooks/useMyPolicies.ts b/frontend/src/hooks/useMyPolicies.ts index 4a4dc293..558251fd 100644 --- a/frontend/src/hooks/useMyPolicies.ts +++ b/frontend/src/hooks/useMyPolicies.ts @@ -17,6 +17,8 @@ export interface MyPolicySummary { statusLabel: string; roles: MyPolicyRole[]; track: 'A' | 'B'; + /** Coverage end timestamp (Track A only) */ + coverageEndTs?: number; /** Track B only fields */ flightNo?: string; route?: string; @@ -33,6 +35,7 @@ interface BackendMasterAgreementFull { reinsurer: string; reinsurer_confirmed: boolean; reinsurer_effective_bps: number; + coverage_end_ts: number; participants: Array<{ insurer: string; share_bps: number; @@ -108,6 +111,7 @@ export function useMyPolicies() { statusLabel: mp.status_label, roles, track: 'A', + coverageEndTs: mp.coverage_end_ts, }); } }