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 |