UNPKG

@premieroctet/next-admin

Version:

Next-Admin provides a customizable and turnkey admin dashboard for applications built with Next.js and powered by the Prisma ORM. It aims to simplify the development process by providing a turnkey admin system that can be easily integrated into your proje

571 lines (570 loc) 26.1 kB
"use strict"; var __webpack_require__ = {}; (()=>{ __webpack_require__.n = (module)=>{ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module; __webpack_require__.d(getter, { a: getter }); return getter; }; })(); (()=>{ __webpack_require__.d = (exports1, definition)=>{ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, { enumerable: true, get: definition[key] }); }; })(); (()=>{ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop); })(); (()=>{ __webpack_require__.r = (exports1)=>{ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, { value: 'Module' }); Object.defineProperty(exports1, '__esModule', { value: true }); }; })(); var __webpack_exports__ = {}; __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, { getRawData: ()=>getRawData, mapDataList: ()=>mapDataList, getMappedDataList: ()=>getMappedDataList, createWherePredicate: ()=>createWherePredicate, mapModelFilters: ()=>mapModelFilters, optionsFromResource: ()=>optionsFromResource, selectPayloadForModel: ()=>selectPayloadForModel, getDataItem: ()=>getDataItem }); const external_lodash_clonedeep_namespaceObject = require("lodash.clonedeep"); var external_lodash_clonedeep_default = /*#__PURE__*/ __webpack_require__.n(external_lodash_clonedeep_namespaceObject); const external_config_js_namespaceObject = require("../config.js"); const external_advancedSearch_js_namespaceObject = require("./advancedSearch.js"); const external_globals_js_namespaceObject = require("./globals.js"); const external_jsonSchema_js_namespaceObject = require("./jsonSchema.js"); const external_prisma_runtime_js_namespaceObject = require("./prisma-runtime.js"); const external_server_js_namespaceObject = require("./server.js"); const external_tools_js_namespaceObject = require("./tools.js"); const createNestedWherePredicate = ({ field, options, search, searchOptions }, acc = {})=>{ const resource = (0, external_globals_js_namespaceObject.getSchema)().definitions[field.type]; const resourceProperties = resource.properties; acc[field.name] = { OR: searchOptions?.map((searchOption)=>{ const [_, subFieldName, ...rest] = searchOption.toString().split("."); const subField = resourceProperties[subFieldName]; if (!subField) return null; if (subField.__nextadmin?.kind === "scalar") { if (subField.__nextadmin?.isList) { let searchTerm = search; if (subField.__nextadmin?.type !== "String" && !isNaN(Number(search))) searchTerm = Number(search); return { has: searchTerm }; } if (subField.__nextadmin?.type === "String") { const mode = external_prisma_runtime_js_namespaceObject.Prisma?.QueryMode ? { mode: external_prisma_runtime_js_namespaceObject.Prisma.QueryMode.insensitive } : {}; return { [subFieldName]: { contains: search, ...mode } }; } if (subField.__nextadmin?.type === "Int" && !isNaN(Number(search))) return { [subFieldName]: Number(search) }; } else if (subField.__nextadmin?.kind === "object") { const predicate = createNestedWherePredicate({ field: { ...subField.__nextadmin, name: subFieldName }, options, search, searchOptions: [ [ subFieldName, ...rest ].join(".") ] }); if (subField.__nextadmin?.isList) predicate[subFieldName] = { some: predicate[subFieldName] }; return predicate; } }).filter(Boolean) }; return acc; }; const createWherePredicate = ({ resource, options, search, otherFilters, advancedSearch })=>{ let fieldsFiltered = getFieldsFiltered(resource, options); const searchFilter = search ? { OR: fieldsFiltered?.filter(([, field])=>field?.__nextadmin?.kind === "scalar" || field?.__nextadmin?.kind === "enum" || field?.__nextadmin?.kind === "object").map(([name, field])=>{ const fieldNextAdmin = field?.__nextadmin; if (fieldNextAdmin?.kind === "object") return createNestedWherePredicate({ field: { ...fieldNextAdmin, name }, options, search, searchOptions: options?.model?.[resource]?.list?.search?.filter((searchOption)=>searchOption?.toString().startsWith(name)) }); if (fieldNextAdmin?.kind === "enum" && fieldNextAdmin?.enum) { const enumDefinition = (0, external_jsonSchema_js_namespaceObject.getDefinitionFromRef)((0, external_globals_js_namespaceObject.getSchema)(), fieldNextAdmin.enum.$ref); const enumValueForSearchTerm = (0, external_server_js_namespaceObject.enumValueForEnumType)(enumDefinition, search); if (enumValueForSearchTerm && enumDefinition?.enum) return { [name]: enumDefinition.enum.find((val)=>val === enumValueForSearchTerm) }; } if (fieldNextAdmin?.kind === "scalar" && fieldNextAdmin?.isList) { if (fieldNextAdmin?.type !== "String" && Number.isNaN(Number(search))) return null; return { [name]: { has: fieldNextAdmin?.type === "String" ? search : Number(search) } }; } if (fieldNextAdmin?.type === "String") { const mode = external_prisma_runtime_js_namespaceObject.Prisma?.QueryMode ? { mode: external_prisma_runtime_js_namespaceObject.Prisma.QueryMode.insensitive } : {}; return { [name]: { contains: search, ...mode } }; } if (fieldNextAdmin?.type === "Int" && !isNaN(Number(search))) return { [name]: Number(search) }; return null; }).filter(Boolean) } : {}; const externalFilters = otherFilters ?? []; const advancedSearchFilter = advancedSearch ? getWherePredicateFromQueryParams(advancedSearch) : null; return { AND: [ ...externalFilters, searchFilter, advancedSearchFilter ].filter(Boolean) }; }; const getFieldsFiltered = (resource, options)=>{ const model = (0, external_globals_js_namespaceObject.getSchema)().definitions[resource]; const modelProperties = model.properties; let fieldsFiltered = Object.entries(modelProperties).filter(([, field])=>field.__nextadmin?.kind === "scalar"); const list = options?.model?.[resource]?.list; if (list) fieldsFiltered = list?.search ? Object.entries(modelProperties).filter(([name])=>list.search?.some((search)=>{ const searchNameSplit = search?.toString().split("."); return searchNameSplit?.[0] === name; })) : fieldsFiltered; return fieldsFiltered; }; const getWherePredicateFromQueryParams = (query)=>(0, external_advancedSearch_js_namespaceObject.validateQuery)(query); const mapModelFilters = async (filters)=>{ if (!filters) return []; const newFilters = await Promise.all(filters.map(async (filter)=>{ if ("function" == typeof filter) { const asyncFilters = await filter(); return asyncFilters; } return filter; })); return newFilters.flat().filter(Boolean); }; const preparePrismaListRequest = async (resource, searchParams, options, skipFilters = false)=>{ const idProperty = (0, external_server_js_namespaceObject.getModelIdProperty)(resource); const model = (0, external_globals_js_namespaceObject.getSchema)().definitions[resource]; const modelProperties = model.properties; const search = searchParams.get("search") || ""; const advancedSearch = searchParams.get("q") || ""; let filtersParams = []; try { filtersParams = skipFilters ? [] : JSON.parse(searchParams.get("filters")); } catch {} const page = Number(searchParams.get("page")) || 1; const itemsPerPage = Number(searchParams.get("itemsPerPage")) || options?.model?.[resource]?.list?.defaultListSize || external_config_js_namespaceObject.ITEMS_PER_PAGE; const fieldSort = options?.model?.[resource]?.list?.defaultSort; const filters = await mapModelFilters(options?.model?.[resource]?.list?.filters); const fieldFilters = filters?.filter((filter)=>{ if (Array.isArray(filtersParams)) return filtersParams.includes(filter.name); return filter.active; })?.map((filter)=>filter.value); let orderBy = {}; if (options?.model?.[resource]?.list?.orderField) orderBy[options?.model?.[resource]?.list?.orderField] = "asc"; else { const createSortObject = (field, direction)=>{ const modelFieldSortParam = modelProperties[field]; const modelFieldNextAdminData = modelFieldSortParam?.__nextadmin; if (direction in external_prisma_runtime_js_namespaceObject.Prisma.SortOrder) { if (field in external_prisma_runtime_js_namespaceObject.Prisma[`${(0, external_tools_js_namespaceObject.capitalize)(resource)}ScalarFieldEnum`]) return { [field]: direction }; else if (modelFieldNextAdminData?.kind === "object") if (modelFieldNextAdminData.isList) return { [field]: { _count: direction } }; else { const modelFieldSortProperty = options?.model?.[resource]?.list?.fields?.[field]?.sortBy; const resourceSortByField = modelFieldSortProperty ?? (0, external_server_js_namespaceObject.getModelIdProperty)(modelFieldNextAdminData.type); return { [field]: { [resourceSortByField]: direction } }; } } return null; }; const sortParam = searchParams.get("sortColumn"); const sortDirection = searchParams.get("sortDirection"); if (sortParam && sortDirection) { const sortObject = createSortObject(sortParam, sortDirection); if (sortObject) orderBy = sortObject; } else if (fieldSort) if (Array.isArray(fieldSort)) { const sortObjects = fieldSort.map((sort)=>createSortObject(sort.field, sort.direction || "asc")).filter(Boolean); if (sortObjects.length > 0) orderBy = sortObjects; } else { const sortObject = createSortObject(fieldSort.field, fieldSort.direction || "asc"); if (sortObject) orderBy = sortObject; } } let select; let where = {}; const list = options?.model?.[resource]?.list; select = selectPayloadForModel(resource, list, "object"); where = createWherePredicate({ resource, options, search, otherFilters: [ ...fieldFilters ?? [], ...list?.where ?? [] ], advancedSearch }); if (0 === Object.keys(orderBy).length) orderBy = { [idProperty]: "asc" }; return { select, where, orderBy: orderBy, skip: (page - 1) * itemsPerPage, take: itemsPerPage }; }; const optionsFromResource = async ({ originResource, property, ...args })=>{ const relationshipField = args.options?.model?.[originResource]?.edit?.fields?.[property]?.relationshipSearchField; if (relationshipField) { const targetModel = (0, external_globals_js_namespaceObject.getSchema)().definitions[args.resource]; if (!targetModel) throw new Error(`Model ${args.resource} not found in schema`); const targetModelProperties = targetModel.properties; const modelField = targetModelProperties[relationshipField]; if (modelField && modelField.__nextadmin?.type !== "scalar") args.resource = modelField.__nextadmin?.type; else console.warn("Used relationshipSearch on a scalar field, ignoring property"); } const data = await fetchDataList(args, true); const { data: dataItems, total, error } = data; const { resource } = args; const idProperty = (0, external_server_js_namespaceObject.getModelIdProperty)(resource); const dataTableItems = mapDataList({ ...args, fetchData: external_lodash_clonedeep_default()(dataItems) }); const toStringModel = (0, external_server_js_namespaceObject.getToStringForRelations)(originResource, property, args.resource, args.options); const displayMode = args.options?.model?.[originResource]?.edit?.fields?.[property]?.display; return { data: dataItems.map((item)=>{ const dataTableItem = dataTableItems.find((dataTableItem)=>dataTableItem[idProperty].value === item[idProperty]); return { label: toStringModel(item), value: item[idProperty], data: "table" === displayMode ? dataTableItem : void 0 }; }), total, error }; }; const fetchDataList = async ({ prisma, resource, options, searchParams }, skipFilters = false)=>{ const prismaListRequest = await preparePrismaListRequest(resource, searchParams, options, skipFilters); let data = []; let total; let error = null; const { where, orderBy, skip, take, select } = prismaListRequest; const idProperty = (0, external_server_js_namespaceObject.getModelIdProperty)(resource); try { const resourceIds = await prisma[(0, external_tools_js_namespaceObject.uncapitalize)(resource)].findMany({ select: { [idProperty]: true }, where, orderBy, skip, take }); data = await prisma[(0, external_tools_js_namespaceObject.uncapitalize)(resource)].findMany({ select, where: { [idProperty]: { in: resourceIds.map((item)=>item[idProperty]) } }, orderBy }); total = await prisma[(0, external_tools_js_namespaceObject.uncapitalize)(resource)].count({ where }); } catch (e) { const { skip, take, orderBy } = prismaListRequest; data = await prisma[(0, external_tools_js_namespaceObject.uncapitalize)(resource)].findMany({ skip, take, orderBy }); total = await prisma[(0, external_tools_js_namespaceObject.uncapitalize)(resource)].count(); error = e.message ? e.message : e; console.error(e); } return { data, total, error }; }; const mapDataList = ({ context, appDir, fetchData, ...args })=>{ const { resource, options } = args; const originalFetchData = external_lodash_clonedeep_default()(fetchData); const data = (0, external_server_js_namespaceObject.findRelationInData)(fetchData, (0, external_globals_js_namespaceObject.getSchema)().definitions[resource]); const listFields = options?.model?.[resource]?.list?.fields ?? {}; const listDisplayOptions = options?.model?.[resource]?.list?.display ?? []; const originalData = external_lodash_clonedeep_default()(data); data.forEach((item, index)=>{ context.row = originalData[index]; if ("_count" in item && "object" == typeof item._count) Object.keys(item._count).forEach((key)=>{ item[key] = { type: "count", value: item._count[key], __nextadmin_formatted: item._count[key].toString() }; }); delete item._count; Object.keys(item).forEach((key)=>{ let itemValue = null; const model = (0, external_tools_js_namespaceObject.capitalize)(key); const idProperty = (0, external_server_js_namespaceObject.getModelIdProperty)(model); if ("object" == typeof item[key] && null !== item[key]) { switch(item[key].type){ case "link": itemValue = item[key].value.label; break; case "count": itemValue = item[key].value; break; case "date": itemValue = item[key].value.toString(); break; default: itemValue = item[key][idProperty]; break; } item[key].__nextadmin_formatted = itemValue; } else if ((0, external_tools_js_namespaceObject.isScalar)(item[key]) && null !== item[key]) { item[key] = { type: "scalar", value: item[key], __nextadmin_formatted: item[key].toString() }; itemValue = item[key].value; } if (appDir && key in listFields && listFields[key]?.formatter && null !== itemValue) item[key].__nextadmin_formatted = listFields[key]?.formatter?.(itemValue ?? item[key], context); else { if ("object" == typeof item[key]?.__nextadmin_formatted) item[key].__nextadmin_formatted = item[key].__nextadmin_formatted[idProperty]; data[index][key] = item[key]; } if ("object" == typeof item[key]?.value) item[key].value.label = item[key].value.label[idProperty]; }); listDisplayOptions.forEach((displayOpt)=>{ if ("object" == typeof displayOpt) { const { key, formatter } = displayOpt; const virtualFieldCtx = { ...context, row: originalFetchData[index] }; const formatted = formatter?.(virtualFieldCtx); item[key] = { type: "string" == typeof formatted ? displayOpt.type ?? "scalar" : displayOpt.type, value: "link" === displayOpt.type ? { url: displayOpt.url(virtualFieldCtx) } : "string" == typeof formatted ? formatted : void 0, __nextadmin_formatted: appDir || "string" != typeof formatted ? formatted : null, isOverridden: "link" === displayOpt.type ? true : null }; } }); }); return data; }; const getMappedDataList = async ({ context, appDir = false, ...args })=>{ const { data: fetchData, total, error } = await fetchDataList(args); const rawData = external_lodash_clonedeep_default()(fetchData); return { data: mapDataList({ context, appDir, fetchData, ...args }), total, error, rawData: (0, external_tools_js_namespaceObject.extractSerializable)(rawData) }; }; const isVirtualField = (displayOpts)=>"object" == typeof displayOpts && "dependsOn" in displayOpts; const selectPayloadForModel = (resource, options, level = "scalar")=>{ const model = (0, external_globals_js_namespaceObject.getSchema)().definitions[resource]; const properties = model.properties; const idProperty = (0, external_server_js_namespaceObject.getModelIdProperty)(resource); const displayKeys = options?.display; const orderField = options?.orderField; const defaultSelectedKeysForVirtualFields = options?.display?.filter(isVirtualField).flatMap((disp)=>disp?.dependsOn)?.reduce((acc, val)=>{ if (val) acc[val] = true; return acc; }, {}) ?? {}; let selectedFields = Object.entries(properties).reduce((acc, [name, field])=>{ const fieldNextAdmin = field.__nextadmin; if (orderField === name) acc[name] = true; if (displayKeys && displayKeys.includes(name) || !displayKeys) if (fieldNextAdmin?.kind === "object" && "object" === level) { if (fieldNextAdmin?.isList) { const countFields = acc["_count"]?.select ?? {}; acc["_count"] = { select: { ...countFields, [name]: true } }; } acc[name] = { select: selectPayloadForModel(fieldNextAdmin.type, {}, "scalar") }; const orderField = options?.fields?.[name]?.orderField || options?.orderField; if (orderField) acc[name].orderBy = { [orderField]: "asc" }; } else acc[name] = true; return acc; }, (0, external_server_js_namespaceObject.modelHasIdField)(resource) ? { [idProperty]: true, ...defaultSelectedKeysForVirtualFields } : {}); return selectedFields; }; const getDataItem = async ({ prisma, resource, options, resourceId, locale, isAppDir })=>{ const edit = options?.model?.[resource]?.edit; const idProperty = (0, external_server_js_namespaceObject.getModelIdProperty)(resource); const select = selectPayloadForModel(resource, edit, "object"); const schemaResourceProperties = (0, external_globals_js_namespaceObject.getSchema)().definitions[resource].properties; Object.entries(select).forEach(([key, value])=>{ const fieldType = schemaResourceProperties[key]?.__nextadmin?.type; if (fieldType) { const relatedResourceOptions = options?.model?.[fieldType]?.list; if (edit?.fields?.[key]?.display === "table") { if (!relatedResourceOptions?.display) throw new Error(`'table' display mode set for field '${key}', but no list display is setup for model ${fieldType}`); select[key] = { select: selectPayloadForModel(fieldType, relatedResourceOptions, "object") }; } } }); let data = await prisma[resource].findUniqueOrThrow({ select, where: { [idProperty]: resourceId } }); const relationshipsRawData = {}; Object.entries(data).forEach(([key, value])=>{ if (Array.isArray(value)) { const fieldType = schemaResourceProperties[key]?.__nextadmin?.type; relationshipsRawData[key] = external_lodash_clonedeep_default()(value); if (fieldType) { if (edit?.fields?.[key]?.display === "table") data[key] = mapDataList({ context: { locale }, appDir: isAppDir, fetchData: value, options, resource: fieldType }); } } }); data = await (0, external_server_js_namespaceObject.transformData)(data, resource, edit ?? {}, options); return { data, relationshipsRawData }; }; const includeDataByDepth = (modelProperties, currentDepth, maxDepth)=>{ const include = Object.entries(modelProperties)?.reduce((acc, [name, field])=>{ if (field.__nextadmin?.kind === "object") if (currentDepth < maxDepth - 1) { const nextModel = (0, external_globals_js_namespaceObject.getSchema)().definitions[field.__nextadmin.type].properties; acc[name] = { include: includeDataByDepth(nextModel, currentDepth + 1, maxDepth) }; } else acc[name] = true; return acc; }, {}); return include; }; const getRawData = async ({ prisma, resource, resourceIds, maxDepth = 2 })=>{ const model = (0, external_globals_js_namespaceObject.getSchema)().definitions[resource]; const modelProperties = model.properties; const include = includeDataByDepth(modelProperties, 1, maxDepth); const idProperty = (0, external_server_js_namespaceObject.getModelIdProperty)(resource); const data = await prisma[resource].findMany({ where: { [idProperty]: { in: resourceIds } }, include }); return data; }; exports.createWherePredicate = __webpack_exports__.createWherePredicate; exports.getDataItem = __webpack_exports__.getDataItem; exports.getMappedDataList = __webpack_exports__.getMappedDataList; exports.getRawData = __webpack_exports__.getRawData; exports.mapDataList = __webpack_exports__.mapDataList; exports.mapModelFilters = __webpack_exports__.mapModelFilters; exports.optionsFromResource = __webpack_exports__.optionsFromResource; exports.selectPayloadForModel = __webpack_exports__.selectPayloadForModel; for(var __webpack_i__ in __webpack_exports__)if (-1 === [ "createWherePredicate", "getDataItem", "getMappedDataList", "getRawData", "mapDataList", "mapModelFilters", "optionsFromResource", "selectPayloadForModel" ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; Object.defineProperty(exports, '__esModule', { value: true });