startx 1.0.2 → 1.0.3

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 (147) hide show
  1. package/.dockerignore +4 -0
  2. package/apps/cli/src/commands/index.ts +1 -1
  3. package/apps/cli/src/commands/{common → test}/test.ts +4 -2
  4. package/apps/cli/tsconfig.json +0 -1
  5. package/apps/core-server/Dockerfile +5 -4
  6. package/apps/core-server/package.json +1 -1
  7. package/apps/core-server/tsconfig.json +1 -1
  8. package/apps/queue-worker/package.json +1 -1
  9. package/apps/queue-worker/tsconfig.json +1 -1
  10. package/apps/startx-cli/dist/index.mjs +68 -53
  11. package/apps/startx-cli/src/commands/package.ts +453 -0
  12. package/apps/startx-cli/src/configs/scripts.ts +18 -2
  13. package/apps/startx-cli/src/index.ts +2 -4
  14. package/apps/startx-cli/src/types.ts +2 -4
  15. package/apps/startx-cli/src/utils/inquirer.ts +8 -1
  16. package/apps/web-client/.dockerignore +4 -0
  17. package/apps/web-client/app/app.css +1 -0
  18. package/apps/web-client/app/components.json +23 -0
  19. package/apps/web-client/app/config/auth/auth-state.ts +59 -0
  20. package/apps/web-client/app/config/axios-client.ts +87 -0
  21. package/apps/web-client/app/config/env.ts +5 -0
  22. package/apps/web-client/app/entry.client.tsx +7 -0
  23. package/apps/web-client/app/eslint.config.ts +4 -0
  24. package/apps/web-client/app/root.tsx +77 -0
  25. package/apps/web-client/app/routes/home.tsx +12 -0
  26. package/apps/web-client/app/routes.ts +3 -0
  27. package/apps/web-client/eslint.config.ts +4 -0
  28. package/apps/web-client/package.json +55 -0
  29. package/apps/web-client/react-router.config.ts +7 -0
  30. package/apps/web-client/tsconfig.json +22 -0
  31. package/apps/web-client/vite-env.d.ts +8 -0
  32. package/apps/web-client/vite.config.ts +30 -0
  33. package/biome.json +5 -0
  34. package/configs/eslint-config/eslint.config.ts +1 -0
  35. package/configs/eslint-config/src/configs/base.ts +0 -1
  36. package/configs/eslint-config/src/configs/frontend.ts +1 -1
  37. package/configs/eslint-config/tsconfig.json +1 -1
  38. package/configs/typescript-config/tsconfig.frontend.json +1 -1
  39. package/configs/vitest-config/tsconfig.json +1 -1
  40. package/package.json +1 -1
  41. package/packages/@db/drizzle/tsconfig.json +1 -1
  42. package/packages/@db/sqlite/tsconfig.json +1 -1
  43. package/packages/@repo/env/package.json +1 -2
  44. package/packages/@repo/env/src/utils.ts +17 -11
  45. package/packages/@repo/lib/package.json +3 -1
  46. package/packages/@repo/lib/src/session-module/i-session.ts +108 -0
  47. package/packages/@repo/lib/src/session-module/index.ts +8 -111
  48. package/packages/@repo/lib/src/session-module/redis-session.ts +44 -0
  49. package/packages/@repo/lib/tsconfig.json +0 -1
  50. package/packages/@repo/logger/package.json +0 -1
  51. package/packages/@repo/logger/tsconfig.json +1 -1
  52. package/packages/@repo/mail/tsconfig.json +1 -1
  53. package/packages/@repo/redis/tsconfig.json +1 -1
  54. package/packages/aix/package.json +2 -0
  55. package/packages/aix/src/providers/ai-interface.ts +4 -4
  56. package/packages/aix/src/providers/bedrock/bedrock.ts +261 -0
  57. package/packages/aix/src/providers/default-models.ts +65 -0
  58. package/packages/aix/src/providers/openai/openai.ts +2 -2
  59. package/packages/aix/src/providers/providers.ts +11 -0
  60. package/packages/aix/src/providers/types.ts +1 -1
  61. package/packages/{constants → common}/package.json +4 -2
  62. package/packages/{constants/src/index.ts → common/src/constants.ts} +0 -5
  63. package/packages/common/src/types/users.ts +10 -0
  64. package/packages/{constants → common}/tsconfig.json +0 -3
  65. package/packages/ui/components.json +15 -8
  66. package/packages/ui/package.json +23 -36
  67. package/packages/ui/src/api/axios/i-client.ts +40 -0
  68. package/packages/ui/src/api/index.ts +6 -0
  69. package/packages/ui/src/api/query-provider.tsx +34 -0
  70. package/packages/ui/src/api/use-api/api-builder.ts +139 -0
  71. package/packages/ui/src/api/use-api/api-helpers.ts +165 -0
  72. package/packages/ui/src/api/use-api/api-types.ts +138 -0
  73. package/packages/ui/src/api/use-api/query-factory.ts +66 -0
  74. package/packages/ui/src/api/use-api/react-query/types.ts +64 -0
  75. package/packages/ui/src/api/use-api/react-query/use-api-client.ts +56 -0
  76. package/packages/ui/src/api/use-api/react-query/use-api.ts +297 -0
  77. package/packages/ui/src/components/custom/form-wrapper.tsx +113 -160
  78. package/packages/ui/src/components/custom/grid-component.tsx +4 -4
  79. package/packages/ui/src/components/custom/hover-tool.tsx +1 -1
  80. package/packages/ui/src/components/custom/image-picker.tsx +18 -20
  81. package/packages/ui/src/components/custom/no-content.tsx +6 -16
  82. package/packages/ui/src/components/custom/page-section.tsx +14 -17
  83. package/packages/ui/src/components/custom/simple-popover.tsx +5 -9
  84. package/packages/ui/src/components/custom/theme-provider.tsx +117 -42
  85. package/packages/ui/src/components/custom/typography.tsx +20 -22
  86. package/packages/ui/src/components/extensions/timeline.tsx +100 -0
  87. package/packages/ui/src/components/ui/alert-dialog.tsx +46 -108
  88. package/packages/ui/src/components/ui/avatar.tsx +79 -42
  89. package/packages/ui/src/components/ui/badge.tsx +29 -34
  90. package/packages/ui/src/components/ui/breadcrumb.tsx +65 -81
  91. package/packages/ui/src/components/ui/button.tsx +80 -80
  92. package/packages/ui/src/components/ui/card.tsx +48 -69
  93. package/packages/ui/src/components/ui/carousel.tsx +184 -211
  94. package/packages/ui/src/components/ui/checkbox.tsx +21 -24
  95. package/packages/ui/src/components/ui/command.tsx +121 -102
  96. package/packages/ui/src/components/ui/dialog.tsx +45 -32
  97. package/packages/ui/src/components/ui/dropdown-menu.tsx +45 -33
  98. package/packages/ui/src/components/ui/field.tsx +218 -0
  99. package/packages/ui/src/components/ui/form.tsx +63 -76
  100. package/packages/ui/src/components/ui/input-group.tsx +137 -0
  101. package/packages/ui/src/components/ui/input-otp.tsx +60 -50
  102. package/packages/ui/src/components/ui/input.tsx +16 -15
  103. package/packages/ui/src/components/ui/label.tsx +14 -17
  104. package/packages/ui/src/components/ui/multiple-select.tsx +22 -33
  105. package/packages/ui/src/components/ui/popover.tsx +20 -8
  106. package/packages/ui/src/components/ui/select.tsx +33 -34
  107. package/packages/ui/src/components/ui/separator.tsx +8 -8
  108. package/packages/ui/src/components/ui/sheet.tsx +32 -59
  109. package/packages/ui/src/components/ui/sidebar.tsx +654 -0
  110. package/packages/ui/src/components/ui/skeleton.tsx +2 -8
  111. package/packages/ui/src/components/ui/sonner.tsx +39 -0
  112. package/packages/ui/src/components/ui/spinner.tsx +6 -13
  113. package/packages/ui/src/components/ui/switch.tsx +15 -10
  114. package/packages/ui/src/components/ui/table.tsx +48 -89
  115. package/packages/ui/src/components/ui/tabs.tsx +37 -15
  116. package/packages/ui/src/components/ui/textarea.tsx +13 -13
  117. package/packages/ui/src/components/ui/tooltip.tsx +37 -23
  118. package/packages/ui/src/{components/hooks → hooks}/event/use-click.tsx +6 -10
  119. package/packages/ui/src/hooks/time/use-timer.tsx +51 -0
  120. package/packages/ui/src/hooks/use-media-query.tsx +19 -0
  121. package/packages/ui/src/hooks/use-mobile.tsx +17 -0
  122. package/packages/ui/src/{components/hooks → hooks}/use-update-effect.tsx +2 -2
  123. package/packages/ui/src/lib/utils.ts +113 -0
  124. package/packages/ui/src/styles/globals.css +311 -0
  125. package/packages/ui/src/styles/tailwind.css +89 -0
  126. package/packages/ui/tsconfig.json +7 -9
  127. package/pnpm-workspace.yaml +74 -64
  128. package/packages/ui/postcss.config.mjs +0 -9
  129. package/packages/ui/src/components/extensions/carousel.tsx +0 -392
  130. package/packages/ui/src/components/hooks/time/useTimer.tsx +0 -51
  131. package/packages/ui/src/components/hooks/use-media-query.tsx +0 -19
  132. package/packages/ui/src/components/lib/utils.ts +0 -242
  133. package/packages/ui/src/components/ui/timeline.tsx +0 -118
  134. package/packages/ui/src/components/util/n-formattor.ts +0 -22
  135. package/packages/ui/src/components/util/storage.ts +0 -37
  136. package/packages/ui/src/globals.css +0 -87
  137. package/packages/ui/tailwind.config.ts +0 -94
  138. /package/packages/{constants → common}/eslint.config.ts +0 -0
  139. /package/packages/{constants → common}/src/api.ts +0 -0
  140. /package/packages/{constants → common}/src/time.ts +0 -0
  141. /package/packages/{constants → common}/vitest.config.ts +0 -0
  142. /package/packages/ui/src/{components/hooks/time/useDebounce.tsx → hooks/time/use-debounce.tsx} +0 -0
  143. /package/packages/ui/src/{components/hooks/time/useInterval.tsx → hooks/time/use-interval.tsx} +0 -0
  144. /package/packages/ui/src/{components/hooks/time/useTimeout.tsx → hooks/time/use-timeout.tsx} +0 -0
  145. /package/packages/ui/src/{components/hooks → hooks}/use-persistent-storage.tsx +0 -0
  146. /package/packages/ui/src/{components/hooks → hooks}/use-window-dimension.tsx +0 -0
  147. /package/packages/ui/src/{components/sonner.tsx → sonner.ts} +0 -0
@@ -11,40 +11,16 @@ import {
11
11
  useFieldArray,
12
12
  } from "react-hook-form";
13
13
  import type { ClassNameValue } from "tailwind-merge";
14
- import type { z } from "zod";
15
14
 
16
- import { cn } from "../lib/utils";
15
+ import { cn } from "@repo/ui/lib/utils";
17
16
  import { Badge } from "../ui/badge";
18
17
  import { Button } from "../ui/button";
19
- import {
20
- Command,
21
- CommandEmpty,
22
- CommandGroup,
23
- CommandInput,
24
- CommandItem,
25
- CommandList,
26
- } from "../ui/command";
27
- import {
28
- Form,
29
- FormControl,
30
- FormDescription,
31
- FormField,
32
- FormItem,
33
- FormLabel,
34
- FormMessage,
35
- } from "../ui/form";
18
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "../ui/command";
19
+ import { Field, FieldContent, FieldDescription, FieldError, FieldLabel } from "../ui/field";
20
+ import { Form, FormField } from "../ui/form";
36
21
  import { Input, type InputProps } from "../ui/input";
37
- import { Label } from "../ui/label";
38
22
  import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
39
- import {
40
- Select,
41
- SelectContent,
42
- SelectGroup,
43
- SelectItem,
44
- SelectLabel,
45
- SelectTrigger,
46
- SelectValue,
47
- } from "../ui/select";
23
+ import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "../ui/select";
48
24
  import { Textarea } from "../ui/textarea";
49
25
 
50
26
  type SimpleFormFieldProps = {
@@ -61,12 +37,12 @@ type RelationSelectOption = {
61
37
  }>;
62
38
  };
63
39
 
64
- type FormSelectFieldProps<
65
- TFieldValues extends FieldValues,
66
- TName extends FieldPath<TFieldValues>,
67
- > = Omit<ControllerProps<TFieldValues, TName>, "render"> & {
68
- label?: ReactNode; // The label for the field
69
- options: Array<SelectOption | RelationSelectOption>; // List of options to display in the select
40
+ type FormSelectFieldProps<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> = Omit<
41
+ ControllerProps<TFieldValues, TName>,
42
+ "render"
43
+ > & {
44
+ label?: ReactNode;
45
+ options: Array<SelectOption | RelationSelectOption>;
70
46
  className?: ClassNameValue;
71
47
  placeholder?: ReactNode;
72
48
  onChange?: (value: string) => void;
@@ -97,31 +73,28 @@ export const FormSelectField = <
97
73
  }): Array<SelectOption | RelationSelectOption> => {
98
74
  if (!defaultValue) return [...options];
99
75
 
100
- // Check if defaultValue exists in either flat options or nested group options
101
76
  const valueExists = options.some(opt => {
102
77
  if ("options" in opt) {
103
- // Check nested options in RelationSelectOption group
104
78
  return opt.options.some(nestedOpt => nestedOpt.value === defaultValue);
105
79
  }
106
- // Check regular SelectOption
107
80
  return opt.value === defaultValue;
108
81
  });
109
82
 
110
83
  if (!valueExists) {
111
- // Prepend the default value as a new option
112
84
  return [{ label: defaultValue, value: defaultValue }, ...options];
113
85
  }
114
86
 
115
87
  return [...options];
116
88
  };
89
+
117
90
  return (
118
91
  <FormField
119
92
  control={control}
120
93
  name={name}
121
- render={({ field }) => (
122
- <FormItem className={cn("flex flex-col", className)}>
123
- <FormLabel className="self-start">{label}</FormLabel>
124
- <FormControl>
94
+ render={({ field, fieldState }) => (
95
+ <Field data-invalid={!!fieldState.error} className={cn("flex flex-col", className)}>
96
+ {label ? <FieldLabel className="self-start">{label}</FieldLabel> : null}
97
+ <FieldContent>
125
98
  <Select
126
99
  disabled={disabled}
127
100
  onValueChange={value => {
@@ -155,42 +128,43 @@ export const FormSelectField = <
155
128
  })}
156
129
  </SelectContent>
157
130
  </Select>
158
- </FormControl>
159
- <FormMessage />
160
- </FormItem>
131
+ </FieldContent>
132
+ <FieldError errors={[fieldState.error]} />
133
+ </Field>
161
134
  )}
162
135
  />
163
136
  );
164
137
  };
138
+
165
139
  export const FormTextField = <
166
140
  TFieldValues extends FieldValues = FieldValues,
167
141
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
168
142
  >(
169
143
  props: Omit<
170
- SimpleFormFieldProps &
171
- ControllerProps<TFieldValues, TName> &
172
- InputProps &
173
- React.RefAttributes<HTMLInputElement>,
144
+ SimpleFormFieldProps & ControllerProps<TFieldValues, TName> & InputProps & React.RefAttributes<HTMLInputElement>,
174
145
  "render"
175
146
  >
176
147
  ) => {
148
+ const { label, description, control, name, ...rest } = props;
149
+
177
150
  return (
178
151
  <FormField
179
- control={props.control}
180
- name={props.name}
181
- render={({ field }) => (
182
- <FormItem className="flex flex-col">
183
- <Label className="self-start">{props.label}</Label>
184
- <FormControl>
185
- <Input {...field} {...props} />
186
- </FormControl>
187
- {props.description ? <FormDescription>{props.description}</FormDescription> : null}
188
- <FormMessage />
189
- </FormItem>
152
+ control={control}
153
+ name={name}
154
+ render={({ field, fieldState }) => (
155
+ <Field data-invalid={!!fieldState.error} className="flex flex-col">
156
+ {label ? <FieldLabel className="self-start">{label}</FieldLabel> : null}
157
+ <FieldContent>
158
+ <Input {...field} {...rest} />
159
+ </FieldContent>
160
+ {description ? <FieldDescription>{description}</FieldDescription> : null}
161
+ <FieldError errors={[fieldState.error]} />
162
+ </Field>
190
163
  )}
191
164
  />
192
165
  );
193
166
  };
167
+
194
168
  export const FormTextAreaField = <
195
169
  TFieldValues extends FieldValues = FieldValues,
196
170
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
@@ -203,56 +177,57 @@ export const FormTextAreaField = <
203
177
  "render"
204
178
  >
205
179
  ) => {
180
+ const { label, description, control, name, ...rest } = props;
181
+
206
182
  return (
207
183
  <FormField
208
- control={props.control}
209
- name={props.name}
210
- render={({ field }) => (
211
- <FormItem className="flex flex-col ">
212
- {props.label ? <Label className="self-start">{props.label}</Label> : null}
213
- <FormControl>
214
- <Textarea {...field} {...props} ref={field.ref} />
215
- </FormControl>
216
- {props.description ? <FormDescription>{props.description}</FormDescription> : null}
217
- <FormMessage />
218
- </FormItem>
184
+ control={control}
185
+ name={name}
186
+ render={({ field, fieldState }) => (
187
+ <Field data-invalid={!!fieldState.error} className="flex flex-col">
188
+ {label ? <FieldLabel className="self-start">{label}</FieldLabel> : null}
189
+ <FieldContent>
190
+ <Textarea {...field} {...rest} ref={field.ref} />
191
+ </FieldContent>
192
+ {description ? <FieldDescription>{description}</FieldDescription> : null}
193
+ <FieldError errors={[fieldState.error]} />
194
+ </Field>
219
195
  )}
220
196
  />
221
197
  );
222
198
  };
199
+
223
200
  export const FormNumberField = <
224
201
  TFieldValues extends FieldValues = FieldValues,
225
202
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
226
203
  >(
227
204
  props: Omit<
228
- SimpleFormFieldProps &
229
- ControllerProps<TFieldValues, TName> &
230
- InputProps &
231
- React.RefAttributes<HTMLInputElement>,
205
+ SimpleFormFieldProps & ControllerProps<TFieldValues, TName> & InputProps & React.RefAttributes<HTMLInputElement>,
232
206
  "render"
233
207
  >
234
208
  ) => {
209
+ const { label, description, control, name, ...rest } = props;
210
+
235
211
  return (
236
212
  <FormField
237
- control={props.control}
238
- name={props.name}
239
- render={({ field }) => (
240
- <FormItem className="flex flex-col">
241
- <FormLabel className="self-start">{props.label}</FormLabel>
242
- <FormControl>
213
+ control={control}
214
+ name={name}
215
+ render={({ field, fieldState }) => (
216
+ <Field data-invalid={!!fieldState.error} className="flex flex-col">
217
+ {label ? <FieldLabel className="self-start">{label}</FieldLabel> : null}
218
+ <FieldContent>
243
219
  <Input
244
220
  inputMode="numeric"
245
221
  {...field}
246
222
  onChange={e => {
247
- if (!isNaN(Number(e.currentTarget.value)))
248
- field.onChange(Number(e.currentTarget.value));
223
+ if (!isNaN(Number(e.currentTarget.value))) field.onChange(Number(e.currentTarget.value));
249
224
  }}
250
- {...props}
225
+ {...rest}
251
226
  />
252
- </FormControl>
253
- {props.description ? <FormDescription>{props.description}</FormDescription> : null}
254
- <FormMessage />
255
- </FormItem>
227
+ </FieldContent>
228
+ {description ? <FieldDescription>{description}</FieldDescription> : null}
229
+ <FieldError errors={[fieldState.error]} />
230
+ </Field>
256
231
  )}
257
232
  />
258
233
  );
@@ -260,24 +235,21 @@ export const FormNumberField = <
260
235
 
261
236
  export type FormWrapperProps<T extends FieldValues> = {
262
237
  onSubmit: (data: T) => void;
263
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
264
238
  formData: UseFormReturn<T, unknown, any>;
265
239
  children: React.ReactNode;
266
240
  className?: string;
267
241
  };
268
-
269
- type MultipleFormItemProps<T extends z.ZodType, K extends ArrayPath<z.infer<T>>> = {
270
- control: Control<z.infer<T>>;
271
- name: K;
242
+ type MultipleFormItemProps<TFieldValues extends FieldValues, TName extends ArrayPath<TFieldValues>> = {
243
+ control: Control<TFieldValues>;
244
+ name: TName;
272
245
  label?: string;
273
- addMoreBtn?: React.ReactNode;
246
+ addMoreBtn?: ReactNode;
274
247
  className?: ClassNameValue;
275
248
  wrapperClassName?: ClassNameValue;
276
- children: (field: { id: string; index: number; remove: () => void }) => React.ReactNode;
277
- defaultValue: z.infer<T>[K][number];
249
+ children: (field: { id: string; index: number; remove: () => void }) => ReactNode;
250
+ defaultValue: TFieldValues[TName] extends Array<infer U> ? U : never;
278
251
  };
279
-
280
- export const MultipleFormItem = <T extends z.ZodType, K extends ArrayPath<z.infer<T>>>({
252
+ export const MultipleFormItem = <TFieldValues extends FieldValues, TName extends ArrayPath<TFieldValues>>({
281
253
  control,
282
254
  name,
283
255
  addMoreBtn,
@@ -286,7 +258,7 @@ export const MultipleFormItem = <T extends z.ZodType, K extends ArrayPath<z.infe
286
258
  wrapperClassName,
287
259
  className,
288
260
  defaultValue,
289
- }: MultipleFormItemProps<T, K>) => {
261
+ }: MultipleFormItemProps<TFieldValues, TName>) => {
290
262
  const { fields, append, remove } = useFieldArray({
291
263
  control,
292
264
  name,
@@ -294,7 +266,7 @@ export const MultipleFormItem = <T extends z.ZodType, K extends ArrayPath<z.infe
294
266
 
295
267
  return (
296
268
  <div className="multiple-form-item">
297
- {label ? <p className="mt-6 text-sm ">{label}</p> : null}
269
+ {label ? <p className="mt-6 text-sm">{label}</p> : null}
298
270
  <div className={cn(wrapperClassName)}>
299
271
  {fields.map((field, index) => (
300
272
  <div key={field.id} className={cn("flex flex-col gap-2 max-w-xl", className)}>
@@ -302,14 +274,14 @@ export const MultipleFormItem = <T extends z.ZodType, K extends ArrayPath<z.infe
302
274
  </div>
303
275
  ))}
304
276
  {addMoreBtn ? (
305
- <div onClick={() => append(defaultValue)}>{addMoreBtn}</div>
277
+ <Button onClick={() => append(defaultValue as any)}>{addMoreBtn}</Button>
306
278
  ) : (
307
279
  <Button
308
280
  className="w-1/3 mt-4 flex gap-1"
309
281
  variant="outline"
310
282
  type="button"
311
283
  size={"sm"}
312
- onClick={() => append(defaultValue)}
284
+ onClick={() => append(defaultValue as any)}
313
285
  >
314
286
  <Plus />
315
287
  <span>Add {label?.toLowerCase() ?? "item"}</span>
@@ -319,7 +291,6 @@ export const MultipleFormItem = <T extends z.ZodType, K extends ArrayPath<z.infe
319
291
  </div>
320
292
  );
321
293
  };
322
-
323
294
  export function FormWrapper<T extends FieldValues>(props: FormWrapperProps<T>) {
324
295
  return (
325
296
  <Form {...props.formData}>
@@ -343,25 +314,22 @@ export const FormDefaultDateField = <
343
314
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
344
315
  >(
345
316
  props: Omit<
346
- SimpleFormFieldProps &
347
- ControllerProps<TFieldValues, TName> &
348
- InputProps &
349
- React.RefAttributes<HTMLInputElement>,
317
+ SimpleFormFieldProps & ControllerProps<TFieldValues, TName> & InputProps & React.RefAttributes<HTMLInputElement>,
350
318
  "render"
351
319
  >
352
320
  ) => {
321
+ const { label, description, control, name, ...rest } = props;
322
+
353
323
  const safeFormat = (date: Date | string | undefined | null) => {
354
324
  try {
355
325
  if (!date) return undefined;
356
326
 
357
327
  const parsedDate = typeof date === "string" ? new Date(date) : date;
358
328
 
359
- // Extract UTC date components
360
329
  const year = parsedDate.getUTCFullYear();
361
330
  const month = parsedDate.getUTCMonth();
362
331
  const day = parsedDate.getUTCDate();
363
332
 
364
- // Create a Date object at midnight UTC
365
333
  const utcMidnight = new Date(Date.UTC(year, month, day));
366
334
  return format(utcMidnight, "yyyy-MM-dd");
367
335
  } catch (error) {
@@ -373,15 +341,12 @@ export const FormDefaultDateField = <
373
341
  try {
374
342
  if (!dateString) return undefined;
375
343
 
376
- // Parse input date as local time
377
344
  const localDate = new Date(dateString);
378
345
 
379
- // Get local date components
380
346
  const year = localDate.getFullYear();
381
347
  const month = localDate.getMonth();
382
348
  const day = localDate.getDate();
383
349
 
384
- // Create Date object at midnight UTC of the local date
385
350
  return new Date(Date.UTC(year, month, day));
386
351
  } catch (error) {
387
352
  console.error(error);
@@ -391,32 +356,33 @@ export const FormDefaultDateField = <
391
356
 
392
357
  return (
393
358
  <FormField
394
- control={props.control}
395
- name={props.name}
396
- render={({ field }) => (
397
- <FormItem className="flex flex-col">
398
- <FormLabel className="self-start">{props.label}</FormLabel>
399
- <FormControl>
359
+ control={control}
360
+ name={name}
361
+ render={({ field, fieldState }) => (
362
+ <Field data-invalid={!!fieldState.error} className="flex flex-col">
363
+ {label ? <FieldLabel className="self-start">{label}</FieldLabel> : null}
364
+ <FieldContent>
400
365
  <Input
401
366
  {...field}
402
- {...props}
367
+ {...rest}
403
368
  value={field.value || ""}
404
369
  onChange={e => field.onChange(e.target.value)}
405
370
  type="date"
406
371
  max="9999-12-31"
407
372
  />
408
- </FormControl>
409
- {props.description ? <FormDescription>{props.description}</FormDescription> : null}
410
- <FormMessage />
411
- </FormItem>
373
+ </FieldContent>
374
+ {description ? <FieldDescription>{description}</FieldDescription> : null}
375
+ <FieldError errors={[fieldState.error]} />
376
+ </Field>
412
377
  )}
413
378
  />
414
379
  );
415
380
  };
416
- type FormMultiSelectFieldProps<
417
- TFieldValues extends FieldValues,
418
- TName extends FieldPath<TFieldValues>,
419
- > = Omit<ControllerProps<TFieldValues, TName>, "render"> & {
381
+
382
+ type FormMultiSelectFieldProps<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> = Omit<
383
+ ControllerProps<TFieldValues, TName>,
384
+ "render"
385
+ > & {
420
386
  label?: React.ReactNode;
421
387
  options: Array<SelectOption | RelationSelectOption>;
422
388
  className?: string;
@@ -449,33 +415,23 @@ export function FormMultiSelectField<
449
415
  control={control}
450
416
  name={name}
451
417
  defaultValue={defaultValue}
452
- render={({ field }) => {
418
+ render={({ field, fieldState }) => {
453
419
  const selected: string[] = field.value ?? [];
454
420
 
455
421
  const toggleOption = (value: string) => {
456
- const newValue = selected.includes(value)
457
- ? selected.filter(v => v !== value)
458
- : [...selected, value];
422
+ const newValue = selected.includes(value) ? selected.filter(v => v !== value) : [...selected, value];
459
423
  field.onChange(newValue);
460
424
  onChange?.(newValue);
461
425
  };
462
426
 
463
427
  return (
464
- <FormItem className={cn("flex flex-col", className)}>
465
- {label ? <FormLabel>{label}</FormLabel> : null}
466
- <FormControl className="w-full">
428
+ <Field data-invalid={!!fieldState.error} className={cn("flex flex-col", className)}>
429
+ {label ? <FieldLabel>{label}</FieldLabel> : null}
430
+ <FieldContent className="w-full">
467
431
  <Popover open={open} onOpenChange={setOpen}>
468
432
  <PopoverTrigger asChild disabled={disabled}>
469
- <Button
470
- variant="outline"
471
- // role="combobox"
472
- size={"sm"}
473
- // aria-expanded={open}
474
- className={cn("w-full justify-between", inputClassName)}
475
- >
476
- {selected.length > 0
477
- ? `${selected.length} selected`
478
- : (placeholder ?? "Select")}
433
+ <Button variant="outline" size={"sm"} className={cn("w-full justify-between", inputClassName)}>
434
+ {selected.length > 0 ? `${selected.length} selected` : (placeholder ?? "Select")}
479
435
  <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
480
436
  </Button>
481
437
  </PopoverTrigger>
@@ -489,10 +445,7 @@ export function FormMultiSelectField<
489
445
  "options" in opt ? (
490
446
  <CommandGroup key={opt.label} heading={opt.label}>
491
447
  {opt.options.map(nested => (
492
- <CommandItem
493
- key={nested.value}
494
- onSelect={() => toggleOption(nested.value)}
495
- >
448
+ <CommandItem key={nested.value} onSelect={() => toggleOption(nested.value)}>
496
449
  <Check
497
450
  className={cn(
498
451
  "mr-2 h-4 w-4",
@@ -520,30 +473,30 @@ export function FormMultiSelectField<
520
473
  </PopoverContent>
521
474
  </div>
522
475
  </Popover>
523
- </FormControl>
524
- <FormMessage />
476
+ </FieldContent>
477
+ <FieldError errors={[fieldState.error]} />
525
478
 
526
- {showSelected ? <div className=" flex flex-wrap gap-2">
479
+ {showSelected && selected.length > 0 ? (
480
+ <div className="flex flex-wrap gap-2 mt-2">
527
481
  {selected.map(value => {
528
- const label =
529
- options
530
- .flatMap(o => ("options" in o ? o.options : o))
531
- .find(o => o.value === value)?.label ?? value;
482
+ const badgeLabel =
483
+ options.flatMap(o => ("options" in o ? o.options : o)).find(o => o.value === value)?.label ?? value;
532
484
  return (
533
485
  <Badge
534
486
  onClick={() => !disabled && toggleOption(value)}
535
487
  key={value}
536
- aria-disabled
488
+ aria-disabled={disabled}
537
489
  variant="secondary"
538
490
  className={cn("px-2 py-1", !disabled && "cursor-pointer")}
539
491
  >
540
- {label}
492
+ {badgeLabel}
541
493
  {!disabled && <X className="ml-1 h-3 w-3 cursor-pointer" />}
542
494
  </Badge>
543
495
  );
544
496
  })}
545
- </div> : null}
546
- </FormItem>
497
+ </div>
498
+ ) : null}
499
+ </Field>
547
500
  );
548
501
  }}
549
502
  />
@@ -1,4 +1,4 @@
1
- import { cn } from '../lib/utils';
1
+ import { cn } from "@repo/ui/lib/utils";
2
2
 
3
3
  export type GridProps = {
4
4
  children: React.ReactNode;
@@ -10,9 +10,9 @@ const Grid = (props: GridProps) => {
10
10
  return (
11
11
  <div
12
12
  className={cn(
13
- 'grid sm:grid-cols-12 gap-2 grid-cols-1',
14
- props.grow ? 'min-h-[calc(100vh-60px)] py-2' : '',
15
- props.className,
13
+ "grid sm:grid-cols-12 gap-2 grid-cols-1",
14
+ props.grow ? "min-h-[calc(100vh-60px)] py-2" : "",
15
+ props.className
16
16
  )}
17
17
  >
18
18
  {props.children}
@@ -1,7 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { ClassNameValue } from "tailwind-merge";
3
3
 
4
- import { cn } from "../lib/utils";
4
+ import { cn } from "@repo/ui/lib/utils";
5
5
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip";
6
6
 
7
7
  const HoverTool = ({
@@ -1,17 +1,17 @@
1
- import { GalleryHorizontal } from 'lucide-react';
2
- import { useRef } from 'react';
3
- import { type ChangeEvent, useState } from 'react';
1
+ import { GalleryHorizontal } from "lucide-react";
2
+ import { useRef } from "react";
3
+ import { type ChangeEvent, useState } from "react";
4
4
 
5
- import { Button } from '@/components/ui/button';
5
+ import { Button } from "@repo/ui/components/ui/button";
6
6
  import {
7
7
  Carousel,
8
8
  CarouselContent,
9
9
  CarouselItem,
10
10
  CarouselNext,
11
11
  CarouselPrevious,
12
- } from '@/components/ui/carousel';
12
+ } from "@repo/ui/components/ui/carousel";
13
+ import { cn } from "@repo/ui/lib/utils.js";
13
14
 
14
- import { cn } from '../lib/utils.js';
15
15
  const ImagePicker = ({
16
16
  onChange,
17
17
  defaultValue,
@@ -25,20 +25,18 @@ const ImagePicker = ({
25
25
  }) => {
26
26
  const getDefaultImage = () => {
27
27
  try {
28
- if (!defaultValue) return '';
28
+ if (!defaultValue) return "";
29
29
  return URL.createObjectURL(defaultValue);
30
30
  } catch (error) {
31
- return '';
31
+ return "";
32
32
  }
33
33
  };
34
- const [selectedImage, setSelectedImage] = useState<string[]>(
35
- getDefaultImage() ? [getDefaultImage()] : [],
36
- );
34
+ const [selectedImage, setSelectedImage] = useState<string[]>(getDefaultImage() ? [getDefaultImage()] : []);
37
35
 
38
36
  const fileInputRef = useRef<HTMLInputElement | null>(null);
39
37
  const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
40
38
  if (event.target.files?.[0]) {
41
- setSelectedImage(Array.from(event.target.files).map((file) => URL.createObjectURL(file)));
39
+ setSelectedImage(Array.from(event.target.files).map(file => URL.createObjectURL(file)));
42
40
  const file = event.target.files;
43
41
  onChange?.(file);
44
42
  }
@@ -46,17 +44,17 @@ const ImagePicker = ({
46
44
  return (
47
45
  <div>
48
46
  <div className="flex items-start w-full aspect-video flex-col gap-2">
49
- <div
47
+ <Button
50
48
  onClick={() => fileInputRef.current?.click()}
51
49
  className={cn(
52
- 'border-dashed grid place-content-center min-w-full aspect-video border border-gray-300 rounded-md overflow-hidden',
53
- className,
50
+ "border-dashed grid place-content-center min-w-full aspect-video border border-gray-300 rounded-md overflow-hidden",
51
+ className
54
52
  )}
55
53
  >
56
54
  {selectedImage.length !== 0 ? (
57
55
  <Carousel>
58
56
  <CarouselContent>
59
- {selectedImage.map((image) => (
57
+ {selectedImage.map(image => (
60
58
  <CarouselItem key={image}>
61
59
  <img
62
60
  alt={image}
@@ -87,19 +85,19 @@ const ImagePicker = ({
87
85
  <GalleryHorizontal className="text-gray-500 drop-shadow-lg " size={40} />
88
86
  </div>
89
87
  )}
90
- </div>
88
+ </Button>
91
89
  <Button
92
90
  type="button"
93
91
  className="px-8 border-dotted"
94
92
  disabled={disabled}
95
- onClick={(e) => {
93
+ onClick={e => {
96
94
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
97
95
  selectedImage.length === 0 ? fileInputRef.current?.click() : setSelectedImage([]);
98
96
  onChange?.(null);
99
97
  }}
100
- variant={selectedImage.length > 0 ? 'destructive' : 'outline'}
98
+ variant={selectedImage.length > 0 ? "destructive" : "outline"}
101
99
  >
102
- {selectedImage.length === 0 ? 'Upload image' : 'Remove image'}
100
+ {selectedImage.length === 0 ? "Upload image" : "Remove image"}
103
101
  </Button>
104
102
  </div>
105
103
  </div>
@@ -1,7 +1,7 @@
1
- import { Loader, Scroll } from 'lucide-react';
2
- import type { ReactNode } from 'react';
1
+ import { Loader, Scroll } from "lucide-react";
2
+ import type { ReactNode } from "react";
3
3
 
4
- import { cn } from '../lib/utils';
4
+ import { cn } from "@repo/ui/lib/utils";
5
5
 
6
6
  type NoContentProps = {
7
7
  icon?: ReactNode;
@@ -12,26 +12,16 @@ type NoContentProps = {
12
12
  export const NoContent = (props: NoContentProps) => {
13
13
  if (props.loading) {
14
14
  return (
15
- <div
16
- className={cn(
17
- 'h-full w-full flex flex-col gap-2 justify-center items-center',
18
- props.className ?? '',
19
- )}
20
- >
15
+ <div className={cn("h-full w-full flex flex-col gap-2 justify-center items-center", props.className ?? "")}>
21
16
  <Loader className="animate-spin" />
22
17
  <p className="text-sm text-muted-foreground">loading</p>
23
18
  </div>
24
19
  );
25
20
  }
26
21
  return (
27
- <div
28
- className={cn(
29
- 'h-full w-full flex text-sm flex-col gap-2 justify-center items-center',
30
- props.className ?? '',
31
- )}
32
- >
22
+ <div className={cn("h-full w-full flex text-sm flex-col gap-2 justify-center items-center", props.className ?? "")}>
33
23
  {props.icon ?? <Scroll />}
34
- <p className=" text-muted-foreground">{props.label ?? 'No Content'}</p>
24
+ <p className=" text-muted-foreground">{props.label ?? "No Content"}</p>
35
25
  </div>
36
26
  );
37
27
  };