import { useDispatch, useSelect } from '@wordpress/data' import { __ } from '@wordpress/i18n' import { store, store_name } from '../../../store/backend' import { Form } from '../../components/Form/Form' import { createFormFromModel } from '../../components/Form/lib/createForm' import { createFormMenuSectionsFromModel } from '../../components/Form/utils/utils' import { useSidebar } from '../../components/Sidebar/SidebarContext' import { ServiceNames } from '../../components/WebbaDataTable/cells/ServiceNames/ServiceNames' import { getCellActions, syncConnectedTables } from '../../components/WebbaDataTable/helpers/getCellActions' import { useWbkTable } from '../../components/WebbaDataTable/hooks/useWbkTable' import { Menu } from '../../components/WebbaDataTable/Menu' import { Table } from '../../components/WebbaDataTable/Table' import { generateColumnDefsFromModel } from '../../components/WebbaDataTable/utils' import { serviceCategoriesModel, servicesModel, unitsModel } from './model' import { ServiceDetail } from '../../components/WebbaDataTable/cells/ServiceDetail/ServiceDetail' import { useMemo, useState } from 'react' import { SearchField } from '../../components/Filter/Fields/SearchField/SearchField' import { getFilteredRowModel } from '@tanstack/react-table' import { isForbidden } from '../../utils/errors' import { FailedMessage } from '../../components/FailedMessage/FailedMessage' import { useSettings } from '../../providers/SettingsProvider' import { DurationCell } from '../../components/WebbaDataTable/cells/DurationCell/DurationCell' import { ServiceImageCell } from '../../components/WebbaDataTable/cells/ServiceImageCell/ServiceImageCell' import { formatPrice } from '../../utils/currency' import { SuccessMessage } from '../../components/SuccessMessage/SuccessMessage' import noItemsImage from '../../../../public/images/bookings-empty.png' import { UnitPrices } from '../../components/WebbaDataTable/cells/UnitPrices/UnitPrices' import './ServicesScreen.scss' import { ProFeatuerWrapper } from '../../components/ProFeatuerWrapper/ProFeatuerWrapper' import { ExtrasScreen } from '../Extras/ExtrasScreen' import { UnitNames } from '../../components/WebbaDataTable/cells/UnitNames/UnitNames' export const ServicesScreen = () => { const formService = createFormFromModel(servicesModel) const menuSectionsService = createFormMenuSectionsFromModel({ model: servicesModel, form: formService, modelName: 'services', }) const columnsServiceCategory = generateColumnDefsFromModel( serviceCategoriesModel, { list: { cell: ServiceNames, }, units: { cell: UnitNames, }, }, { id: { cell: ({ cell }) => {cell.row.original.id}, header: __('ID', 'webba-booking-lite'), index: 0, }, } ) const formServiceCategory = createFormFromModel(serviceCategoriesModel) const menuSectionsServiceCategory = createFormMenuSectionsFromModel({ model: serviceCategoriesModel, form: formServiceCategory, modelName: 'service_categories', }) const columnsUnit = generateColumnDefsFromModel(unitsModel, {}, { id: { cell: ({ cell }) => {cell.row.original.id}, header: __('ID', 'webba-booking-lite'), index: 0, }, price: { cell: UnitPrices } }) const formUnit = createFormFromModel(unitsModel) const menuSectionsUnit = createFormMenuSectionsFromModel({ model: unitsModel, form: formUnit, modelName: 'units', }) const { deleteItems, addItem, setToastNotification } = useDispatch(store) const { services, serviceCategories, units, servicesLoading, serviceCategoriesLoading, unitsLoading, } = useSelect( (select) => ({ services: select(store).getItems('services'), serviceCategories: select(store).getItems('service_categories'), units: select(store).getItems('units'), servicesLoading: select(store).getLoadingState('services'), serviceCategoriesLoading: select(store).getLoadingState('service_categories'), unitsLoading: select(store).getLoadingState('units'), }), [] ) const [search, setSearch] = useState('') const [catSearch, setCatSearch] = useState('') const [unitSearch, setUnitSearch] = useState('') const sidebar = useSidebar() const { plugin_url, plan_map } = useSelect( // @ts-ignore (select) => select(store_name).getPreset(), [] ) const settings = useSettings() const columnsService = useMemo( () => generateColumnDefsFromModel( servicesModel, { image: { cell: ServiceImageCell, }, price: { cell: ({ cell }) => (cell.row.original.price > 0 && formatPrice( cell.row.original.price, settings?.price_format, settings?.price_separator, settings?.price_fractional )) || __('Free', 'webba-booking-lite'), }, duration: { cell: DurationCell, }, }, { id: { cell: ({ cell }) => {cell.row.original.id}, header: __('ID', 'webba-booking-lite'), index: 0, }, } ).filter((col) => col.id !== 'email'), [settings] ) const tableService = useWbkTable({ columns: columnsService, data: services, selectable: true, isAdmin: settings?.is_admin, renderMenu: ({ cell }) => { const { onDelete, onDuplicate, onSubmit } = getCellActions({ cell, collectionName: 'services', }) return ( { sidebar.open(
{ await onSubmit(data) setToastNotification({ type: 'success', message: __( 'Changes were saved.', 'webba-booking-lite' ), }) }} onDelete={async () => { await onDelete() sidebar.close() }} onDuplicate={async () => { await onDuplicate() sidebar.close() }} tooltipMode="description" />, { width: 'medium' } ) }} /> ) }, renderExpandableRow: ServiceDetail, getFilteredRowModel: getFilteredRowModel(), state: { globalFilter: search, }, onGlobalFilterChange: setSearch, globalFilterFn: 'includesString', filterFromLeafRows: true, maxLeafRowFilterDepth: 2, }) const tableServiceCategory = useWbkTable({ columns: columnsServiceCategory, data: serviceCategories, selectable: true, isAdmin: settings?.is_admin, renderMenu: ({ cell }) => { const { onDelete, onDuplicate, onSubmit } = getCellActions({ cell, collectionName: 'service_categories', }) return ( { sidebar.open( { await onSubmit(data) setToastNotification({ type: 'success', message: __( 'Changes were saved.', 'webba-booking-lite' ), }) }} onDelete={async () => { await onDelete() sidebar.close() }} onDuplicate={async () => { await onDuplicate() sidebar.close() }} tooltipMode="description" /> ) }} /> ) }, getFilteredRowModel: getFilteredRowModel(), state: { globalFilter: catSearch, }, onGlobalFilterChange: setCatSearch, globalFilterFn: 'includesString', filterFromLeafRows: true, maxLeafRowFilterDepth: 2, }) const tableUnit = useWbkTable({ columns: columnsUnit, data: units, selectable: true, isAdmin: settings?.is_admin, renderMenu: ({ cell }) => { const { onDelete, onDuplicate, onSubmit } = getCellActions({ cell, collectionName: 'units', }) return ( { sidebar.open( { await onSubmit(data) setToastNotification({ type: 'success', message: __( 'Changes were saved.', 'webba-booking-lite' ), }) }} onDelete={async () => { await onDelete() sidebar.close() }} onDuplicate={async () => { await onDuplicate() sidebar.close() }} tooltipMode="description" /> ) }} /> ) }, getFilteredRowModel: getFilteredRowModel(), state: { globalFilter: unitSearch, }, onGlobalFilterChange: setUnitSearch, globalFilterFn: 'includesString', filterFromLeafRows: true, maxLeafRowFilterDepth: 2, }) const onDeleteSelectedService = async () => { const selectedRowsIds = tableService .getSelectedRowModel() .rows.map((row) => row.original.id) if (!selectedRowsIds.length) { return } await deleteItems('services', selectedRowsIds) } const addModelItemServiceCategory = async (data: any) => { try { const response = await addItem('service_categories', data) await syncConnectedTables( response.id, response?.list, 'services', 'categories' ) return response } catch (e) { console.error('failed to add service category', e) } } const onDeleteSelectedServiceCategory = async () => { const selectedRowsIds = tableServiceCategory .getSelectedRowModel() .rows.map((row) => row.original.id) if (!selectedRowsIds.length) { return } await deleteItems('service_categories', selectedRowsIds) } const onDeleteSelectedUnit = async () => { const selectedRowsIds = tableUnit .getSelectedRowModel() .rows.map((row) => row.original.id) if (!selectedRowsIds.length) { return } await deleteItems('units', selectedRowsIds) } const addModelItemService = async (data: any) => { try { const response = await addItem('services', data) await syncConnectedTables( response.id, response?.categories, 'service_categories', 'list' ) const serviceExtras = response?.extras ?? response?.service_extras if (serviceExtras !== undefined) { await syncConnectedTables( response.id, serviceExtras, 'extras', 'services' ) } return response } catch (e) { console.error('failed to add service', e) } } const addModelItemUnit = async (data: any) => { try { const response = await addItem('units', data) const unitExtras = response?.extras ?? response?.unit_extras if (unitExtras !== undefined) { await syncConnectedTables( response.id, unitExtras, 'extras', 'units' ) } return response } catch (e) { console.error('failed to add unit', e) } } const searchField = ( ) const catSearchField = ( ) const unitSearchField = ( ) const unitsRequiredPlans = ['premium', 'pro'] const isUnitsAvailable = plan_map && unitsRequiredPlans.some((plan) => plan_map[plan] === true) return ( <> { sidebar.open( { return await addModelItemService(data) }} />, { width: 'medium' } ) }} noItemsImageUrl={ noItemsImage } search={searchField} isItemsForbidden={isForbidden(services)} />
{!isUnitsAvailable && ( )}
{ sidebar.open( { return await addModelItemUnit(data) }} /> ) }} noItemsImageUrl={ noItemsImage } search={unitSearchField} isItemsForbidden={isForbidden(units)} />
{ sidebar.open( { return await addModelItemServiceCategory(data) }} /> ) }} noItemsImageUrl={ noItemsImage } search={catSearchField} isItemsForbidden={isForbidden(serviceCategories)} /> ) }