create-astram-app 0.5.0 → 0.6.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 (82) hide show
  1. package/README.md +211 -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 +139 -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 +5 -0
  66. package/template/frontend/src/platform/sessionStore.js +55 -13
  67. package/template/frontend/src/platform/sessionStore.test.mjs +89 -0
  68. package/template/frontend/src/platform/shieldTransport.js +6 -0
  69. package/template/frontend/src/platform/shieldTransport.test.mjs +84 -5
  70. package/template/frontend/src/shell/AppShell.jsx +10 -12
  71. package/template/frontend/src/shell/Sidebar.jsx +15 -3
  72. package/template/frontend/src/shell/nav.js +1 -3
  73. package/template/frontend/vite.config.js +19 -1
  74. package/template/server/_env.example +22 -1
  75. package/template/server/eslint.config.mjs +194 -0
  76. package/template/server/package.json +11 -11
  77. package/template/server/scripts/dev.mjs +17 -6
  78. package/template/server/scripts/sync-astram-guide.mjs +45 -19
  79. package/template/server/src/app.module.ts +11 -5
  80. package/template/server/src/health/demo.controller.ts +16 -12
  81. package/template/server/src/main.ts +23 -1
  82. package/template/server/eslint.config.js +0 -95
@@ -1,8 +1,8 @@
1
- import * as React from "react";
2
- import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
1
+ import * as React from 'react';
2
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
3
3
 
4
- import { cn } from "@/lib/utils";
5
- import { buttonVariants } from "@/components/ui/button";
4
+ import { cn } from '@/lib/utils';
5
+ import { buttonVariants } from '@/components/ui/button';
6
6
 
7
7
  /**
8
8
  * Radix AlertDialog — the semantic confirm primitive (Phase B moves
@@ -20,9 +20,7 @@ import { buttonVariants } from "@/components/ui/button";
20
20
  * NOTE: requires `@radix-ui/react-alert-dialog` to be installed.
21
21
  */
22
22
 
23
- function AlertDialog({
24
- ...props
25
- }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
23
+ function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
26
24
  return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
27
25
  }
28
26
 
@@ -30,20 +28,12 @@ const AlertDialogTrigger = React.forwardRef<
30
28
  React.ElementRef<typeof AlertDialogPrimitive.Trigger>,
31
29
  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Trigger>
32
30
  >((props, ref) => (
33
- <AlertDialogPrimitive.Trigger
34
- ref={ref}
35
- data-slot="alert-dialog-trigger"
36
- {...props}
37
- />
31
+ <AlertDialogPrimitive.Trigger ref={ref} data-slot="alert-dialog-trigger" {...props} />
38
32
  ));
39
- AlertDialogTrigger.displayName = "AlertDialogTrigger";
33
+ AlertDialogTrigger.displayName = 'AlertDialogTrigger';
40
34
 
41
- function AlertDialogPortal({
42
- ...props
43
- }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
44
- return (
45
- <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
46
- );
35
+ function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
36
+ return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />;
47
37
  }
48
38
 
49
39
  const AlertDialogOverlay = React.forwardRef<
@@ -54,13 +44,13 @@ const AlertDialogOverlay = React.forwardRef<
54
44
  ref={ref}
55
45
  data-slot="alert-dialog-overlay"
56
46
  className={cn(
57
- "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",
58
- className
47
+ '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',
48
+ className,
59
49
  )}
60
50
  {...props}
61
51
  />
62
52
  ));
63
- AlertDialogOverlay.displayName = "AlertDialogOverlay";
53
+ AlertDialogOverlay.displayName = 'AlertDialogOverlay';
64
54
 
65
55
  const AlertDialogContent = React.forwardRef<
66
56
  React.ElementRef<typeof AlertDialogPrimitive.Content>,
@@ -72,39 +62,30 @@ const AlertDialogContent = React.forwardRef<
72
62
  ref={ref}
73
63
  data-slot="alert-dialog-content"
74
64
  className={cn(
75
- "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",
76
- className
65
+ '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',
66
+ className,
77
67
  )}
78
68
  {...props}
79
69
  />
80
70
  </AlertDialogPortal>
81
71
  ));
82
- AlertDialogContent.displayName = "AlertDialogContent";
72
+ AlertDialogContent.displayName = 'AlertDialogContent';
83
73
 
84
- function AlertDialogHeader({
85
- className,
86
- ...props
87
- }: React.ComponentProps<"div">) {
74
+ function AlertDialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
88
75
  return (
89
76
  <div
90
77
  data-slot="alert-dialog-header"
91
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
78
+ className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
92
79
  {...props}
93
80
  />
94
81
  );
95
82
  }
96
83
 
97
- function AlertDialogFooter({
98
- className,
99
- ...props
100
- }: React.ComponentProps<"div">) {
84
+ function AlertDialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
101
85
  return (
102
86
  <div
103
87
  data-slot="alert-dialog-footer"
104
- className={cn(
105
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
106
- className
107
- )}
88
+ className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
108
89
  {...props}
109
90
  />
110
91
  );
@@ -117,11 +98,11 @@ const AlertDialogTitle = React.forwardRef<
117
98
  <AlertDialogPrimitive.Title
118
99
  ref={ref}
119
100
  data-slot="alert-dialog-title"
120
- className={cn("text-lg leading-none font-semibold", className)}
101
+ className={cn('text-lg leading-none font-semibold', className)}
121
102
  {...props}
122
103
  />
123
104
  ));
124
- AlertDialogTitle.displayName = "AlertDialogTitle";
105
+ AlertDialogTitle.displayName = 'AlertDialogTitle';
125
106
 
126
107
  const AlertDialogDescription = React.forwardRef<
127
108
  React.ElementRef<typeof AlertDialogPrimitive.Description>,
@@ -130,23 +111,19 @@ const AlertDialogDescription = React.forwardRef<
130
111
  <AlertDialogPrimitive.Description
131
112
  ref={ref}
132
113
  data-slot="alert-dialog-description"
133
- className={cn("text-muted-foreground text-sm", className)}
114
+ className={cn('text-muted-foreground text-sm', className)}
134
115
  {...props}
135
116
  />
136
117
  ));
137
- AlertDialogDescription.displayName = "AlertDialogDescription";
118
+ AlertDialogDescription.displayName = 'AlertDialogDescription';
138
119
 
139
120
  const AlertDialogAction = React.forwardRef<
140
121
  React.ElementRef<typeof AlertDialogPrimitive.Action>,
141
122
  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
142
123
  >(({ className, ...props }, ref) => (
143
- <AlertDialogPrimitive.Action
144
- ref={ref}
145
- className={cn(buttonVariants(), className)}
146
- {...props}
147
- />
124
+ <AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
148
125
  ));
149
- AlertDialogAction.displayName = "AlertDialogAction";
126
+ AlertDialogAction.displayName = 'AlertDialogAction';
150
127
 
151
128
  const AlertDialogCancel = React.forwardRef<
152
129
  React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
@@ -154,11 +131,11 @@ const AlertDialogCancel = React.forwardRef<
154
131
  >(({ className, ...props }, ref) => (
155
132
  <AlertDialogPrimitive.Cancel
156
133
  ref={ref}
157
- className={cn(buttonVariants({ variant: "outline" }), className)}
134
+ className={cn(buttonVariants({ variant: 'outline' }), className)}
158
135
  {...props}
159
136
  />
160
137
  ));
161
- AlertDialogCancel.displayName = "AlertDialogCancel";
138
+ AlertDialogCancel.displayName = 'AlertDialogCancel';
162
139
 
163
140
  export {
164
141
  AlertDialog,
@@ -1,7 +1,7 @@
1
- import * as React from "react";
2
- import { cva, type VariantProps } from "class-variance-authority";
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
3
 
4
- import { cn } from "@/lib/utils";
4
+ import { cn } from '@/lib/utils';
5
5
 
6
6
  /**
7
7
  * Inline alert / callout — the shared replacement for the ~8 ad-hoc
@@ -16,31 +16,31 @@ import { cn } from "@/lib/utils";
16
16
  */
17
17
 
18
18
  const alertVariants = cva(
19
- "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5",
19
+ 'relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5',
20
20
  {
21
21
  variants: {
22
22
  variant: {
23
- default: "bg-card text-card-foreground border-border [&>svg]:text-foreground",
23
+ default: 'bg-card text-card-foreground border-border [&>svg]:text-foreground',
24
24
  destructive:
25
- "border-destructive/30 bg-destructive/10 text-destructive [&>svg]:text-destructive [&_[data-slot=alert-description]]:text-destructive/90",
25
+ 'border-destructive/30 bg-destructive/10 text-destructive [&>svg]:text-destructive [&_[data-slot=alert-description]]:text-destructive/90',
26
26
  warning:
27
- "border-warn/30 bg-warn/10 text-warn [&>svg]:text-warn [&_[data-slot=alert-description]]:text-warn/90",
27
+ 'border-warn/30 bg-warn/10 text-warn [&>svg]:text-warn [&_[data-slot=alert-description]]:text-warn/90',
28
28
  success:
29
- "border-ok/30 bg-ok/10 text-ok [&>svg]:text-ok [&_[data-slot=alert-description]]:text-ok/90",
30
- info: "border-primary/30 bg-primary/10 text-primary [&>svg]:text-primary [&_[data-slot=alert-description]]:text-primary/90",
29
+ 'border-ok/30 bg-ok/10 text-ok [&>svg]:text-ok [&_[data-slot=alert-description]]:text-ok/90',
30
+ info: 'border-primary/30 bg-primary/10 text-primary [&>svg]:text-primary [&_[data-slot=alert-description]]:text-primary/90',
31
31
  },
32
32
  },
33
33
  defaultVariants: {
34
- variant: "default",
34
+ variant: 'default',
35
35
  },
36
- }
36
+ },
37
37
  );
38
38
 
39
39
  function Alert({
40
40
  className,
41
41
  variant,
42
42
  ...props
43
- }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
43
+ }: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
44
44
  return (
45
45
  <div
46
46
  data-slot="alert"
@@ -51,29 +51,23 @@ function Alert({
51
51
  );
52
52
  }
53
53
 
54
- function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
54
+ function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
55
55
  return (
56
56
  <div
57
57
  data-slot="alert-title"
58
- className={cn(
59
- "col-start-2 line-clamp-1 min-h-4 font-semibold tracking-tight",
60
- className
61
- )}
58
+ className={cn('col-start-2 line-clamp-1 min-h-4 font-semibold tracking-tight', className)}
62
59
  {...props}
63
60
  />
64
61
  );
65
62
  }
66
63
 
67
- function AlertDescription({
68
- className,
69
- ...props
70
- }: React.ComponentProps<"div">) {
64
+ function AlertDescription({ className, ...props }: React.ComponentProps<'div'>) {
71
65
  return (
72
66
  <div
73
67
  data-slot="alert-description"
74
68
  className={cn(
75
- "text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
76
- className
69
+ 'text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed',
70
+ className,
77
71
  )}
78
72
  {...props}
79
73
  />
@@ -1,32 +1,23 @@
1
- import * as React from "react";
2
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
1
+ import * as React from 'react';
2
+ import * as AvatarPrimitive from '@radix-ui/react-avatar';
3
3
 
4
- import { cn } from "@/lib/utils";
4
+ import { cn } from '@/lib/utils';
5
5
 
6
- function Avatar({
7
- className,
8
- ...props
9
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
6
+ function Avatar({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
10
7
  return (
11
8
  <AvatarPrimitive.Root
12
9
  data-slot="avatar"
13
- className={cn(
14
- "relative flex size-8 shrink-0 overflow-hidden rounded-full",
15
- className
16
- )}
10
+ className={cn('relative flex size-8 shrink-0 overflow-hidden rounded-full', className)}
17
11
  {...props}
18
12
  />
19
13
  );
20
14
  }
21
15
 
22
- function AvatarImage({
23
- className,
24
- ...props
25
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
16
+ function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
26
17
  return (
27
18
  <AvatarPrimitive.Image
28
19
  data-slot="avatar-image"
29
- className={cn("aspect-square size-full", className)}
20
+ className={cn('aspect-square size-full', className)}
30
21
  {...props}
31
22
  />
32
23
  );
@@ -39,10 +30,7 @@ function AvatarFallback({
39
30
  return (
40
31
  <AvatarPrimitive.Fallback
41
32
  data-slot="avatar-fallback"
42
- className={cn(
43
- "bg-muted flex size-full items-center justify-center rounded-full",
44
- className
45
- )}
33
+ className={cn('bg-muted flex size-full items-center justify-center rounded-full', className)}
46
34
  {...props}
47
35
  />
48
36
  );
@@ -1,8 +1,8 @@
1
- import * as React from "react";
2
- import { Slot } from "@radix-ui/react-slot";
3
- import { cva, type VariantProps } from "class-variance-authority";
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
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
  // Pill shape (`rounded-full`) + OCA's `.dsa-badge` density (.72rem-ish text,
8
8
  // weight 600, 3px/10px padding). Existing solid variants kept intact; the new
@@ -10,29 +10,25 @@ import { cn } from "@/lib/utils";
10
10
  // same-family colored text (OCA's tint-bg + colored-label treatment), driven by
11
11
  // `--ok` / `--warn` / `--primary` so light + dark both read correctly.
12
12
  const badgeVariants = cva(
13
- "inline-flex items-center justify-center rounded-full border px-2.5 py-0.5 text-xs font-semibold w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
13
+ 'inline-flex items-center justify-center rounded-full border px-2.5 py-0.5 text-xs font-semibold w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
14
14
  {
15
15
  variants: {
16
16
  variant: {
17
- default:
18
- "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
17
+ default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
19
18
  secondary:
20
- "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
19
+ 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
21
20
  destructive:
22
- "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
23
- outline:
24
- "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
25
- success:
26
- "border-transparent bg-ok/15 text-ok [a&]:hover:bg-ok/20",
27
- warning:
28
- "border-transparent bg-warn/15 text-warn [a&]:hover:bg-warn/20",
29
- info: "border-transparent bg-primary/15 text-primary [a&]:hover:bg-primary/20",
21
+ 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
22
+ outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
23
+ success: 'border-transparent bg-ok/15 text-ok [a&]:hover:bg-ok/20',
24
+ warning: 'border-transparent bg-warn/15 text-warn [a&]:hover:bg-warn/20',
25
+ info: 'border-transparent bg-primary/15 text-primary [a&]:hover:bg-primary/20',
30
26
  },
31
27
  },
32
28
  defaultVariants: {
33
- variant: "default",
29
+ variant: 'default',
34
30
  },
35
- }
31
+ },
36
32
  );
37
33
 
38
34
  function Badge({
@@ -40,16 +36,11 @@ function Badge({
40
36
  variant,
41
37
  asChild = false,
42
38
  ...props
43
- }: React.ComponentProps<"span"> &
44
- VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
45
- const Comp = asChild ? Slot : "span";
39
+ }: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
40
+ const Comp = asChild ? Slot : 'span';
46
41
 
47
42
  return (
48
- <Comp
49
- data-slot="badge"
50
- className={cn(badgeVariants({ variant }), className)}
51
- {...props}
52
- />
43
+ <Comp data-slot="badge" className={cn(badgeVariants({ variant }), className)} {...props} />
53
44
  );
54
45
  }
55
46
 
@@ -1,8 +1,8 @@
1
- import * as React from "react";
2
- import { Slot } from "@radix-ui/react-slot";
3
- import { cva, type VariantProps } from "class-variance-authority";
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
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
  const buttonVariants = cva(
8
8
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
@@ -13,34 +13,32 @@ const buttonVariants = cva(
13
13
  // depth (a crisp `shadow-sm` in light; the dark `.dark [data-slot=button].bg-primary`
14
14
  // rule in index.css supplies the top-light inset + brand glow in dark),
15
15
  // so it reads as a raised, saturated control rather than a flat swatch.
16
- default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
16
+ default: 'bg-primary text-primary-foreground shadow-sm hover:bg-primary/90',
17
17
  destructive:
18
- "bg-destructive text-white shadow-sm hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18
+ 'bg-destructive text-white shadow-sm hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
19
19
  outline:
20
- "border bg-transparent shadow-xs hover:bg-accent dark:bg-transparent dark:border-input dark:hover:bg-input/50",
21
- secondary:
22
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
23
- ghost:
24
- "hover:bg-accent dark:hover:bg-accent/50",
20
+ 'border bg-transparent shadow-xs hover:bg-accent dark:bg-transparent dark:border-input dark:hover:bg-input/50',
21
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
22
+ ghost: 'hover:bg-accent dark:hover:bg-accent/50',
25
23
  // Tonal button (OCA `.oca-btn--soft-primary`): a brand-tinted fill with
26
24
  // brand-colored text — a lighter-weight primary for secondary actions.
27
- soft: "bg-primary/10 text-primary hover:bg-primary/15",
28
- link: "text-primary underline-offset-4 hover:underline",
25
+ soft: 'bg-primary/10 text-primary hover:bg-primary/15',
26
+ link: 'text-primary underline-offset-4 hover:underline',
29
27
  },
30
28
  size: {
31
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
32
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
33
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
34
- icon: "size-9",
35
- "icon-sm": "size-8",
36
- "icon-lg": "size-10",
29
+ default: 'h-9 px-4 py-2 has-[>svg]:px-3',
30
+ sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
31
+ lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
32
+ icon: 'size-9',
33
+ 'icon-sm': 'size-8',
34
+ 'icon-lg': 'size-10',
37
35
  },
38
36
  },
39
37
  defaultVariants: {
40
- variant: "default",
41
- size: "default",
38
+ variant: 'default',
39
+ size: 'default',
42
40
  },
43
- }
41
+ },
44
42
  );
45
43
 
46
44
  function Button({
@@ -49,11 +47,11 @@ function Button({
49
47
  size,
50
48
  asChild = false,
51
49
  ...props
52
- }: React.ComponentProps<"button"> &
50
+ }: React.ComponentProps<'button'> &
53
51
  VariantProps<typeof buttonVariants> & {
54
52
  asChild?: boolean;
55
53
  }) {
56
- const Comp = asChild ? Slot : "button";
54
+ const Comp = asChild ? Slot : 'button';
57
55
 
58
56
  return (
59
57
  <Comp
@@ -1,92 +1,75 @@
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
- function Card({ className, ...props }: React.ComponentProps<"div">) {
5
+ function Card({ className, ...props }: React.ComponentProps<'div'>) {
6
6
  return (
7
7
  <div
8
8
  data-slot="card"
9
9
  className={cn(
10
- "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
11
- className
10
+ 'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
11
+ className,
12
12
  )}
13
13
  {...props}
14
14
  />
15
15
  );
16
16
  }
17
17
 
18
- function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
18
+ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
19
19
  return (
20
20
  <div
21
21
  data-slot="card-header"
22
22
  className={cn(
23
- "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24
- className
23
+ '@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
24
+ className,
25
25
  )}
26
26
  {...props}
27
27
  />
28
28
  );
29
29
  }
30
30
 
31
- function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
31
+ function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
32
32
  return (
33
33
  <div
34
34
  data-slot="card-title"
35
- className={cn("leading-none font-semibold", className)}
35
+ className={cn('leading-none font-semibold', className)}
36
36
  {...props}
37
37
  />
38
38
  );
39
39
  }
40
40
 
41
- function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
41
+ function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
42
42
  return (
43
43
  <div
44
44
  data-slot="card-description"
45
- className={cn("text-muted-foreground text-sm", className)}
45
+ className={cn('text-muted-foreground text-sm', className)}
46
46
  {...props}
47
47
  />
48
48
  );
49
49
  }
50
50
 
51
- function CardAction({ className, ...props }: React.ComponentProps<"div">) {
51
+ function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
52
52
  return (
53
53
  <div
54
54
  data-slot="card-action"
55
- className={cn(
56
- "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57
- className
58
- )}
55
+ className={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}
59
56
  {...props}
60
57
  />
61
58
  );
62
59
  }
63
60
 
64
- function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65
- return (
66
- <div
67
- data-slot="card-content"
68
- className={cn("px-6", className)}
69
- {...props}
70
- />
71
- );
61
+ function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
62
+ return <div data-slot="card-content" className={cn('px-6', className)} {...props} />;
72
63
  }
73
64
 
74
- function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
65
+ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
75
66
  return (
76
67
  <div
77
68
  data-slot="card-footer"
78
- className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
69
+ className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
79
70
  {...props}
80
71
  />
81
72
  );
82
73
  }
83
74
 
84
- export {
85
- Card,
86
- CardHeader,
87
- CardFooter,
88
- CardTitle,
89
- CardAction,
90
- CardDescription,
91
- CardContent,
92
- };
75
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };
@@ -1,19 +1,16 @@
1
- import * as React from "react";
2
- import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
3
- import { CheckIcon } from "lucide-react";
1
+ import * as React from 'react';
2
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
3
+ import { CheckIcon } from 'lucide-react';
4
4
 
5
- import { cn } from "@/lib/utils";
5
+ import { cn } from '@/lib/utils';
6
6
 
7
- function Checkbox({
8
- className,
9
- ...props
10
- }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
7
+ function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
11
8
  return (
12
9
  <CheckboxPrimitive.Root
13
10
  data-slot="checkbox"
14
11
  className={cn(
15
- "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
16
- className
12
+ 'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
13
+ className,
17
14
  )}
18
15
  {...props}
19
16
  >