Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
160 lines
5.4 KiB
Plaintext
160 lines
5.4 KiB
Plaintext
import { renderTable } from '@payloadcms/ui/rsc';
|
|
import { formatDate } from '@payloadcms/ui/shared';
|
|
import { flattenAllFields } from 'payload';
|
|
import { createSerializableValue } from './createSerializableValue.js';
|
|
import { extractRelationshipDisplayValue } from './extractRelationshipDisplayValue.js';
|
|
import { extractValueOrRelationshipID } from './extractValueOrRelationshipID.js';
|
|
export const handleGroupBy = async ({
|
|
clientCollectionConfig,
|
|
clientConfig,
|
|
collectionConfig,
|
|
collectionSlug,
|
|
columns,
|
|
customCellProps,
|
|
drawerSlug,
|
|
enableRowSelections,
|
|
fieldPermissions,
|
|
query,
|
|
req,
|
|
select,
|
|
trash = false,
|
|
user,
|
|
viewType,
|
|
where: whereWithMergedSearch
|
|
}) => {
|
|
let Table = null;
|
|
let columnState;
|
|
const dataByGroup = {};
|
|
// NOTE: is there a faster/better way to do this?
|
|
const flattenedFields = flattenAllFields({
|
|
fields: collectionConfig.fields
|
|
});
|
|
const groupByFieldPath = query.groupBy.replace(/^-/, '');
|
|
const groupByField = flattenedFields.find(f => f.name === groupByFieldPath);
|
|
// Set up population for relationships
|
|
let populate;
|
|
if (groupByField?.type === 'relationship' && groupByField.relationTo) {
|
|
const relationTo = Array.isArray(groupByField.relationTo) ? groupByField.relationTo : [groupByField.relationTo];
|
|
populate = {};
|
|
relationTo.forEach(rel => {
|
|
const config = clientConfig.collections.find(c => c.slug === rel);
|
|
populate[rel] = {
|
|
[config?.admin?.useAsTitle || 'id']: true
|
|
};
|
|
});
|
|
}
|
|
const distinct = await req.payload.findDistinct({
|
|
collection: collectionSlug,
|
|
depth: 1,
|
|
field: groupByFieldPath,
|
|
limit: query?.limit ? Number(query.limit) : undefined,
|
|
locale: req.locale,
|
|
overrideAccess: false,
|
|
page: query?.page ? Number(query.page) : undefined,
|
|
populate,
|
|
req,
|
|
sort: query?.groupBy,
|
|
trash,
|
|
where: whereWithMergedSearch
|
|
});
|
|
const data = {
|
|
...distinct,
|
|
docs: distinct.values?.map(() => ({})) || [],
|
|
values: undefined
|
|
};
|
|
await Promise.all((distinct.values || []).map(async (distinctValue, i) => {
|
|
const potentiallyPopulatedRelationship = distinctValue[groupByFieldPath];
|
|
// Extract value or relationship ID for database query
|
|
const valueOrRelationshipID = extractValueOrRelationshipID(potentiallyPopulatedRelationship);
|
|
const groupData = await req.payload.find({
|
|
collection: collectionSlug,
|
|
depth: 0,
|
|
draft: true,
|
|
fallbackLocale: false,
|
|
includeLockStatus: true,
|
|
limit: query?.queryByGroup?.[valueOrRelationshipID]?.limit ? Number(query.queryByGroup[valueOrRelationshipID].limit) : undefined,
|
|
locale: req.locale,
|
|
overrideAccess: false,
|
|
page: query?.queryByGroup?.[valueOrRelationshipID]?.page ? Number(query.queryByGroup[valueOrRelationshipID].page) : undefined,
|
|
req,
|
|
// Note: if we wanted to enable table-by-table sorting, we could use this:
|
|
// sort: query?.queryByGroup?.[valueOrRelationshipID]?.sort,
|
|
select,
|
|
sort: query?.sort,
|
|
trash,
|
|
user,
|
|
where: {
|
|
...(whereWithMergedSearch || {}),
|
|
[groupByFieldPath]: {
|
|
equals: valueOrRelationshipID
|
|
}
|
|
}
|
|
});
|
|
// Extract heading
|
|
let heading;
|
|
if (potentiallyPopulatedRelationship === null) {
|
|
heading = req.i18n.t('general:noValue');
|
|
} else if (groupByField?.type === 'relationship') {
|
|
const relationshipConfig = Array.isArray(groupByField.relationTo) ? undefined : clientConfig.collections.find(c => c.slug === groupByField.relationTo);
|
|
heading = extractRelationshipDisplayValue(potentiallyPopulatedRelationship, clientConfig, relationshipConfig);
|
|
} else if (groupByField?.type === 'date') {
|
|
heading = formatDate({
|
|
date: String(valueOrRelationshipID),
|
|
i18n: req.i18n,
|
|
pattern: clientConfig.admin.dateFormat
|
|
});
|
|
} else if (groupByField?.type === 'checkbox') {
|
|
if (valueOrRelationshipID === true) {
|
|
heading = req.i18n.t('general:true');
|
|
}
|
|
if (valueOrRelationshipID === false) {
|
|
heading = req.i18n.t('general:false');
|
|
}
|
|
} else {
|
|
heading = String(valueOrRelationshipID);
|
|
}
|
|
// Create serializable value for client
|
|
const serializableValue = createSerializableValue(valueOrRelationshipID);
|
|
if (groupData.docs && groupData.docs.length > 0) {
|
|
const {
|
|
columnState: newColumnState,
|
|
Table: NewTable
|
|
} = renderTable({
|
|
clientCollectionConfig,
|
|
collectionConfig,
|
|
columns,
|
|
customCellProps,
|
|
data: groupData,
|
|
drawerSlug,
|
|
enableRowSelections,
|
|
fieldPermissions,
|
|
groupByFieldPath,
|
|
groupByValue: serializableValue,
|
|
heading: heading || req.i18n.t('general:noValue'),
|
|
i18n: req.i18n,
|
|
key: `table-${serializableValue}`,
|
|
orderableFieldName: collectionConfig.orderable === true ? '_order' : undefined,
|
|
payload: req.payload,
|
|
query,
|
|
useAsTitle: collectionConfig.admin.useAsTitle,
|
|
viewType
|
|
});
|
|
// Only need to set `columnState` once, using the first table's column state
|
|
// This will avoid needing to generate column state explicitly for root context that wraps all tables
|
|
if (!columnState) {
|
|
columnState = newColumnState;
|
|
}
|
|
if (!Table) {
|
|
Table = [];
|
|
}
|
|
dataByGroup[serializableValue] = groupData;
|
|
Table[i] = NewTable;
|
|
}
|
|
}));
|
|
return {
|
|
columnState,
|
|
data,
|
|
Table
|
|
};
|
|
};
|
|
//# sourceMappingURL=handleGroupBy.js.map |