better-ts-stack 2.1.1 → 2.2.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 (174) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +16 -8
  3. package/dist/args.js +7 -0
  4. package/dist/builder/buildPlanner.js +7 -1
  5. package/dist/builder/configGenerator.js +28 -3
  6. package/dist/builder/index.js +2 -1
  7. package/dist/builder/moduleSelector.js +6 -1
  8. package/dist/builder/readmeGenerator.js +8 -2
  9. package/dist/builder/templateContext.js +1 -0
  10. package/dist/output/nextSteps.js +2 -4
  11. package/dist/prompts/stack.js +6 -1
  12. package/dist/types/index.js +2 -0
  13. package/dist/ui/messages.js +2 -0
  14. package/package.json +71 -71
  15. package/templates/backend/express/.prettierignore +52 -52
  16. package/templates/backend/express/.prettierrc +16 -16
  17. package/templates/backend/express/gitignore +3 -1
  18. package/templates/backend/express/src/env.ts.hbs +13 -2
  19. package/templates/backend/express/src/index.ts.hbs +31 -2
  20. package/templates/backend/express/tsconfig.eslint.json +9 -9
  21. package/templates/backend/nestjs/.prettierignore +52 -52
  22. package/templates/backend/nestjs/gitignore +3 -1
  23. package/templates/backend/nestjs/src/app.module.ts.hbs +4 -1
  24. package/templates/backend/nestjs/src/env.ts.hbs +13 -2
  25. package/templates/backend/nestjs/src/health/health.controller.ts.hbs +7 -0
  26. package/templates/backend/nestjs/src/{main.ts → main.ts.hbs} +11 -0
  27. package/templates/backend/nestjs/tsconfig.eslint.json +9 -9
  28. package/templates/frontend/nextjs/app/globals.css +129 -89
  29. package/templates/frontend/nextjs/app/layout.tsx +36 -34
  30. package/templates/frontend/nextjs/app/page.tsx.hbs +63 -43
  31. package/templates/frontend/nextjs/components/ui/alert.tsx +75 -0
  32. package/templates/frontend/nextjs/components/ui/badge.tsx +51 -0
  33. package/templates/frontend/nextjs/components/ui/button.tsx +57 -51
  34. package/templates/frontend/nextjs/components/ui/card.tsx +102 -60
  35. package/templates/frontend/nextjs/components/ui/field.tsx +238 -67
  36. package/templates/frontend/nextjs/components/ui/input.tsx +19 -18
  37. package/templates/frontend/nextjs/components/ui/label.tsx +19 -0
  38. package/templates/frontend/nextjs/components/ui/separator.tsx +24 -0
  39. package/templates/frontend/nextjs/components/ui/skeleton.tsx +13 -0
  40. package/templates/frontend/nextjs/components/ui/spinner.tsx +10 -0
  41. package/templates/frontend/nextjs/components.json +11 -5
  42. package/templates/frontend/nextjs/config.json +36 -35
  43. package/templates/frontend/nextjs/eslint.config.mjs +11 -11
  44. package/templates/frontend/nextjs/lib/utils.ts +1 -6
  45. package/templates/frontend/nextjs/next.config.ts.hbs +12 -12
  46. package/templates/frontend/nextjs/postcss.config.mjs +7 -7
  47. package/templates/frontend/nextjs/public/file.svg +1 -1
  48. package/templates/frontend/nextjs/public/globe.svg +1 -1
  49. package/templates/frontend/nextjs/public/window.svg +1 -1
  50. package/templates/frontend/tanstack/components.json +25 -18
  51. package/templates/frontend/tanstack/config.json +5 -2
  52. package/templates/frontend/tanstack/eslint.config.mjs +23 -23
  53. package/templates/frontend/tanstack/gitignore +39 -39
  54. package/templates/frontend/tanstack/public/favicon.svg +7 -7
  55. package/templates/frontend/tanstack/src/components/ui/alert.tsx +75 -0
  56. package/templates/frontend/tanstack/src/components/ui/badge.tsx +51 -0
  57. package/templates/frontend/tanstack/src/components/ui/button.tsx +57 -51
  58. package/templates/frontend/tanstack/src/components/ui/card.tsx +102 -60
  59. package/templates/frontend/tanstack/src/components/ui/field.tsx +236 -67
  60. package/templates/frontend/tanstack/src/components/ui/input.tsx +19 -18
  61. package/templates/frontend/tanstack/src/components/ui/label.tsx +17 -0
  62. package/templates/frontend/tanstack/src/components/ui/separator.tsx +24 -0
  63. package/templates/frontend/tanstack/src/components/ui/skeleton.tsx +13 -0
  64. package/templates/frontend/tanstack/src/components/ui/spinner.tsx +10 -0
  65. package/templates/frontend/tanstack/src/lib/utils.ts +1 -6
  66. package/templates/frontend/tanstack/src/router.tsx +31 -31
  67. package/templates/frontend/tanstack/src/routes/__root.tsx +69 -69
  68. package/templates/frontend/tanstack/src/routes/index.tsx.hbs +64 -45
  69. package/templates/frontend/tanstack/src/styles/app.css +100 -55
  70. package/templates/frontend/tanstack/tsconfig.json +22 -22
  71. package/templates/modules/agents/AGENTS.md.hbs +245 -0
  72. package/templates/modules/agents/CLAUDE.md +1 -0
  73. package/templates/modules/agents/config.json +10 -0
  74. package/templates/modules/auth/express/config.json +12 -11
  75. package/templates/modules/auth/express/src/lib/auth.ts.hbs +143 -0
  76. package/templates/modules/auth/express/src/lib/email.ts +37 -0
  77. package/templates/modules/auth/express/src/middleware/requireAuth.ts +29 -0
  78. package/templates/modules/auth/express/src/routes/me.ts +13 -0
  79. package/templates/modules/auth/nestjs/config.json +13 -11
  80. package/templates/modules/auth/nestjs/src/auth/auth.module.ts.hbs +64 -14
  81. package/templates/modules/auth/nestjs/src/auth/auth.ts.hbs +152 -0
  82. package/templates/modules/auth/nestjs/src/auth/email.ts +37 -0
  83. package/templates/modules/auth/nestjs/src/me/me.controller.ts +15 -0
  84. package/templates/modules/auth/nextjs/app/api/auth/[...all]/route.ts +4 -4
  85. package/templates/modules/auth/nextjs/app/dashboard/page.tsx +101 -45
  86. package/templates/modules/auth/nextjs/app/forgot-password/page.tsx +11 -0
  87. package/templates/modules/auth/nextjs/app/reset-password/page.tsx +17 -0
  88. package/templates/modules/auth/nextjs/app/sign-in/page.tsx +23 -17
  89. package/templates/modules/auth/nextjs/app/sign-up/page.tsx +25 -19
  90. package/templates/modules/auth/nextjs/components/auth/change-password-form.tsx +125 -0
  91. package/templates/modules/auth/nextjs/components/auth/email-verification-card.tsx +91 -0
  92. package/templates/modules/auth/nextjs/components/auth/forgot-password-form.tsx +133 -0
  93. package/templates/modules/auth/nextjs/components/auth/reset-password-form.tsx +155 -0
  94. package/templates/modules/auth/nextjs/components/auth/sign-in-form.tsx +46 -32
  95. package/templates/modules/auth/nextjs/components/auth/sign-out-button.tsx +6 -5
  96. package/templates/modules/auth/nextjs/components/auth/sign-up-form.tsx +36 -30
  97. package/templates/modules/auth/nextjs/components/auth/social-buttons.tsx +73 -0
  98. package/templates/modules/auth/nextjs/config.json +24 -18
  99. package/templates/modules/auth/nextjs/lib/auth-client.ts +3 -3
  100. package/templates/modules/auth/nextjs/lib/auth-schema.ts +27 -2
  101. package/templates/modules/auth/nextjs/lib/auth.ts.hbs +65 -0
  102. package/templates/modules/auth/nextjs/lib/email.ts +43 -0
  103. package/templates/modules/auth/nextjs/lib/session.ts +12 -0
  104. package/templates/modules/auth/tanstack/config.json +25 -19
  105. package/templates/modules/auth/tanstack/src/components/auth/change-password-form.tsx +123 -0
  106. package/templates/modules/auth/tanstack/src/components/auth/email-verification-card.tsx +89 -0
  107. package/templates/modules/auth/tanstack/src/components/auth/forgot-password-form.tsx +131 -0
  108. package/templates/modules/auth/tanstack/src/components/auth/reset-password-form.tsx +149 -0
  109. package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +44 -35
  110. package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +7 -6
  111. package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +37 -34
  112. package/templates/modules/auth/tanstack/src/components/auth/social-buttons.tsx +71 -0
  113. package/templates/modules/auth/tanstack/src/lib/auth-client.ts +2 -2
  114. package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +14 -8
  115. package/templates/modules/auth/tanstack/src/lib/auth-schema.ts +27 -2
  116. package/templates/modules/auth/tanstack/src/lib/auth.ts.hbs +98 -38
  117. package/templates/modules/auth/tanstack/src/lib/email.ts +43 -0
  118. package/templates/modules/auth/tanstack/src/routeTree.gen.ts +61 -3
  119. package/templates/modules/auth/tanstack/src/routes/api/auth/$.ts +15 -15
  120. package/templates/modules/auth/tanstack/src/routes/dashboard.tsx +119 -111
  121. package/templates/modules/auth/tanstack/src/routes/forgot-password.tsx +17 -0
  122. package/templates/modules/auth/tanstack/src/routes/reset-password.tsx +25 -0
  123. package/templates/modules/auth/tanstack/src/routes/sign-in.tsx +44 -37
  124. package/templates/modules/auth/tanstack/src/routes/sign-up.tsx +44 -37
  125. package/templates/modules/docker/.dockerignore +2 -0
  126. package/templates/modules/docker/config.json +15 -15
  127. package/templates/modules/drizzle/express/config.json +26 -26
  128. package/templates/modules/drizzle/express/src/lib/schema.ts.hbs +90 -0
  129. package/templates/modules/drizzle/nestjs/config.json +26 -26
  130. package/templates/modules/drizzle/nestjs/src/database/schema.ts.hbs +90 -0
  131. package/templates/modules/drizzle/nextjs/config.json +26 -26
  132. package/templates/modules/drizzle/nextjs/drizzle.config.ts +13 -13
  133. package/templates/modules/drizzle/tanstack/config.json +26 -26
  134. package/templates/modules/drizzle/tanstack/drizzle.config.ts +12 -12
  135. package/templates/modules/drizzle/tanstack/src/lib/db.ts +13 -13
  136. package/templates/modules/mongoose/express/config.json +15 -15
  137. package/templates/modules/mongoose/express/src/lib/db.ts +43 -43
  138. package/templates/modules/mongoose/express/src/models/User.ts +48 -48
  139. package/templates/modules/mongoose/nestjs/config.json +16 -15
  140. package/templates/modules/mongoose/nextjs/config.json +15 -15
  141. package/templates/modules/mongoose/nextjs/models/User.ts +48 -48
  142. package/templates/modules/mongoose/tanstack/config.json +16 -16
  143. package/templates/modules/mongoose/tanstack/src/models/User.ts +48 -48
  144. package/templates/modules/prisma/express/config.json +27 -27
  145. package/templates/modules/prisma/express/prisma/schema.prisma.hbs +92 -0
  146. package/templates/modules/prisma/nestjs/config.json +27 -27
  147. package/templates/modules/prisma/nestjs/prisma/schema.prisma.hbs +91 -0
  148. package/templates/modules/prisma/nextjs/config.json +28 -28
  149. package/templates/modules/prisma/nextjs/lib/prisma.ts +18 -18
  150. package/templates/modules/prisma/nextjs/prisma.config.ts +14 -14
  151. package/templates/modules/prisma/tanstack/config.json +28 -28
  152. package/templates/modules/prisma/tanstack/prisma/schema.prisma.hbs +91 -91
  153. package/templates/modules/prisma/tanstack/prisma.config.ts +14 -14
  154. package/templates/modules/prisma/tanstack/src/lib/prisma.ts +17 -17
  155. package/templates/modules/rate-limit/express/src/middleware/rateLimit.ts +0 -8
  156. package/templates/modules/vscode/.vscode/extensions.json.hbs +29 -0
  157. package/templates/modules/vscode/.vscode/settings.json.hbs +22 -0
  158. package/templates/modules/vscode/config.json +10 -0
  159. package/templates/modules/auth/express/src/controllers/authController.ts.hbs +0 -116
  160. package/templates/modules/auth/express/src/lib/jwt.ts.hbs +0 -27
  161. package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +0 -25
  162. package/templates/modules/auth/express/src/routes/auth.ts.hbs +0 -27
  163. package/templates/modules/auth/express/src/services/userStore.ts.hbs +0 -117
  164. package/templates/modules/auth/nestjs/src/auth/auth.controller.ts.hbs +0 -61
  165. package/templates/modules/auth/nestjs/src/auth/auth.guard.ts +0 -36
  166. package/templates/modules/auth/nestjs/src/auth/auth.schemas.ts +0 -35
  167. package/templates/modules/auth/nestjs/src/auth/auth.service.ts +0 -70
  168. package/templates/modules/auth/nestjs/src/auth/jwt.ts +0 -27
  169. package/templates/modules/auth/nestjs/src/auth/users.service.ts.hbs +0 -144
  170. package/templates/modules/auth/nestjs/src/auth/zod-validation.pipe.ts +0 -16
  171. package/templates/modules/drizzle/express/src/lib/schema.ts +0 -12
  172. package/templates/modules/drizzle/nestjs/src/database/schema.ts +0 -12
  173. package/templates/modules/prisma/express/prisma/schema.prisma +0 -24
  174. package/templates/modules/prisma/nestjs/prisma/schema.prisma +0 -23
@@ -1,67 +1,236 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "~/lib/utils";
4
-
5
- function FieldGroup({
6
- className,
7
- ...props
8
- }: React.HTMLAttributes<HTMLDivElement>) {
9
- return <div className={cn("space-y-5", className)} {...props} />;
10
- }
11
-
12
- type FieldProps = React.HTMLAttributes<HTMLDivElement> & {
13
- "data-invalid"?: boolean;
14
- };
15
-
16
- function Field({
17
- className,
18
- "data-invalid": dataInvalid,
19
- ...props
20
- }: FieldProps) {
21
- return (
22
- <div
23
- data-invalid={dataInvalid}
24
- className={cn("space-y-2", className)}
25
- {...props}
26
- />
27
- );
28
- }
29
-
30
- function FieldLabel({
31
- className,
32
- ...props
33
- }: React.LabelHTMLAttributes<HTMLLabelElement>) {
34
- return (
35
- <label
36
- className={cn("text-sm font-medium text-slate-900", className)}
37
- {...props}
38
- />
39
- );
40
- }
41
-
42
- function FieldDescription({
43
- className,
44
- ...props
45
- }: React.HTMLAttributes<HTMLParagraphElement>) {
46
- return <p className={cn("text-sm text-slate-500", className)} {...props} />;
47
- }
48
-
49
- type FieldErrorProps = React.HTMLAttributes<HTMLParagraphElement> & {
50
- errors?: Array<{ message?: string } | undefined>;
51
- };
52
-
53
- function FieldError({ className, errors, ...props }: FieldErrorProps) {
54
- const message = errors?.find((error) => error?.message)?.message;
55
-
56
- if (!message) {
57
- return null;
58
- }
59
-
60
- return (
61
- <p className={cn("text-sm font-medium text-rose-600", className)} {...props}>
62
- {message}
63
- </p>
64
- );
65
- }
66
-
67
- export { Field, FieldDescription, FieldError, FieldGroup, FieldLabel };
1
+ import { useMemo } from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { cn } from "cn"
4
+
5
+ import { Label } from "~/components/ui/label"
6
+ import { Separator } from "~/components/ui/separator"
7
+
8
+ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
9
+ return (
10
+ <fieldset
11
+ data-slot="field-set"
12
+ className={cn(
13
+ "flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ function FieldLegend({
22
+ className,
23
+ variant = "legend",
24
+ ...props
25
+ }: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
26
+ return (
27
+ <legend
28
+ data-slot="field-legend"
29
+ data-variant={variant}
30
+ className={cn(
31
+ "mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
32
+ className
33
+ )}
34
+ {...props}
35
+ />
36
+ )
37
+ }
38
+
39
+ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
40
+ return (
41
+ <div
42
+ data-slot="field-group"
43
+ className={cn(
44
+ "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
45
+ className
46
+ )}
47
+ {...props}
48
+ />
49
+ )
50
+ }
51
+
52
+ const fieldVariants = cva(
53
+ "group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
54
+ {
55
+ variants: {
56
+ orientation: {
57
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
58
+ horizontal:
59
+ "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
60
+ responsive:
61
+ "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
62
+ },
63
+ },
64
+ defaultVariants: {
65
+ orientation: "vertical",
66
+ },
67
+ }
68
+ )
69
+
70
+ function Field({
71
+ className,
72
+ orientation = "vertical",
73
+ ...props
74
+ }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
75
+ return (
76
+ <div
77
+ role="group"
78
+ data-slot="field"
79
+ data-orientation={orientation}
80
+ className={cn(fieldVariants({ orientation }), className)}
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
87
+ return (
88
+ <div
89
+ data-slot="field-content"
90
+ className={cn(
91
+ "group/field-content flex flex-1 flex-col gap-1 leading-snug",
92
+ className
93
+ )}
94
+ {...props}
95
+ />
96
+ )
97
+ }
98
+
99
+ function FieldLabel({
100
+ className,
101
+ ...props
102
+ }: React.ComponentProps<typeof Label>) {
103
+ return (
104
+ <Label
105
+ data-slot="field-label"
106
+ className={cn(
107
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-3 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
108
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
109
+ className
110
+ )}
111
+ {...props}
112
+ />
113
+ )
114
+ }
115
+
116
+ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
117
+ return (
118
+ <div
119
+ data-slot="field-label"
120
+ className={cn(
121
+ "flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
122
+ className
123
+ )}
124
+ {...props}
125
+ />
126
+ )
127
+ }
128
+
129
+ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
130
+ return (
131
+ <p
132
+ data-slot="field-description"
133
+ className={cn(
134
+ "text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
135
+ "last:mt-0 nth-last-2:-mt-1",
136
+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
137
+ className
138
+ )}
139
+ {...props}
140
+ />
141
+ )
142
+ }
143
+
144
+ function FieldSeparator({
145
+ children,
146
+ className,
147
+ ...props
148
+ }: React.ComponentProps<"div"> & {
149
+ children?: React.ReactNode
150
+ }) {
151
+ return (
152
+ <div
153
+ data-slot="field-separator"
154
+ data-content={!!children}
155
+ className={cn(
156
+ "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
157
+ className
158
+ )}
159
+ {...props}
160
+ >
161
+ <Separator className="absolute inset-0 top-1/2" />
162
+ {children && (
163
+ <span
164
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
165
+ data-slot="field-separator-content"
166
+ >
167
+ {children}
168
+ </span>
169
+ )}
170
+ </div>
171
+ )
172
+ }
173
+
174
+ function FieldError({
175
+ className,
176
+ children,
177
+ errors,
178
+ ...props
179
+ }: React.ComponentProps<"div"> & {
180
+ errors?: Array<{ message?: string } | undefined>
181
+ }) {
182
+ const content = useMemo(() => {
183
+ if (children) {
184
+ return children
185
+ }
186
+
187
+ if (!errors?.length) {
188
+ return null
189
+ }
190
+
191
+ const uniqueErrors = [
192
+ ...new Map(errors.map((error) => [error?.message, error])).values(),
193
+ ]
194
+
195
+ if (uniqueErrors?.length == 1) {
196
+ return uniqueErrors[0]?.message
197
+ }
198
+
199
+ return (
200
+ <ul className="ml-4 flex list-disc flex-col gap-1">
201
+ {uniqueErrors.map(
202
+ (error, index) =>
203
+ error?.message && <li key={index}>{error.message}</li>
204
+ )}
205
+ </ul>
206
+ )
207
+ }, [children, errors])
208
+
209
+ if (!content) {
210
+ return null
211
+ }
212
+
213
+ return (
214
+ <div
215
+ role="alert"
216
+ data-slot="field-error"
217
+ className={cn("text-sm font-normal text-destructive", className)}
218
+ {...props}
219
+ >
220
+ {content}
221
+ </div>
222
+ )
223
+ }
224
+
225
+ export {
226
+ Field,
227
+ FieldLabel,
228
+ FieldDescription,
229
+ FieldError,
230
+ FieldGroup,
231
+ FieldLegend,
232
+ FieldSeparator,
233
+ FieldSet,
234
+ FieldContent,
235
+ FieldTitle,
236
+ }
@@ -1,18 +1,19 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "~/lib/utils";
4
-
5
- function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
- return (
7
- <input
8
- type={type}
9
- className={cn(
10
- "flex h-11 w-full rounded-2xl border border-slate-200 bg-white px-3 py-2 text-sm text-slate-950 shadow-sm transition-[color,box-shadow] outline-none placeholder:text-slate-400 focus-visible:border-sky-500 focus-visible:ring-[3px] focus-visible:ring-sky-500/20 disabled:cursor-not-allowed disabled:opacity-50",
11
- className
12
- )}
13
- {...props}
14
- />
15
- );
16
- }
17
-
18
- export { Input };
1
+ import * as React from "react"
2
+ import { Input as InputPrimitive } from "@base-ui/react/input"
3
+ import { cn } from "cn"
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
+ return (
7
+ <InputPrimitive
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ export { Input }
@@ -0,0 +1,17 @@
1
+ import * as React from "react"
2
+ import { cn } from "cn"
3
+
4
+ function Label({ className, ...props }: React.ComponentProps<"label">) {
5
+ return (
6
+ <label
7
+ data-slot="label"
8
+ className={cn(
9
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
10
+ className
11
+ )}
12
+ {...props}
13
+ />
14
+ )
15
+ }
16
+
17
+ export { Label }
@@ -0,0 +1,24 @@
1
+ "use client"
2
+
3
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
4
+ import { cn } from "cn"
5
+
6
+ function Separator({
7
+ className,
8
+ orientation = "horizontal",
9
+ ...props
10
+ }: SeparatorPrimitive.Props) {
11
+ return (
12
+ <SeparatorPrimitive
13
+ data-slot="separator"
14
+ orientation={orientation}
15
+ className={cn(
16
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ export { Separator }
@@ -0,0 +1,13 @@
1
+ import { cn } from "cn"
2
+
3
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn("animate-pulse rounded-md bg-muted", className)}
8
+ {...props}
9
+ />
10
+ )
11
+ }
12
+
13
+ export { Skeleton }
@@ -0,0 +1,10 @@
1
+ import { cn } from "cn"
2
+ import { Loader2Icon } from "lucide-react"
3
+
4
+ function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
5
+ return (
6
+ <Loader2Icon data-slot="spinner" role="status" aria-label="Loading" className={cn("size-4 animate-spin", className)} {...props} />
7
+ )
8
+ }
9
+
10
+ export { Spinner }
@@ -1,6 +1 @@
1
- import { clsx, type ClassValue } from "clsx";
2
- import { twMerge } from "tailwind-merge";
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs));
6
- }
1
+ export { cn } from "cn";
@@ -1,32 +1,32 @@
1
- import { QueryClient } from "@tanstack/react-query";
2
- import { createRouter } from "@tanstack/react-router";
3
- import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
4
-
5
- import { routeTree } from "~/routeTree.gen";
6
-
7
- export function getRouter() {
8
- const queryClient = new QueryClient({
9
- defaultOptions: {
10
- queries: {
11
- staleTime: 30_000,
12
- },
13
- },
14
- });
15
-
16
- const router = createRouter({
17
- routeTree,
18
- context: {
19
- queryClient,
20
- },
21
- defaultPreload: "intent",
22
- defaultPreloadStaleTime: 0,
23
- scrollRestoration: true,
24
- });
25
-
26
- setupRouterSsrQueryIntegration({
27
- router,
28
- queryClient,
29
- });
30
-
31
- return router;
1
+ import { QueryClient } from "@tanstack/react-query";
2
+ import { createRouter } from "@tanstack/react-router";
3
+ import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
4
+
5
+ import { routeTree } from "~/routeTree.gen";
6
+
7
+ export function getRouter() {
8
+ const queryClient = new QueryClient({
9
+ defaultOptions: {
10
+ queries: {
11
+ staleTime: 30_000,
12
+ },
13
+ },
14
+ });
15
+
16
+ const router = createRouter({
17
+ routeTree,
18
+ context: {
19
+ queryClient,
20
+ },
21
+ defaultPreload: "intent",
22
+ defaultPreloadStaleTime: 0,
23
+ scrollRestoration: true,
24
+ });
25
+
26
+ setupRouterSsrQueryIntegration({
27
+ router,
28
+ queryClient,
29
+ });
30
+
31
+ return router;
32
32
  }
@@ -1,70 +1,70 @@
1
- /// <reference types="vite/client" />
2
- import {
3
- HeadContent,
4
- Outlet,
5
- Scripts,
6
- createRootRouteWithContext,
7
- } from "@tanstack/react-router";
8
- import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
9
- import type { QueryClient } from "@tanstack/react-query";
10
- import * as React from "react";
11
-
12
- import appCss from "~/styles/app.css?url";
13
-
14
- export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
15
- head: () => ({
16
- meta: [
17
- {
18
- charSet: "utf-8",
19
- },
20
- {
21
- name: "viewport",
22
- content: "width=device-width, initial-scale=1",
23
- },
24
- ],
25
- links: [
26
- {
27
- rel: "icon",
28
- type: "image/svg+xml",
29
- href: "/favicon.svg",
30
- },
31
- ],
32
- }),
33
- component: RootComponent,
34
- notFoundComponent: () => <NotFound />,
35
- })
36
-
37
- function RootComponent() {
38
- return (
39
- <RootDocument>
40
- <Outlet />
41
- </RootDocument>
42
- )
43
- }
44
-
45
- function RootDocument({ children }: { children: React.ReactNode }) {
46
- return (
47
- <html lang="en">
48
- <head>
49
- <HeadContent />
50
- <link rel="stylesheet" href={appCss} />
51
- </head>
52
- <body>
53
- {children}
54
- <TanStackRouterDevtools />
55
- <Scripts />
56
- </body>
57
- </html>
58
- )
59
- }
60
-
61
- function NotFound() {
62
- return (
63
- <div className="flex min-h-[50vh] items-center justify-center">
64
- <div className="text-center">
65
- <h1 className="text-4xl font-bold">404</h1>
66
- <p className="mt-2 text-muted-foreground">Page not found</p>
67
- </div>
68
- </div>
69
- )
1
+ /// <reference types="vite/client" />
2
+ import {
3
+ HeadContent,
4
+ Outlet,
5
+ Scripts,
6
+ createRootRouteWithContext,
7
+ } from "@tanstack/react-router";
8
+ import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
9
+ import type { QueryClient } from "@tanstack/react-query";
10
+ import * as React from "react";
11
+
12
+ import appCss from "~/styles/app.css?url";
13
+
14
+ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
15
+ head: () => ({
16
+ meta: [
17
+ {
18
+ charSet: "utf-8",
19
+ },
20
+ {
21
+ name: "viewport",
22
+ content: "width=device-width, initial-scale=1",
23
+ },
24
+ ],
25
+ links: [
26
+ {
27
+ rel: "icon",
28
+ type: "image/svg+xml",
29
+ href: "/favicon.svg",
30
+ },
31
+ ],
32
+ }),
33
+ component: RootComponent,
34
+ notFoundComponent: () => <NotFound />,
35
+ })
36
+
37
+ function RootComponent() {
38
+ return (
39
+ <RootDocument>
40
+ <Outlet />
41
+ </RootDocument>
42
+ )
43
+ }
44
+
45
+ function RootDocument({ children }: { children: React.ReactNode }) {
46
+ return (
47
+ <html lang="en">
48
+ <head>
49
+ <HeadContent />
50
+ <link rel="stylesheet" href={appCss} />
51
+ </head>
52
+ <body>
53
+ {children}
54
+ <TanStackRouterDevtools />
55
+ <Scripts />
56
+ </body>
57
+ </html>
58
+ )
59
+ }
60
+
61
+ function NotFound() {
62
+ return (
63
+ <div className="flex min-h-[50vh] items-center justify-center">
64
+ <div className="text-center">
65
+ <h1 className="text-4xl font-bold">404</h1>
66
+ <p className="mt-2 text-muted-foreground">Page not found</p>
67
+ </div>
68
+ </div>
69
+ )
70
70
  }