SHOW:
|
|
- or go back to the newest paste.
| 1 | - | // pages/cms/products/CMSProductsPage.tsx |
| 1 | + | // pages/cms/products/components/CMSProductForm.tsx |
| 2 | - | import { useEffect } from 'react';
|
| 2 | + | import clsx from 'clsx'; |
| 3 | - | import { useProductsService } from '@/services/products';
|
| 3 | + | import { ChangeEvent, FormEvent, useEffect, useState } from 'react';
|
| 4 | - | import { ServerError, Spinner } from '@/shared/';
|
| 4 | + | import { Product } from '@/model/product';
|
| 5 | - | import { CMSProductForm } from './components/CMSProductForm';
|
| 5 | + | |
| 6 | - | import { CMSProductsList } from './components/CMSProductsList';
|
| 6 | + | export interface CMSProductFormProps {
|
| 7 | activeItem: Partial<Product> | null; | |
| 8 | - | export function CMSProductsPage() {
|
| 8 | + | onClose: () => void; |
| 9 | - | const { state, actions } = useProductsService();
|
| 9 | + | onAdd: (product: Partial<Product>) => void; |
| 10 | onEdit: (product: Partial<Product>) => void; | |
| 11 | } | |
| 12 | - | actions.getProducts() |
| 12 | + | |
| 13 | - | }, []) |
| 13 | + | const initialState: Partial<Product> = {
|
| 14 | name: '', cost: 0, description: '' | |
| 15 | } | |
| 16 | - | <div> |
| 16 | + | |
| 17 | - | <h1 className="title">CMS</h1> |
| 17 | + | export function CMSProductForm(props: CMSProductFormProps) {
|
| 18 | const [formData, setFormData] = useState(initialState); | |
| 19 | - | {state.pending && <Spinner />}
|
| 19 | + | |
| 20 | - | {state.error && <ServerError message={state.error} />}
|
| 20 | + | |
| 21 | if (props.activeItem?.id) {
| |
| 22 | - | <CMSProductForm |
| 22 | + | setFormData({ ...props.activeItem })
|
| 23 | - | activeItem={state.activeItem}
|
| 23 | + | } else {
|
| 24 | - | onClose={actions.resetActiveItem}
|
| 24 | + | setFormData(initialState) |
| 25 | - | onAdd={actions.addProduct}
|
| 25 | + | } |
| 26 | - | onEdit={actions.editProduct}
|
| 26 | + | }, [props.activeItem]) |
| 27 | - | /> |
| 27 | + | |
| 28 | function changeHandler(e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
| |
| 29 | - | <CMSProductsList |
| 29 | + | const value = e.currentTarget.value; |
| 30 | - | items={state.products}
|
| 30 | + | const name = e.currentTarget.name; |
| 31 | - | activeItem={state.activeItem}
|
| 31 | + | setFormData(s => ({ ...s, [name]: value }))
|
| 32 | - | onEditItem={actions.setActiveItem}
|
| 32 | + | } |
| 33 | - | onDeleteItem={actions.deleteProduct}
|
| 33 | + | |
| 34 | - | /> |
| 34 | + | function saveHandler(e: FormEvent<HTMLFormElement>) {
|
| 35 | e.preventDefault(); | |
| 36 | - | <button |
| 36 | + | if (props.activeItem?.id) {
|
| 37 | - | className="btn primary" |
| 37 | + | props.onEdit(formData); |
| 38 | - | onClick={() => actions.setActiveItem({})}
|
| 38 | + | } else {
|
| 39 | - | > |
| 39 | + | props.onAdd(formData); |
| 40 | - | ADD NEW |
| 40 | + | } |
| 41 | - | </button> |
| 41 | + | } |
| 42 | ||
| 43 | const isNameValid = formData.name?.length; | |
| 44 | const isCostValid = formData.cost! > 0; | |
| 45 | const isDescValid = formData.description?.length; | |
| 46 | ||
| 47 | const isValid = isNameValid && isCostValid && isDescValid; | |
| 48 | ||
| 49 | return ( | |
| 50 | <div className={clsx(
| |
| 51 | 'fixed bg-slate-200 z-10 text-black top-0 w-96 h-full transition-all', | |
| 52 | {'-right-96': !props.activeItem, 'right-0': props.activeItem}
| |
| 53 | )}> | |
| 54 | ||
| 55 | <form onSubmit={saveHandler}>
| |
| 56 | <div className="flex justify-around h-16"> | |
| 57 | <button | |
| 58 | className="text-white w-1/2 bg-green-500 hover:bg-green-600 disabled:opacity-30" | |
| 59 | disabled={!isValid}
| |
| 60 | type="submit" | |
| 61 | >SAVE</button> | |
| 62 | <button | |
| 63 | className="text-white w-1/2 bg-slate-500 hover:bg-slate-600" | |
| 64 | onClick={props.onClose}
| |
| 65 | type="button" | |
| 66 | >CLOSE</button> | |
| 67 | </div> | |
| 68 | ||
| 69 | <div className="flex flex-col gap-3 mx-3 mt-16"> | |
| 70 | Product Name: | |
| 71 | <input | |
| 72 | className={clsx({ 'error': !isNameValid})}
| |
| 73 | type="text" value={formData?.name} name="name" onChange={changeHandler}
| |
| 74 | /> | |
| 75 | ||
| 76 | Product Cost: | |
| 77 | <input | |
| 78 | className={clsx({ 'error': !isCostValid})}
| |
| 79 | type="number" value={formData?.cost} name="cost" onChange={changeHandler}
| |
| 80 | /> | |
| 81 | ||
| 82 | Description | |
| 83 | <textarea | |
| 84 | className={clsx({ 'error': !isDescValid})}
| |
| 85 | value={formData.description} name="description" onChange={changeHandler}
| |
| 86 | ></textarea> | |
| 87 | ||
| 88 | </div> | |
| 89 | </form> | |
| 90 | ||
| 91 | ||
| 92 | </div> | |
| 93 | ) | |
| 94 | } | |
| 95 |