@agent-native/scheduling 0.1.11 → 0.1.13

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.
Files changed (41) hide show
  1. package/dist/react/components/SlotPicker.d.ts.map +1 -1
  2. package/dist/react/components/SlotPicker.js +3 -1
  3. package/dist/react/components/SlotPicker.js.map +1 -1
  4. package/dist/react/components/booking-links/AvailabilityEditor.d.ts +2 -1
  5. package/dist/react/components/booking-links/AvailabilityEditor.d.ts.map +1 -1
  6. package/dist/react/components/booking-links/AvailabilityEditor.js +28 -22
  7. package/dist/react/components/booking-links/AvailabilityEditor.js.map +1 -1
  8. package/dist/react/components/booking-links/BookingLinkCreateDialog.d.ts.map +1 -1
  9. package/dist/react/components/booking-links/BookingLinkCreateDialog.js +6 -4
  10. package/dist/react/components/booking-links/BookingLinkCreateDialog.js.map +1 -1
  11. package/dist/react/components/booking-links/ConferencingSelector.d.ts.map +1 -1
  12. package/dist/react/components/booking-links/ConferencingSelector.js +25 -19
  13. package/dist/react/components/booking-links/ConferencingSelector.js.map +1 -1
  14. package/dist/react/components/booking-links/CustomFieldsEditor.d.ts.map +1 -1
  15. package/dist/react/components/booking-links/CustomFieldsEditor.js +39 -25
  16. package/dist/react/components/booking-links/CustomFieldsEditor.js.map +1 -1
  17. package/dist/react/components/booking-links/DurationPicker.d.ts.map +1 -1
  18. package/dist/react/components/booking-links/DurationPicker.js +6 -2
  19. package/dist/react/components/booking-links/DurationPicker.js.map +1 -1
  20. package/dist/react/components/booking-links/SlugEditor.d.ts.map +1 -1
  21. package/dist/react/components/booking-links/SlugEditor.js +5 -3
  22. package/dist/react/components/booking-links/SlugEditor.js.map +1 -1
  23. package/dist/react/i18n.d.ts +109 -0
  24. package/dist/react/i18n.d.ts.map +1 -0
  25. package/dist/react/i18n.js +976 -0
  26. package/dist/react/i18n.js.map +1 -0
  27. package/dist/schema/bookings.d.ts +12 -12
  28. package/dist/schema/event-types.d.ts +12 -12
  29. package/dist/schema/routing-forms.d.ts +12 -12
  30. package/dist/schema/schedules.d.ts +12 -12
  31. package/dist/schema/teams.d.ts +12 -12
  32. package/dist/schema/workflows.d.ts +15 -15
  33. package/package.json +10 -11
  34. package/src/react/components/SlotPicker.tsx +6 -1
  35. package/src/react/components/booking-links/AvailabilityEditor.tsx +48 -27
  36. package/src/react/components/booking-links/BookingLinkCreateDialog.tsx +17 -15
  37. package/src/react/components/booking-links/ConferencingSelector.tsx +36 -31
  38. package/src/react/components/booking-links/CustomFieldsEditor.tsx +71 -52
  39. package/src/react/components/booking-links/DurationPicker.tsx +8 -4
  40. package/src/react/components/booking-links/SlugEditor.tsx +7 -5
  41. package/src/react/i18n.ts +1050 -0
@@ -27,6 +27,7 @@ import { Input } from "@/components/ui/input";
27
27
  import { Label } from "@/components/ui/label";
28
28
  import { Textarea } from "@/components/ui/textarea";
29
29
  import { Switch } from "@/components/ui/switch";
30
+ import { useSchedulingT } from "../../i18n.js";
30
31
 
31
32
  export type CustomFieldType =
32
33
  | "text"
@@ -55,33 +56,37 @@ export interface CustomFieldsEditorProps {
55
56
  hideLabel?: boolean;
56
57
  }
57
58
 
58
- const TYPE_LABELS: Record<CustomFieldType, string> = {
59
- text: "Short text",
60
- email: "Email",
61
- url: "URL",
62
- tel: "Phone",
63
- textarea: "Long text",
64
- select: "Dropdown",
65
- checkbox: "Checkbox",
59
+ const TYPE_LABEL_KEYS: Record<
60
+ CustomFieldType,
61
+ Parameters<ReturnType<typeof useSchedulingT>>[0]
62
+ > = {
63
+ text: "shortText",
64
+ email: "email",
65
+ url: "url",
66
+ tel: "phone",
67
+ textarea: "longText",
68
+ select: "dropdown",
69
+ checkbox: "checkbox",
66
70
  };
67
71
 
68
72
  const PRESETS: {
69
- label: string;
73
+ labelKey: Parameters<ReturnType<typeof useSchedulingT>>[0];
70
74
  type: CustomFieldType;
71
75
  placeholder?: string;
76
+ placeholderKey?: Parameters<ReturnType<typeof useSchedulingT>>[0];
72
77
  pattern?: string;
73
- patternError?: string;
78
+ patternErrorKey?: Parameters<ReturnType<typeof useSchedulingT>>[0];
74
79
  }[] = [
75
80
  {
76
- label: "LinkedIn Profile",
81
+ labelKey: "linkedInProfile",
77
82
  type: "url",
78
83
  placeholder: "https://linkedin.com/in/yourname",
79
84
  pattern: "^https?://(www\\.)?linkedin\\.com/in/.+",
80
- patternError: "Please enter a valid LinkedIn profile URL",
85
+ patternErrorKey: "linkedInUrlError",
81
86
  },
82
- { label: "Company", type: "text", placeholder: "Your company name" },
83
- { label: "Phone Number", type: "tel", placeholder: "+1 (555) 123-4567" },
84
- { label: "Website", type: "url", placeholder: "https://example.com" },
87
+ { labelKey: "company", type: "text", placeholderKey: "companyPlaceholder" },
88
+ { labelKey: "phoneNumber", type: "tel", placeholder: "+1 (555) 123-4567" },
89
+ { labelKey: "website", type: "url", placeholder: "https://example.com" },
85
90
  ];
86
91
 
87
92
  function cls(...parts: (string | false | null | undefined)[]) {
@@ -89,6 +94,7 @@ function cls(...parts: (string | false | null | undefined)[]) {
89
94
  }
90
95
 
91
96
  export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
97
+ const t = useSchedulingT();
92
98
  const { fields, onChange, hideLabel } = props;
93
99
  const [editingId, setEditingId] = useState<string | null>(null);
94
100
  const [showPresets, setShowPresets] = useState(false);
@@ -98,7 +104,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
98
104
  ) {
99
105
  const field: CustomField = {
100
106
  id: nanoid(8),
101
- label: partial?.label || "New Field",
107
+ label: partial?.label || t("newField"),
102
108
  type: partial?.type || "text",
103
109
  required: partial?.required ?? true,
104
110
  placeholder: partial?.placeholder,
@@ -136,7 +142,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
136
142
  <div className="flex items-center justify-between">
137
143
  <Label className="flex items-center gap-1.5">
138
144
  <IconListCheck className="h-4 w-4" />
139
- Custom fields
145
+ {t("customFields")}
140
146
  </Label>
141
147
  <div className="flex items-center gap-1">
142
148
  <button
@@ -150,7 +156,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
150
156
  showPresets && "rotate-180",
151
157
  )}
152
158
  />
153
- Presets
159
+ {t("presets")}
154
160
  </button>
155
161
  <button
156
162
  type="button"
@@ -158,7 +164,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
158
164
  className="flex items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-accent/60 hover:text-foreground"
159
165
  >
160
166
  <IconPlus className="h-3 w-3" />
161
- Add
167
+ {t("add")}
162
168
  </button>
163
169
  </div>
164
170
  </div>
@@ -168,14 +174,26 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
168
174
  <div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2">
169
175
  {PRESETS.map((preset) => (
170
176
  <button
171
- key={preset.label}
177
+ key={preset.labelKey}
172
178
  type="button"
173
- onClick={() => addField(preset)}
179
+ onClick={() =>
180
+ addField({
181
+ label: t(preset.labelKey),
182
+ type: preset.type,
183
+ placeholder: preset.placeholderKey
184
+ ? t(preset.placeholderKey)
185
+ : preset.placeholder,
186
+ pattern: preset.pattern,
187
+ patternError: preset.patternErrorKey
188
+ ? t(preset.patternErrorKey)
189
+ : undefined,
190
+ })
191
+ }
174
192
  className="rounded-lg border border-border/60 px-3 py-2 text-left text-xs hover:border-primary/30 hover:bg-accent/60"
175
193
  >
176
- <p className="font-medium">{preset.label}</p>
194
+ <p className="font-medium">{t(preset.labelKey)}</p>
177
195
  <p className="text-muted-foreground">
178
- {TYPE_LABELS[preset.type]}
196
+ {t(TYPE_LABEL_KEYS[preset.type])}
179
197
  </p>
180
198
  </button>
181
199
  ))}
@@ -184,8 +202,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
184
202
 
185
203
  {fields.length === 0 && !showPresets && (
186
204
  <p className="text-xs text-muted-foreground">
187
- Add custom fields to collect information from bookers — e.g. LinkedIn
188
- profile, company name, phone number.
205
+ {t("customFieldsEmpty")}
189
206
  </p>
190
207
  )}
191
208
 
@@ -215,9 +232,11 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
215
232
  {field.label}
216
233
  </span>
217
234
  <span className="text-[11px] text-muted-foreground">
218
- {TYPE_LABELS[field.type]}
219
- {field.required ? " · Required" : " · Optional"}
220
- {field.pattern ? " · Pattern" : ""}
235
+ {t(TYPE_LABEL_KEYS[field.type])}
236
+ {field.required
237
+ ? ` · ${t("required")}`
238
+ : ` · ${t("optional")}`}
239
+ {field.pattern ? ` · ${t("pattern")}` : ""}
221
240
  </span>
222
241
  </div>
223
242
  <div className="flex shrink-0 items-center gap-1">
@@ -228,7 +247,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
228
247
  moveField(field.id, -1);
229
248
  }}
230
249
  className="p-0.5 text-muted-foreground/40 hover:text-foreground"
231
- title="Move up"
250
+ title={t("moveUp")}
232
251
  >
233
252
  <IconChevronLeft className="h-3 w-3 rotate-90" />
234
253
  </button>
@@ -239,7 +258,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
239
258
  moveField(field.id, 1);
240
259
  }}
241
260
  className="p-0.5 text-muted-foreground/40 hover:text-foreground"
242
- title="Move down"
261
+ title={t("moveDown")}
243
262
  >
244
263
  <IconChevronRight className="h-3 w-3 rotate-90" />
245
264
  </button>
@@ -250,7 +269,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
250
269
  removeField(field.id);
251
270
  }}
252
271
  className="p-0.5 text-muted-foreground/40 hover:text-destructive"
253
- title="Remove field"
272
+ title={t("removeField")}
254
273
  >
255
274
  <IconTrash className="h-3 w-3" />
256
275
  </button>
@@ -261,18 +280,18 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
261
280
  <div className="space-y-3 border-t border-border bg-muted/20 px-3 py-3">
262
281
  <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
263
282
  <div className="space-y-1.5">
264
- <Label className="text-xs">Label</Label>
283
+ <Label className="text-xs">{t("label")}</Label>
265
284
  <Input
266
285
  value={field.label}
267
286
  onChange={(e) =>
268
287
  updateField(field.id, { label: e.target.value })
269
288
  }
270
- placeholder="Field label"
289
+ placeholder={t("fieldLabel")}
271
290
  className="h-8 text-sm"
272
291
  />
273
292
  </div>
274
293
  <div className="space-y-1.5">
275
- <Label className="text-xs">Type</Label>
294
+ <Label className="text-xs">{t("type")}</Label>
276
295
  <select
277
296
  value={field.type}
278
297
  onChange={(e) =>
@@ -283,13 +302,13 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
283
302
  className="h-8 w-full rounded-md border border-input bg-background px-2 text-sm"
284
303
  >
285
304
  {(
286
- Object.entries(TYPE_LABELS) as [
305
+ Object.entries(TYPE_LABEL_KEYS) as [
287
306
  CustomFieldType,
288
- string,
307
+ Parameters<ReturnType<typeof useSchedulingT>>[0],
289
308
  ][]
290
- ).map(([value, label]) => (
309
+ ).map(([value, labelKey]) => (
291
310
  <option key={value} value={value}>
292
- {label}
311
+ {t(labelKey)}
293
312
  </option>
294
313
  ))}
295
314
  </select>
@@ -297,7 +316,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
297
316
  </div>
298
317
 
299
318
  <div className="space-y-1.5">
300
- <Label className="text-xs">Placeholder</Label>
319
+ <Label className="text-xs">{t("placeholder")}</Label>
301
320
  <Input
302
321
  value={field.placeholder || ""}
303
322
  onChange={(e) =>
@@ -305,13 +324,13 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
305
324
  placeholder: e.target.value || undefined,
306
325
  })
307
326
  }
308
- placeholder="Placeholder text"
327
+ placeholder={t("placeholderText")}
309
328
  className="h-8 text-sm"
310
329
  />
311
330
  </div>
312
331
 
313
332
  <div className="flex items-center justify-between">
314
- <Label className="text-xs">Required</Label>
333
+ <Label className="text-xs">{t("required")}</Label>
315
334
  <Switch
316
335
  checked={field.required}
317
336
  onCheckedChange={(checked) =>
@@ -323,9 +342,9 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
323
342
  {field.type === "select" && (
324
343
  <div className="space-y-1.5">
325
344
  <Label className="text-xs">
326
- Options{" "}
345
+ {t("options")}{" "}
327
346
  <span className="font-normal text-muted-foreground">
328
- (one per line)
347
+ {t("onePerLine")}
329
348
  </span>
330
349
  </Label>
331
350
  <Textarea
@@ -336,7 +355,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
336
355
  .filter((o) => o.trim());
337
356
  updateField(field.id, { options });
338
357
  }}
339
- placeholder={"Option 1\nOption 2\nOption 3"}
358
+ placeholder={t("optionListPlaceholder")}
340
359
  rows={3}
341
360
  className="text-sm"
342
361
  />
@@ -346,9 +365,9 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
346
365
  {field.type !== "checkbox" && field.type !== "select" && (
347
366
  <div className="space-y-1.5">
348
367
  <Label className="text-xs">
349
- Validation pattern{" "}
368
+ {t("validationPattern")}{" "}
350
369
  <span className="font-normal text-muted-foreground">
351
- (regex, optional)
370
+ {t("regexOptional")}
352
371
  </span>
353
372
  </Label>
354
373
  <Input
@@ -358,15 +377,15 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
358
377
  pattern: e.target.value || undefined,
359
378
  })
360
379
  }
361
- placeholder="e.g. ^https?://(www\.)?linkedin\.com/in/.+"
380
+ placeholder={t("validationPatternPlaceholder")}
362
381
  className="h-8 font-mono text-sm"
363
382
  />
364
383
  {field.pattern && (
365
384
  <div className="space-y-1.5">
366
385
  <Label className="text-xs">
367
- Error message{" "}
386
+ {t("errorMessage")}{" "}
368
387
  <span className="font-normal text-muted-foreground">
369
- (shown when pattern doesn't match)
388
+ {t("errorMessageHint")}
370
389
  </span>
371
390
  </Label>
372
391
  <Input
@@ -376,7 +395,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
376
395
  patternError: e.target.value || undefined,
377
396
  })
378
397
  }
379
- placeholder="e.g. Please enter a valid LinkedIn URL"
398
+ placeholder={t("validationErrorPlaceholder")}
380
399
  className="h-8 text-sm"
381
400
  />
382
401
  </div>
@@ -403,7 +422,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
403
422
  showPresets && "rotate-180",
404
423
  )}
405
424
  />
406
- Presets
425
+ {t("presets")}
407
426
  </button>
408
427
  <button
409
428
  type="button"
@@ -411,7 +430,7 @@ export function CustomFieldsEditor(props: CustomFieldsEditorProps) {
411
430
  className="flex items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-accent/60 hover:text-foreground"
412
431
  >
413
432
  <IconPlus className="h-3 w-3" />
414
- Add field
433
+ {t("addField")}
415
434
  </button>
416
435
  </div>
417
436
  )}
@@ -11,6 +11,7 @@ import { useState } from "react";
11
11
  import { IconPlus, IconX } from "@tabler/icons-react";
12
12
  import { Button } from "@/components/ui/button";
13
13
  import { Input } from "@/components/ui/input";
14
+ import { useSchedulingT } from "../../i18n.js";
14
15
 
15
16
  const DEFAULT_PRESETS = [15, 30, 45, 60];
16
17
 
@@ -27,6 +28,7 @@ function cls(...parts: (string | false | null | undefined)[]) {
27
28
  }
28
29
 
29
30
  export function DurationPicker(props: DurationPickerProps) {
31
+ const t = useSchedulingT();
30
32
  const { value, onChange, presets = DEFAULT_PRESETS, min = 5 } = props;
31
33
  const [custom, setCustom] = useState(15);
32
34
 
@@ -73,7 +75,7 @@ export function DurationPicker(props: DurationPickerProps) {
73
75
  : "border-border text-muted-foreground hover:bg-accent/60 hover:text-foreground",
74
76
  )}
75
77
  >
76
- {mins} min
78
+ {mins} {t("minAbbrev")}
77
79
  {isSelected && isCustom && (
78
80
  <span
79
81
  role="button"
@@ -99,15 +101,17 @@ export function DurationPicker(props: DurationPickerProps) {
99
101
  onChange={(e) => setCustom(Number(e.currentTarget.value))}
100
102
  min={min}
101
103
  />
102
- <span className="text-sm text-muted-foreground">minutes</span>
104
+ <span className="text-sm text-muted-foreground">{t("minutes")}</span>
103
105
  <Button size="sm" variant="outline" onClick={add}>
104
106
  <IconPlus className="mr-1 h-3.5 w-3.5" />
105
- Add
107
+ {t("add")}
106
108
  </Button>
107
109
  </div>
108
110
  {value.length > 1 && (
109
111
  <p className="text-xs text-muted-foreground">
110
- Bookers will choose between: {value.map((d) => `${d} min`).join(", ")}
112
+ {t("bookersChooseBetween", {
113
+ durations: value.map((d) => `${d} ${t("minAbbrev")}`).join(", "),
114
+ })}
111
115
  </p>
112
116
  )}
113
117
  </div>
@@ -11,6 +11,7 @@
11
11
  */
12
12
  import { useState } from "react";
13
13
  import { Label } from "@/components/ui/label";
14
+ import { useSchedulingT } from "../../i18n.js";
14
15
 
15
16
  export interface SlugEditorProps {
16
17
  host: string;
@@ -39,6 +40,7 @@ function cls(...parts: (string | false | null | undefined)[]) {
39
40
  }
40
41
 
41
42
  export function SlugEditor(props: SlugEditorProps) {
43
+ const t = useSchedulingT();
42
44
  const {
43
45
  host,
44
46
  pathPrefix = "",
@@ -47,7 +49,7 @@ export function SlugEditor(props: SlugEditorProps) {
47
49
  onUsernameChange,
48
50
  onSlugChange,
49
51
  hideLabel,
50
- label = "URL",
52
+ label = t("url"),
51
53
  } = props;
52
54
 
53
55
  const [editing, setEditing] = useState<"username" | "slug" | null>(null);
@@ -108,9 +110,9 @@ export function SlugEditor(props: SlugEditorProps) {
108
110
  ? "text-foreground hover:bg-primary/10 hover:text-primary"
109
111
  : "text-primary/60 bg-primary/5 border border-dashed border-primary/30 hover:bg-primary/10",
110
112
  )}
111
- title="Click to edit username"
113
+ title={t("clickToEditUsername")}
112
114
  >
113
- {username || "your-name"}
115
+ {username || t("yourName")}
114
116
  </button>
115
117
  ) : (
116
118
  <span className="font-mono text-foreground">{username}</span>
@@ -138,9 +140,9 @@ export function SlugEditor(props: SlugEditorProps) {
138
140
  type="button"
139
141
  onClick={() => startEdit("slug")}
140
142
  className="inline rounded px-0.5 -mx-0.5 font-mono text-foreground hover:bg-primary/10 hover:text-primary"
141
- title="Click to edit slug"
143
+ title={t("clickToEditSlug")}
142
144
  >
143
- {slug || "meeting"}
145
+ {slug || t("meeting")}
144
146
  </button>
145
147
  )}
146
148
  </div>