@strapi/content-manager 5.50.2 → 5.51.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/content-manager.js +31 -4
- package/dist/admin/content-manager.js.map +1 -1
- package/dist/admin/content-manager.mjs +31 -4
- package/dist/admin/content-manager.mjs.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.js +23 -7
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.mjs +23 -7
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksEditor.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.js +5 -13
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.mjs +5 -13
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js +4 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs +4 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs.map +1 -1
- package/dist/admin/pages/EditView/utils/draftRelationCounts.js +14 -7
- package/dist/admin/pages/EditView/utils/draftRelationCounts.js.map +1 -1
- package/dist/admin/pages/EditView/utils/draftRelationCounts.mjs +14 -8
- package/dist/admin/pages/EditView/utils/draftRelationCounts.mjs.map +1 -1
- package/dist/admin/preview/components/PreviewSidePanel.js +46 -9
- package/dist/admin/preview/components/PreviewSidePanel.js.map +1 -1
- package/dist/admin/preview/components/PreviewSidePanel.mjs +27 -10
- package/dist/admin/preview/components/PreviewSidePanel.mjs.map +1 -1
- package/dist/admin/src/index.d.ts +1 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/Relations/Relations.d.ts +9 -1
- package/dist/admin/src/pages/EditView/utils/draftRelationCounts.d.ts +1 -0
- package/dist/server/homepage/services/homepage.js +24 -7
- package/dist/server/homepage/services/homepage.js.map +1 -1
- package/dist/server/homepage/services/homepage.mjs +24 -7
- package/dist/server/homepage/services/homepage.mjs.map +1 -1
- package/dist/server/src/history/routes/index.d.ts +3 -1
- package/dist/server/src/history/routes/index.d.ts.map +1 -1
- package/dist/server/src/homepage/index.d.ts +6 -2
- package/dist/server/src/homepage/index.d.ts.map +1 -1
- package/dist/server/src/homepage/routes/index.d.ts +3 -1
- package/dist/server/src/homepage/routes/index.d.ts.map +1 -1
- package/dist/server/src/homepage/services/homepage.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +3 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/preview/routes/index.d.ts +3 -1
- package/dist/server/src/preview/routes/index.d.ts.map +1 -1
- package/dist/server/src/routes/index.d.ts +3 -1
- package/dist/server/src/routes/index.d.ts.map +1 -1
- package/dist/shared/contracts/collection-types.d.ts.map +1 -1
- package/dist/shared/contracts/components.d.ts +1 -1
- package/dist/shared/contracts/components.d.ts.map +1 -1
- package/dist/shared/contracts/homepage.d.ts +2 -2
- package/dist/shared/contracts/homepage.d.ts.map +1 -1
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draftRelationCounts.mjs","sources":["../../../../../admin/src/pages/EditView/utils/draftRelationCounts.ts"],"sourcesContent":["import type { Component } from '../../../../../shared/contracts/components';\nimport type { ComponentsDictionary, Schema } from '../../../hooks/useDocument';\nimport type { RelationsFormValue } from '../components/FormInputs/Relations/Relations';\n\nexport interface DraftRelationCounts {\n unpublishedRelations: number;\n draftM2mLinks: number;\n}\n\nexport const EMPTY_DRAFT_RELATION_COUNTS: DraftRelationCounts = {\n unpublishedRelations: 0,\n draftM2mLinks: 0,\n};\n\nexport const isBidirectionalManyToMany = (attribute: Schema['attributes'][string]) => {\n if (attribute.type !== 'relation' || attribute.relation !== 'manyToMany') {\n return false;\n }\n\n const relationAttribute = attribute as Extract<\n Schema['attributes'][string],\n { type: 'relation'; relation: 'manyToMany' }\n > & {\n inversedBy?: string;\n mappedBy?: string;\n };\n\n return Boolean(relationAttribute.inversedBy || relationAttribute.mappedBy);\n};\n\nexport const mergeDraftRelationCounts = (\n left: DraftRelationCounts,\n right: DraftRelationCounts\n): DraftRelationCounts => ({\n unpublishedRelations: left.unpublishedRelations + right.unpublishedRelations,\n draftM2mLinks: left.draftM2mLinks + right.draftM2mLinks,\n});\n\nexport const normalizeDraftRelationCounts = (payload: unknown): DraftRelationCounts => {\n if (\n payload &&\n typeof payload === 'object' &&\n 'unpublishedRelations' in payload &&\n 'draftM2mLinks' in payload\n ) {\n return payload as DraftRelationCounts;\n }\n\n if (payload && typeof payload === 'object' && 'data' in payload) {\n return normalizeDraftRelationCounts((payload as { data: unknown }).data);\n }\n\n return EMPTY_DRAFT_RELATION_COUNTS;\n};\n\nexport const resolveDraftRelationCounts = (\n documentId: string | undefined,\n modified: boolean,\n localCounts: DraftRelationCounts,\n serverCounts: DraftRelationCounts\n): DraftRelationCounts => {\n if (!documentId) {\n return localCounts;\n }\n\n const hasUnsavedDraftRelations =\n localCounts.unpublishedRelations > 0 || localCounts.draftM2mLinks > 0;\n\n if (modified || hasUnsavedDraftRelations) {\n return mergeDraftRelationCounts(localCounts, serverCounts);\n }\n\n return serverCounts;\n};\n\nexport interface DraftRelationsPublishState {\n hasUnpublishedRelations: boolean;\n hasDraftM2mLinks: boolean;\n hasDraftRelations: boolean;\n isM2mOnlyDraftRelations: boolean;\n dialogVariant: 'default' | 'danger';\n bodyIcon: 'default' | 'danger';\n confirmLabel: 'publish' | 'publish-without-relations';\n}\n\nexport const getDraftRelationsPublishState = (\n counts: DraftRelationCounts\n): DraftRelationsPublishState => {\n const hasUnpublishedRelations = counts.unpublishedRelations > 0;\n const hasDraftM2mLinks = counts.draftM2mLinks > 0;\n const hasDraftRelations = hasUnpublishedRelations || hasDraftM2mLinks;\n const isM2mOnlyDraftRelations = hasDraftM2mLinks && !hasUnpublishedRelations;\n\n return {\n hasUnpublishedRelations,\n hasDraftM2mLinks,\n hasDraftRelations,\n isM2mOnlyDraftRelations,\n dialogVariant: isM2mOnlyDraftRelations ? 'default' : 'danger',\n bodyIcon: isM2mOnlyDraftRelations ? 'default' : 'danger',\n confirmLabel: isM2mOnlyDraftRelations ? 'publish' : 'publish-without-relations',\n };\n};\n\n/**\n * Counts draft relations in unsaved form values, excluding self-referential relations\n * (preserved on publish via document-service self-referential-relations).\n */\nexport const countLocalDraftRelations = (\n data: Record<string, unknown>,\n schema: Schema | Component | undefined,\n components: ComponentsDictionary,\n contentTypeUid: string\n): DraftRelationCounts => {\n if (!schema?.attributes) {\n return EMPTY_DRAFT_RELATION_COUNTS;\n }\n\n return Object.keys(schema.attributes).reduce((counts, attributeName) => {\n const attribute = schema.attributes[attributeName];\n const value = data[attributeName];\n\n if (!value) {\n return counts;\n }\n\n switch (attribute.type) {\n case 'relation': {\n if (!('target' in attribute) || attribute.target === contentTypeUid) {\n return counts;\n }\n\n if (typeof value === 'object' && value !== null && 'connect' in value) {\n const draftConnectCount =\n (value as RelationsFormValue).connect?.filter((relation) => relation.status === 'draft')\n .length ?? 0;\n\n if (draftConnectCount === 0) {\n return counts;\n }\n\n if (isBidirectionalManyToMany(attribute)) {\n return {\n ...counts,\n draftM2mLinks: counts.draftM2mLinks + draftConnectCount,\n };\n }\n\n return {\n ...counts,\n unpublishedRelations: counts.unpublishedRelations + draftConnectCount,\n };\n }\n\n return counts;\n }\n case 'component': {\n const componentItems = Array.isArray(value) ? value : [value];\n const componentSchema = components[attribute.component];\n\n return componentItems.reduce(\n (componentCounts, componentValue) =>\n mergeDraftRelationCounts(\n componentCounts,\n countLocalDraftRelations(\n componentValue as Record<string, unknown>,\n componentSchema,\n components,\n contentTypeUid\n )\n ),\n counts\n );\n }\n case 'dynamiczone': {\n return (value as Array<Record<string, unknown>>).reduce((zoneCounts, componentValue) => {\n const componentUid = componentValue.__component as string;\n\n return mergeDraftRelationCounts(\n zoneCounts,\n countLocalDraftRelations(\n componentValue,\n components[componentUid],\n components,\n contentTypeUid\n )\n );\n }, counts);\n }\n default:\n return counts;\n }\n }, EMPTY_DRAFT_RELATION_COUNTS);\n};\n"],"names":["EMPTY_DRAFT_RELATION_COUNTS","unpublishedRelations","draftM2mLinks","isBidirectionalManyToMany","attribute","type","relation","relationAttribute","Boolean","inversedBy","mappedBy","mergeDraftRelationCounts","left","right","normalizeDraftRelationCounts","payload","data","resolveDraftRelationCounts","documentId","modified","localCounts","serverCounts","hasUnsavedDraftRelations","getDraftRelationsPublishState","counts","hasUnpublishedRelations","hasDraftM2mLinks","hasDraftRelations","isM2mOnlyDraftRelations","dialogVariant","bodyIcon","confirmLabel","countLocalDraftRelations","schema","components","contentTypeUid","attributes","Object","keys","reduce","attributeName","value","target","draftConnectCount","connect","filter","status","length","componentItems","Array","isArray","componentSchema","component","componentCounts","componentValue","zoneCounts","componentUid","__component"],"mappings":"MASaA,2BAAAA,GAAmD;IAC9DC,oBAAAA,EAAsB,CAAA;IACtBC,aAAAA,EAAe;AACjB;AAEO,MAAMC,4BAA4B,CAACC,SAAAA,GAAAA;AACxC,IAAA,IAAIA,UAAUC,IAAI,KAAK,cAAcD,SAAAA,CAAUE,QAAQ,KAAK,YAAA,EAAc;QACxE,OAAO,KAAA;AACT,IAAA;AAEA,IAAA,MAAMC,iBAAAA,GAAoBH,SAAAA;AAQ1B,IAAA,OAAOI,OAAAA,CAAQD,iBAAAA,CAAkBE,UAAU,IAAIF,kBAAkBG,QAAQ,CAAA;AAC3E;AAEO,MAAMC,wBAAAA,GAA2B,CACtCC,IAAAA,EACAC,SACyB;AACzBZ,QAAAA,oBAAAA,EAAsBW,IAAAA,CAAKX,oBAAoB,GAAGY,KAAAA,CAAMZ,oBAAoB;AAC5EC,QAAAA,aAAAA,EAAeU,IAAAA,CAAKV,aAAa,GAAGW,KAAAA,CAAMX;AAC5C,KAAA;AAEO,MAAMY,+BAA+B,CAACC,OAAAA,GAAAA;AAC3C,IAAA,IACEA,WACA,OAAOA,OAAAA,KAAY,YACnB,sBAAA,IAA0BA,OAAAA,IAC1B,mBAAmBA,OAAAA,EACnB;QACA,OAAOA,OAAAA;AACT,IAAA;AAEA,IAAA,IAAIA,OAAAA,IAAW,OAAOA,OAAAA,KAAY,QAAA,IAAY,UAAUA,OAAAA,EAAS;QAC/D,OAAOD,4BAAAA,CAA6B,OAACC,CAA8BC,IAAI,CAAA;AACzE,IAAA;IAEA,OAAOhB,2BAAAA;AACT;AAEO,MAAMiB,0BAAAA,GAA6B,CACxCC,UAAAA,EACAC,UACAC,WAAAA,EACAC,YAAAA,GAAAA;AAEA,IAAA,IAAI,CAACH,UAAAA,EAAY;QACf,OAAOE,WAAAA;AACT,IAAA;AAEA,IAAA,MAAME,2BACJF,WAAAA,CAAYnB,oBAAoB,GAAG,CAAA,IAAKmB,WAAAA,CAAYlB,aAAa,GAAG,CAAA;AAEtE,IAAA,IAAIiB,YAAYG,wBAAAA,EAA0B;AACxC,QAAA,OAAOX,yBAAyBS,WAAAA,EAAaC,YAAAA,CAAAA;AAC/C,IAAA;IAEA,OAAOA,YAAAA;AACT;AAYO,MAAME,gCAAgC,CAC3CC,MAAAA,GAAAA;IAEA,MAAMC,uBAAAA,GAA0BD,MAAAA,CAAOvB,oBAAoB,GAAG,CAAA;IAC9D,MAAMyB,gBAAAA,GAAmBF,MAAAA,CAAOtB,aAAa,GAAG,CAAA;AAChD,IAAA,MAAMyB,oBAAoBF,uBAAAA,IAA2BC,gBAAAA;IACrD,MAAME,uBAAAA,GAA0BF,oBAAoB,CAACD,uBAAAA;IAErD,OAAO;AACLA,QAAAA,uBAAAA;AACAC,QAAAA,gBAAAA;AACAC,QAAAA,iBAAAA;AACAC,QAAAA,uBAAAA;AACAC,QAAAA,aAAAA,EAAeD,0BAA0B,SAAA,GAAY,QAAA;AACrDE,QAAAA,QAAAA,EAAUF,0BAA0B,SAAA,GAAY,QAAA;AAChDG,QAAAA,YAAAA,EAAcH,0BAA0B,SAAA,GAAY;AACtD,KAAA;AACF;AAEA;;;AAGC,IACM,MAAMI,wBAAAA,GAA2B,CACtChB,IAAAA,EACAiB,QACAC,UAAAA,EACAC,cAAAA,GAAAA;IAEA,IAAI,CAACF,QAAQG,UAAAA,EAAY;QACvB,OAAOpC,2BAAAA;AACT,IAAA;IAEA,OAAOqC,MAAAA,CAAOC,IAAI,CAACL,MAAAA,CAAOG,UAAU,CAAA,CAAEG,MAAM,CAAC,CAACf,MAAAA,EAAQgB,aAAAA,GAAAA;AACpD,QAAA,MAAMpC,SAAAA,GAAY6B,MAAAA,CAAOG,UAAU,CAACI,aAAAA,CAAc;QAClD,MAAMC,KAAAA,GAAQzB,IAAI,CAACwB,aAAAA,CAAc;AAEjC,QAAA,IAAI,CAACC,KAAAA,EAAO;YACV,OAAOjB,MAAAA;AACT,QAAA;AAEA,QAAA,OAAQpB,UAAUC,IAAI;YACpB,KAAK,UAAA;AAAY,gBAAA;oBACf,IAAI,EAAE,QAAA,IAAYD,SAAQ,KAAMA,SAAAA,CAAUsC,MAAM,KAAKP,cAAAA,EAAgB;wBACnE,OAAOX,MAAAA;AACT,oBAAA;AAEA,oBAAA,IAAI,OAAOiB,KAAAA,KAAU,QAAA,IAAYA,KAAAA,KAAU,IAAA,IAAQ,aAAaA,KAAAA,EAAO;AACrE,wBAAA,MAAME,iBAAAA,GACHF,KAAAA,CAA6BG,OAAO,EAAEC,MAAAA,CAAO,CAACvC,QAAAA,GAAaA,QAAAA,CAASwC,MAAM,KAAK,OAAA,CAAA,CAC7EC,MAAAA,IAAU,CAAA;AAEf,wBAAA,IAAIJ,sBAAsB,CAAA,EAAG;4BAC3B,OAAOnB,MAAAA;AACT,wBAAA;AAEA,wBAAA,IAAIrB,0BAA0BC,SAAAA,CAAAA,EAAY;4BACxC,OAAO;AACL,gCAAA,GAAGoB,MAAM;gCACTtB,aAAAA,EAAesB,MAAAA,CAAOtB,aAAa,GAAGyC;AACxC,6BAAA;AACF,wBAAA;wBAEA,OAAO;AACL,4BAAA,GAAGnB,MAAM;4BACTvB,oBAAAA,EAAsBuB,MAAAA,CAAOvB,oBAAoB,GAAG0C;AACtD,yBAAA;AACF,oBAAA;oBAEA,OAAOnB,MAAAA;AACT,gBAAA;YACA,KAAK,WAAA;AAAa,gBAAA;AAChB,oBAAA,MAAMwB,cAAAA,GAAiBC,KAAAA,CAAMC,OAAO,CAACT,SAASA,KAAAA,GAAQ;AAACA,wBAAAA;AAAM,qBAAA;AAC7D,oBAAA,MAAMU,eAAAA,GAAkBjB,UAAU,CAAC9B,SAAAA,CAAUgD,SAAS,CAAC;AAEvD,oBAAA,OAAOJ,cAAAA,CAAeT,MAAM,CAC1B,CAACc,eAAAA,EAAiBC,cAAAA,GAChB3C,wBAAAA,CACE0C,eAAAA,EACArB,wBAAAA,CACEsB,cAAAA,EACAH,eAAAA,EACAjB,UAAAA,EACAC,cAAAA,CAAAA,CAAAA,EAGNX,MAAAA,CAAAA;AAEJ,gBAAA;YACA,KAAK,aAAA;AAAe,gBAAA;AAClB,oBAAA,OAAO,KAACiB,CAAyCF,MAAM,CAAC,CAACgB,UAAAA,EAAYD,cAAAA,GAAAA;wBACnE,MAAME,YAAAA,GAAeF,eAAeG,WAAW;wBAE/C,OAAO9C,wBAAAA,CACL4C,YACAvB,wBAAAA,CACEsB,cAAAA,EACApB,UAAU,CAACsB,YAAAA,CAAa,EACxBtB,UAAAA,EACAC,cAAAA,CAAAA,CAAAA;oBAGN,CAAA,EAAGX,MAAAA,CAAAA;AACL,gBAAA;AACA,YAAA;gBACE,OAAOA,MAAAA;AACX;IACF,CAAA,EAAGxB,2BAAAA,CAAAA;AACL;;;;"}
|
|
1
|
+
{"version":3,"file":"draftRelationCounts.mjs","sources":["../../../../../admin/src/pages/EditView/utils/draftRelationCounts.ts"],"sourcesContent":["import type { Component } from '../../../../../shared/contracts/components';\nimport type { ComponentsDictionary, Schema } from '../../../hooks/useDocument';\nimport type { RelationsFormValue } from '../components/FormInputs/Relations/Relations';\n\nexport interface DraftRelationCounts {\n unpublishedRelations: number;\n draftM2mLinks: number;\n}\n\nexport const EMPTY_DRAFT_RELATION_COUNTS: DraftRelationCounts = {\n unpublishedRelations: 0,\n draftM2mLinks: 0,\n};\n\nexport const isBidirectionalManyToMany = (attribute: Schema['attributes'][string]) => {\n if (attribute.type !== 'relation' || attribute.relation !== 'manyToMany') {\n return false;\n }\n\n const relationAttribute = attribute as Extract<\n Schema['attributes'][string],\n { type: 'relation'; relation: 'manyToMany' }\n > & {\n inversedBy?: string;\n mappedBy?: string;\n };\n\n return Boolean(relationAttribute.inversedBy || relationAttribute.mappedBy);\n};\n\nexport const mergeDraftRelationCounts = (\n left: DraftRelationCounts,\n right: DraftRelationCounts\n): DraftRelationCounts => ({\n unpublishedRelations: left.unpublishedRelations + right.unpublishedRelations,\n draftM2mLinks: left.draftM2mLinks + right.draftM2mLinks,\n});\n\nexport const clampDraftRelationCounts = (counts: DraftRelationCounts): DraftRelationCounts => ({\n unpublishedRelations: Math.max(0, counts.unpublishedRelations),\n draftM2mLinks: Math.max(0, counts.draftM2mLinks),\n});\n\nexport const normalizeDraftRelationCounts = (payload: unknown): DraftRelationCounts => {\n if (\n payload &&\n typeof payload === 'object' &&\n 'unpublishedRelations' in payload &&\n 'draftM2mLinks' in payload\n ) {\n return payload as DraftRelationCounts;\n }\n\n if (payload && typeof payload === 'object' && 'data' in payload) {\n return normalizeDraftRelationCounts((payload as { data: unknown }).data);\n }\n\n return EMPTY_DRAFT_RELATION_COUNTS;\n};\n\nexport const resolveDraftRelationCounts = (\n documentId: string | undefined,\n modified: boolean,\n localCounts: DraftRelationCounts,\n serverCounts: DraftRelationCounts\n): DraftRelationCounts => {\n if (!documentId) {\n return clampDraftRelationCounts(localCounts);\n }\n\n const hasUnsavedDraftRelations =\n localCounts.unpublishedRelations > 0 || localCounts.draftM2mLinks > 0;\n\n if (modified || hasUnsavedDraftRelations) {\n return clampDraftRelationCounts(mergeDraftRelationCounts(localCounts, serverCounts));\n }\n\n return serverCounts;\n};\n\nexport interface DraftRelationsPublishState {\n hasUnpublishedRelations: boolean;\n hasDraftM2mLinks: boolean;\n hasDraftRelations: boolean;\n isM2mOnlyDraftRelations: boolean;\n dialogVariant: 'default' | 'danger';\n bodyIcon: 'default' | 'danger';\n confirmLabel: 'publish' | 'publish-without-relations';\n}\n\nexport const getDraftRelationsPublishState = (\n counts: DraftRelationCounts\n): DraftRelationsPublishState => {\n const hasUnpublishedRelations = counts.unpublishedRelations > 0;\n const hasDraftM2mLinks = counts.draftM2mLinks > 0;\n const hasDraftRelations = hasUnpublishedRelations || hasDraftM2mLinks;\n const isM2mOnlyDraftRelations = hasDraftM2mLinks && !hasUnpublishedRelations;\n\n return {\n hasUnpublishedRelations,\n hasDraftM2mLinks,\n hasDraftRelations,\n isM2mOnlyDraftRelations,\n dialogVariant: isM2mOnlyDraftRelations ? 'default' : 'danger',\n bodyIcon: isM2mOnlyDraftRelations ? 'default' : 'danger',\n confirmLabel: isM2mOnlyDraftRelations ? 'publish' : 'publish-without-relations',\n };\n};\n\n/**\n * Counts draft relations in unsaved form values, excluding self-referential relations\n * (preserved on publish via document-service self-referential-relations).\n */\nexport const countLocalDraftRelations = (\n data: Record<string, unknown>,\n schema: Schema | Component | undefined,\n components: ComponentsDictionary,\n contentTypeUid: string\n): DraftRelationCounts => {\n if (!schema?.attributes) {\n return EMPTY_DRAFT_RELATION_COUNTS;\n }\n\n return Object.keys(schema.attributes).reduce((counts, attributeName) => {\n const attribute = schema.attributes[attributeName];\n const value = data[attributeName];\n\n if (!value) {\n return counts;\n }\n\n switch (attribute.type) {\n case 'relation': {\n if (!('target' in attribute) || attribute.target === contentTypeUid) {\n return counts;\n }\n\n if (\n typeof value === 'object' &&\n value !== null &&\n ('connect' in value || 'disconnect' in value)\n ) {\n const formValue = value as RelationsFormValue;\n const draftConnectCount =\n formValue.connect?.filter((relation) => relation.status === 'draft').length ?? 0;\n const draftDisconnectCount =\n formValue.disconnect?.filter((relation) => relation.status === 'draft').length ?? 0;\n\n if (draftConnectCount === 0 && draftDisconnectCount === 0) {\n return counts;\n }\n\n if (isBidirectionalManyToMany(attribute)) {\n return {\n ...counts,\n draftM2mLinks: counts.draftM2mLinks + draftConnectCount - draftDisconnectCount,\n };\n }\n\n return {\n ...counts,\n unpublishedRelations:\n counts.unpublishedRelations + draftConnectCount - draftDisconnectCount,\n };\n }\n\n return counts;\n }\n case 'component': {\n const componentItems = Array.isArray(value) ? value : [value];\n const componentSchema = components[attribute.component];\n\n return componentItems.reduce(\n (componentCounts, componentValue) =>\n mergeDraftRelationCounts(\n componentCounts,\n countLocalDraftRelations(\n componentValue as Record<string, unknown>,\n componentSchema,\n components,\n contentTypeUid\n )\n ),\n counts\n );\n }\n case 'dynamiczone': {\n return (value as Array<Record<string, unknown>>).reduce((zoneCounts, componentValue) => {\n const componentUid = componentValue.__component as string;\n\n return mergeDraftRelationCounts(\n zoneCounts,\n countLocalDraftRelations(\n componentValue,\n components[componentUid],\n components,\n contentTypeUid\n )\n );\n }, counts);\n }\n default:\n return counts;\n }\n }, EMPTY_DRAFT_RELATION_COUNTS);\n};\n"],"names":["EMPTY_DRAFT_RELATION_COUNTS","unpublishedRelations","draftM2mLinks","isBidirectionalManyToMany","attribute","type","relation","relationAttribute","Boolean","inversedBy","mappedBy","mergeDraftRelationCounts","left","right","clampDraftRelationCounts","counts","Math","max","normalizeDraftRelationCounts","payload","data","resolveDraftRelationCounts","documentId","modified","localCounts","serverCounts","hasUnsavedDraftRelations","getDraftRelationsPublishState","hasUnpublishedRelations","hasDraftM2mLinks","hasDraftRelations","isM2mOnlyDraftRelations","dialogVariant","bodyIcon","confirmLabel","countLocalDraftRelations","schema","components","contentTypeUid","attributes","Object","keys","reduce","attributeName","value","target","formValue","draftConnectCount","connect","filter","status","length","draftDisconnectCount","disconnect","componentItems","Array","isArray","componentSchema","component","componentCounts","componentValue","zoneCounts","componentUid","__component"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
require('react');
|
|
4
|
+
var React = require('react');
|
|
5
5
|
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
6
6
|
var designSystem = require('@strapi/design-system');
|
|
7
7
|
var qs = require('qs');
|
|
@@ -9,14 +9,37 @@ var reactIntl = require('react-intl');
|
|
|
9
9
|
var reactRouterDom = require('react-router-dom');
|
|
10
10
|
var preview = require('../services/preview.js');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
function _interopNamespace(e) {
|
|
13
|
+
if (e && e.__esModule) return e;
|
|
14
|
+
var n = Object.create(null);
|
|
15
|
+
if (e) {
|
|
16
|
+
Object.keys(e).forEach(function (k) {
|
|
17
|
+
if (k !== 'default') {
|
|
18
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return e[k]; }
|
|
17
22
|
});
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
n.default = e;
|
|
27
|
+
return Object.freeze(n);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Always keeps the Tooltip (and therefore the children subtree) mounted: conditionally wrapping
|
|
34
|
+
* would remount the child button whenever `isShown` flips, and a click landing during that swap
|
|
35
|
+
* is silently lost. When the tooltip must not show, it is force-closed via the controlled `open`
|
|
36
|
+
* prop instead of being removed from the tree.
|
|
37
|
+
*/ const ConditionalTooltip = ({ isShown, label, children })=>{
|
|
38
|
+
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tooltip, {
|
|
39
|
+
label: label,
|
|
40
|
+
open: isShown ? undefined : false,
|
|
41
|
+
children: children
|
|
42
|
+
});
|
|
20
43
|
};
|
|
21
44
|
const PreviewSidePanel = ({ model, documentId, document })=>{
|
|
22
45
|
const { formatMessage } = reactIntl.useIntl();
|
|
@@ -46,6 +69,20 @@ const PreviewSidePanel = ({ model, documentId, document })=>{
|
|
|
46
69
|
{
|
|
47
70
|
skip: isUnsaved
|
|
48
71
|
});
|
|
72
|
+
/**
|
|
73
|
+
* The query args above change while the document settles (locale/status resolve after load and
|
|
74
|
+
* on save/publish), and each change is a new cache entry with transiently empty data. Latch
|
|
75
|
+
* enablement once a URL has been seen so the "Open preview" button doesn't unmount and remount
|
|
76
|
+
* mid-interaction — a click landing during such a swap is silently lost.
|
|
77
|
+
*/ const [isPreviewEnabled, setIsPreviewEnabled] = React__namespace.useState(false);
|
|
78
|
+
const previewUrl = data?.data?.url;
|
|
79
|
+
React__namespace.useEffect(()=>{
|
|
80
|
+
if (previewUrl) {
|
|
81
|
+
setIsPreviewEnabled(true);
|
|
82
|
+
}
|
|
83
|
+
}, [
|
|
84
|
+
previewUrl
|
|
85
|
+
]);
|
|
49
86
|
if (isUnsaved) {
|
|
50
87
|
return null;
|
|
51
88
|
}
|
|
@@ -68,7 +105,7 @@ const PreviewSidePanel = ({ model, documentId, document })=>{
|
|
|
68
105
|
})
|
|
69
106
|
};
|
|
70
107
|
}
|
|
71
|
-
if (!data?.data?.url || error) {
|
|
108
|
+
if (!isPreviewEnabled && (!data?.data?.url || error)) {
|
|
72
109
|
return null;
|
|
73
110
|
}
|
|
74
111
|
const trackNavigation = ()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PreviewSidePanel.js","sources":["../../../../admin/src/preview/components/PreviewSidePanel.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useQueryParams, useTracking, useForm } from '@strapi/admin/strapi-admin';\nimport { Box, Button, Tooltip, type TooltipProps } from '@strapi/design-system';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { Link, useLocation } from 'react-router-dom';\n\nimport { useGetPreviewUrlQuery } from '../services/preview';\n\nimport type { PanelComponent } from '@strapi/content-manager/strapi-admin';\nimport type { UID } from '@strapi/types';\n\ninterface ConditionalTooltipProps {\n isShown: boolean;\n label: TooltipProps['label'];\n children: React.ReactNode;\n}\n\nconst ConditionalTooltip = ({ isShown, label, children }: ConditionalTooltipProps) => {\n
|
|
1
|
+
{"version":3,"file":"PreviewSidePanel.js","sources":["../../../../admin/src/preview/components/PreviewSidePanel.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useQueryParams, useTracking, useForm } from '@strapi/admin/strapi-admin';\nimport { Box, Button, Tooltip, type TooltipProps } from '@strapi/design-system';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { Link, useLocation } from 'react-router-dom';\n\nimport { useGetPreviewUrlQuery } from '../services/preview';\n\nimport type { PanelComponent } from '@strapi/content-manager/strapi-admin';\nimport type { UID } from '@strapi/types';\n\ninterface ConditionalTooltipProps {\n isShown: boolean;\n label: TooltipProps['label'];\n children: React.ReactNode;\n}\n\n/**\n * Always keeps the Tooltip (and therefore the children subtree) mounted: conditionally wrapping\n * would remount the child button whenever `isShown` flips, and a click landing during that swap\n * is silently lost. When the tooltip must not show, it is force-closed via the controlled `open`\n * prop instead of being removed from the tree.\n */\nconst ConditionalTooltip = ({ isShown, label, children }: ConditionalTooltipProps) => {\n return (\n <Tooltip label={label} open={isShown ? undefined : false}>\n {children}\n </Tooltip>\n );\n};\n\nconst PreviewSidePanel: PanelComponent = ({ model, documentId, document }) => {\n const { formatMessage } = useIntl();\n const { trackUsage } = useTracking();\n const { pathname } = useLocation();\n const [{ query }] = useQueryParams();\n const isModified = useForm('PreviewSidePanel', (state) => state.modified);\n const isUnsaved = Boolean(!document || !document.id);\n\n const title = formatMessage({\n id: 'content-manager.preview.panel.title',\n defaultMessage: 'Preview',\n });\n\n /**\n * The preview URL isn't used in this component, we just fetch it to know if preview is enabled\n * for the content type. If it's not, the panel is not displayed. If it is, we display a link to\n * /preview, and the URL will already be loaded in the RTK query cache.\n */\n const { data, error } = useGetPreviewUrlQuery(\n {\n params: {\n contentType: model as UID.ContentType,\n },\n query: {\n documentId,\n locale: document?.locale,\n status: document?.status,\n },\n },\n // Don't bother making the request since we won't show any UI\n { skip: isUnsaved }\n );\n\n /**\n * The query args above change while the document settles (locale/status resolve after load and\n * on save/publish), and each change is a new cache entry with transiently empty data. Latch\n * enablement once a URL has been seen so the \"Open preview\" button doesn't unmount and remount\n * mid-interaction — a click landing during such a swap is silently lost.\n */\n const [isPreviewEnabled, setIsPreviewEnabled] = React.useState(false);\n const previewUrl = data?.data?.url;\n React.useEffect(() => {\n if (previewUrl) {\n setIsPreviewEnabled(true);\n }\n }, [previewUrl]);\n\n if (isUnsaved) {\n return null;\n }\n\n // Preview was not configured but not disabled either (otherwise it would be a success 204).\n // So we encourage the user to set it up.\n if (error && error.name === 'NotFoundError') {\n return {\n title,\n content: (\n <Button\n variant=\"tertiary\"\n tag={Link}\n to=\"https://docs.strapi.io/cms/features/preview\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n width=\"100%\"\n >\n {formatMessage({\n id: 'content-manager.preview.panel.button-configuration',\n defaultMessage: 'Set up preview',\n })}\n </Button>\n ),\n };\n }\n\n if (!isPreviewEnabled && (!data?.data?.url || error)) {\n return null;\n }\n\n const trackNavigation = () => {\n // Append /preview to the current URL\n const destinationPathname = pathname.replace(/\\/$/, '') + '/preview';\n trackUsage('willNavigate', { from: pathname, to: destinationPathname });\n };\n\n return {\n title,\n content: (\n <ConditionalTooltip\n label={formatMessage({\n id: 'content-manager.preview.panel.button-disabled-tooltip',\n defaultMessage: 'Please save to open the preview',\n })}\n isShown={isModified}\n >\n <Box cursor=\"not-allowed\" width=\"100%\">\n <Button\n variant=\"tertiary\"\n tag={Link}\n to={{ pathname: 'preview', search: stringify(query, { encode: false }) }}\n onClick={trackNavigation}\n width=\"100%\"\n disabled={isModified}\n pointerEvents={isModified ? 'none' : undefined}\n tabIndex={isModified ? -1 : undefined}\n >\n {formatMessage({\n id: 'content-manager.preview.panel.button',\n defaultMessage: 'Open preview',\n })}\n </Button>\n </Box>\n </ConditionalTooltip>\n ),\n };\n};\n\nexport { PreviewSidePanel };\n"],"names":["ConditionalTooltip","isShown","label","children","_jsx","Tooltip","open","undefined","PreviewSidePanel","model","documentId","document","formatMessage","useIntl","trackUsage","useTracking","pathname","useLocation","query","useQueryParams","isModified","useForm","state","modified","isUnsaved","Boolean","id","title","defaultMessage","data","error","useGetPreviewUrlQuery","params","contentType","locale","status","skip","isPreviewEnabled","setIsPreviewEnabled","React","useState","previewUrl","url","useEffect","name","content","Button","variant","tag","Link","to","target","rel","width","trackNavigation","destinationPathname","replace","from","Box","cursor","search","stringify","encode","onClick","disabled","pointerEvents","tabIndex"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import 'react';
|
|
2
|
+
import * as React from 'react';
|
|
3
3
|
import { useTracking, useQueryParams, useForm } from '@strapi/admin/strapi-admin';
|
|
4
4
|
import { Button, Box, Tooltip } from '@strapi/design-system';
|
|
5
5
|
import { stringify } from 'qs';
|
|
@@ -7,14 +7,17 @@ import { useIntl } from 'react-intl';
|
|
|
7
7
|
import { useLocation, Link } from 'react-router-dom';
|
|
8
8
|
import { useGetPreviewUrlQuery } from '../services/preview.mjs';
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Always keeps the Tooltip (and therefore the children subtree) mounted: conditionally wrapping
|
|
12
|
+
* would remount the child button whenever `isShown` flips, and a click landing during that swap
|
|
13
|
+
* is silently lost. When the tooltip must not show, it is force-closed via the controlled `open`
|
|
14
|
+
* prop instead of being removed from the tree.
|
|
15
|
+
*/ const ConditionalTooltip = ({ isShown, label, children })=>{
|
|
16
|
+
return /*#__PURE__*/ jsx(Tooltip, {
|
|
17
|
+
label: label,
|
|
18
|
+
open: isShown ? undefined : false,
|
|
19
|
+
children: children
|
|
20
|
+
});
|
|
18
21
|
};
|
|
19
22
|
const PreviewSidePanel = ({ model, documentId, document })=>{
|
|
20
23
|
const { formatMessage } = useIntl();
|
|
@@ -44,6 +47,20 @@ const PreviewSidePanel = ({ model, documentId, document })=>{
|
|
|
44
47
|
{
|
|
45
48
|
skip: isUnsaved
|
|
46
49
|
});
|
|
50
|
+
/**
|
|
51
|
+
* The query args above change while the document settles (locale/status resolve after load and
|
|
52
|
+
* on save/publish), and each change is a new cache entry with transiently empty data. Latch
|
|
53
|
+
* enablement once a URL has been seen so the "Open preview" button doesn't unmount and remount
|
|
54
|
+
* mid-interaction — a click landing during such a swap is silently lost.
|
|
55
|
+
*/ const [isPreviewEnabled, setIsPreviewEnabled] = React.useState(false);
|
|
56
|
+
const previewUrl = data?.data?.url;
|
|
57
|
+
React.useEffect(()=>{
|
|
58
|
+
if (previewUrl) {
|
|
59
|
+
setIsPreviewEnabled(true);
|
|
60
|
+
}
|
|
61
|
+
}, [
|
|
62
|
+
previewUrl
|
|
63
|
+
]);
|
|
47
64
|
if (isUnsaved) {
|
|
48
65
|
return null;
|
|
49
66
|
}
|
|
@@ -66,7 +83,7 @@ const PreviewSidePanel = ({ model, documentId, document })=>{
|
|
|
66
83
|
})
|
|
67
84
|
};
|
|
68
85
|
}
|
|
69
|
-
if (!data?.data?.url || error) {
|
|
86
|
+
if (!isPreviewEnabled && (!data?.data?.url || error)) {
|
|
70
87
|
return null;
|
|
71
88
|
}
|
|
72
89
|
const trackNavigation = ()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PreviewSidePanel.mjs","sources":["../../../../admin/src/preview/components/PreviewSidePanel.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useQueryParams, useTracking, useForm } from '@strapi/admin/strapi-admin';\nimport { Box, Button, Tooltip, type TooltipProps } from '@strapi/design-system';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { Link, useLocation } from 'react-router-dom';\n\nimport { useGetPreviewUrlQuery } from '../services/preview';\n\nimport type { PanelComponent } from '@strapi/content-manager/strapi-admin';\nimport type { UID } from '@strapi/types';\n\ninterface ConditionalTooltipProps {\n isShown: boolean;\n label: TooltipProps['label'];\n children: React.ReactNode;\n}\n\nconst ConditionalTooltip = ({ isShown, label, children }: ConditionalTooltipProps) => {\n
|
|
1
|
+
{"version":3,"file":"PreviewSidePanel.mjs","sources":["../../../../admin/src/preview/components/PreviewSidePanel.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { useQueryParams, useTracking, useForm } from '@strapi/admin/strapi-admin';\nimport { Box, Button, Tooltip, type TooltipProps } from '@strapi/design-system';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { Link, useLocation } from 'react-router-dom';\n\nimport { useGetPreviewUrlQuery } from '../services/preview';\n\nimport type { PanelComponent } from '@strapi/content-manager/strapi-admin';\nimport type { UID } from '@strapi/types';\n\ninterface ConditionalTooltipProps {\n isShown: boolean;\n label: TooltipProps['label'];\n children: React.ReactNode;\n}\n\n/**\n * Always keeps the Tooltip (and therefore the children subtree) mounted: conditionally wrapping\n * would remount the child button whenever `isShown` flips, and a click landing during that swap\n * is silently lost. When the tooltip must not show, it is force-closed via the controlled `open`\n * prop instead of being removed from the tree.\n */\nconst ConditionalTooltip = ({ isShown, label, children }: ConditionalTooltipProps) => {\n return (\n <Tooltip label={label} open={isShown ? undefined : false}>\n {children}\n </Tooltip>\n );\n};\n\nconst PreviewSidePanel: PanelComponent = ({ model, documentId, document }) => {\n const { formatMessage } = useIntl();\n const { trackUsage } = useTracking();\n const { pathname } = useLocation();\n const [{ query }] = useQueryParams();\n const isModified = useForm('PreviewSidePanel', (state) => state.modified);\n const isUnsaved = Boolean(!document || !document.id);\n\n const title = formatMessage({\n id: 'content-manager.preview.panel.title',\n defaultMessage: 'Preview',\n });\n\n /**\n * The preview URL isn't used in this component, we just fetch it to know if preview is enabled\n * for the content type. If it's not, the panel is not displayed. If it is, we display a link to\n * /preview, and the URL will already be loaded in the RTK query cache.\n */\n const { data, error } = useGetPreviewUrlQuery(\n {\n params: {\n contentType: model as UID.ContentType,\n },\n query: {\n documentId,\n locale: document?.locale,\n status: document?.status,\n },\n },\n // Don't bother making the request since we won't show any UI\n { skip: isUnsaved }\n );\n\n /**\n * The query args above change while the document settles (locale/status resolve after load and\n * on save/publish), and each change is a new cache entry with transiently empty data. Latch\n * enablement once a URL has been seen so the \"Open preview\" button doesn't unmount and remount\n * mid-interaction — a click landing during such a swap is silently lost.\n */\n const [isPreviewEnabled, setIsPreviewEnabled] = React.useState(false);\n const previewUrl = data?.data?.url;\n React.useEffect(() => {\n if (previewUrl) {\n setIsPreviewEnabled(true);\n }\n }, [previewUrl]);\n\n if (isUnsaved) {\n return null;\n }\n\n // Preview was not configured but not disabled either (otherwise it would be a success 204).\n // So we encourage the user to set it up.\n if (error && error.name === 'NotFoundError') {\n return {\n title,\n content: (\n <Button\n variant=\"tertiary\"\n tag={Link}\n to=\"https://docs.strapi.io/cms/features/preview\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n width=\"100%\"\n >\n {formatMessage({\n id: 'content-manager.preview.panel.button-configuration',\n defaultMessage: 'Set up preview',\n })}\n </Button>\n ),\n };\n }\n\n if (!isPreviewEnabled && (!data?.data?.url || error)) {\n return null;\n }\n\n const trackNavigation = () => {\n // Append /preview to the current URL\n const destinationPathname = pathname.replace(/\\/$/, '') + '/preview';\n trackUsage('willNavigate', { from: pathname, to: destinationPathname });\n };\n\n return {\n title,\n content: (\n <ConditionalTooltip\n label={formatMessage({\n id: 'content-manager.preview.panel.button-disabled-tooltip',\n defaultMessage: 'Please save to open the preview',\n })}\n isShown={isModified}\n >\n <Box cursor=\"not-allowed\" width=\"100%\">\n <Button\n variant=\"tertiary\"\n tag={Link}\n to={{ pathname: 'preview', search: stringify(query, { encode: false }) }}\n onClick={trackNavigation}\n width=\"100%\"\n disabled={isModified}\n pointerEvents={isModified ? 'none' : undefined}\n tabIndex={isModified ? -1 : undefined}\n >\n {formatMessage({\n id: 'content-manager.preview.panel.button',\n defaultMessage: 'Open preview',\n })}\n </Button>\n </Box>\n </ConditionalTooltip>\n ),\n };\n};\n\nexport { PreviewSidePanel };\n"],"names":["ConditionalTooltip","isShown","label","children","_jsx","Tooltip","open","undefined","PreviewSidePanel","model","documentId","document","formatMessage","useIntl","trackUsage","useTracking","pathname","useLocation","query","useQueryParams","isModified","useForm","state","modified","isUnsaved","Boolean","id","title","defaultMessage","data","error","useGetPreviewUrlQuery","params","contentType","locale","status","skip","isPreviewEnabled","setIsPreviewEnabled","React","useState","previewUrl","url","useEffect","name","content","Button","variant","tag","Link","to","target","rel","width","trackNavigation","destinationPathname","replace","from","Box","cursor","search","stringify","encode","onClick","disabled","pointerEvents","tabIndex"],"mappings":";;;;;;;;;AAmBA;;;;;IAMA,MAAMA,qBAAqB,CAAC,EAAEC,OAAO,EAAEC,KAAK,EAAEC,QAAQ,EAA2B,GAAA;AAC/E,IAAA,qBACEC,GAAA,CAACC,OAAAA,EAAAA;QAAQH,KAAAA,EAAOA,KAAAA;AAAOI,QAAAA,IAAAA,EAAML,UAAUM,SAAAA,GAAY,KAAA;AAChDJ,QAAAA,QAAAA,EAAAA;;AAGP,CAAA;AAEA,MAAMK,gBAAAA,GAAmC,CAAC,EAAEC,KAAK,EAAEC,UAAU,EAAEC,QAAQ,EAAE,GAAA;IACvE,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;IAC1B,MAAM,EAAEC,UAAU,EAAE,GAAGC,WAAAA,EAAAA;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC,WAAAA,EAAAA;AACrB,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,cAAAA,EAAAA;AACpB,IAAA,MAAMC,aAAaC,OAAAA,CAAQ,kBAAA,EAAoB,CAACC,KAAAA,GAAUA,MAAMC,QAAQ,CAAA;AACxE,IAAA,MAAMC,YAAYC,OAAAA,CAAQ,CAACd,QAAAA,IAAY,CAACA,SAASe,EAAE,CAAA;AAEnD,IAAA,MAAMC,QAAQf,aAAAA,CAAc;QAC1Bc,EAAAA,EAAI,qCAAA;QACJE,cAAAA,EAAgB;AAClB,KAAA,CAAA;AAEA;;;;AAIC,MACD,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAGC,qBAAAA,CACtB;QACEC,MAAAA,EAAQ;YACNC,WAAAA,EAAaxB;AACf,SAAA;QACAS,KAAAA,EAAO;AACLR,YAAAA,UAAAA;AACAwB,YAAAA,MAAAA,EAAQvB,QAAAA,EAAUuB,MAAAA;AAClBC,YAAAA,MAAAA,EAAQxB,QAAAA,EAAUwB;AACpB;AACF,KAAA;AAEA,IAAA;QAAEC,IAAAA,EAAMZ;AAAU,KAAA,CAAA;AAGpB;;;;;AAKC,MACD,MAAM,CAACa,gBAAAA,EAAkBC,oBAAoB,GAAGC,KAAAA,CAAMC,QAAQ,CAAC,KAAA,CAAA;IAC/D,MAAMC,UAAAA,GAAaZ,MAAMA,IAAAA,EAAMa,GAAAA;AAC/BH,IAAAA,KAAAA,CAAMI,SAAS,CAAC,IAAA;AACd,QAAA,IAAIF,UAAAA,EAAY;YACdH,mBAAAA,CAAoB,IAAA,CAAA;AACtB,QAAA;IACF,CAAA,EAAG;AAACG,QAAAA;AAAW,KAAA,CAAA;AAEf,IAAA,IAAIjB,SAAAA,EAAW;QACb,OAAO,IAAA;AACT,IAAA;;;AAIA,IAAA,IAAIM,KAAAA,IAASA,KAAAA,CAAMc,IAAI,KAAK,eAAA,EAAiB;QAC3C,OAAO;AACLjB,YAAAA,KAAAA;AACAkB,YAAAA,OAAAA,gBACEzC,GAAA,CAAC0C,MAAAA,EAAAA;gBACCC,OAAAA,EAAQ,UAAA;gBACRC,GAAAA,EAAKC,IAAAA;gBACLC,EAAAA,EAAG,6CAAA;gBACHC,MAAAA,EAAO,QAAA;gBACPC,GAAAA,EAAI,qBAAA;gBACJC,KAAAA,EAAM,MAAA;0BAELzC,aAAAA,CAAc;oBACbc,EAAAA,EAAI,oDAAA;oBACJE,cAAAA,EAAgB;AAClB,iBAAA;;AAGN,SAAA;AACF,IAAA;IAEA,IAAI,CAACS,qBAAqB,CAACR,MAAMA,IAAAA,EAAMa,GAAAA,IAAOZ,KAAI,CAAA,EAAI;QACpD,OAAO,IAAA;AACT,IAAA;AAEA,IAAA,MAAMwB,eAAAA,GAAkB,IAAA;;AAEtB,QAAA,MAAMC,mBAAAA,GAAsBvC,QAAAA,CAASwC,OAAO,CAAC,OAAO,EAAA,CAAA,GAAM,UAAA;AAC1D1C,QAAAA,UAAAA,CAAW,cAAA,EAAgB;YAAE2C,IAAAA,EAAMzC,QAAAA;YAAUkC,EAAAA,EAAIK;AAAoB,SAAA,CAAA;AACvE,IAAA,CAAA;IAEA,OAAO;AACL5B,QAAAA,KAAAA;AACAkB,QAAAA,OAAAA,gBACEzC,GAAA,CAACJ,kBAAAA,EAAAA;AACCE,YAAAA,KAAAA,EAAOU,aAAAA,CAAc;gBACnBc,EAAAA,EAAI,uDAAA;gBACJE,cAAAA,EAAgB;AAClB,aAAA,CAAA;YACA3B,OAAAA,EAASmB,UAAAA;AAET,YAAA,QAAA,gBAAAhB,GAAA,CAACsD,GAAAA,EAAAA;gBAAIC,MAAAA,EAAO,aAAA;gBAAcN,KAAAA,EAAM,MAAA;AAC9B,gBAAA,QAAA,gBAAAjD,GAAA,CAAC0C,MAAAA,EAAAA;oBACCC,OAAAA,EAAQ,UAAA;oBACRC,GAAAA,EAAKC,IAAAA;oBACLC,EAAAA,EAAI;wBAAElC,QAAAA,EAAU,SAAA;AAAW4C,wBAAAA,MAAAA,EAAQC,UAAU3C,KAAAA,EAAO;4BAAE4C,MAAAA,EAAQ;AAAM,yBAAA;AAAG,qBAAA;oBACvEC,OAAAA,EAAST,eAAAA;oBACTD,KAAAA,EAAM,MAAA;oBACNW,QAAAA,EAAU5C,UAAAA;AACV6C,oBAAAA,aAAAA,EAAe7C,aAAa,MAAA,GAASb,SAAAA;oBACrC2D,QAAAA,EAAU9C,UAAAA,GAAa,EAAC,GAAIb,SAAAA;8BAE3BK,aAAAA,CAAc;wBACbc,EAAAA,EAAI,sCAAA;wBACJE,cAAAA,EAAgB;AAClB,qBAAA;;;;AAKV,KAAA;AACF;;;;"}
|
|
@@ -5,10 +5,18 @@ import { type EditFieldLayout } from '../../../../../hooks/useDocumentLayout';
|
|
|
5
5
|
import { RelationResult } from '../../../../../services/relations';
|
|
6
6
|
type RelationPosition = (Pick<RelationResult, 'status' | 'locale'> & {
|
|
7
7
|
before: string;
|
|
8
|
+
start?: never;
|
|
8
9
|
end?: never;
|
|
9
10
|
}) | {
|
|
11
|
+
start: boolean;
|
|
12
|
+
before?: never;
|
|
13
|
+
end?: never;
|
|
14
|
+
status?: never;
|
|
15
|
+
locale?: never;
|
|
16
|
+
} | {
|
|
10
17
|
end: boolean;
|
|
11
18
|
before?: never;
|
|
19
|
+
start?: never;
|
|
12
20
|
status?: never;
|
|
13
21
|
locale?: never;
|
|
14
22
|
};
|
|
@@ -31,7 +39,7 @@ interface RelationsFieldProps extends Omit<Extract<EditFieldLayout, {
|
|
|
31
39
|
}
|
|
32
40
|
export interface RelationsFormValue {
|
|
33
41
|
connect?: Relation[];
|
|
34
|
-
disconnect?: Pick<Relation, 'id'>[];
|
|
42
|
+
disconnect?: Pick<Relation, 'id' | 'status'>[];
|
|
35
43
|
}
|
|
36
44
|
declare const FlexWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<Omit<import("@strapi/design-system").FlexProps<"div">, "ref"> & React.RefAttributes<unknown>, "ref"> & {
|
|
37
45
|
ref?: ((instance: unknown) => void | React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | React.RefObject<unknown> | null | undefined;
|
|
@@ -7,6 +7,7 @@ export interface DraftRelationCounts {
|
|
|
7
7
|
export declare const EMPTY_DRAFT_RELATION_COUNTS: DraftRelationCounts;
|
|
8
8
|
export declare const isBidirectionalManyToMany: (attribute: Schema["attributes"][string]) => boolean;
|
|
9
9
|
export declare const mergeDraftRelationCounts: (left: DraftRelationCounts, right: DraftRelationCounts) => DraftRelationCounts;
|
|
10
|
+
export declare const clampDraftRelationCounts: (counts: DraftRelationCounts) => DraftRelationCounts;
|
|
10
11
|
export declare const normalizeDraftRelationCounts: (payload: unknown) => DraftRelationCounts;
|
|
11
12
|
export declare const resolveDraftRelationCounts: (documentId: string | undefined, modified: boolean, localCounts: DraftRelationCounts, serverCounts: DraftRelationCounts) => DraftRelationCounts;
|
|
12
13
|
export interface DraftRelationsPublishState {
|
|
@@ -81,6 +81,16 @@ const createHomepageService = ({ strapi })=>{
|
|
|
81
81
|
return acc;
|
|
82
82
|
}, []);
|
|
83
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* Homepage widgets expect JSON-safe ISO strings. Returning `Date` objects can
|
|
86
|
+
* serialize as `{}` after spread/clone (see https://github.com/strapi/strapi/issues/27013).
|
|
87
|
+
*/ const toIsoDateString = (value)=>{
|
|
88
|
+
if (value == null || value === '') {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const date = value instanceof Date ? value : new Date(String(value));
|
|
92
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
93
|
+
};
|
|
84
94
|
const formatDocuments = (documents, meta, populate)=>{
|
|
85
95
|
return documents.map((document)=>{
|
|
86
96
|
const additionalFields = populate?.reduce((acc, key)=>{
|
|
@@ -90,13 +100,14 @@ const createHomepageService = ({ strapi })=>{
|
|
|
90
100
|
return {
|
|
91
101
|
documentId: document.documentId,
|
|
92
102
|
locale: document.locale ?? null,
|
|
93
|
-
updatedAt: new Date(document.updatedAt),
|
|
94
103
|
title: document[meta.mainField ?? 'documentId'],
|
|
95
|
-
publishedAt: meta.hasDraftAndPublish && document.publishedAt ? new Date(document.publishedAt) : null,
|
|
96
104
|
contentTypeUid: meta.uid,
|
|
97
105
|
contentTypeDisplayName: meta.contentType.info.displayName,
|
|
98
106
|
kind: meta.contentType.kind,
|
|
99
|
-
...additionalFields
|
|
107
|
+
...additionalFields,
|
|
108
|
+
// Keep dates last so populate cannot overwrite with non-JSON-safe values
|
|
109
|
+
updatedAt: toIsoDateString(document.updatedAt) ?? '',
|
|
110
|
+
publishedAt: meta.hasDraftAndPublish && document.publishedAt ? toIsoDateString(document.publishedAt) : null
|
|
100
111
|
};
|
|
101
112
|
});
|
|
102
113
|
};
|
|
@@ -154,19 +165,25 @@ const createHomepageService = ({ strapi })=>{
|
|
|
154
165
|
}, populate);
|
|
155
166
|
}));
|
|
156
167
|
return recentDocuments.flat().sort((a, b)=>{
|
|
168
|
+
// ISO-8601 strings compare lexicographically in chronological order
|
|
169
|
+
const compareIso = (left, right, direction)=>{
|
|
170
|
+
if (left < right) return -1 * direction;
|
|
171
|
+
if (left > right) return 1 * direction;
|
|
172
|
+
return 0;
|
|
173
|
+
};
|
|
157
174
|
switch(additionalQueryParams?.sort){
|
|
158
175
|
case 'publishedAt:desc':
|
|
159
176
|
if (!a.publishedAt || !b.publishedAt) return 0;
|
|
160
|
-
return b.publishedAt
|
|
177
|
+
return compareIso(a.publishedAt, b.publishedAt, -1);
|
|
161
178
|
case 'publishedAt:asc':
|
|
162
179
|
if (!a.publishedAt || !b.publishedAt) return 0;
|
|
163
|
-
return a.publishedAt
|
|
180
|
+
return compareIso(a.publishedAt, b.publishedAt, 1);
|
|
164
181
|
case 'updatedAt:desc':
|
|
165
182
|
if (!a.updatedAt || !b.updatedAt) return 0;
|
|
166
|
-
return b.updatedAt
|
|
183
|
+
return compareIso(a.updatedAt, b.updatedAt, -1);
|
|
167
184
|
case 'updatedAt:asc':
|
|
168
185
|
if (!a.updatedAt || !b.updatedAt) return 0;
|
|
169
|
-
return a.updatedAt
|
|
186
|
+
return compareIso(a.updatedAt, b.updatedAt, 1);
|
|
170
187
|
default:
|
|
171
188
|
return 0;
|
|
172
189
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"homepage.js","sources":["../../../../server/src/homepage/services/homepage.ts"],"sourcesContent":["/* eslint-disable func-names */\nimport type { Core, Modules, Schema } from '@strapi/types';\nimport { contentTypes } from '@strapi/utils';\n\nimport type {\n GetCountDocuments,\n GetRecentDocuments,\n RecentDocument,\n} from '../../../../shared/contracts/homepage';\n\nimport {\n buildHomepageQueryFields,\n compactSanitizedFields,\n resolveReadableMainField,\n resolveTitleField,\n} from './homepage-query-utils';\n\nconst createHomepageService = ({ strapi }: { strapi: Core.Strapi }) => {\n const MAX_DOCUMENTS = 4;\n\n const metadataService = strapi.plugin('content-manager').service('document-metadata');\n const permissionService = strapi.admin.services.permission;\n\n const getRegisteredContentType = (uid: RecentDocument['contentTypeUid']) => {\n const contentType = strapi.contentTypes[uid];\n\n if (contentType === undefined) {\n strapi.log.warn(\n `Skipping homepage content type \"${uid}\" because it is no longer registered.`\n );\n return undefined;\n }\n\n return contentType;\n };\n\n type ContentTypeConfiguration = {\n uid: RecentDocument['contentTypeUid'];\n settings: { mainField: string };\n };\n const getConfiguration = async (\n contentTypeUids: RecentDocument['contentTypeUid'][]\n ): Promise<ContentTypeConfiguration[]> => {\n /**\n * Don't use the strapi.store util because we need to make\n * more precise queries than exact key matches, in order to make as few queries as possible.\n */\n const coreStore = strapi.db.query('strapi::core-store');\n const rawConfigurations = await coreStore.findMany({\n where: {\n key: {\n $in: contentTypeUids.map(\n (contentType) => `plugin_content_manager_configuration_content_types::${contentType}`\n ),\n },\n },\n });\n\n return rawConfigurations.map((rawConfiguration) => {\n return JSON.parse(rawConfiguration.value);\n });\n };\n\n const getPermittedContentTypes = async () => {\n const readPermissions: Modules.Permissions.PermissionRule[] = await permissionService.findMany({\n where: {\n role: { users: { id: strapi.requestContext.get()?.state?.user.id } },\n action: 'plugin::content-manager.explorer.read',\n },\n });\n\n // Deduplicate subjects using a Set: the JOIN across permission -> role -> users produces one row\n // per role the user belongs to, so a multi-role user gets duplicate subjects.\n // Using a Set collapses them to unique content type UID.\n return [\n ...new Set(\n readPermissions\n .map((permission) => permission.subject)\n .filter((subject): subject is RecentDocument['contentTypeUid'] => {\n if (!subject) {\n return false;\n }\n\n const contentType = strapi.contentTypes[subject as keyof typeof strapi.contentTypes];\n const contentTypeOptions = contentType?.pluginOptions?.['content-manager'] as\n | { visible?: boolean }\n | undefined;\n\n return contentTypeOptions?.visible !== false;\n })\n ),\n ];\n };\n\n type ContentTypeMeta = {\n fields: string[];\n mainField: string;\n contentType: Schema.ContentType;\n hasDraftAndPublish: boolean;\n uid: RecentDocument['contentTypeUid'];\n };\n\n const permissionCheckerService = strapi.plugin('content-manager').service('permission-checker');\n const getPermissionChecker = (uid: string) =>\n permissionCheckerService.create({\n userAbility: strapi.requestContext.get()?.state.userAbility,\n model: uid,\n });\n\n const getContentTypesMeta = (\n allowedContentTypeUids: RecentDocument['contentTypeUid'][],\n configurations: ContentTypeConfiguration[]\n ): ContentTypeMeta[] => {\n return allowedContentTypeUids.reduce<ContentTypeMeta[]>((acc, uid) => {\n const configuration = configurations.find((config) => config.uid === uid);\n const contentType = getRegisteredContentType(uid);\n\n if (contentType === undefined) {\n return acc;\n }\n\n const mainField = resolveReadableMainField(\n contentType,\n configuration,\n getPermissionChecker(uid)\n );\n const fields = buildHomepageQueryFields(contentType, mainField);\n const hasDraftAndPublish = contentTypes.hasDraftAndPublish(contentType);\n\n acc.push({\n fields,\n mainField,\n contentType,\n hasDraftAndPublish,\n uid,\n });\n\n return acc;\n }, []);\n };\n\n const formatDocuments = (\n documents: Modules.Documents.AnyDocument[],\n meta: ContentTypeMeta,\n populate?: string[]\n ) => {\n return documents.map((document) => {\n const additionalFields =\n populate?.reduce(\n (acc, key) => {\n acc[key] = document[key];\n return acc;\n },\n {} as Record<string, any>\n ) || {};\n return {\n documentId: document.documentId,\n locale: document.locale ?? null,\n updatedAt: new Date(document.updatedAt),\n title: document[meta.mainField ?? 'documentId'],\n publishedAt:\n meta.hasDraftAndPublish && document.publishedAt ? new Date(document.publishedAt) : null,\n contentTypeUid: meta.uid,\n contentTypeDisplayName: meta.contentType.info.displayName,\n kind: meta.contentType.kind,\n ...additionalFields,\n };\n });\n };\n\n const sanitizeHomepageQuery = async (\n meta: ContentTypeMeta,\n additionalQueryParams?: Record<string, unknown>\n ) => {\n const permissionQuery = await getPermissionChecker(meta.uid).sanitizedQuery.read({\n limit: MAX_DOCUMENTS,\n fields: meta.fields,\n ...additionalQueryParams,\n locale: '*',\n });\n\n const sanitizedFields = compactSanitizedFields(permissionQuery.fields);\n if (sanitizedFields !== undefined) {\n permissionQuery.fields = sanitizedFields;\n }\n\n return {\n permissionQuery,\n titleField: resolveTitleField(meta.mainField, sanitizedFields),\n };\n };\n\n return {\n async addStatusToDocuments(documents: RecentDocument[]): Promise<RecentDocument[]> {\n return Promise.all(\n documents.map(async (recentDocument) => {\n const hasDraftAndPublish = contentTypes.hasDraftAndPublish(\n strapi.contentType(recentDocument.contentTypeUid)\n );\n /**\n * Tries to query the other version of the document if draft and publish is enabled,\n * so that we know when to give the \"modified\" status.\n */\n const { availableStatus } = await metadataService.getMetadata(\n recentDocument.contentTypeUid,\n recentDocument,\n {\n availableStatus: hasDraftAndPublish,\n availableLocales: false,\n }\n );\n const status: RecentDocument['status'] = metadataService.getStatus(\n recentDocument,\n availableStatus\n );\n\n return {\n ...recentDocument,\n status: hasDraftAndPublish ? status : undefined,\n };\n })\n );\n },\n\n async queryLastDocuments(\n additionalQueryParams?: Record<string, unknown>,\n draftAndPublishOnly?: boolean\n ): Promise<RecentDocument[]> {\n const permittedContentTypes = await getPermittedContentTypes();\n const allowedContentTypeUids = draftAndPublishOnly\n ? permittedContentTypes.filter((uid) => {\n const contentType = getRegisteredContentType(uid);\n\n return contentType !== undefined && contentTypes.hasDraftAndPublish(contentType);\n })\n : permittedContentTypes;\n // Fetch the configuration for each content type in a single query\n const configurations = await getConfiguration(allowedContentTypeUids);\n // Get the necessary metadata for the documents\n const contentTypesMeta = getContentTypesMeta(allowedContentTypeUids, configurations);\n\n const recentDocuments = await Promise.all(\n contentTypesMeta.map(async (meta) => {\n const { permissionQuery, titleField } = await sanitizeHomepageQuery(\n meta,\n additionalQueryParams\n );\n\n const docs = await strapi.documents(meta.uid).findMany(permissionQuery);\n const populate = additionalQueryParams?.populate as string[];\n\n return formatDocuments(docs, { ...meta, mainField: titleField }, populate);\n })\n );\n\n return recentDocuments\n .flat()\n .sort((a, b) => {\n switch (additionalQueryParams?.sort) {\n case 'publishedAt:desc':\n if (!a.publishedAt || !b.publishedAt) return 0;\n return b.publishedAt.valueOf() - a.publishedAt.valueOf();\n case 'publishedAt:asc':\n if (!a.publishedAt || !b.publishedAt) return 0;\n return a.publishedAt.valueOf() - b.publishedAt.valueOf();\n case 'updatedAt:desc':\n if (!a.updatedAt || !b.updatedAt) return 0;\n return b.updatedAt.valueOf() - a.updatedAt.valueOf();\n case 'updatedAt:asc':\n if (!a.updatedAt || !b.updatedAt) return 0;\n return a.updatedAt.valueOf() - b.updatedAt.valueOf();\n default:\n return 0;\n }\n })\n .slice(0, MAX_DOCUMENTS);\n },\n\n async getRecentlyPublishedDocuments(): Promise<GetRecentDocuments.Response['data']> {\n const recentlyPublishedDocuments = await this.queryLastDocuments(\n {\n sort: 'publishedAt:desc',\n status: 'published',\n },\n true\n );\n\n return this.addStatusToDocuments(recentlyPublishedDocuments);\n },\n\n async getRecentlyUpdatedDocuments(): Promise<GetRecentDocuments.Response['data']> {\n const recentlyUpdatedDocuments = await this.queryLastDocuments({\n sort: 'updatedAt:desc',\n });\n\n return this.addStatusToDocuments(recentlyUpdatedDocuments);\n },\n\n async getCountDocuments(): Promise<GetCountDocuments.Response['data']> {\n const permittedContentTypes = await getPermittedContentTypes();\n // Fetch the configuration for each content type in a single query\n const configurations = await getConfiguration(permittedContentTypes);\n // Get the necessary metadata for the documents\n const contentTypesMeta = getContentTypesMeta(permittedContentTypes, configurations);\n\n const countDocuments = {\n draft: 0,\n published: 0,\n modified: 0,\n };\n\n await Promise.all(\n contentTypesMeta.map(async (meta) => {\n const strapiDBConnection = strapi.db.connection;\n const tableName = strapi.contentType(meta.uid).collectionName;\n if (!tableName) return;\n\n if (!meta.hasDraftAndPublish) {\n const publishedDocuments = await strapiDBConnection(tableName)\n .countDistinct('document_id as count')\n .first();\n countDocuments.published += Number(publishedDocuments?.count) || 0;\n return;\n }\n\n // Classify each document_id into a single bucket (draft / published / modified)\n // in one pass. Replaces three separate self-join queries that scaled poorly on\n // large tables — see https://github.com/strapi/strapi/issues/25200.\n const classified = strapiDBConnection(tableName)\n .select('document_id')\n .select(\n strapiDBConnection.raw(\n `CASE\n WHEN MAX(CASE WHEN published_at IS NOT NULL THEN 1 ELSE 0 END) = 0\n THEN 'draft'\n WHEN MAX(CASE WHEN published_at IS NULL THEN updated_at END) =\n MAX(CASE WHEN published_at IS NOT NULL THEN updated_at END)\n THEN 'published'\n ELSE 'modified'\n END AS bucket`\n )\n )\n .groupBy('document_id');\n\n const counts = await strapiDBConnection\n .from(classified.as('classified'))\n .select(\n strapiDBConnection.raw(\n `COUNT(CASE WHEN bucket = 'draft' THEN 1 END) AS draft,\n COUNT(CASE WHEN bucket = 'published' THEN 1 END) AS published,\n COUNT(CASE WHEN bucket = 'modified' THEN 1 END) AS modified`\n )\n )\n .first();\n\n countDocuments.draft += Number(counts?.draft) || 0;\n countDocuments.published += Number(counts?.published) || 0;\n countDocuments.modified += Number(counts?.modified) || 0;\n })\n );\n\n return countDocuments;\n },\n };\n};\n\nexport { createHomepageService };\n"],"names":["createHomepageService","strapi","MAX_DOCUMENTS","metadataService","plugin","service","permissionService","admin","services","permission","getRegisteredContentType","uid","contentType","contentTypes","undefined","log","warn","getConfiguration","contentTypeUids","coreStore","db","query","rawConfigurations","findMany","where","key","$in","map","rawConfiguration","JSON","parse","value","getPermittedContentTypes","readPermissions","role","users","id","requestContext","get","state","user","action","Set","subject","filter","contentTypeOptions","pluginOptions","visible","permissionCheckerService","getPermissionChecker","create","userAbility","model","getContentTypesMeta","allowedContentTypeUids","configurations","reduce","acc","configuration","find","config","mainField","resolveReadableMainField","fields","buildHomepageQueryFields","hasDraftAndPublish","push","formatDocuments","documents","meta","populate","document","additionalFields","documentId","locale","updatedAt","Date","title","publishedAt","contentTypeUid","contentTypeDisplayName","info","displayName","kind","sanitizeHomepageQuery","additionalQueryParams","permissionQuery","sanitizedQuery","read","limit","sanitizedFields","compactSanitizedFields","titleField","resolveTitleField","addStatusToDocuments","Promise","all","recentDocument","availableStatus","getMetadata","availableLocales","status","getStatus","queryLastDocuments","draftAndPublishOnly","permittedContentTypes","contentTypesMeta","recentDocuments","docs","flat","sort","a","b","valueOf","slice","getRecentlyPublishedDocuments","recentlyPublishedDocuments","getRecentlyUpdatedDocuments","recentlyUpdatedDocuments","getCountDocuments","countDocuments","draft","published","modified","strapiDBConnection","connection","tableName","collectionName","publishedDocuments","countDistinct","first","Number","count","classified","select","raw","groupBy","counts","from","as"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"homepage.js","sources":["../../../../server/src/homepage/services/homepage.ts"],"sourcesContent":["/* eslint-disable func-names */\nimport type { Core, Modules, Schema } from '@strapi/types';\nimport { contentTypes } from '@strapi/utils';\n\nimport type {\n GetCountDocuments,\n GetRecentDocuments,\n RecentDocument,\n} from '../../../../shared/contracts/homepage';\n\nimport {\n buildHomepageQueryFields,\n compactSanitizedFields,\n resolveReadableMainField,\n resolveTitleField,\n} from './homepage-query-utils';\n\nconst createHomepageService = ({ strapi }: { strapi: Core.Strapi }) => {\n const MAX_DOCUMENTS = 4;\n\n const metadataService = strapi.plugin('content-manager').service('document-metadata');\n const permissionService = strapi.admin.services.permission;\n\n const getRegisteredContentType = (uid: RecentDocument['contentTypeUid']) => {\n const contentType = strapi.contentTypes[uid];\n\n if (contentType === undefined) {\n strapi.log.warn(\n `Skipping homepage content type \"${uid}\" because it is no longer registered.`\n );\n return undefined;\n }\n\n return contentType;\n };\n\n type ContentTypeConfiguration = {\n uid: RecentDocument['contentTypeUid'];\n settings: { mainField: string };\n };\n const getConfiguration = async (\n contentTypeUids: RecentDocument['contentTypeUid'][]\n ): Promise<ContentTypeConfiguration[]> => {\n /**\n * Don't use the strapi.store util because we need to make\n * more precise queries than exact key matches, in order to make as few queries as possible.\n */\n const coreStore = strapi.db.query('strapi::core-store');\n const rawConfigurations = await coreStore.findMany({\n where: {\n key: {\n $in: contentTypeUids.map(\n (contentType) => `plugin_content_manager_configuration_content_types::${contentType}`\n ),\n },\n },\n });\n\n return rawConfigurations.map((rawConfiguration) => {\n return JSON.parse(rawConfiguration.value);\n });\n };\n\n const getPermittedContentTypes = async () => {\n const readPermissions: Modules.Permissions.PermissionRule[] = await permissionService.findMany({\n where: {\n role: { users: { id: strapi.requestContext.get()?.state?.user.id } },\n action: 'plugin::content-manager.explorer.read',\n },\n });\n\n // Deduplicate subjects using a Set: the JOIN across permission -> role -> users produces one row\n // per role the user belongs to, so a multi-role user gets duplicate subjects.\n // Using a Set collapses them to unique content type UID.\n return [\n ...new Set(\n readPermissions\n .map((permission) => permission.subject)\n .filter((subject): subject is RecentDocument['contentTypeUid'] => {\n if (!subject) {\n return false;\n }\n\n const contentType = strapi.contentTypes[subject as keyof typeof strapi.contentTypes];\n const contentTypeOptions = contentType?.pluginOptions?.['content-manager'] as\n | { visible?: boolean }\n | undefined;\n\n return contentTypeOptions?.visible !== false;\n })\n ),\n ];\n };\n\n type ContentTypeMeta = {\n fields: string[];\n mainField: string;\n contentType: Schema.ContentType;\n hasDraftAndPublish: boolean;\n uid: RecentDocument['contentTypeUid'];\n };\n\n const permissionCheckerService = strapi.plugin('content-manager').service('permission-checker');\n const getPermissionChecker = (uid: string) =>\n permissionCheckerService.create({\n userAbility: strapi.requestContext.get()?.state.userAbility,\n model: uid,\n });\n\n const getContentTypesMeta = (\n allowedContentTypeUids: RecentDocument['contentTypeUid'][],\n configurations: ContentTypeConfiguration[]\n ): ContentTypeMeta[] => {\n return allowedContentTypeUids.reduce<ContentTypeMeta[]>((acc, uid) => {\n const configuration = configurations.find((config) => config.uid === uid);\n const contentType = getRegisteredContentType(uid);\n\n if (contentType === undefined) {\n return acc;\n }\n\n const mainField = resolveReadableMainField(\n contentType,\n configuration,\n getPermissionChecker(uid)\n );\n const fields = buildHomepageQueryFields(contentType, mainField);\n const hasDraftAndPublish = contentTypes.hasDraftAndPublish(contentType);\n\n acc.push({\n fields,\n mainField,\n contentType,\n hasDraftAndPublish,\n uid,\n });\n\n return acc;\n }, []);\n };\n\n /**\n * Homepage widgets expect JSON-safe ISO strings. Returning `Date` objects can\n * serialize as `{}` after spread/clone (see https://github.com/strapi/strapi/issues/27013).\n */\n const toIsoDateString = (value: unknown): string | null => {\n if (value == null || value === '') {\n return null;\n }\n\n const date = value instanceof Date ? value : new Date(String(value));\n return Number.isNaN(date.getTime()) ? null : date.toISOString();\n };\n\n const formatDocuments = (\n documents: Modules.Documents.AnyDocument[],\n meta: ContentTypeMeta,\n populate?: string[]\n ) => {\n return documents.map((document) => {\n const additionalFields =\n populate?.reduce(\n (acc, key) => {\n acc[key] = document[key];\n return acc;\n },\n {} as Record<string, any>\n ) || {};\n return {\n documentId: document.documentId,\n locale: document.locale ?? null,\n title: document[meta.mainField ?? 'documentId'],\n contentTypeUid: meta.uid,\n contentTypeDisplayName: meta.contentType.info.displayName,\n kind: meta.contentType.kind,\n ...additionalFields,\n // Keep dates last so populate cannot overwrite with non-JSON-safe values\n updatedAt: toIsoDateString(document.updatedAt) ?? '',\n publishedAt:\n meta.hasDraftAndPublish && document.publishedAt\n ? toIsoDateString(document.publishedAt)\n : null,\n };\n });\n };\n\n const sanitizeHomepageQuery = async (\n meta: ContentTypeMeta,\n additionalQueryParams?: Record<string, unknown>\n ) => {\n const permissionQuery = await getPermissionChecker(meta.uid).sanitizedQuery.read({\n limit: MAX_DOCUMENTS,\n fields: meta.fields,\n ...additionalQueryParams,\n locale: '*',\n });\n\n const sanitizedFields = compactSanitizedFields(permissionQuery.fields);\n if (sanitizedFields !== undefined) {\n permissionQuery.fields = sanitizedFields;\n }\n\n return {\n permissionQuery,\n titleField: resolveTitleField(meta.mainField, sanitizedFields),\n };\n };\n\n return {\n async addStatusToDocuments(documents: RecentDocument[]): Promise<RecentDocument[]> {\n return Promise.all(\n documents.map(async (recentDocument) => {\n const hasDraftAndPublish = contentTypes.hasDraftAndPublish(\n strapi.contentType(recentDocument.contentTypeUid)\n );\n /**\n * Tries to query the other version of the document if draft and publish is enabled,\n * so that we know when to give the \"modified\" status.\n */\n const { availableStatus } = await metadataService.getMetadata(\n recentDocument.contentTypeUid,\n recentDocument,\n {\n availableStatus: hasDraftAndPublish,\n availableLocales: false,\n }\n );\n const status: RecentDocument['status'] = metadataService.getStatus(\n recentDocument,\n availableStatus\n );\n\n return {\n ...recentDocument,\n status: hasDraftAndPublish ? status : undefined,\n };\n })\n );\n },\n\n async queryLastDocuments(\n additionalQueryParams?: Record<string, unknown>,\n draftAndPublishOnly?: boolean\n ): Promise<RecentDocument[]> {\n const permittedContentTypes = await getPermittedContentTypes();\n const allowedContentTypeUids = draftAndPublishOnly\n ? permittedContentTypes.filter((uid) => {\n const contentType = getRegisteredContentType(uid);\n\n return contentType !== undefined && contentTypes.hasDraftAndPublish(contentType);\n })\n : permittedContentTypes;\n // Fetch the configuration for each content type in a single query\n const configurations = await getConfiguration(allowedContentTypeUids);\n // Get the necessary metadata for the documents\n const contentTypesMeta = getContentTypesMeta(allowedContentTypeUids, configurations);\n\n const recentDocuments = await Promise.all(\n contentTypesMeta.map(async (meta) => {\n const { permissionQuery, titleField } = await sanitizeHomepageQuery(\n meta,\n additionalQueryParams\n );\n\n const docs = await strapi.documents(meta.uid).findMany(permissionQuery);\n const populate = additionalQueryParams?.populate as string[];\n\n return formatDocuments(docs, { ...meta, mainField: titleField }, populate);\n })\n );\n\n return recentDocuments\n .flat()\n .sort((a, b) => {\n // ISO-8601 strings compare lexicographically in chronological order\n const compareIso = (left: string, right: string, direction: 1 | -1) => {\n if (left < right) return -1 * direction;\n if (left > right) return 1 * direction;\n return 0;\n };\n\n switch (additionalQueryParams?.sort) {\n case 'publishedAt:desc':\n if (!a.publishedAt || !b.publishedAt) return 0;\n return compareIso(a.publishedAt, b.publishedAt, -1);\n case 'publishedAt:asc':\n if (!a.publishedAt || !b.publishedAt) return 0;\n return compareIso(a.publishedAt, b.publishedAt, 1);\n case 'updatedAt:desc':\n if (!a.updatedAt || !b.updatedAt) return 0;\n return compareIso(a.updatedAt, b.updatedAt, -1);\n case 'updatedAt:asc':\n if (!a.updatedAt || !b.updatedAt) return 0;\n return compareIso(a.updatedAt, b.updatedAt, 1);\n default:\n return 0;\n }\n })\n .slice(0, MAX_DOCUMENTS);\n },\n\n async getRecentlyPublishedDocuments(): Promise<GetRecentDocuments.Response['data']> {\n const recentlyPublishedDocuments = await this.queryLastDocuments(\n {\n sort: 'publishedAt:desc',\n status: 'published',\n },\n true\n );\n\n return this.addStatusToDocuments(recentlyPublishedDocuments);\n },\n\n async getRecentlyUpdatedDocuments(): Promise<GetRecentDocuments.Response['data']> {\n const recentlyUpdatedDocuments = await this.queryLastDocuments({\n sort: 'updatedAt:desc',\n });\n\n return this.addStatusToDocuments(recentlyUpdatedDocuments);\n },\n\n async getCountDocuments(): Promise<GetCountDocuments.Response['data']> {\n const permittedContentTypes = await getPermittedContentTypes();\n // Fetch the configuration for each content type in a single query\n const configurations = await getConfiguration(permittedContentTypes);\n // Get the necessary metadata for the documents\n const contentTypesMeta = getContentTypesMeta(permittedContentTypes, configurations);\n\n const countDocuments = {\n draft: 0,\n published: 0,\n modified: 0,\n };\n\n await Promise.all(\n contentTypesMeta.map(async (meta) => {\n const strapiDBConnection = strapi.db.connection;\n const tableName = strapi.contentType(meta.uid).collectionName;\n if (!tableName) return;\n\n if (!meta.hasDraftAndPublish) {\n const publishedDocuments = await strapiDBConnection(tableName)\n .countDistinct('document_id as count')\n .first();\n countDocuments.published += Number(publishedDocuments?.count) || 0;\n return;\n }\n\n // Classify each document_id into a single bucket (draft / published / modified)\n // in one pass. Replaces three separate self-join queries that scaled poorly on\n // large tables — see https://github.com/strapi/strapi/issues/25200.\n const classified = strapiDBConnection(tableName)\n .select('document_id')\n .select(\n strapiDBConnection.raw(\n `CASE\n WHEN MAX(CASE WHEN published_at IS NOT NULL THEN 1 ELSE 0 END) = 0\n THEN 'draft'\n WHEN MAX(CASE WHEN published_at IS NULL THEN updated_at END) =\n MAX(CASE WHEN published_at IS NOT NULL THEN updated_at END)\n THEN 'published'\n ELSE 'modified'\n END AS bucket`\n )\n )\n .groupBy('document_id');\n\n const counts = await strapiDBConnection\n .from(classified.as('classified'))\n .select(\n strapiDBConnection.raw(\n `COUNT(CASE WHEN bucket = 'draft' THEN 1 END) AS draft,\n COUNT(CASE WHEN bucket = 'published' THEN 1 END) AS published,\n COUNT(CASE WHEN bucket = 'modified' THEN 1 END) AS modified`\n )\n )\n .first();\n\n countDocuments.draft += Number(counts?.draft) || 0;\n countDocuments.published += Number(counts?.published) || 0;\n countDocuments.modified += Number(counts?.modified) || 0;\n })\n );\n\n return countDocuments;\n },\n };\n};\n\nexport { createHomepageService };\n"],"names":["createHomepageService","strapi","MAX_DOCUMENTS","metadataService","plugin","service","permissionService","admin","services","permission","getRegisteredContentType","uid","contentType","contentTypes","undefined","log","warn","getConfiguration","contentTypeUids","coreStore","db","query","rawConfigurations","findMany","where","key","$in","map","rawConfiguration","JSON","parse","value","getPermittedContentTypes","readPermissions","role","users","id","requestContext","get","state","user","action","Set","subject","filter","contentTypeOptions","pluginOptions","visible","permissionCheckerService","getPermissionChecker","create","userAbility","model","getContentTypesMeta","allowedContentTypeUids","configurations","reduce","acc","configuration","find","config","mainField","resolveReadableMainField","fields","buildHomepageQueryFields","hasDraftAndPublish","push","toIsoDateString","date","Date","String","Number","isNaN","getTime","toISOString","formatDocuments","documents","meta","populate","document","additionalFields","documentId","locale","title","contentTypeUid","contentTypeDisplayName","info","displayName","kind","updatedAt","publishedAt","sanitizeHomepageQuery","additionalQueryParams","permissionQuery","sanitizedQuery","read","limit","sanitizedFields","compactSanitizedFields","titleField","resolveTitleField","addStatusToDocuments","Promise","all","recentDocument","availableStatus","getMetadata","availableLocales","status","getStatus","queryLastDocuments","draftAndPublishOnly","permittedContentTypes","contentTypesMeta","recentDocuments","docs","flat","sort","a","b","compareIso","left","right","direction","slice","getRecentlyPublishedDocuments","recentlyPublishedDocuments","getRecentlyUpdatedDocuments","recentlyUpdatedDocuments","getCountDocuments","countDocuments","draft","published","modified","strapiDBConnection","connection","tableName","collectionName","publishedDocuments","countDistinct","first","count","classified","select","raw","groupBy","counts","from","as"],"mappings":";;;;;AAiBA,MAAMA,qBAAAA,GAAwB,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAChE,IAAA,MAAMC,aAAAA,GAAgB,CAAA;AAEtB,IAAA,MAAMC,kBAAkBF,MAAAA,CAAOG,MAAM,CAAC,iBAAA,CAAA,CAAmBC,OAAO,CAAC,mBAAA,CAAA;AACjE,IAAA,MAAMC,oBAAoBL,MAAAA,CAAOM,KAAK,CAACC,QAAQ,CAACC,UAAU;AAE1D,IAAA,MAAMC,2BAA2B,CAACC,GAAAA,GAAAA;AAChC,QAAA,MAAMC,WAAAA,GAAcX,MAAAA,CAAOY,YAAY,CAACF,GAAAA,CAAI;AAE5C,QAAA,IAAIC,gBAAgBE,SAAAA,EAAW;YAC7Bb,MAAAA,CAAOc,GAAG,CAACC,IAAI,CACb,CAAC,gCAAgC,EAAEL,GAAAA,CAAI,qCAAqC,CAAC,CAAA;YAE/E,OAAOG,SAAAA;AACT,QAAA;QAEA,OAAOF,WAAAA;AACT,IAAA,CAAA;AAMA,IAAA,MAAMK,mBAAmB,OACvBC,eAAAA,GAAAA;AAEA;;;AAGC,QACD,MAAMC,SAAAA,GAAYlB,MAAAA,CAAOmB,EAAE,CAACC,KAAK,CAAC,oBAAA,CAAA;AAClC,QAAA,MAAMC,iBAAAA,GAAoB,MAAMH,SAAAA,CAAUI,QAAQ,CAAC;YACjDC,KAAAA,EAAO;gBACLC,GAAAA,EAAK;oBACHC,GAAAA,EAAKR,eAAAA,CAAgBS,GAAG,CACtB,CAACf,cAAgB,CAAC,oDAAoD,EAAEA,WAAAA,CAAAA,CAAa;AAEzF;AACF;AACF,SAAA,CAAA;QAEA,OAAOU,iBAAAA,CAAkBK,GAAG,CAAC,CAACC,gBAAAA,GAAAA;AAC5B,YAAA,OAAOC,IAAAA,CAAKC,KAAK,CAACF,gBAAAA,CAAiBG,KAAK,CAAA;AAC1C,QAAA,CAAA,CAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAMC,wBAAAA,GAA2B,UAAA;AAC/B,QAAA,MAAMC,eAAAA,GAAwD,MAAM3B,iBAAAA,CAAkBiB,QAAQ,CAAC;YAC7FC,KAAAA,EAAO;gBACLU,IAAAA,EAAM;oBAAEC,KAAAA,EAAO;AAAEC,wBAAAA,EAAAA,EAAInC,OAAOoC,cAAc,CAACC,GAAG,EAAA,EAAIC,OAAOC,IAAAA,CAAKJ;AAAG;AAAE,iBAAA;gBACnEK,MAAAA,EAAQ;AACV;AACF,SAAA,CAAA;;;;QAKA,OAAO;eACF,IAAIC,GAAAA,CACLT,eAAAA,CACGN,GAAG,CAAC,CAAClB,UAAAA,GAAeA,UAAAA,CAAWkC,OAAO,CAAA,CACtCC,MAAM,CAAC,CAACD,OAAAA,GAAAA;AACP,gBAAA,IAAI,CAACA,OAAAA,EAAS;oBACZ,OAAO,KAAA;AACT,gBAAA;AAEA,gBAAA,MAAM/B,WAAAA,GAAcX,MAAAA,CAAOY,YAAY,CAAC8B,OAAAA,CAA4C;AACpF,gBAAA,MAAME,kBAAAA,GAAqBjC,WAAAA,EAAakC,aAAAA,GAAgB,iBAAA,CAAkB;AAI1E,gBAAA,OAAOD,oBAAoBE,OAAAA,KAAY,KAAA;AACzC,YAAA,CAAA,CAAA;AAEL,SAAA;AACH,IAAA,CAAA;AAUA,IAAA,MAAMC,2BAA2B/C,MAAAA,CAAOG,MAAM,CAAC,iBAAA,CAAA,CAAmBC,OAAO,CAAC,oBAAA,CAAA;AAC1E,IAAA,MAAM4C,oBAAAA,GAAuB,CAACtC,GAAAA,GAC5BqC,wBAAAA,CAAyBE,MAAM,CAAC;AAC9BC,YAAAA,WAAAA,EAAalD,MAAAA,CAAOoC,cAAc,CAACC,GAAG,IAAIC,KAAAA,CAAMY,WAAAA;YAChDC,KAAAA,EAAOzC;AACT,SAAA,CAAA;IAEF,MAAM0C,mBAAAA,GAAsB,CAC1BC,sBAAAA,EACAC,cAAAA,GAAAA;AAEA,QAAA,OAAOD,sBAAAA,CAAuBE,MAAM,CAAoB,CAACC,GAAAA,EAAK9C,GAAAA,GAAAA;YAC5D,MAAM+C,aAAAA,GAAgBH,eAAeI,IAAI,CAAC,CAACC,MAAAA,GAAWA,MAAAA,CAAOjD,GAAG,KAAKA,GAAAA,CAAAA;AACrE,YAAA,MAAMC,cAAcF,wBAAAA,CAAyBC,GAAAA,CAAAA;AAE7C,YAAA,IAAIC,gBAAgBE,SAAAA,EAAW;gBAC7B,OAAO2C,GAAAA;AACT,YAAA;AAEA,YAAA,MAAMI,SAAAA,GAAYC,2CAAAA,CAChBlD,WAAAA,EACA8C,aAAAA,EACAT,oBAAAA,CAAqBtC,GAAAA,CAAAA,CAAAA;YAEvB,MAAMoD,MAAAA,GAASC,4CAAyBpD,WAAAA,EAAaiD,SAAAA,CAAAA;YACrD,MAAMI,kBAAAA,GAAqBpD,wBAAAA,CAAaoD,kBAAkB,CAACrD,WAAAA,CAAAA;AAE3D6C,YAAAA,GAAAA,CAAIS,IAAI,CAAC;AACPH,gBAAAA,MAAAA;AACAF,gBAAAA,SAAAA;AACAjD,gBAAAA,WAAAA;AACAqD,gBAAAA,kBAAAA;AACAtD,gBAAAA;AACF,aAAA,CAAA;YAEA,OAAO8C,GAAAA;AACT,QAAA,CAAA,EAAG,EAAE,CAAA;AACP,IAAA,CAAA;AAEA;;;MAIA,MAAMU,kBAAkB,CAACpC,KAAAA,GAAAA;QACvB,IAAIA,KAAAA,IAAS,IAAA,IAAQA,KAAAA,KAAU,EAAA,EAAI;YACjC,OAAO,IAAA;AACT,QAAA;AAEA,QAAA,MAAMqC,OAAOrC,KAAAA,YAAiBsC,IAAAA,GAAOtC,KAAAA,GAAQ,IAAIsC,KAAKC,MAAAA,CAAOvC,KAAAA,CAAAA,CAAAA;QAC7D,OAAOwC,MAAAA,CAAOC,KAAK,CAACJ,IAAAA,CAAKK,OAAO,EAAA,CAAA,GAAM,IAAA,GAAOL,KAAKM,WAAW,EAAA;AAC/D,IAAA,CAAA;IAEA,MAAMC,eAAAA,GAAkB,CACtBC,SAAAA,EACAC,IAAAA,EACAC,QAAAA,GAAAA;QAEA,OAAOF,SAAAA,CAAUjD,GAAG,CAAC,CAACoD,QAAAA,GAAAA;AACpB,YAAA,MAAMC,gBAAAA,GACJF,QAAAA,EAAUtB,MAAAA,CACR,CAACC,GAAAA,EAAKhC,GAAAA,GAAAA;AACJgC,gBAAAA,GAAG,CAAChC,GAAAA,CAAI,GAAGsD,QAAQ,CAACtD,GAAAA,CAAI;gBACxB,OAAOgC,GAAAA;YACT,CAAA,EACA,OACG,EAAC;YACR,OAAO;AACLwB,gBAAAA,UAAAA,EAAYF,SAASE,UAAU;gBAC/BC,MAAAA,EAAQH,QAAAA,CAASG,MAAM,IAAI,IAAA;AAC3BC,gBAAAA,KAAAA,EAAOJ,QAAQ,CAACF,IAAAA,CAAKhB,SAAS,IAAI,YAAA,CAAa;AAC/CuB,gBAAAA,cAAAA,EAAgBP,KAAKlE,GAAG;AACxB0E,gBAAAA,sBAAAA,EAAwBR,IAAAA,CAAKjE,WAAW,CAAC0E,IAAI,CAACC,WAAW;gBACzDC,IAAAA,EAAMX,IAAAA,CAAKjE,WAAW,CAAC4E,IAAI;AAC3B,gBAAA,GAAGR,gBAAgB;;gBAEnBS,SAAAA,EAAWtB,eAAAA,CAAgBY,QAAAA,CAASU,SAAS,CAAA,IAAK,EAAA;gBAClDC,WAAAA,EACEb,IAAAA,CAAKZ,kBAAkB,IAAIc,QAAAA,CAASW,WAAW,GAC3CvB,eAAAA,CAAgBY,QAAAA,CAASW,WAAW,CAAA,GACpC;AACR,aAAA;AACF,QAAA,CAAA,CAAA;AACF,IAAA,CAAA;IAEA,MAAMC,qBAAAA,GAAwB,OAC5Bd,IAAAA,EACAe,qBAAAA,GAAAA;QAEA,MAAMC,eAAAA,GAAkB,MAAM5C,oBAAAA,CAAqB4B,IAAAA,CAAKlE,GAAG,CAAA,CAAEmF,cAAc,CAACC,IAAI,CAAC;YAC/EC,KAAAA,EAAO9F,aAAAA;AACP6D,YAAAA,MAAAA,EAAQc,KAAKd,MAAM;AACnB,YAAA,GAAG6B,qBAAqB;YACxBV,MAAAA,EAAQ;AACV,SAAA,CAAA;QAEA,MAAMe,eAAAA,GAAkBC,yCAAAA,CAAuBL,eAAAA,CAAgB9B,MAAM,CAAA;AACrE,QAAA,IAAIkC,oBAAoBnF,SAAAA,EAAW;AACjC+E,YAAAA,eAAAA,CAAgB9B,MAAM,GAAGkC,eAAAA;AAC3B,QAAA;QAEA,OAAO;AACLJ,YAAAA,eAAAA;YACAM,UAAAA,EAAYC,oCAAAA,CAAkBvB,IAAAA,CAAKhB,SAAS,EAAEoC,eAAAA;AAChD,SAAA;AACF,IAAA,CAAA;IAEA,OAAO;AACL,QAAA,MAAMI,sBAAqBzB,SAA2B,EAAA;AACpD,YAAA,OAAO0B,QAAQC,GAAG,CAChB3B,SAAAA,CAAUjD,GAAG,CAAC,OAAO6E,cAAAA,GAAAA;gBACnB,MAAMvC,kBAAAA,GAAqBpD,yBAAaoD,kBAAkB,CACxDhE,OAAOW,WAAW,CAAC4F,eAAepB,cAAc,CAAA,CAAA;AAElD;;;AAGC,cACD,MAAM,EAAEqB,eAAe,EAAE,GAAG,MAAMtG,eAAAA,CAAgBuG,WAAW,CAC3DF,cAAAA,CAAepB,cAAc,EAC7BoB,cAAAA,EACA;oBACEC,eAAAA,EAAiBxC,kBAAAA;oBACjB0C,gBAAAA,EAAkB;AACpB,iBAAA,CAAA;AAEF,gBAAA,MAAMC,MAAAA,GAAmCzG,eAAAA,CAAgB0G,SAAS,CAChEL,cAAAA,EACAC,eAAAA,CAAAA;gBAGF,OAAO;AACL,oBAAA,GAAGD,cAAc;AACjBI,oBAAAA,MAAAA,EAAQ3C,qBAAqB2C,MAAAA,GAAS9F;AACxC,iBAAA;AACF,YAAA,CAAA,CAAA,CAAA;AAEJ,QAAA,CAAA;QAEA,MAAMgG,kBAAAA,CAAAA,CACJlB,qBAA+C,EAC/CmB,mBAA6B,EAAA;AAE7B,YAAA,MAAMC,wBAAwB,MAAMhF,wBAAAA,EAAAA;AACpC,YAAA,MAAMsB,sBAAAA,GAAyByD,mBAAAA,GAC3BC,qBAAAA,CAAsBpE,MAAM,CAAC,CAACjC,GAAAA,GAAAA;AAC5B,gBAAA,MAAMC,cAAcF,wBAAAA,CAAyBC,GAAAA,CAAAA;AAE7C,gBAAA,OAAOC,WAAAA,KAAgBE,SAAAA,IAAaD,wBAAAA,CAAaoD,kBAAkB,CAACrD,WAAAA,CAAAA;YACtE,CAAA,CAAA,GACAoG,qBAAAA;;YAEJ,MAAMzD,cAAAA,GAAiB,MAAMtC,gBAAAA,CAAiBqC,sBAAAA,CAAAA;;YAE9C,MAAM2D,gBAAAA,GAAmB5D,oBAAoBC,sBAAAA,EAAwBC,cAAAA,CAAAA;YAErE,MAAM2D,eAAAA,GAAkB,MAAMZ,OAAAA,CAAQC,GAAG,CACvCU,gBAAAA,CAAiBtF,GAAG,CAAC,OAAOkD,IAAAA,GAAAA;gBAC1B,MAAM,EAAEgB,eAAe,EAAEM,UAAU,EAAE,GAAG,MAAMR,sBAC5Cd,IAAAA,EACAe,qBAAAA,CAAAA;gBAGF,MAAMuB,IAAAA,GAAO,MAAMlH,MAAAA,CAAO2E,SAAS,CAACC,IAAAA,CAAKlE,GAAG,CAAA,CAAEY,QAAQ,CAACsE,eAAAA,CAAAA;AACvD,gBAAA,MAAMf,WAAWc,qBAAAA,EAAuBd,QAAAA;AAExC,gBAAA,OAAOH,gBAAgBwC,IAAAA,EAAM;AAAE,oBAAA,GAAGtC,IAAI;oBAAEhB,SAAAA,EAAWsC;iBAAW,EAAGrB,QAAAA,CAAAA;AACnE,YAAA,CAAA,CAAA,CAAA;AAGF,YAAA,OAAOoC,gBACJE,IAAI,EAAA,CACJC,IAAI,CAAC,CAACC,CAAAA,EAAGC,CAAAA,GAAAA;;gBAER,MAAMC,UAAAA,GAAa,CAACC,IAAAA,EAAcC,KAAAA,EAAeC,SAAAA,GAAAA;AAC/C,oBAAA,IAAIF,IAAAA,GAAOC,KAAAA,EAAO,OAAO,EAAC,GAAIC,SAAAA;oBAC9B,IAAIF,IAAAA,GAAOC,KAAAA,EAAO,OAAO,CAAA,GAAIC,SAAAA;oBAC7B,OAAO,CAAA;AACT,gBAAA,CAAA;AAEA,gBAAA,OAAQ/B,qBAAAA,EAAuByB,IAAAA;oBAC7B,KAAK,kBAAA;wBACH,IAAI,CAACC,EAAE5B,WAAW,IAAI,CAAC6B,CAAAA,CAAE7B,WAAW,EAAE,OAAO,CAAA;AAC7C,wBAAA,OAAO8B,WAAWF,CAAAA,CAAE5B,WAAW,EAAE6B,CAAAA,CAAE7B,WAAW,EAAE,EAAC,CAAA;oBACnD,KAAK,iBAAA;wBACH,IAAI,CAAC4B,EAAE5B,WAAW,IAAI,CAAC6B,CAAAA,CAAE7B,WAAW,EAAE,OAAO,CAAA;AAC7C,wBAAA,OAAO8B,WAAWF,CAAAA,CAAE5B,WAAW,EAAE6B,CAAAA,CAAE7B,WAAW,EAAE,CAAA,CAAA;oBAClD,KAAK,gBAAA;wBACH,IAAI,CAAC4B,EAAE7B,SAAS,IAAI,CAAC8B,CAAAA,CAAE9B,SAAS,EAAE,OAAO,CAAA;AACzC,wBAAA,OAAO+B,WAAWF,CAAAA,CAAE7B,SAAS,EAAE8B,CAAAA,CAAE9B,SAAS,EAAE,EAAC,CAAA;oBAC/C,KAAK,eAAA;wBACH,IAAI,CAAC6B,EAAE7B,SAAS,IAAI,CAAC8B,CAAAA,CAAE9B,SAAS,EAAE,OAAO,CAAA;AACzC,wBAAA,OAAO+B,WAAWF,CAAAA,CAAE7B,SAAS,EAAE8B,CAAAA,CAAE9B,SAAS,EAAE,CAAA,CAAA;AAC9C,oBAAA;wBACE,OAAO,CAAA;AACX;YACF,CAAA,CAAA,CACCmC,KAAK,CAAC,CAAA,EAAG1H,aAAAA,CAAAA;AACd,QAAA,CAAA;QAEA,MAAM2H,6BAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMC,0BAAAA,GAA6B,MAAM,IAAI,CAAChB,kBAAkB,CAC9D;gBACEO,IAAAA,EAAM,kBAAA;gBACNT,MAAAA,EAAQ;aACV,EACA,IAAA,CAAA;YAGF,OAAO,IAAI,CAACP,oBAAoB,CAACyB,0BAAAA,CAAAA;AACnC,QAAA,CAAA;QAEA,MAAMC,2BAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMC,wBAAAA,GAA2B,MAAM,IAAI,CAAClB,kBAAkB,CAAC;gBAC7DO,IAAAA,EAAM;AACR,aAAA,CAAA;YAEA,OAAO,IAAI,CAAChB,oBAAoB,CAAC2B,wBAAAA,CAAAA;AACnC,QAAA,CAAA;QAEA,MAAMC,iBAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMjB,wBAAwB,MAAMhF,wBAAAA,EAAAA;;YAEpC,MAAMuB,cAAAA,GAAiB,MAAMtC,gBAAAA,CAAiB+F,qBAAAA,CAAAA;;YAE9C,MAAMC,gBAAAA,GAAmB5D,oBAAoB2D,qBAAAA,EAAuBzD,cAAAA,CAAAA;AAEpE,YAAA,MAAM2E,cAAAA,GAAiB;gBACrBC,KAAAA,EAAO,CAAA;gBACPC,SAAAA,EAAW,CAAA;gBACXC,QAAAA,EAAU;AACZ,aAAA;AAEA,YAAA,MAAM/B,QAAQC,GAAG,CACfU,gBAAAA,CAAiBtF,GAAG,CAAC,OAAOkD,IAAAA,GAAAA;AAC1B,gBAAA,MAAMyD,kBAAAA,GAAqBrI,MAAAA,CAAOmB,EAAE,CAACmH,UAAU;AAC/C,gBAAA,MAAMC,YAAYvI,MAAAA,CAAOW,WAAW,CAACiE,IAAAA,CAAKlE,GAAG,EAAE8H,cAAc;AAC7D,gBAAA,IAAI,CAACD,SAAAA,EAAW;gBAEhB,IAAI,CAAC3D,IAAAA,CAAKZ,kBAAkB,EAAE;AAC5B,oBAAA,MAAMyE,qBAAqB,MAAMJ,kBAAAA,CAAmBE,WACjDG,aAAa,CAAC,wBACdC,KAAK,EAAA;AACRV,oBAAAA,cAAAA,CAAeE,SAAS,IAAI7D,MAAAA,CAAOmE,kBAAAA,EAAoBG,KAAAA,CAAAA,IAAU,CAAA;AACjE,oBAAA;AACF,gBAAA;;;;gBAKA,MAAMC,UAAAA,GAAaR,kBAAAA,CAAmBE,SAAAA,CAAAA,CACnCO,MAAM,CAAC,aAAA,CAAA,CACPA,MAAM,CACLT,kBAAAA,CAAmBU,GAAG,CACpB,CAAC;;;;;;;6BAOY,CAAC,CAAA,CAAA,CAGjBC,OAAO,CAAC,aAAA,CAAA;AAEX,gBAAA,MAAMC,MAAAA,GAAS,MAAMZ,kBAAAA,CAClBa,IAAI,CAACL,UAAAA,CAAWM,EAAE,CAAC,YAAA,CAAA,CAAA,CACnBL,MAAM,CACLT,kBAAAA,CAAmBU,GAAG,CACpB,CAAC;;AAE2D,4EAAA,CAAC,GAGhEJ,KAAK,EAAA;AAERV,gBAAAA,cAAAA,CAAeC,KAAK,IAAI5D,MAAAA,CAAO2E,MAAAA,EAAQf,KAAAA,CAAAA,IAAU,CAAA;AACjDD,gBAAAA,cAAAA,CAAeE,SAAS,IAAI7D,MAAAA,CAAO2E,MAAAA,EAAQd,SAAAA,CAAAA,IAAc,CAAA;AACzDF,gBAAAA,cAAAA,CAAeG,QAAQ,IAAI9D,MAAAA,CAAO2E,MAAAA,EAAQb,QAAAA,CAAAA,IAAa,CAAA;AACzD,YAAA,CAAA,CAAA,CAAA;YAGF,OAAOH,cAAAA;AACT,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -79,6 +79,16 @@ const createHomepageService = ({ strapi })=>{
|
|
|
79
79
|
return acc;
|
|
80
80
|
}, []);
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Homepage widgets expect JSON-safe ISO strings. Returning `Date` objects can
|
|
84
|
+
* serialize as `{}` after spread/clone (see https://github.com/strapi/strapi/issues/27013).
|
|
85
|
+
*/ const toIsoDateString = (value)=>{
|
|
86
|
+
if (value == null || value === '') {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const date = value instanceof Date ? value : new Date(String(value));
|
|
90
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
91
|
+
};
|
|
82
92
|
const formatDocuments = (documents, meta, populate)=>{
|
|
83
93
|
return documents.map((document)=>{
|
|
84
94
|
const additionalFields = populate?.reduce((acc, key)=>{
|
|
@@ -88,13 +98,14 @@ const createHomepageService = ({ strapi })=>{
|
|
|
88
98
|
return {
|
|
89
99
|
documentId: document.documentId,
|
|
90
100
|
locale: document.locale ?? null,
|
|
91
|
-
updatedAt: new Date(document.updatedAt),
|
|
92
101
|
title: document[meta.mainField ?? 'documentId'],
|
|
93
|
-
publishedAt: meta.hasDraftAndPublish && document.publishedAt ? new Date(document.publishedAt) : null,
|
|
94
102
|
contentTypeUid: meta.uid,
|
|
95
103
|
contentTypeDisplayName: meta.contentType.info.displayName,
|
|
96
104
|
kind: meta.contentType.kind,
|
|
97
|
-
...additionalFields
|
|
105
|
+
...additionalFields,
|
|
106
|
+
// Keep dates last so populate cannot overwrite with non-JSON-safe values
|
|
107
|
+
updatedAt: toIsoDateString(document.updatedAt) ?? '',
|
|
108
|
+
publishedAt: meta.hasDraftAndPublish && document.publishedAt ? toIsoDateString(document.publishedAt) : null
|
|
98
109
|
};
|
|
99
110
|
});
|
|
100
111
|
};
|
|
@@ -152,19 +163,25 @@ const createHomepageService = ({ strapi })=>{
|
|
|
152
163
|
}, populate);
|
|
153
164
|
}));
|
|
154
165
|
return recentDocuments.flat().sort((a, b)=>{
|
|
166
|
+
// ISO-8601 strings compare lexicographically in chronological order
|
|
167
|
+
const compareIso = (left, right, direction)=>{
|
|
168
|
+
if (left < right) return -1 * direction;
|
|
169
|
+
if (left > right) return 1 * direction;
|
|
170
|
+
return 0;
|
|
171
|
+
};
|
|
155
172
|
switch(additionalQueryParams?.sort){
|
|
156
173
|
case 'publishedAt:desc':
|
|
157
174
|
if (!a.publishedAt || !b.publishedAt) return 0;
|
|
158
|
-
return b.publishedAt
|
|
175
|
+
return compareIso(a.publishedAt, b.publishedAt, -1);
|
|
159
176
|
case 'publishedAt:asc':
|
|
160
177
|
if (!a.publishedAt || !b.publishedAt) return 0;
|
|
161
|
-
return a.publishedAt
|
|
178
|
+
return compareIso(a.publishedAt, b.publishedAt, 1);
|
|
162
179
|
case 'updatedAt:desc':
|
|
163
180
|
if (!a.updatedAt || !b.updatedAt) return 0;
|
|
164
|
-
return b.updatedAt
|
|
181
|
+
return compareIso(a.updatedAt, b.updatedAt, -1);
|
|
165
182
|
case 'updatedAt:asc':
|
|
166
183
|
if (!a.updatedAt || !b.updatedAt) return 0;
|
|
167
|
-
return a.updatedAt
|
|
184
|
+
return compareIso(a.updatedAt, b.updatedAt, 1);
|
|
168
185
|
default:
|
|
169
186
|
return 0;
|
|
170
187
|
}
|