@agent-native/core 0.154.4 → 0.155.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/account-health/SKILL.md +76 -0
- package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
- package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
- package/corpus/templates/analytics/AGENTS.md +3 -0
- package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
- package/corpus/templates/analytics/actions/bigquery.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
- package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
- package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/calendar/AGENTS.md +2 -0
- package/corpus/templates/calendar/DEVELOPING.md +4 -4
- package/corpus/templates/calendar/actions/get-settings.ts +4 -11
- package/corpus/templates/calendar/actions/update-availability.ts +15 -4
- package/corpus/templates/calendar/actions/update-settings.ts +15 -4
- package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
- package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
- package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
- package/corpus/templates/calendar/app/i18n-data.ts +30 -0
- package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
- package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
- package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
- package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
- package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
- package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
- package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
- package/corpus/templates/calendar/shared/api.ts +1 -0
- package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
- package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
- package/corpus/templates/calendar/shared/settings.ts +44 -0
- package/corpus/templates/clips/AGENTS.md +2 -0
- package/corpus/templates/clips/actions/add-comment.ts +5 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
- package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
- package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
- package/corpus/templates/clips/actions/update-comment.ts +9 -2
- package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
- package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
- package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
- package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
- package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
- package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
- package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
- package/corpus/templates/clips/app/root.tsx +2 -1
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +14 -7
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +8 -4
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
- package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +116 -19
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
- package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
- package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
- package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
- package/corpus/templates/clips/desktop/src/styles.css +117 -0
- package/corpus/templates/clips/server/db/index.ts +10 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/recordings.ts +3 -1
- package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
- package/corpus/templates/clips/server/plugins/db.ts +5 -0
- package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
- package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
- package/corpus/templates/content/.agents/skills/content/SKILL.md +2 -2
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +20 -4
- package/corpus/templates/content/actions/_database-row-mutation.ts +1490 -0
- package/corpus/templates/content/actions/_database-utils.ts +44 -0
- package/corpus/templates/content/actions/_property-utils.ts +4 -1
- package/corpus/templates/content/actions/add-comment.ts +2 -1
- package/corpus/templates/content/actions/add-database-item.ts +62 -240
- package/corpus/templates/content/actions/configure-document-property.ts +165 -1
- package/corpus/templates/content/actions/delete-document-property.ts +18 -0
- package/corpus/templates/content/actions/set-document-property.ts +78 -1
- package/corpus/templates/content/actions/update-comment.ts +1 -1
- package/corpus/templates/content/actions/update-database-item.ts +62 -0
- package/corpus/templates/content/actions/upsert-database-item-by-key.ts +43 -796
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +10 -8
- package/corpus/templates/content/app/hooks/use-content-database.ts +33 -32
- package/corpus/templates/content/changelog/2026-08-10-database-row-actions-now-validate-exact-schemas-and-safe-ret.md +6 -0
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +2 -0
- package/corpus/templates/content/server/db/schema.ts +39 -3
- package/corpus/templates/content/server/plugins/db.ts +30 -0
- package/corpus/templates/content/shared/api.ts +75 -2
- package/corpus/templates/design/actions/generate-design.ts +10 -7
- package/corpus/templates/design/actions/present-design-variants.ts +35 -6
- package/corpus/templates/design/actions/run-design-audit.ts +33 -0
- package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
- package/corpus/templates/design/shared/design-review.ts +1 -0
- package/corpus/templates/design/shared/html-integrity.ts +66 -0
- package/corpus/templates/dispatch/.env.example +5 -0
- package/corpus/templates/dispatch/AGENTS.md +24 -19
- package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
- package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +23 -0
- package/corpus/templates/factory/actions/view-screen.ts +17 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
- package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
- package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
- package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
- package/corpus/templates/factory/app/routes/factory.tsx +41 -0
- package/corpus/templates/factory/package.json +1 -0
- package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
- package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
- package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
- package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
- package/corpus/templates/mail/vite.config.ts +7 -0
- package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
- package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
- package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
- package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
- package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
- package/dist/action.d.ts +22 -0
- package/dist/action.js +27 -0
- package/dist/agent/production-agent.js +5 -1
- package/dist/agent/thread-data-builder.js +17 -1
- package/dist/cli/skills-content/help.js +6 -4
- package/dist/cli/skills.js +17 -2
- package/dist/cli/workspacify.d.ts +5 -0
- package/dist/cli/workspacify.js +65 -8
- package/dist/client/AgentPanel.d.ts +5 -0
- package/dist/client/AgentPanel.js +20 -4
- package/dist/client/AssistantChat.d.ts +21 -0
- package/dist/client/AssistantChat.js +66 -68
- package/dist/client/active-run-state.d.ts +14 -1
- package/dist/client/active-run-state.js +0 -0
- package/dist/client/agent-chat-adapter.js +107 -5
- package/dist/client/analytics.js +9 -2
- package/dist/client/chat/tool-call-display.js +4 -4
- package/dist/client/chat-first/apps-rail.js +9 -4
- package/dist/client/chat-first/primary-nav.d.ts +2 -1
- package/dist/client/chat-first/primary-nav.js +3 -3
- package/dist/client/chat-first/types.d.ts +5 -1
- package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
- package/dist/client/markdown/InlineMarkdown.js +65 -0
- package/dist/client/markdown/index.d.ts +1 -0
- package/dist/client/markdown/index.js +1 -0
- package/dist/client/review/ReviewThreadPanel.js +2 -1
- package/dist/client/run-stream-ownership.d.ts +39 -0
- package/dist/client/run-stream-ownership.js +65 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/feature-flags/index.d.ts +1 -1
- package/dist/feature-flags/index.js +1 -1
- package/dist/feature-flags/store.d.ts +10 -0
- package/dist/feature-flags/store.js +112 -1
- package/dist/guards/no-env-credentials.js +4 -0
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/integrations/adapters/slack.js +5 -1
- package/dist/localization/default-messages.d.ts +1 -0
- package/dist/localization/default-messages.js +1 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/resources/agents.js +40 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/resources/metadata.d.ts +10 -0
- package/dist/resources/metadata.js +7 -3
- package/dist/review/actions/create-review-comment.js +1 -1
- package/dist/review/actions/reply-review-comment.js +1 -1
- package/dist/review/actions/resolve-review-thread.js +1 -1
- package/dist/server/action-routes.js +42 -8
- package/dist/server/agent-discovery.js +14 -3
- package/dist/server/core-routes-plugin.js +1 -1
- package/dist/server/identity-sso-migrations.d.ts +7 -0
- package/dist/server/identity-sso-migrations.js +36 -0
- package/dist/server/identity-sso-store.d.ts +3 -2
- package/dist/server/identity-sso-store.js +8 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +4 -0
- package/dist/tracking/providers.js +13 -3
- package/dist/workspace-files/tool.js +1 -1
- package/package.json +3 -2
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { getRequestUserEmail } from "@agent-native/core/server";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getUserSetting,
|
|
5
|
+
putUserSetting,
|
|
6
|
+
putSetting,
|
|
7
|
+
} from "@agent-native/core/settings";
|
|
4
8
|
import { z } from "zod";
|
|
5
9
|
|
|
6
|
-
import
|
|
10
|
+
import { normalizeCalendarSettings } from "../shared/settings.js";
|
|
7
11
|
|
|
8
12
|
export default defineAction({
|
|
9
13
|
description: "Update calendar settings",
|
|
@@ -20,12 +24,19 @@ export default defineAction({
|
|
|
20
24
|
.positive()
|
|
21
25
|
.optional()
|
|
22
26
|
.describe("Default event duration in minutes"),
|
|
27
|
+
weekStart: z
|
|
28
|
+
.enum(["sunday", "monday"])
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("First day shown in calendar weeks"),
|
|
23
31
|
}),
|
|
24
32
|
run: async (args) => {
|
|
25
33
|
const email = getRequestUserEmail();
|
|
26
34
|
if (!email) throw new Error("no authenticated user");
|
|
27
|
-
|
|
28
|
-
const settings =
|
|
35
|
+
const currentSettings = await getUserSetting(email, "calendar-settings");
|
|
36
|
+
const settings = normalizeCalendarSettings({
|
|
37
|
+
...normalizeCalendarSettings(currentSettings),
|
|
38
|
+
...args,
|
|
39
|
+
});
|
|
29
40
|
const settingsRecord = settings as unknown as Record<string, unknown>;
|
|
30
41
|
await putUserSetting(email, "calendar-settings", settingsRecord);
|
|
31
42
|
await putSetting("calendar-settings", settingsRecord);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AvailabilityConfig } from "@shared/api";
|
|
2
|
+
import { getWeekdayOrder } from "@shared/calendar-week";
|
|
2
3
|
import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
|
|
3
4
|
import {
|
|
4
5
|
startOfMonth,
|
|
@@ -31,6 +32,7 @@ interface DatePickerProps {
|
|
|
31
32
|
availabilityLoading?: boolean;
|
|
32
33
|
viewMonth: Date;
|
|
33
34
|
onViewMonthChange: (month: Date) => void;
|
|
35
|
+
weekStartsOn?: 0 | 1;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
const WEEKDAY_HEADERS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
@@ -53,12 +55,16 @@ export function DatePicker({
|
|
|
53
55
|
availabilityLoading = false,
|
|
54
56
|
viewMonth,
|
|
55
57
|
onViewMonthChange,
|
|
58
|
+
weekStartsOn = 0,
|
|
56
59
|
}: DatePickerProps) {
|
|
57
60
|
const monthStart = startOfMonth(viewMonth);
|
|
58
61
|
const monthEnd = endOfMonth(viewMonth);
|
|
59
|
-
const calendarStart = startOfWeek(monthStart);
|
|
60
|
-
const calendarEnd = endOfWeek(monthEnd);
|
|
62
|
+
const calendarStart = startOfWeek(monthStart, { weekStartsOn });
|
|
63
|
+
const calendarEnd = endOfWeek(monthEnd, { weekStartsOn });
|
|
61
64
|
const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd });
|
|
65
|
+
const weekdayHeaders = getWeekdayOrder(weekStartsOn).map(
|
|
66
|
+
(day) => WEEKDAY_HEADERS[day],
|
|
67
|
+
);
|
|
62
68
|
const availableDateSet = useMemo(
|
|
63
69
|
() => new Set(availableDates),
|
|
64
70
|
[availableDates],
|
|
@@ -103,7 +109,7 @@ export function DatePicker({
|
|
|
103
109
|
|
|
104
110
|
{/* Weekday headers */}
|
|
105
111
|
<div className="grid grid-cols-7 mb-1">
|
|
106
|
-
{
|
|
112
|
+
{weekdayHeaders.map((d) => (
|
|
107
113
|
<div
|
|
108
114
|
key={d}
|
|
109
115
|
className="py-1 text-center text-xs font-medium text-muted-foreground"
|
|
@@ -76,8 +76,8 @@ import {
|
|
|
76
76
|
createReminderDraft,
|
|
77
77
|
dateTimeInTimezoneToIso,
|
|
78
78
|
getEventEndValidationMessage,
|
|
79
|
-
getLocalTimezone,
|
|
80
79
|
remindersToDraftState,
|
|
80
|
+
resolveEventTimezone,
|
|
81
81
|
type AttachmentDraft,
|
|
82
82
|
type ReminderDraft,
|
|
83
83
|
type ReminderMode,
|
|
@@ -236,7 +236,7 @@ export function CreateEventPopover({
|
|
|
236
236
|
const defaultDurationMinutes = Number.isFinite(rawDefaultDuration)
|
|
237
237
|
? Math.max(5, rawDefaultDuration)
|
|
238
238
|
: 30;
|
|
239
|
-
const defaultTimezone = settings?.timezone ||
|
|
239
|
+
const defaultTimezone = settings?.timezone || resolveEventTimezone();
|
|
240
240
|
const fallbackStart = "09:00";
|
|
241
241
|
const fallbackEnd = addMinutesToTimeString(
|
|
242
242
|
fallbackStart,
|
|
@@ -279,6 +279,7 @@ export function CreateEventPopover({
|
|
|
279
279
|
const [findTimeOpen, setFindTimeOpen] = useState(false);
|
|
280
280
|
const isOutOfOffice = eventType === "outOfOffice";
|
|
281
281
|
const timedOnlyStatus = eventType === "focusTime";
|
|
282
|
+
const eventTimezone = resolveEventTimezone(timezone);
|
|
282
283
|
|
|
283
284
|
const createEvent = useCreateEvent();
|
|
284
285
|
const delEvent = useDeleteEvent();
|
|
@@ -308,8 +309,9 @@ export function CreateEventPopover({
|
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
const nextDate = format(defaultDate || new Date(), "yyyy-MM-dd");
|
|
311
|
-
const draftTimezone =
|
|
312
|
-
draft?.startTimeZone || draft?.endTimeZone || defaultTimezone
|
|
312
|
+
const draftTimezone = resolveEventTimezone(
|
|
313
|
+
draft?.startTimeZone || draft?.endTimeZone || defaultTimezone,
|
|
314
|
+
);
|
|
313
315
|
const initKey = buildEventFormInitializationKey({
|
|
314
316
|
draftId: draft?.id,
|
|
315
317
|
date: nextDate,
|
|
@@ -455,12 +457,12 @@ export function CreateEventPopover({
|
|
|
455
457
|
? date
|
|
456
458
|
: effectiveAllDay
|
|
457
459
|
? new Date(`${date}T00:00:00`).toISOString()
|
|
458
|
-
: dateTimeInTimezoneToIso(date, startTime,
|
|
460
|
+
: dateTimeInTimezoneToIso(date, startTime, eventTimezone);
|
|
459
461
|
const endValue = fullDayOutOfOffice
|
|
460
462
|
? endDate
|
|
461
463
|
: effectiveAllDay
|
|
462
464
|
? allDayEnd.toISOString()
|
|
463
|
-
: dateTimeInTimezoneToIso(endDate, endTime,
|
|
465
|
+
: dateTimeInTimezoneToIso(endDate, endTime, eventTimezone);
|
|
464
466
|
const attachmentResult = validateAttachmentDrafts(attachments);
|
|
465
467
|
const reminderPatch = buildReminderPayload(reminderMode, reminders);
|
|
466
468
|
const nextDraft: CalendarEventDraft = {
|
|
@@ -470,8 +472,8 @@ export function CreateEventPopover({
|
|
|
470
472
|
description: isOutOfOffice ? "" : description,
|
|
471
473
|
start: startValue,
|
|
472
474
|
end: endValue,
|
|
473
|
-
startTimeZone: effectiveAllDay ? undefined :
|
|
474
|
-
endTimeZone: effectiveAllDay ? undefined :
|
|
475
|
+
startTimeZone: effectiveAllDay ? undefined : eventTimezone,
|
|
476
|
+
endTimeZone: effectiveAllDay ? undefined : eventTimezone,
|
|
475
477
|
location: isOutOfOffice ? "" : location,
|
|
476
478
|
allDay: effectiveAllDay,
|
|
477
479
|
fullDay: fullDayOutOfOffice,
|
|
@@ -547,7 +549,7 @@ export function CreateEventPopover({
|
|
|
547
549
|
declineMessage,
|
|
548
550
|
availability,
|
|
549
551
|
visibility,
|
|
550
|
-
|
|
552
|
+
eventTimezone,
|
|
551
553
|
colorId,
|
|
552
554
|
reminderMode,
|
|
553
555
|
reminders,
|
|
@@ -578,7 +580,7 @@ export function CreateEventPopover({
|
|
|
578
580
|
time: allDay
|
|
579
581
|
? t("eventForm.allDay")
|
|
580
582
|
: t("eventForm.ai.timeRange", { startTime, endTime }),
|
|
581
|
-
timezone,
|
|
583
|
+
timezone: eventTimezone,
|
|
582
584
|
location: location || t("eventForm.ai.none"),
|
|
583
585
|
attendees:
|
|
584
586
|
attendees.map((attendee) => attendee.email).join(", ") ||
|
|
@@ -636,11 +638,11 @@ export function CreateEventPopover({
|
|
|
636
638
|
const effectiveAllDay = allDay && !isOutOfOffice && !timedOnlyStatus;
|
|
637
639
|
const currentStartISO =
|
|
638
640
|
!effectiveAllDay && date && startTime
|
|
639
|
-
? dateTimeInTimezoneToIso(date, startTime,
|
|
641
|
+
? dateTimeInTimezoneToIso(date, startTime, eventTimezone)
|
|
640
642
|
: undefined;
|
|
641
643
|
const currentEndISO =
|
|
642
644
|
!effectiveAllDay && endDate && endTime
|
|
643
|
-
? dateTimeInTimezoneToIso(endDate, endTime,
|
|
645
|
+
? dateTimeInTimezoneToIso(endDate, endTime, eventTimezone)
|
|
644
646
|
: undefined;
|
|
645
647
|
const findTimeDurationMinutes =
|
|
646
648
|
currentStartISO && currentEndISO
|
|
@@ -654,8 +656,8 @@ export function CreateEventPopover({
|
|
|
654
656
|
: defaultDurationMinutes;
|
|
655
657
|
|
|
656
658
|
function handleSelectFindTimeSlot(slot: { start: string; end: string }) {
|
|
657
|
-
const startParts = dateTimePartsInTimezone(slot.start,
|
|
658
|
-
const endParts = dateTimePartsInTimezone(slot.end,
|
|
659
|
+
const startParts = dateTimePartsInTimezone(slot.start, eventTimezone);
|
|
660
|
+
const endParts = dateTimePartsInTimezone(slot.end, eventTimezone);
|
|
659
661
|
if (!startParts || !endParts) return;
|
|
660
662
|
setAllDay(false);
|
|
661
663
|
setDate(startParts.date);
|
|
@@ -708,12 +710,12 @@ export function CreateEventPopover({
|
|
|
708
710
|
? date
|
|
709
711
|
: effectiveAllDay
|
|
710
712
|
? new Date(`${date}T00:00:00`).toISOString()
|
|
711
|
-
: dateTimeInTimezoneToIso(date, startTime,
|
|
713
|
+
: dateTimeInTimezoneToIso(date, startTime, eventTimezone);
|
|
712
714
|
const endValue = fullDayOutOfOffice
|
|
713
715
|
? endDate
|
|
714
716
|
: effectiveAllDay
|
|
715
717
|
? allDayEnd.toISOString()
|
|
716
|
-
: dateTimeInTimezoneToIso(endDate, endTime,
|
|
718
|
+
: dateTimeInTimezoneToIso(endDate, endTime, eventTimezone);
|
|
717
719
|
|
|
718
720
|
if (
|
|
719
721
|
fullDayOutOfOffice
|
|
@@ -761,8 +763,8 @@ export function CreateEventPopover({
|
|
|
761
763
|
description: isOutOfOffice ? "" : description,
|
|
762
764
|
start: startValue,
|
|
763
765
|
end: endValue,
|
|
764
|
-
startTimeZone: effectiveAllDay ? undefined :
|
|
765
|
-
endTimeZone: effectiveAllDay ? undefined :
|
|
766
|
+
startTimeZone: effectiveAllDay ? undefined : eventTimezone,
|
|
767
|
+
endTimeZone: effectiveAllDay ? undefined : eventTimezone,
|
|
766
768
|
location: isOutOfOffice ? "" : location,
|
|
767
769
|
accountEmail,
|
|
768
770
|
allDay: effectiveAllDay,
|
|
@@ -1459,7 +1461,7 @@ export function CreateEventPopover({
|
|
|
1459
1461
|
(draft ? t("eventForm.invite") : t("eventForm.newEventLower"))
|
|
1460
1462
|
}
|
|
1461
1463
|
date={date}
|
|
1462
|
-
timezone={
|
|
1464
|
+
timezone={eventTimezone}
|
|
1463
1465
|
durationMinutes={findTimeDurationMinutes}
|
|
1464
1466
|
attendees={attendees}
|
|
1465
1467
|
accountEmail={accountEmail}
|
|
@@ -1441,7 +1441,7 @@ export function EventDetailPopover({
|
|
|
1441
1441
|
side={isMobile ? "bottom" : "right"}
|
|
1442
1442
|
sideOffset={isMobile ? 6 : 8}
|
|
1443
1443
|
collisionPadding={12}
|
|
1444
|
-
className="flex max-h-[
|
|
1444
|
+
className="flex max-h-[var(--radix-popover-content-available-height)] w-[min(420px,calc(100vw-2rem))] flex-col overflow-hidden p-0"
|
|
1445
1445
|
onClick={(e) => e.stopPropagation()}
|
|
1446
1446
|
onOpenAutoFocus={(e) => {
|
|
1447
1447
|
e.preventDefault();
|
|
@@ -1671,12 +1671,15 @@ export function EventDetailPopover({
|
|
|
1671
1671
|
/>
|
|
1672
1672
|
</>
|
|
1673
1673
|
)}
|
|
1674
|
+
<span className="ml-auto">
|
|
1675
|
+
<TimezonePickerPopover
|
|
1676
|
+
compact
|
|
1677
|
+
value={editTimezone}
|
|
1678
|
+
label={t("eventForm.timezone")}
|
|
1679
|
+
onChange={handleInlineTimezoneChange}
|
|
1680
|
+
/>
|
|
1681
|
+
</span>
|
|
1674
1682
|
</div>
|
|
1675
|
-
<TimezonePickerPopover
|
|
1676
|
-
value={editTimezone}
|
|
1677
|
-
label={t("eventForm.timezone")}
|
|
1678
|
-
onChange={handleInlineTimezoneChange}
|
|
1679
|
-
/>
|
|
1680
1683
|
</>
|
|
1681
1684
|
)}
|
|
1682
1685
|
</div>
|
|
@@ -5,12 +5,14 @@ import type {
|
|
|
5
5
|
FindTimeResult,
|
|
6
6
|
FindTimeSlot,
|
|
7
7
|
} from "@shared/api";
|
|
8
|
+
import { getWeekStartsOn } from "@shared/calendar-week";
|
|
8
9
|
import {
|
|
9
10
|
IconAlertCircle,
|
|
10
11
|
IconCalendarTime,
|
|
11
12
|
IconChevronLeft,
|
|
12
13
|
IconChevronRight,
|
|
13
14
|
IconLoader2,
|
|
15
|
+
IconX,
|
|
14
16
|
IconUsers,
|
|
15
17
|
} from "@tabler/icons-react";
|
|
16
18
|
import {
|
|
@@ -31,11 +33,13 @@ import { Badge } from "@/components/ui/badge";
|
|
|
31
33
|
import { Button } from "@/components/ui/button";
|
|
32
34
|
import {
|
|
33
35
|
Dialog,
|
|
36
|
+
DialogClose,
|
|
34
37
|
DialogContent,
|
|
35
38
|
DialogDescription,
|
|
36
39
|
DialogTitle,
|
|
37
40
|
} from "@/components/ui/dialog";
|
|
38
41
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
42
|
+
import { useSettings } from "@/hooks/use-settings";
|
|
39
43
|
import { dateTimeInTimezoneToIso } from "@/lib/event-form-utils";
|
|
40
44
|
import { cn } from "@/lib/utils";
|
|
41
45
|
|
|
@@ -188,6 +192,8 @@ export function FindTimePanel({
|
|
|
188
192
|
isTakeover = false,
|
|
189
193
|
}: FindTimePanelProps) {
|
|
190
194
|
const t = useT();
|
|
195
|
+
const { data: settings } = useSettings();
|
|
196
|
+
const weekStartsOn = getWeekStartsOn(settings?.weekStart);
|
|
191
197
|
const [anchorDate, setAnchorDate] = useState(() => parseDateOnly(date));
|
|
192
198
|
|
|
193
199
|
useEffect(() => {
|
|
@@ -220,7 +226,10 @@ export function FindTimePanel({
|
|
|
220
226
|
return () => window.removeEventListener("keydown", handleKeyDown, true);
|
|
221
227
|
}, [isTakeover]);
|
|
222
228
|
|
|
223
|
-
const weekStart = useMemo(
|
|
229
|
+
const weekStart = useMemo(
|
|
230
|
+
() => startOfWeek(anchorDate, { weekStartsOn }),
|
|
231
|
+
[anchorDate, weekStartsOn],
|
|
232
|
+
);
|
|
224
233
|
const days = useMemo(
|
|
225
234
|
() =>
|
|
226
235
|
eachDayOfInterval({
|
|
@@ -663,9 +672,11 @@ export function FindTimeTakeover({
|
|
|
663
672
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
664
673
|
<DialogContent
|
|
665
674
|
data-find-time-takeover
|
|
666
|
-
|
|
675
|
+
hideClose
|
|
676
|
+
overlayClassName="z-[310]"
|
|
677
|
+
className="z-[320] flex h-[100dvh] max-h-[100dvh] w-screen max-w-none translate-x-[-50%] translate-y-[-50%] flex-col gap-0 overflow-hidden border-0 p-0 shadow-xl sm:rounded-none"
|
|
667
678
|
>
|
|
668
|
-
<header className="flex h-14 shrink-0 items-center border-b border-border px-4
|
|
679
|
+
<header className="flex h-14 shrink-0 items-center border-b border-border px-4 md:px-6">
|
|
669
680
|
<div className="min-w-0">
|
|
670
681
|
<DialogTitle className="truncate text-base">
|
|
671
682
|
{dialogTitle}
|
|
@@ -679,6 +690,17 @@ export function FindTimeTakeover({
|
|
|
679
690
|
{subtitle || t("findTime.takeoverDescription")}
|
|
680
691
|
</DialogDescription>
|
|
681
692
|
</div>
|
|
693
|
+
<DialogClose asChild>
|
|
694
|
+
<button
|
|
695
|
+
type="button"
|
|
696
|
+
aria-label={t("eventDialog.close")}
|
|
697
|
+
title={t("eventDialog.close")}
|
|
698
|
+
className="ml-auto flex h-10 shrink-0 items-center gap-2 rounded-md px-3 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
699
|
+
>
|
|
700
|
+
<IconX className="h-4 w-4" />
|
|
701
|
+
<span>{t("eventDialog.close")}</span>
|
|
702
|
+
</button>
|
|
703
|
+
</DialogClose>
|
|
682
704
|
</header>
|
|
683
705
|
<div className="min-h-0 flex-1 overflow-auto p-4 md:p-6">
|
|
684
706
|
<FindTimePanel
|
|
@@ -193,10 +193,12 @@ export function TimezonePickerPopover({
|
|
|
193
193
|
value,
|
|
194
194
|
onChange,
|
|
195
195
|
label,
|
|
196
|
+
compact = false,
|
|
196
197
|
}: {
|
|
197
198
|
value: string;
|
|
198
199
|
onChange: (value: string) => void;
|
|
199
200
|
label: string;
|
|
201
|
+
compact?: boolean;
|
|
200
202
|
}) {
|
|
201
203
|
const t = useT();
|
|
202
204
|
const [open, setOpen] = useState(false);
|
|
@@ -208,11 +210,16 @@ export function TimezonePickerPopover({
|
|
|
208
210
|
type="button"
|
|
209
211
|
variant="ghost"
|
|
210
212
|
size="sm"
|
|
211
|
-
className=
|
|
213
|
+
className={cn(
|
|
214
|
+
compact
|
|
215
|
+
? "size-7 rounded-full p-0 text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
216
|
+
: "h-auto justify-start rounded px-1.5 py-0.5 text-sm font-normal text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
217
|
+
)}
|
|
212
218
|
aria-label={label}
|
|
219
|
+
title={compact ? label : undefined}
|
|
213
220
|
>
|
|
214
|
-
<IconWorld className="
|
|
215
|
-
{formatTimezoneLabel(value)}
|
|
221
|
+
<IconWorld className={cn("size-4 shrink-0", !compact && "mr-2")} />
|
|
222
|
+
{!compact && formatTimezoneLabel(value)}
|
|
216
223
|
</Button>
|
|
217
224
|
</PopoverTrigger>
|
|
218
225
|
<PopoverContent
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CalendarEvent } from "@shared/api";
|
|
2
|
+
import { getWeekdayOrder } from "@shared/calendar-week";
|
|
2
3
|
import {
|
|
3
4
|
startOfMonth,
|
|
4
5
|
endOfMonth,
|
|
@@ -49,6 +50,7 @@ interface MonthViewProps {
|
|
|
49
50
|
) => void;
|
|
50
51
|
onDraftDiscard?: (eventId: string) => void;
|
|
51
52
|
isLoading?: boolean;
|
|
53
|
+
weekStartsOn?: 0 | 1;
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
// Skeleton pill widths per day-of-week (Sun–Sat), empty = no skeletons
|
|
@@ -85,6 +87,7 @@ export const MonthView = memo(function MonthView({
|
|
|
85
87
|
onDraftCreate,
|
|
86
88
|
onDraftDiscard,
|
|
87
89
|
isLoading = false,
|
|
90
|
+
weekStartsOn = 0,
|
|
88
91
|
}: MonthViewProps) {
|
|
89
92
|
const isMobile = useIsMobile();
|
|
90
93
|
const { prefs } = useViewPreferences();
|
|
@@ -93,8 +96,8 @@ export const MonthView = memo(function MonthView({
|
|
|
93
96
|
|
|
94
97
|
const monthStart = startOfMonth(selectedDate);
|
|
95
98
|
const monthEnd = endOfMonth(selectedDate);
|
|
96
|
-
const calendarStart = startOfWeek(monthStart);
|
|
97
|
-
const calendarEnd = endOfWeek(monthEnd);
|
|
99
|
+
const calendarStart = startOfWeek(monthStart, { weekStartsOn });
|
|
100
|
+
const calendarEnd = endOfWeek(monthEnd, { weekStartsOn });
|
|
98
101
|
const allDays = eachDayOfInterval({
|
|
99
102
|
start: calendarStart,
|
|
100
103
|
end: calendarEnd,
|
|
@@ -103,13 +106,10 @@ export const MonthView = memo(function MonthView({
|
|
|
103
106
|
? allDays.filter((d) => d.getDay() !== 0 && d.getDay() !== 6)
|
|
104
107
|
: allDays;
|
|
105
108
|
const colCount = prefs.hideWeekends ? 5 : 7;
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
: isMobile
|
|
111
|
-
? WEEKDAY_HEADERS_SHORT
|
|
112
|
-
: WEEKDAY_HEADERS;
|
|
109
|
+
const headerLabels = isMobile ? WEEKDAY_HEADERS_SHORT : WEEKDAY_HEADERS;
|
|
110
|
+
const headers = getWeekdayOrder(weekStartsOn)
|
|
111
|
+
.filter((day) => !prefs.hideWeekends || (day !== 0 && day !== 6))
|
|
112
|
+
.map((day) => headerLabels[day]);
|
|
113
113
|
|
|
114
114
|
// Pre-group events by every day they overlap (not just their start day) so
|
|
115
115
|
// multi-day events keep appearing as the grid moves past their start date.
|
|
@@ -107,6 +107,7 @@ interface WeekViewProps {
|
|
|
107
107
|
) => void;
|
|
108
108
|
onDraftDiscard?: (eventId: string) => void;
|
|
109
109
|
isLoading?: boolean;
|
|
110
|
+
weekStartsOn?: 0 | 1;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
// [startHour, startMin, durationMin, widthPct] per day column (Sun–Sat)
|
|
@@ -559,6 +560,7 @@ export const WeekView = memo(function WeekView({
|
|
|
559
560
|
onDraftCreate,
|
|
560
561
|
onDraftDiscard,
|
|
561
562
|
isLoading = false,
|
|
563
|
+
weekStartsOn = 0,
|
|
562
564
|
}: WeekViewProps) {
|
|
563
565
|
const t = useT();
|
|
564
566
|
const workingLocationLabels = useMemo(
|
|
@@ -606,8 +608,14 @@ export const WeekView = memo(function WeekView({
|
|
|
606
608
|
}, []);
|
|
607
609
|
|
|
608
610
|
const { prefs } = useViewPreferences();
|
|
609
|
-
const weekStart = useMemo(
|
|
610
|
-
|
|
611
|
+
const weekStart = useMemo(
|
|
612
|
+
() => startOfWeek(selectedDate, { weekStartsOn }),
|
|
613
|
+
[selectedDate, weekStartsOn],
|
|
614
|
+
);
|
|
615
|
+
const weekEnd = useMemo(
|
|
616
|
+
() => endOfWeek(selectedDate, { weekStartsOn }),
|
|
617
|
+
[selectedDate, weekStartsOn],
|
|
618
|
+
);
|
|
611
619
|
// Stable day/hour arrays — recomputed only when the week or weekend
|
|
612
620
|
// visibility actually changes, so memoized children (event buttons) don't
|
|
613
621
|
// see a new array identity on every drag/focus re-render.
|
|
@@ -1408,7 +1416,7 @@ export const WeekView = memo(function WeekView({
|
|
|
1408
1416
|
|
|
1409
1417
|
{/* Skeleton events when loading */}
|
|
1410
1418
|
{isLoading &&
|
|
1411
|
-
WEEK_SKELETONS[
|
|
1419
|
+
WEEK_SKELETONS[day.getDay()]?.map(
|
|
1412
1420
|
([startHour, startMin, duration, widthPct], i) => {
|
|
1413
1421
|
const topPx =
|
|
1414
1422
|
((startHour - START_HOUR) * 60 + startMin) *
|
|
@@ -5,6 +5,7 @@ import { openCommandMenu } from "@agent-native/core/client/navigation";
|
|
|
5
5
|
import { OrgSwitcher } from "@agent-native/core/client/org";
|
|
6
6
|
import { FeedbackButton } from "@agent-native/core/client/ui";
|
|
7
7
|
import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
|
|
8
|
+
import { getWeekdayOrder, getWeekStartsOn } from "@shared/calendar-week";
|
|
8
9
|
import {
|
|
9
10
|
IconCalendar,
|
|
10
11
|
IconSettings,
|
|
@@ -77,6 +78,7 @@ import {
|
|
|
77
78
|
useRemoveOverlayPerson,
|
|
78
79
|
useUpdateOverlayPersonColor,
|
|
79
80
|
} from "@/hooks/use-overlay-people";
|
|
81
|
+
import { useSettings } from "@/hooks/use-settings";
|
|
80
82
|
import { useViewPreferences } from "@/hooks/use-view-preferences";
|
|
81
83
|
import {
|
|
82
84
|
CALENDAR_COLORS,
|
|
@@ -200,6 +202,8 @@ function MiniCalendar({
|
|
|
200
202
|
}) {
|
|
201
203
|
const [viewMonth, setViewMonth] = useState(() => startOfMonth(selectedDate));
|
|
202
204
|
const [pickerOpen, setPickerOpen] = useState(false);
|
|
205
|
+
const { data: settings } = useSettings();
|
|
206
|
+
const weekStartsOn = getWeekStartsOn(settings?.weekStart);
|
|
203
207
|
|
|
204
208
|
// Sync viewMonth when selectedDate changes to a different month
|
|
205
209
|
useEffect(() => {
|
|
@@ -211,8 +215,8 @@ function MiniCalendar({
|
|
|
211
215
|
const days = useMemo(() => {
|
|
212
216
|
const monthStart = startOfMonth(viewMonth);
|
|
213
217
|
const monthEnd = endOfMonth(viewMonth);
|
|
214
|
-
const calStart = startOfWeek(monthStart);
|
|
215
|
-
const calEnd = endOfWeek(monthEnd);
|
|
218
|
+
const calStart = startOfWeek(monthStart, { weekStartsOn });
|
|
219
|
+
const calEnd = endOfWeek(monthEnd, { weekStartsOn });
|
|
216
220
|
|
|
217
221
|
const result: Date[] = [];
|
|
218
222
|
let current = calStart;
|
|
@@ -221,9 +225,11 @@ function MiniCalendar({
|
|
|
221
225
|
current = addDays(current, 1);
|
|
222
226
|
}
|
|
223
227
|
return result;
|
|
224
|
-
}, [viewMonth]);
|
|
228
|
+
}, [viewMonth, weekStartsOn]);
|
|
225
229
|
|
|
226
|
-
const weekdays =
|
|
230
|
+
const weekdays = getWeekdayOrder(weekStartsOn).map(
|
|
231
|
+
(day) => ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"][day],
|
|
232
|
+
);
|
|
227
233
|
|
|
228
234
|
return (
|
|
229
235
|
<div className="px-3 py-3">
|
|
@@ -265,6 +265,9 @@ const enUS = {
|
|
|
265
265
|
defaultDurationLabel: "Default event duration (minutes)",
|
|
266
266
|
defaultDurationHelp:
|
|
267
267
|
"Default length for new calendar events and booking slots. Booking links can override this per link.",
|
|
268
|
+
weekStartLabel: "Week starts on",
|
|
269
|
+
weekStartSunday: "Sunday - Saturday",
|
|
270
|
+
weekStartMonday: "Monday - Sunday",
|
|
268
271
|
},
|
|
269
272
|
eventDialog: {
|
|
270
273
|
eventUpdated: "Event updated",
|
|
@@ -4303,6 +4306,9 @@ const translatedCalendarExtras = {
|
|
|
4303
4306
|
defaultDurationLabel: "默认事件时长(分钟)",
|
|
4304
4307
|
defaultDurationHelp:
|
|
4305
4308
|
"新日历事件和预约时段的默认长度。预约链接可按链接覆盖此设置。",
|
|
4309
|
+
weekStartLabel: "每周从哪天开始",
|
|
4310
|
+
weekStartSunday: "周日 - 周六",
|
|
4311
|
+
weekStartMonday: "周一 - 周日",
|
|
4306
4312
|
},
|
|
4307
4313
|
eventDialog: {
|
|
4308
4314
|
eventUpdated: "事件已更新",
|
|
@@ -4372,6 +4378,9 @@ const translatedCalendarExtras = {
|
|
|
4372
4378
|
defaultDurationLabel: "Duración predeterminada del evento (minutos)",
|
|
4373
4379
|
defaultDurationHelp:
|
|
4374
4380
|
"Duración predeterminada para eventos nuevos y franjas de reserva. Cada enlace de reserva puede anularla.",
|
|
4381
|
+
weekStartLabel: "La semana empieza el",
|
|
4382
|
+
weekStartSunday: "Domingo - sábado",
|
|
4383
|
+
weekStartMonday: "Lunes - domingo",
|
|
4375
4384
|
},
|
|
4376
4385
|
eventDialog: {
|
|
4377
4386
|
eventUpdated: "Evento actualizado",
|
|
@@ -4442,6 +4451,9 @@ const translatedCalendarExtras = {
|
|
|
4442
4451
|
defaultDurationLabel: "Durée d'événement par défaut (minutes)",
|
|
4443
4452
|
defaultDurationHelp:
|
|
4444
4453
|
"Durée par défaut des nouveaux événements et créneaux de réservation. Chaque lien de réservation peut la remplacer.",
|
|
4454
|
+
weekStartLabel: "La semaine commence le",
|
|
4455
|
+
weekStartSunday: "Dimanche - samedi",
|
|
4456
|
+
weekStartMonday: "Lundi - dimanche",
|
|
4445
4457
|
},
|
|
4446
4458
|
eventDialog: {
|
|
4447
4459
|
eventUpdated: "Événement mis à jour",
|
|
@@ -4512,6 +4524,9 @@ const translatedCalendarExtras = {
|
|
|
4512
4524
|
defaultDurationLabel: "Standarddauer für Ereignisse (Minuten)",
|
|
4513
4525
|
defaultDurationHelp:
|
|
4514
4526
|
"Standardlänge für neue Kalenderereignisse und Buchungsslots. Buchungslinks können sie pro Link überschreiben.",
|
|
4527
|
+
weekStartLabel: "Wochenbeginn",
|
|
4528
|
+
weekStartSunday: "Sonntag - Samstag",
|
|
4529
|
+
weekStartMonday: "Montag - Sonntag",
|
|
4515
4530
|
},
|
|
4516
4531
|
eventDialog: {
|
|
4517
4532
|
eventUpdated: "Ereignis aktualisiert",
|
|
@@ -4581,6 +4596,9 @@ const translatedCalendarExtras = {
|
|
|
4581
4596
|
defaultDurationLabel: "デフォルトのイベント時間(分)",
|
|
4582
4597
|
defaultDurationHelp:
|
|
4583
4598
|
"新しいカレンダーイベントと予約枠のデフォルト時間です。予約リンクごとに上書きできます。",
|
|
4599
|
+
weekStartLabel: "週の開始日",
|
|
4600
|
+
weekStartSunday: "日曜日 - 土曜日",
|
|
4601
|
+
weekStartMonday: "月曜日 - 日曜日",
|
|
4584
4602
|
},
|
|
4585
4603
|
eventDialog: {
|
|
4586
4604
|
eventUpdated: "イベントを更新しました",
|
|
@@ -4647,6 +4665,9 @@ const translatedCalendarExtras = {
|
|
|
4647
4665
|
defaultDurationLabel: "기본 이벤트 시간(분)",
|
|
4648
4666
|
defaultDurationHelp:
|
|
4649
4667
|
"새 캘린더 이벤트와 예약 슬롯의 기본 길이입니다. 예약 링크별로 재정의할 수 있습니다.",
|
|
4668
|
+
weekStartLabel: "주 시작일",
|
|
4669
|
+
weekStartSunday: "일요일 - 토요일",
|
|
4670
|
+
weekStartMonday: "월요일 - 일요일",
|
|
4650
4671
|
},
|
|
4651
4672
|
eventDialog: {
|
|
4652
4673
|
eventUpdated: "이벤트가 업데이트되었습니다",
|
|
@@ -4716,6 +4737,9 @@ const translatedCalendarExtras = {
|
|
|
4716
4737
|
defaultDurationLabel: "Duração padrão do evento (minutos)",
|
|
4717
4738
|
defaultDurationHelp:
|
|
4718
4739
|
"Duração padrão para novos eventos e horários de agendamento. Links de agendamento podem substituir isso por link.",
|
|
4740
|
+
weekStartLabel: "A semana começa no",
|
|
4741
|
+
weekStartSunday: "Domingo - sábado",
|
|
4742
|
+
weekStartMonday: "Segunda-feira - domingo",
|
|
4719
4743
|
},
|
|
4720
4744
|
eventDialog: {
|
|
4721
4745
|
eventUpdated: "Evento atualizado",
|
|
@@ -4784,6 +4808,9 @@ const translatedCalendarExtras = {
|
|
|
4784
4808
|
defaultDurationLabel: "डिफ़ॉल्ट इवेंट अवधि (मिनट)",
|
|
4785
4809
|
defaultDurationHelp:
|
|
4786
4810
|
"नए कैलेंडर इवेंट और बुकिंग स्लॉट की डिफ़ॉल्ट लंबाई। बुकिंग लिंक इसे प्रति लिंक बदल सकते हैं।",
|
|
4811
|
+
weekStartLabel: "सप्ताह की शुरुआत",
|
|
4812
|
+
weekStartSunday: "रविवार - शनिवार",
|
|
4813
|
+
weekStartMonday: "सोमवार - रविवार",
|
|
4787
4814
|
},
|
|
4788
4815
|
eventDialog: {
|
|
4789
4816
|
eventUpdated: "इवेंट अपडेट हो गया",
|
|
@@ -4851,6 +4878,9 @@ const translatedCalendarExtras = {
|
|
|
4851
4878
|
defaultDurationLabel: "مدة الحدث الافتراضية (بالدقائق)",
|
|
4852
4879
|
defaultDurationHelp:
|
|
4853
4880
|
"المدة الافتراضية لأحداث التقويم الجديدة وفتحات الحجز. يمكن لروابط الحجز تجاوزها لكل رابط.",
|
|
4881
|
+
weekStartLabel: "يبدأ الأسبوع يوم",
|
|
4882
|
+
weekStartSunday: "الأحد - السبت",
|
|
4883
|
+
weekStartMonday: "الاثنين - الأحد",
|
|
4854
4884
|
},
|
|
4855
4885
|
eventDialog: {
|
|
4856
4886
|
eventUpdated: "تم تحديث الحدث",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { DaySchedule, TimeSlot } from "../../shared/api";
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_TIME_SLOT: TimeSlot = {
|
|
4
|
+
start: "09:00",
|
|
5
|
+
end: "17:00",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function getEditableTimeSlots(day: DaySchedule): TimeSlot[] {
|
|
9
|
+
return day.slots.length > 0 ? day.slots : [{ ...DEFAULT_TIME_SLOT }];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function setDayEnabled(day: DaySchedule, enabled: boolean): DaySchedule {
|
|
13
|
+
return {
|
|
14
|
+
...day,
|
|
15
|
+
enabled,
|
|
16
|
+
slots: enabled ? getEditableTimeSlots(day) : day.slots,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function updateTimeSlot(
|
|
21
|
+
day: DaySchedule,
|
|
22
|
+
slotIndex: number,
|
|
23
|
+
field: "start" | "end",
|
|
24
|
+
value: string,
|
|
25
|
+
): DaySchedule {
|
|
26
|
+
return {
|
|
27
|
+
...day,
|
|
28
|
+
slots: getEditableTimeSlots(day).map((slot, index) =>
|
|
29
|
+
index === slotIndex ? { ...slot, [field]: value } : slot,
|
|
30
|
+
),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function addTimeSlot(day: DaySchedule): DaySchedule {
|
|
35
|
+
return {
|
|
36
|
+
...day,
|
|
37
|
+
slots: [...getEditableTimeSlots(day), { ...DEFAULT_TIME_SLOT }],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function removeTimeSlot(
|
|
42
|
+
day: DaySchedule,
|
|
43
|
+
slotIndex: number,
|
|
44
|
+
): DaySchedule {
|
|
45
|
+
const slots = getEditableTimeSlots(day).filter(
|
|
46
|
+
(_, index) => index !== slotIndex,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
...day,
|
|
51
|
+
slots: slots.length > 0 ? slots : [{ ...DEFAULT_TIME_SLOT }],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -230,6 +230,11 @@ export function getLocalTimezone() {
|
|
|
230
230
|
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
/** New event drafts follow the viewer's browser zone unless they name one. */
|
|
234
|
+
export function resolveEventTimezone(timezone?: string | null) {
|
|
235
|
+
return timezone?.trim() || getLocalTimezone();
|
|
236
|
+
}
|
|
237
|
+
|
|
233
238
|
function getTimezoneOffsetMs(date: Date, timezone: string) {
|
|
234
239
|
const parts = new Intl.DateTimeFormat("en-US", {
|
|
235
240
|
timeZone: timezone,
|