All checks were successful
build-and-push / build (push) Successful in 32s
- 각 메일 행에 공동 메모 미리보기(📝), 펼치면 편집
- 메일 숨기기/다시보기, '숨긴 메일 보기(N)' 토글
- 마지막 동기화 시각 + 수동 '동기화' 버튼, 참조(CC) 표시
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
457 lines
9.2 KiB
TypeScript
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[];
|
|
}
|