// Types mirror the Go models (camelCase JSON). Kept in one file for clarity. export interface User { id: string; name: string; email: string; role: string; groups?: string[]; isSuperAdmin: boolean; } export type Rank = "인턴" | "주임" | "선임" | "책임" | "파트너"; export interface Member { id: string; email: string; displayName: string; rank: Rank | ""; departmentId?: string | null; role: "admin" | "user"; isPartner: boolean; phone: string; position: string; status: string; joinDate?: string | null; annualLeave: number; avatarKey: string; createdAt: string; updatedAt: string; } export interface Notification { id: string; recipient: string; type: "project" | "leave" | "overtime" | "incentive" | "settlement"; title: string; body: string; link: string; read: boolean; createdAt: string; } export type WorkStatusKind = "in" | "out" | "break" | "meeting" | "move"; export interface WorkStatusEvent { id: string; memberEmail: string; date: string; status: WorkStatusKind; at: string; note: string; } export interface LeaveBalance { year: number; granted: number; used: number; remaining: number; } export interface Me { user: User; member: Member | null; isAdmin: boolean; logoutUrl: string; } export interface NavItem { key: string; label: string; path: string; icon: string; adminOnly: boolean; section: string; } export interface Department { id: string; name: string; leadEmail: string; } export interface AuditLog { id: string; actor: string; action: string; entity: string; entityId: string; detail: string; createdAt: string; } /* ---- attendance ---- */ export type ReqStatus = "pending" | "approved" | "rejected" | "canceled"; export interface Attendance { id: string; memberEmail: string; date: string; clockIn?: string | null; clockOut?: string | null; workMinutes: number; source: string; note: string; } export type LeaveType = | "annual" | "half_am" | "half_pm" | "public" | "sick" | "family" | "unpaid"; export interface LeaveRequest { id: string; memberEmail: string; type: LeaveType; startDate: string; endDate: string; days: number; reason: string; status: ReqStatus; approver: string; decidedAt?: string | null; decisionMemo: string; createdAt: string; } export interface OvertimeRequest { id: string; memberEmail: string; date: string; minutes: number; reason: string; status: ReqStatus; approver: string; decidedAt?: string | null; decisionMemo: string; createdAt: string; } export interface WorkPolicy { id: string; name: string; weeklyHours: number; dailyStandardMin: number; coreStart: string; coreEnd: string; lunchMinutes: number; annualLeaveBase: number; active: boolean; } export interface Timesheet { year: number; month: number; businessDays: number; standardMinutes: number; workedMinutes: number; leaveMinutes: number; overtimeMinutes: number; recognizedTotal: number; fulfillmentPct: number; daysPresent: number; } export interface ApprovalQueue { leave: LeaveRequest[]; overtime: OvertimeRequest[]; } /* ---- projects ---- */ export interface Company { id: string; name: string; code: string; note: string; } export interface Product { id: string; companyId: string; name: string; code: string; } export interface Version { id: string; productId: string; label: string; } export type ProjectStatus = "planned" | "active" | "hold" | "done" | "dropped"; export interface Project { id: string; name: string; companyId: string; productId: string; versionId: string; companyName: string; productName: string; versionName: string; consultingType: string; country: string; scopeText: string; // 글 계약 범위 scopeGraphic: string; // 그림 계약 범위 pmEmail: string; clientDomain: string; // 고객사 메일 도메인(postfix) cautions: string; status: ProjectStatus; startDate: string; dueDate: string; createdAt: string; updatedAt: string; } export interface ProjectMail { id: string; messageId: string; threadId: string; from: string; to: string; cc: string; subject: string; date: string; snippet: string; mailbox: string; ts: number; hidden: boolean; hiddenBy: string; note: string; // 공동 메모 본문 (인라인 표시) noteEditedBy: string; // 메모 마지막 수정자 } export interface ProjectMailsResponse { enabled: boolean; domain: string; messages: ProjectMail[]; error?: string; lastSyncedAt?: string; syncing?: boolean; } export interface MailNote { id: string; projectId: string; messageId: string; body: string; lastEditedBy: string; createdAt: string; updatedAt: string; } export interface ProjectMember { id: string; projectId: string; memberEmail: string; portion: number; role: string; } export interface ClientContact { id: string; projectId: string; name: string; title: string; phone: string; email: string; } export type Lane = "todo" | "doing" | "review" | "done"; // 전 구성원 공개 최소 디렉터리 (이메일 → 이름 표시용) export interface DirectoryEntry { id: string; email: string; displayName: string; avatarKey: string; } export type TaskPriority = "low" | "medium" | "high" | "urgent"; export interface ProjectTask { id: string; projectId: string; title: string; description: string; lane: Lane; priority: TaskPriority | ""; labels: string[] | null; start: string; end: string; assignee: string; orderIdx: number; progress: number; dependsOn: string[] | null; color: string; } export interface TaskComment { id: string; taskId: string; authorEmail: string; body: string; createdAt: string; } export interface Contract { id: string; projectId: string; totalAmount: number; beAmount: number; adminCaution: string; memo: string; } export interface ContractFile { id: string; projectId: string; kind: string; filename: string; s3Key: string; size: number; uploadedBy: string; createdAt: string; } export interface PaymentSplit { id: string; projectId: string; label: string; amount: number; expectedDate: string; paidDate: string; paid: boolean; memo: string; orderIdx: number; } /* ---- incentive ---- */ export type StageKind = "deposit" | "middle" | "final"; export type StageScope = "be" | "non_be"; export type FixStatus = "planned" | "applying" | "applied" | "paid"; export interface IncentiveConfig { id: string; year: number; pointRate: number; depositPct: number; middlePct: number; finalPct: number; nonBeCompanyPct: number; nonBePartnerPct: number; rankQuota: Record; frozen: boolean; } export interface PaymentStage { id: string; projectId: string; kind: StageKind; scope: StageScope; amount: number; pct: number; expectedDate: string; fixedDate: string; status: FixStatus; } export interface UserIncentive { id: string; projectId: string; memberEmail: string; stageId: string; kind: StageKind; scope: StageScope; year: number; quarter: number; portion: number; amount: number; points: number; fixStatus: FixStatus; override: boolean; memo: string; appliedAt?: string | null; paidAt?: string | null; } export interface MyIncentive { year: number; rank: string; quota: number; pointsTotal: number; pointsApplied: number; excessPoints: number; pointRate: number; estPayout: number; items: UserIncentive[]; byProject: Record; } export interface Settlement { id: string; memberEmail: string; year: number; quarter: number; rank: string; quota: number; pointsCumul: number; excessPoints: number; paidPointsYtd: number; payoutPoints: number; payoutAmount: number; fixed: boolean; fixedAt?: string | null; } export interface SimResult { stages: { kind: StageKind; scope: StageScope; amount: number; pct: number }[]; allocs: { email: string; kind: StageKind; scope: StageScope; portion: number; amount: number; points: number }[]; byMember: Record; } /* ---- accounting ---- */ export type TxnKind = "income" | "expense" | "tax" | "payroll" | "incentive"; export interface Account { id: string; code: string; name: string; type: string; } export interface Transaction { id: string; date: string; kind: TxnKind; accountId?: string | null; amount: number; projectId?: string | null; memberEmail?: string | null; counterparty: string; memo: string; createdBy: string; } export interface TaxRecord { id: string; period: string; type: string; base: number; amount: number; dueDate: string; paid: boolean; memo: string; } export interface AcctSummary { year: number; cashIn: number; cashOut: number; net: number; incentiveApplied: number; incentivePaid: number; gap: number; monthly: { month: string; income: number; expense: number; net: number }[]; byKind: Record; } export interface Dashboard { isAdmin: boolean; year: number; myProjects: number; myPoints: number; myPendingRequests: number; pendingApprovals?: number; activeProjects?: number; cashIn?: number; cashOut?: number; cashNet?: number; upcomingPayments?: PaymentSplit[]; }