new-marketplace
405 строк · 17.6 Кб
1'use client';
2import { CustomerService } from '../../../demo/service/CustomerService';
3import { ProductService } from '../../../demo/service/ProductService';
4import { FilterMatchMode, FilterOperator } from 'primereact/api';
5import { Button } from 'primereact/button';
6import { Calendar } from 'primereact/calendar';
7import { Column, ColumnFilterApplyTemplateOptions, ColumnFilterClearTemplateOptions, ColumnFilterElementTemplateOptions } from 'primereact/column';
8import { DataTable, DataTableExpandedRows, DataTableFilterMeta } from 'primereact/datatable';
9import { Dropdown } from 'primereact/dropdown';
10import { InputNumber } from 'primereact/inputnumber';
11import { InputText } from 'primereact/inputtext';
12import { MultiSelect } from 'primereact/multiselect';
13import { ProgressBar } from 'primereact/progressbar';
14import { Rating } from 'primereact/rating';
15import { Slider } from 'primereact/slider';
16import { ToggleButton } from 'primereact/togglebutton';
17import { TriStateCheckbox } from 'primereact/tristatecheckbox';
18import { classNames } from 'primereact/utils';
19import React, { useEffect, useState } from 'react';
20import type { Demo } from '@/types';
21
22const TableDemo = () => {
23const [customers1, setCustomers1] = useState<Demo.Customer[]>([]);
24const [customers2, setCustomers2] = useState<Demo.Customer[]>([]);
25const [customers3, setCustomers3] = useState<Demo.Customer[]>([]);
26const [filters1, setFilters1] = useState<DataTableFilterMeta>({});
27const [loading1, setLoading1] = useState(true);
28const [loading2, setLoading2] = useState(true);
29const [idFrozen, setIdFrozen] = useState(false);
30const [products, setProducts] = useState<Demo.Product[]>([]);
31const [globalFilterValue1, setGlobalFilterValue1] = useState('');
32const [expandedRows, setExpandedRows] = useState<any[] | DataTableExpandedRows>([]);
33const [allExpanded, setAllExpanded] = useState(false);
34
35const representatives = [
36{ name: 'Amy Elsner', image: 'amyelsner.png' },
37{ name: 'Anna Fali', image: 'annafali.png' },
38{ name: 'Asiya Javayant', image: 'asiyajavayant.png' },
39{ name: 'Bernardo Dominic', image: 'bernardodominic.png' },
40{ name: 'Elwin Sharvill', image: 'elwinsharvill.png' },
41{ name: 'Ioni Bowcher', image: 'ionibowcher.png' },
42{ name: 'Ivan Magalhaes', image: 'ivanmagalhaes.png' },
43{ name: 'Onyama Limba', image: 'onyamalimba.png' },
44{ name: 'Stephen Shaw', image: 'stephenshaw.png' },
45{ name: 'XuXue Feng', image: 'xuxuefeng.png' }
46];
47
48const statuses = ['unqualified', 'qualified', 'new', 'negotiation', 'renewal', 'proposal'];
49
50const clearFilter1 = () => {
51initFilters1();
52};
53
54const onGlobalFilterChange1 = (e: React.ChangeEvent<HTMLInputElement>) => {
55const value = e.target.value;
56let _filters1 = { ...filters1 };
57(_filters1['global'] as any).value = value;
58
59setFilters1(_filters1);
60setGlobalFilterValue1(value);
61};
62
63const renderHeader1 = () => {
64return (
65<div className="flex justify-content-between">
66<Button type="button" icon="pi pi-filter-slash" label="Очистить" outlined onClick={clearFilter1} />
67<span className="p-input-icon-left">
68<i className="pi pi-search" />
69<InputText value={globalFilterValue1} onChange={onGlobalFilterChange1} placeholder="Поиск по ключевому слову" />
70</span>
71</div>
72);
73};
74
75useEffect(() => {
76setLoading2(true);
77
78CustomerService.getCustomersLarge().then((data) => {
79setCustomers1(getCustomers(data));
80setLoading1(false);
81});
82CustomerService.getCustomersLarge().then((data) => {
83setCustomers2(getCustomers(data));
84setLoading2(false);
85});
86CustomerService.getCustomersMedium().then((data) => setCustomers3(data));
87ProductService.getProductsWithOrdersSmall().then((data) => setProducts(data));
88
89initFilters1();
90}, []);
91
92const balanceTemplate = (rowData: Demo.Customer) => {
93return (
94<div>
95<span className="text-bold">{formatCurrency(rowData.balance as number)}</span>
96</div>
97);
98};
99
100const getCustomers = (data: Demo.Customer[]) => {
101return [...(data || [])].map((d) => {
102d.date = new Date(d.date);
103return d;
104});
105};
106
107const formatDate = (value: Date) => {
108return value.toLocaleDateString('en-US', {
109day: '2-digit',
110month: '2-digit',
111year: 'numeric'
112});
113};
114
115const formatCurrency = (value: number) => {
116return value.toLocaleString('en-US', {
117style: 'currency',
118currency: 'USD'
119});
120};
121
122const initFilters1 = () => {
123setFilters1({
124global: { value: null, matchMode: FilterMatchMode.CONTAINS },
125name: {
126operator: FilterOperator.AND,
127constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
128},
129'country.name': {
130operator: FilterOperator.AND,
131constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
132},
133representative: { value: null, matchMode: FilterMatchMode.IN },
134date: {
135operator: FilterOperator.AND,
136constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
137},
138balance: {
139operator: FilterOperator.AND,
140constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }]
141},
142status: {
143operator: FilterOperator.OR,
144constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }]
145},
146activity: { value: null, matchMode: FilterMatchMode.BETWEEN },
147verified: { value: null, matchMode: FilterMatchMode.EQUALS }
148});
149setGlobalFilterValue1('');
150};
151
152const countryBodyTemplate = (rowData: Demo.Customer) => {
153return (
154<React.Fragment>
155<img alt="flag" src={`/demo/images/flag/flag_placeholder.png`} className={`flag flag-${rowData.country.code}`} width={30} />
156<span style={{ marginLeft: '.5em', verticalAlign: 'middle' }}>{rowData.country.name}</span>
157</React.Fragment>
158);
159};
160
161const filterClearTemplate = (options: ColumnFilterClearTemplateOptions) => {
162return <Button type="button" icon="pi pi-times" onClick={options.filterClearCallback} severity="secondary"></Button>;
163};
164
165const filterApplyTemplate = (options: ColumnFilterApplyTemplateOptions) => {
166return <Button type="button" icon="pi pi-check" onClick={options.filterApplyCallback} severity="success"></Button>;
167};
168
169const representativeBodyTemplate = (rowData: Demo.Customer) => {
170const representative = rowData.representative;
171return (
172<React.Fragment>
173<img
174alt={representative.name}
175src={`/demo/images/avatar/${representative.image}`}
176onError={(e) => ((e.target as HTMLImageElement).src = 'https://www.primefaces.org/wp-content/uploads/2020/05/placeholder.png')}
177width={32}
178style={{ verticalAlign: 'middle' }}
179/>
180<span style={{ marginLeft: '.5em', verticalAlign: 'middle' }}>{representative.name}</span>
181</React.Fragment>
182);
183};
184
185const representativeFilterTemplate = (options: ColumnFilterElementTemplateOptions) => {
186return (
187<>
188<div className="mb-3 text-bold">Agent Picker</div>
189<MultiSelect value={options.value} options={representatives} itemTemplate={representativesItemTemplate} onChange={(e) => options.filterCallback(e.value)} optionLabel="name" placeholder="Any" className="p-column-filter" />
190</>
191);
192};
193
194const representativesItemTemplate = (option: any) => {
195return (
196<div className="p-multiselect-representative-option">
197<img alt={option.name} src={`/demo/images/avatar/${option.image}`} width={32} style={{ verticalAlign: 'middle' }} />
198<span style={{ marginLeft: '.5em', verticalAlign: 'middle' }}>{option.name}</span>
199</div>
200);
201};
202
203const dateBodyTemplate = (rowData: Demo.Customer) => {
204return formatDate(rowData.date);
205};
206
207const dateFilterTemplate = (options: ColumnFilterElementTemplateOptions) => {
208return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />;
209};
210
211const balanceBodyTemplate = (rowData: Demo.Customer) => {
212return formatCurrency(rowData.balance as number);
213};
214
215const balanceFilterTemplate = (options: ColumnFilterElementTemplateOptions) => {
216return <InputNumber value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} mode="currency" currency="USD" locale="en-US" />;
217};
218
219const statusBodyTemplate = (rowData: Demo.Customer) => {
220return <span className={`customer-badge status-${rowData.status}`}>{rowData.status}</span>;
221};
222
223const statusFilterTemplate = (options: ColumnFilterElementTemplateOptions) => {
224return <Dropdown value={options.value} options={statuses} onChange={(e) => options.filterCallback(e.value, options.index)} itemTemplate={statusItemTemplate} placeholder="Select a Status" className="p-column-filter" showClear />;
225};
226
227const statusItemTemplate = (option: any) => {
228return <span className={`customer-badge status-${option}`}>{option}</span>;
229};
230
231const activityBodyTemplate = (rowData: Demo.Customer) => {
232return <ProgressBar value={rowData.activity} showValue={false} style={{ height: '.5rem' }}></ProgressBar>;
233};
234
235const activityFilterTemplate = (options: ColumnFilterElementTemplateOptions) => {
236return (
237<React.Fragment>
238<Slider value={options.value} onChange={(e) => options.filterCallback(e.value)} range className="m-3"></Slider>
239<div className="flex align-items-center justify-content-between px-2">
240<span>{options.value ? options.value[0] : 0}</span>
241<span>{options.value ? options.value[1] : 100}</span>
242</div>
243</React.Fragment>
244);
245};
246
247const verifiedBodyTemplate = (rowData: Demo.Customer) => {
248return (
249<i
250className={classNames('pi', {
251'text-green-500 pi-check-circle': rowData.verified,
252'text-pink-500 pi-times-circle': !rowData.verified
253})}
254></i>
255);
256};
257
258const verifiedFilterTemplate = (options: ColumnFilterElementTemplateOptions) => {
259return <TriStateCheckbox value={options.value} onChange={(e) => options.filterCallback(e.value)} />;
260};
261
262const toggleAll = () => {
263if (allExpanded) collapseAll();
264else expandAll();
265};
266
267const expandAll = () => {
268let _expandedRows = {} as { [key: string]: boolean };
269products.forEach((p) => (_expandedRows[`${p.id}`] = true));
270
271setExpandedRows(_expandedRows);
272setAllExpanded(true);
273};
274
275const collapseAll = () => {
276setExpandedRows([]);
277setAllExpanded(false);
278};
279
280const amountBodyTemplate = (rowData: Demo.Customer) => {
281return formatCurrency(rowData.amount as number);
282};
283
284const statusOrderBodyTemplate = (rowData: Demo.Customer) => {
285return <span className={`order-badge order-${rowData.status?.toLowerCase()}`}>{rowData.status}</span>;
286};
287
288const searchBodyTemplate = () => {
289return <Button icon="pi pi-search" />;
290};
291
292const imageBodyTemplate = (rowData: Demo.Product) => {
293return <img src={`/demo/images/product/${rowData.image}`} onError={(e) => ((e.target as HTMLImageElement).src = 'https://www.primefaces.org/wp-content/uploads/2020/05/placeholder.png')} alt={rowData.image} className="shadow-2" width={100} />;
294};
295
296const priceBodyTemplate = (rowData: Demo.Product) => {
297return formatCurrency(rowData.price as number);
298};
299
300const ratingBodyTemplate = (rowData: Demo.Product) => {
301return <Rating value={rowData.rating} readOnly cancel={false} />;
302};
303
304const statusBodyTemplate2 = (rowData: Demo.Product) => {
305return <span className={`product-badge status-${rowData.inventoryStatus?.toLowerCase()}`}>{rowData.inventoryStatus}</span>;
306};
307
308const rowExpansionTemplate = (data: Demo.Product) => {
309return (
310<div className="orders-subtable">
311<h5>Orders for {data.name}</h5>
312<DataTable value={data.orders} responsiveLayout="scroll">
313<Column field="id" header="Id" sortable></Column>
314<Column field="customer" header="Покупатель" sortable></Column>
315<Column field="date" header="Дата" sortable></Column>
316<Column field="amount" header="Количество" body={amountBodyTemplate} sortable></Column>
317<Column field="status" header="Статус" body={statusOrderBodyTemplate} sortable></Column>
318<Column headerStyle={{ width: '4rem' }} body={searchBodyTemplate}></Column>
319</DataTable>
320</div>
321);
322};
323
324const header = <Button icon={allExpanded ? 'pi pi-minus' : 'pi pi-plus'} label={allExpanded ? 'Свернуть все' : 'Развернуть все'} onClick={toggleAll} className="w-11rem" />;
325
326const headerTemplate = (data: Demo.Customer) => {
327return (
328<React.Fragment>
329<img alt={data.representative.name} src={`/demo/images/avatar/${data.representative.image}`} width="32" style={{ verticalAlign: 'middle' }} />
330<span className="font-bold ml-2">{data.representative.name}</span>
331</React.Fragment>
332);
333};
334
335const footerTemplate = (data: Demo.Customer) => {
336return (
337<React.Fragment>
338<td colSpan={4} style={{ textAlign: 'right' }} className="text-bold pr-6">
339Total Customers
340</td>
341<td>{calculateCustomerTotal(data.representative.name)}</td>
342</React.Fragment>
343);
344};
345
346const calculateCustomerTotal = (name: string) => {
347let total = 0;
348
349if (customers3) {
350for (let customer of customers3) {
351if (customer.representative.name === name) {
352total++;
353}
354}
355}
356
357return total;
358};
359
360const header1 = renderHeader1();
361
362return (
363<div className="grid">
364<div className="col-12">
365<div className="card">
366<h5>Покупатели</h5>
367<DataTable
368value={customers1}
369paginator
370className="p-datatable-gridlines"
371showGridlines
372rows={10}
373dataKey="id"
374filters={filters1}
375filterDisplay="menu"
376loading={loading1}
377responsiveLayout="scroll"
378emptyMessage="Покупатель не найден."
379header={header1}
380>
381<Column field="name" header="Имя" filter filterPlaceholder="Искать по имени" style={{ minWidth: '12rem' }} />
382<Column header="Страна" filterField="country.name" style={{ minWidth: '12rem' }} body={countryBodyTemplate} filter filterPlaceholder="Поиск по стране" filterClear={filterClearTemplate} filterApply={filterApplyTemplate} />
383<Column
384header="Посредник"
385filterField="representative"
386showFilterMatchModes={false}
387filterMenuStyle={{ width: '14rem' }}
388style={{ minWidth: '14rem' }}
389body={representativeBodyTemplate}
390filter
391filterElement={representativeFilterTemplate}
392/>
393<Column header="Дата" filterField="date" dataType="date" style={{ minWidth: '10rem' }} body={dateBodyTemplate} filter filterElement={dateFilterTemplate} />
394<Column header="Баланс" filterField="balance" dataType="numeric" style={{ minWidth: '10rem' }} body={balanceBodyTemplate} filter filterElement={balanceFilterTemplate} />
395<Column field="status" header="Статус" filterMenuStyle={{ width: '14rem' }} style={{ minWidth: '12rem' }} body={statusBodyTemplate} filter filterElement={statusFilterTemplate} />
396<Column field="activity" header="Активности" showFilterMatchModes={false} style={{ minWidth: '12rem' }} body={activityBodyTemplate} filter filterElement={activityFilterTemplate} />
397<Column field="verified" header="Проверен" dataType="boolean" bodyClassName="text-center" style={{ minWidth: '8rem' }} body={verifiedBodyTemplate} filter filterElement={verifiedFilterTemplate} />
398</DataTable>
399</div>
400</div>
401</div>
402);
403};
404
405export default TableDemo;
406