nextworks 0.0.1 → 0.1.0-alpha.1
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.
- package/README.md +209 -30
- package/dist/.gitkeep +0 -0
- package/dist/cli_manifests/auth_manifest.json +86 -0
- package/dist/cli_manifests/blocks_manifest.json +185 -0
- package/dist/cli_manifests/data_manifest.json +51 -0
- package/dist/cli_manifests/forms_manifest.json +61 -0
- package/dist/commands/admin-posts.d.ts +2 -0
- package/dist/commands/admin-posts.d.ts.map +1 -0
- package/dist/commands/admin-posts.js +15 -0
- package/dist/commands/admin-posts.js.map +1 -0
- package/dist/commands/admin-users.d.ts +2 -0
- package/dist/commands/admin-users.d.ts.map +1 -0
- package/dist/commands/admin-users.js +15 -0
- package/dist/commands/admin-users.js.map +1 -0
- package/dist/commands/auth-core.d.ts +2 -0
- package/dist/commands/auth-core.d.ts.map +1 -0
- package/dist/commands/auth-core.js +83 -0
- package/dist/commands/auth-core.js.map +1 -0
- package/dist/commands/auth-forms.d.ts +2 -0
- package/dist/commands/auth-forms.d.ts.map +1 -0
- package/dist/commands/auth-forms.js +15 -0
- package/dist/commands/auth-forms.js.map +1 -0
- package/dist/commands/blocks-options.d.ts +7 -0
- package/dist/commands/blocks-options.d.ts.map +1 -0
- package/dist/commands/blocks-options.js +19 -0
- package/dist/commands/blocks-options.js.map +1 -0
- package/dist/commands/blocks.d.ts +7 -0
- package/dist/commands/blocks.d.ts.map +1 -0
- package/dist/commands/blocks.js +145 -0
- package/dist/commands/blocks.js.map +1 -0
- package/dist/commands/data.d.ts +3 -0
- package/dist/commands/data.d.ts.map +1 -0
- package/dist/commands/data.js +88 -0
- package/dist/commands/data.js.map +1 -0
- package/dist/commands/forms.d.ts +6 -0
- package/dist/commands/forms.d.ts.map +1 -0
- package/dist/commands/forms.js +107 -0
- package/dist/commands/forms.js.map +1 -0
- package/dist/commands/remove-auth-core.d.ts +2 -0
- package/dist/commands/remove-auth-core.d.ts.map +1 -0
- package/dist/commands/remove-auth-core.js +69 -0
- package/dist/commands/remove-auth-core.js.map +1 -0
- package/dist/commands/remove-blocks.d.ts +2 -0
- package/dist/commands/remove-blocks.d.ts.map +1 -0
- package/dist/commands/remove-blocks.js +36 -0
- package/dist/commands/remove-blocks.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/kits/auth-core/README.md +106 -0
- package/dist/kits/auth-core/app/(protected)/dashboard/page.tsx +8 -0
- package/dist/kits/auth-core/app/(protected)/layout.tsx +18 -0
- package/dist/kits/auth-core/app/(protected)/settings/profile/page.tsx +15 -0
- package/dist/kits/auth-core/app/(protected)/settings/profile/profile-form.tsx +114 -0
- package/dist/kits/auth-core/app/api/auth/[...nextauth]/route.ts +1 -0
- package/dist/kits/auth-core/app/api/auth/forgot-password/route.ts +114 -0
- package/dist/kits/auth-core/app/api/auth/providers/route.ts +6 -0
- package/dist/kits/auth-core/app/api/auth/reset-password/route.ts +63 -0
- package/dist/kits/auth-core/app/api/auth/send-verify-email/route.ts +6 -0
- package/dist/kits/auth-core/app/api/signup/route.ts +41 -0
- package/dist/kits/auth-core/app/auth/forgot-password/page.tsx +21 -0
- package/dist/kits/auth-core/app/auth/login/page.tsx +5 -0
- package/dist/kits/auth-core/app/auth/reset-password/page.tsx +187 -0
- package/dist/kits/auth-core/app/auth/signup/page.tsx +5 -0
- package/dist/kits/auth-core/app/auth/verify-email/page.tsx +11 -0
- package/dist/kits/auth-core/components/admin/admin-header.tsx +57 -0
- package/dist/kits/auth-core/components/auth/dashboard.tsx +237 -0
- package/dist/kits/auth-core/components/auth/forgot-password-form.tsx +90 -0
- package/dist/kits/auth-core/components/auth/login-form.tsx +467 -0
- package/dist/kits/auth-core/components/auth/logout-button.tsx +50 -0
- package/dist/kits/auth-core/components/auth/minimal-logout-button.tsx +40 -0
- package/dist/kits/auth-core/components/auth/signup-form.tsx +468 -0
- package/dist/kits/auth-core/components/require-auth.tsx +59 -0
- package/dist/kits/auth-core/components/session-provider.tsx +11 -0
- package/dist/kits/auth-core/components/ui/README.txt +1 -0
- package/dist/kits/auth-core/components/ui/button.tsx +55 -0
- package/dist/kits/auth-core/components/ui/input.tsx +25 -0
- package/dist/kits/auth-core/components/ui/label.tsx +23 -0
- package/dist/kits/auth-core/lib/api/errors.ts +14 -0
- package/dist/kits/auth-core/lib/auth-helpers.ts +29 -0
- package/dist/kits/auth-core/lib/auth.ts +142 -0
- package/dist/kits/auth-core/lib/email/dev-transport.ts +42 -0
- package/dist/kits/auth-core/lib/email/index.ts +28 -0
- package/dist/kits/auth-core/lib/email/provider-smtp.ts +36 -0
- package/dist/kits/auth-core/lib/forms/map-errors.ts +11 -0
- package/dist/kits/auth-core/lib/hash.ts +6 -0
- package/dist/kits/auth-core/lib/prisma.ts +15 -0
- package/dist/kits/auth-core/lib/server/result.ts +45 -0
- package/dist/kits/auth-core/lib/utils.ts +6 -0
- package/dist/kits/auth-core/lib/validation/forms.ts +88 -0
- package/dist/kits/auth-core/package-deps.json +19 -0
- package/dist/kits/auth-core/prisma/auth-models.prisma +81 -0
- package/dist/kits/auth-core/prisma/schema.prisma +81 -0
- package/dist/kits/auth-core/scripts/populate-tokenhash.mjs +26 -0
- package/dist/kits/auth-core/scripts/promote-admin.mjs +33 -0
- package/dist/kits/auth-core/scripts/seed-demo.mjs +40 -0
- package/dist/kits/auth-core/types/next-auth.d.ts +25 -0
- package/dist/kits/blocks/README.md +53 -0
- package/dist/kits/blocks/app/globals.css +175 -0
- package/dist/kits/blocks/app/templates/digitalagency/PresetThemeVars.tsx +80 -0
- package/dist/kits/blocks/app/templates/digitalagency/README.md +36 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/About.tsx +99 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/CTA.tsx +74 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Contact.tsx +227 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Footer.tsx +89 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Hero.tsx +90 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Navbar.tsx +168 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/NetworkPattern.tsx +297 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Portfolio.tsx +157 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Pricing.tsx +114 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Process.tsx +59 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Services.tsx +55 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Team.tsx +28 -0
- package/dist/kits/blocks/app/templates/digitalagency/components/Testimonials.tsx +65 -0
- package/dist/kits/blocks/app/templates/digitalagency/page.tsx +38 -0
- package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +85 -0
- package/dist/kits/blocks/app/templates/gallery/page.tsx +303 -0
- package/dist/kits/blocks/app/templates/productlaunch/PresetThemeVars.tsx +74 -0
- package/dist/kits/blocks/app/templates/productlaunch/README.md +55 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/About.tsx +178 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/CTA.tsx +93 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/Contact.tsx +231 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/FAQ.tsx +93 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/Features.tsx +84 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/Footer.tsx +132 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/Hero.tsx +89 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/Navbar.tsx +162 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/Pricing.tsx +106 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/ProcessTimeline.tsx +110 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/ServicesGrid.tsx +68 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/Team.tsx +104 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/Testimonials.tsx +89 -0
- package/dist/kits/blocks/app/templates/productlaunch/components/TrustBadges.tsx +76 -0
- package/dist/kits/blocks/app/templates/productlaunch/page.tsx +45 -0
- package/dist/kits/blocks/app/templates/saasdashboard/PresetThemeVars.tsx +80 -0
- package/dist/kits/blocks/app/templates/saasdashboard/README.md +38 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Contact.tsx +176 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Dashboard.tsx +293 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/FAQ.tsx +55 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Features.tsx +91 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Footer.tsx +77 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Hero.tsx +105 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Hero_mask.tsx +127 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Navbar.tsx +159 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Pricing.tsx +90 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/SmoothScroll.tsx +97 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/Testimonials.tsx +72 -0
- package/dist/kits/blocks/app/templates/saasdashboard/components/TrustBadges.tsx +53 -0
- package/dist/kits/blocks/app/templates/saasdashboard/page.tsx +39 -0
- package/dist/kits/blocks/components/app-providers.tsx +1 -0
- package/dist/kits/blocks/components/enhanced-theme-provider.tsx +195 -0
- package/dist/kits/blocks/components/sections/About.tsx +291 -0
- package/dist/kits/blocks/components/sections/CTA.tsx +258 -0
- package/dist/kits/blocks/components/sections/Contact.tsx +267 -0
- package/dist/kits/blocks/components/sections/FAQ.tsx +226 -0
- package/dist/kits/blocks/components/sections/Features.tsx +269 -0
- package/dist/kits/blocks/components/sections/Footer.tsx +302 -0
- package/dist/kits/blocks/components/sections/HeroMotion.tsx +307 -0
- package/dist/kits/blocks/components/sections/HeroOverlay.tsx +358 -0
- package/dist/kits/blocks/components/sections/HeroSplit.tsx +352 -0
- package/dist/kits/blocks/components/sections/Navbar.tsx +353 -0
- package/dist/kits/blocks/components/sections/Newsletter.tsx +156 -0
- package/dist/kits/blocks/components/sections/PortfolioSimple.tsx +550 -0
- package/dist/kits/blocks/components/sections/Pricing.tsx +264 -0
- package/dist/kits/blocks/components/sections/ProcessTimeline.tsx +325 -0
- package/dist/kits/blocks/components/sections/ServicesGrid.tsx +210 -0
- package/dist/kits/blocks/components/sections/Team.tsx +309 -0
- package/dist/kits/blocks/components/sections/Testimonials.tsx +158 -0
- package/dist/kits/blocks/components/sections/TrustBadges.tsx +162 -0
- package/dist/kits/blocks/components/theme-provider.tsx +34 -0
- package/dist/kits/blocks/components/ui/alert-dialog.tsx +134 -0
- package/dist/kits/blocks/components/ui/brand-node.tsx +121 -0
- package/dist/kits/blocks/components/ui/button.tsx +122 -0
- package/dist/kits/blocks/components/ui/button_bck.tsx +93 -0
- package/dist/kits/blocks/components/ui/card.tsx +95 -0
- package/dist/kits/blocks/components/ui/checkbox.tsx +30 -0
- package/dist/kits/blocks/components/ui/cta-button.tsx +125 -0
- package/dist/kits/blocks/components/ui/dropdown-menu.tsx +201 -0
- package/dist/kits/blocks/components/ui/feature-card.tsx +91 -0
- package/dist/kits/blocks/components/ui/input.tsx +27 -0
- package/dist/kits/blocks/components/ui/label.tsx +29 -0
- package/dist/kits/blocks/components/ui/pricing-card.tsx +120 -0
- package/dist/kits/blocks/components/ui/select.tsx +25 -0
- package/dist/kits/blocks/components/ui/skeleton.tsx +13 -0
- package/dist/kits/blocks/components/ui/switch.tsx +78 -0
- package/dist/kits/blocks/components/ui/table.tsx +98 -0
- package/dist/kits/blocks/components/ui/testimonial-card.tsx +108 -0
- package/dist/kits/blocks/components/ui/textarea.tsx +26 -0
- package/dist/kits/blocks/components/ui/theme-selector.tsx +247 -0
- package/dist/kits/blocks/components/ui/theme-toggle.tsx +74 -0
- package/dist/kits/blocks/components/ui/toaster.tsx +7 -0
- package/dist/kits/blocks/lib/themes.ts +399 -0
- package/dist/kits/blocks/lib/themes_old.ts +37 -0
- package/dist/kits/blocks/lib/utils.ts +9 -0
- package/dist/kits/blocks/next.config.ts +11 -0
- package/dist/kits/blocks/notes/THEME_GUIDE.md +29 -0
- package/dist/kits/blocks/notes/THEMING_CONVERSION_SUMMARY.md +14 -0
- package/dist/kits/blocks/package-deps.json +22 -0
- package/dist/kits/blocks/public/placeholders/gallery/hero-pexels-broken-9945014.avif +0 -0
- package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626431.jpg +0 -0
- package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626432.jpg +0 -0
- package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626434.jpg +0 -0
- package/dist/kits/blocks/public/placeholders/gallery/pexels-googledeepmind-25626436.jpg +0 -0
- package/dist/kits/blocks/public/placeholders/product_launch/feature_1.png +0 -0
- package/dist/kits/blocks/public/placeholders/product_launch/feature_2.png +0 -0
- package/dist/kits/blocks/public/placeholders/product_launch/feature_3.png +0 -0
- package/dist/kits/blocks/public/placeholders/product_launch/feature_4.png +0 -0
- package/dist/kits/blocks/public/placeholders/product_launch/hero.png +0 -0
- package/dist/kits/blocks/public/placeholders/saas_dashboard/analytics.png +0 -0
- package/dist/kits/blocks/public/placeholders/saas_dashboard/chat.png +0 -0
- package/dist/kits/blocks/public/placeholders/saas_dashboard/projectBoard.png +0 -0
- package/dist/kits/data/.gitkeep +0 -0
- package/dist/kits/data/README.md +104 -0
- package/dist/kits/data/app/(protected)/admin/posts/page.tsx +5 -0
- package/dist/kits/data/app/(protected)/admin/users/page.tsx +5 -0
- package/dist/kits/data/app/api/posts/[id]/route.ts +83 -0
- package/dist/kits/data/app/api/posts/route.ts +138 -0
- package/dist/kits/data/app/api/seed-demo/route.ts +45 -0
- package/dist/kits/data/app/api/users/[id]/route.ts +127 -0
- package/dist/kits/data/app/api/users/check-email/route.ts +18 -0
- package/dist/kits/data/app/api/users/check-unique/route.ts +27 -0
- package/dist/kits/data/app/api/users/route.ts +79 -0
- package/dist/kits/data/app/examples/demo/README.md +4 -0
- package/dist/kits/data/app/examples/demo/create-post-form.tsx +106 -0
- package/dist/kits/data/app/examples/demo/page.tsx +118 -0
- package/dist/kits/data/app/examples/demo/seed-demo-button.tsx +37 -0
- package/dist/kits/data/components/admin/posts-manager.tsx +719 -0
- package/dist/kits/data/components/admin/users-manager.tsx +432 -0
- package/dist/kits/data/lib/prisma.ts +15 -0
- package/dist/kits/data/lib/server/result.ts +90 -0
- package/dist/kits/data/package-deps.json +11 -0
- package/dist/kits/data/scripts/seed-demo.mjs +41 -0
- package/dist/kits/forms/.gitkeep +0 -0
- package/dist/kits/forms/README.md +49 -0
- package/dist/kits/forms/app/.gitkeep +0 -0
- package/dist/kits/forms/app/api/wizard/route.ts +71 -0
- package/dist/kits/forms/app/examples/forms/basic/page.tsx +124 -0
- package/dist/kits/forms/app/examples/forms/server-action/form-client.tsx +28 -0
- package/dist/kits/forms/app/examples/forms/server-action/page.tsx +71 -0
- package/dist/kits/forms/app/examples/forms/wizard/page.tsx +15 -0
- package/dist/kits/forms/app/examples/forms/wizard/wizard-client.tsx +2 -0
- package/dist/kits/forms/components/.gitkeep +0 -0
- package/dist/kits/forms/components/examples/wizard-client.tsx +231 -0
- package/dist/kits/forms/components/hooks/useCheckUnique.ts +79 -0
- package/dist/kits/forms/components/ui/button.tsx +122 -0
- package/dist/kits/forms/components/ui/checkbox.tsx +30 -0
- package/dist/kits/forms/components/ui/form/context.ts +33 -0
- package/dist/kits/forms/components/ui/form/form-control.tsx +28 -0
- package/dist/kits/forms/components/ui/form/form-description.tsx +22 -0
- package/dist/kits/forms/components/ui/form/form-field.tsx +36 -0
- package/dist/kits/forms/components/ui/form/form-item.tsx +21 -0
- package/dist/kits/forms/components/ui/form/form-label.tsx +24 -0
- package/dist/kits/forms/components/ui/form/form-message.tsx +29 -0
- package/dist/kits/forms/components/ui/form/form.tsx +26 -0
- package/dist/kits/forms/components/ui/input.tsx +27 -0
- package/dist/kits/forms/components/ui/label.tsx +29 -0
- package/dist/kits/forms/components/ui/select.tsx +25 -0
- package/dist/kits/forms/components/ui/switch.tsx +78 -0
- package/dist/kits/forms/components/ui/textarea.tsx +26 -0
- package/dist/kits/forms/lib/.gitkeep +0 -0
- package/dist/kits/forms/lib/forms/map-errors.ts +29 -0
- package/dist/kits/forms/lib/prisma.ts +16 -0
- package/dist/kits/forms/lib/utils.ts +9 -0
- package/dist/kits/forms/lib/validation/forms.ts +88 -0
- package/dist/kits/forms/lib/validation/wizard.ts +32 -0
- package/dist/kits/forms/package-deps.json +17 -0
- package/dist/utils/file-operations.d.ts +18 -0
- package/dist/utils/file-operations.d.ts.map +1 -0
- package/dist/utils/file-operations.js +327 -0
- package/dist/utils/file-operations.js.map +1 -0
- package/dist/utils/installation-tracker.d.ts +26 -0
- package/dist/utils/installation-tracker.d.ts.map +1 -0
- package/dist/utils/installation-tracker.js +98 -0
- package/dist/utils/installation-tracker.js.map +1 -0
- package/package.json +51 -21
- package/index.js +0 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 shadow-xs",
|
|
16
|
+
outline:
|
|
17
|
+
"bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border shadow-xs",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-xs",
|
|
20
|
+
ghost:
|
|
21
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
26
|
+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
27
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
28
|
+
icon: "size-9",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
variant: "default",
|
|
33
|
+
size: "default",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
type ButtonProps = React.ComponentProps<"button"> &
|
|
39
|
+
VariantProps<typeof buttonVariants> & {
|
|
40
|
+
asChild?: boolean;
|
|
41
|
+
/** When true, bypasses tokenized buttonVariants so callers fully control classes */
|
|
42
|
+
unstyled?: boolean;
|
|
43
|
+
/** Opt-in: force inline CSS var styles for color/bg/border/ring */
|
|
44
|
+
forceInlineVars?: boolean;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
48
|
+
(
|
|
49
|
+
{
|
|
50
|
+
className,
|
|
51
|
+
variant,
|
|
52
|
+
size,
|
|
53
|
+
asChild = false,
|
|
54
|
+
unstyled = false,
|
|
55
|
+
forceInlineVars = false,
|
|
56
|
+
...props
|
|
57
|
+
},
|
|
58
|
+
ref,
|
|
59
|
+
) => {
|
|
60
|
+
const Comp = asChild ? Slot : "button";
|
|
61
|
+
|
|
62
|
+
// Use caller-provided style; only inject inline var-driven colors when explicitly requested
|
|
63
|
+
const incomingStyle =
|
|
64
|
+
(props.style as React.CSSProperties | undefined) ?? undefined;
|
|
65
|
+
const finalStyle =
|
|
66
|
+
forceInlineVars && !unstyled
|
|
67
|
+
? {
|
|
68
|
+
...incomingStyle,
|
|
69
|
+
color: "var(--btn-fg)",
|
|
70
|
+
backgroundColor: "var(--btn-bg)",
|
|
71
|
+
borderColor: "var(--btn-border)",
|
|
72
|
+
"--tw-ring-color": "var(--btn-ring)",
|
|
73
|
+
}
|
|
74
|
+
: incomingStyle;
|
|
75
|
+
|
|
76
|
+
// Only enable CSS variable hooks when explicitly requested via inline vars
|
|
77
|
+
// or when the caller sets any [--btn-*] classes in className.
|
|
78
|
+
const wantsVarHooks =
|
|
79
|
+
!unstyled &&
|
|
80
|
+
(forceInlineVars ||
|
|
81
|
+
(typeof className === "string" && className.includes("[--btn-")));
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Comp
|
|
85
|
+
ref={ref}
|
|
86
|
+
data-slot="button"
|
|
87
|
+
className={
|
|
88
|
+
unstyled
|
|
89
|
+
? cn(
|
|
90
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap disabled:pointer-events-none disabled:opacity-50",
|
|
91
|
+
className,
|
|
92
|
+
)
|
|
93
|
+
: cn(
|
|
94
|
+
buttonVariants({ variant, size }),
|
|
95
|
+
wantsVarHooks && [
|
|
96
|
+
// Color var hooks (apply only when CSS vars are provided)
|
|
97
|
+
"text-[var(--btn-fg)]",
|
|
98
|
+
"bg-[var(--btn-bg)]",
|
|
99
|
+
"hover:bg-[var(--btn-hover-bg)]",
|
|
100
|
+
"hover:text-[var(--btn-hover-fg)]",
|
|
101
|
+
// explicit dark variants to compete with dark: utilities from variants like outline
|
|
102
|
+
"dark:bg-[var(--btn-bg)]",
|
|
103
|
+
"dark:hover:bg-[var(--btn-hover-bg)]",
|
|
104
|
+
"dark:hover:text-[var(--btn-hover-fg)]",
|
|
105
|
+
// Focus ring and border hooks
|
|
106
|
+
"focus-visible:ring-[var(--btn-ring)]",
|
|
107
|
+
"border-[var(--btn-border)]",
|
|
108
|
+
"dark:border-[var(--btn-border)]",
|
|
109
|
+
],
|
|
110
|
+
className,
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
style={finalStyle}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
Button.displayName = "Button";
|
|
121
|
+
|
|
122
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
export type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement>;
|
|
5
|
+
|
|
6
|
+
const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
|
|
7
|
+
({ className, ...props }, ref) => {
|
|
8
|
+
// Use the native accent-color where supported for consistent checkbox fill
|
|
9
|
+
const style: React.CSSProperties = { accentColor: "var(--primary)" }; // fallback; modern browsers will use this accent color
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<input
|
|
13
|
+
type="checkbox"
|
|
14
|
+
ref={ref}
|
|
15
|
+
style={style}
|
|
16
|
+
className={cn(
|
|
17
|
+
// Keep layout small but add smooth transitions and focus ring
|
|
18
|
+
"h-4 w-4 rounded border-[var(--input-border)] bg-[var(--input-bg)] text-[var(--input-fg)] transition-colors duration-200 ease-in-out",
|
|
19
|
+
"focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:outline-none",
|
|
20
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
21
|
+
className,
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
Checkbox.displayName = "Checkbox";
|
|
29
|
+
|
|
30
|
+
export { Checkbox };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useFormContext } from "react-hook-form";
|
|
3
|
+
|
|
4
|
+
export const FormFieldContext = React.createContext<{ name: string } | null>(
|
|
5
|
+
null,
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export const FormItemContext = React.createContext<{ id: string } | null>(null);
|
|
9
|
+
|
|
10
|
+
export function useFormField() {
|
|
11
|
+
const fieldContext = React.useContext(FormFieldContext);
|
|
12
|
+
const itemContext = React.useContext(FormItemContext);
|
|
13
|
+
const { getFieldState, formState } = useFormContext();
|
|
14
|
+
|
|
15
|
+
if (!fieldContext) {
|
|
16
|
+
throw new Error("useFormField should be used within <FormField>");
|
|
17
|
+
}
|
|
18
|
+
if (!itemContext) {
|
|
19
|
+
throw new Error("useFormField should be used within <FormItem>");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const fieldState = getFieldState(fieldContext.name, formState);
|
|
23
|
+
const id = itemContext.id;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
id,
|
|
27
|
+
name: fieldContext.name,
|
|
28
|
+
formItemId: `${id}-form-item`,
|
|
29
|
+
formDescriptionId: `${id}-form-item-description`,
|
|
30
|
+
formMessageId: `${id}-form-item-message`,
|
|
31
|
+
...fieldState,
|
|
32
|
+
} as const;
|
|
33
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useFormField } from "./context";
|
|
5
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
6
|
+
|
|
7
|
+
export interface FormControlProps
|
|
8
|
+
extends React.ComponentPropsWithoutRef<typeof Slot> {}
|
|
9
|
+
|
|
10
|
+
export const FormControl = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof Slot>,
|
|
12
|
+
FormControlProps
|
|
13
|
+
>(({ ...props }, ref) => {
|
|
14
|
+
const { formItemId, formDescriptionId, formMessageId, error } =
|
|
15
|
+
useFormField();
|
|
16
|
+
return (
|
|
17
|
+
<Slot
|
|
18
|
+
ref={ref}
|
|
19
|
+
id={formItemId}
|
|
20
|
+
aria-describedby={
|
|
21
|
+
error ? `${formDescriptionId} ${formMessageId}` : formDescriptionId
|
|
22
|
+
}
|
|
23
|
+
aria-invalid={!!error}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
FormControl.displayName = "FormControl";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useFormField } from "./context";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
export interface FormDescriptionProps
|
|
8
|
+
extends React.HTMLAttributes<HTMLParagraphElement> {}
|
|
9
|
+
|
|
10
|
+
export const FormDescription = ({
|
|
11
|
+
className,
|
|
12
|
+
...props
|
|
13
|
+
}: FormDescriptionProps) => {
|
|
14
|
+
const { formDescriptionId } = useFormField();
|
|
15
|
+
return (
|
|
16
|
+
<p
|
|
17
|
+
id={formDescriptionId}
|
|
18
|
+
className={cn("text-xs text-muted-foreground", className)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
Controller,
|
|
6
|
+
type ControllerProps,
|
|
7
|
+
type FieldPath,
|
|
8
|
+
type FieldValues,
|
|
9
|
+
} from "react-hook-form";
|
|
10
|
+
import { FormFieldContext } from "./context";
|
|
11
|
+
|
|
12
|
+
export interface FormFieldProps<
|
|
13
|
+
TFieldValues extends FieldValues,
|
|
14
|
+
TName extends FieldPath<TFieldValues>,
|
|
15
|
+
> extends Omit<ControllerProps<TFieldValues, TName>, "render"> {
|
|
16
|
+
render: ControllerProps<TFieldValues, TName>["render"];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function FormField<
|
|
20
|
+
TFieldValues extends FieldValues,
|
|
21
|
+
TName extends FieldPath<TFieldValues>,
|
|
22
|
+
>(props: FormFieldProps<TFieldValues, TName>) {
|
|
23
|
+
const { name, control, rules, shouldUnregister, render } = props;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<FormFieldContext.Provider value={{ name: name as string }}>
|
|
27
|
+
<Controller
|
|
28
|
+
name={name}
|
|
29
|
+
control={control}
|
|
30
|
+
rules={rules}
|
|
31
|
+
shouldUnregister={shouldUnregister}
|
|
32
|
+
render={render}
|
|
33
|
+
/>
|
|
34
|
+
</FormFieldContext.Provider>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import { FormItemContext } from "./context";
|
|
6
|
+
|
|
7
|
+
export interface FormItemProps
|
|
8
|
+
extends React.HTMLAttributes<HTMLDivElement> {}
|
|
9
|
+
|
|
10
|
+
export const FormItem = React.forwardRef<HTMLDivElement, FormItemProps>(
|
|
11
|
+
({ className, ...props }, ref) => {
|
|
12
|
+
const id = React.useId();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<FormItemContext.Provider value={{ id }}>
|
|
16
|
+
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
|
17
|
+
</FormItemContext.Provider>
|
|
18
|
+
);
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
FormItem.displayName = "FormItem";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Label } from "@/components/ui/label";
|
|
5
|
+
import { useFormField } from "./context";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
export interface FormLabelProps extends React.ComponentProps<typeof Label> {}
|
|
9
|
+
|
|
10
|
+
export const FormLabel = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof Label>,
|
|
12
|
+
FormLabelProps
|
|
13
|
+
>(({ className, ...props }, ref) => {
|
|
14
|
+
const { formItemId } = useFormField();
|
|
15
|
+
return (
|
|
16
|
+
<Label
|
|
17
|
+
ref={ref}
|
|
18
|
+
htmlFor={formItemId}
|
|
19
|
+
className={cn(className)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
FormLabel.displayName = "FormLabel";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useFormField } from "./context";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
export interface FormMessageProps
|
|
8
|
+
extends React.HTMLAttributes<HTMLParagraphElement> {}
|
|
9
|
+
|
|
10
|
+
export const FormMessage = ({
|
|
11
|
+
className,
|
|
12
|
+
children,
|
|
13
|
+
...props
|
|
14
|
+
}: FormMessageProps) => {
|
|
15
|
+
const { error, formMessageId } = useFormField();
|
|
16
|
+
const body = error ? String(error.message || children) : children;
|
|
17
|
+
|
|
18
|
+
if (!body) return null;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<p
|
|
22
|
+
id={formMessageId}
|
|
23
|
+
className={cn("text-xs text-destructive", className)}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
{body}
|
|
27
|
+
</p>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
FormProvider,
|
|
6
|
+
type UseFormReturn,
|
|
7
|
+
type FieldValues,
|
|
8
|
+
} from "react-hook-form";
|
|
9
|
+
|
|
10
|
+
export interface FormProps<TFieldValues extends FieldValues = FieldValues> {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
methods: UseFormReturn<TFieldValues>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Form<TFieldValues extends FieldValues = FieldValues>({
|
|
17
|
+
children,
|
|
18
|
+
className,
|
|
19
|
+
methods,
|
|
20
|
+
}: FormProps<TFieldValues>) {
|
|
21
|
+
return (
|
|
22
|
+
<FormProvider<TFieldValues> {...methods}>
|
|
23
|
+
<div className={className}>{children}</div>
|
|
24
|
+
</FormProvider>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
|
6
|
+
|
|
7
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
8
|
+
({ className, type, ...props }, ref) => {
|
|
9
|
+
return (
|
|
10
|
+
<input
|
|
11
|
+
type={type}
|
|
12
|
+
className={cn(
|
|
13
|
+
// Base structural + token fallbacks
|
|
14
|
+
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive/30 dark:aria-invalid:focus-visible:ring-destructive/40 flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
15
|
+
// CSS variable hooks (preset-first). When vars are unset, these are ignored and tokens above apply.
|
|
16
|
+
"focus-visible:ring-offset-background border-[var(--input-border)] bg-[var(--input-bg)] text-[var(--input-fg)] placeholder:text-[var(--input-placeholder)] focus-visible:ring-[var(--input-focus-ring,var(--ring))] focus-visible:ring-offset-2",
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
ref={ref}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
Input.displayName = "Input";
|
|
26
|
+
|
|
27
|
+
export { Input };
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const labelVariants = cva(
|
|
8
|
+
"text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const Label = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
14
|
+
VariantProps<typeof labelVariants>
|
|
15
|
+
>(({ className, ...props }, ref) => (
|
|
16
|
+
<LabelPrimitive.Root
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cn(
|
|
19
|
+
labelVariants(),
|
|
20
|
+
// Optional color override via var; falls back to inherited color when unset
|
|
21
|
+
"text-[var(--label-fg)]",
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
));
|
|
27
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
28
|
+
|
|
29
|
+
export { Label };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
export type SelectProps = React.SelectHTMLAttributes<HTMLSelectElement>;
|
|
5
|
+
|
|
6
|
+
const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
|
|
7
|
+
({ className, children, ...props }, ref) => {
|
|
8
|
+
return (
|
|
9
|
+
<select
|
|
10
|
+
ref={ref}
|
|
11
|
+
className={cn(
|
|
12
|
+
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive/30 dark:aria-invalid:focus-visible:ring-destructive/40 flex h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
13
|
+
"bg-[var(--input-bg)] text-[var(--input-fg)]",
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
>
|
|
18
|
+
{children}
|
|
19
|
+
</select>
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
Select.displayName = "Select";
|
|
24
|
+
|
|
25
|
+
export { Select };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
export type SwitchProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
|
5
|
+
isLoading?: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
|
|
9
|
+
({ className, isLoading, disabled, ...props }, ref) => {
|
|
10
|
+
const checked = !!(props as any).checked;
|
|
11
|
+
const isDisabled = !!disabled || !!isLoading;
|
|
12
|
+
return (
|
|
13
|
+
<label
|
|
14
|
+
className={cn(
|
|
15
|
+
"focus-within:ring-offset-background inline-flex items-center rounded-full focus-within:ring-2 focus-within:ring-[var(--ring)] focus-within:ring-offset-2",
|
|
16
|
+
isDisabled ? "cursor-not-allowed opacity-80" : "cursor-pointer",
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
>
|
|
20
|
+
<input
|
|
21
|
+
type="checkbox"
|
|
22
|
+
role="switch"
|
|
23
|
+
ref={ref}
|
|
24
|
+
className="sr-only"
|
|
25
|
+
disabled={isDisabled}
|
|
26
|
+
aria-busy={isLoading ? "true" : undefined}
|
|
27
|
+
{...(props as any)}
|
|
28
|
+
/>
|
|
29
|
+
<span
|
|
30
|
+
aria-hidden
|
|
31
|
+
className={cn(
|
|
32
|
+
"relative inline-flex h-6 w-11 flex-shrink-0 rounded-full transition-colors duration-200",
|
|
33
|
+
checked ? "bg-[var(--primary)]" : "bg-[var(--primary)]/80",
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
<span
|
|
37
|
+
className={cn(
|
|
38
|
+
// Thumb should adjust for theme to guarantee contrast
|
|
39
|
+
"absolute top-0.5 left-0.5 h-5 w-5 transform rounded-full bg-[var(--switch-thumb)] shadow-md transition-transform duration-200 ease-in-out",
|
|
40
|
+
checked ? "translate-x-5" : "translate-x-0",
|
|
41
|
+
)}
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
{/* Loading indicator centered inside the switch when isLoading */}
|
|
45
|
+
{isLoading && (
|
|
46
|
+
<span className="absolute inset-0 flex items-center justify-center">
|
|
47
|
+
<svg
|
|
48
|
+
className="h-4 w-4 animate-spin text-white"
|
|
49
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
50
|
+
fill="none"
|
|
51
|
+
viewBox="0 0 24 24"
|
|
52
|
+
aria-hidden
|
|
53
|
+
>
|
|
54
|
+
<circle
|
|
55
|
+
className="opacity-25"
|
|
56
|
+
cx="12"
|
|
57
|
+
cy="12"
|
|
58
|
+
r="10"
|
|
59
|
+
stroke="currentColor"
|
|
60
|
+
strokeWidth="4"
|
|
61
|
+
/>
|
|
62
|
+
<path
|
|
63
|
+
className="opacity-75"
|
|
64
|
+
fill="currentColor"
|
|
65
|
+
d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
|
|
66
|
+
/>
|
|
67
|
+
</svg>
|
|
68
|
+
</span>
|
|
69
|
+
)}
|
|
70
|
+
</span>
|
|
71
|
+
</label>
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
Switch.displayName = "Switch";
|
|
77
|
+
|
|
78
|
+
export { Switch };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
|
6
|
+
|
|
7
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
8
|
+
({ className, ...props }, ref) => {
|
|
9
|
+
return (
|
|
10
|
+
<textarea
|
|
11
|
+
className={cn(
|
|
12
|
+
// Base structural + token fallbacks
|
|
13
|
+
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive/30 dark:aria-invalid:focus-visible:ring-destructive/40 flex min-h-[80px] w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
14
|
+
// CSS variable hooks (preset-first). When vars are unset, tokens above apply.
|
|
15
|
+
"border-[var(--input-border)] bg-[var(--input-bg)] text-[var(--input-fg)] placeholder:text-[var(--input-placeholder)] focus-visible:ring-[var(--input-focus-ring)] focus-visible:ring-offset-[var(--input-ring-offset)]",
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
ref={ref}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
Textarea.displayName = "Textarea";
|
|
25
|
+
|
|
26
|
+
export { Textarea };
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FieldValues, Path, UseFormReturn } from "react-hook-form";
|
|
2
|
+
import type { ApiResult } from "@/lib/server/result";
|
|
3
|
+
|
|
4
|
+
export function mapApiErrorsToForm<TFieldValues extends FieldValues>(
|
|
5
|
+
form: UseFormReturn<TFieldValues>,
|
|
6
|
+
result: ApiResult<any>,
|
|
7
|
+
options?: { formKey?: string },
|
|
8
|
+
): string | undefined {
|
|
9
|
+
if (!result || result.success) return undefined;
|
|
10
|
+
|
|
11
|
+
const formKey = options?.formKey ?? "form";
|
|
12
|
+
let globalMessage = result.message;
|
|
13
|
+
|
|
14
|
+
const entries = Object.entries(result.errors ?? {});
|
|
15
|
+
for (const [key, message] of entries) {
|
|
16
|
+
if (!message) continue;
|
|
17
|
+
if (key === formKey) {
|
|
18
|
+
globalMessage = message;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
form.setError(key as Path<TFieldValues>, { type: "server", message });
|
|
23
|
+
} catch {
|
|
24
|
+
globalMessage = globalMessage ?? message;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return globalMessage;
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
|
|
3
|
+
import { PrismaClient } from "@prisma/client";
|
|
4
|
+
|
|
5
|
+
const globalForPrisma = globalThis as unknown as {
|
|
6
|
+
prisma: PrismaClient | undefined;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const prisma =
|
|
10
|
+
globalForPrisma.prisma ??
|
|
11
|
+
new PrismaClient({
|
|
12
|
+
log: ["error", "warn"],
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
|
|
16
|
+
|