create-astram-app 0.5.0 → 0.7.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 (83) hide show
  1. package/README.md +256 -38
  2. package/dist/add-frontend.js.map +1 -1
  3. package/dist/index.js +14 -9
  4. package/dist/index.js.map +1 -1
  5. package/package.json +2 -2
  6. package/template/README.md +51 -20
  7. package/template/frontend/README.md +21 -11
  8. package/template/frontend/index.html +16 -0
  9. package/template/frontend/package.json +1 -1
  10. package/template/frontend/src/App.jsx +82 -20
  11. package/template/frontend/src/auth/AuthContext.jsx +141 -21
  12. package/template/frontend/src/auth/routeGate.js +31 -0
  13. package/template/frontend/src/auth/routeGate.test.mjs +59 -0
  14. package/template/frontend/src/auth/sessionFlow.js +27 -0
  15. package/template/frontend/src/auth/sessionFlow.test.mjs +63 -0
  16. package/template/frontend/src/components/EntityList.jsx +1 -7
  17. package/template/frontend/src/components/PageHeader.jsx +5 -4
  18. package/template/frontend/src/components/ShareDialog.jsx +3 -5
  19. package/template/frontend/src/components/access/CollapsibleGroup.jsx +4 -1
  20. package/template/frontend/src/components/ui/alert-dialog.tsx +27 -50
  21. package/template/frontend/src/components/ui/alert.tsx +17 -23
  22. package/template/frontend/src/components/ui/avatar.tsx +8 -20
  23. package/template/frontend/src/components/ui/badge.tsx +17 -26
  24. package/template/frontend/src/components/ui/button.tsx +22 -24
  25. package/template/frontend/src/components/ui/card.tsx +19 -36
  26. package/template/frontend/src/components/ui/checkbox.tsx +7 -10
  27. package/template/frontend/src/components/ui/date-field.tsx +26 -27
  28. package/template/frontend/src/components/ui/dialog.tsx +24 -40
  29. package/template/frontend/src/components/ui/dropdown-menu.tsx +25 -54
  30. package/template/frontend/src/components/ui/input-otp.tsx +1 -4
  31. package/template/frontend/src/components/ui/label.tsx +10 -12
  32. package/template/frontend/src/components/ui/searchable-select.tsx +1 -3
  33. package/template/frontend/src/components/ui/select.tsx +31 -43
  34. package/template/frontend/src/components/ui/separator.tsx +6 -6
  35. package/template/frontend/src/components/ui/sheet.tsx +29 -38
  36. package/template/frontend/src/components/ui/skeleton.tsx +3 -3
  37. package/template/frontend/src/components/ui/sonner.tsx +7 -7
  38. package/template/frontend/src/components/ui/table.tsx +56 -70
  39. package/template/frontend/src/components/ui/tooltip.tsx +7 -11
  40. package/template/frontend/src/config/branding.js +1 -4
  41. package/template/frontend/src/hooks/useMobile.tsx +4 -6
  42. package/template/frontend/src/index.css +5 -2
  43. package/template/frontend/src/pages/Dashboard.jsx +13 -7
  44. package/template/frontend/src/pages/ForgotPassword.jsx +9 -2
  45. package/template/frontend/src/pages/Login.jsx +85 -70
  46. package/template/frontend/src/pages/ResetPassword.jsx +9 -2
  47. package/template/frontend/src/pages/access/AccessForm.jsx +49 -15
  48. package/template/frontend/src/pages/access/AccessPage.jsx +10 -6
  49. package/template/frontend/src/pages/access/RecordPoliciesPage.jsx +9 -2
  50. package/template/frontend/src/pages/account/AccountLayout.jsx +11 -6
  51. package/template/frontend/src/pages/account/ChangePasswordPage.jsx +2 -2
  52. package/template/frontend/src/pages/account/ReenrollPage.jsx +3 -5
  53. package/template/frontend/src/pages/account/TwoFactorPage.jsx +3 -8
  54. package/template/frontend/src/pages/companies/CompaniesPage.jsx +7 -9
  55. package/template/frontend/src/pages/cross-tenant/GrantForm.jsx +41 -8
  56. package/template/frontend/src/pages/cross-tenant/GrantsPage.jsx +30 -7
  57. package/template/frontend/src/pages/cross-tenant/InboxPage.jsx +12 -3
  58. package/template/frontend/src/pages/roles/RolesPage.jsx +5 -9
  59. package/template/frontend/src/pages/service-accounts/ServiceAccountsPage.jsx +77 -18
  60. package/template/frontend/src/pages/service-accounts/accessModel.js +22 -0
  61. package/template/frontend/src/pages/service-accounts/accessModel.test.mjs +28 -0
  62. package/template/frontend/src/pages/users/MembershipCard.jsx +3 -1
  63. package/template/frontend/src/pages/users/PostingRow.jsx +8 -14
  64. package/template/frontend/src/pages/users/UsersPage.jsx +7 -9
  65. package/template/frontend/src/platform/httpClient.js +18 -0
  66. package/template/frontend/src/platform/httpClient.test.mjs +60 -0
  67. package/template/frontend/src/platform/sessionStore.js +55 -13
  68. package/template/frontend/src/platform/sessionStore.test.mjs +89 -0
  69. package/template/frontend/src/platform/shieldTransport.js +6 -0
  70. package/template/frontend/src/platform/shieldTransport.test.mjs +84 -5
  71. package/template/frontend/src/shell/AppShell.jsx +10 -12
  72. package/template/frontend/src/shell/Sidebar.jsx +15 -3
  73. package/template/frontend/src/shell/nav.js +1 -3
  74. package/template/frontend/vite.config.js +19 -1
  75. package/template/server/_env.example +22 -1
  76. package/template/server/eslint.config.mjs +314 -0
  77. package/template/server/package.json +11 -11
  78. package/template/server/scripts/dev.mjs +17 -6
  79. package/template/server/scripts/sync-astram-guide.mjs +45 -19
  80. package/template/server/src/app.module.ts +16 -7
  81. package/template/server/src/health/demo.controller.ts +16 -12
  82. package/template/server/src/main.ts +23 -1
  83. package/template/server/eslint.config.js +0 -95
@@ -1,6 +1,6 @@
1
- import * as React from "react";
1
+ import * as React from 'react';
2
2
 
3
- import { cn } from "@/lib/utils";
3
+ import { cn } from '@/lib/utils';
4
4
 
5
5
  /**
6
6
  * DateField — a styled `<input type="date">` wrapper matching the shadcn `Input`
@@ -18,30 +18,29 @@ import { cn } from "@/lib/utils";
18
18
  * React 18.3: `React.forwardRef` so a forwarded ref reaches the underlying
19
19
  * `<input>` (form libraries / focus management) and never warns.
20
20
  */
21
- const DateField = React.forwardRef<
22
- HTMLInputElement,
23
- React.ComponentPropsWithoutRef<"input">
24
- >(({ className, type = "date", ...props }, ref) => {
25
- return (
26
- <input
27
- ref={ref}
28
- type={type}
29
- data-slot="date-field"
30
- className={cn(
31
- "border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none tabular-nums md:text-sm",
32
- "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground",
33
- "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
34
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
35
- "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
36
- // The native calendar-picker glyph: tint to the muted ink, brighten on
37
- // hover, and invert in dark mode so it stays visible on the dark field.
38
- "[&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-60 [&::-webkit-calendar-picker-indicator]:hover:opacity-100 dark:[&::-webkit-calendar-picker-indicator]:invert",
39
- className
40
- )}
41
- {...props}
42
- />
43
- );
44
- });
45
- DateField.displayName = "DateField";
21
+ const DateField = React.forwardRef<HTMLInputElement, React.ComponentPropsWithoutRef<'input'>>(
22
+ ({ className, type = 'date', ...props }, ref) => {
23
+ return (
24
+ <input
25
+ ref={ref}
26
+ type={type}
27
+ data-slot="date-field"
28
+ className={cn(
29
+ 'border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none tabular-nums md:text-sm',
30
+ 'placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground',
31
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
32
+ 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
33
+ 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
34
+ // The native calendar-picker glyph: tint to the muted ink, brighten on
35
+ // hover, and invert in dark mode so it stays visible on the dark field.
36
+ '[&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-60 [&::-webkit-calendar-picker-indicator]:hover:opacity-100 dark:[&::-webkit-calendar-picker-indicator]:invert',
37
+ className,
38
+ )}
39
+ {...props}
40
+ />
41
+ );
42
+ },
43
+ );
44
+ DateField.displayName = 'DateField';
46
45
 
47
46
  export { DateField };
@@ -1,7 +1,7 @@
1
- import { cn } from "@/lib/utils";
2
- import * as DialogPrimitive from "@radix-ui/react-dialog";
3
- import { XIcon } from "lucide-react";
4
- import * as React from "react";
1
+ import { cn } from '@/lib/utils';
2
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
3
+ import { XIcon } from 'lucide-react';
4
+ import * as React from 'react';
5
5
 
6
6
  // Context to track composition state across dialog children
7
7
  const DialogCompositionContext = React.createContext<{
@@ -16,12 +16,9 @@ const DialogCompositionContext = React.createContext<{
16
16
  markCompositionEnd: () => {},
17
17
  });
18
18
 
19
- export const useDialogComposition = () =>
20
- React.useContext(DialogCompositionContext);
19
+ export const useDialogComposition = () => React.useContext(DialogCompositionContext);
21
20
 
22
- function Dialog({
23
- ...props
24
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
21
+ function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
25
22
  const composingRef = React.useRef(false);
26
23
  const justEndedRef = React.useRef(false);
27
24
  const endTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -43,7 +40,7 @@ function Dialog({
43
40
  }, 150);
44
41
  },
45
42
  }),
46
- []
43
+ [],
47
44
  );
48
45
 
49
46
  return (
@@ -53,21 +50,15 @@ function Dialog({
53
50
  );
54
51
  }
55
52
 
56
- function DialogTrigger({
57
- ...props
58
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
53
+ function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
59
54
  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
60
55
  }
61
56
 
62
- function DialogPortal({
63
- ...props
64
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
57
+ function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
65
58
  return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
66
59
  }
67
60
 
68
- function DialogClose({
69
- ...props
70
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
61
+ function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
71
62
  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
72
63
  }
73
64
 
@@ -84,15 +75,15 @@ const DialogOverlay = React.forwardRef<
84
75
  ref={ref}
85
76
  data-slot="dialog-overlay"
86
77
  className={cn(
87
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
88
- className
78
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
79
+ className,
89
80
  )}
90
81
  {...props}
91
82
  />
92
83
  );
93
84
  });
94
85
 
95
- DialogOverlay.displayName = "DialogOverlay";
86
+ DialogOverlay.displayName = 'DialogOverlay';
96
87
 
97
88
  function DialogContent({
98
89
  className,
@@ -120,7 +111,7 @@ function DialogContent({
120
111
  // Call user's onEscapeKeyDown if provided
121
112
  onEscapeKeyDown?.(e);
122
113
  },
123
- [isComposing, onEscapeKeyDown]
114
+ [isComposing, onEscapeKeyDown],
124
115
  );
125
116
 
126
117
  return (
@@ -129,8 +120,8 @@ function DialogContent({
129
120
  <DialogPrimitive.Content
130
121
  data-slot="dialog-content"
131
122
  className={cn(
132
- "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100vw-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
133
- className
123
+ 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100vw-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
124
+ className,
134
125
  )}
135
126
  onEscapeKeyDown={handleEscapeKeyDown}
136
127
  {...props}
@@ -150,37 +141,31 @@ function DialogContent({
150
141
  );
151
142
  }
152
143
 
153
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
144
+ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
154
145
  return (
155
146
  <div
156
147
  data-slot="dialog-header"
157
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
148
+ className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
158
149
  {...props}
159
150
  />
160
151
  );
161
152
  }
162
153
 
163
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
154
+ function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
164
155
  return (
165
156
  <div
166
157
  data-slot="dialog-footer"
167
- className={cn(
168
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
169
- className
170
- )}
158
+ className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
171
159
  {...props}
172
160
  />
173
161
  );
174
162
  }
175
163
 
176
- function DialogTitle({
177
- className,
178
- ...props
179
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
164
+ function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
180
165
  return (
181
166
  <DialogPrimitive.Title
182
167
  data-slot="dialog-title"
183
- className={cn("text-lg leading-none font-semibold", className)}
168
+ className={cn('text-lg leading-none font-semibold', className)}
184
169
  {...props}
185
170
  />
186
171
  );
@@ -193,7 +178,7 @@ function DialogDescription({
193
178
  return (
194
179
  <DialogPrimitive.Description
195
180
  data-slot="dialog-description"
196
- className={cn("text-muted-foreground text-sm", className)}
181
+ className={cn('text-muted-foreground text-sm', className)}
197
182
  {...props}
198
183
  />
199
184
  );
@@ -209,6 +194,5 @@ export {
209
194
  DialogOverlay,
210
195
  DialogPortal,
211
196
  DialogTitle,
212
- DialogTrigger
197
+ DialogTrigger,
213
198
  };
214
-
@@ -1,32 +1,23 @@
1
- import * as React from "react";
2
- import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
3
- import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
1
+ import * as React from 'react';
2
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
3
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
4
4
 
5
- import { cn } from "@/lib/utils";
5
+ import { cn } from '@/lib/utils';
6
6
 
7
- function DropdownMenu({
8
- ...props
9
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
7
+ function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
10
8
  return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
11
9
  }
12
10
 
13
11
  function DropdownMenuPortal({
14
12
  ...props
15
13
  }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
16
- return (
17
- <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
18
- );
14
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
19
15
  }
20
16
 
21
17
  function DropdownMenuTrigger({
22
18
  ...props
23
19
  }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
24
- return (
25
- <DropdownMenuPrimitive.Trigger
26
- data-slot="dropdown-menu-trigger"
27
- {...props}
28
- />
29
- );
20
+ return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
30
21
  }
31
22
 
32
23
  function DropdownMenuContent({
@@ -40,8 +31,8 @@ function DropdownMenuContent({
40
31
  data-slot="dropdown-menu-content"
41
32
  sideOffset={sideOffset}
42
33
  className={cn(
43
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
44
- className
34
+ 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
35
+ className,
45
36
  )}
46
37
  {...props}
47
38
  />
@@ -49,22 +40,18 @@ function DropdownMenuContent({
49
40
  );
50
41
  }
51
42
 
52
- function DropdownMenuGroup({
53
- ...props
54
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
55
- return (
56
- <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
57
- );
43
+ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
44
+ return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
58
45
  }
59
46
 
60
47
  function DropdownMenuItem({
61
48
  className,
62
49
  inset,
63
- variant = "default",
50
+ variant = 'default',
64
51
  ...props
65
52
  }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
66
53
  inset?: boolean;
67
- variant?: "default" | "destructive";
54
+ variant?: 'default' | 'destructive';
68
55
  }) {
69
56
  return (
70
57
  <DropdownMenuPrimitive.Item
@@ -73,7 +60,7 @@ function DropdownMenuItem({
73
60
  data-variant={variant}
74
61
  className={cn(
75
62
  "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
76
- className
63
+ className,
77
64
  )}
78
65
  {...props}
79
66
  />
@@ -91,7 +78,7 @@ function DropdownMenuCheckboxItem({
91
78
  data-slot="dropdown-menu-checkbox-item"
92
79
  className={cn(
93
80
  "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
94
- className
81
+ className,
95
82
  )}
96
83
  checked={checked}
97
84
  {...props}
@@ -109,12 +96,7 @@ function DropdownMenuCheckboxItem({
109
96
  function DropdownMenuRadioGroup({
110
97
  ...props
111
98
  }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
112
- return (
113
- <DropdownMenuPrimitive.RadioGroup
114
- data-slot="dropdown-menu-radio-group"
115
- {...props}
116
- />
117
- );
99
+ return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
118
100
  }
119
101
 
120
102
  function DropdownMenuRadioItem({
@@ -127,7 +109,7 @@ function DropdownMenuRadioItem({
127
109
  data-slot="dropdown-menu-radio-item"
128
110
  className={cn(
129
111
  "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
130
- className
112
+ className,
131
113
  )}
132
114
  {...props}
133
115
  >
@@ -152,10 +134,7 @@ function DropdownMenuLabel({
152
134
  <DropdownMenuPrimitive.Label
153
135
  data-slot="dropdown-menu-label"
154
136
  data-inset={inset}
155
- className={cn(
156
- "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
157
- className
158
- )}
137
+ className={cn('px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', className)}
159
138
  {...props}
160
139
  />
161
140
  );
@@ -168,31 +147,23 @@ function DropdownMenuSeparator({
168
147
  return (
169
148
  <DropdownMenuPrimitive.Separator
170
149
  data-slot="dropdown-menu-separator"
171
- className={cn("bg-border -mx-1 my-1 h-px", className)}
150
+ className={cn('bg-border -mx-1 my-1 h-px', className)}
172
151
  {...props}
173
152
  />
174
153
  );
175
154
  }
176
155
 
177
- function DropdownMenuShortcut({
178
- className,
179
- ...props
180
- }: React.ComponentProps<"span">) {
156
+ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
181
157
  return (
182
158
  <span
183
159
  data-slot="dropdown-menu-shortcut"
184
- className={cn(
185
- "text-muted-foreground ml-auto text-xs tracking-widest",
186
- className
187
- )}
160
+ className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
188
161
  {...props}
189
162
  />
190
163
  );
191
164
  }
192
165
 
193
- function DropdownMenuSub({
194
- ...props
195
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
166
+ function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
196
167
  return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
197
168
  }
198
169
 
@@ -210,7 +181,7 @@ function DropdownMenuSubTrigger({
210
181
  data-inset={inset}
211
182
  className={cn(
212
183
  "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
213
- className
184
+ className,
214
185
  )}
215
186
  {...props}
216
187
  >
@@ -228,8 +199,8 @@ function DropdownMenuSubContent({
228
199
  <DropdownMenuPrimitive.SubContent
229
200
  data-slot="dropdown-menu-sub-content"
230
201
  className={cn(
231
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
232
- className
202
+ 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
203
+ className,
233
204
  )}
234
205
  {...props}
235
206
  />
@@ -16,10 +16,7 @@ const InputOTP = React.forwardRef<
16
16
  <OTPInput
17
17
  ref={ref}
18
18
  data-slot="input-otp"
19
- containerClassName={cn(
20
- 'flex items-center gap-2 has-disabled:opacity-50',
21
- containerClassName,
22
- )}
19
+ containerClassName={cn('flex items-center gap-2 has-disabled:opacity-50', containerClassName)}
23
20
  className={cn('disabled:cursor-not-allowed', className)}
24
21
  {...props}
25
22
  />
@@ -1,8 +1,8 @@
1
- import * as React from "react";
2
- import * as LabelPrimitive from "@radix-ui/react-label";
3
- import { cva, type VariantProps } from "class-variance-authority";
1
+ import * as React from 'react';
2
+ import * as LabelPrimitive from '@radix-ui/react-label';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
4
 
5
- import { cn } from "@/lib/utils";
5
+ import { cn } from '@/lib/utils';
6
6
 
7
7
  /**
8
8
  * Label — default is the standard shadcn label (existing callers unchanged).
@@ -13,27 +13,25 @@ import { cn } from "@/lib/utils";
13
13
  * the OCA-crisp field labels.
14
14
  */
15
15
  const labelVariants = cva(
16
- "flex items-center gap-2 leading-none select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
16
+ 'flex items-center gap-2 leading-none select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
17
17
  {
18
18
  variants: {
19
19
  variant: {
20
- default: "text-sm font-medium",
21
- field:
22
- "font-mono text-[0.72rem] font-semibold tracking-[0.08em] uppercase text-primary",
20
+ default: 'text-sm font-medium',
21
+ field: 'font-mono text-[0.72rem] font-semibold tracking-[0.08em] uppercase text-primary',
23
22
  },
24
23
  },
25
24
  defaultVariants: {
26
- variant: "default",
25
+ variant: 'default',
27
26
  },
28
- }
27
+ },
29
28
  );
30
29
 
31
30
  function Label({
32
31
  className,
33
32
  variant,
34
33
  ...props
35
- }: React.ComponentProps<typeof LabelPrimitive.Root> &
36
- VariantProps<typeof labelVariants>) {
34
+ }: React.ComponentProps<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>) {
37
35
  return (
38
36
  <LabelPrimitive.Root
39
37
  data-slot="label"
@@ -413,9 +413,7 @@ export function SearchableSelect({
413
413
  )}
414
414
  >
415
415
  <span className="line-clamp-1">{opt.label}</span>
416
- {selected && (
417
- <Check className="absolute right-2 size-4 shrink-0" />
418
- )}
416
+ {selected && <Check className="absolute right-2 size-4 shrink-0" />}
419
417
  </div>
420
418
  );
421
419
  })