@websolutespa/payload-plugin-bowl
Version:
Bowl PayloadCms plugin of the BOM Repository
430 lines (429 loc) • 18.4 kB
JavaScript
'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