Changes
10 changed files (+324/-213)
-
-
@@ -21,9 +21,14 @@export const DayOff: Story = { args: { workRecord: proto.create(WorkRecordSchema, { record: { case: "dayOff", value: {}, kind: { case: "dayWhole", value: { kind: { case: "dayOff", value: {}, }, }, }, }), },
-
@@ -32,27 +37,17 @@export const WorkingDay: Story = { args: { workRecord: proto.create(WorkRecordSchema, { record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: true, timeOffs: [ { kind: { case: "halvedPaidLeave", value: {}, kind: { case: "worked", value: { hourlyPaidLeave: { hours: 3, }, }, { kind: { case: "hourlyPaidLeave", value: { hours: 3, }, }, }, {}, ], }, }, }, }),
-
@@ -62,10 +57,13 @@export const SkippedWork: Story = { args: { workRecord: proto.create(WorkRecordSchema, { record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: false, kind: { case: "skipped", value: {}, }, }, }, }),
-
@@ -75,9 +73,14 @@export const PaidLeave: Story = { args: { workRecord: proto.create(WorkRecordSchema, { record: { case: "paidLeave", value: {}, kind: { case: "dayWhole", value: { kind: { case: "paidLeave", value: {}, }, }, }, }), },
-
@@ -86,10 +89,15 @@export const WorkspaceDefinedLeave: Story = { args: { workRecord: proto.create(WorkRecordSchema, { record: { case: "workspaceDefinedLeave", kind: { case: "dayWhole", value: { displayName: "リフレッシュ休暇", kind: { case: "workspaceDefinedLeave", value: { displayName: "リフレッシュ休暇", }, }, }, }, }),
-
@@ -99,13 +107,18 @@export const LeaveDeemedToBeWorked: Story = { args: { workRecord: proto.create(WorkRecordSchema, { record: { case: "workspaceDefinedLeave", kind: { case: "dayWhole", value: { displayName: "産前産後休業", currentRevision: { snapshot: { isWorkerDeemedToBeWorked: true, kind: { case: "workspaceDefinedLeave", value: { displayName: "産前産後休業", currentRevision: { snapshot: { isWorkerDeemedToBeWorked: true, }, }, }, }, },
-
-
-
@@ -4,6 +4,7 @@import * as proto from "@bufbuild/protobuf"; import { Badge, type BadgeProps, Text } from "@radix-ui/themes"; import { WorkRecordSchema } from "@yamori/proto/yamori/work_record/v1/work_record_pb.js"; import { RecordKindSchema } from "@yamori/proto/yamori/work_record/v1/record_kind_pb.js"; import { type AbbreviationsSchema } from "@yamori/proto/yamori/workspace/v1/abbreviations_pb.js"; import { type FC } from "react";
-
@@ -15,67 +16,36 @@ </Badge>); }; export interface WorkRecordBadgesProps extends Pick< BadgeProps, "size" | "m" | `m${"t" | "b" | "r" | "l" | "x" | "y"}` | "style" | "className" > { interface RecordKindBadgeProps { abbreviations?: proto.MessageShape<typeof AbbreviationsSchema>; workRecord: proto.MessageShape<typeof WorkRecordSchema>; record: proto.MessageShape<typeof RecordKindSchema>; } export const WorkRecordBadges: FC<WorkRecordBadgesProps> = ({ const RecordKindBadge: FC<RecordKindBadgeProps & BadgeProps> = ({ abbreviations, workRecord, record, ...rest }) => { switch (workRecord.record.case) { case "workingDay": { const { hasWorkerWorked, timeOffs } = workRecord.record.value; switch (record.kind.case) { case "worked": return ( <> {hasWorkerWorked ? ( <ResponsiveBadge {...rest} color="blue"> {abbreviations?.worked || "出勤"} </ResponsiveBadge> ) : ( <ResponsiveBadge {...rest} color="amber"> {abbreviations?.skipWork || "欠勤"} </ResponsiveBadge> )} {timeOffs.map((timeOff, i) => { switch (timeOff.kind.case) { case "halvedPaidLeave": return ( <ResponsiveBadge key={i} {...rest} color="green"> 半休 </ResponsiveBadge> ); case "hourlyPaidLeave": return ( <ResponsiveBadge key={i} {...rest} color="green"> 時間単位年休 </ResponsiveBadge> ); default: return ( <ResponsiveBadge key={i} {...rest} color="gray"> 不明なレコード </ResponsiveBadge> ); } })} </> <ResponsiveBadge {...rest} color="blue"> {abbreviations?.worked || "出勤"} </ResponsiveBadge> ); } case "dayOff": return ( <ResponsiveBadge {...rest} color="red"> {abbreviations?.dayoff || "休日"} </ResponsiveBadge> ); case "skipped": return ( <ResponsiveBadge {...rest} color="amber"> {abbreviations?.skipWork || "欠勤"} </ResponsiveBadge> ); case "paidLeave": return ( <ResponsiveBadge {...rest} color="green">
-
@@ -87,21 +57,63 @@ return (<ResponsiveBadge {...rest} color={ workRecord.record.value.currentRevision?.snapshot?.isWorkerDeemedToBeWorked record.kind.value.currentRevision?.snapshot?.isWorkerDeemedToBeWorked ? "green" : "amber" } > {workRecord.record.value.abbreviationName || workRecord.record.value.displayName || "休暇"} {record.kind.value.abbreviationName || record.kind.value.displayName || "休暇"} </ResponsiveBadge> ); default: return null; } }; export interface WorkRecordBadgesProps extends Pick< BadgeProps, "size" | "m" | `m${"t" | "b" | "r" | "l" | "x" | "y"}` | "style" | "className" > { abbreviations?: proto.MessageShape<typeof AbbreviationsSchema>; workRecord: proto.MessageShape<typeof WorkRecordSchema>; } export const WorkRecordBadges: FC<WorkRecordBadgesProps> = ({ abbreviations, workRecord, ...rest }) => { switch (workRecord.kind.case) { case "dayWhole": return ( <ResponsiveBadge {...rest} color="gray"> 不明なデータ </ResponsiveBadge> <RecordKindBadge {...rest} abbreviations={abbreviations} record={workRecord.kind.value} /> ); case "dayHalved": return ( <> {workRecord.kind.value.am && ( <RecordKindBadge {...rest} abbreviations={abbreviations} record={workRecord.kind.value.am} /> )} {workRecord.kind.value.pm && ( <RecordKindBadge {...rest} abbreviations={abbreviations} record={workRecord.kind.value.pm} /> )} </> ); default: return null; } };
-
-
-
@@ -39,9 +39,14 @@ providePaidLeaveKey: { key: new Uint8Array([]) },workRecords: [ { date: toProtoDate(subDays(Date.now(), 1)), record: { case: "paidLeave", value: {}, kind: { case: "dayWhole", value: { kind: { case: "paidLeave", value: {}, }, }, }, }, ],
-
@@ -55,52 +60,62 @@ writeWorkRecordKey: { key: new Uint8Array([]) },workRecords: [ { date: toProtoDate(subDays(Date.now(), 2)), record: { case: "workingDay", kind: { case: "dayHalved", value: { hasWorkerWorked: true, timeOffs: [ { kind: { case: "halvedPaidLeave", value: {}, }, }, { kind: { case: "hourlyPaidLeave", value: { am: { kind: { case: "worked", value: { hourlyPaidLeave: { hours: 3, }, }, }, ], }, pm: { kind: { case: "paidLeave", value: {}, }, }, }, }, }, { date: toProtoDate(subDays(Date.now(), 3)), record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: true, kind: { case: "worked", value: {}, }, }, }, }, { date: toProtoDate(subDays(Date.now(), 4)), record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: false, kind: { case: "skipped", value: {}, }, }, }, }, { date: toProtoDate(subDays(Date.now(), 5)), record: { case: "dayOff", value: {}, kind: { case: "dayWhole", value: { kind: { case: "dayOff", value: {}, }, }, }, }, ],
-
@@ -115,10 +130,13 @@ writeWorkRecordKey: { key: new Uint8Array([]) },workRecords: [ { date: toProtoDate(subDays(Date.now(), 2)), record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: true, kind: { case: "worked", value: {}, }, }, }, },
-
@@ -264,34 +282,49 @@ ],workRecords: [ { date: toProtoDate(subDays(Date.now(), 7)), record: { case: "dayOff", value: {}, kind: { case: "dayWhole", value: { kind: { case: "dayOff", value: {}, }, }, }, note: "やる気がないので休日", }, { date: toProtoDate(subDays(Date.now(), 6)), record: { case: "paidLeave", kind: { case: "dayWhole", value: { providedAt: { year: 2024, month: 3, day: 1, kind: { case: "paidLeave", value: { providedAt: { year: 2024, month: 3, day: 1, }, }, }, }, }, }, { date: toProtoDate(subDays(Date.now(), 5)), record: { case: "workspaceDefinedLeave", kind: { case: "dayWhole", value: { displayName: "産前産後休業", currentRevision: { snapshot: { isWorkerDeemedToBeWorked: true, kind: { case: "workspaceDefinedLeave", value: { displayName: "産前産後休業", currentRevision: { snapshot: { isWorkerDeemedToBeWorked: true, }, }, }, }, },
-
@@ -299,55 +332,65 @@ },}, { date: toProtoDate(subDays(Date.now(), 4)), record: { case: "workspaceDefinedLeave", kind: { case: "dayWhole", value: { displayName: "冠婚葬祭", kind: { case: "workspaceDefinedLeave", value: { displayName: "冠婚葬祭", }, }, }, }, }, { date: toProtoDate(subDays(Date.now(), 3)), record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: false, kind: { case: "skipped", value: {}, }, }, }, note: "寝坊", }, { date: toProtoDate(subDays(Date.now(), 2)), record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: true, kind: { case: "worked", value: {}, }, }, }, }, { date: toProtoDate(subDays(Date.now(), 1)), record: { case: "workingDay", kind: { case: "dayHalved", value: { hasWorkerWorked: true, timeOffs: [ { kind: { case: "halvedPaidLeave", value: { providedAt: { year: 2024, month: 3, day: 1, }, am: { kind: { case: "paidLeave", value: { providedAt: { year: 2024, month: 3, day: 1, }, }, }, { kind: { case: "hourlyPaidLeave", value: { }, pm: { kind: { case: "worked", value: { hourlyPaidLeave: { providedAt: { year: 2024, month: 3,
-
@@ -357,7 +400,7 @@ hours: 2,}, }, }, ], }, }, }, },
-
-
-
@@ -8,6 +8,7 @@import { withMockedBackend } from "../../../../.storybook/decorators/withMockedBackend.tsx"; import { withInmemoryRouter } from "../../../../.storybook/decorators/withInmemoryRouter.tsx"; import { List } from "../../../mocks/yamori/worker/v1/worker_service.ts"; import * as workspaceService from "../../../mocks/yamori/workspace/v1/workspace_service.ts"; import { Page } from "./page.tsx";
-
@@ -25,7 +26,7 @@ workspace,}, decorators: [ withInmemoryRouter({ initialURL: "/ws-foo/calendar" }), withMockedBackend([List()]), withMockedBackend([workspaceService.Get(), List()]), ], } satisfies Meta<typeof Page>;
-
-
-
@@ -26,7 +26,7 @@ import { ListRequestSchema } from "@yamori/proto/yamori/worker/v1/list_request_pb.js";import { ListResponseSchema } from "@yamori/proto/yamori/worker/v1/list_response_pb.js"; import { WorkerSchema } from "@yamori/proto/yamori/worker/v1/worker_pb.js"; import { PaidLeaveProvisionSchema } from "@yamori/proto/yamori/worker/v1/paid_leave_provision_pb.js"; import { LeaveSchema } from "@yamori/proto/yamori/work_record/v1/leave_pb.js"; import { WorkRecordSchema } from "@yamori/proto/yamori/work_record/v1/work_record_pb.js"; import { addMonths, subMonths,
-
@@ -170,14 +170,11 @@ PaidLeaveProvisionSchema.field.isHalvedDayRemaining.number,], }, workRecordsMask: { workspaceDefinedLeaveMask: { fields: [ LeaveSchema.field.id.number, LeaveSchema.field.displayName.number, LeaveSchema.field.currentRevision.number, LeaveSchema.field.abbreviationName.number, ], }, fields: [ WorkRecordSchema.field.date.number, WorkRecordSchema.field.dayWhole.number, WorkRecordSchema.field.dayHalved.number, ], }, }, },
-
-
-
@@ -178,10 +178,13 @@ workerId: worker.id,writeWorkRecordKey: worker.writeWorkRecordKey, workRecord: { dates, record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: true, kind: { case: "worked", value: {}, }, }, }, },
-
@@ -196,10 +199,13 @@ workerId: worker.id,writeWorkRecordKey: worker.writeWorkRecordKey, workRecord: { dates, record: { case: "workingDay", kind: { case: "dayWhole", value: { hasWorkerWorked: false, kind: { case: "skipped", value: {}, }, }, }, },
-
@@ -214,9 +220,14 @@ workerId: worker.id,writeWorkRecordKey: worker.writeWorkRecordKey, workRecord: { dates, record: { case: "paidLeave", value: {}, kind: { case: "dayWhole", value: { kind: { case: "paidLeave", value: {}, }, }, }, }, });
-
@@ -246,9 +257,14 @@ workerId: worker.id,writeWorkRecordKey: worker.writeWorkRecordKey, workRecord: { dates, record: { case: "workspaceDefinedLeaveId", value: leave.id!, kind: { case: "dayWhole", value: { kind: { case: "workspaceDefinedLeaveId", value: leave.id!, }, }, }, }, });
-
-
-
@@ -18,6 +18,8 @@ Tooltip,} from "@radix-ui/themes"; import { DateSchema } from "@yamori/proto/yamori/type/v1/date_pb.js"; import { LeaveSchema } from "@yamori/proto/yamori/work_record/v1/leave_pb.js"; import { WorkRecordSchema } from "@yamori/proto/yamori/work_record/v1/work_record_pb.js"; import { RecordKindSchema } from "@yamori/proto/yamori/work_record/v1/record_kind_pb.js"; import { WorkspaceSchema } from "@yamori/proto/yamori/workspace/v1/workspace_pb.js"; import { GetRequestSchema } from "@yamori/proto/yamori/workspace/v1/get_request_pb.js"; import { GetResponseSchema } from "@yamori/proto/yamori/workspace/v1/get_response_pb.js";
-
@@ -146,9 +148,11 @@ WorkerSchema.field.displayName.number,WorkerSchema.field.workRecords.number, ], workRecordsMask: { workspaceDefinedLeaveMask: { fields: [LeaveSchema.field.id.number], }, fields: [ WorkRecordSchema.field.date.number, WorkRecordSchema.field.dayWhole.number, WorkRecordSchema.field.dayHalved.number, ], }, }, },
-
@@ -221,35 +225,57 @@ let skipped = 0;let paidLeave = 0; const workspaceDefinedLeaves = new Map<string, number>(); for (const record of worker.workRecords) { switch (record.record.case) { case "workingDay": if (record.record.value.hasWorkerWorked) { worked++; } else { skipped++; } break; case "dayOff": dayoff++; break; const incr = ( record: proto.MessageShape<typeof RecordKindSchema>, amount: number = 1, ): void => { switch (record.kind.case) { case "worked": worked += amount; return; case "skipped": skipped += amount; return; case "paidLeave": paidLeave++; break; paidLeave += amount; return; case "dayOff": dayoff += amount; return; case "workspaceDefinedLeave": if (record.record.value.id?.value) { const current = workspaceDefinedLeaves.get(record.record.value.id.value); if (record.kind.value.id?.value) { const current = workspaceDefinedLeaves.get(record.kind.value.id.value); workspaceDefinedLeaves.set( record.record.value.id.value, (current ?? 0) + 1, record.kind.value.id.value, (current ?? 0) + amount, ); } return; } }; for (const record of worker.workRecords) { switch (record.kind.case) { case "dayWhole": { incr(record.kind.value); break; } case "dayHalved": { if (record.kind.value.am) { incr(record.kind.value.am, 0.5); } if (record.kind.value.pm) { incr(record.kind.value.pm, 0.5); } break; } } } const emptyDates = datesCount - worker.workRecords.filter((r) => !!r.record.case).length; datesCount - worker.workRecords.filter((r) => !!r.kind.case).length; return [ worker,
-
-
-
@@ -48,11 +48,9 @@ },workRecordsMask: { fields: [ WorkRecordSchema.field.date.number, WorkRecordSchema.field.workingDay.number, WorkRecordSchema.field.dayOff.number, WorkRecordSchema.field.paidLeave.number, WorkRecordSchema.field.workspaceDefinedLeave.number, WorkRecordSchema.field.note.number, WorkRecordSchema.field.dayWhole.number, WorkRecordSchema.field.dayHalved.number, ], workspaceDefinedLeaveMask: { fields: [
-
-
-
@@ -27,9 +27,14 @@export const DayOff: Story = { args: { workRecord: proto.create(WorkRecordSchema, { record: { case: "dayOff", value: {}, kind: { case: "dayWhole", value: { kind: { case: "dayOff", value: {}, }, }, }, }), },
-
-
-
@@ -35,7 +35,7 @@ </Text><Text size="2" weight="bold"> {date.day} </Text> {workRecord?.record.case ? ( {workRecord?.kind.case ? ( <WorkRecordBadges workRecord={workRecord} /> ) : ( <Text truncate size="1" color="gray">
-