@aglyn/plugins-data 1.0.0-beta.221 → 1.0.0-beta.223
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/README.md +9 -6
- package/package.json +10 -10
- package/src/index.d.ts +1 -1
- package/src/index.js +1 -1
- package/src/index.js.map +1 -1
- package/src/lib/artifact/dataset-schema-artifact.server.d.ts +57 -0
- package/src/lib/artifact/dataset-schema-artifact.server.js +308 -0
- package/src/lib/artifact/dataset-schema-artifact.server.js.map +1 -0
- package/src/lib/artifact/dataset-schema.d.ts +136 -0
- package/src/lib/artifact/dataset-schema.js +232 -0
- package/src/lib/artifact/dataset-schema.js.map +1 -0
- package/src/lib/components/dataset-record-dialog.component.d.ts +1 -1
- package/src/lib/components/dataset-record-dialog.component.js +1 -1
- package/src/lib/components/dataset-record-dialog.component.js.map +1 -1
- package/src/lib/components/dataset-record-filter.d.ts +1 -1
- package/src/lib/components/dataset-record-filter.js +2 -2
- package/src/lib/components/dataset-record-filter.js.map +1 -1
- package/src/lib/components/dataset-schema-dialog.component.d.ts +1 -1
- package/src/lib/components/dataset-schema-dialog.component.js +55 -23
- package/src/lib/components/dataset-schema-dialog.component.js.map +1 -1
- package/src/lib/components/host-datasets-card.component.js +91 -403
- package/src/lib/components/host-datasets-card.component.js.map +1 -1
- package/src/lib/constants/bundle-common.d.ts +12 -0
- package/src/lib/constants/bundle-common.js +13 -0
- package/src/lib/constants/bundle-common.js.map +1 -1
- package/src/lib/declarations.console-server.d.ts +16 -1
- package/src/lib/declarations.console-server.js +34 -2
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.server.d.ts +13 -0
- package/src/lib/declarations.server.js +48 -2
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/form-target/dataset-form-record-target.server.js +5 -3
- package/src/lib/form-target/dataset-form-record-target.server.js.map +1 -1
- package/src/lib/model/dataset-models.d.ts +392 -0
- package/src/lib/model/dataset-models.js +806 -0
- package/src/lib/model/dataset-models.js.map +1 -0
- package/src/lib/model/dataset-record-view.d.ts +1 -1
- package/src/lib/model/dataset-record-view.js +1 -2
- package/src/lib/model/dataset-record-view.js.map +1 -1
- package/src/lib/model/dataset-record.js +2 -2
- package/src/lib/model/dataset-record.js.map +1 -1
- package/src/lib/model/datasets.d.ts +162 -0
- package/src/lib/model/datasets.js +227 -0
- package/src/lib/model/datasets.js.map +1 -0
- package/src/lib/plugin.d.ts +4 -0
- package/src/lib/plugin.js +40 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/record-pages/record-page-live-paths.server.d.ts +55 -0
- package/src/lib/record-pages/record-page-live-paths.server.js +83 -0
- package/src/lib/record-pages/record-page-live-paths.server.js.map +1 -0
- package/src/lib/record-pages/record-page-read.server.d.ts +70 -0
- package/src/lib/record-pages/record-page-read.server.js +175 -0
- package/src/lib/record-pages/record-page-read.server.js.map +1 -0
- package/src/lib/record-pages/record-page-resolver.server.d.ts +37 -0
- package/src/lib/record-pages/record-page-resolver.server.js +131 -0
- package/src/lib/record-pages/record-page-resolver.server.js.map +1 -0
- package/src/lib/record-pages/record-page-sitemap.server.d.ts +32 -0
- package/src/lib/record-pages/record-page-sitemap.server.js +102 -0
- package/src/lib/record-pages/record-page-sitemap.server.js.map +1 -0
- package/src/lib/record-pages/record-page-source.d.ts +42 -0
- package/src/lib/record-pages/record-page-source.js +176 -0
- package/src/lib/record-pages/record-page-source.js.map +1 -0
- package/src/lib/record-pages/record-pages-route.d.ts +42 -0
- package/src/lib/record-pages/record-pages-route.js +348 -0
- package/src/lib/record-pages/record-pages-route.js.map +1 -0
- package/src/lib/record-pages/record-pages.d.ts +218 -0
- package/src/lib/record-pages/record-pages.js +351 -0
- package/src/lib/record-pages/record-pages.js.map +1 -0
- package/src/lib/record-pages/record-template-row-chip.component.d.ts +28 -0
- package/src/lib/record-pages/record-template-row-chip.component.js +87 -0
- package/src/lib/record-pages/record-template-row-chip.component.js.map +1 -0
- package/src/lib/record-pages/record-template-section.component.d.ts +30 -0
- package/src/lib/record-pages/record-template-section.component.js +491 -0
- package/src/lib/record-pages/record-template-section.component.js.map +1 -0
- package/src/lib/repeat/dataset-repeat-rows.d.ts +2 -1
- package/src/lib/repeat/dataset-repeat-rows.js +30 -12
- package/src/lib/repeat/dataset-repeat-rows.js.map +1 -1
- package/src/lib/repeat/dataset-repeat-rows.server.d.ts +1 -1
- package/src/lib/repeat/dataset-repeat-rows.server.js +23 -12
- package/src/lib/repeat/dataset-repeat-rows.server.js.map +1 -1
- package/src/lib/repeat/repeat-record-pages.d.ts +48 -0
- package/src/lib/repeat/repeat-record-pages.js +64 -0
- package/src/lib/repeat/repeat-record-pages.js.map +1 -0
- package/src/lib/server/announce-dataset-records.d.ts +1 -1
- package/src/lib/server/announce-dataset-records.js +1 -1
- package/src/lib/server/announce-dataset-records.js.map +1 -1
- package/src/lib/server/api-v1/datasets.js +5 -3
- package/src/lib/server/api-v1/datasets.js.map +1 -1
- package/src/lib/server/dataset-figures.js +2 -2
- package/src/lib/server/dataset-figures.js.map +1 -1
- package/src/lib/server/dataset-live-pages.d.ts +147 -0
- package/src/lib/server/dataset-live-pages.js +436 -0
- package/src/lib/server/dataset-live-pages.js.map +1 -0
- package/src/lib/server/dataset-steps.server.d.ts +45 -0
- package/src/lib/server/dataset-steps.server.js +268 -0
- package/src/lib/server/dataset-steps.server.js.map +1 -0
- package/src/lib/server/datasets-route.d.ts +2 -0
- package/src/lib/server/datasets-route.js +102 -6
- package/src/lib/server/datasets-route.js.map +1 -1
- package/src/lib/server/resolve-dataset.d.ts +40 -0
- package/src/lib/server/resolve-dataset.js +64 -0
- package/src/lib/server/resolve-dataset.js.map +1 -0
- package/src/lib/server.d.ts +3 -2
- package/src/lib/server.js +8 -9
- package/src/lib/server.js.map +1 -1
- package/src/lib/site-bundle/datasets-package.d.ts +42 -0
- package/src/lib/site-bundle/datasets-package.js +95 -0
- package/src/lib/site-bundle/datasets-package.js.map +1 -0
- package/src/lib/site-bundle/datasets-site-bundle.server.js +2 -2
- package/src/lib/site-bundle/datasets-site-bundle.server.js.map +1 -1
- package/src/lib/{model/index.js → tenant-api.d.ts} +12 -7
- package/src/lib/tenant-api.js +30 -0
- package/src/lib/tenant-api.js.map +1 -0
- package/src/lib/transfer/dataset-transfer-key.d.ts +30 -0
- package/src/lib/transfer/dataset-transfer-key.js +31 -0
- package/src/lib/transfer/dataset-transfer-key.js.map +1 -0
- package/src/lib/transfer/dataset-transfer-model.d.ts +146 -0
- package/src/lib/transfer/dataset-transfer-model.js +536 -0
- package/src/lib/transfer/dataset-transfer-model.js.map +1 -0
- package/src/lib/{model/index.d.ts → transfer/dataset-transfer-resource.d.ts} +2 -6
- package/src/lib/transfer/dataset-transfer-resource.js +41 -0
- package/src/lib/transfer/dataset-transfer-resource.js.map +1 -0
- package/src/lib/transfer/dataset-transfer.server.d.ts +69 -0
- package/src/lib/transfer/dataset-transfer.server.js +930 -0
- package/src/lib/transfer/dataset-transfer.server.js.map +1 -0
- package/src/lib/model/dataset-csv.d.ts +0 -36
- package/src/lib/model/dataset-csv.js +0 -85
- package/src/lib/model/dataset-csv.js.map +0 -1
- package/src/lib/model/dataset-io.d.ts +0 -44
- package/src/lib/model/dataset-io.js +0 -85
- package/src/lib/model/dataset-io.js.map +0 -1
- package/src/lib/model/index.js.map +0 -1
- package/src/lib/server/datasets-export-route.d.ts +0 -57
- package/src/lib/server/datasets-export-route.js +0 -229
- package/src/lib/server/datasets-export-route.js.map +0 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { ConsoleHostScreenRowZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins';
|
|
18
|
+
/**
|
|
19
|
+
* A Pages list row's record template chip (AGL-3475): how many record pages the
|
|
20
|
+
* page serves, with the base they live under in its tooltip — what an author
|
|
21
|
+
* scanning the list needs to know about a page that has no address of its own.
|
|
22
|
+
*
|
|
23
|
+
* The site's bindings come from one query however many rows ask, which the
|
|
24
|
+
* Firestore SDK shares between identical listeners; only a row that IS a
|
|
25
|
+
* record template reads anything more, and that is one count.
|
|
26
|
+
*/
|
|
27
|
+
export declare function RecordTemplateRowChip(props: ConsoleHostScreenRowZoneProps): import("react").JSX.Element;
|
|
28
|
+
export default RecordTemplateRowChip;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
+
import { useFirestore, useOrgDataScope } from "@aglyn/tenant-feature-instance";
|
|
19
|
+
import { Chip, Tooltip } from "@mui/material";
|
|
20
|
+
import { collection, doc, getCountFromServer, getDoc } from "firebase/firestore";
|
|
21
|
+
import { useEffect, useState } from "react";
|
|
22
|
+
import { datasetDisplayName } from "../model/datasets.js";
|
|
23
|
+
import { useSiteRecordPageBindings } from "./record-page-source.js";
|
|
24
|
+
/**
|
|
25
|
+
* A Pages list row's record template chip (AGL-3475): how many record pages the
|
|
26
|
+
* page serves, with the base they live under in its tooltip — what an author
|
|
27
|
+
* scanning the list needs to know about a page that has no address of its own.
|
|
28
|
+
*
|
|
29
|
+
* The site's bindings come from one query however many rows ask, which the
|
|
30
|
+
* Firestore SDK shares between identical listeners; only a row that IS a
|
|
31
|
+
* record template reads anything more, and that is one count.
|
|
32
|
+
*/ export function RecordTemplateRowChip(props) {
|
|
33
|
+
const { hostId, orgId, screenId } = props;
|
|
34
|
+
const { bindings } = useSiteRecordPageBindings(hostId);
|
|
35
|
+
const binding = bindings.find((one)=>one.screenId === screenId);
|
|
36
|
+
const firestore = useFirestore();
|
|
37
|
+
const { scope } = useOrgDataScope({
|
|
38
|
+
hostId,
|
|
39
|
+
orgId
|
|
40
|
+
});
|
|
41
|
+
const [summary, setSummary] = useState(null);
|
|
42
|
+
const datasetId = binding == null ? void 0 : binding.datasetId;
|
|
43
|
+
useEffect(()=>{
|
|
44
|
+
if (!datasetId || !scope) {
|
|
45
|
+
setSummary(null);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
let active = true;
|
|
49
|
+
const datasetRef = doc(firestore, scope[0], scope[1], 'datasets', datasetId);
|
|
50
|
+
Promise.all([
|
|
51
|
+
getDoc(datasetRef),
|
|
52
|
+
getCountFromServer(collection(datasetRef, 'records'))
|
|
53
|
+
]).then(([dataset, count])=>{
|
|
54
|
+
if (!active) return;
|
|
55
|
+
setSummary({
|
|
56
|
+
name: datasetDisplayName(dataset.data()),
|
|
57
|
+
count: count.data().count
|
|
58
|
+
});
|
|
59
|
+
}).catch((error)=>{
|
|
60
|
+
// A dataset this viewer may not read still gets its chip, unnumbered.
|
|
61
|
+
console.error(error);
|
|
62
|
+
if (active) setSummary(null);
|
|
63
|
+
});
|
|
64
|
+
return ()=>{
|
|
65
|
+
active = false;
|
|
66
|
+
};
|
|
67
|
+
}, [
|
|
68
|
+
firestore,
|
|
69
|
+
scope,
|
|
70
|
+
datasetId
|
|
71
|
+
]);
|
|
72
|
+
if (!binding) return null;
|
|
73
|
+
// Short, because it shares the name's cell: the count is the news, and the
|
|
74
|
+
// tooltip says where the pages live.
|
|
75
|
+
const label = summary ? `${summary.count} record ${summary.count === 1 ? 'page' : 'pages'}` : 'Record template';
|
|
76
|
+
return /*#__PURE__*/ _jsx(Tooltip, {
|
|
77
|
+
title: `Record template${(summary == null ? void 0 : summary.name) ? ` of ${summary.name}` : ''}: served once per record at /${binding.base}/ and each record's page address`,
|
|
78
|
+
children: /*#__PURE__*/ _jsx(Chip, {
|
|
79
|
+
size: "small",
|
|
80
|
+
variant: "outlined",
|
|
81
|
+
label: label
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export default RecordTemplateRowChip;
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=record-template-row-chip.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/data/src/lib/record-pages/record-template-row-chip.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport type { ConsoleHostScreenRowZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { useFirestore, useOrgDataScope } from '@aglyn/tenant-feature-instance'\nimport { Chip, Tooltip } from '@mui/material'\nimport { collection, doc, getCountFromServer, getDoc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport { datasetDisplayName, type HostDataset } from '../model/datasets'\nimport { useSiteRecordPageBindings } from './record-page-source'\n\n/**\n * A Pages list row's record template chip (AGL-3475): how many record pages the\n * page serves, with the base they live under in its tooltip — what an author\n * scanning the list needs to know about a page that has no address of its own.\n *\n * The site's bindings come from one query however many rows ask, which the\n * Firestore SDK shares between identical listeners; only a row that IS a\n * record template reads anything more, and that is one count.\n */\nexport function RecordTemplateRowChip(props: ConsoleHostScreenRowZoneProps) {\n const { hostId, orgId, screenId } = props\n const { bindings } = useSiteRecordPageBindings(hostId)\n const binding = bindings.find((one) => one.screenId === screenId)\n const firestore = useFirestore()\n const { scope } = useOrgDataScope({ hostId, orgId })\n const [summary, setSummary] = useState<{ name: string; count: number } | null>(null)\n const datasetId = binding?.datasetId\n\n useEffect(() => {\n if (!datasetId || !scope) {\n setSummary(null)\n return undefined\n }\n let active = true\n const datasetRef = doc(firestore, scope[0], scope[1], 'datasets', datasetId)\n Promise.all([getDoc(datasetRef), getCountFromServer(collection(datasetRef, 'records'))])\n .then(([dataset, count]) => {\n if (!active) return\n setSummary({\n name: datasetDisplayName(dataset.data() as HostDataset | undefined),\n count: count.data().count,\n })\n })\n .catch((error) => {\n // A dataset this viewer may not read still gets its chip, unnumbered.\n console.error(error)\n if (active) setSummary(null)\n })\n return () => {\n active = false\n }\n }, [firestore, scope, datasetId])\n\n if (!binding) return null\n // Short, because it shares the name's cell: the count is the news, and the\n // tooltip says where the pages live.\n const label = summary\n ? `${summary.count} record ${summary.count === 1 ? 'page' : 'pages'}`\n : 'Record template'\n return (\n <Tooltip\n title={`Record template${summary?.name ? ` of ${summary.name}` : ''}: served once per record at /${binding.base}/ and each record's page address`}\n >\n <Chip size=\"small\" variant=\"outlined\" label={label} />\n </Tooltip>\n )\n}\n\nexport default RecordTemplateRowChip\n"],"names":["useFirestore","useOrgDataScope","Chip","Tooltip","collection","doc","getCountFromServer","getDoc","useEffect","useState","datasetDisplayName","useSiteRecordPageBindings","RecordTemplateRowChip","props","hostId","orgId","screenId","bindings","binding","find","one","firestore","scope","summary","setSummary","datasetId","undefined","active","datasetRef","Promise","all","then","dataset","count","name","data","catch","error","console","label","title","base","size","variant"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAGA,SAASA,YAAY,EAAEC,eAAe,QAAQ,iCAAgC;AAC9E,SAASC,IAAI,EAAEC,OAAO,QAAQ,gBAAe;AAC7C,SAASC,UAAU,EAAEC,GAAG,EAAEC,kBAAkB,EAAEC,MAAM,QAAQ,qBAAoB;AAChF,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAC3C,SAASC,kBAAkB,QAA0B,uBAAmB;AACxE,SAASC,yBAAyB,QAAQ,0BAAsB;AAEhE;;;;;;;;CAQC,GACD,OAAO,SAASC,sBAAsBC,KAAoC;IACxE,MAAM,EAAEC,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAE,GAAGH;IACpC,MAAM,EAAEI,QAAQ,EAAE,GAAGN,0BAA0BG;IAC/C,MAAMI,UAAUD,SAASE,IAAI,CAAC,CAACC,MAAQA,IAAIJ,QAAQ,KAAKA;IACxD,MAAMK,YAAYrB;IAClB,MAAM,EAAEsB,KAAK,EAAE,GAAGrB,gBAAgB;QAAEa;QAAQC;IAAM;IAClD,MAAM,CAACQ,SAASC,WAAW,GAAGf,SAAiD;IAC/E,MAAMgB,YAAYP,2BAAAA,QAASO,SAAS;IAEpCjB,UAAU;QACR,IAAI,CAACiB,aAAa,CAACH,OAAO;YACxBE,WAAW;YACX,OAAOE;QACT;QACA,IAAIC,SAAS;QACb,MAAMC,aAAavB,IAAIgB,WAAWC,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE,EAAE,YAAYG;QAClEI,QAAQC,GAAG,CAAC;YAACvB,OAAOqB;YAAatB,mBAAmBF,WAAWwB,YAAY;SAAY,EACpFG,IAAI,CAAC,CAAC,CAACC,SAASC,MAAM;YACrB,IAAI,CAACN,QAAQ;YACbH,WAAW;gBACTU,MAAMxB,mBAAmBsB,QAAQG,IAAI;gBACrCF,OAAOA,MAAME,IAAI,GAAGF,KAAK;YAC3B;QACF,GACCG,KAAK,CAAC,CAACC;YACN,sEAAsE;YACtEC,QAAQD,KAAK,CAACA;YACd,IAAIV,QAAQH,WAAW;QACzB;QACF,OAAO;YACLG,SAAS;QACX;IACF,GAAG;QAACN;QAAWC;QAAOG;KAAU;IAEhC,IAAI,CAACP,SAAS,OAAO;IACrB,2EAA2E;IAC3E,qCAAqC;IACrC,MAAMqB,QAAQhB,UACV,GAAGA,QAAQU,KAAK,CAAC,QAAQ,EAAEV,QAAQU,KAAK,KAAK,IAAI,SAAS,SAAS,GACnE;IACJ,qBACE,KAAC9B;QACCqC,OAAO,CAAC,eAAe,EAAEjB,CAAAA,2BAAAA,QAASW,IAAI,IAAG,CAAC,IAAI,EAAEX,QAAQW,IAAI,EAAE,GAAG,GAAG,6BAA6B,EAAEhB,QAAQuB,IAAI,CAAC,gCAAgC,CAAC;kBAEjJ,cAAA,KAACvC;YAAKwC,MAAK;YAAQC,SAAQ;YAAWJ,OAAOA;;;AAGnD;AAEA,eAAe3B,sBAAqB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { ConsoleBesignerPagePropertiesZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins';
|
|
18
|
+
/**
|
|
19
|
+
* Page Properties' "Record pages" section (AGL-3475): makes the page in the
|
|
20
|
+
* editor the record template of a dataset, so it is served once per record at
|
|
21
|
+
* `/{base}/{address}`, and picks the record the canvas draws it for.
|
|
22
|
+
*
|
|
23
|
+
* Saving a page that is not yet a template converts it first, through the
|
|
24
|
+
* platform's own route, after saying what that means: it stops answering at
|
|
25
|
+
* its own address and stops counting toward the plan's pages. The binding is
|
|
26
|
+
* then written by the record-pages route, which re-checks everything this
|
|
27
|
+
* section checks and the few things only the server can see.
|
|
28
|
+
*/
|
|
29
|
+
export declare function RecordTemplateSection(props: ConsoleBesignerPagePropertiesZoneProps): import("react").JSX.Element;
|
|
30
|
+
export default RecordTemplateSection;
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
19
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
20
|
+
import { checkEntitlement, scopeTokensForHost } from "@aglyn/aglyn";
|
|
21
|
+
import { useConfirmationContext } from "@aglyn/shared-ui-jsx";
|
|
22
|
+
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
23
|
+
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
24
|
+
import { useFirestore, useFirestoreCollection, useOrgDataScope, useOrgPlan, useUser } from "@aglyn/tenant-feature-instance";
|
|
25
|
+
import { Button, InputAdornment, MenuItem, Stack, TextField, Typography } from "@mui/material";
|
|
26
|
+
import { collection, limit, query, where } from "firebase/firestore";
|
|
27
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
28
|
+
import { effectiveDatasetModel } from "../model/dataset-models.js";
|
|
29
|
+
import { datasetDisplayName } from "../model/datasets.js";
|
|
30
|
+
import { isRecordAddressField, recordAddressFieldIds, recordPageBaseRefusal, recordPagePath } from "./record-pages.js";
|
|
31
|
+
import { useRecordPagePreview, useSiteRecordPageBindings } from "./record-page-source.js";
|
|
32
|
+
/** The optional field pickers, in the order the section offers them. */ const FIELD_PICKERS = [
|
|
33
|
+
{
|
|
34
|
+
key: 'titleField',
|
|
35
|
+
label: 'Page name from',
|
|
36
|
+
helper: 'Names each page — its tab title and breadcrumb. Defaults to the first text field.',
|
|
37
|
+
empty: 'First text field'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: 'seoTitleField',
|
|
41
|
+
label: 'Search title from',
|
|
42
|
+
helper: 'Used exactly as written. Without one, the page name and the site title are used.',
|
|
43
|
+
empty: 'Page name'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: 'seoDescriptionField',
|
|
47
|
+
label: 'Search description from',
|
|
48
|
+
helper: 'Without one, this page’s own description is used.',
|
|
49
|
+
empty: 'This page’s description'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: 'seoImageField',
|
|
53
|
+
label: 'Sharing image from',
|
|
54
|
+
helper: 'A media field or an image URL. Without one, this page’s image is used.',
|
|
55
|
+
empty: 'This page’s image'
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
const EMPTY_DRAFT = {
|
|
59
|
+
datasetId: '',
|
|
60
|
+
base: '',
|
|
61
|
+
slugField: ''
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Page Properties' "Record pages" section (AGL-3475): makes the page in the
|
|
65
|
+
* editor the record template of a dataset, so it is served once per record at
|
|
66
|
+
* `/{base}/{address}`, and picks the record the canvas draws it for.
|
|
67
|
+
*
|
|
68
|
+
* Saving a page that is not yet a template converts it first, through the
|
|
69
|
+
* platform's own route, after saying what that means: it stops answering at
|
|
70
|
+
* its own address and stops counting toward the plan's pages. The binding is
|
|
71
|
+
* then written by the record-pages route, which re-checks everything this
|
|
72
|
+
* section checks and the few things only the server can see.
|
|
73
|
+
*/ export function RecordTemplateSection(props) {
|
|
74
|
+
const { hostId, orgId, screenId, screenKind } = props;
|
|
75
|
+
const firestore = useFirestore();
|
|
76
|
+
const { data: user } = useUser();
|
|
77
|
+
const { enqueueSnackbar } = useSnackbar();
|
|
78
|
+
const { confirm } = useConfirmationContext();
|
|
79
|
+
const { org, ready: planReady } = useOrgPlan(hostId);
|
|
80
|
+
const { scope } = useOrgDataScope({
|
|
81
|
+
hostId,
|
|
82
|
+
orgId
|
|
83
|
+
});
|
|
84
|
+
const { bindings } = useSiteRecordPageBindings(hostId);
|
|
85
|
+
const current = bindings.find((binding)=>binding.screenId === screenId);
|
|
86
|
+
const preview = useRecordPagePreview({
|
|
87
|
+
hostId,
|
|
88
|
+
screenId
|
|
89
|
+
});
|
|
90
|
+
// The datasets THIS site may see, which are the ones a record page can
|
|
91
|
+
// render: the same scope the published page's reads apply.
|
|
92
|
+
const { data: datasetDocs } = useFirestoreCollection(()=>scope ? query(collection(firestore, scope[0], scope[1], 'datasets'), where('visibleTo', 'array-contains-any', scopeTokensForHost(hostId)), limit(100)) : null, [
|
|
93
|
+
firestore,
|
|
94
|
+
scope,
|
|
95
|
+
hostId
|
|
96
|
+
], {
|
|
97
|
+
idField: '$id'
|
|
98
|
+
});
|
|
99
|
+
const datasets = useMemo(()=>(datasetDocs != null ? datasetDocs : []).filter((dataset)=>!dataset.deletedAt).sort((a, b)=>datasetDisplayName(a).localeCompare(datasetDisplayName(b))), [
|
|
100
|
+
datasetDocs
|
|
101
|
+
]);
|
|
102
|
+
const [draft, setDraft] = useState(EMPTY_DRAFT);
|
|
103
|
+
const [sourceField, setSourceField] = useState('');
|
|
104
|
+
const [busy, setBusy] = useState(false);
|
|
105
|
+
useEffect(()=>{
|
|
106
|
+
if (current) {
|
|
107
|
+
const { screenId: _id } = current, stored = _object_without_properties_loose(current, [
|
|
108
|
+
"screenId"
|
|
109
|
+
]);
|
|
110
|
+
setDraft(stored);
|
|
111
|
+
} else {
|
|
112
|
+
setDraft(EMPTY_DRAFT);
|
|
113
|
+
}
|
|
114
|
+
}, [
|
|
115
|
+
current
|
|
116
|
+
]);
|
|
117
|
+
const dataset = datasets.find((one)=>one.$id === draft.datasetId);
|
|
118
|
+
const model = dataset ? effectiveDatasetModel(dataset) : undefined;
|
|
119
|
+
const addressFields = model ? recordAddressFieldIds(model) : [];
|
|
120
|
+
const textFields = model ? model.order.filter((fieldId)=>{
|
|
121
|
+
var _model_fields_fieldId;
|
|
122
|
+
return ((_model_fields_fieldId = model.fields[fieldId]) == null ? void 0 : _model_fields_fieldId.type) === 'text' && !isRecordAddressField(model.fields[fieldId]);
|
|
123
|
+
}) : [];
|
|
124
|
+
const baseRefusal = draft.base ? recordPageBaseRefusal(draft.base, {
|
|
125
|
+
otherBases: bindings.filter((binding)=>binding.screenId !== screenId).map((binding)=>binding.base)
|
|
126
|
+
}) : null;
|
|
127
|
+
// A dataset with exactly one address field needs no choice.
|
|
128
|
+
useEffect(()=>{
|
|
129
|
+
if (model && !draft.slugField && addressFields.length === 1) {
|
|
130
|
+
setDraft((previous)=>_extends({}, previous, {
|
|
131
|
+
slugField: addressFields[0]
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
}, [
|
|
135
|
+
model,
|
|
136
|
+
draft.slugField,
|
|
137
|
+
addressFields
|
|
138
|
+
]);
|
|
139
|
+
const post = useCallback(async (path, body)=>{
|
|
140
|
+
var _ref;
|
|
141
|
+
const response = await authorizedFetch(user, path, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
'Content-Type': 'application/json'
|
|
145
|
+
},
|
|
146
|
+
body: JSON.stringify(body)
|
|
147
|
+
});
|
|
148
|
+
const result = await response.json().catch(()=>({}));
|
|
149
|
+
if (!response.ok) throw new Error((_ref = result == null ? void 0 : result['error']) != null ? _ref : 'Something went wrong');
|
|
150
|
+
return result;
|
|
151
|
+
}, [
|
|
152
|
+
user
|
|
153
|
+
]);
|
|
154
|
+
const handleAddAddresses = useCallback(async ()=>{
|
|
155
|
+
if (!dataset || !sourceField || !orgId) return;
|
|
156
|
+
setBusy(true);
|
|
157
|
+
try {
|
|
158
|
+
const result = await post('/api/orgs/datasets', {
|
|
159
|
+
orgId,
|
|
160
|
+
action: 'add-address-field',
|
|
161
|
+
datasetId: dataset.$id,
|
|
162
|
+
sourceField
|
|
163
|
+
});
|
|
164
|
+
setDraft((previous)=>{
|
|
165
|
+
var _result_fieldId;
|
|
166
|
+
return _extends({}, previous, {
|
|
167
|
+
slugField: String((_result_fieldId = result['fieldId']) != null ? _result_fieldId : '')
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
enqueueSnackbar(result['truncated'] ? `Gave ${result['filled']} records a page address — run it again for the rest` : `Gave ${result['filled']} records a page address`, {
|
|
171
|
+
variant: 'success'
|
|
172
|
+
});
|
|
173
|
+
} catch (error) {
|
|
174
|
+
enqueueSnackbar(error.message, {
|
|
175
|
+
variant: 'error'
|
|
176
|
+
});
|
|
177
|
+
} finally{
|
|
178
|
+
setBusy(false);
|
|
179
|
+
}
|
|
180
|
+
}, [
|
|
181
|
+
dataset,
|
|
182
|
+
sourceField,
|
|
183
|
+
orgId,
|
|
184
|
+
post,
|
|
185
|
+
enqueueSnackbar
|
|
186
|
+
]);
|
|
187
|
+
const handleSave = useCallback(async ()=>{
|
|
188
|
+
if (screenKind !== 'template') {
|
|
189
|
+
const accepted = await confirm({
|
|
190
|
+
title: 'Make this page a record template?',
|
|
191
|
+
description: `Each record gets its own page at ${recordPagePath(draft.base || 'base', 'address')}, ` + 'drawn by this design. This page stops answering at its own address and no ' + 'longer counts toward your plan’s pages.',
|
|
192
|
+
confirmationText: 'Make it a template'
|
|
193
|
+
})// The dialog rejects on cancel.
|
|
194
|
+
.then(()=>true).catch(()=>false);
|
|
195
|
+
if (!accepted) return;
|
|
196
|
+
}
|
|
197
|
+
setBusy(true);
|
|
198
|
+
try {
|
|
199
|
+
if (screenKind !== 'template') {
|
|
200
|
+
await post('/api/hosts/screens', {
|
|
201
|
+
action: 'convert',
|
|
202
|
+
hostId,
|
|
203
|
+
id: screenId,
|
|
204
|
+
kind: 'template'
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
await post('/api/hosts/record-pages', _extends({
|
|
208
|
+
action: 'save',
|
|
209
|
+
hostId,
|
|
210
|
+
screenId
|
|
211
|
+
}, draft));
|
|
212
|
+
enqueueSnackbar('Record pages saved', {
|
|
213
|
+
variant: 'success'
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
enqueueSnackbar(error.message, {
|
|
217
|
+
variant: 'error'
|
|
218
|
+
});
|
|
219
|
+
} finally{
|
|
220
|
+
setBusy(false);
|
|
221
|
+
}
|
|
222
|
+
}, [
|
|
223
|
+
screenKind,
|
|
224
|
+
confirm,
|
|
225
|
+
draft,
|
|
226
|
+
post,
|
|
227
|
+
hostId,
|
|
228
|
+
screenId,
|
|
229
|
+
enqueueSnackbar
|
|
230
|
+
]);
|
|
231
|
+
const handleRemove = useCallback(async ()=>{
|
|
232
|
+
var _ref;
|
|
233
|
+
const accepted = await confirm({
|
|
234
|
+
title: 'Stop serving record pages?',
|
|
235
|
+
description: `Every page under /${(_ref = current == null ? void 0 : current.base) != null ? _ref : ''}/ stops answering. This page stays a ` + 'template, so it doesn’t answer at its own address either.',
|
|
236
|
+
confirmationText: 'Stop serving them',
|
|
237
|
+
confirmationButtonProps: {
|
|
238
|
+
color: 'error'
|
|
239
|
+
}
|
|
240
|
+
}).then(()=>true).catch(()=>false);
|
|
241
|
+
if (!accepted) return;
|
|
242
|
+
setBusy(true);
|
|
243
|
+
try {
|
|
244
|
+
await post('/api/hosts/record-pages', {
|
|
245
|
+
action: 'remove',
|
|
246
|
+
hostId,
|
|
247
|
+
screenId
|
|
248
|
+
});
|
|
249
|
+
enqueueSnackbar('Record pages removed', {
|
|
250
|
+
variant: 'success'
|
|
251
|
+
});
|
|
252
|
+
} catch (error) {
|
|
253
|
+
enqueueSnackbar(error.message, {
|
|
254
|
+
variant: 'error'
|
|
255
|
+
});
|
|
256
|
+
setBusy(false);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// The page is left a template, which nobody can visit. Making it a page
|
|
260
|
+
// again is offered once, here, and checked against the plan's pages by
|
|
261
|
+
// the platform's convert route, whose refusal names the count.
|
|
262
|
+
const restore = await confirm({
|
|
263
|
+
title: 'Make this a normal page again?',
|
|
264
|
+
description: 'It counts toward your plan’s pages and, if it was published, answers at its ' + 'own address again right away. Placeholders like {{item.name}} show as plain ' + 'text until you edit them out.',
|
|
265
|
+
confirmationText: 'Make it a page',
|
|
266
|
+
cancellationText: 'Keep it hidden'
|
|
267
|
+
}).then(()=>true).catch(()=>false);
|
|
268
|
+
if (restore) {
|
|
269
|
+
try {
|
|
270
|
+
await post('/api/hosts/screens', {
|
|
271
|
+
action: 'convert',
|
|
272
|
+
hostId,
|
|
273
|
+
id: screenId,
|
|
274
|
+
kind: 'page'
|
|
275
|
+
});
|
|
276
|
+
enqueueSnackbar('This is a normal page again', {
|
|
277
|
+
variant: 'success'
|
|
278
|
+
});
|
|
279
|
+
} catch (error) {
|
|
280
|
+
enqueueSnackbar(error.message, {
|
|
281
|
+
variant: 'error'
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
setBusy(false);
|
|
286
|
+
}, [
|
|
287
|
+
confirm,
|
|
288
|
+
current == null ? void 0 : current.base,
|
|
289
|
+
post,
|
|
290
|
+
hostId,
|
|
291
|
+
screenId,
|
|
292
|
+
enqueueSnackbar
|
|
293
|
+
]);
|
|
294
|
+
if (planReady && !checkEntitlement(org, 'dataStore')) {
|
|
295
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
296
|
+
spacing: 1,
|
|
297
|
+
children: [
|
|
298
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
299
|
+
variant: "subtitle2",
|
|
300
|
+
children: 'Record pages'
|
|
301
|
+
}),
|
|
302
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
303
|
+
variant: "caption",
|
|
304
|
+
color: "text.secondary",
|
|
305
|
+
children: 'Serve this page once per record of a dataset — a page for every service or ' + 'location. Record pages need a Starter plan or higher.'
|
|
306
|
+
})
|
|
307
|
+
]
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
const ready = preview.status === 'ready' ? preview : null;
|
|
311
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
312
|
+
spacing: 1.5,
|
|
313
|
+
children: [
|
|
314
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
315
|
+
variant: "subtitle2",
|
|
316
|
+
children: 'Record pages'
|
|
317
|
+
}),
|
|
318
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
319
|
+
variant: "caption",
|
|
320
|
+
color: "text.secondary",
|
|
321
|
+
children: current ? `This page is the template of every record of ${dataset ? datasetDisplayName(dataset) : 'its dataset'}, each at /${current.base}/ and its page address. Inside it, ` + '{{item.field}} fills in from the record.' : 'Serve this page once per record of a dataset, each at its own address — ' + '/services/roofing. Inside it, {{item.field}} fills in from the record, as in ' + 'a repeat.'
|
|
322
|
+
}),
|
|
323
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
324
|
+
select: true,
|
|
325
|
+
size: "small",
|
|
326
|
+
label: "Dataset",
|
|
327
|
+
value: draft.datasetId,
|
|
328
|
+
onChange: (event)=>setDraft(_extends({}, EMPTY_DRAFT, {
|
|
329
|
+
base: draft.base,
|
|
330
|
+
datasetId: event.target.value
|
|
331
|
+
})),
|
|
332
|
+
helperText: "Only datasets shared with this site are listed",
|
|
333
|
+
children: datasets.map((one)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
334
|
+
value: one.$id,
|
|
335
|
+
children: datasetDisplayName(one) || one.$id
|
|
336
|
+
}, one.$id))
|
|
337
|
+
}),
|
|
338
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
339
|
+
size: "small",
|
|
340
|
+
label: "Address",
|
|
341
|
+
value: draft.base,
|
|
342
|
+
onChange: (event)=>setDraft(_extends({}, draft, {
|
|
343
|
+
base: event.target.value
|
|
344
|
+
})),
|
|
345
|
+
error: Boolean(baseRefusal),
|
|
346
|
+
helperText: baseRefusal != null ? baseRefusal : draft.base ? `Records are served at /${draft.base.replace(/^\/+|\/+$/g, '')}/their-address` : 'One or more segments, like services or services/residential',
|
|
347
|
+
slotProps: {
|
|
348
|
+
input: {
|
|
349
|
+
startAdornment: /*#__PURE__*/ _jsx(InputAdornment, {
|
|
350
|
+
position: "start",
|
|
351
|
+
children: "/"
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}),
|
|
356
|
+
model && addressFields.length ? /*#__PURE__*/ _jsx(TextField, {
|
|
357
|
+
select: true,
|
|
358
|
+
size: "small",
|
|
359
|
+
label: "Page address field",
|
|
360
|
+
value: draft.slugField,
|
|
361
|
+
onChange: (event)=>setDraft(_extends({}, draft, {
|
|
362
|
+
slugField: event.target.value
|
|
363
|
+
})),
|
|
364
|
+
helperText: "Each record's own segment of its address",
|
|
365
|
+
children: addressFields.map((fieldId)=>{
|
|
366
|
+
var _ref;
|
|
367
|
+
var _model_fields_fieldId;
|
|
368
|
+
return /*#__PURE__*/ _jsx(MenuItem, {
|
|
369
|
+
value: fieldId,
|
|
370
|
+
children: (_ref = (_model_fields_fieldId = model.fields[fieldId]) == null ? void 0 : _model_fields_fieldId.name) != null ? _ref : fieldId
|
|
371
|
+
}, fieldId);
|
|
372
|
+
})
|
|
373
|
+
}) : model ? /*#__PURE__*/ _jsxs(Stack, {
|
|
374
|
+
spacing: 1,
|
|
375
|
+
children: [
|
|
376
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
377
|
+
variant: "caption",
|
|
378
|
+
color: "text.secondary",
|
|
379
|
+
children: 'This dataset has no Page address field yet. Make one from a text field: ' + 'every record gets an address made from its value (Roof Repair → ' + 'roof-repair), and new records get one as they are added. A record keeps its ' + 'address when it is renamed.'
|
|
380
|
+
}),
|
|
381
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
382
|
+
direction: "row",
|
|
383
|
+
spacing: 1,
|
|
384
|
+
sx: {
|
|
385
|
+
alignItems: 'center'
|
|
386
|
+
},
|
|
387
|
+
children: [
|
|
388
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
389
|
+
select: true,
|
|
390
|
+
size: "small",
|
|
391
|
+
label: "Make addresses from",
|
|
392
|
+
value: sourceField,
|
|
393
|
+
onChange: (event)=>setSourceField(event.target.value),
|
|
394
|
+
sx: {
|
|
395
|
+
flex: 1
|
|
396
|
+
},
|
|
397
|
+
children: textFields.map((fieldId)=>{
|
|
398
|
+
var _ref;
|
|
399
|
+
var _model_fields_fieldId;
|
|
400
|
+
return /*#__PURE__*/ _jsx(MenuItem, {
|
|
401
|
+
value: fieldId,
|
|
402
|
+
children: (_ref = (_model_fields_fieldId = model.fields[fieldId]) == null ? void 0 : _model_fields_fieldId.name) != null ? _ref : fieldId
|
|
403
|
+
}, fieldId);
|
|
404
|
+
})
|
|
405
|
+
}),
|
|
406
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
407
|
+
size: "small",
|
|
408
|
+
variant: "outlined",
|
|
409
|
+
onClick: handleAddAddresses,
|
|
410
|
+
disabled: !sourceField || busy,
|
|
411
|
+
children: 'Make addresses'
|
|
412
|
+
})
|
|
413
|
+
]
|
|
414
|
+
})
|
|
415
|
+
]
|
|
416
|
+
}) : null,
|
|
417
|
+
model ? FIELD_PICKERS.map((picker)=>{
|
|
418
|
+
var _draft_picker_key;
|
|
419
|
+
return /*#__PURE__*/ _jsxs(TextField, {
|
|
420
|
+
select: true,
|
|
421
|
+
size: "small",
|
|
422
|
+
label: picker.label,
|
|
423
|
+
value: (_draft_picker_key = draft[picker.key]) != null ? _draft_picker_key : '',
|
|
424
|
+
onChange: (event)=>setDraft(_extends({}, draft, {
|
|
425
|
+
[picker.key]: event.target.value || undefined
|
|
426
|
+
})),
|
|
427
|
+
helperText: picker.helper,
|
|
428
|
+
// The empty choice is a real one — "use the default" — so it is
|
|
429
|
+
// shown rather than leaving the field looking blank.
|
|
430
|
+
slotProps: {
|
|
431
|
+
select: {
|
|
432
|
+
displayEmpty: true
|
|
433
|
+
},
|
|
434
|
+
inputLabel: {
|
|
435
|
+
shrink: true
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
children: [
|
|
439
|
+
/*#__PURE__*/ _jsx(MenuItem, {
|
|
440
|
+
value: "",
|
|
441
|
+
children: picker.empty
|
|
442
|
+
}),
|
|
443
|
+
model.order.map((fieldId)=>{
|
|
444
|
+
var _ref;
|
|
445
|
+
var _model_fields_fieldId;
|
|
446
|
+
return /*#__PURE__*/ _jsx(MenuItem, {
|
|
447
|
+
value: fieldId,
|
|
448
|
+
children: (_ref = (_model_fields_fieldId = model.fields[fieldId]) == null ? void 0 : _model_fields_fieldId.name) != null ? _ref : fieldId
|
|
449
|
+
}, fieldId);
|
|
450
|
+
})
|
|
451
|
+
]
|
|
452
|
+
}, picker.key);
|
|
453
|
+
}) : null,
|
|
454
|
+
ready ? /*#__PURE__*/ _jsx(TextField, {
|
|
455
|
+
select: true,
|
|
456
|
+
size: "small",
|
|
457
|
+
label: "Preview with",
|
|
458
|
+
value: ready.selectedId,
|
|
459
|
+
onChange: (event)=>ready.select(event.target.value),
|
|
460
|
+
helperText: `The canvas draws this page for one of ${ready.choices.length}${ready.choices.length >= 100 ? '+' : ''} records`,
|
|
461
|
+
children: ready.choices.map((choice)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
462
|
+
value: choice.id,
|
|
463
|
+
children: choice.label
|
|
464
|
+
}, choice.id))
|
|
465
|
+
}) : null,
|
|
466
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
467
|
+
direction: "row",
|
|
468
|
+
spacing: 1,
|
|
469
|
+
children: [
|
|
470
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
471
|
+
size: "small",
|
|
472
|
+
variant: "outlined",
|
|
473
|
+
onClick: handleSave,
|
|
474
|
+
disabled: busy || !draft.datasetId || !draft.base || !draft.slugField || Boolean(baseRefusal),
|
|
475
|
+
children: current ? 'Save record pages' : 'Serve record pages'
|
|
476
|
+
}),
|
|
477
|
+
current ? /*#__PURE__*/ _jsx(Button, {
|
|
478
|
+
size: "small",
|
|
479
|
+
color: "error",
|
|
480
|
+
onClick: handleRemove,
|
|
481
|
+
disabled: busy,
|
|
482
|
+
children: 'Stop serving'
|
|
483
|
+
}) : null
|
|
484
|
+
]
|
|
485
|
+
})
|
|
486
|
+
]
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
export default RecordTemplateSection;
|
|
490
|
+
|
|
491
|
+
//# sourceMappingURL=record-template-section.component.js.map
|