spin-frontend/src/types.ts
theorose49 05eb82c635
All checks were successful
build-and-push / build (push) Successful in 32s
feat(mail): 메일 리스트에 메모 인라인 표시 + 숨김 토글 + 동기화 버튼
- 각 메일 행에 공동 메모 미리보기(📝), 펼치면 편집
- 메일 숨기기/다시보기, '숨긴 메일 보기(N)' 토글
- 마지막 동기화 시각 + 수동 '동기화' 버튼, 참조(CC) 표시

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 12:44:58 +09:00

457 lines
9.2 KiB
TypeScript

// 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<string, number>;
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<string, number>;
}
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<string, number>;
}
/* ---- 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<string, number>;
}
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[];
}