Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Commit

Permalink
fix: product admin page issue
Browse files Browse the repository at this point in the history
  • Loading branch information
foxminchan committed Jun 4, 2024
1 parent 6e92cb0 commit e654458
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function EditCategory({
return (
<div className="flex-1 space-y-4 p-8">
<BreadCrumb items={breadcrumbItems} />
<CategoryForm initialData={data || null} key={null} />
{data && <CategoryForm initialData={data} />}
</div>
)
}
7 changes: 6 additions & 1 deletion ui/backoffice/app/(dashboard)/dashboard/category/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client"

import { useEffect } from "react"
import Link from "next/link"
import useListCategories from "@/features/category/useListCategories"

Expand All @@ -25,7 +26,7 @@ export default function CatgoryPage({ searchParams }: Readonly<paramsProps>) {
const pageLimit = Number(searchParams.limit) || DEFAULT_PAGE_SIZE
const name = (searchParams.search as string) || null

const { data } = useListCategories({
const { data, refetch } = useListCategories({
pageIndex: page,
pageSize: pageLimit,
search: name,
Expand All @@ -34,6 +35,10 @@ export default function CatgoryPage({ searchParams }: Readonly<paramsProps>) {
const categories = data?.categories || []
const totalCategories = data?.pagedInfo.totalRecords ?? 0

useEffect(() => {
refetch()
}, [data, name, refetch])

return (
<div className="flex-1 space-y-4 p-4 pt-6 md:p-8">
<Breadcrumb items={breadcrumbItems} />
Expand Down
2 changes: 1 addition & 1 deletion ui/backoffice/app/(dashboard)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function MainDashboardLayout({
return (
<>
<Header />
<div className="flex h-screen overflow-hidden">
<div className="flex overflow-hidden">
<Sidebar />
<main className="w-full pt-16">{children}</main>
</div>
Expand Down
26 changes: 26 additions & 0 deletions ui/backoffice/app/(dashboard)/dashboard/product/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client"

import useGetProduct from "@/features/product/useGetProduct"

import BreadCrumb from "@/components/ui/breadcrumb"
import { ProductForm } from "@/components/forms/product-form"

export default function EditProduct({
params,
}: Readonly<{ params: { id: string } }>) {
const breadcrumbItems = [
{ title: "Product", link: "/dashboard/product" },
{ title: "Edit", link: `/dashboard/product/${params.id}` },
]

const { data } = useGetProduct(params.id)

return (
<div className="flex-1 space-y-4 p-8">
<BreadCrumb items={breadcrumbItems} />
{data && (
<ProductForm initialData={data} currentProductImages={data.imageUrl} />
)}
</div>
)
}
18 changes: 2 additions & 16 deletions ui/backoffice/components/custom/file-upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,10 @@ export default function FileUpload({
{!value && (
<Label
htmlFor="picture"
className="flex flex-col items-center justify-center w-full h-54 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
className="flex flex-col items-center justify-center w-full h-80 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
>
<div className="flex flex-col items-center justify-center pt-5 pb-6">
<svg
className="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 16"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
/>
</svg>
<Icons.upload className="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400" />
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400">
<span className="font-semibold">Click to upload</span> or drag
and drop
Expand Down
10 changes: 6 additions & 4 deletions ui/backoffice/components/custom/filter-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
useReactTable,
} from "@tanstack/react-table"

import useDebounce from "@/hooks/useDebounce"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
Expand Down Expand Up @@ -106,21 +107,22 @@ export default function FilterTable<TData, TValue>({
})

const searchValue = table.getColumn(searchKey)?.getFilterValue() as string
const debouncedSearchValue = useDebounce(searchValue, 500)

useEffect(() => {
if (searchValue?.length > 0) {
if (debouncedSearchValue?.length > 0) {
router.push(
`${pathname}?${createQueryString({
page: null,
limit: null,
search: searchValue,
search: debouncedSearchValue,
})}`,
{
scroll: false,
}
)
}
if (searchValue?.length === 0 || searchValue === undefined) {
if (debouncedSearchValue?.length === 0 || searchValue === undefined) {
router.push(
`${pathname}?${createQueryString({
page: null,
Expand All @@ -134,7 +136,7 @@ export default function FilterTable<TData, TValue>({
}

setPagination((prev) => ({ ...prev, pageIndex: 0 }))
}, [searchValue])
}, [debouncedSearchValue])

return (
<>
Expand Down
2 changes: 2 additions & 0 deletions ui/backoffice/components/custom/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
Store,
SunMedium,
Trash,
Upload,
User,
X,
} from "lucide-react"
Expand Down Expand Up @@ -66,4 +67,5 @@ export const Icons = {
arrow: ArrowUpDown,
more: MoreHorizontal,
edit: Edit,
upload: Upload,
}
2 changes: 2 additions & 0 deletions ui/backoffice/components/forms/category-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export const CategoryForm: FC<CategoryFormProps> = ({ initialData }) => {
description: "",
}

console.log("defaultValues", defaultValues)

const form = useForm<CategoryFormValues>({
resolver: zodResolver(categorySchema),
defaultValues,
Expand Down
127 changes: 71 additions & 56 deletions ui/backoffice/components/forms/product-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Image from "next/image"
import { useParams, useRouter } from "next/navigation"
import useListCategories from "@/features/category/useListCategories"
import {
CreateProductRequest,
ProductStatus,
UpdateProductRequest,
} from "@/features/product/product.type"
Expand All @@ -14,6 +15,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"

import { cn } from "@/lib/utils"
import { productSchema } from "@/lib/validations/product"

import FileUpload from "../custom/file-upload"
Expand Down Expand Up @@ -45,7 +47,7 @@ import { useToast } from "../ui/use-toast"
type ProductFormValues = z.infer<typeof productSchema>

type ProductFormProps = {
initialData: UpdateProductRequest | null
initialData: CreateProductRequest | UpdateProductRequest | null
currentProductImages?: string | null
}

Expand Down Expand Up @@ -79,23 +81,25 @@ export const ProductForm: FC<ProductFormProps> = ({
const toastMessage = initialData ? "Product updated." : "Product created."
const action = initialData ? "Save changes" : "Create"

const defaultValues = initialData ?? {
name: "",
description: "",
quantity: 0,
price: 0,
priceSale: 0,
isDeletedOldImage: null,
status: ProductStatus.OutOfStock,
productImages: null,
categoryId: null,
}
const defaultValues =
initialData ??
({
name: "",
description: "",
quantity: 0,
price: 0,
priceSale: 0,
productImages: undefined,
categoryId: undefined,
} satisfies CreateProductRequest)

const form = useForm<ProductFormValues>({
resolver: zodResolver(productSchema),
// defaultValues,
defaultValues,
})

const isDeleteImageSelected = form.watch("isDeletedOldImage")

const onSubmit = async (data: ProductFormValues) => {
try {
if (!initialData) {
Expand Down Expand Up @@ -142,50 +146,62 @@ export const ProductForm: FC<ProductFormProps> = ({
className="w-full space-y-8"
encType="multipart/form-data"
>
<FormField
control={form.control}
name="productImages"
render={({ field }) => (
<FormItem>
<FormLabel>Images</FormLabel>
<FormControl>
<FileUpload onChange={field.onChange} value={field.value} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{initialData && currentProductImages && (
<div className="gap-3 md:grid md:grid-cols-2 rounded-md border p-5">
<Image
src={currentProductImages}
alt="Product image"
width={50}
height={50}
/>
<div className="grid grid-cols-4 gap-8">
<div className="col-span-3">
<FormField
control={form.control}
name="isDeletedOldImage"
name="productImages"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4">
<FormLabel></FormLabel>
<FormItem>
<FormLabel>Images</FormLabel>
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
<FileUpload
onChange={field.onChange}
value={field.value}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>Remove old images</FormLabel>
<FormDescription>
Your old images will be removed.
</FormDescription>
</div>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
{initialData && currentProductImages && (
<div className="cols-span-1 space-y-4">
<div className="w-full p-2">
<img
loading="lazy"
src={currentProductImages}
alt="Product image"
className={cn(
"w-full",
isDeleteImageSelected && "opacity-50"
)}
/>
</div>
<FormField
control={form.control}
name="isDeletedOldImage"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4">
<FormLabel></FormLabel>
<FormControl>
<Checkbox
checked={field.value || undefined}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>Remove old images</FormLabel>
<FormDescription>
Your old images will be removed.
</FormDescription>
</div>
</FormItem>
)}
/>
</div>
)}
</div>
<div className="gap-8 md:grid md:grid-cols-2">
<FormField
control={form.control}
Expand Down Expand Up @@ -288,23 +304,23 @@ export const ProductForm: FC<ProductFormProps> = ({
<FormLabel>Status</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={`${field.value}`}
defaultValue={ProductStatus[field.value]}
>
<FormControl>
<SelectTrigger className="w-[180px]">
<SelectTrigger>
<SelectValue placeholder="Select status" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectGroup>
<SelectLabel>Status</SelectLabel>
<SelectItem value={`${ProductStatus.InStock}`}>
<SelectItem value={ProductStatus.InStock}>
In stock
</SelectItem>
<SelectItem value={`${ProductStatus.OutOfStock}`}>
<SelectItem value={ProductStatus.OutOfStock}>
Out of stock
</SelectItem>
<SelectItem value={`${ProductStatus.InStock}`}>
<SelectItem value={ProductStatus.Discontinued}>
Out of stock
</SelectItem>
</SelectGroup>
Expand All @@ -323,16 +339,15 @@ export const ProductForm: FC<ProductFormProps> = ({
<FormLabel>Category</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={`${field.value}`}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select category" />
<SelectTrigger>
<SelectValue placeholder="Select a category" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectGroup>
<SelectLabel>Category</SelectLabel>
{data?.categories?.map((category) => (
<SelectItem key={category.id} value={category.id}>
{category.name}
Expand Down
3 changes: 2 additions & 1 deletion ui/backoffice/features/product/product.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ListProducts,
Product,
ProductFilterParams,
UpdateProductRequest,
} from "./product.type"

class ProductService extends HttpService {
Expand All @@ -26,7 +27,7 @@ class ProductService extends HttpService {
return this.post(`/products`, data)
}

updateProduct(data: CreateProductRequest): Promise<Product> {
updateProduct(data: UpdateProductRequest): Promise<Product> {
return this.put(`/products`, data)
}

Expand Down
Loading

0 comments on commit e654458

Please sign in to comment.