@akira-io/ui 1.0.0 → 1.1.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.
Files changed (70) hide show
  1. package/README.md +15 -10
  2. package/dist/blocks.d.ts +159 -6
  3. package/dist/blocks.js +1388 -29
  4. package/dist/blocks.js.map +1 -1
  5. package/dist/button-B6wQRe7p.d.ts +16 -0
  6. package/dist/chunk-5AJFQ6C4.js +35 -0
  7. package/dist/chunk-5AJFQ6C4.js.map +1 -0
  8. package/dist/chunk-6QM5BA7B.js +148 -0
  9. package/dist/chunk-6QM5BA7B.js.map +1 -0
  10. package/dist/chunk-6X4TKSF6.js +21 -0
  11. package/dist/chunk-6X4TKSF6.js.map +1 -0
  12. package/dist/chunk-DSD7MVLL.js +287 -0
  13. package/dist/chunk-DSD7MVLL.js.map +1 -0
  14. package/dist/chunk-IQMRJXEO.js +59 -0
  15. package/dist/chunk-IQMRJXEO.js.map +1 -0
  16. package/dist/chunk-JAHTUAQL.js +82 -0
  17. package/dist/chunk-JAHTUAQL.js.map +1 -0
  18. package/dist/{chunk-A2SIZOEA.js → chunk-KYWA5UYN.js} +125 -45
  19. package/dist/chunk-KYWA5UYN.js.map +1 -0
  20. package/dist/chunk-L2H5GIF7.js +22 -0
  21. package/dist/chunk-L2H5GIF7.js.map +1 -0
  22. package/dist/chunk-LKSM3WLK.js +44 -0
  23. package/dist/chunk-LKSM3WLK.js.map +1 -0
  24. package/dist/{chunk-5MQWJVM2.js → chunk-MNGNBEYN.js} +168 -200
  25. package/dist/chunk-MNGNBEYN.js.map +1 -0
  26. package/dist/chunk-SA5LM2GD.js +102 -0
  27. package/dist/chunk-SA5LM2GD.js.map +1 -0
  28. package/dist/chunk-UUEWFCKT.js +28 -0
  29. package/dist/chunk-UUEWFCKT.js.map +1 -0
  30. package/dist/{chunk-42BJBH56.js → chunk-UUTXPPRN.js} +521 -107
  31. package/dist/chunk-UUTXPPRN.js.map +1 -0
  32. package/dist/{chunk-UXL33CZ4.js → chunk-YOVY3KT6.js} +13 -11
  33. package/dist/chunk-YOVY3KT6.js.map +1 -0
  34. package/dist/code.d.ts +6 -0
  35. package/dist/code.js +651 -0
  36. package/dist/code.js.map +1 -0
  37. package/dist/context-VgHq4U98.d.ts +384 -0
  38. package/dist/editor.d.ts +126 -0
  39. package/dist/editor.js +436 -0
  40. package/dist/editor.js.map +1 -0
  41. package/dist/index.d.ts +93 -17
  42. package/dist/index.js +996 -472
  43. package/dist/index.js.map +1 -1
  44. package/dist/inertia.d.ts +47 -2
  45. package/dist/inertia.js +138 -7
  46. package/dist/inertia.js.map +1 -1
  47. package/dist/input-4uxAHW6y.d.ts +5 -0
  48. package/dist/json-viewer-BTHBACj9.d.ts +50 -0
  49. package/dist/{language-Bft6rBBt.d.ts → language-Dgjr9bm3.d.ts} +1 -1
  50. package/dist/locales/pt.d.ts +26 -4
  51. package/dist/locales/pt.js +127 -1
  52. package/dist/locales/pt.js.map +1 -1
  53. package/dist/password-input-r39y2pCT.d.ts +50 -0
  54. package/dist/shells.d.ts +45 -7
  55. package/dist/shells.js +95 -60
  56. package/dist/shells.js.map +1 -1
  57. package/dist/{sidebar-BDhG9SUU.d.ts → sidebar-BhOIlSjt.d.ts} +4 -12
  58. package/dist/{types-Cno1_Ijx.d.ts → types-B4QS3h5p.d.ts} +9 -1
  59. package/dist/types-CT0rBsFc.d.ts +110 -0
  60. package/package.json +170 -130
  61. package/theme.css +20 -0
  62. package/themes/nosferry.css +4 -0
  63. package/dist/chunk-42BJBH56.js.map +0 -1
  64. package/dist/chunk-5MQWJVM2.js.map +0 -1
  65. package/dist/chunk-A2SIZOEA.js.map +0 -1
  66. package/dist/chunk-POE7OYET.js +0 -96
  67. package/dist/chunk-POE7OYET.js.map +0 -1
  68. package/dist/chunk-UXL33CZ4.js.map +0 -1
  69. package/dist/date-range-filter-QEa8fhUM.d.ts +0 -149
  70. package/dist/types-cOieac8G.d.ts +0 -77
package/dist/blocks.js CHANGED
@@ -1,12 +1,23 @@
1
- import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, Popover, PopoverTrigger, PopoverContent, Tabs, TabsList, TabsTrigger, TabsContent, Label, Textarea, Switch, Calendar, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './chunk-42BJBH56.js';
2
- export { DEFAULT_TOUR_LABELS, TourProvider, resolveSteps, shouldStartTour, stepsForBreakpoint, useTour, useTourController } from './chunk-UXL33CZ4.js';
3
- import { Button, cn, Input, elevatedSurface, recessedSurface } from './chunk-POE7OYET.js';
4
- import { createContext, useState, useCallback, useEffect, useContext } from 'react';
1
+ import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, ConfirmDialog, Popover, PopoverTrigger, PopoverContent, FloatingSheet, FloatingSheetBody, FloatingSheetFooter, Tabs, TabsList, TabsTrigger, TabsContent, Textarea, Switch, FieldError, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, InputOTP, InputOTPGroup, InputOTPSlot, Alert, AlertDescription, Calendar } from './chunk-UUTXPPRN.js';
2
+ import { CopyButton } from './chunk-JAHTUAQL.js';
3
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Label } from './chunk-6QM5BA7B.js';
4
+ import { Card } from './chunk-SA5LM2GD.js';
5
+ export { DEFAULT_TOUR_LABELS, TourProvider, resolveSteps, shouldStartTour, stepsForBreakpoint, useTour, useTourController } from './chunk-YOVY3KT6.js';
6
+ import { useUiLabels, useUiLocale } from './chunk-5AJFQ6C4.js';
7
+ export { UiLocaleProvider, useUiLabels, useUiLocale } from './chunk-5AJFQ6C4.js';
8
+ import { resolveLink } from './chunk-L2H5GIF7.js';
9
+ import { Input } from './chunk-6X4TKSF6.js';
10
+ import { Button, cn, elevatedSurface, nestedSurfaceReset, recessedSurface, compactRadius, controlRadius, Spinner } from './chunk-DSD7MVLL.js';
11
+ import { createContext, useState, useCallback, useEffect, useContext, useId, useRef } from 'react';
5
12
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
- import { CalendarDays, ChevronLeft, ArrowLeftToLine, X, ArrowRight, ArrowUpRight, ArrowDownRight } from 'lucide-react';
13
+ import { TriangleAlert, CalendarDays, ArrowLeft, ChevronRight, AlertCircle, ShieldOff, EyeOff, Eye, RefreshCw, ChevronLeft, ArrowLeftToLine, X, ArrowRight, ArrowUpRight, ArrowDownRight } from 'lucide-react';
7
14
  import { subYears, subQuarters, subMonths, subWeeks, subDays, endOfYear, endOfQuarter, endOfMonth, endOfDay, startOfYear, startOfQuarter, startOfMonth, startOfDay, isSameYear, format, endOfWeek, startOfWeek } from 'date-fns';
8
15
  import { pt } from 'date-fns/locale';
9
16
 
17
+ var commandPaletteDefaultLabels = {
18
+ placeholder: "Search...",
19
+ noResultsLabel: "No results found"
20
+ };
10
21
  function CommandPalette({
11
22
  open,
12
23
  onOpenChange,
@@ -14,12 +25,16 @@ function CommandPalette({
14
25
  onSelect,
15
26
  query,
16
27
  onQueryChange,
17
- placeholder = "Search...",
18
- noResultsLabel = "No results found",
28
+ placeholder,
29
+ noResultsLabel,
19
30
  emptyState,
20
31
  className
21
32
  }) {
22
- const resolvedEmptyState = emptyState ?? noResultsLabel;
33
+ const labels = useUiLabels("commandPalette", commandPaletteDefaultLabels, {
34
+ placeholder,
35
+ noResultsLabel
36
+ });
37
+ const resolvedEmptyState = emptyState ?? labels.noResultsLabel;
23
38
  return /* @__PURE__ */ jsxs(
24
39
  CommandDialog,
25
40
  {
@@ -30,7 +45,7 @@ function CommandPalette({
30
45
  /* @__PURE__ */ jsx(
31
46
  CommandInput,
32
47
  {
33
- placeholder,
48
+ placeholder: labels.placeholder,
34
49
  value: query,
35
50
  onValueChange: onQueryChange
36
51
  }
@@ -70,6 +85,97 @@ function useCommandPalette(initialOpen = false) {
70
85
  }, [toggle]);
71
86
  return { open, setOpen, toggle };
72
87
  }
88
+ var dangerZoneLabels = {
89
+ title: "Danger zone",
90
+ description: "These actions are permanent and cannot be undone.",
91
+ actionLabel: "Delete",
92
+ confirmTitle: "Confirm Action",
93
+ confirmDescription: "Are you sure you want to continue? This action cannot be undone.",
94
+ confirmText: "Confirm",
95
+ cancelText: "Cancel",
96
+ requiredValueLabel: "Type {{value}} to confirm"
97
+ };
98
+ function DangerZone({
99
+ title,
100
+ description,
101
+ actions,
102
+ processing = false,
103
+ labels,
104
+ footer,
105
+ className
106
+ }) {
107
+ const copy = { ...dangerZoneLabels, ...labels };
108
+ const [activeId, setActiveId] = useState(null);
109
+ const active = actions.find((action) => action.id === activeId) ?? null;
110
+ return /* @__PURE__ */ jsxs(
111
+ "section",
112
+ {
113
+ "data-slot": "danger-zone",
114
+ className: cn(
115
+ elevatedSurface,
116
+ nestedSurfaceReset,
117
+ "gap-6 p-6 flex flex-col bg-destructive/5 ring-destructive/30",
118
+ className
119
+ ),
120
+ children: [
121
+ /* @__PURE__ */ jsxs("div", { "data-slot": "danger-zone-header", className: "gap-3 flex", children: [
122
+ /* @__PURE__ */ jsx("span", { className: "size-11 rounded-2xl flex shrink-0 items-center justify-center bg-destructive/10 text-destructive", children: /* @__PURE__ */ jsx(TriangleAlert, { className: "size-5" }) }),
123
+ /* @__PURE__ */ jsxs("div", { children: [
124
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-destructive", children: title ?? copy.title }),
125
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: description ?? copy.description })
126
+ ] })
127
+ ] }),
128
+ /* @__PURE__ */ jsx("ul", { "data-slot": "danger-zone-actions", className: "gap-4 flex flex-col", children: actions.map((action) => /* @__PURE__ */ jsxs(
129
+ "li",
130
+ {
131
+ "data-slot": "danger-zone-action",
132
+ "data-action-id": action.id,
133
+ className: "gap-4 sm:flex-row sm:items-center sm:justify-between flex flex-col",
134
+ children: [
135
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
136
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: action.title }),
137
+ action.description && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: action.description })
138
+ ] }),
139
+ /* @__PURE__ */ jsx(
140
+ Button,
141
+ {
142
+ type: "button",
143
+ variant: "destructive",
144
+ disabled: processing || action.disabled,
145
+ onClick: () => setActiveId(action.id),
146
+ className: "sm:w-auto w-full",
147
+ children: action.actionLabel ?? copy.actionLabel
148
+ }
149
+ )
150
+ ]
151
+ },
152
+ action.id
153
+ )) }),
154
+ footer,
155
+ /* @__PURE__ */ jsx(
156
+ ConfirmDialog,
157
+ {
158
+ open: active !== null,
159
+ onOpenChange: (open) => {
160
+ if (!open) {
161
+ setActiveId(null);
162
+ }
163
+ },
164
+ variant: "destructive",
165
+ processing,
166
+ title: active?.confirmTitle ?? copy.confirmTitle,
167
+ description: active?.confirmDescription ?? copy.confirmDescription,
168
+ confirmText: active?.confirmText ?? copy.confirmText,
169
+ cancelText: active?.cancelText ?? copy.cancelText,
170
+ requiredValue: active?.requiredValue,
171
+ requiredValueLabel: active?.requiredValueLabel ?? copy.requiredValueLabel,
172
+ onConfirm: () => active?.onConfirm()
173
+ }
174
+ )
175
+ ]
176
+ }
177
+ );
178
+ }
73
179
  var DateFilterContext = createContext(null);
74
180
  var DateFilterProvider = DateFilterContext.Provider;
75
181
  function useDateFilter() {
@@ -407,22 +513,24 @@ var DEFAULT_LABELS = {
407
513
  function DateFilter({
408
514
  value,
409
515
  onChange,
410
- presets = DEFAULT_PRESETS,
411
- operators = DEFAULT_OPERATORS,
412
- units = DEFAULT_UNITS,
516
+ presets,
517
+ operators,
518
+ units,
413
519
  labels,
414
520
  children
415
521
  }) {
522
+ const locale = useUiLocale();
523
+ const resolvedLabels = useUiLabels("dateFilter", DEFAULT_LABELS, labels);
416
524
  const [open, setOpen] = useState(false);
417
525
  const [panel, setPanel] = useState("root");
418
526
  const [draft, setDraft] = useState(value);
419
527
  const context = {
420
528
  value,
421
529
  draft,
422
- labels: { ...DEFAULT_LABELS, ...labels },
423
- presets,
424
- operators,
425
- units,
530
+ labels: resolvedLabels,
531
+ presets: presets ?? locale.dateFilterPresets ?? DEFAULT_PRESETS,
532
+ operators: operators ?? locale.dateFilterOperators ?? DEFAULT_OPERATORS,
533
+ units: units ?? locale.dateFilterUnits ?? DEFAULT_UNITS,
426
534
  panel,
427
535
  setDraft,
428
536
  openPanel: (next, seed) => {
@@ -572,9 +680,87 @@ function DateFilterRelative({ children }) {
572
680
  );
573
681
  }
574
682
 
575
- // src/blocks/date-filter/encode.ts
683
+ // src/blocks/date-filter/decode.ts
576
684
  var RANGE_SEPARATOR = "~";
577
685
  var RELATIVE_PREFIX = "previous";
686
+ var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
687
+ var UNITS = ["day", "week", "month", "quarter", "year"];
688
+ var ALL = { mode: "all" };
689
+ function decodeDateFilter(encoded) {
690
+ if (!encoded) {
691
+ return ALL;
692
+ }
693
+ if (encoded.startsWith(`${RELATIVE_PREFIX}:`)) {
694
+ return decodeRelative(encoded);
695
+ }
696
+ if (encoded.includes(RANGE_SEPARATOR)) {
697
+ return decodeFixedRange(encoded);
698
+ }
699
+ if (ISO_DATE.test(encoded)) {
700
+ return isDate(encoded) ? { mode: "fixed", operator: "on", start: encoded } : ALL;
701
+ }
702
+ return { mode: "preset", preset: encoded };
703
+ }
704
+ function decodeFixedRange(encoded) {
705
+ const parts = encoded.split(RANGE_SEPARATOR);
706
+ if (parts.length !== 2) {
707
+ return ALL;
708
+ }
709
+ const [start, end] = parts;
710
+ if (start && end) {
711
+ return isDate(start) && isDate(end) ? { mode: "fixed", operator: "between", start, end } : ALL;
712
+ }
713
+ if (start) {
714
+ return isDate(start) ? { mode: "fixed", operator: "after", start } : ALL;
715
+ }
716
+ if (end) {
717
+ return isDate(end) ? { mode: "fixed", operator: "before", start: end } : ALL;
718
+ }
719
+ return ALL;
720
+ }
721
+ function decodeRelative(encoded) {
722
+ const [, rawAmount, rawUnit, ...rest] = encoded.split(":");
723
+ const amount = Number(rawAmount);
724
+ const unit = toUnit(rawUnit);
725
+ if (!unit || !Number.isInteger(amount) || amount < 1) {
726
+ return ALL;
727
+ }
728
+ const value = { mode: "relative", amount, unit };
729
+ const tail = [...rest];
730
+ if (tail[0] === "current") {
731
+ value.include_current = true;
732
+ tail.shift();
733
+ }
734
+ if (tail.length === 0) {
735
+ return value;
736
+ }
737
+ if (tail.length !== 3 || tail[0] !== "offset") {
738
+ return ALL;
739
+ }
740
+ const offsetAmount = Number(tail[1]);
741
+ const offsetUnit = toUnit(tail[2]);
742
+ if (!offsetUnit || !Number.isInteger(offsetAmount) || offsetAmount < 1) {
743
+ return ALL;
744
+ }
745
+ value.offset_amount = offsetAmount;
746
+ value.offset_unit = offsetUnit;
747
+ return value;
748
+ }
749
+ function toUnit(value) {
750
+ return UNITS.find((unit) => unit === value) ?? null;
751
+ }
752
+ function isDate(value) {
753
+ if (!ISO_DATE.test(value)) {
754
+ return false;
755
+ }
756
+ const [year, month, day] = value.split("-").map(Number);
757
+ const date = new Date(Date.UTC(year, month - 1, day));
758
+ return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
759
+ }
760
+
761
+ // src/blocks/date-filter/encode.ts
762
+ var RANGE_SEPARATOR2 = "~";
763
+ var RELATIVE_PREFIX2 = "previous";
578
764
  function encodeDateFilter(filter) {
579
765
  if (filter.mode === "preset") {
580
766
  return filter.preset ?? null;
@@ -592,21 +778,21 @@ function encodeFixed(filter) {
592
778
  return null;
593
779
  }
594
780
  if (filter.operator === "before") {
595
- return `${RANGE_SEPARATOR}${filter.start}`;
781
+ return `${RANGE_SEPARATOR2}${filter.start}`;
596
782
  }
597
783
  if (filter.operator === "after") {
598
- return `${filter.start}${RANGE_SEPARATOR}`;
784
+ return `${filter.start}${RANGE_SEPARATOR2}`;
599
785
  }
600
786
  if (filter.operator === "on") {
601
787
  return filter.start;
602
788
  }
603
- return filter.end ? `${filter.start}${RANGE_SEPARATOR}${filter.end}` : null;
789
+ return filter.end ? `${filter.start}${RANGE_SEPARATOR2}${filter.end}` : null;
604
790
  }
605
791
  function encodeRelative(filter) {
606
792
  if (!filter.unit || !filter.amount || filter.amount < 1) {
607
793
  return null;
608
794
  }
609
- let value = `${RELATIVE_PREFIX}:${filter.amount}:${filter.unit}`;
795
+ let value = `${RELATIVE_PREFIX2}:${filter.amount}:${filter.unit}`;
610
796
  if (filter.include_current) {
611
797
  value += ":current";
612
798
  }
@@ -615,20 +801,259 @@ function encodeRelative(filter) {
615
801
  }
616
802
  return value;
617
803
  }
804
+ var formOverlayDefaultLabels = {
805
+ cancelLabel: "Cancel",
806
+ saveLabel: "Save",
807
+ savingLabel: "Saving..."
808
+ };
809
+ function FormOverlayActions({
810
+ labels = formOverlayDefaultLabels,
811
+ processing = false,
812
+ intent = "default",
813
+ submit = false,
814
+ className,
815
+ onCancel,
816
+ onSave
817
+ }) {
818
+ return /* @__PURE__ */ jsxs(
819
+ "div",
820
+ {
821
+ "data-slot": "form-overlay-actions",
822
+ className: cn("gap-2 flex items-center justify-end", className),
823
+ children: [
824
+ /* @__PURE__ */ jsx(
825
+ Button,
826
+ {
827
+ type: "button",
828
+ variant: "ghost",
829
+ disabled: processing,
830
+ onClick: onCancel,
831
+ children: labels.cancelLabel
832
+ }
833
+ ),
834
+ /* @__PURE__ */ jsx(
835
+ Button,
836
+ {
837
+ type: submit ? "submit" : "button",
838
+ "aria-label": processing ? labels.savingLabel : void 0,
839
+ variant: intent,
840
+ loading: processing,
841
+ loadingLabel: labels.savingLabel,
842
+ onClick: onSave,
843
+ children: processing ? labels.savingLabel : labels.saveLabel
844
+ }
845
+ )
846
+ ]
847
+ }
848
+ );
849
+ }
850
+ function DetailEditSheet({
851
+ open,
852
+ onOpenChange,
853
+ title,
854
+ description,
855
+ processing = false,
856
+ intent,
857
+ labels,
858
+ className,
859
+ children,
860
+ onSave,
861
+ onCancel
862
+ }) {
863
+ const handleSubmit = (event) => {
864
+ event.preventDefault();
865
+ if (processing) {
866
+ return;
867
+ }
868
+ onSave();
869
+ };
870
+ const handleCancel = () => {
871
+ if (processing) {
872
+ return;
873
+ }
874
+ onCancel?.();
875
+ onOpenChange(false);
876
+ };
877
+ return /* @__PURE__ */ jsx(
878
+ FloatingSheet,
879
+ {
880
+ open,
881
+ onOpenChange,
882
+ title,
883
+ description,
884
+ persistent: true,
885
+ className,
886
+ children: /* @__PURE__ */ jsxs(
887
+ "form",
888
+ {
889
+ "data-slot": "detail-edit-sheet",
890
+ onSubmit: handleSubmit,
891
+ className: "min-h-0 flex flex-1 flex-col",
892
+ children: [
893
+ /* @__PURE__ */ jsx(FloatingSheetBody, { children }),
894
+ /* @__PURE__ */ jsx(FloatingSheetFooter, { children: /* @__PURE__ */ jsx(
895
+ FormOverlayActions,
896
+ {
897
+ submit: true,
898
+ labels,
899
+ processing,
900
+ intent,
901
+ onCancel: handleCancel
902
+ }
903
+ ) })
904
+ ]
905
+ }
906
+ )
907
+ }
908
+ );
909
+ }
910
+ function FormDialog({
911
+ open,
912
+ onOpenChange,
913
+ title,
914
+ description,
915
+ processing = false,
916
+ intent,
917
+ labels,
918
+ className,
919
+ children,
920
+ onSave,
921
+ onCancel
922
+ }) {
923
+ const handleSubmit = (event) => {
924
+ event.preventDefault();
925
+ if (processing) {
926
+ return;
927
+ }
928
+ onSave();
929
+ };
930
+ const handleCancel = () => {
931
+ if (processing) {
932
+ return;
933
+ }
934
+ onCancel?.();
935
+ onOpenChange(false);
936
+ };
937
+ const blockWhileProcessing = (event) => {
938
+ if (processing) {
939
+ event.preventDefault();
940
+ }
941
+ };
942
+ return /* @__PURE__ */ jsx(
943
+ Dialog,
944
+ {
945
+ open,
946
+ onOpenChange: (next) => {
947
+ if (processing && !next) {
948
+ return;
949
+ }
950
+ onOpenChange(next);
951
+ },
952
+ children: /* @__PURE__ */ jsx(
953
+ DialogContent,
954
+ {
955
+ "data-slot": "form-dialog",
956
+ ...description ? {} : { "aria-describedby": void 0 },
957
+ className: cn("gap-0 p-0", className),
958
+ onEscapeKeyDown: blockWhileProcessing,
959
+ onInteractOutside: blockWhileProcessing,
960
+ children: /* @__PURE__ */ jsxs(
961
+ "form",
962
+ {
963
+ "data-slot": "form-dialog-form",
964
+ onSubmit: handleSubmit,
965
+ className: "grid max-h-[calc(100dvh-8rem)] grid-rows-[auto_minmax(0,1fr)_auto]",
966
+ children: [
967
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "text-left", children: [
968
+ /* @__PURE__ */ jsx(DialogTitle, { children: title }),
969
+ description ? /* @__PURE__ */ jsx(DialogDescription, { children: description }) : null
970
+ ] }),
971
+ /* @__PURE__ */ jsx(
972
+ "div",
973
+ {
974
+ "data-slot": "form-dialog-body",
975
+ className: "gap-4 px-6 pb-6 md:px-8 flex flex-col overflow-y-auto",
976
+ children
977
+ }
978
+ ),
979
+ /* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(
980
+ FormOverlayActions,
981
+ {
982
+ submit: true,
983
+ labels,
984
+ processing,
985
+ intent,
986
+ onCancel: handleCancel,
987
+ className: "w-full"
988
+ }
989
+ ) })
990
+ ]
991
+ }
992
+ )
993
+ }
994
+ )
995
+ }
996
+ );
997
+ }
998
+ function copyableText(value, copyValue) {
999
+ if (copyValue !== void 0) {
1000
+ return copyValue.trim();
1001
+ }
1002
+ if (typeof value === "string" || typeof value === "number") {
1003
+ return String(value).trim();
1004
+ }
1005
+ return "";
1006
+ }
618
1007
  function InfoField({
619
1008
  icon: Icon,
620
1009
  label,
621
1010
  value,
1011
+ copyable = false,
1012
+ copyValue,
1013
+ copyLabel,
1014
+ copiedLabel,
622
1015
  iconClassName,
623
1016
  className
624
1017
  }) {
625
- return /* @__PURE__ */ jsxs("div", { className: cn("gap-3 flex items-center", className), children: [
626
- /* @__PURE__ */ jsx("div", { className: cn("p-2 rounded-xl bg-muted", iconClassName), children: /* @__PURE__ */ jsx(Icon, { className: "size-5 text-muted-foreground" }) }),
627
- /* @__PURE__ */ jsxs("div", { children: [
628
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium tracking-wider text-muted-foreground uppercase", children: label }),
629
- /* @__PURE__ */ jsx("p", { className: "font-semibold", children: value })
630
- ] })
631
- ] });
1018
+ const text = copyableText(value, copyValue);
1019
+ return /* @__PURE__ */ jsxs(
1020
+ "div",
1021
+ {
1022
+ "data-slot": "info-field",
1023
+ className: cn("gap-3 flex items-center", className),
1024
+ children: [
1025
+ /* @__PURE__ */ jsx("div", { className: cn("p-2 rounded-xl bg-muted", iconClassName), children: /* @__PURE__ */ jsx(Icon, { className: "size-5 text-muted-foreground" }) }),
1026
+ /* @__PURE__ */ jsxs("div", { children: [
1027
+ /* @__PURE__ */ jsx(
1028
+ "p",
1029
+ {
1030
+ "data-slot": "info-field-label",
1031
+ className: "text-xs font-medium tracking-wider text-muted-foreground uppercase",
1032
+ children: label
1033
+ }
1034
+ ),
1035
+ /* @__PURE__ */ jsxs(
1036
+ "p",
1037
+ {
1038
+ "data-slot": "info-field-value",
1039
+ className: "gap-1 font-semibold flex items-center",
1040
+ children: [
1041
+ value,
1042
+ copyable && text !== "" && /* @__PURE__ */ jsx(
1043
+ CopyButton,
1044
+ {
1045
+ value: text,
1046
+ copyLabel,
1047
+ copiedLabel
1048
+ }
1049
+ )
1050
+ ]
1051
+ }
1052
+ )
1053
+ ] })
1054
+ ]
1055
+ }
1056
+ );
632
1057
  }
633
1058
  function InfoFieldGroup({ children, className }) {
634
1059
  return /* @__PURE__ */ jsx("div", { className: cn("space-y-4", className), children });
@@ -827,6 +1252,345 @@ function ToggleRow({
827
1252
  )
828
1253
  ] });
829
1254
  }
1255
+ function SettingsField({
1256
+ id,
1257
+ label,
1258
+ description,
1259
+ error,
1260
+ required,
1261
+ className,
1262
+ children
1263
+ }) {
1264
+ const generated = useId();
1265
+ const fieldId = id ?? generated;
1266
+ return /* @__PURE__ */ jsxs(
1267
+ "div",
1268
+ {
1269
+ "data-slot": "settings-field",
1270
+ "data-invalid": error ? true : void 0,
1271
+ className: cn("space-y-2 py-1", className),
1272
+ children: [
1273
+ /* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
1274
+ label,
1275
+ required && /* @__PURE__ */ jsx(
1276
+ "span",
1277
+ {
1278
+ "data-slot": "settings-field-required",
1279
+ "aria-hidden": "true",
1280
+ className: "text-destructive",
1281
+ children: "*"
1282
+ }
1283
+ )
1284
+ ] }),
1285
+ children(fieldId),
1286
+ description && /* @__PURE__ */ jsx(
1287
+ "p",
1288
+ {
1289
+ "data-slot": "settings-field-description",
1290
+ className: "text-xs font-medium text-muted-foreground",
1291
+ children: description
1292
+ }
1293
+ ),
1294
+ /* @__PURE__ */ jsx(FieldError, { message: error })
1295
+ ]
1296
+ }
1297
+ );
1298
+ }
1299
+ function TextField({
1300
+ type = "text",
1301
+ value,
1302
+ onChange,
1303
+ disabled,
1304
+ placeholder,
1305
+ ...field
1306
+ }) {
1307
+ return /* @__PURE__ */ jsx(SettingsField, { ...field, children: (fieldId) => /* @__PURE__ */ jsx(
1308
+ Input,
1309
+ {
1310
+ id: fieldId,
1311
+ type,
1312
+ value,
1313
+ disabled,
1314
+ placeholder,
1315
+ "aria-invalid": field.error ? true : void 0,
1316
+ "data-slot": "text-field",
1317
+ onChange: (event) => onChange(event.target.value)
1318
+ }
1319
+ ) });
1320
+ }
1321
+ function NumberField({
1322
+ value,
1323
+ onChange,
1324
+ disabled,
1325
+ placeholder,
1326
+ min,
1327
+ max,
1328
+ step,
1329
+ ...field
1330
+ }) {
1331
+ return /* @__PURE__ */ jsx(SettingsField, { ...field, children: (fieldId) => /* @__PURE__ */ jsx(
1332
+ Input,
1333
+ {
1334
+ id: fieldId,
1335
+ type: "number",
1336
+ inputMode: "decimal",
1337
+ value: value === "" ? "" : String(value),
1338
+ min,
1339
+ max,
1340
+ step,
1341
+ disabled,
1342
+ placeholder,
1343
+ "aria-invalid": field.error ? true : void 0,
1344
+ "data-slot": "number-field",
1345
+ onChange: (event) => onChange(
1346
+ event.target.value === "" ? "" : event.target.valueAsNumber
1347
+ )
1348
+ }
1349
+ ) });
1350
+ }
1351
+ function DateField({
1352
+ value,
1353
+ onChange,
1354
+ disabled,
1355
+ min,
1356
+ max,
1357
+ ...field
1358
+ }) {
1359
+ return /* @__PURE__ */ jsx(SettingsField, { ...field, children: (fieldId) => /* @__PURE__ */ jsx(
1360
+ Input,
1361
+ {
1362
+ id: fieldId,
1363
+ type: "date",
1364
+ value,
1365
+ min,
1366
+ max,
1367
+ disabled,
1368
+ "aria-invalid": field.error ? true : void 0,
1369
+ "data-slot": "date-field",
1370
+ onChange: (event) => onChange(event.target.value)
1371
+ }
1372
+ ) });
1373
+ }
1374
+ function SelectField({
1375
+ value,
1376
+ onChange,
1377
+ options,
1378
+ disabled,
1379
+ placeholder,
1380
+ ...field
1381
+ }) {
1382
+ return /* @__PURE__ */ jsx(SettingsField, { ...field, children: (fieldId) => /* @__PURE__ */ jsxs(
1383
+ Select,
1384
+ {
1385
+ value,
1386
+ onValueChange: onChange,
1387
+ disabled,
1388
+ children: [
1389
+ /* @__PURE__ */ jsx(
1390
+ SelectTrigger,
1391
+ {
1392
+ id: fieldId,
1393
+ "data-slot": "select-field",
1394
+ "aria-invalid": field.error ? true : void 0,
1395
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder })
1396
+ }
1397
+ ),
1398
+ /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(
1399
+ SelectItem,
1400
+ {
1401
+ value: option.value,
1402
+ disabled: option.disabled,
1403
+ children: option.label
1404
+ },
1405
+ option.value
1406
+ )) })
1407
+ ]
1408
+ }
1409
+ ) });
1410
+ }
1411
+ var SettingsLinkContext = createContext(void 0);
1412
+ function SettingsPage({
1413
+ title,
1414
+ description,
1415
+ control,
1416
+ linkComponent,
1417
+ children,
1418
+ className
1419
+ }) {
1420
+ return /* @__PURE__ */ jsx(SettingsLinkContext.Provider, { value: linkComponent, children: /* @__PURE__ */ jsxs(
1421
+ "div",
1422
+ {
1423
+ "data-slot": "settings-page",
1424
+ className: cn(
1425
+ "gap-8 max-w-4xl mx-auto flex w-full flex-col",
1426
+ className
1427
+ ),
1428
+ children: [
1429
+ title && /* @__PURE__ */ jsx(
1430
+ SectionHeader,
1431
+ {
1432
+ title,
1433
+ description,
1434
+ control
1435
+ }
1436
+ ),
1437
+ children
1438
+ ]
1439
+ }
1440
+ ) });
1441
+ }
1442
+ var settingsLabels = {
1443
+ back: "Back"
1444
+ };
1445
+ function SettingsSection({
1446
+ title,
1447
+ description,
1448
+ control,
1449
+ backHref = "/settings",
1450
+ backLabel,
1451
+ wide = false,
1452
+ linkComponent,
1453
+ children,
1454
+ className
1455
+ }) {
1456
+ const labels = useUiLabels("settings", settingsLabels, {
1457
+ back: backLabel
1458
+ });
1459
+ const contextLink = useContext(SettingsLinkContext);
1460
+ const Link = resolveLink(linkComponent ?? contextLink);
1461
+ return /* @__PURE__ */ jsxs(
1462
+ "section",
1463
+ {
1464
+ "data-slot": "settings-section",
1465
+ className: cn(
1466
+ "gap-6 mx-auto flex w-full flex-col",
1467
+ wide ? "max-w-4xl" : "max-w-2xl",
1468
+ className
1469
+ ),
1470
+ children: [
1471
+ backHref && /* @__PURE__ */ jsxs(
1472
+ Link,
1473
+ {
1474
+ "data-slot": "settings-section-back",
1475
+ href: backHref,
1476
+ className: "gap-2 text-sm font-medium inline-flex w-fit items-center text-muted-foreground transition-colors hover:text-foreground",
1477
+ children: [
1478
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "size-4" }),
1479
+ labels.back
1480
+ ]
1481
+ }
1482
+ ),
1483
+ title && /* @__PURE__ */ jsx(
1484
+ SectionHeader,
1485
+ {
1486
+ title,
1487
+ description,
1488
+ control
1489
+ }
1490
+ ),
1491
+ children
1492
+ ]
1493
+ }
1494
+ );
1495
+ }
1496
+ function SettingsGroup({
1497
+ label,
1498
+ columns = 2,
1499
+ children,
1500
+ className
1501
+ }) {
1502
+ return /* @__PURE__ */ jsxs(
1503
+ "div",
1504
+ {
1505
+ "data-slot": "settings-group",
1506
+ className: cn("gap-3 flex flex-col", className),
1507
+ children: [
1508
+ label && /* @__PURE__ */ jsx("p", { className: "font-bold tracking-widest text-[11px] text-muted-foreground uppercase", children: label }),
1509
+ /* @__PURE__ */ jsx(
1510
+ "div",
1511
+ {
1512
+ "data-slot": "settings-group-grid",
1513
+ className: cn(
1514
+ "gap-4 grid grid-cols-1",
1515
+ columns === 2 && "md:grid-cols-2"
1516
+ ),
1517
+ children
1518
+ }
1519
+ )
1520
+ ]
1521
+ }
1522
+ );
1523
+ }
1524
+ function SettingsEntry({
1525
+ icon: Icon,
1526
+ iconClassName,
1527
+ title,
1528
+ description,
1529
+ href,
1530
+ badge,
1531
+ disabled = false,
1532
+ linkComponent,
1533
+ className
1534
+ }) {
1535
+ const contextLink = useContext(SettingsLinkContext);
1536
+ const Link = resolveLink(linkComponent ?? contextLink);
1537
+ const card = /* @__PURE__ */ jsxs(
1538
+ Card,
1539
+ {
1540
+ interactive: !disabled,
1541
+ padding: "none",
1542
+ "data-disabled": disabled || void 0,
1543
+ className: cn(
1544
+ "gap-4 p-5 flex h-full flex-row items-center",
1545
+ disabled && "opacity-60",
1546
+ className
1547
+ ),
1548
+ children: [
1549
+ /* @__PURE__ */ jsx(
1550
+ "span",
1551
+ {
1552
+ className: cn(
1553
+ "size-11 flex shrink-0 items-center justify-center rounded-full",
1554
+ iconClassName ?? "bg-primary/10 text-primary"
1555
+ ),
1556
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-5" })
1557
+ }
1558
+ ),
1559
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex flex-1 flex-col", children: [
1560
+ /* @__PURE__ */ jsxs("span", { className: "gap-2 flex items-center", children: [
1561
+ /* @__PURE__ */ jsx("span", { className: "font-bold text-foreground", children: title }),
1562
+ badge
1563
+ ] }),
1564
+ description && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: description })
1565
+ ] }),
1566
+ /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 shrink-0 text-muted-foreground" })
1567
+ ]
1568
+ }
1569
+ );
1570
+ if (disabled) {
1571
+ return /* @__PURE__ */ jsx(
1572
+ "div",
1573
+ {
1574
+ "data-slot": "settings-entry",
1575
+ "data-disabled": "true",
1576
+ "aria-disabled": "true",
1577
+ children: card
1578
+ }
1579
+ );
1580
+ }
1581
+ return /* @__PURE__ */ jsx(
1582
+ Link,
1583
+ {
1584
+ "data-slot": "settings-entry",
1585
+ href,
1586
+ className: cn(
1587
+ compactRadius,
1588
+ "outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
1589
+ ),
1590
+ children: card
1591
+ }
1592
+ );
1593
+ }
830
1594
  function resolveTrend(trend) {
831
1595
  if (trend === null || trend === void 0 || Number.isNaN(trend)) {
832
1596
  return null;
@@ -916,6 +1680,601 @@ function StatsGrid({ children, className }) {
916
1680
  );
917
1681
  }
918
1682
 
919
- export { CommandPalette, DEFAULT_LABELS as DATE_FILTER_LABELS, DEFAULT_OPERATORS as DATE_FILTER_OPERATORS, DEFAULT_PRESETS as DATE_FILTER_PRESETS, DEFAULT_UNITS as DATE_FILTER_UNITS, DateFilter, DateFilterAll, DateFilterContent, DateFilterFixed, DateFilterItem, DateFilterPresets, DateFilterRelative, DateFilterSeparator, DateFilterTrigger, InfoField, InfoFieldGroup, LocalizedFields, SectionHeader, SettingsCard, SettingsPanel, StatCard, StatsGrid, ToggleRow, encodeDateFilter, formatRangePreview, resolveRelativeRange, summariseDateFilter, useCommandPalette, useDateFilter };
1683
+ // src/blocks/two-factor/types.ts
1684
+ var twoFactorLabels = {
1685
+ setupTitle: "Two-factor authentication",
1686
+ setupDescription: "Add a second step to your sign in with an authenticator app.",
1687
+ pendingLabel: "Preparing your setup key",
1688
+ scanTitle: "Scan the code",
1689
+ scanDescription: "Open your authenticator app and scan the code below to add this account.",
1690
+ qrFallbackLabel: "The QR code is not available yet.",
1691
+ manualKeyLabel: "Setup key",
1692
+ manualKeyDescription: "Enter this key by hand if your app cannot scan the code.",
1693
+ manualKeyRevealLabel: "Show setup key",
1694
+ manualKeyHideLabel: "Hide setup key",
1695
+ continueLabel: "Continue",
1696
+ confirmTitle: "Confirm the code",
1697
+ confirmDescription: "Enter the six digit code your authenticator app is showing.",
1698
+ codeLabel: "Authentication code",
1699
+ recoveryCodeLabel: "Recovery code",
1700
+ recoveryCodePlaceholder: "Enter a recovery code",
1701
+ useRecoveryCodeLabel: "Use a recovery code",
1702
+ useCodeLabel: "Use an authentication code",
1703
+ verifyLabel: "Verify",
1704
+ verifyingLabel: "Verifying",
1705
+ errorFallbackLabel: "That did not work. Try again.",
1706
+ cancelLabel: "Cancel",
1707
+ challengeTitle: "Two-factor confirmation",
1708
+ challengeDescription: "Confirm access to your account with the code from your authenticator app.",
1709
+ recoveryTitle: "Recovery codes",
1710
+ recoveryDescription: "Store these codes somewhere safe. Each one signs you in once if you lose your device.",
1711
+ recoveryWarning: "They are shown once and cannot be read again afterwards.",
1712
+ revealLabel: "Show codes",
1713
+ hideLabel: "Hide codes",
1714
+ copyLabel: "Copy",
1715
+ copiedLabel: "Copied",
1716
+ copyFailedLabel: "Copying is unavailable here. Select the codes manually.",
1717
+ regenerateLabel: "Regenerate codes",
1718
+ doneLabel: "Done",
1719
+ disableLabel: "Disable two-factor authentication",
1720
+ disableTitle: "Disable two-factor authentication",
1721
+ disableDescription: "Your account will be protected by your password alone. Recovery codes stop working.",
1722
+ disableConfirmLabel: "Disable",
1723
+ disableCancelLabel: "Keep it on"
1724
+ };
1725
+ function resolveLabels(labels) {
1726
+ return labels ? { ...twoFactorLabels, ...labels } : twoFactorLabels;
1727
+ }
1728
+ function messageList(errors) {
1729
+ if (!errors) {
1730
+ return [];
1731
+ }
1732
+ return (Array.isArray(errors) ? errors : [errors]).filter(
1733
+ (message) => message.length > 0
1734
+ );
1735
+ }
1736
+ function errorMessage(reason, fallback) {
1737
+ return reason instanceof Error && reason.message ? reason.message : fallback;
1738
+ }
1739
+ function TwoFactorVerifyForm({
1740
+ onSubmit,
1741
+ errors,
1742
+ allowRecoveryCode = false,
1743
+ length = 6,
1744
+ autoFocus = false,
1745
+ submitLabel,
1746
+ footer,
1747
+ labels,
1748
+ className
1749
+ }) {
1750
+ const text = resolveLabels(labels);
1751
+ const fieldId = useId();
1752
+ const [mode, setMode] = useState("code");
1753
+ const [value, setValue] = useState("");
1754
+ const [pending, setPending] = useState(false);
1755
+ const [failure, setFailure] = useState(null);
1756
+ const messages = [...messageList(errors), ...messageList(failure)];
1757
+ const complete = mode === "code" ? value.length === length : value.length > 0;
1758
+ const handleSubmit = async (event) => {
1759
+ event.preventDefault();
1760
+ if (pending || !complete) {
1761
+ return;
1762
+ }
1763
+ setFailure(null);
1764
+ setPending(true);
1765
+ try {
1766
+ await onSubmit(value, mode);
1767
+ } catch (reason) {
1768
+ setFailure(errorMessage(reason, text.errorFallbackLabel));
1769
+ } finally {
1770
+ setPending(false);
1771
+ }
1772
+ };
1773
+ const switchMode = () => {
1774
+ setMode(mode === "code" ? "recovery" : "code");
1775
+ setValue("");
1776
+ setFailure(null);
1777
+ };
1778
+ return /* @__PURE__ */ jsxs(
1779
+ "form",
1780
+ {
1781
+ "data-slot": "two-factor-verify-form",
1782
+ "data-mode": mode,
1783
+ "data-pending": pending || void 0,
1784
+ onSubmit: handleSubmit,
1785
+ className: cn("gap-5 flex w-full flex-col", className),
1786
+ children: [
1787
+ /* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-col", children: [
1788
+ /* @__PURE__ */ jsx(Label, { htmlFor: fieldId, children: mode === "code" ? text.codeLabel : text.recoveryCodeLabel }),
1789
+ mode === "code" ? /* @__PURE__ */ jsx(
1790
+ InputOTP,
1791
+ {
1792
+ id: fieldId,
1793
+ "data-slot": "two-factor-code-input",
1794
+ maxLength: length,
1795
+ value,
1796
+ autoFocus,
1797
+ disabled: pending,
1798
+ onChange: setValue,
1799
+ "aria-label": text.codeLabel,
1800
+ children: /* @__PURE__ */ jsx(InputOTPGroup, { children: Array.from({ length }, (_, index) => /* @__PURE__ */ jsx(InputOTPSlot, { index }, index)) })
1801
+ }
1802
+ ) : /* @__PURE__ */ jsx(
1803
+ Input,
1804
+ {
1805
+ id: fieldId,
1806
+ "data-slot": "two-factor-recovery-input",
1807
+ value,
1808
+ autoComplete: "one-time-code",
1809
+ spellCheck: false,
1810
+ disabled: pending,
1811
+ placeholder: text.recoveryCodePlaceholder,
1812
+ "aria-label": text.recoveryCodeLabel,
1813
+ onChange: (event) => setValue(event.target.value)
1814
+ }
1815
+ )
1816
+ ] }),
1817
+ messages.length > 0 && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-slot": "two-factor-error", children: [
1818
+ /* @__PURE__ */ jsx(AlertCircle, {}),
1819
+ /* @__PURE__ */ jsx(AlertDescription, { children: messages.map((message) => /* @__PURE__ */ jsx("p", { children: message }, message)) })
1820
+ ] }),
1821
+ /* @__PURE__ */ jsxs("div", { className: "gap-3 flex flex-col", children: [
1822
+ /* @__PURE__ */ jsx(
1823
+ Button,
1824
+ {
1825
+ type: "submit",
1826
+ loading: pending,
1827
+ loadingLabel: text.verifyingLabel,
1828
+ disabled: !complete,
1829
+ children: submitLabel ?? text.verifyLabel
1830
+ }
1831
+ ),
1832
+ allowRecoveryCode && /* @__PURE__ */ jsx(
1833
+ Button,
1834
+ {
1835
+ type: "button",
1836
+ variant: "ghost",
1837
+ size: "sm",
1838
+ disabled: pending,
1839
+ onClick: switchMode,
1840
+ children: mode === "code" ? text.useRecoveryCodeLabel : text.useCodeLabel
1841
+ }
1842
+ ),
1843
+ footer
1844
+ ] })
1845
+ ]
1846
+ }
1847
+ );
1848
+ }
1849
+ function TwoFactorChallenge({
1850
+ onSubmit,
1851
+ errors,
1852
+ allowRecoveryCode = true,
1853
+ title,
1854
+ description,
1855
+ footer,
1856
+ labels,
1857
+ className
1858
+ }) {
1859
+ const text = resolveLabels(labels);
1860
+ return /* @__PURE__ */ jsxs(
1861
+ "section",
1862
+ {
1863
+ "data-slot": "two-factor-challenge",
1864
+ className: cn("gap-6 flex w-full flex-col", className),
1865
+ children: [
1866
+ /* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-col", children: [
1867
+ /* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold tracking-tight text-foreground", children: title ?? text.challengeTitle }),
1868
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: description ?? text.challengeDescription })
1869
+ ] }),
1870
+ /* @__PURE__ */ jsx(
1871
+ TwoFactorVerifyForm,
1872
+ {
1873
+ autoFocus: true,
1874
+ allowRecoveryCode,
1875
+ errors,
1876
+ labels,
1877
+ footer,
1878
+ onSubmit
1879
+ }
1880
+ )
1881
+ ]
1882
+ }
1883
+ );
1884
+ }
1885
+ function TwoFactorDisableButton({
1886
+ onDisable,
1887
+ disabled = false,
1888
+ variant = "destructive",
1889
+ size = "default",
1890
+ labels,
1891
+ className
1892
+ }) {
1893
+ const text = resolveLabels(labels);
1894
+ const [open, setOpen] = useState(false);
1895
+ const [processing, setProcessing] = useState(false);
1896
+ const handleConfirm = async () => {
1897
+ setProcessing(true);
1898
+ try {
1899
+ await onDisable();
1900
+ } finally {
1901
+ setProcessing(false);
1902
+ }
1903
+ };
1904
+ return /* @__PURE__ */ jsxs(
1905
+ "span",
1906
+ {
1907
+ "data-slot": "two-factor-disable",
1908
+ "data-open": open || void 0,
1909
+ className: cn("inline-flex", className),
1910
+ children: [
1911
+ /* @__PURE__ */ jsxs(
1912
+ Button,
1913
+ {
1914
+ type: "button",
1915
+ variant,
1916
+ size,
1917
+ disabled: disabled || processing,
1918
+ onClick: () => setOpen(true),
1919
+ children: [
1920
+ /* @__PURE__ */ jsx(ShieldOff, {}),
1921
+ text.disableLabel
1922
+ ]
1923
+ }
1924
+ ),
1925
+ /* @__PURE__ */ jsx(
1926
+ ConfirmDialog,
1927
+ {
1928
+ open,
1929
+ onOpenChange: setOpen,
1930
+ title: text.disableTitle,
1931
+ description: text.disableDescription,
1932
+ confirmText: text.disableConfirmLabel,
1933
+ cancelText: text.disableCancelLabel,
1934
+ processing,
1935
+ onConfirm: handleConfirm
1936
+ }
1937
+ )
1938
+ ]
1939
+ }
1940
+ );
1941
+ }
1942
+ function TwoFactorRecoveryCodes({
1943
+ codes,
1944
+ defaultRevealed = false,
1945
+ onRegenerate,
1946
+ showHeading = true,
1947
+ labels,
1948
+ className
1949
+ }) {
1950
+ const text = resolveLabels(labels);
1951
+ const [revealed, setRevealed] = useState(defaultRevealed);
1952
+ const [copyFailed, setCopyFailed] = useState(false);
1953
+ const [regenerating, setRegenerating] = useState(false);
1954
+ const handleRegenerate = async () => {
1955
+ if (!onRegenerate || regenerating) {
1956
+ return;
1957
+ }
1958
+ setRegenerating(true);
1959
+ try {
1960
+ await onRegenerate();
1961
+ } finally {
1962
+ setRegenerating(false);
1963
+ }
1964
+ };
1965
+ return /* @__PURE__ */ jsxs(
1966
+ "div",
1967
+ {
1968
+ "data-slot": "two-factor-recovery-codes",
1969
+ "data-revealed": revealed || void 0,
1970
+ className: cn("gap-4 flex w-full flex-col", className),
1971
+ children: [
1972
+ showHeading && /* @__PURE__ */ jsxs("div", { className: "gap-1 flex flex-col", children: [
1973
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: text.recoveryTitle }),
1974
+ /* @__PURE__ */ jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: [
1975
+ text.recoveryDescription,
1976
+ " ",
1977
+ text.recoveryWarning
1978
+ ] })
1979
+ ] }),
1980
+ revealed && /* @__PURE__ */ jsx(
1981
+ "ul",
1982
+ {
1983
+ "data-slot": "two-factor-recovery-list",
1984
+ className: cn(
1985
+ recessedSurface,
1986
+ "gap-2 p-4 sm:grid-cols-2 grid"
1987
+ ),
1988
+ children: codes.map((code) => /* @__PURE__ */ jsx(
1989
+ "li",
1990
+ {
1991
+ "data-slot": "two-factor-recovery-code",
1992
+ className: cn(
1993
+ compactRadius,
1994
+ "px-2 py-1 text-sm font-medium font-mono break-all text-foreground"
1995
+ ),
1996
+ children: code
1997
+ },
1998
+ code
1999
+ ))
2000
+ }
2001
+ ),
2002
+ /* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-wrap items-center", children: [
2003
+ /* @__PURE__ */ jsxs(
2004
+ Button,
2005
+ {
2006
+ type: "button",
2007
+ variant: "ghost",
2008
+ size: "sm",
2009
+ "aria-pressed": revealed,
2010
+ onClick: () => setRevealed(!revealed),
2011
+ children: [
2012
+ revealed ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {}),
2013
+ revealed ? text.hideLabel : text.revealLabel
2014
+ ]
2015
+ }
2016
+ ),
2017
+ /* @__PURE__ */ jsx(
2018
+ CopyButton,
2019
+ {
2020
+ value: codes.join("\n"),
2021
+ copyLabel: text.copyLabel,
2022
+ copiedLabel: text.copiedLabel,
2023
+ onCopied: () => setCopyFailed(false),
2024
+ onCopyFailed: () => setCopyFailed(true)
2025
+ }
2026
+ ),
2027
+ onRegenerate && /* @__PURE__ */ jsxs(
2028
+ Button,
2029
+ {
2030
+ type: "button",
2031
+ variant: "outline",
2032
+ size: "sm",
2033
+ loading: regenerating,
2034
+ loadingLabel: text.regenerateLabel,
2035
+ onClick: handleRegenerate,
2036
+ children: [
2037
+ /* @__PURE__ */ jsx(RefreshCw, {}),
2038
+ text.regenerateLabel
2039
+ ]
2040
+ }
2041
+ )
2042
+ ] }),
2043
+ copyFailed && /* @__PURE__ */ jsx(
2044
+ "p",
2045
+ {
2046
+ "data-slot": "two-factor-copy-failed",
2047
+ className: "text-xs font-medium text-muted-foreground",
2048
+ children: text.copyFailedLabel
2049
+ }
2050
+ )
2051
+ ]
2052
+ }
2053
+ );
2054
+ }
2055
+ function TwoFactorScanStep({
2056
+ qrCode,
2057
+ qrCodeSvg,
2058
+ manualSetupKey,
2059
+ labels,
2060
+ className
2061
+ }) {
2062
+ const text = resolveLabels(labels);
2063
+ const [revealed, setRevealed] = useState(false);
2064
+ return /* @__PURE__ */ jsxs(
2065
+ "div",
2066
+ {
2067
+ "data-slot": "two-factor-scan-step",
2068
+ className: cn("gap-5 flex w-full flex-col", className),
2069
+ children: [
2070
+ /* @__PURE__ */ jsx(
2071
+ "div",
2072
+ {
2073
+ "data-slot": "two-factor-qr",
2074
+ className: cn(
2075
+ recessedSurface,
2076
+ "p-4 flex items-center justify-center [&_svg]:size-full [&_svg]:h-auto"
2077
+ ),
2078
+ children: qrCode ?? (qrCodeSvg ? /* @__PURE__ */ jsx(
2079
+ "div",
2080
+ {
2081
+ "data-slot": "two-factor-qr-markup",
2082
+ className: cn(controlRadius, "p-3 bg-card"),
2083
+ dangerouslySetInnerHTML: { __html: qrCodeSvg }
2084
+ }
2085
+ ) : /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: text.qrFallbackLabel }))
2086
+ }
2087
+ ),
2088
+ manualSetupKey && /* @__PURE__ */ jsxs(
2089
+ "div",
2090
+ {
2091
+ "data-slot": "two-factor-setup-key",
2092
+ className: "gap-2 flex flex-col",
2093
+ children: [
2094
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: text.manualKeyLabel }),
2095
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: text.manualKeyDescription }),
2096
+ /* @__PURE__ */ jsxs("div", { className: "gap-2 flex flex-wrap items-center", children: [
2097
+ /* @__PURE__ */ jsx(
2098
+ "code",
2099
+ {
2100
+ "data-slot": "two-factor-setup-key-value",
2101
+ className: cn(
2102
+ compactRadius,
2103
+ "px-3 py-2 text-sm font-medium font-mono bg-muted break-all text-foreground"
2104
+ ),
2105
+ children: revealed ? manualSetupKey : "\u2022".repeat(manualSetupKey.length)
2106
+ }
2107
+ ),
2108
+ /* @__PURE__ */ jsxs(
2109
+ Button,
2110
+ {
2111
+ type: "button",
2112
+ variant: "ghost",
2113
+ size: "sm",
2114
+ "aria-pressed": revealed,
2115
+ onClick: () => setRevealed(!revealed),
2116
+ children: [
2117
+ revealed ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {}),
2118
+ revealed ? text.manualKeyHideLabel : text.manualKeyRevealLabel
2119
+ ]
2120
+ }
2121
+ ),
2122
+ /* @__PURE__ */ jsx(
2123
+ CopyButton,
2124
+ {
2125
+ value: manualSetupKey,
2126
+ copyLabel: text.copyLabel,
2127
+ copiedLabel: text.copiedLabel
2128
+ }
2129
+ )
2130
+ ] })
2131
+ ]
2132
+ }
2133
+ )
2134
+ ]
2135
+ }
2136
+ );
2137
+ }
2138
+ function TwoFactorSetupDialog({
2139
+ open,
2140
+ onOpenChange,
2141
+ enabled = false,
2142
+ qrCode,
2143
+ qrCodeSvg,
2144
+ manualSetupKey,
2145
+ recoveryCodes,
2146
+ errors,
2147
+ onConfirm,
2148
+ onRequestSetupData,
2149
+ onRegenerateRecoveryCodes,
2150
+ onCompleted,
2151
+ labels,
2152
+ className
2153
+ }) {
2154
+ const text = resolveLabels(labels);
2155
+ const [step, setStep] = useState(
2156
+ enabled ? "recovery" : "scan"
2157
+ );
2158
+ const requestSetupData = useRef(onRequestSetupData);
2159
+ const openedBefore = useRef(false);
2160
+ useEffect(() => {
2161
+ requestSetupData.current = onRequestSetupData;
2162
+ }, [onRequestSetupData]);
2163
+ useEffect(() => {
2164
+ if (!open) {
2165
+ openedBefore.current = false;
2166
+ return;
2167
+ }
2168
+ if (openedBefore.current) {
2169
+ return;
2170
+ }
2171
+ openedBefore.current = true;
2172
+ setStep(enabled ? "recovery" : "scan");
2173
+ if (!enabled) {
2174
+ void requestSetupData.current?.();
2175
+ }
2176
+ }, [open, enabled]);
2177
+ const ready = Boolean(qrCode || qrCodeSvg || manualSetupKey);
2178
+ const codes = recoveryCodes ?? [];
2179
+ const handleConfirm = async (code) => {
2180
+ await onConfirm(code);
2181
+ setStep("recovery");
2182
+ };
2183
+ const finish = () => {
2184
+ onOpenChange(false);
2185
+ onCompleted?.();
2186
+ };
2187
+ const heading = {
2188
+ pending: text.setupTitle,
2189
+ scan: text.scanTitle,
2190
+ confirm: text.confirmTitle,
2191
+ recovery: text.recoveryTitle
2192
+ }[step];
2193
+ const description = {
2194
+ pending: text.setupDescription,
2195
+ scan: text.scanDescription,
2196
+ confirm: text.confirmDescription,
2197
+ recovery: `${text.recoveryDescription} ${text.recoveryWarning}`
2198
+ }[step];
2199
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
2200
+ DialogContent,
2201
+ {
2202
+ "data-slot": "two-factor-setup-dialog",
2203
+ "data-step": step,
2204
+ className: cn("max-w-md p-0", className),
2205
+ children: [
2206
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "p-6 md:p-8", children: [
2207
+ /* @__PURE__ */ jsx(DialogTitle, { children: heading }),
2208
+ /* @__PURE__ */ jsx(DialogDescription, { children: description })
2209
+ ] }),
2210
+ /* @__PURE__ */ jsxs("div", { className: "gap-5 p-6 md:p-8 pt-0 md:pt-0 flex flex-col", children: [
2211
+ step === "scan" && (ready ? /* @__PURE__ */ jsxs(Fragment, { children: [
2212
+ /* @__PURE__ */ jsx(
2213
+ TwoFactorScanStep,
2214
+ {
2215
+ qrCode,
2216
+ qrCodeSvg,
2217
+ manualSetupKey,
2218
+ labels
2219
+ }
2220
+ ),
2221
+ /* @__PURE__ */ jsx(
2222
+ Button,
2223
+ {
2224
+ type: "button",
2225
+ onClick: () => setStep("confirm"),
2226
+ children: text.continueLabel
2227
+ }
2228
+ )
2229
+ ] }) : /* @__PURE__ */ jsxs(
2230
+ "div",
2231
+ {
2232
+ "data-slot": "two-factor-pending",
2233
+ className: "gap-3 py-6 text-sm font-medium flex items-center justify-center text-muted-foreground",
2234
+ children: [
2235
+ /* @__PURE__ */ jsx(Spinner, { label: text.pendingLabel }),
2236
+ text.pendingLabel
2237
+ ]
2238
+ }
2239
+ )),
2240
+ step === "confirm" && /* @__PURE__ */ jsx(
2241
+ TwoFactorVerifyForm,
2242
+ {
2243
+ autoFocus: true,
2244
+ errors,
2245
+ labels,
2246
+ onSubmit: handleConfirm,
2247
+ footer: /* @__PURE__ */ jsx(
2248
+ Button,
2249
+ {
2250
+ type: "button",
2251
+ variant: "ghost",
2252
+ size: "sm",
2253
+ onClick: () => setStep("scan"),
2254
+ children: text.cancelLabel
2255
+ }
2256
+ )
2257
+ }
2258
+ ),
2259
+ step === "recovery" && /* @__PURE__ */ jsxs(Fragment, { children: [
2260
+ /* @__PURE__ */ jsx(
2261
+ TwoFactorRecoveryCodes,
2262
+ {
2263
+ codes,
2264
+ defaultRevealed: true,
2265
+ showHeading: false,
2266
+ labels,
2267
+ onRegenerate: onRegenerateRecoveryCodes
2268
+ }
2269
+ ),
2270
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: finish, children: text.doneLabel })
2271
+ ] })
2272
+ ] })
2273
+ ]
2274
+ }
2275
+ ) });
2276
+ }
2277
+
2278
+ export { CommandPalette, DEFAULT_LABELS as DATE_FILTER_LABELS, DEFAULT_OPERATORS as DATE_FILTER_OPERATORS, DEFAULT_PRESETS as DATE_FILTER_PRESETS, DEFAULT_UNITS as DATE_FILTER_UNITS, DangerZone, DateField, DateFilter, DateFilterAll, DateFilterContent, DateFilterFixed, DateFilterItem, DateFilterPresets, DateFilterRelative, DateFilterSeparator, DateFilterTrigger, DetailEditSheet, FormDialog, FormOverlayActions, InfoField, InfoFieldGroup, LocalizedFields, NumberField, SectionHeader, SelectField, SettingsCard, SettingsEntry, SettingsField, SettingsGroup, SettingsPage, SettingsPanel, SettingsSection, StatCard, StatsGrid, TextField, ToggleRow, TwoFactorChallenge, TwoFactorDisableButton, TwoFactorRecoveryCodes, TwoFactorScanStep, TwoFactorSetupDialog, TwoFactorVerifyForm, dangerZoneLabels, decodeDateFilter, encodeDateFilter, formOverlayDefaultLabels, formatRangePreview, resolveRelativeRange, settingsLabels, summariseDateFilter, twoFactorLabels, useCommandPalette, useDateFilter };
920
2279
  //# sourceMappingURL=blocks.js.map
921
2280
  //# sourceMappingURL=blocks.js.map