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,8 +1,8 @@
1
- import * as React from "react";
2
- import * as SelectPrimitive from "@radix-ui/react-select";
3
- import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
1
+ import * as React from 'react';
2
+ import * as SelectPrimitive from '@radix-ui/react-select';
3
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
4
4
 
5
- import { cn } from "@/lib/utils";
5
+ import { cn } from '@/lib/utils';
6
6
 
7
7
  /**
8
8
  * Radix Select — the shadcn/new-york Select set, styled to match this repo's
@@ -22,37 +22,31 @@ import { cn } from "@/lib/utils";
22
22
  * NOTE: requires `@radix-ui/react-select` to be installed.
23
23
  */
24
24
 
25
- function Select({
26
- ...props
27
- }: React.ComponentProps<typeof SelectPrimitive.Root>) {
25
+ function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>) {
28
26
  return <SelectPrimitive.Root data-slot="select" {...props} />;
29
27
  }
30
28
 
31
- function SelectGroup({
32
- ...props
33
- }: React.ComponentProps<typeof SelectPrimitive.Group>) {
29
+ function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>) {
34
30
  return <SelectPrimitive.Group data-slot="select-group" {...props} />;
35
31
  }
36
32
 
37
- function SelectValue({
38
- ...props
39
- }: React.ComponentProps<typeof SelectPrimitive.Value>) {
33
+ function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>) {
40
34
  return <SelectPrimitive.Value data-slot="select-value" {...props} />;
41
35
  }
42
36
 
43
37
  const SelectTrigger = React.forwardRef<
44
38
  React.ElementRef<typeof SelectPrimitive.Trigger>,
45
39
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
46
- size?: "sm" | "default";
40
+ size?: 'sm' | 'default';
47
41
  }
48
- >(({ className, size = "default", children, ...props }, ref) => (
42
+ >(({ className, size = 'default', children, ...props }, ref) => (
49
43
  <SelectPrimitive.Trigger
50
44
  ref={ref}
51
45
  data-slot="select-trigger"
52
46
  data-size={size}
53
47
  className={cn(
54
48
  "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex w-full items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
55
- className
49
+ className,
56
50
  )}
57
51
  {...props}
58
52
  >
@@ -62,7 +56,7 @@ const SelectTrigger = React.forwardRef<
62
56
  </SelectPrimitive.Icon>
63
57
  </SelectPrimitive.Trigger>
64
58
  ));
65
- SelectTrigger.displayName = "SelectTrigger";
59
+ SelectTrigger.displayName = 'SelectTrigger';
66
60
 
67
61
  const SelectScrollUpButton = React.forwardRef<
68
62
  React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
@@ -71,16 +65,13 @@ const SelectScrollUpButton = React.forwardRef<
71
65
  <SelectPrimitive.ScrollUpButton
72
66
  ref={ref}
73
67
  data-slot="select-scroll-up-button"
74
- className={cn(
75
- "flex cursor-default items-center justify-center py-1",
76
- className
77
- )}
68
+ className={cn('flex cursor-default items-center justify-center py-1', className)}
78
69
  {...props}
79
70
  >
80
71
  <ChevronUpIcon className="size-4" />
81
72
  </SelectPrimitive.ScrollUpButton>
82
73
  ));
83
- SelectScrollUpButton.displayName = "SelectScrollUpButton";
74
+ SelectScrollUpButton.displayName = 'SelectScrollUpButton';
84
75
 
85
76
  const SelectScrollDownButton = React.forwardRef<
86
77
  React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
@@ -89,30 +80,27 @@ const SelectScrollDownButton = React.forwardRef<
89
80
  <SelectPrimitive.ScrollDownButton
90
81
  ref={ref}
91
82
  data-slot="select-scroll-down-button"
92
- className={cn(
93
- "flex cursor-default items-center justify-center py-1",
94
- className
95
- )}
83
+ className={cn('flex cursor-default items-center justify-center py-1', className)}
96
84
  {...props}
97
85
  >
98
86
  <ChevronDownIcon className="size-4" />
99
87
  </SelectPrimitive.ScrollDownButton>
100
88
  ));
101
- SelectScrollDownButton.displayName = "SelectScrollDownButton";
89
+ SelectScrollDownButton.displayName = 'SelectScrollDownButton';
102
90
 
103
91
  const SelectContent = React.forwardRef<
104
92
  React.ElementRef<typeof SelectPrimitive.Content>,
105
93
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
106
- >(({ className, children, position = "popper", ...props }, ref) => (
94
+ >(({ className, children, position = 'popper', ...props }, ref) => (
107
95
  <SelectPrimitive.Portal>
108
96
  <SelectPrimitive.Content
109
97
  ref={ref}
110
98
  data-slot="select-content"
111
99
  className={cn(
112
- "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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
113
- position === "popper" &&
114
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
115
- className
100
+ '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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md',
101
+ position === 'popper' &&
102
+ 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
103
+ className,
116
104
  )}
117
105
  position={position}
118
106
  {...props}
@@ -120,9 +108,9 @@ const SelectContent = React.forwardRef<
120
108
  <SelectScrollUpButton />
121
109
  <SelectPrimitive.Viewport
122
110
  className={cn(
123
- "p-1",
124
- position === "popper" &&
125
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
111
+ 'p-1',
112
+ position === 'popper' &&
113
+ 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1',
126
114
  )}
127
115
  >
128
116
  {children}
@@ -131,7 +119,7 @@ const SelectContent = React.forwardRef<
131
119
  </SelectPrimitive.Content>
132
120
  </SelectPrimitive.Portal>
133
121
  ));
134
- SelectContent.displayName = "SelectContent";
122
+ SelectContent.displayName = 'SelectContent';
135
123
 
136
124
  const SelectLabel = React.forwardRef<
137
125
  React.ElementRef<typeof SelectPrimitive.Label>,
@@ -141,13 +129,13 @@ const SelectLabel = React.forwardRef<
141
129
  ref={ref}
142
130
  data-slot="select-label"
143
131
  className={cn(
144
- "text-muted-foreground px-2 py-1.5 font-mono text-[0.68rem] font-semibold tracking-[0.08em] uppercase",
145
- className
132
+ 'text-muted-foreground px-2 py-1.5 font-mono text-[0.68rem] font-semibold tracking-[0.08em] uppercase',
133
+ className,
146
134
  )}
147
135
  {...props}
148
136
  />
149
137
  ));
150
- SelectLabel.displayName = "SelectLabel";
138
+ SelectLabel.displayName = 'SelectLabel';
151
139
 
152
140
  const SelectItem = React.forwardRef<
153
141
  React.ElementRef<typeof SelectPrimitive.Item>,
@@ -158,7 +146,7 @@ const SelectItem = React.forwardRef<
158
146
  data-slot="select-item"
159
147
  className={cn(
160
148
  "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 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 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
161
- className
149
+ className,
162
150
  )}
163
151
  {...props}
164
152
  >
@@ -170,7 +158,7 @@ const SelectItem = React.forwardRef<
170
158
  <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
171
159
  </SelectPrimitive.Item>
172
160
  ));
173
- SelectItem.displayName = "SelectItem";
161
+ SelectItem.displayName = 'SelectItem';
174
162
 
175
163
  const SelectSeparator = React.forwardRef<
176
164
  React.ElementRef<typeof SelectPrimitive.Separator>,
@@ -179,11 +167,11 @@ const SelectSeparator = React.forwardRef<
179
167
  <SelectPrimitive.Separator
180
168
  ref={ref}
181
169
  data-slot="select-separator"
182
- className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
170
+ className={cn('bg-border pointer-events-none -mx-1 my-1 h-px', className)}
183
171
  {...props}
184
172
  />
185
173
  ));
186
- SelectSeparator.displayName = "SelectSeparator";
174
+ SelectSeparator.displayName = 'SelectSeparator';
187
175
 
188
176
  export {
189
177
  Select,
@@ -1,11 +1,11 @@
1
- import * as React from "react";
2
- import * as SeparatorPrimitive from "@radix-ui/react-separator";
1
+ import * as React from 'react';
2
+ import * as SeparatorPrimitive from '@radix-ui/react-separator';
3
3
 
4
- import { cn } from "@/lib/utils";
4
+ import { cn } from '@/lib/utils';
5
5
 
6
6
  function Separator({
7
7
  className,
8
- orientation = "horizontal",
8
+ orientation = 'horizontal',
9
9
  decorative = true,
10
10
  ...props
11
11
  }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
@@ -15,8 +15,8 @@ function Separator({
15
15
  decorative={decorative}
16
16
  orientation={orientation}
17
17
  className={cn(
18
- "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
19
- className
18
+ 'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
19
+ className,
20
20
  )}
21
21
  {...props}
22
22
  />
@@ -1,30 +1,24 @@
1
- "use client";
1
+ 'use client';
2
2
 
3
- import * as React from "react";
4
- import * as SheetPrimitive from "@radix-ui/react-dialog";
5
- import { XIcon } from "lucide-react";
3
+ import * as React from 'react';
4
+ import * as SheetPrimitive from '@radix-ui/react-dialog';
5
+ import { XIcon } from 'lucide-react';
6
6
 
7
- import { cn } from "@/lib/utils";
7
+ import { cn } from '@/lib/utils';
8
8
 
9
9
  function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
10
10
  return <SheetPrimitive.Root data-slot="sheet" {...props} />;
11
11
  }
12
12
 
13
- function SheetTrigger({
14
- ...props
15
- }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
13
+ function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
16
14
  return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
17
15
  }
18
16
 
19
- function SheetClose({
20
- ...props
21
- }: React.ComponentProps<typeof SheetPrimitive.Close>) {
17
+ function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>) {
22
18
  return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
23
19
  }
24
20
 
25
- function SheetPortal({
26
- ...props
27
- }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
21
+ function SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
28
22
  return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
29
23
  }
30
24
 
@@ -36,8 +30,8 @@ function SheetOverlay({
36
30
  <SheetPrimitive.Overlay
37
31
  data-slot="sheet-overlay"
38
32
  className={cn(
39
- "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",
40
- className
33
+ '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',
34
+ className,
41
35
  )}
42
36
  {...props}
43
37
  />
@@ -47,10 +41,10 @@ function SheetOverlay({
47
41
  function SheetContent({
48
42
  className,
49
43
  children,
50
- side = "right",
44
+ side = 'right',
51
45
  ...props
52
46
  }: React.ComponentProps<typeof SheetPrimitive.Content> & {
53
- side?: "top" | "right" | "bottom" | "left";
47
+ side?: 'top' | 'right' | 'bottom' | 'left';
54
48
  }) {
55
49
  return (
56
50
  <SheetPortal>
@@ -58,16 +52,16 @@ function SheetContent({
58
52
  <SheetPrimitive.Content
59
53
  data-slot="sheet-content"
60
54
  className={cn(
61
- "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
62
- side === "right" &&
63
- "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
64
- side === "left" &&
65
- "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
66
- side === "top" &&
67
- "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
68
- side === "bottom" &&
69
- "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
70
- className
55
+ 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
56
+ side === 'right' &&
57
+ 'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',
58
+ side === 'left' &&
59
+ 'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm',
60
+ side === 'top' &&
61
+ 'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b',
62
+ side === 'bottom' &&
63
+ 'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t',
64
+ className,
71
65
  )}
72
66
  {...props}
73
67
  >
@@ -81,34 +75,31 @@ function SheetContent({
81
75
  );
82
76
  }
83
77
 
84
- function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
78
+ function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {
85
79
  return (
86
80
  <div
87
81
  data-slot="sheet-header"
88
- className={cn("flex flex-col gap-1.5 p-4", className)}
82
+ className={cn('flex flex-col gap-1.5 p-4', className)}
89
83
  {...props}
90
84
  />
91
85
  );
92
86
  }
93
87
 
94
- function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
88
+ function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {
95
89
  return (
96
90
  <div
97
91
  data-slot="sheet-footer"
98
- className={cn("mt-auto flex flex-col gap-2 p-4", className)}
92
+ className={cn('mt-auto flex flex-col gap-2 p-4', className)}
99
93
  {...props}
100
94
  />
101
95
  );
102
96
  }
103
97
 
104
- function SheetTitle({
105
- className,
106
- ...props
107
- }: React.ComponentProps<typeof SheetPrimitive.Title>) {
98
+ function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {
108
99
  return (
109
100
  <SheetPrimitive.Title
110
101
  data-slot="sheet-title"
111
- className={cn("text-foreground font-semibold", className)}
102
+ className={cn('text-foreground font-semibold', className)}
112
103
  {...props}
113
104
  />
114
105
  );
@@ -121,7 +112,7 @@ function SheetDescription({
121
112
  return (
122
113
  <SheetPrimitive.Description
123
114
  data-slot="sheet-description"
124
- className={cn("text-muted-foreground text-sm", className)}
115
+ className={cn('text-muted-foreground text-sm', className)}
125
116
  {...props}
126
117
  />
127
118
  );
@@ -1,10 +1,10 @@
1
- import { cn } from "@/lib/utils";
1
+ import { cn } from '@/lib/utils';
2
2
 
3
- function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
3
+ function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {
4
4
  return (
5
5
  <div
6
6
  data-slot="skeleton"
7
- className={cn("bg-accent animate-pulse rounded-md", className)}
7
+ className={cn('bg-accent animate-pulse rounded-md', className)}
8
8
  {...props}
9
9
  />
10
10
  );
@@ -1,18 +1,18 @@
1
- import { useTheme } from "next-themes";
2
- import { Toaster as Sonner, type ToasterProps } from "sonner";
1
+ import { useTheme } from 'next-themes';
2
+ import { Toaster as Sonner, type ToasterProps } from 'sonner';
3
3
 
4
4
  const Toaster = ({ ...props }: ToasterProps) => {
5
- const { theme = "system" } = useTheme();
5
+ const { theme = 'system' } = useTheme();
6
6
 
7
7
  return (
8
8
  <Sonner
9
- theme={theme as ToasterProps["theme"]}
9
+ theme={theme as ToasterProps['theme']}
10
10
  className="toaster group"
11
11
  style={
12
12
  {
13
- "--normal-bg": "var(--popover)",
14
- "--normal-text": "var(--popover-foreground)",
15
- "--normal-border": "var(--border)",
13
+ '--normal-bg': 'var(--popover)',
14
+ '--normal-text': 'var(--popover-foreground)',
15
+ '--normal-border': 'var(--border)',
16
16
  } as React.CSSProperties
17
17
  }
18
18
  {...props}
@@ -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
  * shadcn Table primitive set, tuned to OCA's matured DataTable look
@@ -27,84 +27,79 @@ import { cn } from "@/lib/utils";
27
27
  * components cannot be given refs".
28
28
  */
29
29
 
30
- const Table = React.forwardRef<
31
- HTMLTableElement,
32
- React.ComponentPropsWithoutRef<"table">
33
- >(({ className, ...props }, ref) => (
34
- <div data-slot="table-container" className="relative w-full overflow-x-auto">
35
- <table
36
- ref={ref}
37
- data-slot="table"
38
- className={cn("w-full caption-bottom border-collapse text-sm", className)}
39
- {...props}
40
- />
41
- </div>
42
- ));
43
- Table.displayName = "Table";
30
+ const Table = React.forwardRef<HTMLTableElement, React.ComponentPropsWithoutRef<'table'>>(
31
+ ({ className, ...props }, ref) => (
32
+ <div data-slot="table-container" className="relative w-full overflow-x-auto">
33
+ <table
34
+ ref={ref}
35
+ data-slot="table"
36
+ className={cn('w-full caption-bottom border-collapse text-sm', className)}
37
+ {...props}
38
+ />
39
+ </div>
40
+ ),
41
+ );
42
+ Table.displayName = 'Table';
44
43
 
45
44
  const TableHeader = React.forwardRef<
46
45
  HTMLTableSectionElement,
47
- React.ComponentPropsWithoutRef<"thead">
46
+ React.ComponentPropsWithoutRef<'thead'>
48
47
  >(({ className, ...props }, ref) => (
49
48
  <thead
50
49
  ref={ref}
51
50
  data-slot="table-header"
52
- className={cn("[&_tr]:border-b", className)}
51
+ className={cn('[&_tr]:border-b', className)}
53
52
  {...props}
54
53
  />
55
54
  ));
56
- TableHeader.displayName = "TableHeader";
55
+ TableHeader.displayName = 'TableHeader';
57
56
 
58
57
  const TableBody = React.forwardRef<
59
58
  HTMLTableSectionElement,
60
- React.ComponentPropsWithoutRef<"tbody">
59
+ React.ComponentPropsWithoutRef<'tbody'>
61
60
  >(({ className, ...props }, ref) => (
62
61
  <tbody
63
62
  ref={ref}
64
63
  data-slot="table-body"
65
- className={cn("[&_tr:last-child]:border-0", className)}
64
+ className={cn('[&_tr:last-child]:border-0', className)}
66
65
  {...props}
67
66
  />
68
67
  ));
69
- TableBody.displayName = "TableBody";
68
+ TableBody.displayName = 'TableBody';
70
69
 
71
70
  const TableFooter = React.forwardRef<
72
71
  HTMLTableSectionElement,
73
- React.ComponentPropsWithoutRef<"tfoot">
72
+ React.ComponentPropsWithoutRef<'tfoot'>
74
73
  >(({ className, ...props }, ref) => (
75
74
  <tfoot
76
75
  ref={ref}
77
76
  data-slot="table-footer"
78
- className={cn(
79
- "bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
80
- className
81
- )}
77
+ className={cn('bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', className)}
82
78
  {...props}
83
79
  />
84
80
  ));
85
- TableFooter.displayName = "TableFooter";
81
+ TableFooter.displayName = 'TableFooter';
86
82
 
87
- const TableRow = React.forwardRef<
88
- HTMLTableRowElement,
89
- React.ComponentPropsWithoutRef<"tr">
90
- >(({ className, ...props }, ref) => (
91
- <tr
92
- ref={ref}
93
- data-slot="table-row"
94
- // `group` so sticky cells can react to row hover (OCA keeps the pinned
95
- // Actions cell in step with the row's hover surface).
96
- className={cn(
97
- "group border-b border-border transition-colors hover:bg-muted/40 data-[state=selected]:bg-muted",
98
- className
99
- )}
100
- {...props}
101
- />
102
- ));
103
- TableRow.displayName = "TableRow";
83
+ const TableRow = React.forwardRef<HTMLTableRowElement, React.ComponentPropsWithoutRef<'tr'>>(
84
+ ({ className, ...props }, ref) => (
85
+ <tr
86
+ ref={ref}
87
+ data-slot="table-row"
88
+ // `group` so sticky cells can react to row hover (OCA keeps the pinned
89
+ // Actions cell in step with the row's hover surface).
90
+ className={cn(
91
+ 'group border-b border-border transition-colors hover:bg-muted/40 data-[state=selected]:bg-muted',
92
+ className,
93
+ )}
94
+ {...props}
95
+ />
96
+ ),
97
+ );
98
+ TableRow.displayName = 'TableRow';
104
99
 
105
100
  const TableHead = React.forwardRef<
106
101
  HTMLTableCellElement,
107
- React.ComponentPropsWithoutRef<"th"> & {
102
+ React.ComponentPropsWithoutRef<'th'> & {
108
103
  /** Freeze this header to the right edge (the Actions column). */
109
104
  sticky?: boolean;
110
105
  }
@@ -112,26 +107,26 @@ const TableHead = React.forwardRef<
112
107
  <th
113
108
  ref={ref}
114
109
  data-slot="table-head"
115
- data-sticky={sticky ? "" : undefined}
110
+ data-sticky={sticky ? '' : undefined}
116
111
  className={cn(
117
112
  // OCA thead th: mono-uppercase, tiny, wide-tracked, muted, on a subtle
118
113
  // surface with a hairline bottom border.
119
- "bg-muted/60 text-muted-foreground h-9 px-3.5 text-left align-middle font-mono text-[0.68rem] font-semibold tracking-[0.08em] whitespace-nowrap uppercase [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
114
+ 'bg-muted/60 text-muted-foreground h-9 px-3.5 text-left align-middle font-mono text-[0.68rem] font-semibold tracking-[0.08em] whitespace-nowrap uppercase [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
120
115
  sticky &&
121
116
  // Pinned to the right edge: opaque header surface, sits above the
122
117
  // other sticky-top cells, centered label, token left-border (light) +
123
118
  // left shadow divider (dark).
124
- "bg-muted/60 border-border sticky right-0 z-20 w-[1%] border-l text-center shadow-[-8px_0_10px_-8px_rgba(0,0,0,0.28)]",
125
- className
119
+ 'bg-muted/60 border-border sticky right-0 z-20 w-[1%] border-l text-center shadow-[-8px_0_10px_-8px_rgba(0,0,0,0.28)]',
120
+ className,
126
121
  )}
127
122
  {...props}
128
123
  />
129
124
  ));
130
- TableHead.displayName = "TableHead";
125
+ TableHead.displayName = 'TableHead';
131
126
 
132
127
  const TableCell = React.forwardRef<
133
128
  HTMLTableCellElement,
134
- React.ComponentPropsWithoutRef<"td"> & {
129
+ React.ComponentPropsWithoutRef<'td'> & {
135
130
  /** Freeze this cell to the right edge (the Actions column). */
136
131
  sticky?: boolean;
137
132
  }
@@ -139,43 +134,34 @@ const TableCell = React.forwardRef<
139
134
  <td
140
135
  ref={ref}
141
136
  data-slot="table-cell"
142
- data-sticky={sticky ? "" : undefined}
137
+ data-sticky={sticky ? '' : undefined}
143
138
  className={cn(
144
139
  // OCA tbody td: 11px/14px padding (px-3.5 py-2.5), hairline border,
145
140
  // secondary ink, middle-aligned.
146
- "text-foreground/90 px-3.5 py-2.5 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
141
+ 'text-foreground/90 px-3.5 py-2.5 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
147
142
  sticky &&
148
143
  // Pinned cell: opaque background that tracks the row hover surface
149
144
  // (via the row's `group` + group-hover), centered actions, token
150
145
  // left-border (light) + left shadow (dark).
151
- "bg-background group-hover:bg-muted/40 border-border sticky right-0 z-10 w-[1%] border-l text-center shadow-[-8px_0_10px_-8px_rgba(0,0,0,0.28)]",
152
- className
146
+ 'bg-background group-hover:bg-muted/40 border-border sticky right-0 z-10 w-[1%] border-l text-center shadow-[-8px_0_10px_-8px_rgba(0,0,0,0.28)]',
147
+ className,
153
148
  )}
154
149
  {...props}
155
150
  />
156
151
  ));
157
- TableCell.displayName = "TableCell";
152
+ TableCell.displayName = 'TableCell';
158
153
 
159
154
  const TableCaption = React.forwardRef<
160
155
  HTMLTableCaptionElement,
161
- React.ComponentPropsWithoutRef<"caption">
156
+ React.ComponentPropsWithoutRef<'caption'>
162
157
  >(({ className, ...props }, ref) => (
163
158
  <caption
164
159
  ref={ref}
165
160
  data-slot="table-caption"
166
- className={cn("text-muted-foreground mt-4 text-sm", className)}
161
+ className={cn('text-muted-foreground mt-4 text-sm', className)}
167
162
  {...props}
168
163
  />
169
164
  ));
170
- TableCaption.displayName = "TableCaption";
165
+ TableCaption.displayName = 'TableCaption';
171
166
 
172
- export {
173
- Table,
174
- TableHeader,
175
- TableBody,
176
- TableFooter,
177
- TableHead,
178
- TableRow,
179
- TableCell,
180
- TableCaption,
181
- };
167
+ export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };