create-fsd-architecture 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 (51) hide show
  1. package/bin/index.mjs +3 -3
  2. package/package.json +6 -2
  3. package/.claude/settings.local.json +0 -9
  4. package/ashraf/README.md +0 -122
  5. package/ashraf/bun.lock +0 -1347
  6. package/ashraf/components.json +0 -23
  7. package/ashraf/eslint.config.js +0 -23
  8. package/ashraf/index.html +0 -13
  9. package/ashraf/package-lock.json +0 -9886
  10. package/ashraf/package.json +0 -43
  11. package/ashraf/src/app/App.tsx +0 -12
  12. package/ashraf/src/app/providers/axios-interceptor.tsx +0 -0
  13. package/ashraf/src/app/providers.tsx +0 -0
  14. package/ashraf/src/app/routing/auth-routes.tsx +0 -0
  15. package/ashraf/src/app/routing/dashboard-routes.tsx +0 -0
  16. package/ashraf/src/app/routing/guards/auth-guard.tsx +0 -0
  17. package/ashraf/src/app/routing/guards/dashboard-guard.tsx +0 -0
  18. package/ashraf/src/app/styles/index.css +0 -190
  19. package/ashraf/src/assets/index.ts +0 -0
  20. package/ashraf/src/entities/products/api/get-product-by-id.ts +0 -8
  21. package/ashraf/src/entities/products/api/get-products.ts +0 -8
  22. package/ashraf/src/entities/products/index.ts +0 -5
  23. package/ashraf/src/entities/products/model/query-keys.ts +0 -5
  24. package/ashraf/src/entities/products/model/types.ts +0 -18
  25. package/ashraf/src/entities/products/ui/product-card.tsx +0 -25
  26. package/ashraf/src/features/products/create-product/api/create-product.ts +0 -7
  27. package/ashraf/src/features/products/create-product/index.ts +0 -2
  28. package/ashraf/src/features/products/create-product/model/create-product-schema.ts +0 -11
  29. package/ashraf/src/features/products/create-product/model/use-create-product.ts +0 -15
  30. package/ashraf/src/features/products/create-product/ui/create-product-form.tsx +0 -85
  31. package/ashraf/src/features/products/update-product/api/update-product.ts +0 -12
  32. package/ashraf/src/features/products/update-product/index.ts +0 -2
  33. package/ashraf/src/features/products/update-product/model/update-product.schema.ts +0 -11
  34. package/ashraf/src/features/products/update-product/model/use-update-product.ts +0 -17
  35. package/ashraf/src/features/products/update-product/ui/update-product-form.tsx +0 -69
  36. package/ashraf/src/main.tsx +0 -10
  37. package/ashraf/src/pages/products/all-products.tsx +0 -23
  38. package/ashraf/src/pages/products/create-product-page.tsx +0 -10
  39. package/ashraf/src/pages/products/update-product-page.tsx +0 -24
  40. package/ashraf/src/shared/config/env.ts +0 -1
  41. package/ashraf/src/shared/lib/utils.ts +0 -6
  42. package/ashraf/src/shared/types/types.d.ts +0 -0
  43. package/ashraf/src/shared/ui/input.tsx +0 -21
  44. package/ashraf/src/shared/ui/label.tsx +0 -22
  45. package/ashraf/src/widgets/dashboard-header.tsx +0 -0
  46. package/ashraf/src/widgets/index.ts +0 -0
  47. package/ashraf/src/widgets/sidebar.tsx +0 -0
  48. package/ashraf/tsconfig.app.json +0 -32
  49. package/ashraf/tsconfig.json +0 -21
  50. package/ashraf/tsconfig.node.json +0 -26
  51. package/ashraf/vite.config.ts +0 -14
@@ -1,43 +0,0 @@
1
- {
2
- "name": "mudular-structure-v2",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "tsc -b && vite build",
9
- "lint": "eslint .",
10
- "preview": "vite preview"
11
- },
12
- "dependencies": {
13
- "@tailwindcss/vite": "^4.2.1",
14
- "@tanstack/react-query": "^5.90.21",
15
- "axios": "^1.15.0",
16
- "class-variance-authority": "^0.7.1",
17
- "clsx": "^2.1.1",
18
- "lucide-react": "^0.575.0",
19
- "radix-ui": "^1.4.3",
20
- "react": "^19.2.0",
21
- "react-dom": "^19.2.0",
22
- "react-hook-form": "^7.72.1",
23
- "react-router": "^7.13.1",
24
- "tailwind-merge": "^3.5.0",
25
- "tailwindcss": "^4.2.1"
26
- },
27
- "devDependencies": {
28
- "@eslint/js": "^9.39.1",
29
- "@types/node": "^24.10.1",
30
- "@types/react": "^19.2.7",
31
- "@types/react-dom": "^19.2.3",
32
- "@vitejs/plugin-react-swc": "^4.2.2",
33
- "eslint": "^9.39.1",
34
- "eslint-plugin-react-hooks": "^7.0.1",
35
- "eslint-plugin-react-refresh": "^0.4.24",
36
- "globals": "^16.5.0",
37
- "shadcn": "^3.8.5",
38
- "tw-animate-css": "^1.4.0",
39
- "typescript": "~5.9.3",
40
- "typescript-eslint": "^8.48.0",
41
- "vite": "^7.3.1"
42
- }
43
- }
@@ -1,12 +0,0 @@
1
- function App() {
2
-
3
- return (
4
- <div>
5
- <h1 className="text-3xl font-bold underline">
6
-
7
- </h1>
8
- </div>
9
- )
10
- }
11
-
12
- export default App;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,190 +0,0 @@
1
- @import "tailwindcss";
2
- @import "tw-animate-css";
3
- @import "shadcn/tailwind.css";
4
-
5
- @custom-variant dark (&:is(.dark *));
6
-
7
- :root {
8
- font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
9
- line-height: 1.5;
10
- font-weight: 400;
11
-
12
- color-scheme: light dark;
13
- color: rgba(255, 255, 255, 0.87);
14
- background-color: #242424;
15
-
16
- font-synthesis: none;
17
- text-rendering: optimizeLegibility;
18
- -webkit-font-smoothing: antialiased;
19
- -moz-osx-font-smoothing: grayscale;
20
- --radius: 0.625rem;
21
- --background: oklch(1 0 0);
22
- --foreground: oklch(0.145 0 0);
23
- --card: oklch(1 0 0);
24
- --card-foreground: oklch(0.145 0 0);
25
- --popover: oklch(1 0 0);
26
- --popover-foreground: oklch(0.145 0 0);
27
- --primary: oklch(0.205 0 0);
28
- --primary-foreground: oklch(0.985 0 0);
29
- --secondary: oklch(0.97 0 0);
30
- --secondary-foreground: oklch(0.205 0 0);
31
- --muted: oklch(0.97 0 0);
32
- --muted-foreground: oklch(0.556 0 0);
33
- --accent: oklch(0.97 0 0);
34
- --accent-foreground: oklch(0.205 0 0);
35
- --destructive: oklch(0.577 0.245 27.325);
36
- --border: oklch(0.922 0 0);
37
- --input: oklch(0.922 0 0);
38
- --ring: oklch(0.708 0 0);
39
- --chart-1: oklch(0.646 0.222 41.116);
40
- --chart-2: oklch(0.6 0.118 184.704);
41
- --chart-3: oklch(0.398 0.07 227.392);
42
- --chart-4: oklch(0.828 0.189 84.429);
43
- --chart-5: oklch(0.769 0.188 70.08);
44
- --sidebar: oklch(0.985 0 0);
45
- --sidebar-foreground: oklch(0.145 0 0);
46
- --sidebar-primary: oklch(0.205 0 0);
47
- --sidebar-primary-foreground: oklch(0.985 0 0);
48
- --sidebar-accent: oklch(0.97 0 0);
49
- --sidebar-accent-foreground: oklch(0.205 0 0);
50
- --sidebar-border: oklch(0.922 0 0);
51
- --sidebar-ring: oklch(0.708 0 0);
52
- }
53
-
54
- a {
55
- font-weight: 500;
56
- color: #646cff;
57
- text-decoration: inherit;
58
- }
59
- a:hover {
60
- color: #535bf2;
61
- }
62
-
63
- body {
64
- margin: 0;
65
- display: flex;
66
- place-items: center;
67
- min-width: 320px;
68
- min-height: 100vh;
69
- }
70
-
71
- h1 {
72
- font-size: 3.2em;
73
- line-height: 1.1;
74
- }
75
-
76
- button {
77
- border-radius: 8px;
78
- border: 1px solid transparent;
79
- padding: 0.6em 1.2em;
80
- font-size: 1em;
81
- font-weight: 500;
82
- font-family: inherit;
83
- background-color: #1a1a1a;
84
- cursor: pointer;
85
- transition: border-color 0.25s;
86
- }
87
- button:hover {
88
- border-color: #646cff;
89
- }
90
- button:focus,
91
- button:focus-visible {
92
- outline: 4px auto -webkit-focus-ring-color;
93
- }
94
-
95
- @media (prefers-color-scheme: light) {
96
- :root {
97
- color: #213547;
98
- background-color: #ffffff;
99
- }
100
- a:hover {
101
- color: #747bff;
102
- }
103
- button {
104
- background-color: #f9f9f9;
105
- }
106
- }
107
-
108
- @theme inline {
109
- --radius-sm: calc(var(--radius) - 4px);
110
- --radius-md: calc(var(--radius) - 2px);
111
- --radius-lg: var(--radius);
112
- --radius-xl: calc(var(--radius) + 4px);
113
- --radius-2xl: calc(var(--radius) + 8px);
114
- --radius-3xl: calc(var(--radius) + 12px);
115
- --radius-4xl: calc(var(--radius) + 16px);
116
- --color-background: var(--background);
117
- --color-foreground: var(--foreground);
118
- --color-card: var(--card);
119
- --color-card-foreground: var(--card-foreground);
120
- --color-popover: var(--popover);
121
- --color-popover-foreground: var(--popover-foreground);
122
- --color-primary: var(--primary);
123
- --color-primary-foreground: var(--primary-foreground);
124
- --color-secondary: var(--secondary);
125
- --color-secondary-foreground: var(--secondary-foreground);
126
- --color-muted: var(--muted);
127
- --color-muted-foreground: var(--muted-foreground);
128
- --color-accent: var(--accent);
129
- --color-accent-foreground: var(--accent-foreground);
130
- --color-destructive: var(--destructive);
131
- --color-border: var(--border);
132
- --color-input: var(--input);
133
- --color-ring: var(--ring);
134
- --color-chart-1: var(--chart-1);
135
- --color-chart-2: var(--chart-2);
136
- --color-chart-3: var(--chart-3);
137
- --color-chart-4: var(--chart-4);
138
- --color-chart-5: var(--chart-5);
139
- --color-sidebar: var(--sidebar);
140
- --color-sidebar-foreground: var(--sidebar-foreground);
141
- --color-sidebar-primary: var(--sidebar-primary);
142
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
143
- --color-sidebar-accent: var(--sidebar-accent);
144
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
145
- --color-sidebar-border: var(--sidebar-border);
146
- --color-sidebar-ring: var(--sidebar-ring);
147
- }
148
-
149
- .dark {
150
- --background: oklch(0.145 0 0);
151
- --foreground: oklch(0.985 0 0);
152
- --card: oklch(0.205 0 0);
153
- --card-foreground: oklch(0.985 0 0);
154
- --popover: oklch(0.205 0 0);
155
- --popover-foreground: oklch(0.985 0 0);
156
- --primary: oklch(0.922 0 0);
157
- --primary-foreground: oklch(0.205 0 0);
158
- --secondary: oklch(0.269 0 0);
159
- --secondary-foreground: oklch(0.985 0 0);
160
- --muted: oklch(0.269 0 0);
161
- --muted-foreground: oklch(0.708 0 0);
162
- --accent: oklch(0.269 0 0);
163
- --accent-foreground: oklch(0.985 0 0);
164
- --destructive: oklch(0.704 0.191 22.216);
165
- --border: oklch(1 0 0 / 10%);
166
- --input: oklch(1 0 0 / 15%);
167
- --ring: oklch(0.556 0 0);
168
- --chart-1: oklch(0.488 0.243 264.376);
169
- --chart-2: oklch(0.696 0.17 162.48);
170
- --chart-3: oklch(0.769 0.188 70.08);
171
- --chart-4: oklch(0.627 0.265 303.9);
172
- --chart-5: oklch(0.645 0.246 16.439);
173
- --sidebar: oklch(0.205 0 0);
174
- --sidebar-foreground: oklch(0.985 0 0);
175
- --sidebar-primary: oklch(0.488 0.243 264.376);
176
- --sidebar-primary-foreground: oklch(0.985 0 0);
177
- --sidebar-accent: oklch(0.269 0 0);
178
- --sidebar-accent-foreground: oklch(0.985 0 0);
179
- --sidebar-border: oklch(1 0 0 / 10%);
180
- --sidebar-ring: oklch(0.556 0 0);
181
- }
182
-
183
- @layer base {
184
- * {
185
- @apply border-border outline-ring/50;
186
- }
187
- body {
188
- @apply bg-background text-foreground;
189
- }
190
- }
File without changes
@@ -1,8 +0,0 @@
1
- import axios from "axios";
2
-
3
- import type { Product } from "../model/types";
4
-
5
- export const getProductById = async (id: string): Promise<Product> => {
6
- const response = await axios.get(`/products/${id}`);
7
- return response.data;
8
- };
@@ -1,8 +0,0 @@
1
- import axios from "axios";
2
-
3
- import type { Product } from "../model/types";
4
-
5
- export const getProducts = async (): Promise<Product[]> => {
6
- const response = await axios.get("/products");
7
- return response.data;
8
- };
@@ -1,5 +0,0 @@
1
- export * from "./model/types";
2
- export * from "./model/query-keys";
3
- export * from "./api/get-products";
4
- export * from "./api/get-product-by-id";
5
- export * from "./ui/product-card";
@@ -1,5 +0,0 @@
1
- export const productQueryKeys = {
2
- all: ["products"] as const,
3
- lists: () => [...productQueryKeys.all, "list"] as const,
4
- detail: (id: string) => [...productQueryKeys.all, "detail", id] as const,
5
- };
@@ -1,18 +0,0 @@
1
- export type Product = {
2
- id: string;
3
- title: string;
4
- description: string;
5
- price: number;
6
- imageUrl: string;
7
- stock: number;
8
- createdAt?: string;
9
- updatedAt?: string;
10
- };
11
-
12
- export type ProductPayload = {
13
- title: string;
14
- description: string;
15
- price: number;
16
- imageUrl: string;
17
- stock: number;
18
- };
@@ -1,25 +0,0 @@
1
- import type { Product } from "../model/types";
2
-
3
- type ProductCardProps = {
4
- product: Product;
5
- };
6
-
7
- export const ProductCard = ({ product }: ProductCardProps) => {
8
- return (
9
- <div className="rounded-2xl border p-4 shadow-sm">
10
- <img
11
- src={product.imageUrl}
12
- alt={product.title}
13
- className="mb-3 h-48 w-full rounded-xl object-cover"
14
- />
15
-
16
- <h3 className="text-lg font-bold">{product.title}</h3>
17
- <p className="mt-2 text-sm text-gray-600">{product.description}</p>
18
-
19
- <div className="mt-3 flex items-center justify-between">
20
- <span className="font-semibold">{product.price} EGP</span>
21
- <span className="text-sm text-gray-500">Stock: {product.stock}</span>
22
- </div>
23
- </div>
24
- );
25
- };
@@ -1,7 +0,0 @@
1
- import type { Product, ProductPayload } from "@/entities/products";
2
- import axios from "axios";
3
-
4
- export const createProduct = async (payload: ProductPayload): Promise<Product> => {
5
- const response = await axios.post("/products", payload);
6
- return response.data;
7
- };
@@ -1,2 +0,0 @@
1
- export * from "./ui/create-product-form";
2
- export * from "./model/use-create-product";
@@ -1,11 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const createProductSchema = z.object({
4
- title: z.string().min(2, "Title is required"),
5
- description: z.string().min(5, "Description is required"),
6
- price: z.coerce.number().min(1, "Price must be greater than 0"),
7
- imageUrl: z.string().url("Invalid image url"),
8
- stock: z.coerce.number().min(0, "Stock cannot be negative"),
9
- });
10
-
11
- export type CreateProductFormValues = z.infer<typeof createProductSchema>;
@@ -1,15 +0,0 @@
1
- import { useMutation, useQueryClient } from "@tanstack/react-query";
2
- import { productQueryKeys } from "@/entities/products";
3
-
4
- import { createProduct } from "../api/create-product";
5
-
6
- export const useCreateProduct = () => {
7
- const queryClient = useQueryClient();
8
-
9
- return useMutation({
10
- mutationFn: createProduct,
11
- onSuccess: () => {
12
- queryClient.invalidateQueries({ queryKey: productQueryKeys.all });
13
- },
14
- });
15
- };
@@ -1,85 +0,0 @@
1
- import { useForm } from "react-hook-form";
2
-
3
- import { zodResolver } from "@hookform/resolvers/zod";
4
-
5
- import { createProductSchema, type CreateProductFormValues } from "../model/create-product-schema";
6
- import { useCreateProduct } from "../model/use-create-product";
7
-
8
- export const CreateProductForm = () => {
9
- const { mutate, isPending } = useCreateProduct();
10
-
11
- const form = useForm<CreateProductFormValues>({
12
- resolver: zodResolver(createProductSchema),
13
- defaultValues: {
14
- title: "",
15
- description: "",
16
- price: 0,
17
- imageUrl: "",
18
- stock: 0,
19
- },
20
- });
21
-
22
- const onSubmit = (values: CreateProductFormValues) => {
23
- mutate(values, {
24
- onSuccess: () => {
25
- form.reset();
26
- },
27
- });
28
- };
29
-
30
- return (
31
- <form
32
- onSubmit={form.handleSubmit(onSubmit)}
33
- className="space-y-4 rounded-2xl border p-5"
34
- >
35
- <input
36
- placeholder="Title"
37
- className="w-full rounded-xl border p-3"
38
- {...form.register("title")}
39
- />
40
- <p className="text-sm text-red-500">{form.formState.errors.title?.message}</p>
41
-
42
- <textarea
43
- placeholder="Description"
44
- className="w-full rounded-xl border p-3"
45
- {...form.register("description")}
46
- />
47
- <p className="text-sm text-red-500">
48
- {form.formState.errors.description?.message}
49
- </p>
50
-
51
- <input
52
- type="number"
53
- placeholder="Price"
54
- className="w-full rounded-xl border p-3"
55
- {...form.register("price")}
56
- />
57
- <p className="text-sm text-red-500">{form.formState.errors.price?.message}</p>
58
-
59
- <input
60
- placeholder="Image URL"
61
- className="w-full rounded-xl border p-3"
62
- {...form.register("imageUrl")}
63
- />
64
- <p className="text-sm text-red-500">
65
- {form.formState.errors.imageUrl?.message}
66
- </p>
67
-
68
- <input
69
- type="number"
70
- placeholder="Stock"
71
- className="w-full rounded-xl border p-3"
72
- {...form.register("stock")}
73
- />
74
- <p className="text-sm text-red-500">{form.formState.errors.stock?.message}</p>
75
-
76
- <button
77
- type="submit"
78
- disabled={isPending}
79
- className="rounded-xl bg-black px-4 py-2 text-white disabled:opacity-50"
80
- >
81
- {isPending ? "Creating..." : "Create Product"}
82
- </button>
83
- </form>
84
- );
85
- };
@@ -1,12 +0,0 @@
1
- import type { Product, ProductPayload } from "@/entities/products";
2
- import axios from "axios";
3
-
4
- type UpdateProductParams = {
5
- id: string;
6
- payload: ProductPayload;
7
- };
8
-
9
- export const updateProduct = async ({ id, payload }: UpdateProductParams): Promise<Product> => {
10
- const response = await axios.put(`/products/${id}`, payload);
11
- return response.data;
12
- };
@@ -1,2 +0,0 @@
1
- export * from "./ui/update-product-form";
2
- export * from "./model/use-update-product";
@@ -1,11 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const updateProductSchema = z.object({
4
- title: z.string().min(2, "Title is required"),
5
- description: z.string().min(5, "Description is required"),
6
- price: z.coerce.number().min(1, "Price must be greater than 0"),
7
- imageUrl: z.string().url("Invalid image url"),
8
- stock: z.coerce.number().min(0, "Stock cannot be negative"),
9
- });
10
-
11
- export type UpdateProductFormValues = z.infer<typeof updateProductSchema>;
@@ -1,17 +0,0 @@
1
- import { useMutation, useQueryClient } from "@tanstack/react-query";
2
- import { updateProduct } from "../api/update-product";
3
- import { productQueryKeys } from "@/entities/products";
4
-
5
- export const useUpdateProduct = () => {
6
- const queryClient = useQueryClient();
7
-
8
- return useMutation({
9
- mutationFn: updateProduct,
10
- onSuccess: (updatedProduct) => {
11
- queryClient.invalidateQueries({ queryKey: productQueryKeys.all });
12
- queryClient.invalidateQueries({
13
- queryKey: productQueryKeys.detail(updatedProduct.id),
14
- });
15
- },
16
- });
17
- };
@@ -1,69 +0,0 @@
1
- import { useForm } from "react-hook-form";
2
- import { zodResolver } from "@hookform/resolvers/zod";
3
- import type { Product } from "@/entities/products";
4
- import {
5
- updateProductSchema,
6
- type UpdateProductFormValues,
7
- } from "../model/update-product.schema";
8
- import { useUpdateProduct } from "../model/use-update-product";
9
-
10
- type UpdateProductFormProps = {
11
- product: Product;
12
- };
13
-
14
- export const UpdateProductForm = ({ product }: UpdateProductFormProps) => {
15
- const { mutate, isPending } = useUpdateProduct();
16
-
17
- const form = useForm<UpdateProductFormValues>({
18
- resolver: zodResolver(updateProductSchema),
19
- defaultValues: {
20
- title: product.title,
21
- description: product.description,
22
- price: product.price,
23
- imageUrl: product.imageUrl,
24
- stock: product.stock,
25
- },
26
- });
27
-
28
- const onSubmit = (values: UpdateProductFormValues) => {
29
- mutate({
30
- id: product.id,
31
- payload: values,
32
- });
33
- };
34
-
35
- return (
36
- <form
37
- onSubmit={form.handleSubmit(onSubmit)}
38
- className="space-y-4 rounded-2xl border p-5"
39
- >
40
- <input className="w-full rounded-xl border p-3" {...form.register("title")} />
41
- <textarea
42
- className="w-full rounded-xl border p-3"
43
- {...form.register("description")}
44
- />
45
- <input
46
- type="number"
47
- className="w-full rounded-xl border p-3"
48
- {...form.register("price")}
49
- />
50
- <input
51
- className="w-full rounded-xl border p-3"
52
- {...form.register("imageUrl")}
53
- />
54
- <input
55
- type="number"
56
- className="w-full rounded-xl border p-3"
57
- {...form.register("stock")}
58
- />
59
-
60
- <button
61
- type="submit"
62
- disabled={isPending}
63
- className="rounded-xl bg-black px-4 py-2 text-white disabled:opacity-50"
64
- >
65
- {isPending ? "Updating..." : "Update Product"}
66
- </button>
67
- </form>
68
- );
69
- };
@@ -1,10 +0,0 @@
1
- import { StrictMode } from 'react'
2
- import { createRoot } from 'react-dom/client'
3
- import './index.css'
4
- import App from './app/App.tsx'
5
-
6
- createRoot(document.getElementById('root')!).render(
7
- <StrictMode>
8
- <App />
9
- </StrictMode>,
10
- )
@@ -1,23 +0,0 @@
1
- import { useQuery } from "@tanstack/react-query";
2
- import { getProducts, productQueryKeys, ProductCard, type Product } from "@/entities/products";
3
-
4
- export const AllProductsPage = () => {
5
- const { data, isLoading, isError } = useQuery<Product[]>({
6
- queryKey: productQueryKeys.lists(),
7
- queryFn: getProducts,
8
- });
9
-
10
- if (isLoading) return <p>Loading...</p>;
11
- if (isError) return <p>Something went wrong</p>;
12
-
13
- return (
14
- <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
15
- {data?.map((product) => (
16
- <div key={product.id} className="space-y-3">
17
- <ProductCard product={product} />
18
- {/* <DeleteProductButton productId={product.id} /> */}
19
- </div>
20
- ))}
21
- </div>
22
- );
23
- };
@@ -1,10 +0,0 @@
1
- import { CreateProductForm } from "@/features/products/create-product";
2
-
3
- export const CreateProductPage = () => {
4
- return (
5
- <div className="mx-auto max-w-2xl">
6
- <h1 className="mb-4 text-2xl font-bold">Create Product</h1>
7
- <CreateProductForm />
8
- </div>
9
- );
10
- };
@@ -1,24 +0,0 @@
1
- import { useQuery } from "@tanstack/react-query";
2
- import { getProductById, productQueryKeys } from "@/entities/products";
3
- import { UpdateProductForm } from "@/features/products/update-product";
4
-
5
- type UpdateProductPageProps = {
6
- productId: string;
7
- };
8
-
9
- export const UpdateProductPage = ({ productId }: UpdateProductPageProps) => {
10
- const { data, isLoading, isError } = useQuery({
11
- queryKey: productQueryKeys.detail(productId),
12
- queryFn: () => getProductById(productId),
13
- });
14
-
15
- if (isLoading) return <p>Loading...</p>;
16
- if (isError || !data) return <p>Product not found</p>;
17
-
18
- return (
19
- <div className="mx-auto max-w-2xl">
20
- <h1 className="mb-4 text-2xl font-bold">Update Product</h1>
21
- <UpdateProductForm product={data} />
22
- </div>
23
- );
24
- };
@@ -1 +0,0 @@
1
- export const BASE_URL = "http://localhost:3000";
@@ -1,6 +0,0 @@
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
- }