UNPKG

@websolutespa/payload-plugin-bowl

Version:

Bowl PayloadCms plugin of the BOM Repository

430 lines (429 loc) 18.4 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { FormSubmit, Gutter, Link, ListHeader, Popup, ViewDescription, toast, useAuth, useConfig, useDocumentDrawer, useLocale, useTranslation } from '@payloadcms/ui'; import { getRouteResolver } from '@websolutespa/payload-utils'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { options } from '../../options'; import { DataTree } from '../DataTree/DataTree'; import LeaveWithoutSaving from '../LeaveWithoutSaving/LeaveWithoutSaving'; import { ROOT_ITEM, getNewCategoriesFromChanges } from './category.service'; import { CategoryMenu } from './CategoryMenu'; import './CategoryTree.scss'; // https://rct.lukasbach.com/docs/getstarted const USE_BULK_UPDATE = true; function CategoryTreeConflict({ conflicts }) { const { config } = useConfig(); const { routes: { admin: adminRoute } } = config; const summaryConflicts = useMemo(()=>{ const summaryConflicts = []; const getRouteKey = (routes)=>{ return routes.map((x)=>`${x.schema}-${x.page}`).join('_'); }; conflicts.forEach((routes)=>{ const routeKey = getRouteKey(routes); if (summaryConflicts.find((x)=>getRouteKey(x) === routeKey) === undefined) { summaryConflicts.push(routes); } }); return summaryConflicts; }, [ conflicts ]); return /*#__PURE__*/ _jsx("span", { className: "category__conflicts", children: summaryConflicts.length > 0 && /*#__PURE__*/ _jsx(Popup, { button: /*#__PURE__*/ _jsx("span", { className: "category__conflict", children: "conflict detected" }), buttonType: "custom", className: 'category-popup', horizontalAlign: "left", size: "large", verticalAlign: "bottom", children: /*#__PURE__*/ _jsx("div", { className: 'category-popup__content', children: summaryConflicts.map((routes, c)=>/*#__PURE__*/ _jsxs("div", { className: 'category-popup__card', children: [ /*#__PURE__*/ _jsxs("h4", { children: [ /*#__PURE__*/ _jsxs("span", { children: [ "Conflict ", c + 1 ] }), " ", /*#__PURE__*/ _jsx("small", { children: routes.map((r)=>r.schema).join(', ') }) ] }), /*#__PURE__*/ _jsx("ul", { children: routes.map((route, r)=>/*#__PURE__*/ _jsxs("li", { children: [ /*#__PURE__*/ _jsxs(Link, { className: "category-popup__link btn btn--style-secondary", id: 'action-edit', href: `${adminRoute}/collections/${route.schema}/${route.page}`, children: [ /*#__PURE__*/ _jsx("span", { children: route.title }), " ", /*#__PURE__*/ _jsx("small", { children: route.schema }) ] }, `link-${r}-${route.page}`), /*#__PURE__*/ _jsx("p", { className: "category-popup__url", children: route.id }) ] }, `route-${r}-${route.page}`)) }) ] }, `card-${c}`)) }) }) }); } export const CategoryTree = (props)=>{ const { slug } = props; const { config, getEntityConfig } = useConfig(); const collectionConfig = getEntityConfig({ collectionSlug: slug }); const { code: locale } = useLocale(); const { t, i18n } = useTranslation(); const { permissions, user } = useAuth(); const dataTreeRef = useRef(null); const [categories, setCategories] = useState(null); const [items, setItems] = useState(null); const [changes, setChanges] = useState(null); const [dirty, setDirty] = useState(false); const [id, setId] = useState(null); const routeResolver = getRouteResolver(config); useEffect(()=>{ const getData = async ()=>{ try { const search = `?pagination=false&depth=0&locale=${locale}`; const url = routeResolver.api(`/${options.slug.category}${search}`); const httpResponse = await fetch(url, { method: 'GET', credentials: 'include', headers: { 'Content-Type': 'application/json' } }); if (!httpResponse.ok) { throw httpResponse; } const categories = await httpResponse.json(); categories.sort((a, b)=>{ return (a.order || 0) - (b.order || 0); }); // const rootCategory: ICategory | undefined = getRootCategory(categories); const children = categories.filter((x)=>x.category == null || x.category === x.id || x.category === ROOT_ITEM).map((x)=>x.id); const items = {}; items[ROOT_ITEM] = { index: ROOT_ITEM, data: { title: ROOT_ITEM, conflicts: [] }, // children: rootCategory ? [rootCategory.id] : [], children, isFolder: true, canMove: false, canRename: false }; categories.forEach((x)=>{ // const isTopCategory = x.id === firstTopCategory?.id; const children = categories.filter((child)=>child.category === x.id && child.id !== x.id).sort((a, b)=>{ return (a.order || 0) - (b.order || 0); }).map((x)=>x.id); items[x.id] = { index: x.id, data: { title: x.title, conflicts: [] }, children, isFolder: true, canRename: true, canMove: true }; }); setCategories(categories); setItems({ ...items }); // console.log('CategoryTree.getData', 'categories', categories, 'items', items); // console.log('CategoryTree', 'items', items); } catch (error) { console.log('CategoryTree.getData.error', error); } }; getData(); }, [ locale ]); const [DocumentDrawer, , { isDrawerOpen, openDrawer }] = useDocumentDrawer({ collectionSlug: slug, id }); const openDrawerRef = useRef(false); useEffect(()=>{ if (openDrawerRef.current) { openDrawer(); openDrawerRef.current = false; } }, [ openDrawer, id ]); const onChange = (changes)=>{ // console.log('onChange', changes); setChanges(changes); setDirty(true); // console.log(JSON.stringify(items) !== JSON.stringify(changes)); }; const onEdit = (items, treeId)=>{ // console.log('onEdit', items, treeId, items.index); if (typeof items.index === 'string') { openDrawerRef.current = true; setId(items.index); } }; const onSubmit = async ()=>{ // console.log('onSubmit', changes); if (!categories || !changes) { return; } const newCategories = getNewCategoriesFromChanges(categories, changes); const updateCategory = async (item)=>{ // console.log('updateCategory', item); try { const search = `?depth=0&locale=${locale}&fallback-locale=null`; const url = routeResolver.api(`/${slug}/${item.id}${search}`); const httpResponse = await fetch(url, { method: 'PATCH', body: JSON.stringify({ id: item.id, category: item.category, order: item.order }), credentials: 'include', headers: { 'Content-Type': 'application/json' } }); if (!httpResponse.ok) { throw httpResponse; } const response = await httpResponse.json(); // console.log('updateCategory', response); return response; } catch (error) { console.log('CategoryTree.updateCategory.error', error); } }; if (USE_BULK_UPDATE) { try { const records = newCategories.map((x)=>({ action: 'upadate', item: { id: x.id, category: x.category, order: x.order } })); const url = routeResolver.api(`/${slug}/bulk/`); const httpResponse = await fetch(url, { method: 'PATCH', body: JSON.stringify(records), credentials: 'include', headers: { 'Content-Type': 'application/json' } }); if (!httpResponse.ok) { throw httpResponse; } const categories = await httpResponse.json(); categories.sort((a, b)=>(a.order || 0) - (b.order || 0)); // console.log('CategoryTree.onSubmit.success', categories); setDirty(false); toast.success('Updated successfully.'); } catch (error) { toast.error(`There was an error while saving ${slug}.`); console.log('CategoryTree.onSubmit.error', error); } } else { try { const promises = newCategories.map((x)=>()=>updateCategory(x)); const responses = []; for (const promise of promises){ const response = await promise(); responses.push(response); } // console.log('CategoryTree.onSubmit.success', responses); toast.success('Updated successfully.'); } catch (error) { toast.error(`There was an error while saving ${slug}.`); console.log('CategoryTree.onSubmit.error', error); } } }; useEffect(()=>{ if (!categories) { return; } const getPostData = (changes)=>{ const payload = Object.fromEntries(Object.entries(changes).map(([k, v])=>[ k, { index: v.index, children: v.children } ])); return payload; }; const getRoutes = async ()=>{ try { const postData = changes ? getPostData(changes) : undefined; const search = ''; const url = routeResolver.api(`/route/changes${search}`); const httpResponse = await fetch(url, { method: 'POST', body: postData ? JSON.stringify(postData) : undefined, credentials: 'include', headers: { 'Content-Type': 'application/json' } }); if (!httpResponse.ok) { throw httpResponse; } const routes = await httpResponse.json(); return routes; } catch (error) { console.log('CategoryTree.getRoutes.error', error); throw error; } }; getRoutes().then((routes)=>{ const routeMap = new Map(); for (const route of routes){ if (routeMap.has(route.id)) { routeMap.get(route.id).push(route); } else { routeMap.set(route.id, [ route ]); } } const conflictingRouteMap = new Map([ ...routeMap ].filter(([k, v])=>v.length > 1)); // console.log('CategoryTree.changes.getRoutes', routes); // console.log('CategoryTree.changes.routeMap', routeMap); // console.log('CategoryTree.changes.conflictingRouteMap', conflictingRouteMap); if (items) { Object.entries(items).forEach(([k, v])=>{ v.data.conflicts = []; for (const conflict of conflictingRouteMap){ const routes = conflict[1]; if (routes.find((x)=>x.category === k)) { v.data.conflicts.push(routes); } } // console.log(k, v.data.conflicts); v.data.extra = /*#__PURE__*/ _jsx(CategoryTreeConflict, { conflicts: v.data.conflicts }); if (dataTreeRef.current) { dataTreeRef.current.onSetData(v, v.data); } }); // console.log(items); setItems({ ...items }); } }); }, [ changes, categories ]); // const modalKey = `${locale}-localized-field-modal-${path}`; // const isModalOpened = isModalOpen(modalSlug); const onSave = useCallback((args)=>{ setId(args.doc.id); }, []); return /*#__PURE__*/ _jsxs("div", { className: "collection-list", children: [ /*#__PURE__*/ _jsx(CategoryMenu, { collectionSlug: slug }), /*#__PURE__*/ _jsx(Gutter, { children: /*#__PURE__*/ _jsxs("div", { className: "collection-list__wrap", children: [ /*#__PURE__*/ _jsx(ListHeader, { collectionConfig: collectionConfig, Description: /*#__PURE__*/ _jsx("div", { className: 'collection-list__sub-header', children: collectionConfig.admin?.description && /*#__PURE__*/ _jsx(ViewDescription, { collectionSlug: collectionConfig.slug, description: collectionConfig.admin.description }) }), newDocumentURL: "", disableBulkDelete: true, disableBulkEdit: true, hasCreatePermission: false, i18n: i18n, isBulkUploadEnabled: false, smallBreak: true, openBulkUpload: ()=>{}, onBulkUploadSuccess: ()=>{}, viewType: "list" }), items && /*#__PURE__*/ _jsxs(_Fragment, { children: [ /*#__PURE__*/ _jsx(DataTree, { ref: dataTreeRef, items: items, rootItem: ROOT_ITEM, onChange: onChange, onEdit: onEdit }), /*#__PURE__*/ _jsx("div", { className: "collection-list__foot", children: /*#__PURE__*/ _jsx(FormSubmit, { type: "button", disabled: !dirty, onClick: onSubmit, children: t('general:save') }) }), /*#__PURE__*/ _jsx(LeaveWithoutSaving, { modified: dirty }) ] }), id && /*#__PURE__*/ _jsx(DocumentDrawer, { onSave: onSave }) ] }) }) ] }); }; //# sourceMappingURL=CategoryTree.js.map