feat(calendar): add isSameLocalDay function and integrate it into calendar components
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import assert from "node:assert/strict"
|
import assert from "node:assert/strict"
|
||||||
import test from "node:test"
|
import test from "node:test"
|
||||||
|
|
||||||
import { addDays, formatWeekTitle, startOfWeek } from "./calendar-date-range.ts"
|
import { addDays, formatWeekTitle, isSameLocalDay, startOfWeek } from "./calendar-date-range.ts"
|
||||||
|
|
||||||
test("builds Monday-based week range and title", () => {
|
test("builds Monday-based week range and title", () => {
|
||||||
const start = startOfWeek(new Date(2026, 3, 29, 14, 0, 0))
|
const start = startOfWeek(new Date(2026, 3, 29, 14, 0, 0))
|
||||||
@@ -12,3 +12,8 @@ test("builds Monday-based week range and title", () => {
|
|||||||
assert.equal(formatWeekTitle(start), "2026-04-27 - 2026-05-03")
|
assert.equal(formatWeekTitle(start), "2026-04-27 - 2026-05-03")
|
||||||
assert.equal(addDays(start, 7).getDate(), 4)
|
assert.equal(addDays(start, 7).getDate(), 4)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("compares local calendar dates", () => {
|
||||||
|
assert.equal(isSameLocalDay(new Date(2026, 3, 29, 0, 0), new Date(2026, 3, 29, 23, 59)), true)
|
||||||
|
assert.equal(isSameLocalDay(new Date(2026, 3, 29, 23, 59), new Date(2026, 3, 30, 0, 0)), false)
|
||||||
|
})
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ export function startOfWeek(date: Date) {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isSameLocalDay(a: Date, b: Date) {
|
||||||
|
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate()
|
||||||
|
}
|
||||||
|
|
||||||
export function startOfMonth(date: Date) {
|
export function startOfMonth(date: Date) {
|
||||||
const ret = startOfDay(date)
|
const ret = startOfDay(date)
|
||||||
ret.setDate(1)
|
ret.setDate(1)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type {
|
|||||||
UpdateAdminAgentTeamSchedulePayload,
|
UpdateAdminAgentTeamSchedulePayload,
|
||||||
} from "@/lib/api/admin"
|
} from "@/lib/api/admin"
|
||||||
import { cn, formatDateTime } from "@/lib/utils"
|
import { cn, formatDateTime } from "@/lib/utils"
|
||||||
|
import { isSameLocalDay } from "./calendar-date-range"
|
||||||
import { buildDayTimeLayout } from "./calendar-time-layout"
|
import { buildDayTimeLayout } from "./calendar-time-layout"
|
||||||
|
|
||||||
const weekDayNames = ["一", "二", "三", "四", "五", "六", "日"]
|
const weekDayNames = ["一", "二", "三", "四", "五", "六", "日"]
|
||||||
@@ -131,10 +132,6 @@ function isHistoricalDay(day: Date) {
|
|||||||
return startOfDay(day).getTime() < startOfDay(new Date()).getTime()
|
return startOfDay(day).getTime() < startOfDay(new Date()).getTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSameLocalDay(a: Date, b: Date) {
|
|
||||||
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate()
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMovePayload(item: AdminAgentTeamSchedule, date: string): UpdateAdminAgentTeamSchedulePayload {
|
function buildMovePayload(item: AdminAgentTeamSchedule, date: string): UpdateAdminAgentTeamSchedulePayload {
|
||||||
const originalStart = parseLocalDateTime(item.startAt)
|
const originalStart = parseLocalDateTime(item.startAt)
|
||||||
const originalEnd = parseLocalDateTime(item.endAt)
|
const originalEnd = parseLocalDateTime(item.endAt)
|
||||||
@@ -380,6 +377,7 @@ export function ScheduleCalendar({
|
|||||||
const date = formatDate(day)
|
const date = formatDate(day)
|
||||||
const inMonth = options?.inMonth ?? day.getMonth() === monthStart.getMonth()
|
const inMonth = options?.inMonth ?? day.getMonth() === monthStart.getMonth()
|
||||||
const historical = isHistoricalDay(day)
|
const historical = isHistoricalDay(day)
|
||||||
|
const today = isSameLocalDay(day, new Date())
|
||||||
const daySchedules = schedules
|
const daySchedules = schedules
|
||||||
.filter((item) => intersectsDay(item, day))
|
.filter((item) => intersectsDay(item, day))
|
||||||
.sort((a, b) => parseLocalDateTime(a.startAt).getTime() - parseLocalDateTime(b.startAt).getTime())
|
.sort((a, b) => parseLocalDateTime(a.startAt).getTime() - parseLocalDateTime(b.startAt).getTime())
|
||||||
@@ -428,7 +426,14 @@ export function ScheduleCalendar({
|
|||||||
<div className="mt-0.5 text-[10px] leading-none text-muted-foreground">{dayTimeLayout.rangeLabel}</div>
|
<div className="mt-0.5 text-[10px] leading-none text-muted-foreground">{dayTimeLayout.rangeLabel}</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{historical ? null : <CalendarPlusIcon className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />}
|
<div className="flex shrink-0 items-center gap-1">
|
||||||
|
{today ? (
|
||||||
|
<span className="rounded-sm bg-primary px-1.5 py-0.5 text-[10px] font-medium leading-none text-primary-foreground">
|
||||||
|
今天
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
{historical ? null : <CalendarPlusIcon className="size-3.5 text-muted-foreground" />}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{daySchedules.slice(0, 5).map((item) => {
|
{daySchedules.slice(0, 5).map((item) => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
CalendarClockIcon,
|
CalendarClockIcon,
|
||||||
CalendarDaysIcon,
|
CalendarDaysIcon,
|
||||||
CalendarRangeIcon,
|
CalendarRangeIcon,
|
||||||
|
CalendarSearchIcon,
|
||||||
ChevronLeftIcon,
|
ChevronLeftIcon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
ListIcon,
|
ListIcon,
|
||||||
@@ -270,6 +271,12 @@ export default function DashboardAgentTeamSchedulesPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goToToday() {
|
||||||
|
const today = new Date()
|
||||||
|
setMonthStart(startOfMonth(today))
|
||||||
|
setWeekStart(startOfWeek(today))
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex h-[calc(100vh-var(--header-height))] min-h-0 flex-1 flex-col gap-4 overflow-hidden p-4 lg:p-6">
|
<div className="flex h-[calc(100vh-var(--header-height))] min-h-0 flex-1 flex-col gap-4 overflow-hidden p-4 lg:p-6">
|
||||||
@@ -333,6 +340,12 @@ export default function DashboardAgentTeamSchedulesPage() {
|
|||||||
{viewMode === "week" ? (
|
{viewMode === "week" ? (
|
||||||
<div className="text-sm text-muted-foreground">{formatWeekTitle(weekStart)}</div>
|
<div className="text-sm text-muted-foreground">{formatWeekTitle(weekStart)}</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{viewMode !== "list" ? (
|
||||||
|
<Button variant="outline" size="sm" onClick={goToToday}>
|
||||||
|
<CalendarSearchIcon />
|
||||||
|
今天
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center xl:justify-end">
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-center xl:justify-end">
|
||||||
|
|||||||
Reference in New Issue
Block a user