Files
btpxpress-frontend/app/(main)/ecommerce/new-product/page.tsx
2025-10-01 01:39:07 +00:00

292 lines
14 KiB
TypeScript

'use client';
import { Button } from 'primereact/button';
import { Chip } from 'primereact/chip';
import { Dropdown } from 'primereact/dropdown';
import { Editor } from 'primereact/editor';
import { FileUpload, FileUploadSelectEvent, FileUploadUploadEvent, ItemTemplateOptions } from 'primereact/fileupload';
import { InputSwitch } from 'primereact/inputswitch';
import { InputText } from 'primereact/inputtext';
import { classNames } from 'primereact/utils';
import React, { useRef, useState } from 'react';
import type { Demo } from '@/types';
function NewProduct() {
const colorOptions = [
{ name: 'Black', background: 'bg-gray-900' },
{ name: 'Orange', background: 'bg-orange-500' },
{ name: 'Navy', background: 'bg-blue-500' }
];
const [product, setProduct] = useState<Demo.Product>({
name: '',
price: '',
code: '',
sku: '',
status: 'Draft',
tags: ['Nike', 'Sneaker'],
category: 'Sneakers',
colors: [],
stock: 'Sneakers',
inStock: true,
description: '',
images: []
});
const [selectedCategory, setSelectedCategory] = useState(product.category);
const [selectedStock, setSelectedStock] = useState(product.category);
const categoryOptions = ['Sneakers', 'Apparel', 'Socks'];
const fileUploader = useRef<FileUpload>(null);
const chipTemplate = (tag: string) => {
return (
<React.Fragment>
<span className="mr-3">{tag}</span>
<span className="chip-remove-icon flex align-items-center justify-content-center border-1 surface-border bg-gray-100 border-circle cursor-pointer" onClick={() => onChipRemove(tag)}>
<i className="pi pi-fw pi-times text-black-alpha-60"></i>
</span>
</React.Fragment>
);
};
const onImageMouseOver = (ref: React.RefObject<Button>, fileName: string) => {
if ((ref.current as any).id === fileName) (ref.current as any).style.display = 'flex';
};
const onImageMouseLeave = (ref: React.RefObject<Button>, fileName: string) => {
if ((ref.current as any).id === fileName) {
(ref.current as any).style.display = 'none';
}
};
const onChipRemove = (item: string) => {
const newTags = (product.tags as string[])?.filter((i) => i !== item);
setProduct((prevState) => ({ ...prevState, tags: newTags }));
};
const onColorSelect = (colorName: string, i: number) => {
if ((product.colors as string[])?.indexOf(colorName) !== -1) {
(product.colors as string[]).splice((product.colors as string[]).indexOf(colorName), 1);
setProduct((prevState) => ({
...prevState,
colors: (prevState.colors as string[]).filter((color) => color !== colorName)
}));
} else {
setProduct((prevState) => ({
...prevState,
colors: [...(prevState.colors as string[]), colorName]
}));
}
};
const onUpload = (event: FileUploadUploadEvent | FileUploadSelectEvent) => {
setProduct((prevState) => ({ ...prevState, images: event.files }));
};
const onFileUploadClick = () => {
const inputEl = fileUploader.current?.getInput();
inputEl?.click();
};
const emptyTemplate = () => {
return (
<div className="h-15rem overflow-y-auto py-3 border-round" style={{ cursor: 'copy' }}>
<div className="flex flex-column w-full h-full justify-content-center align-items-center" onClick={onFileUploadClick}>
<i className="pi pi-file text-4xl text-primary"></i>
<span className="block font-semibold text-900 text-lg mt-3">Drop or select images</span>
</div>
</div>
);
};
const itemTemplate = (file: object, props: ItemTemplateOptions) => {
const item = file as Demo.Base;
const buttonEl = React.createRef<Button>();
return (
<div className="flex h-15rem overflow-y-auto py-3 border-round" style={{ cursor: 'copy' }} onClick={onFileUploadClick}>
<div className="flex flex-row flex-wrap gap-3 border-round">
<div
className="h-full relative w-7rem h-7rem border-3 border-transparent border-round hover:bg-primary transition-duration-100 cursor-auto"
onMouseEnter={() => onImageMouseOver(buttonEl, item.name)}
onMouseLeave={() => onImageMouseLeave(buttonEl, item.name)}
style={{ padding: '1px' }}
>
<img src={item.objectURL} className="w-full h-full border-round shadow-2" alt={item.name} />
<Button
ref={buttonEl}
id={item.name}
type="button"
icon="pi pi-times"
className="hover:flex text-sm absolute justify-content-center align-items-center cursor-pointer w-2rem h-2rem"
rounded
style={{ top: '-10px', right: '-10px', display: 'none' }}
onClick={(event) => {
event.stopPropagation();
props.onRemove(event);
}}
></Button>
</div>
</div>
</div>
);
};
return (
<div className="card">
<span className="block text-900 font-bold text-xl mb-4">Create Product</span>
<div className="grid grid-nogutter flex-wrap gap-3 p-fluid">
<div className="col-12 lg:col-8">
<div className="grid formgrid">
<div className="col-12 field">
<InputText
type="text"
value={product.name}
onChange={(e) =>
setProduct((prevState) => ({
...prevState,
name: e.target.value
}))
}
placeholder="Product Name"
/>
</div>
<div className="col-12 lg:col-4 field">
<InputText
type="text"
placeholder="Price"
value={product.price?.toString()}
onChange={(e) =>
setProduct((prevState) => ({
...prevState,
price: parseFloat(e.target.value) || undefined
}))
}
/>
</div>
<div className="col-12 lg:col-4 field">
<InputText
type="text"
placeholder="Product Code"
value={product.code}
onChange={(e) =>
setProduct((prevState) => ({
...prevState,
code: e.target.value
}))
}
/>
</div>
<div className="col-12 lg:col-4 field">
<InputText
type="text"
placeholder="Product SKU"
value={product.sku as string}
onChange={(e) =>
setProduct((prevState) => ({
...prevState,
sku: e.target.value
}))
}
/>
</div>
<div className="col-12 field">
<Editor value={product.description} style={{ height: '250px' }}></Editor>
</div>
<div className="col-12 field">
<FileUpload
ref={fileUploader}
name="demo[]"
url="./upload.php"
itemTemplate={itemTemplate}
emptyTemplate={emptyTemplate}
onUpload={onUpload}
customUpload={true}
multiple
onSelect={onUpload}
accept="image/*"
auto
className={'upload-button-hidden border-1 surface-border surface-card border-round'}
/>
</div>
</div>
</div>
<div className="flex-1 w-full lg:w-3 xl:w-4 flex flex-column row-gap-3">
<div className="border-1 surface-border border-round">
<span className="text-900 font-bold block border-bottom-1 surface-border p-3">Publish</span>
<div className="p-3">
<div className="bg-gray-100 py-2 px-3 flex align-items-center border-round">
<span className="text-black-alpha-90 font-bold mr-3">Status:</span>
<span className="text-black-alpha-60 font-semibold">{product.status as string}</span>
<Button type="button" icon="pi pi-fw pi-pencil" className="text-black-alpha-60 ml-auto" text rounded></Button>
</div>
</div>
</div>
<div className="border-1 surface-border border-round">
<span className="text-900 font-bold block border-bottom-1 surface-border p-3">Tags</span>
<div className="p-3 flex flex-wrap gap-1">
{(product.tags as string[])?.map((tag, i) => {
return <Chip key={i} className="mr-2 py-2 px-3 text-900 font-bold surface-card border-1 surface-border" style={{ borderRadius: '20px' }} template={() => chipTemplate(tag)} />;
})}
</div>
</div>
<div className="border-1 surface-border border-round">
<span className="text-900 font-bold block border-bottom-1 surface-border p-3">Category</span>
<div className="p-3">
<Dropdown options={categoryOptions} value={selectedCategory} onChange={(e) => setSelectedCategory(e.value)} placeholder="Select a category"></Dropdown>
</div>
</div>
<div className="border-1 surface-border border-round">
<span className="text-900 font-bold block border-bottom-1 surface-border p-3">Colors</span>
<div className="p-3 flex">
{colorOptions.map((color, i) => {
return (
<div
key={i}
className={classNames('w-2rem h-2rem mr-2 border-1 surface-border border-circle cursor-pointer flex justify-content-center align-items-center', color.background)}
onClick={() => {
onColorSelect(color.name, i);
}}
>
{(product.colors as string[]).includes(color.name) ? <i key={i} className="pi pi-check text-sm text-white z-5"></i> : null}
</div>
);
})}
</div>
</div>
<div className="border-1 surface-border border-round">
<span className="text-900 font-bold block border-bottom-1 surface-border p-3">Stock</span>
<div className="p-3">
<Dropdown options={categoryOptions} value={selectedStock} onChange={(e) => setSelectedStock(e.value)} placeholder="Select stock"></Dropdown>
</div>
</div>
<div className="border-1 surface-border flex justify-content-between align-items-center py-2 px-3 border-round">
<span className="text-900 font-bold p-3">In stock</span>
<InputSwitch
checked={product.inStock as boolean}
onChange={(e) =>
setProduct((prevState) => ({
...prevState,
inStock: e.value as boolean
}))
}
></InputSwitch>
</div>
<div className="flex flex-column sm:flex-row justify-content-between align-items-center gap-3 py-2">
<Button className="flex-1 " security="danger" outlined label="Discard" icon="pi pi-fw pi-trash"></Button>
<Button className="flex-1 border-round" label="Save" icon="pi pi-fw pi-check"></Button>
</div>
</div>
</div>
</div>
);
}
export default NewProduct;