-
Notifications
You must be signed in to change notification settings - Fork 265
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #671 from hngprojects/feat/HNG-147-super-admin-pro…
…duct-list-page Feat/hng 147 super admin product list page
- Loading branch information
Showing
10 changed files
with
768 additions
and
5 deletions.
There are no files selected for viewing
114 changes: 114 additions & 0 deletions
114
src/app/dashboard/(admin)/admin/products/_components/ProductModal/add-product-modal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { Button } from "~/components/common/common-button"; | ||
import { Input } from "~/components/common/input"; | ||
import { | ||
Dialog, | ||
DialogContent, | ||
DialogDescription, | ||
DialogFooter, | ||
DialogHeader, | ||
DialogTitle, | ||
DialogTrigger, | ||
} from "~/components/ui/dialog"; | ||
import { Label } from "~/components/ui/label"; | ||
import { Textarea } from "~/components/ui/textarea"; | ||
|
||
interface AddProductModalProperties { | ||
children: React.ReactNode; | ||
} | ||
const AddProductModal = ({ children }: AddProductModalProperties) => { | ||
return ( | ||
<Dialog> | ||
<DialogTrigger asChild>{children}</DialogTrigger> | ||
<DialogContent className="flex h-fit max-h-[500px] flex-col gap-[23px] overflow-y-auto rounded-none border bg-white p-6 sm:max-w-[500px]"> | ||
<DialogHeader className="inline-flex flex-col items-start justify-start"> | ||
<DialogTitle className="text-lg font-bold text-neutral-950"> | ||
Add new product | ||
</DialogTitle> | ||
<DialogDescription className="text-xs leading-3 text-slate-500"> | ||
Create a new product | ||
</DialogDescription> | ||
</DialogHeader> | ||
<div className="flex flex-col gap-3 px-2"> | ||
<div className="items-left flex flex-col gap-1"> | ||
<Label | ||
htmlFor="productname" | ||
className="left-0 text-left text-sm font-medium text-slate-900" | ||
> | ||
Product name | ||
</Label> | ||
<Input | ||
id="productname" | ||
required | ||
placeholder="@Joh Doe" | ||
className="col-span-3 inline-flex h-10 items-start justify-start gap-2" | ||
/> | ||
</div> | ||
<div className="items-left flex flex-col gap-1"> | ||
<Label | ||
htmlFor="productdescription" | ||
className="left-0 text-left text-sm font-medium text-slate-900" | ||
> | ||
Product Description | ||
</Label> | ||
<Textarea | ||
id="productdescription" | ||
required | ||
placeholder="add product description" | ||
className="col-span-3 inline-flex h-10 items-start justify-start gap-2 border bg-transparent text-primary focus:outline-none focus:ring-1 focus:ring-primary focus-visible:ring-1 focus-visible:ring-primary focus-visible:ring-offset-0" | ||
/> | ||
</div> | ||
<div className="items-left flex flex-col gap-1"> | ||
<Label | ||
htmlFor="price" | ||
className="left-0 text-left text-sm font-medium text-slate-900" | ||
> | ||
Price | ||
</Label> | ||
<Input | ||
type="number" | ||
id="price" | ||
required | ||
placeholder="e.g 2000.00" | ||
className="col-span-3 inline-flex h-10 items-start justify-start gap-2" | ||
/> | ||
</div> | ||
<div className="items-left flex flex-col gap-1"> | ||
<Label | ||
htmlFor="quantity" | ||
className="left-0 text-left text-sm font-medium text-slate-900" | ||
> | ||
Quantity | ||
</Label> | ||
<Input | ||
type="number" | ||
id="quantity" | ||
required | ||
placeholder="e.g 1000" | ||
className="col-span-3 inline-flex h-10 items-start justify-start gap-2" | ||
/> | ||
</div> | ||
<div className="items-left flex flex-col gap-1"> | ||
<span className="left-0 text-left text-sm font-medium text-slate-900"> | ||
Upload Images | ||
</span> | ||
<Button className="w-48" variant={"subtle"} size={"sm"}> | ||
Choose file No file choosen | ||
</Button> | ||
<div className="mt-4 inline-flex h-[56px] items-start justify-start gap-[15px]"> | ||
<div className="h-14 w-14 rounded bg-neutral-50" /> | ||
<div className="h-14 w-14 rounded bg-neutral-50" /> | ||
<div className="h-14 w-14 rounded bg-neutral-50" /> | ||
</div> | ||
</div> | ||
</div> | ||
<DialogFooter> | ||
<Button variant={"primary"} type="submit"> | ||
Add Product | ||
</Button> | ||
</DialogFooter> | ||
</DialogContent> | ||
</Dialog> | ||
); | ||
}; | ||
|
||
export default AddProductModal; |
73 changes: 73 additions & 0 deletions
73
src/app/dashboard/(admin)/admin/products/_components/ProductModal/add-product.modal.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { fireEvent, render, screen } from "@testing-library/react"; | ||
import { describe, expect, it } from "vitest"; | ||
|
||
import AddProductModal from "./add-product-modal"; | ||
|
||
describe("addProductModal", () => { | ||
it("renders the modal with correct title and description", async () => { | ||
expect.hasAssertions(); | ||
render( | ||
<AddProductModal> | ||
<button>Open Modal</button> | ||
</AddProductModal>, | ||
); | ||
|
||
// Check if the trigger button is rendered | ||
expect(screen.getByText("Open Modal")).toBeInTheDocument(); | ||
|
||
// Open the modal | ||
fireEvent.click(screen.getByText("Open Modal")); | ||
|
||
// Check if the modal title and description are rendered | ||
await expect( | ||
screen.findByText("Add new product"), | ||
).resolves.toBeInTheDocument(); | ||
await expect( | ||
screen.findByText("Create a new product"), | ||
).resolves.toBeInTheDocument(); | ||
}); | ||
|
||
it("renders form fields", async () => { | ||
expect.hasAssertions(); | ||
render( | ||
<AddProductModal> | ||
<button>Open Modal</button> | ||
</AddProductModal>, | ||
); | ||
|
||
// Open the modal | ||
fireEvent.click(screen.getByText("Open Modal")); | ||
|
||
// Check if form fields are rendered | ||
await expect( | ||
screen.findByLabelText("Product name"), | ||
).resolves.toBeInTheDocument(); | ||
await expect( | ||
screen.findByLabelText("Product Description"), | ||
).resolves.toBeInTheDocument(); | ||
await expect(screen.findByLabelText("Price")).resolves.toBeInTheDocument(); | ||
await expect( | ||
screen.findByLabelText("Quantity"), | ||
).resolves.toBeInTheDocument(); | ||
await expect( | ||
screen.findByText("Upload Images"), | ||
).resolves.toBeInTheDocument(); | ||
}); | ||
|
||
it("renders submit button", async () => { | ||
expect.hasAssertions(); | ||
render( | ||
<AddProductModal> | ||
<button>Open Modal</button> | ||
</AddProductModal>, | ||
); | ||
|
||
// Open the modal | ||
fireEvent.click(screen.getByText("Open Modal")); | ||
|
||
// Check if the submit button is rendered | ||
await expect( | ||
screen.findByRole("button", { name: "Add Product" }), | ||
).resolves.toBeInTheDocument(); | ||
}); | ||
}); |
39 changes: 39 additions & 0 deletions
39
src/app/dashboard/(admin)/admin/products/_components/ProductModal/delete-dialog.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { fireEvent, render, screen } from "@testing-library/react"; | ||
import { describe, expect, it, vi } from "vitest"; | ||
|
||
import DeleteDialog from "./delete-dialog"; | ||
|
||
describe("deleteDialog", () => { | ||
it("renders the dialog with correct content", () => { | ||
expect.hasAssertions(); | ||
const onClose = vi.fn(); | ||
render(<DeleteDialog onClose={onClose} />); | ||
|
||
expect(screen.getByText("Are you absolutely sure?")).toBeInTheDocument(); | ||
expect( | ||
screen.getByText( | ||
"This action cannot be undone. This will permanently delete this product from the database.", | ||
), | ||
).toBeInTheDocument(); | ||
expect(screen.getByRole("button", { name: /cancel/i })).toBeInTheDocument(); | ||
expect(screen.getByRole("button", { name: /delete/i })).toBeInTheDocument(); | ||
}); | ||
|
||
it("calls onClose when Cancel button is clicked", () => { | ||
expect.hasAssertions(); | ||
const onClose = vi.fn(); | ||
render(<DeleteDialog onClose={onClose} />); | ||
|
||
fireEvent.click(screen.getByRole("button", { name: /cancel/i })); | ||
expect(onClose).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it("calls onClose when Delete button is clicked", () => { | ||
expect.hasAssertions(); | ||
const onClose = vi.fn(); | ||
render(<DeleteDialog onClose={onClose} />); | ||
|
||
fireEvent.click(screen.getByRole("button", { name: /delete/i })); | ||
expect(onClose).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
43 changes: 43 additions & 0 deletions
43
src/app/dashboard/(admin)/admin/products/_components/ProductModal/delete-dialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Button } from "~/components/common/common-button"; | ||
import { | ||
Dialog, | ||
DialogContent, | ||
DialogDescription, | ||
DialogHeader, | ||
DialogTitle, | ||
} from "~/components/ui/dialog"; | ||
|
||
const DeleteDialog = ({ onClose }: { onClose: () => void }) => { | ||
return ( | ||
<Dialog open onOpenChange={onClose}> | ||
<DialogContent> | ||
<DialogHeader> | ||
<DialogTitle className="text-lg font-semibold leading-7 text-slate-900"> | ||
Are you absolutely sure? | ||
</DialogTitle> | ||
<DialogDescription className="text-sm font-normal leading-tight text-slate-500"> | ||
This action cannot be undone. This will permanently delete this | ||
product from the database. | ||
</DialogDescription> | ||
</DialogHeader> | ||
<div className="flex w-full"> | ||
<Button | ||
variant="outline" | ||
className="ml-auto flex items-center justify-center gap-2.5 rounded-md px-4 py-2 text-sm font-medium leading-normal" | ||
onClick={onClose} | ||
> | ||
Cancel | ||
</Button> | ||
<Button | ||
onClick={onClose} | ||
className="ml-2 flex items-center justify-center gap-2.5 rounded-md bg-red-600 px-4 py-2 text-sm font-medium leading-normal text-white" | ||
> | ||
Delete | ||
</Button> | ||
</div> | ||
</DialogContent> | ||
</Dialog> | ||
); | ||
}; | ||
|
||
export default DeleteDialog; |
Oops, something went wrong.