@agent-native/core 0.154.5 → 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/actions/add-comment.ts +2 -1
- package/corpus/templates/content/actions/update-comment.ts +1 -1
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
- 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/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/awareness.d.ts +2 -2
- 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.d.ts +1 -1
- package/dist/index.js +1 -1
- 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/observability/routes.d.ts +6 -6
- 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/secrets/routes.d.ts +3 -3
- package/dist/server/action-routes.js +32 -6
- package/dist/server/agent-discovery.js +14 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- 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,5 +1,6 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
2
|
import type { AvailabilityConfig, DaySchedule } from "@shared/api";
|
|
3
|
+
import { IconPlus, IconTrash } from "@tabler/icons-react";
|
|
3
4
|
import { useState, useEffect } from "react";
|
|
4
5
|
import { toast } from "sonner";
|
|
5
6
|
|
|
@@ -21,6 +22,14 @@ import {
|
|
|
21
22
|
useUpdateAvailability,
|
|
22
23
|
} from "@/hooks/use-availability";
|
|
23
24
|
import { useDbStatus } from "@/hooks/use-db-status";
|
|
25
|
+
import {
|
|
26
|
+
DEFAULT_TIME_SLOT,
|
|
27
|
+
addTimeSlot,
|
|
28
|
+
getEditableTimeSlots,
|
|
29
|
+
removeTimeSlot,
|
|
30
|
+
setDayEnabled,
|
|
31
|
+
updateTimeSlot,
|
|
32
|
+
} from "@/lib/availability-schedule";
|
|
24
33
|
import { copyTextToClipboard } from "@/lib/clipboard";
|
|
25
34
|
|
|
26
35
|
type DayName = keyof AvailabilityConfig["weeklySchedule"];
|
|
@@ -37,7 +46,7 @@ const DAYS: { key: DayName }[] = [
|
|
|
37
46
|
|
|
38
47
|
const DEFAULT_SCHEDULE: DaySchedule = {
|
|
39
48
|
enabled: false,
|
|
40
|
-
slots: [{
|
|
49
|
+
slots: [{ ...DEFAULT_TIME_SLOT }],
|
|
41
50
|
};
|
|
42
51
|
|
|
43
52
|
export default function AvailabilitySettings() {
|
|
@@ -93,17 +102,36 @@ export default function AvailabilitySettings() {
|
|
|
93
102
|
function updateDay(day: DayName, updates: Partial<DaySchedule>) {
|
|
94
103
|
setSchedule((prev) => ({
|
|
95
104
|
...prev,
|
|
96
|
-
[day]:
|
|
105
|
+
[day]:
|
|
106
|
+
typeof updates.enabled === "boolean"
|
|
107
|
+
? setDayEnabled(prev[day], updates.enabled)
|
|
108
|
+
: { ...prev[day], ...updates },
|
|
97
109
|
}));
|
|
98
110
|
}
|
|
99
111
|
|
|
100
|
-
function updateDaySlot(
|
|
112
|
+
function updateDaySlot(
|
|
113
|
+
day: DayName,
|
|
114
|
+
slotIndex: number,
|
|
115
|
+
field: "start" | "end",
|
|
116
|
+
value: string,
|
|
117
|
+
) {
|
|
101
118
|
setSchedule((prev) => ({
|
|
102
119
|
...prev,
|
|
103
|
-
[day]:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
120
|
+
[day]: updateTimeSlot(prev[day], slotIndex, field, value),
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function addDaySlot(day: DayName) {
|
|
125
|
+
setSchedule((prev) => ({
|
|
126
|
+
...prev,
|
|
127
|
+
[day]: addTimeSlot(prev[day]),
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function removeDaySlot(day: DayName, slotIndex: number) {
|
|
132
|
+
setSchedule((prev) => ({
|
|
133
|
+
...prev,
|
|
134
|
+
[day]: removeTimeSlot(prev[day], slotIndex),
|
|
107
135
|
}));
|
|
108
136
|
}
|
|
109
137
|
|
|
@@ -181,7 +209,7 @@ export default function AvailabilitySettings() {
|
|
|
181
209
|
</div>
|
|
182
210
|
{DAYS.map(({ key }) => {
|
|
183
211
|
const day = schedule[key];
|
|
184
|
-
const
|
|
212
|
+
const slots = getEditableTimeSlots(day);
|
|
185
213
|
return (
|
|
186
214
|
<div
|
|
187
215
|
key={key}
|
|
@@ -198,26 +226,60 @@ export default function AvailabilitySettings() {
|
|
|
198
226
|
</div>
|
|
199
227
|
|
|
200
228
|
{day.enabled ? (
|
|
201
|
-
<div className="flex
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
229
|
+
<div className="min-w-0 flex-1 space-y-2">
|
|
230
|
+
{slots.map((slot, slotIndex) => (
|
|
231
|
+
<div
|
|
232
|
+
key={`${key}-${slotIndex}`}
|
|
233
|
+
className="flex flex-wrap items-center gap-2"
|
|
234
|
+
>
|
|
235
|
+
<Input
|
|
236
|
+
type="time"
|
|
237
|
+
value={slot.start}
|
|
238
|
+
onChange={(e) =>
|
|
239
|
+
updateDaySlot(
|
|
240
|
+
key,
|
|
241
|
+
slotIndex,
|
|
242
|
+
"start",
|
|
243
|
+
e.target.value,
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
className="w-28 sm:w-32"
|
|
247
|
+
/>
|
|
248
|
+
<span className="text-muted-foreground">
|
|
249
|
+
{t("bookingLinks.to")}
|
|
250
|
+
</span>
|
|
251
|
+
<Input
|
|
252
|
+
type="time"
|
|
253
|
+
value={slot.end}
|
|
254
|
+
onChange={(e) =>
|
|
255
|
+
updateDaySlot(key, slotIndex, "end", e.target.value)
|
|
256
|
+
}
|
|
257
|
+
className="w-28 sm:w-32"
|
|
258
|
+
/>
|
|
259
|
+
{slots.length > 1 && (
|
|
260
|
+
<Button
|
|
261
|
+
type="button"
|
|
262
|
+
variant="ghost"
|
|
263
|
+
size="sm"
|
|
264
|
+
className="h-8 px-2 text-muted-foreground hover:text-destructive"
|
|
265
|
+
onClick={() => removeDaySlot(key, slotIndex)}
|
|
266
|
+
>
|
|
267
|
+
<IconTrash className="mr-1.5 h-3.5 w-3.5" />
|
|
268
|
+
{t("eventForm.delete")}
|
|
269
|
+
</Button>
|
|
270
|
+
)}
|
|
271
|
+
</div>
|
|
272
|
+
))}
|
|
273
|
+
<Button
|
|
274
|
+
type="button"
|
|
275
|
+
variant="ghost"
|
|
276
|
+
size="sm"
|
|
277
|
+
className="h-8 px-2"
|
|
278
|
+
onClick={() => addDaySlot(key)}
|
|
279
|
+
>
|
|
280
|
+
<IconPlus className="mr-1.5 h-3.5 w-3.5" />
|
|
281
|
+
{t("bookingLinks.add")}
|
|
282
|
+
</Button>
|
|
221
283
|
</div>
|
|
222
284
|
) : (
|
|
223
285
|
<span className="text-sm text-muted-foreground">
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
CustomField,
|
|
15
15
|
DaySchedule,
|
|
16
16
|
} from "@shared/api";
|
|
17
|
+
import { getWeekdayOrder, getWeekStartsOn } from "@shared/calendar-week";
|
|
17
18
|
import {
|
|
18
19
|
IconBrandGoogle,
|
|
19
20
|
IconBrandZoom,
|
|
@@ -113,7 +114,16 @@ import {
|
|
|
113
114
|
OPTIMISTIC_PREFIX,
|
|
114
115
|
} from "@/hooks/use-booking-links";
|
|
115
116
|
import { useGoogleAuthStatus } from "@/hooks/use-google-auth";
|
|
117
|
+
import { useSettings } from "@/hooks/use-settings";
|
|
116
118
|
import { useZoomStatus, useConnectZoom } from "@/hooks/use-zoom-auth";
|
|
119
|
+
import {
|
|
120
|
+
DEFAULT_TIME_SLOT,
|
|
121
|
+
addTimeSlot,
|
|
122
|
+
getEditableTimeSlots,
|
|
123
|
+
removeTimeSlot,
|
|
124
|
+
setDayEnabled,
|
|
125
|
+
updateTimeSlot,
|
|
126
|
+
} from "@/lib/availability-schedule";
|
|
117
127
|
import { copyTextToClipboard } from "@/lib/clipboard";
|
|
118
128
|
import { cn } from "@/lib/utils";
|
|
119
129
|
|
|
@@ -175,7 +185,7 @@ const DAYS: { key: DayName }[] = [
|
|
|
175
185
|
|
|
176
186
|
const DEFAULT_SCHEDULE: DaySchedule = {
|
|
177
187
|
enabled: false,
|
|
178
|
-
slots: [{
|
|
188
|
+
slots: [{ ...DEFAULT_TIME_SLOT }],
|
|
179
189
|
};
|
|
180
190
|
|
|
181
191
|
type Tab = "links" | "availability" | "bookings";
|
|
@@ -290,11 +300,12 @@ function formatAvailabilitySummary(
|
|
|
290
300
|
dayLabel = enabledDays.map((d) => shortNames[d]).join(", ");
|
|
291
301
|
}
|
|
292
302
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (!slot) return dayLabel;
|
|
303
|
+
const slots = ws[enabledDays[0]].slots;
|
|
304
|
+
if (slots.length === 0) return dayLabel;
|
|
296
305
|
|
|
297
|
-
return `${dayLabel}, ${
|
|
306
|
+
return `${dayLabel}, ${slots
|
|
307
|
+
.map((slot) => `${formatTime12(slot.start)} - ${formatTime12(slot.end)}`)
|
|
308
|
+
.join(", ")}`;
|
|
298
309
|
}
|
|
299
310
|
|
|
300
311
|
function BookingLinksListSkeleton() {
|
|
@@ -697,17 +708,36 @@ export default function BookingLinksPage({
|
|
|
697
708
|
function updateDay(day: DayName, updates: Partial<DaySchedule>) {
|
|
698
709
|
setSchedule((prev) => ({
|
|
699
710
|
...prev,
|
|
700
|
-
[day]:
|
|
711
|
+
[day]:
|
|
712
|
+
typeof updates.enabled === "boolean"
|
|
713
|
+
? setDayEnabled(prev[day], updates.enabled)
|
|
714
|
+
: { ...prev[day], ...updates },
|
|
701
715
|
}));
|
|
702
716
|
}
|
|
703
717
|
|
|
704
|
-
function updateDaySlot(
|
|
718
|
+
function updateDaySlot(
|
|
719
|
+
day: DayName,
|
|
720
|
+
slotIndex: number,
|
|
721
|
+
field: "start" | "end",
|
|
722
|
+
value: string,
|
|
723
|
+
) {
|
|
705
724
|
setSchedule((prev) => ({
|
|
706
725
|
...prev,
|
|
707
|
-
[day]:
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
726
|
+
[day]: updateTimeSlot(prev[day], slotIndex, field, value),
|
|
727
|
+
}));
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function addDaySlot(day: DayName) {
|
|
731
|
+
setSchedule((prev) => ({
|
|
732
|
+
...prev,
|
|
733
|
+
[day]: addTimeSlot(prev[day]),
|
|
734
|
+
}));
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function removeDaySlot(day: DayName, slotIndex: number) {
|
|
738
|
+
setSchedule((prev) => ({
|
|
739
|
+
...prev,
|
|
740
|
+
[day]: removeTimeSlot(prev[day], slotIndex),
|
|
711
741
|
}));
|
|
712
742
|
}
|
|
713
743
|
|
|
@@ -1756,7 +1786,7 @@ export default function BookingLinksPage({
|
|
|
1756
1786
|
</div>
|
|
1757
1787
|
{DAYS.map(({ key }) => {
|
|
1758
1788
|
const day = schedule[key];
|
|
1759
|
-
const
|
|
1789
|
+
const slots = getEditableTimeSlots(day);
|
|
1760
1790
|
const label = t(`bookingLinks.days.${key}`);
|
|
1761
1791
|
const short = t(`bookingLinks.days.${key}Short`);
|
|
1762
1792
|
return (
|
|
@@ -1778,26 +1808,65 @@ export default function BookingLinksPage({
|
|
|
1778
1808
|
</div>
|
|
1779
1809
|
|
|
1780
1810
|
{day.enabled ? (
|
|
1781
|
-
<div className="flex
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1811
|
+
<div className="min-w-0 flex-1 space-y-2">
|
|
1812
|
+
{slots.map((slot, slotIndex) => (
|
|
1813
|
+
<div
|
|
1814
|
+
key={`${key}-${slotIndex}`}
|
|
1815
|
+
className="flex flex-wrap items-center gap-2"
|
|
1816
|
+
>
|
|
1817
|
+
<Input
|
|
1818
|
+
type="time"
|
|
1819
|
+
value={slot.start}
|
|
1820
|
+
onChange={(e) =>
|
|
1821
|
+
updateDaySlot(
|
|
1822
|
+
key,
|
|
1823
|
+
slotIndex,
|
|
1824
|
+
"start",
|
|
1825
|
+
e.target.value,
|
|
1826
|
+
)
|
|
1827
|
+
}
|
|
1828
|
+
className="w-28 sm:w-32"
|
|
1829
|
+
/>
|
|
1830
|
+
<span className="text-muted-foreground">
|
|
1831
|
+
{t("bookingLinks.to")}
|
|
1832
|
+
</span>
|
|
1833
|
+
<Input
|
|
1834
|
+
type="time"
|
|
1835
|
+
value={slot.end}
|
|
1836
|
+
onChange={(e) =>
|
|
1837
|
+
updateDaySlot(
|
|
1838
|
+
key,
|
|
1839
|
+
slotIndex,
|
|
1840
|
+
"end",
|
|
1841
|
+
e.target.value,
|
|
1842
|
+
)
|
|
1843
|
+
}
|
|
1844
|
+
className="w-28 sm:w-32"
|
|
1845
|
+
/>
|
|
1846
|
+
{slots.length > 1 && (
|
|
1847
|
+
<Button
|
|
1848
|
+
type="button"
|
|
1849
|
+
variant="ghost"
|
|
1850
|
+
size="sm"
|
|
1851
|
+
className="h-8 px-2 text-muted-foreground hover:text-destructive"
|
|
1852
|
+
onClick={() => removeDaySlot(key, slotIndex)}
|
|
1853
|
+
>
|
|
1854
|
+
<IconTrash className="mr-1.5 h-3.5 w-3.5" />
|
|
1855
|
+
{t("eventForm.delete")}
|
|
1856
|
+
</Button>
|
|
1857
|
+
)}
|
|
1858
|
+
</div>
|
|
1859
|
+
))}
|
|
1860
|
+
<Button
|
|
1861
|
+
type="button"
|
|
1862
|
+
variant="ghost"
|
|
1863
|
+
size="sm"
|
|
1864
|
+
className="h-8 px-2"
|
|
1865
|
+
onClick={() => addDaySlot(key)}
|
|
1866
|
+
>
|
|
1867
|
+
<IconPlus className="mr-1.5 h-3.5 w-3.5" />
|
|
1868
|
+
{t("bookingLinks.add")}
|
|
1869
|
+
</Button>
|
|
1801
1870
|
</div>
|
|
1802
1871
|
) : (
|
|
1803
1872
|
<span className="text-sm text-muted-foreground">
|
|
@@ -1971,6 +2040,8 @@ function BookingPreview({
|
|
|
1971
2040
|
onCollapse?: () => void;
|
|
1972
2041
|
}) {
|
|
1973
2042
|
const t = useT();
|
|
2043
|
+
const { data: settings } = useSettings();
|
|
2044
|
+
const weekStartsOn = getWeekStartsOn(settings?.weekStart);
|
|
1974
2045
|
const displayTitle = title.trim() || t("bookingLinks.untitledMeeting");
|
|
1975
2046
|
const hasDurationChoice = durations.length > 1;
|
|
1976
2047
|
const primaryDuration = durations[0] ?? 30;
|
|
@@ -2005,8 +2076,8 @@ function BookingPreview({
|
|
|
2005
2076
|
// Calendar data for viewed month
|
|
2006
2077
|
const monthStart = startOfMonth(viewMonth);
|
|
2007
2078
|
const monthEnd = endOfMonth(viewMonth);
|
|
2008
|
-
const calStart = startOfWeek(monthStart);
|
|
2009
|
-
const calEnd = endOfWeek(monthEnd);
|
|
2079
|
+
const calStart = startOfWeek(monthStart, { weekStartsOn });
|
|
2080
|
+
const calEnd = endOfWeek(monthEnd, { weekStartsOn });
|
|
2010
2081
|
const calDays = eachDayOfInterval({ start: calStart, end: calEnd });
|
|
2011
2082
|
|
|
2012
2083
|
function isDayDisabled(day: Date) {
|
|
@@ -2027,28 +2098,28 @@ function BookingPreview({
|
|
|
2027
2098
|
const dayName = DAY_MAP[getDay(selectedDate)];
|
|
2028
2099
|
const daySchedule = availability.weeklySchedule[dayName];
|
|
2029
2100
|
if (!daySchedule?.enabled) return [];
|
|
2030
|
-
const slot = daySchedule.slots[0];
|
|
2031
|
-
if (!slot) return [];
|
|
2032
2101
|
|
|
2033
2102
|
const dur = selectedDuration ?? primaryDuration;
|
|
2034
|
-
const [startH, startM] = slot.start.split(":").map(Number);
|
|
2035
|
-
const [endH, endM] = slot.end.split(":").map(Number);
|
|
2036
|
-
const startMin = startH * 60 + startM;
|
|
2037
|
-
const endMin = endH * 60 + endM;
|
|
2038
2103
|
const slots: string[] = [];
|
|
2039
|
-
const
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2104
|
+
for (const slot of daySchedule.slots) {
|
|
2105
|
+
const [startH, startM] = slot.start.split(":").map(Number);
|
|
2106
|
+
const [endH, endM] = slot.end.split(":").map(Number);
|
|
2107
|
+
const startMin = startH * 60 + startM;
|
|
2108
|
+
const endMin = endH * 60 + endM;
|
|
2109
|
+
const firstStart =
|
|
2110
|
+
Math.ceil(startMin / BOOKING_SLOT_STEP_MINUTES) *
|
|
2111
|
+
BOOKING_SLOT_STEP_MINUTES;
|
|
2112
|
+
for (
|
|
2113
|
+
let m = firstStart;
|
|
2114
|
+
m + dur <= endMin;
|
|
2115
|
+
m += BOOKING_SLOT_STEP_MINUTES
|
|
2116
|
+
) {
|
|
2117
|
+
const h = Math.floor(m / 60);
|
|
2118
|
+
const mm = m % 60;
|
|
2119
|
+
const ampm = h >= 12 ? "PM" : "AM";
|
|
2120
|
+
const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
|
|
2121
|
+
slots.push(`${h12}:${mm.toString().padStart(2, "0")} ${ampm}`);
|
|
2122
|
+
}
|
|
2052
2123
|
}
|
|
2053
2124
|
return slots;
|
|
2054
2125
|
}, [selectedDate, selectedDuration, primaryDuration, availability]);
|
|
@@ -2307,14 +2378,17 @@ function BookingPreview({
|
|
|
2307
2378
|
|
|
2308
2379
|
{/* Weekday headers */}
|
|
2309
2380
|
<div className="grid grid-cols-7 mb-0.5">
|
|
2310
|
-
{
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2381
|
+
{getWeekdayOrder(weekStartsOn).map((day) => {
|
|
2382
|
+
const dayKey = WEEKDAY_HEADER_KEYS[day];
|
|
2383
|
+
return (
|
|
2384
|
+
<div
|
|
2385
|
+
key={dayKey}
|
|
2386
|
+
className="py-0.5 text-center text-[10px] font-medium text-muted-foreground/60"
|
|
2387
|
+
>
|
|
2388
|
+
{t(`bookingLinks.days.${dayKey}`)}
|
|
2389
|
+
</div>
|
|
2390
|
+
);
|
|
2391
|
+
})}
|
|
2318
2392
|
</div>
|
|
2319
2393
|
|
|
2320
2394
|
{/* Days grid */}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
StarfieldBackground,
|
|
6
6
|
} from "@agent-native/core/client/ui";
|
|
7
7
|
import type { Booking } from "@shared/api";
|
|
8
|
+
import { getWeekStartsOn } from "@shared/calendar-week";
|
|
8
9
|
import { IconAlertTriangle, IconCalendar } from "@tabler/icons-react";
|
|
9
10
|
import {
|
|
10
11
|
addMinutes,
|
|
@@ -432,6 +433,7 @@ export default function BookingPage() {
|
|
|
432
433
|
availabilityLoading={availableDatesLoading}
|
|
433
434
|
viewMonth={viewMonth}
|
|
434
435
|
onViewMonthChange={setViewMonth}
|
|
436
|
+
weekStartsOn={getWeekStartsOn(settings?.weekStart)}
|
|
435
437
|
/>
|
|
436
438
|
</div>
|
|
437
439
|
)}
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
CalendarEventDraft,
|
|
7
7
|
UpdateEventScope,
|
|
8
8
|
} from "@shared/api";
|
|
9
|
+
import { getWeekStartsOn } from "@shared/calendar-week";
|
|
9
10
|
import {
|
|
10
11
|
IconCheck,
|
|
11
12
|
IconChevronLeft,
|
|
@@ -89,8 +90,8 @@ import {
|
|
|
89
90
|
buildEventTitleUpdate,
|
|
90
91
|
dateTimeInTimezoneToIso,
|
|
91
92
|
getEditableEventTitle,
|
|
92
|
-
getLocalTimezone,
|
|
93
93
|
UNNAMED_EVENT_TITLE,
|
|
94
|
+
resolveEventTimezone,
|
|
94
95
|
} from "@/lib/event-form-utils";
|
|
95
96
|
import { buildDeleteEventMutationInput } from "@/lib/event-mutation-inputs";
|
|
96
97
|
import { getLocationSuggestions } from "@/lib/location-suggestions";
|
|
@@ -373,6 +374,7 @@ export default function CalendarView() {
|
|
|
373
374
|
const defaultAccountEmail = googleStatus.data?.accounts?.[0]?.email;
|
|
374
375
|
const settingsQuery = useSettings();
|
|
375
376
|
const { data: settings } = settingsQuery;
|
|
377
|
+
const weekStartsOn = getWeekStartsOn(settings?.weekStart);
|
|
376
378
|
const { data: rawOverlayPeople } = useOverlayPeople();
|
|
377
379
|
const overlayPeople = Array.isArray(rawOverlayPeople) ? rawOverlayPeople : [];
|
|
378
380
|
const overlayEmails = useMemo(
|
|
@@ -398,14 +400,14 @@ export default function CalendarView() {
|
|
|
398
400
|
const ms = startOfMonth(selectedDate);
|
|
399
401
|
const me = endOfMonth(selectedDate);
|
|
400
402
|
return {
|
|
401
|
-
from: startOfWeek(ms).toISOString(),
|
|
402
|
-
to: endOfWeek(me).toISOString(),
|
|
403
|
+
from: startOfWeek(ms, { weekStartsOn }).toISOString(),
|
|
404
|
+
to: endOfWeek(me, { weekStartsOn }).toISOString(),
|
|
403
405
|
};
|
|
404
406
|
}
|
|
405
407
|
case "week": {
|
|
406
408
|
return {
|
|
407
|
-
from: startOfWeek(selectedDate).toISOString(),
|
|
408
|
-
to: endOfWeek(selectedDate).toISOString(),
|
|
409
|
+
from: startOfWeek(selectedDate, { weekStartsOn }).toISOString(),
|
|
410
|
+
to: endOfWeek(selectedDate, { weekStartsOn }).toISOString(),
|
|
409
411
|
};
|
|
410
412
|
}
|
|
411
413
|
case "day": {
|
|
@@ -416,7 +418,7 @@ export default function CalendarView() {
|
|
|
416
418
|
return { from: dayStart.toISOString(), to: dayEnd.toISOString() };
|
|
417
419
|
}
|
|
418
420
|
}
|
|
419
|
-
}, [viewMode, selectedDate]);
|
|
421
|
+
}, [viewMode, selectedDate, weekStartsOn]);
|
|
420
422
|
|
|
421
423
|
const {
|
|
422
424
|
data: rawEventsData,
|
|
@@ -470,8 +472,8 @@ export default function CalendarView() {
|
|
|
470
472
|
const next = addMonths(selectedDate, 1);
|
|
471
473
|
const prev = subMonths(selectedDate, 1);
|
|
472
474
|
return [next, prev].map((d) => ({
|
|
473
|
-
from: startOfWeek(startOfMonth(d)).toISOString(),
|
|
474
|
-
to: endOfWeek(endOfMonth(d)).toISOString(),
|
|
475
|
+
from: startOfWeek(startOfMonth(d), { weekStartsOn }).toISOString(),
|
|
476
|
+
to: endOfWeek(endOfMonth(d), { weekStartsOn }).toISOString(),
|
|
475
477
|
}));
|
|
476
478
|
}
|
|
477
479
|
case "week": {
|
|
@@ -480,8 +482,8 @@ export default function CalendarView() {
|
|
|
480
482
|
const next2 = addWeeks(selectedDate, 2);
|
|
481
483
|
const prev = subWeeks(selectedDate, 1);
|
|
482
484
|
return [next, next2, prev].map((d) => ({
|
|
483
|
-
from: startOfWeek(d).toISOString(),
|
|
484
|
-
to: endOfWeek(d).toISOString(),
|
|
485
|
+
from: startOfWeek(d, { weekStartsOn }).toISOString(),
|
|
486
|
+
to: endOfWeek(d, { weekStartsOn }).toISOString(),
|
|
485
487
|
}));
|
|
486
488
|
}
|
|
487
489
|
case "day": {
|
|
@@ -500,7 +502,14 @@ export default function CalendarView() {
|
|
|
500
502
|
for (const range of ranges) {
|
|
501
503
|
void prefetchEvents(queryClient, range.from, range.to, overlayEmails);
|
|
502
504
|
}
|
|
503
|
-
}, [
|
|
505
|
+
}, [
|
|
506
|
+
isLoading,
|
|
507
|
+
viewMode,
|
|
508
|
+
selectedDate,
|
|
509
|
+
overlayEmails,
|
|
510
|
+
queryClient,
|
|
511
|
+
weekStartsOn,
|
|
512
|
+
]);
|
|
504
513
|
|
|
505
514
|
// Show the skeleton only when there is genuinely nothing to show for the
|
|
506
515
|
// current date range — the first load, or navigating to a range we have not
|
|
@@ -675,7 +684,9 @@ export default function CalendarView() {
|
|
|
675
684
|
}
|
|
676
685
|
|
|
677
686
|
const location = draft.location ?? draft.workingLocationLabel ?? "";
|
|
678
|
-
const timezone =
|
|
687
|
+
const timezone = resolveEventTimezone(
|
|
688
|
+
draft.startTimeZone ?? draft.endTimeZone,
|
|
689
|
+
);
|
|
679
690
|
const semanticFullDay =
|
|
680
691
|
eventType === "outOfOffice" &&
|
|
681
692
|
draft.fullDay === true &&
|
|
@@ -1143,7 +1154,9 @@ export default function CalendarView() {
|
|
|
1143
1154
|
if (newEnd.getTime() <= newStart.getTime()) return;
|
|
1144
1155
|
|
|
1145
1156
|
if (calendarDraftIdFromEventId(eventId)) {
|
|
1146
|
-
const timezone =
|
|
1157
|
+
const timezone = resolveEventTimezone(
|
|
1158
|
+
event.startTimeZone ?? event.endTimeZone,
|
|
1159
|
+
);
|
|
1147
1160
|
updateDraftEvent(eventId, {
|
|
1148
1161
|
start: newStart.toISOString(),
|
|
1149
1162
|
end: newEnd.toISOString(),
|
|
@@ -1592,8 +1605,8 @@ export default function CalendarView() {
|
|
|
1592
1605
|
? format(selectedDate, "MMM yyyy")
|
|
1593
1606
|
: format(selectedDate, "MMMM yyyy");
|
|
1594
1607
|
case "week": {
|
|
1595
|
-
const ws = startOfWeek(selectedDate);
|
|
1596
|
-
const we = endOfWeek(selectedDate);
|
|
1608
|
+
const ws = startOfWeek(selectedDate, { weekStartsOn });
|
|
1609
|
+
const we = endOfWeek(selectedDate, { weekStartsOn });
|
|
1597
1610
|
return isMobile
|
|
1598
1611
|
? `${format(ws, "MMM d")} – ${format(we, "d")}`
|
|
1599
1612
|
: `${format(ws, "MMM d")} – ${format(we, "d, yyyy")}`;
|
|
@@ -1801,6 +1814,7 @@ export default function CalendarView() {
|
|
|
1801
1814
|
onDraftCreate={createDraftEvent}
|
|
1802
1815
|
onDraftDiscard={discardDraftEvent}
|
|
1803
1816
|
isLoading={eventsLoading}
|
|
1817
|
+
weekStartsOn={weekStartsOn}
|
|
1804
1818
|
/>
|
|
1805
1819
|
)}
|
|
1806
1820
|
{viewMode === "week" && (
|
|
@@ -1819,6 +1833,7 @@ export default function CalendarView() {
|
|
|
1819
1833
|
onDraftCreate={createDraftEvent}
|
|
1820
1834
|
onDraftDiscard={discardDraftEvent}
|
|
1821
1835
|
isLoading={eventsLoading}
|
|
1836
|
+
weekStartsOn={weekStartsOn}
|
|
1822
1837
|
/>
|
|
1823
1838
|
)}
|
|
1824
1839
|
{viewMode === "day" && (
|