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

Commit

Permalink
style: add product form
Browse files Browse the repository at this point in the history
  • Loading branch information
foxminchan committed Jun 3, 2024
1 parent 8d6a32d commit f36d71f
Show file tree
Hide file tree
Showing 11 changed files with 529 additions and 65 deletions.
2 changes: 1 addition & 1 deletion docs/.vuepress/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default hopeTheme({

repo: "foxminchan/RookieShop",

docsDir: "src",
docsDir: "docs",

// navbar
navbar,
Expand Down
63 changes: 10 additions & 53 deletions ui/backoffice/app/(dashboard)/dashboard/product/new/page.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,16 @@
import Link from "next/link"
import useListCategories from "@/features/category/useListCategories"
import { Plus } from "lucide-react"
import BreadCrumb from "@/components/ui/breadcrumb"
import { ProductForm } from "@/components/forms/product-form"

import { cn } from "@/lib/utils"
import Breadcrumb from "@/components/ui/breadcrumb"
import { buttonVariants } from "@/components/ui/button"
import { Heading } from "@/components/ui/heading"
import { Separator } from "@/components/ui/separator"
import CategoryTable from "@/components/tables/category/table"

const breadcrumbItems = [{ title: "Category", link: "/dashboard/category" }]

type paramsProps = {
searchParams: {
[key: string]: string | string[] | undefined
}
}

export default async function page({ searchParams }: paramsProps) {
const page = Number(searchParams.page) || 1
const pageLimit = Number(searchParams.limit) || 10
const name = (searchParams.search as string) || null

const categories = useListCategories({
pageNumber: page,
pageSize: pageLimit,
search: name,
})

const data = categories.data?.data
const totalCategories = categories.data?.data.pagedInfo.totalRecords
export default function AddProduct() {
const breadcrumbItems = [
{ title: "Product", link: "/dashboard/product" },
{ title: "Create", link: "/dashboard/product/new" },
]

return (
<div className="flex-1 space-y-4 p-4 pt-6 md:p-8">
<Breadcrumb items={breadcrumbItems} />
<div className="flex items-start justify-between">
<Heading
title={`Category (${totalCategories})`}
description="Manage categories"
/>

<Link
href={"/dashboard/employee/new"}
className={cn(buttonVariants({ variant: "default" }))}
>
<Plus className="mr-2 h-4 w-4" /> Add New
</Link>
</div>
<Separator />
<CategoryTable
page={page}
pageCount={data?.pagedInfo.totalPages || 0}
data={data?.categories || []}
totalRecords={totalCategories || 0}
/>
<div className="flex-1 space-y-4 p-8">
<BreadCrumb items={breadcrumbItems} />
<ProductForm initialData={null} key={null} />
</div>
)
}
Binary file modified ui/backoffice/bun.lockb
Binary file not shown.
116 changes: 116 additions & 0 deletions ui/backoffice/components/custom/file-upload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"use client"

import Image from "next/image"
import { Trash } from "lucide-react"

import { IMG_MAX_SIZE } from "@/lib/constants/default"

import { Button } from "../ui/button"
import { Input } from "../ui/input"
import { Label } from "../ui/label"
import { useToast } from "../ui/use-toast"

type ImageUploadProps = {
onChange?: any
value?: File | null
}

export default function FileUpload({ onChange, value }: ImageUploadProps) {
const { toast } = useToast()

const onDeleteFile = () => {
onChange(null)
}

return (
<>
<div className="mb-4 flex items-center gap-4">
{value && (
<div className="relative h-[200px] w-[200px] overflow-hidden rounded-md">
<div className="absolute right-2 top-2 z-10">
<Button
type="button"
onClick={() => onDeleteFile()}
variant="destructive"
size="sm"
>
<Trash className="h-4 w-4" />
</Button>
</div>
<div>
<Image
src={URL.createObjectURL(value)}
alt="Product image"
layout="fill"
objectFit="cover"
/>
</div>
</div>
)}
</div>
<div className="flex items-center justify-center w-full">
{!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"
>
<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>
<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
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
PNG, JPG or JPEG (MAX: 1MB)
</p>
</div>
<Input
type="file"
id="picture"
accept="image/*"
className="hidden"
onChange={(e) => {
const file = e.target.files?.[0]
if (file) {
if (file.size > IMG_MAX_SIZE) {
toast({
variant: "destructive",
title: "Uh oh! Something went wrong.",
description: "Image size must be less than 1MB",
})
} else if (
!["image/jpeg", "image/png", "image/jpg"].includes(
file.type
)
) {
toast({
variant: "destructive",
title: "Uh oh! Something went wrong.",
description: "Image must be in jpeg, jpg, or png format",
})
} else {
onChange(file)
}
}
}}
/>
</Label>
)}
</div>
</>
)
}
Loading

0 comments on commit f36d71f

Please sign in to comment.