@mulmoclaude/collection-plugin 0.7.4 → 0.7.6
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/style.css +78 -61
- package/dist/vue/components/CollectionKanbanView.vue.d.ts.map +1 -1
- package/dist/vue/components/CollectionRecordPanel.vue.d.ts +24 -2
- package/dist/vue/components/CollectionRecordPanel.vue.d.ts.map +1 -1
- package/dist/vue/components/CollectionView.vue.d.ts.map +1 -1
- package/dist/vue/lang/de.d.ts.map +1 -1
- package/dist/vue/lang/en.d.ts +3 -0
- package/dist/vue/lang/en.d.ts.map +1 -1
- package/dist/vue/lang/es.d.ts.map +1 -1
- package/dist/vue/lang/fr.d.ts.map +1 -1
- package/dist/vue/lang/index.d.ts +3 -0
- package/dist/vue/lang/index.d.ts.map +1 -1
- package/dist/vue/lang/ja.d.ts.map +1 -1
- package/dist/vue/lang/ko.d.ts.map +1 -1
- package/dist/vue/lang/ptBR.d.ts.map +1 -1
- package/dist/vue/lang/zh.d.ts.map +1 -1
- package/dist/vue/useCollectionRendering.d.ts +1 -2
- package/dist/vue/useCollectionRendering.d.ts.map +1 -1
- package/dist/vue/useCollectionRendering.helpers.d.ts +29 -0
- package/dist/vue/useCollectionRendering.helpers.d.ts.map +1 -0
- package/dist/vue/useCollectionRendering.renderers.d.ts +22 -0
- package/dist/vue/useCollectionRendering.renderers.d.ts.map +1 -0
- package/dist/vue/useLinkedCollectionCaches.d.ts +33 -0
- package/dist/vue/useLinkedCollectionCaches.d.ts.map +1 -0
- package/dist/vue.cjs +559 -414
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +559 -414
- package/dist/vue.js.map +1 -1
- package/package.json +7 -7
package/dist/vue.cjs
CHANGED
|
@@ -142,6 +142,8 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
142
142
|
kanbanUncategorized: "Uncategorized",
|
|
143
143
|
kanbanOpenCard: "Open {label}",
|
|
144
144
|
addView: "Add view",
|
|
145
|
+
addViewDesktop: "Custom view",
|
|
146
|
+
addViewMobile: "Phone view",
|
|
145
147
|
config: {
|
|
146
148
|
open: "Collection settings",
|
|
147
149
|
title: "{title} · settings",
|
|
@@ -153,6 +155,7 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
153
155
|
customViewLoading: "Loading view…",
|
|
154
156
|
customViewError: "Couldn't load this view: {error}",
|
|
155
157
|
addViewPrompt: "I want to add a custom view to the \"{title}\" collection. Ask me what I want to see or edit, then author the HTML view file at {base}/views/your-view.html and register it in {base}/schema.json under `views[]` (capabilities [\"read\"] for a read-only view, [\"read\",\"write\"] if it edits records). Follow the custom-view help for the data contract.",
|
|
158
|
+
addMobileViewPrompt: "I want to add a phone (remote app) custom view to the \"{title}\" collection. Ask me what I want to see or edit, then author the HTML view file at {base}/views/your-view.html and register it in {base}/schema.json under `views[]` with `target: \"mobile\"` (read-only by default; declare `editableFields` / `allowDelete` only if it edits records). Follow the custom-view-remote help for the postMessage data contract — no fetch of any kind in the view.",
|
|
156
159
|
repair: "Repair",
|
|
157
160
|
dataIssuesDetected: "{count} record file(s) have data problems and may be missing from this view.",
|
|
158
161
|
repairPrompt: "The \"{title}\" collection has {count} record file(s) with data problems that keep them from appearing. Fix each one — Read the file, correct it, then Write it back:\n{issues}\n\nWhen you're done, call presentCollection to confirm the records load.",
|
|
@@ -275,6 +278,8 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
275
278
|
kanbanUncategorized: "未分類",
|
|
276
279
|
kanbanOpenCard: "{label} を開く",
|
|
277
280
|
addView: "ビューを追加",
|
|
281
|
+
addViewDesktop: "カスタムビュー",
|
|
282
|
+
addViewMobile: "スマホビュー",
|
|
278
283
|
config: {
|
|
279
284
|
open: "コレクション設定",
|
|
280
285
|
title: "{title} · 設定",
|
|
@@ -286,6 +291,7 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
286
291
|
customViewLoading: "ビューを読み込み中…",
|
|
287
292
|
customViewError: "このビューを読み込めませんでした: {error}",
|
|
288
293
|
addViewPrompt: "「{title}」コレクションにカスタムビューを追加したい。何を見たい/編集したいか質問してから、HTML ビューファイルを {base}/views/your-view.html に作成し、{base}/schema.json の `views[]` に登録して(読み取り専用なら capabilities [\"read\"]、レコードを編集するなら [\"read\",\"write\"])。データ契約は custom-view ヘルプに従ってください。",
|
|
294
|
+
addMobileViewPrompt: "「{title}」コレクションにスマホ(リモートアプリ)用カスタムビューを追加したい。何を見たい/編集したいか質問してから、HTML ビューファイルを {base}/views/your-view.html に作成し、{base}/schema.json の `views[]` に `target: \"mobile\"` 付きで登録して(デフォルトは読み取り専用。レコードを編集する場合のみ `editableFields` / `allowDelete` を宣言)。データ契約は custom-view-remote ヘルプに従ってください — ビュー内での fetch は一切禁止です。",
|
|
289
295
|
repair: "修復",
|
|
290
296
|
dataIssuesDetected: "{count} 件のレコードファイルにデータの問題があり、この表示に出てこない可能性があります。",
|
|
291
297
|
repairPrompt: "コレクション「{title}」に、表示されない原因となるデータの問題を持つレコードファイルが {count} 件あります。それぞれを修正してください — ファイルを Read し、修正してから Write し直します:\n{issues}\n\n完了したら presentCollection を呼び出して、レコードが読み込めることを確認してください。",
|
|
@@ -408,6 +414,8 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
408
414
|
kanbanUncategorized: "未分类",
|
|
409
415
|
kanbanOpenCard: "打开 {label}",
|
|
410
416
|
addView: "添加视图",
|
|
417
|
+
addViewDesktop: "自定义视图",
|
|
418
|
+
addViewMobile: "手机视图",
|
|
411
419
|
config: {
|
|
412
420
|
open: "集合设置",
|
|
413
421
|
title: "{title} · 设置",
|
|
@@ -419,6 +427,7 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
419
427
|
customViewLoading: "正在加载视图…",
|
|
420
428
|
customViewError: "无法加载此视图:{error}",
|
|
421
429
|
addViewPrompt: "我想为“{title}”集合添加一个自定义视图。先问我想查看或编辑什么,然后在 {base}/views/your-view.html 创建 HTML 视图文件,并在 {base}/schema.json 的 `views[]` 中注册(只读视图用 capabilities [\"read\"],需要编辑记录则用 [\"read\",\"write\"])。数据契约请遵循 custom-view 帮助文档。",
|
|
430
|
+
addMobileViewPrompt: "我想为“{title}”集合添加一个手机(远程应用)自定义视图。先问我想查看或编辑什么,然后在 {base}/views/your-view.html 创建 HTML 视图文件,并在 {base}/schema.json 的 `views[]` 中以 `target: \"mobile\"` 注册(默认只读;仅在需要编辑记录时声明 `editableFields` / `allowDelete`)。数据契约请遵循 custom-view-remote 帮助文档 — 视图内禁止任何 fetch。",
|
|
422
431
|
repair: "修复",
|
|
423
432
|
dataIssuesDetected: "有 {count} 个记录文件存在数据问题,可能未显示在此视图中。",
|
|
424
433
|
repairPrompt: "集合 {title} 有 {count} 个记录文件存在导致无法显示的数据问题。请逐一修复——用 Read 读取该文件,更正后再用 Write 写回:\n{issues}\n\n完成后,调用 presentCollection 确认记录可以正常加载。",
|
|
@@ -541,6 +550,8 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
541
550
|
kanbanUncategorized: "미분류",
|
|
542
551
|
kanbanOpenCard: "{label} 열기",
|
|
543
552
|
addView: "보기 추가",
|
|
553
|
+
addViewDesktop: "사용자 지정 보기",
|
|
554
|
+
addViewMobile: "스마트폰 보기",
|
|
544
555
|
config: {
|
|
545
556
|
open: "컬렉션 설정",
|
|
546
557
|
title: "{title} · 설정",
|
|
@@ -552,6 +563,7 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
552
563
|
customViewLoading: "보기를 불러오는 중…",
|
|
553
564
|
customViewError: "이 보기를 불러오지 못했습니다: {error}",
|
|
554
565
|
addViewPrompt: "{title} 컬렉션에 사용자 지정 보기를 추가하고 싶어요. 무엇을 보거나 편집하고 싶은지 먼저 물어본 뒤, {base}/views/your-view.html 에 HTML 보기 파일을 만들고 {base}/schema.json의 `views[]`에 등록해 주세요(읽기 전용 보기는 capabilities [\"read\"], 레코드를 편집하면 [\"read\",\"write\"]). 데이터 계약은 custom-view 도움말을 따르세요.",
|
|
566
|
+
addMobileViewPrompt: "{title} 컬렉션에 스마트폰(리모트 앱)용 사용자 지정 보기를 추가하고 싶어요. 무엇을 보거나 편집하고 싶은지 먼저 물어본 뒤, {base}/views/your-view.html 에 HTML 보기 파일을 만들고 {base}/schema.json의 `views[]`에 `target: \"mobile\"`과 함께 등록해 주세요(기본은 읽기 전용, 레코드를 편집할 때만 `editableFields` / `allowDelete` 선언). 데이터 계약은 custom-view-remote 도움말을 따르세요 — 보기 안에서 fetch는 일절 금지입니다.",
|
|
555
567
|
repair: "복구",
|
|
556
568
|
dataIssuesDetected: "{count}개의 레코드 파일에 데이터 문제가 있어 이 보기에 표시되지 않을 수 있습니다.",
|
|
557
569
|
repairPrompt: "{title} 컬렉션에 표시되지 않는 원인이 되는 데이터 문제가 있는 레코드 파일이 {count}개 있습니다. 각 파일을 수정하세요 — Read로 파일을 읽고 수정한 뒤 Write로 다시 저장하세요:\n{issues}\n\n완료되면 presentCollection을 호출하여 레코드가 로드되는지 확인하세요.",
|
|
@@ -674,6 +686,8 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
674
686
|
kanbanUncategorized: "Sin categoría",
|
|
675
687
|
kanbanOpenCard: "Abrir {label}",
|
|
676
688
|
addView: "Añadir vista",
|
|
689
|
+
addViewDesktop: "Vista personalizada",
|
|
690
|
+
addViewMobile: "Vista móvil",
|
|
677
691
|
config: {
|
|
678
692
|
open: "Ajustes de la colección",
|
|
679
693
|
title: "{title} · ajustes",
|
|
@@ -685,6 +699,7 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
685
699
|
customViewLoading: "Cargando vista…",
|
|
686
700
|
customViewError: "No se pudo cargar esta vista: {error}",
|
|
687
701
|
addViewPrompt: "Quiero añadir una vista personalizada a la colección «{title}». Pregúntame qué quiero ver o editar, luego crea el archivo HTML de la vista en {base}/views/your-view.html y regístralo en {base}/schema.json dentro de `views[]` (capabilities [\"read\"] para una vista de solo lectura, [\"read\",\"write\"] si edita registros). Sigue la ayuda custom-view para el contrato de datos.",
|
|
702
|
+
addMobileViewPrompt: "Quiero añadir una vista personalizada para el móvil (aplicación remota) a la colección «{title}». Pregúntame qué quiero ver o editar, luego crea el archivo HTML de la vista en {base}/views/your-view.html y regístralo en {base}/schema.json dentro de `views[]` con `target: \"mobile\"` (de solo lectura por defecto; declara `editableFields` / `allowDelete` solo si edita registros). Sigue la ayuda custom-view-remote para el contrato de datos por postMessage — nada de fetch en la vista.",
|
|
688
703
|
repair: "Reparar",
|
|
689
704
|
dataIssuesDetected: "{count} archivo(s) de registro tienen problemas de datos y podrían no aparecer en esta vista.",
|
|
690
705
|
repairPrompt: "La colección {title} tiene {count} archivo(s) de registro con problemas de datos que impiden que aparezcan. Corrige cada uno: lee el archivo con Read, corrígelo y vuelve a escribirlo con Write:\n{issues}\n\nCuando termines, llama a presentCollection para confirmar que los registros se cargan.",
|
|
@@ -807,6 +822,8 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
807
822
|
kanbanUncategorized: "Sem categoria",
|
|
808
823
|
kanbanOpenCard: "Abrir {label}",
|
|
809
824
|
addView: "Adicionar visualização",
|
|
825
|
+
addViewDesktop: "Visualização personalizada",
|
|
826
|
+
addViewMobile: "Visualização para celular",
|
|
810
827
|
config: {
|
|
811
828
|
open: "Configurações da coleção",
|
|
812
829
|
title: "{title} · configurações",
|
|
@@ -818,6 +835,7 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
818
835
|
customViewLoading: "Carregando visualização…",
|
|
819
836
|
customViewError: "Não foi possível carregar esta visualização: {error}",
|
|
820
837
|
addViewPrompt: "Quero adicionar uma visualização personalizada à coleção \"{title}\". Pergunte o que eu quero ver ou editar, depois crie o arquivo HTML da visualização em {base}/views/your-view.html e registre-o em {base}/schema.json em `views[]` (capabilities [\"read\"] para uma visualização somente leitura, [\"read\",\"write\"] se editar registros). Siga a ajuda custom-view para o contrato de dados.",
|
|
838
|
+
addMobileViewPrompt: "Quero adicionar uma visualização personalizada para o celular (app remoto) à coleção \"{title}\". Pergunte o que eu quero ver ou editar, depois crie o arquivo HTML da visualização em {base}/views/your-view.html e registre-o em {base}/schema.json em `views[]` com `target: \"mobile\"` (somente leitura por padrão; declare `editableFields` / `allowDelete` apenas se editar registros). Siga a ajuda custom-view-remote para o contrato de dados via postMessage — nenhum fetch na visualização.",
|
|
821
839
|
repair: "Reparar",
|
|
822
840
|
dataIssuesDetected: "{count} arquivo(s) de registro têm problemas de dados e podem não aparecer nesta visualização.",
|
|
823
841
|
repairPrompt: "A coleção {title} tem {count} arquivo(s) de registro com problemas de dados que impedem que apareçam. Corrija cada um: leia o arquivo com Read, corrija-o e grave-o novamente com Write:\n{issues}\n\nQuando terminar, chame presentCollection para confirmar que os registros carregam.",
|
|
@@ -940,6 +958,8 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
940
958
|
kanbanUncategorized: "Non classé",
|
|
941
959
|
kanbanOpenCard: "Ouvrir {label}",
|
|
942
960
|
addView: "Ajouter une vue",
|
|
961
|
+
addViewDesktop: "Vue personnalisée",
|
|
962
|
+
addViewMobile: "Vue mobile",
|
|
943
963
|
config: {
|
|
944
964
|
open: "Paramètres de la collection",
|
|
945
965
|
title: "{title} · paramètres",
|
|
@@ -951,6 +971,7 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
951
971
|
customViewLoading: "Chargement de la vue…",
|
|
952
972
|
customViewError: "Impossible de charger cette vue : {error}",
|
|
953
973
|
addViewPrompt: "Je veux ajouter une vue personnalisée à la collection « {title} ». Demande-moi ce que je veux voir ou modifier, puis crée le fichier HTML de la vue dans {base}/views/your-view.html et enregistre-le dans {base}/schema.json sous `views[]` (capabilities [\"read\"] pour une vue en lecture seule, [\"read\",\"write\"] si elle modifie des enregistrements). Suis l'aide custom-view pour le contrat de données.",
|
|
974
|
+
addMobileViewPrompt: "Je veux ajouter une vue personnalisée pour le mobile (application distante) à la collection « {title} ». Demande-moi ce que je veux voir ou modifier, puis crée le fichier HTML de la vue dans {base}/views/your-view.html et enregistre-le dans {base}/schema.json sous `views[]` avec `target: \"mobile\"` (en lecture seule par défaut ; déclare `editableFields` / `allowDelete` uniquement si elle modifie des enregistrements). Suis l'aide custom-view-remote pour le contrat de données par postMessage — aucun fetch dans la vue.",
|
|
954
975
|
repair: "Réparer",
|
|
955
976
|
dataIssuesDetected: "{count} fichier(s) d'enregistrement présentent des problèmes de données et peuvent être absents de cette vue.",
|
|
956
977
|
repairPrompt: "La collection « {title} » comporte {count} fichier(s) d'enregistrement présentant des problèmes de données qui les empêchent d'apparaître. Corrigez chacun : lisez le fichier avec Read, corrigez-le, puis réécrivez-le avec Write :\n{issues}\n\nUne fois terminé, appelez presentCollection pour confirmer que les enregistrements se chargent.",
|
|
@@ -1073,6 +1094,8 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
1073
1094
|
kanbanUncategorized: "Nicht kategorisiert",
|
|
1074
1095
|
kanbanOpenCard: "{label} öffnen",
|
|
1075
1096
|
addView: "Ansicht hinzufügen",
|
|
1097
|
+
addViewDesktop: "Benutzerdefinierte Ansicht",
|
|
1098
|
+
addViewMobile: "Smartphone-Ansicht",
|
|
1076
1099
|
config: {
|
|
1077
1100
|
open: "Sammlungseinstellungen",
|
|
1078
1101
|
title: "{title} · Einstellungen",
|
|
@@ -1084,6 +1107,7 @@ var i18n = (0, vue_i18n.createI18n)({
|
|
|
1084
1107
|
customViewLoading: "Ansicht wird geladen…",
|
|
1085
1108
|
customViewError: "Diese Ansicht konnte nicht geladen werden: {error}",
|
|
1086
1109
|
addViewPrompt: "Ich möchte der Sammlung {title} eine benutzerdefinierte Ansicht hinzufügen. Frag mich, was ich sehen oder bearbeiten möchte, erstelle dann die HTML-Ansichtsdatei unter {base}/views/your-view.html und registriere sie in {base}/schema.json unter `views[]` (capabilities [\"read\"] für eine schreibgeschützte Ansicht, [\"read\",\"write\"] wenn sie Datensätze bearbeitet). Folge der custom-view-Hilfe für den Datenvertrag.",
|
|
1110
|
+
addMobileViewPrompt: "Ich möchte der Sammlung {title} eine benutzerdefinierte Ansicht für das Smartphone (Remote-App) hinzufügen. Frag mich, was ich sehen oder bearbeiten möchte, erstelle dann die HTML-Ansichtsdatei unter {base}/views/your-view.html und registriere sie in {base}/schema.json unter `views[]` mit `target: \"mobile\"` (standardmäßig schreibgeschützt; deklariere `editableFields` / `allowDelete` nur, wenn sie Datensätze bearbeitet). Folge der custom-view-remote-Hilfe für den postMessage-Datenvertrag — kein fetch jeglicher Art in der Ansicht.",
|
|
1087
1111
|
repair: "Reparieren",
|
|
1088
1112
|
dataIssuesDetected: "{count} Datensatzdatei(en) haben Datenprobleme und fehlen möglicherweise in dieser Ansicht.",
|
|
1089
1113
|
repairPrompt: "Die Sammlung {title} hat {count} Datensatzdatei(en) mit Datenproblemen, die ihr Erscheinen verhindern. Korrigiere jede — die Datei mit Read lesen, korrigieren und mit Write zurückschreiben:\n{issues}\n\nRufe anschließend presentCollection auf, um zu bestätigen, dass die Datensätze geladen werden.",
|
|
@@ -1717,7 +1741,10 @@ var CollectionKanbanView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1717
1741
|
const emit = __emit;
|
|
1718
1742
|
const { t } = useCollectionI18n();
|
|
1719
1743
|
/** The Uncategorized column uses the empty string as its sentinel value. */
|
|
1720
|
-
const groupSpec = (0, vue.computed)(() =>
|
|
1744
|
+
const groupSpec = (0, vue.computed)(() => {
|
|
1745
|
+
const spec = props.schema.fields[props.groupField];
|
|
1746
|
+
return spec?.type === "enum" ? spec : void 0;
|
|
1747
|
+
});
|
|
1721
1748
|
/** Declared enum values become columns in order, with a trailing
|
|
1722
1749
|
* Uncategorized column for empty/unknown values (also a drop target that
|
|
1723
1750
|
* clears the field). The Uncategorized column is omitted when the group
|
|
@@ -1764,7 +1791,10 @@ var CollectionKanbanView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
1764
1791
|
const value = String(raw);
|
|
1765
1792
|
return (groupSpec.value?.values ?? []).includes(value) ? value : UNCATEGORIZED;
|
|
1766
1793
|
}
|
|
1767
|
-
const visibleItems = (0, vue.computed)(() =>
|
|
1794
|
+
const visibleItems = (0, vue.computed)(() => {
|
|
1795
|
+
const spec = groupSpec.value;
|
|
1796
|
+
return spec ? props.items.filter((item) => (0, _mulmoclaude_core_collection.fieldVisible)(spec, item)) : [];
|
|
1797
|
+
});
|
|
1768
1798
|
const itemsByColumnMap = (0, vue.computed)(() => {
|
|
1769
1799
|
const map = /* @__PURE__ */ new Map();
|
|
1770
1800
|
for (const column of columns.value) map.set(column.value, []);
|
|
@@ -2291,7 +2321,8 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
2291
2321
|
/** Required flag for an embed's per-record picker — read off the storage
|
|
2292
2322
|
* field it writes (`idField`), since the embed itself stores nothing. */
|
|
2293
2323
|
function embedPickerRequired(field) {
|
|
2294
|
-
const
|
|
2324
|
+
const idField = field.type === "embed" ? field.idField : void 0;
|
|
2325
|
+
const target = idField ? props.collection.schema.fields[idField] : void 0;
|
|
2295
2326
|
return target ? isFieldRequiredInUi(target) : false;
|
|
2296
2327
|
}
|
|
2297
2328
|
/** Tailwind fill/text/border classes tinting an enum `<select>` by its current
|
|
@@ -3012,284 +3043,342 @@ var CollectionRemoteViewPreview_default = /*#__PURE__*/ _plugin_vue_export_helpe
|
|
|
3012
3043
|
}
|
|
3013
3044
|
}), [["__scopeId", "data-v-965ade6c"]]);
|
|
3014
3045
|
//#endregion
|
|
3015
|
-
//#region src/vue/useCollectionRendering.ts
|
|
3046
|
+
//#region src/vue/useCollectionRendering.helpers.ts
|
|
3047
|
+
var EM_DASH = "—";
|
|
3048
|
+
var DEFAULT_CURRENCY = "USD";
|
|
3049
|
+
var MARKDOWN_CELL_PREVIEW_MAX = 80;
|
|
3016
3050
|
function stepForFieldType(type) {
|
|
3017
3051
|
if (type === "money") return "0.01";
|
|
3018
3052
|
if (type === "number") return "any";
|
|
3019
3053
|
}
|
|
3020
|
-
function
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3054
|
+
function inputTypeFor(type) {
|
|
3055
|
+
if (type === "email") return "email";
|
|
3056
|
+
if (type === "number") return "number";
|
|
3057
|
+
if (type === "money") return "number";
|
|
3058
|
+
if (type === "date") return "date";
|
|
3059
|
+
if (type === "datetime") return "datetime-local";
|
|
3060
|
+
return "text";
|
|
3061
|
+
}
|
|
3062
|
+
function isExternalUrl(value) {
|
|
3063
|
+
return typeof value === "string" && /^https?:\/\//i.test(value);
|
|
3064
|
+
}
|
|
3065
|
+
function detailText(value) {
|
|
3066
|
+
if (value === void 0 || value === null || value === "") return EM_DASH;
|
|
3067
|
+
return String(value);
|
|
3068
|
+
}
|
|
3069
|
+
function formatCell(value, type) {
|
|
3070
|
+
if (value === void 0 || value === null || value === "") return EM_DASH;
|
|
3071
|
+
if (type === "markdown" && typeof value === "string") return value.length > MARKDOWN_CELL_PREVIEW_MAX ? `${value.slice(0, MARKDOWN_CELL_PREVIEW_MAX)}…` : value;
|
|
3072
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
3073
|
+
return JSON.stringify(value);
|
|
3074
|
+
}
|
|
3075
|
+
/** Resolve the ISO 4217 code for a money field: a per-record
|
|
3076
|
+
* `currencyField` (when present and non-blank) wins over the field's
|
|
3077
|
+
* literal `currency`. Only `money` / `derived` variants carry currency
|
|
3078
|
+
* keys; any other field resolves to undefined (the formatter's USD
|
|
3079
|
+
* fallback), as before. */
|
|
3080
|
+
function resolveCurrency(field, record) {
|
|
3081
|
+
if (field.type !== "money" && field.type !== "derived") return void 0;
|
|
3082
|
+
if (field.currencyField && record) {
|
|
3083
|
+
const code = record[field.currencyField];
|
|
3084
|
+
if (typeof code === "string" && code.trim().length > 0) return code;
|
|
3044
3085
|
}
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3086
|
+
return field.currency;
|
|
3087
|
+
}
|
|
3088
|
+
function formatMoney(value, currency, displayLocale) {
|
|
3089
|
+
if (value === void 0 || value === "") return EM_DASH;
|
|
3090
|
+
const amount = typeof value === "number" ? value : Number(value);
|
|
3091
|
+
if (!Number.isFinite(amount)) return String(value);
|
|
3092
|
+
const currencyCode = currency && currency.length > 0 ? currency : DEFAULT_CURRENCY;
|
|
3093
|
+
try {
|
|
3094
|
+
return new Intl.NumberFormat(displayLocale, {
|
|
3095
|
+
style: "currency",
|
|
3096
|
+
currency: currencyCode
|
|
3097
|
+
}).format(amount);
|
|
3098
|
+
} catch {
|
|
3099
|
+
return String(amount);
|
|
3056
3100
|
}
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3101
|
+
}
|
|
3102
|
+
function currencySymbolForLocale(currency, locale) {
|
|
3103
|
+
const code = currency && currency.length > 0 ? currency : DEFAULT_CURRENCY;
|
|
3104
|
+
try {
|
|
3105
|
+
return new Intl.NumberFormat(locale, {
|
|
3106
|
+
style: "currency",
|
|
3107
|
+
currency: code
|
|
3108
|
+
}).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
|
|
3109
|
+
} catch {
|
|
3110
|
+
return code;
|
|
3065
3111
|
}
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
const nextRefRecords = {};
|
|
3088
|
-
const nextEmbed = {};
|
|
3089
|
-
for (const { target, result } of results) {
|
|
3090
|
-
if (!result.ok) continue;
|
|
3091
|
-
if (refTargets.has(target)) {
|
|
3092
|
-
nextRef[target] = buildRefDisplayMap(result.data);
|
|
3093
|
-
nextRefRecords[target] = buildRefRecordMap(result.data);
|
|
3094
|
-
}
|
|
3095
|
-
if (embedTargets.has(target)) nextEmbed[target] = {
|
|
3096
|
-
schema: result.data.collection.schema,
|
|
3097
|
-
items: result.data.items
|
|
3098
|
-
};
|
|
3112
|
+
}
|
|
3113
|
+
function tableRows(value) {
|
|
3114
|
+
if (!Array.isArray(value)) return [];
|
|
3115
|
+
return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
|
|
3116
|
+
}
|
|
3117
|
+
function hasTableRows(value) {
|
|
3118
|
+
return tableRows(value).length > 0;
|
|
3119
|
+
}
|
|
3120
|
+
/** Pick the field used to label a referenced/embedded record: prefer a
|
|
3121
|
+
* `name` field, then `title`, else fall back to the primary key. */
|
|
3122
|
+
function displayFieldFor(fields, primaryKey) {
|
|
3123
|
+
if ("name" in fields) return "name";
|
|
3124
|
+
if ("title" in fields) return "title";
|
|
3125
|
+
return primaryKey;
|
|
3126
|
+
}
|
|
3127
|
+
function uniqueRefTargets(schema) {
|
|
3128
|
+
const targets = /* @__PURE__ */ new Set();
|
|
3129
|
+
const walk = (fields) => {
|
|
3130
|
+
for (const field of Object.values(fields)) {
|
|
3131
|
+
if (field.type === "ref" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
|
|
3132
|
+
if (field.type === "table" && field.of) walk(field.of);
|
|
3099
3133
|
}
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3134
|
+
};
|
|
3135
|
+
walk(schema.fields);
|
|
3136
|
+
return [...targets];
|
|
3137
|
+
}
|
|
3138
|
+
function uniqueEmbedTargets(schema) {
|
|
3139
|
+
const targets = /* @__PURE__ */ new Set();
|
|
3140
|
+
for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
|
|
3141
|
+
return [...targets];
|
|
3142
|
+
}
|
|
3143
|
+
function buildRefDisplayMap(detail) {
|
|
3144
|
+
const { fields, primaryKey } = detail.collection.schema;
|
|
3145
|
+
const displayField = displayFieldFor(fields, primaryKey);
|
|
3146
|
+
const map = {};
|
|
3147
|
+
for (const item of detail.items) {
|
|
3148
|
+
const slugRaw = item[primaryKey];
|
|
3149
|
+
if (typeof slugRaw !== "string" || slugRaw.length === 0) continue;
|
|
3150
|
+
const displayRaw = item[displayField];
|
|
3151
|
+
map[slugRaw] = typeof displayRaw === "string" && displayRaw.length > 0 ? displayRaw : slugRaw;
|
|
3115
3152
|
}
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
|
|
3125
|
-
return data.items.map((item) => {
|
|
3126
|
-
const slug = String(item[primaryKey] ?? "");
|
|
3127
|
-
const labelRaw = item[displayField];
|
|
3128
|
-
return {
|
|
3129
|
-
slug,
|
|
3130
|
-
display: typeof labelRaw === "string" && labelRaw.length > 0 ? labelRaw : slug
|
|
3131
|
-
};
|
|
3132
|
-
}).filter((opt) => opt.slug.length > 0).sort((left, right) => left.display.localeCompare(right.display));
|
|
3153
|
+
return map;
|
|
3154
|
+
}
|
|
3155
|
+
function buildRefRecordMap(detail) {
|
|
3156
|
+
const { schema } = detail.collection;
|
|
3157
|
+
const map = {};
|
|
3158
|
+
for (const item of detail.items) {
|
|
3159
|
+
const slugRaw = item[schema.primaryKey];
|
|
3160
|
+
if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = (0, _mulmoclaude_core_collection.deriveAll)(schema, item, {});
|
|
3133
3161
|
}
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3162
|
+
return map;
|
|
3163
|
+
}
|
|
3164
|
+
function sortedRefOptions(map) {
|
|
3165
|
+
return Object.entries(map).map(([slug, display]) => ({
|
|
3166
|
+
slug,
|
|
3167
|
+
display
|
|
3168
|
+
})).sort((left, right) => left.display.localeCompare(right.display));
|
|
3169
|
+
}
|
|
3170
|
+
/** Dropdown options for an `embed` field's per-record picker: every
|
|
3171
|
+
* record in the target collection, labelled by its name/title (or
|
|
3172
|
+
* primary key), skipping records without a slug and sorted by label. */
|
|
3173
|
+
function buildEmbedOptions(schema, items) {
|
|
3174
|
+
const { fields, primaryKey } = schema;
|
|
3175
|
+
const displayField = displayFieldFor(fields, primaryKey);
|
|
3176
|
+
return items.map((item) => {
|
|
3177
|
+
const slug = String(item[primaryKey] ?? "");
|
|
3178
|
+
const labelRaw = item[displayField];
|
|
3146
3179
|
return {
|
|
3147
|
-
|
|
3148
|
-
|
|
3180
|
+
slug,
|
|
3181
|
+
display: typeof labelRaw === "string" && labelRaw.length > 0 ? labelRaw : slug
|
|
3149
3182
|
};
|
|
3150
|
-
}
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
display: embedValue(subField, value, item)
|
|
3174
|
-
});
|
|
3175
|
-
}
|
|
3176
|
-
out[key] = {
|
|
3177
|
-
found: Boolean(item),
|
|
3178
|
-
rows,
|
|
3179
|
-
targetSlug: field.to ?? "",
|
|
3180
|
-
recordId: (0, _mulmoclaude_core_collection.embedTargetId)(field, record)
|
|
3181
|
-
};
|
|
3182
|
-
}
|
|
3183
|
-
return out;
|
|
3184
|
-
}
|
|
3185
|
-
function resolveCurrency(field, record) {
|
|
3186
|
-
if (field.currencyField && record) {
|
|
3187
|
-
const code = record[field.currencyField];
|
|
3188
|
-
if (typeof code === "string" && code.trim().length > 0) return code;
|
|
3189
|
-
}
|
|
3190
|
-
return field.currency;
|
|
3191
|
-
}
|
|
3192
|
-
function currencySymbol(currency) {
|
|
3193
|
-
const code = currency && currency.length > 0 ? currency : "USD";
|
|
3194
|
-
try {
|
|
3195
|
-
return new Intl.NumberFormat(locale.value, {
|
|
3196
|
-
style: "currency",
|
|
3197
|
-
currency: code
|
|
3198
|
-
}).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
|
|
3199
|
-
} catch {
|
|
3200
|
-
return code;
|
|
3201
|
-
}
|
|
3202
|
-
}
|
|
3203
|
-
function formatMoney(value, currency, displayLocale) {
|
|
3204
|
-
if (value === void 0 || value === "") return "—";
|
|
3205
|
-
const amount = typeof value === "number" ? value : Number(value);
|
|
3206
|
-
if (!Number.isFinite(amount)) return String(value);
|
|
3207
|
-
const currencyCode = currency && currency.length > 0 ? currency : "USD";
|
|
3183
|
+
}).filter((opt) => opt.slug.length > 0).sort((left, right) => left.display.localeCompare(right.display));
|
|
3184
|
+
}
|
|
3185
|
+
//#endregion
|
|
3186
|
+
//#region src/vue/useLinkedCollectionCaches.ts
|
|
3187
|
+
/** The de-duplicated ref + embed target slugs a schema links to. `allTargets`
|
|
3188
|
+
* is the union (each target fetched once even when both ref'd and embedded). */
|
|
3189
|
+
function linkedTargets(schema) {
|
|
3190
|
+
const refTargets = new Set(uniqueRefTargets(schema));
|
|
3191
|
+
const embedTargets = new Set(uniqueEmbedTargets(schema));
|
|
3192
|
+
return {
|
|
3193
|
+
refTargets,
|
|
3194
|
+
embedTargets,
|
|
3195
|
+
allTargets: [.../* @__PURE__ */ new Set([...refTargets, ...embedTargets])]
|
|
3196
|
+
};
|
|
3197
|
+
}
|
|
3198
|
+
/** Fan-out fetch that hydrates the linked-collection caches. Best-effort: a
|
|
3199
|
+
* target whose fetch *rejects* (vs. resolving `{ ok: false }`) is coerced to a
|
|
3200
|
+
* skip and must not abort the others. Returns null when a quicker subsequent
|
|
3201
|
+
* load has already moved on (stale-write guard) so the caller drops the write.
|
|
3202
|
+
* Pure + injectable (`fetchDetail`, `currentSlug`) so both paths are testable. */
|
|
3203
|
+
async function fetchLinkedCaches(targets, fetchDetail, currentSlug, expectedSlug) {
|
|
3204
|
+
const { refTargets, embedTargets, allTargets } = targets;
|
|
3205
|
+
const results = await Promise.all(allTargets.map(async (target) => {
|
|
3208
3206
|
try {
|
|
3209
|
-
return
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
}
|
|
3207
|
+
return {
|
|
3208
|
+
target,
|
|
3209
|
+
result: await fetchDetail(target)
|
|
3210
|
+
};
|
|
3213
3211
|
} catch {
|
|
3214
|
-
return
|
|
3212
|
+
return {
|
|
3213
|
+
target,
|
|
3214
|
+
result: { ok: false }
|
|
3215
|
+
};
|
|
3215
3216
|
}
|
|
3217
|
+
}));
|
|
3218
|
+
if (currentSlug() !== expectedSlug) return null;
|
|
3219
|
+
const refCache = {};
|
|
3220
|
+
const refRecordCache = {};
|
|
3221
|
+
const embedCache = {};
|
|
3222
|
+
for (const { target, result } of results) {
|
|
3223
|
+
if (!result.ok) continue;
|
|
3224
|
+
if (refTargets.has(target)) {
|
|
3225
|
+
refCache[target] = buildRefDisplayMap(result.data);
|
|
3226
|
+
refRecordCache[target] = buildRefRecordMap(result.data);
|
|
3227
|
+
}
|
|
3228
|
+
if (embedTargets.has(target)) embedCache[target] = {
|
|
3229
|
+
schema: result.data.collection.schema,
|
|
3230
|
+
items: result.data.items
|
|
3231
|
+
};
|
|
3216
3232
|
}
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
}
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
}
|
|
3232
|
-
function detailText(value) {
|
|
3233
|
-
if (value === void 0 || value === null || value === "") return "—";
|
|
3234
|
-
return String(value);
|
|
3235
|
-
}
|
|
3236
|
-
function tableRows(value) {
|
|
3237
|
-
if (!Array.isArray(value)) return [];
|
|
3238
|
-
return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
|
|
3239
|
-
}
|
|
3240
|
-
function hasTableRows(value) {
|
|
3241
|
-
return tableRows(value).length > 0;
|
|
3242
|
-
}
|
|
3243
|
-
function formatSubCell(subField, value, record) {
|
|
3244
|
-
if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale.value);
|
|
3245
|
-
if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return refDisplay(subField.to, value);
|
|
3246
|
-
return formatCell(value, subField.type);
|
|
3247
|
-
}
|
|
3248
|
-
function inputTypeFor(type) {
|
|
3249
|
-
if (type === "email") return "email";
|
|
3250
|
-
if (type === "number") return "number";
|
|
3251
|
-
if (type === "money") return "number";
|
|
3252
|
-
if (type === "date") return "date";
|
|
3253
|
-
if (type === "datetime") return "datetime-local";
|
|
3254
|
-
return "text";
|
|
3255
|
-
}
|
|
3256
|
-
const stepFor = stepForFieldType;
|
|
3257
|
-
function evaluateDerivedAgainstItem(field, fieldKey, item) {
|
|
3258
|
-
if (!field.formula || !collection.value) return null;
|
|
3259
|
-
const result = (0, _mulmoclaude_core_collection.deriveAll)(collection.value.schema, item, refRecordCache.value)[fieldKey];
|
|
3260
|
-
return typeof result === "number" && Number.isFinite(result) ? result : null;
|
|
3233
|
+
return {
|
|
3234
|
+
refCache,
|
|
3235
|
+
refRecordCache,
|
|
3236
|
+
embedCache
|
|
3237
|
+
};
|
|
3238
|
+
}
|
|
3239
|
+
function useLinkedCollectionCaches(collection) {
|
|
3240
|
+
const refCache = (0, vue.ref)({});
|
|
3241
|
+
const refRecordCache = (0, vue.ref)({});
|
|
3242
|
+
const embedCache = (0, vue.ref)({});
|
|
3243
|
+
function resetLinkedCaches() {
|
|
3244
|
+
refCache.value = {};
|
|
3245
|
+
refRecordCache.value = {};
|
|
3246
|
+
embedCache.value = {};
|
|
3261
3247
|
}
|
|
3262
|
-
function
|
|
3263
|
-
|
|
3264
|
-
if (
|
|
3265
|
-
|
|
3248
|
+
async function loadLinkedCollections(schema, expectedSlug) {
|
|
3249
|
+
const targets = linkedTargets(schema);
|
|
3250
|
+
if (targets.allTargets.length === 0) return;
|
|
3251
|
+
const binding = collectionUi();
|
|
3252
|
+
const snapshot = await fetchLinkedCaches(targets, (slug) => binding.fetchCollectionDetail(slug), () => collection.value?.slug, expectedSlug);
|
|
3253
|
+
if (!snapshot) return;
|
|
3254
|
+
refCache.value = snapshot.refCache;
|
|
3255
|
+
refRecordCache.value = snapshot.refRecordCache;
|
|
3256
|
+
embedCache.value = snapshot.embedCache;
|
|
3266
3257
|
}
|
|
3267
3258
|
return {
|
|
3268
3259
|
refCache,
|
|
3269
3260
|
refRecordCache,
|
|
3270
3261
|
embedCache,
|
|
3271
3262
|
resetLinkedCaches,
|
|
3272
|
-
loadLinkedCollections
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3263
|
+
loadLinkedCollections
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
//#endregion
|
|
3267
|
+
//#region src/vue/useCollectionRendering.renderers.ts
|
|
3268
|
+
function lookupRefDisplay(refCache, targetSlug, itemSlug) {
|
|
3269
|
+
const map = refCache[targetSlug];
|
|
3270
|
+
return map && map[itemSlug] || itemSlug;
|
|
3271
|
+
}
|
|
3272
|
+
function refOptionsFor(refCache, targetSlug) {
|
|
3273
|
+
const map = refCache[targetSlug];
|
|
3274
|
+
return map ? sortedRefOptions(map) : [];
|
|
3275
|
+
}
|
|
3276
|
+
/** Dropdown options for an `embed` field's per-record picker (`idField`):
|
|
3277
|
+
* every record in the target collection, labelled by its name/title (or
|
|
3278
|
+
* primary key). Built from `embedCache` so it works for embed targets
|
|
3279
|
+
* that aren't also `ref` targets (the profile collection, say). */
|
|
3280
|
+
function embedOptionsFor(embedCache, targetSlug) {
|
|
3281
|
+
const data = embedCache[targetSlug];
|
|
3282
|
+
return data ? buildEmbedOptions(data.schema, data.items) : [];
|
|
3283
|
+
}
|
|
3284
|
+
function resolveEmbed(field, record, embedCache) {
|
|
3285
|
+
if (field.type !== "embed" || !field.to) return {
|
|
3286
|
+
schema: null,
|
|
3287
|
+
item: null
|
|
3288
|
+
};
|
|
3289
|
+
const targetId = (0, _mulmoclaude_core_collection.embedTargetId)(field, record);
|
|
3290
|
+
const data = targetId ? embedCache[field.to] : void 0;
|
|
3291
|
+
if (!data) return {
|
|
3292
|
+
schema: null,
|
|
3293
|
+
item: null
|
|
3294
|
+
};
|
|
3295
|
+
const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === targetId) ?? null;
|
|
3296
|
+
return {
|
|
3297
|
+
schema: data.schema,
|
|
3298
|
+
item
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
function formatEmbedValue(field, value, record, locale) {
|
|
3302
|
+
if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale);
|
|
3303
|
+
return detailText(value);
|
|
3304
|
+
}
|
|
3305
|
+
/** Build the read-only embed view-models for one record. A function of the
|
|
3306
|
+
* open record (not a bare computed) because a per-record `idField` embed
|
|
3307
|
+
* resolves a different target per row. `schema` is the OPEN collection's
|
|
3308
|
+
* schema (null when no collection is loaded → no views). */
|
|
3309
|
+
function buildEmbedViews(schema, embedCache, record, locale) {
|
|
3310
|
+
const out = {};
|
|
3311
|
+
if (!schema) return out;
|
|
3312
|
+
for (const [key, field] of Object.entries(schema.fields)) {
|
|
3313
|
+
if (field.type !== "embed") continue;
|
|
3314
|
+
const { schema: targetSchema, item } = resolveEmbed(field, record, embedCache);
|
|
3315
|
+
const rows = [];
|
|
3316
|
+
if (targetSchema && item) for (const [subKey, subField] of Object.entries(targetSchema.fields)) {
|
|
3317
|
+
const value = item[subKey];
|
|
3318
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
3319
|
+
rows.push({
|
|
3320
|
+
key: subKey,
|
|
3321
|
+
label: subField.label,
|
|
3322
|
+
type: subField.type,
|
|
3323
|
+
value,
|
|
3324
|
+
display: formatEmbedValue(subField, value, item, locale)
|
|
3325
|
+
});
|
|
3326
|
+
}
|
|
3327
|
+
out[key] = {
|
|
3328
|
+
found: Boolean(item),
|
|
3329
|
+
rows,
|
|
3330
|
+
targetSlug: field.to ?? "",
|
|
3331
|
+
recordId: (0, _mulmoclaude_core_collection.embedTargetId)(field, record)
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
return out;
|
|
3335
|
+
}
|
|
3336
|
+
function renderSubCell(subField, value, record, refCache, locale) {
|
|
3337
|
+
if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale);
|
|
3338
|
+
if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return lookupRefDisplay(refCache, subField.to, value);
|
|
3339
|
+
return formatCell(value, subField.type);
|
|
3340
|
+
}
|
|
3341
|
+
function evaluateDerived(field, fieldKey, item, schema, refRecords) {
|
|
3342
|
+
if (field.type !== "derived" || !schema) return null;
|
|
3343
|
+
const result = (0, _mulmoclaude_core_collection.deriveAll)(schema, item, refRecords)[fieldKey];
|
|
3344
|
+
return typeof result === "number" && Number.isFinite(result) ? result : null;
|
|
3345
|
+
}
|
|
3346
|
+
function renderDerived(field, computedValue, record, locale) {
|
|
3347
|
+
if (computedValue === null || computedValue === void 0) return "—";
|
|
3348
|
+
const display = field.type === "derived" ? field.display : void 0;
|
|
3349
|
+
if (display === "money") return formatMoney(computedValue, resolveCurrency(field, record), locale);
|
|
3350
|
+
return formatCell(computedValue, display ?? "number");
|
|
3351
|
+
}
|
|
3352
|
+
//#endregion
|
|
3353
|
+
//#region src/vue/useCollectionRendering.ts
|
|
3354
|
+
var STATELESS_RENDERERS = {
|
|
3355
|
+
resolveCurrency,
|
|
3356
|
+
formatMoney,
|
|
3357
|
+
formatCell,
|
|
3358
|
+
detailText,
|
|
3359
|
+
isExternalUrl,
|
|
3360
|
+
tableRows,
|
|
3361
|
+
hasTableRows,
|
|
3362
|
+
inputTypeFor,
|
|
3363
|
+
stepFor: stepForFieldType,
|
|
3364
|
+
deriveAll: _mulmoclaude_core_collection.deriveAll
|
|
3365
|
+
};
|
|
3366
|
+
function useCollectionRendering(collection, locale) {
|
|
3367
|
+
const caches = useLinkedCollectionCaches(collection);
|
|
3368
|
+
const { refCache, refRecordCache, embedCache } = caches;
|
|
3369
|
+
return {
|
|
3370
|
+
...caches,
|
|
3371
|
+
...STATELESS_RENDERERS,
|
|
3372
|
+
refDisplay: (targetSlug, itemSlug) => lookupRefDisplay(refCache.value, targetSlug, itemSlug),
|
|
3373
|
+
refOptions: (targetSlug) => refOptionsFor(refCache.value, targetSlug),
|
|
3374
|
+
embedOptions: (targetSlug) => embedOptionsFor(embedCache.value, targetSlug),
|
|
3375
|
+
embedViewsFor: (record) => buildEmbedViews(collection.value?.schema ?? null, embedCache.value, record, locale.value),
|
|
3376
|
+
currencySymbol: (currency) => currencySymbolForLocale(currency, locale.value),
|
|
3377
|
+
artifactUrl: (value) => collectionUi().fileAssetUrl(value),
|
|
3378
|
+
fileRoutePath: (value) => collectionUi().fileRoutePath(value),
|
|
3379
|
+
formatSubCell: (subField, value, record) => renderSubCell(subField, value, record, refCache.value, locale.value),
|
|
3380
|
+
evaluateDerivedAgainstItem: (field, fieldKey, item) => evaluateDerived(field, fieldKey, item, collection.value?.schema ?? null, refRecordCache.value),
|
|
3381
|
+
derivedDisplay: (field, computedValue, record) => renderDerived(field, computedValue, record, locale.value)
|
|
3293
3382
|
};
|
|
3294
3383
|
}
|
|
3295
3384
|
//#endregion
|
|
@@ -3436,166 +3525,175 @@ var _hoisted_26$1 = [
|
|
|
3436
3525
|
"onClick"
|
|
3437
3526
|
];
|
|
3438
3527
|
var _hoisted_27$1 = { class: "material-icons text-sm" };
|
|
3439
|
-
var _hoisted_28 = [
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
var
|
|
3445
|
-
|
|
3528
|
+
var _hoisted_28 = [
|
|
3529
|
+
"title",
|
|
3530
|
+
"aria-label",
|
|
3531
|
+
"aria-expanded"
|
|
3532
|
+
];
|
|
3533
|
+
var _hoisted_29 = {
|
|
3534
|
+
key: 0,
|
|
3535
|
+
class: "absolute left-0 top-full mt-1 z-20 min-w-max rounded border border-slate-200 bg-white shadow-lg py-1",
|
|
3536
|
+
"data-testid": "collection-view-add-menu"
|
|
3537
|
+
};
|
|
3538
|
+
var _hoisted_30 = ["title", "aria-label"];
|
|
3539
|
+
var _hoisted_31 = ["value", "aria-label"];
|
|
3540
|
+
var _hoisted_32 = ["value"];
|
|
3541
|
+
var _hoisted_33 = ["value", "aria-label"];
|
|
3542
|
+
var _hoisted_34 = ["value"];
|
|
3543
|
+
var _hoisted_35 = {
|
|
3446
3544
|
key: 3,
|
|
3447
3545
|
class: "text-[10px] text-slate-400 font-bold uppercase tracking-wider select-none"
|
|
3448
3546
|
};
|
|
3449
|
-
var
|
|
3547
|
+
var _hoisted_36 = {
|
|
3450
3548
|
key: 3,
|
|
3451
3549
|
class: "mx-6 mt-4 rounded-xl border border-amber-200 bg-amber-50/60 p-4 text-sm text-amber-900 shadow-sm flex items-center gap-3",
|
|
3452
3550
|
"data-testid": "collections-data-issues"
|
|
3453
3551
|
};
|
|
3454
|
-
var
|
|
3455
|
-
var
|
|
3456
|
-
var
|
|
3552
|
+
var _hoisted_37 = { class: "flex-1" };
|
|
3553
|
+
var _hoisted_38 = { class: "flex-1 overflow-auto" };
|
|
3554
|
+
var _hoisted_39 = {
|
|
3457
3555
|
key: 0,
|
|
3458
3556
|
class: "flex flex-col items-center justify-center py-20 text-sm text-slate-500 gap-3"
|
|
3459
3557
|
};
|
|
3460
|
-
var
|
|
3558
|
+
var _hoisted_40 = {
|
|
3461
3559
|
key: 1,
|
|
3462
3560
|
class: "m-6 rounded-xl border border-red-200 bg-red-50/50 p-4 text-sm text-red-800 shadow-sm flex items-center gap-3"
|
|
3463
3561
|
};
|
|
3464
|
-
var
|
|
3465
|
-
var
|
|
3562
|
+
var _hoisted_41 = { key: 2 };
|
|
3563
|
+
var _hoisted_42 = {
|
|
3466
3564
|
key: 3,
|
|
3467
3565
|
class: "p-4"
|
|
3468
3566
|
};
|
|
3469
|
-
var
|
|
3567
|
+
var _hoisted_43 = {
|
|
3470
3568
|
key: 4,
|
|
3471
3569
|
class: "h-full flex flex-col"
|
|
3472
3570
|
};
|
|
3473
|
-
var
|
|
3571
|
+
var _hoisted_44 = {
|
|
3474
3572
|
key: 0,
|
|
3475
3573
|
class: "m-3 mb-0 rounded-xl border border-red-200 bg-red-50/50 p-4 text-sm text-red-800 shadow-sm flex items-center gap-3",
|
|
3476
3574
|
"data-testid": "collections-inline-error"
|
|
3477
3575
|
};
|
|
3478
|
-
var
|
|
3479
|
-
var
|
|
3480
|
-
var
|
|
3481
|
-
var
|
|
3576
|
+
var _hoisted_45 = { class: "flex-1" };
|
|
3577
|
+
var _hoisted_46 = ["aria-label"];
|
|
3578
|
+
var _hoisted_47 = { class: "flex-1 min-h-0 px-3 py-2" };
|
|
3579
|
+
var _hoisted_48 = {
|
|
3482
3580
|
key: 5,
|
|
3483
3581
|
class: "h-full",
|
|
3484
3582
|
"data-testid": "collection-custom-view-body"
|
|
3485
3583
|
};
|
|
3486
|
-
var
|
|
3584
|
+
var _hoisted_49 = {
|
|
3487
3585
|
key: 6,
|
|
3488
3586
|
class: "flex flex-col items-center justify-center py-20 text-sm text-slate-400 gap-2"
|
|
3489
3587
|
};
|
|
3490
|
-
var
|
|
3491
|
-
var
|
|
3588
|
+
var _hoisted_50 = { class: "font-semibold text-slate-600" };
|
|
3589
|
+
var _hoisted_51 = {
|
|
3492
3590
|
key: 7,
|
|
3493
3591
|
class: "flex flex-col items-center justify-center py-20 text-sm text-slate-400 gap-2"
|
|
3494
3592
|
};
|
|
3495
|
-
var
|
|
3496
|
-
var
|
|
3593
|
+
var _hoisted_52 = { class: "font-semibold text-slate-600" };
|
|
3594
|
+
var _hoisted_53 = {
|
|
3497
3595
|
key: 8,
|
|
3498
3596
|
class: "overflow-x-auto [container-type:inline-size]"
|
|
3499
3597
|
};
|
|
3500
|
-
var
|
|
3598
|
+
var _hoisted_54 = {
|
|
3501
3599
|
key: 0,
|
|
3502
3600
|
class: "m-4 rounded-xl border border-red-200 bg-red-50/50 p-4 text-sm text-red-800 shadow-sm flex items-center gap-3",
|
|
3503
3601
|
"data-testid": "collections-inline-error"
|
|
3504
3602
|
};
|
|
3505
|
-
var
|
|
3506
|
-
var
|
|
3507
|
-
var
|
|
3508
|
-
var
|
|
3509
|
-
var
|
|
3510
|
-
var
|
|
3511
|
-
var
|
|
3512
|
-
var
|
|
3603
|
+
var _hoisted_55 = { class: "flex-1" };
|
|
3604
|
+
var _hoisted_56 = ["aria-label"];
|
|
3605
|
+
var _hoisted_57 = { class: "min-w-full text-xs" };
|
|
3606
|
+
var _hoisted_58 = { class: "bg-slate-50 border-b border-slate-200" };
|
|
3607
|
+
var _hoisted_59 = ["aria-sort"];
|
|
3608
|
+
var _hoisted_60 = { class: "flex items-center gap-1" };
|
|
3609
|
+
var _hoisted_61 = ["title"];
|
|
3610
|
+
var _hoisted_62 = [
|
|
3513
3611
|
"data-testid",
|
|
3514
3612
|
"aria-label",
|
|
3515
3613
|
"onClick",
|
|
3516
3614
|
"onPointerenter"
|
|
3517
3615
|
];
|
|
3518
|
-
var
|
|
3519
|
-
var
|
|
3520
|
-
var
|
|
3616
|
+
var _hoisted_63 = { class: "material-icons text-base align-middle" };
|
|
3617
|
+
var _hoisted_64 = { class: "divide-y divide-slate-100 bg-white" };
|
|
3618
|
+
var _hoisted_65 = [
|
|
3521
3619
|
"aria-label",
|
|
3522
3620
|
"data-testid",
|
|
3523
3621
|
"onClick",
|
|
3524
3622
|
"onKeydown"
|
|
3525
3623
|
];
|
|
3526
|
-
var
|
|
3624
|
+
var _hoisted_66 = [
|
|
3527
3625
|
"checked",
|
|
3528
3626
|
"disabled",
|
|
3529
3627
|
"data-testid",
|
|
3530
3628
|
"aria-label",
|
|
3531
3629
|
"onChange"
|
|
3532
3630
|
];
|
|
3533
|
-
var
|
|
3631
|
+
var _hoisted_67 = [
|
|
3534
3632
|
"checked",
|
|
3535
3633
|
"disabled",
|
|
3536
3634
|
"data-testid",
|
|
3537
3635
|
"aria-label",
|
|
3538
3636
|
"onChange"
|
|
3539
3637
|
];
|
|
3540
|
-
var
|
|
3638
|
+
var _hoisted_68 = {
|
|
3541
3639
|
key: 2,
|
|
3542
3640
|
class: "block truncate"
|
|
3543
3641
|
};
|
|
3544
|
-
var
|
|
3642
|
+
var _hoisted_69 = [
|
|
3545
3643
|
"href",
|
|
3546
3644
|
"tabindex",
|
|
3547
3645
|
"data-testid",
|
|
3548
3646
|
"onClick",
|
|
3549
3647
|
"onKeydown"
|
|
3550
3648
|
];
|
|
3551
|
-
var
|
|
3649
|
+
var _hoisted_70 = [
|
|
3552
3650
|
"value",
|
|
3553
3651
|
"disabled",
|
|
3554
3652
|
"data-testid",
|
|
3555
3653
|
"aria-label",
|
|
3556
3654
|
"onChange"
|
|
3557
3655
|
];
|
|
3558
|
-
var
|
|
3656
|
+
var _hoisted_71 = {
|
|
3559
3657
|
key: 0,
|
|
3560
3658
|
value: ""
|
|
3561
3659
|
};
|
|
3562
|
-
var
|
|
3563
|
-
var
|
|
3660
|
+
var _hoisted_72 = ["value"];
|
|
3661
|
+
var _hoisted_73 = {
|
|
3564
3662
|
key: 4,
|
|
3565
3663
|
class: "block truncate tabular-nums font-semibold text-slate-900"
|
|
3566
3664
|
};
|
|
3567
|
-
var
|
|
3665
|
+
var _hoisted_74 = {
|
|
3568
3666
|
key: 5,
|
|
3569
3667
|
class: "inline-flex items-center gap-1 px-2 py-0.5 rounded-lg text-[10px] font-bold bg-slate-100 text-slate-600 border border-slate-200/40"
|
|
3570
3668
|
};
|
|
3571
|
-
var
|
|
3669
|
+
var _hoisted_75 = {
|
|
3572
3670
|
key: 6,
|
|
3573
3671
|
class: "inline-block truncate tabular-nums font-bold text-indigo-900 bg-indigo-50/50 px-1.5 py-0.5 rounded border border-indigo-100/50"
|
|
3574
3672
|
};
|
|
3575
|
-
var _hoisted_75 = ["href", "data-testid"];
|
|
3576
3673
|
var _hoisted_76 = ["href", "data-testid"];
|
|
3577
|
-
var _hoisted_77 = [
|
|
3674
|
+
var _hoisted_77 = ["href", "data-testid"];
|
|
3675
|
+
var _hoisted_78 = [
|
|
3578
3676
|
"href",
|
|
3579
3677
|
"data-testid",
|
|
3580
3678
|
"onClick"
|
|
3581
3679
|
];
|
|
3582
|
-
var
|
|
3680
|
+
var _hoisted_79 = {
|
|
3583
3681
|
key: 10,
|
|
3584
3682
|
class: "block truncate text-slate-600"
|
|
3585
3683
|
};
|
|
3586
|
-
var
|
|
3587
|
-
var
|
|
3588
|
-
var
|
|
3589
|
-
var
|
|
3684
|
+
var _hoisted_80 = { class: "bg-white rounded-2xl shadow-2xl w-full max-w-xl flex flex-col border border-slate-200 overflow-hidden" };
|
|
3685
|
+
var _hoisted_81 = { class: "px-6 py-4 border-b border-slate-100 flex items-center gap-3 bg-slate-50/50" };
|
|
3686
|
+
var _hoisted_82 = { class: "flex-1" };
|
|
3687
|
+
var _hoisted_83 = {
|
|
3590
3688
|
id: "collections-chat-title",
|
|
3591
3689
|
class: "text-sm font-bold text-slate-800 uppercase tracking-wide"
|
|
3592
3690
|
};
|
|
3593
|
-
var
|
|
3594
|
-
var
|
|
3595
|
-
var
|
|
3596
|
-
var
|
|
3597
|
-
var
|
|
3598
|
-
var
|
|
3691
|
+
var _hoisted_84 = { class: "text-xs text-slate-400 font-semibold" };
|
|
3692
|
+
var _hoisted_85 = ["aria-label"];
|
|
3693
|
+
var _hoisted_86 = { class: "px-6 py-5" };
|
|
3694
|
+
var _hoisted_87 = ["placeholder", "onKeydown"];
|
|
3695
|
+
var _hoisted_88 = { class: "px-6 py-3.5 border-t border-slate-100 flex items-center justify-end gap-2 bg-slate-50/50" };
|
|
3696
|
+
var _hoisted_89 = ["disabled"];
|
|
3599
3697
|
/** `slug` / `selected` are supplied only in EMBEDDED mode (the
|
|
3600
3698
|
* `presentCollection` chat card mounts this component and drives both
|
|
3601
3699
|
* from the tool result). In standalone route mode (the
|
|
@@ -3771,12 +3869,12 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
3771
3869
|
return scalarSortValue(field, item[key]);
|
|
3772
3870
|
}
|
|
3773
3871
|
/** Derived rows sort by their display type: money/number → numeric,
|
|
3774
|
-
* date
|
|
3872
|
+
* date → epoch, anything else → the enriched value as a string. */
|
|
3775
3873
|
function derivedSortValue(field, key, item) {
|
|
3776
|
-
const
|
|
3874
|
+
const display = field.type === "derived" ? field.display : void 0;
|
|
3777
3875
|
if (display === void 0 || display === "number" || display === "money") return (0, _mulmoclaude_core_collection.numericSortValue)(evaluateDerivedAgainstItem(field, key, item));
|
|
3778
3876
|
const enriched = collection.value ? render.deriveAll(collection.value.schema, item, render.refRecordCache.value) : item;
|
|
3779
|
-
if (display === "date"
|
|
3877
|
+
if (display === "date") return (0, _mulmoclaude_core_collection.dateSortValue)(enriched[key]);
|
|
3780
3878
|
return (0, _mulmoclaude_core_collection.stringSortValue)(enriched[key]);
|
|
3781
3879
|
}
|
|
3782
3880
|
const sortedItems = (0, vue.computed)(() => {
|
|
@@ -3954,10 +4052,12 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
3954
4052
|
function buildChatSeed(slug, message, itemId) {
|
|
3955
4053
|
const current = collection.value;
|
|
3956
4054
|
if (current?.source !== "feed") return itemId ? `/${slug} id=${itemId} ${message}` : `/${slug} ${message}`;
|
|
4055
|
+
const dataPath = current.schema.dataPath ?? `data/feeds/${slug}`;
|
|
4056
|
+
const scoped = itemId ? `(for record \`${itemId}\`) ${message}` : message;
|
|
3957
4057
|
return t("collectionsView.feedChatSeed", {
|
|
3958
4058
|
slug,
|
|
3959
|
-
dataPath
|
|
3960
|
-
message:
|
|
4059
|
+
dataPath,
|
|
4060
|
+
message: scoped
|
|
3961
4061
|
});
|
|
3962
4062
|
}
|
|
3963
4063
|
/** Start a new general-role chat seeded from the current view. */
|
|
@@ -4032,7 +4132,10 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4032
4132
|
enumOriginallyEmpty.value = snapshotEmptyEnums(result.data.collection.schema, result.data.items);
|
|
4033
4133
|
await render.loadLinkedCollections(result.data.collection.schema, slug);
|
|
4034
4134
|
if (activeSlug.value !== slug) return;
|
|
4035
|
-
if (viewing.value)
|
|
4135
|
+
if (viewing.value) {
|
|
4136
|
+
const openId = String(viewing.value[result.data.collection.schema.primaryKey] ?? "");
|
|
4137
|
+
viewing.value = findItemById(openId) ?? null;
|
|
4138
|
+
}
|
|
4036
4139
|
}
|
|
4037
4140
|
function maybeAutoRefreshFeed(slug) {
|
|
4038
4141
|
if (embedded.value) return;
|
|
@@ -4121,16 +4224,41 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4121
4224
|
* page only (the seed starts a chat). Feeds qualify too — their views are
|
|
4122
4225
|
* authored under feeds/<slug>/ and the seed prompt points there. */
|
|
4123
4226
|
const canAddCustomView = (0, vue.computed)(() => Boolean(collection.value) && !embedded.value);
|
|
4227
|
+
const addMenuOpen = (0, vue.ref)(false);
|
|
4228
|
+
const addMenuRef = (0, vue.ref)(null);
|
|
4229
|
+
/** Whether authoring a phone (remote app) view is worth offering — mirrors
|
|
4230
|
+
* the selector filter above: without the host's `fetchRemoteView` binding a
|
|
4231
|
+
* mobile view could be authored but never shown here. */
|
|
4232
|
+
const canAddMobileView = (0, vue.computed)(() => Boolean(cui.fetchRemoteView));
|
|
4233
|
+
/** "+" click: open the target chooser, or skip the one-item menu and seed
|
|
4234
|
+
* the desktop prompt directly when mobile views aren't available. */
|
|
4235
|
+
function onAddViewClick() {
|
|
4236
|
+
if (!canAddMobileView.value) {
|
|
4237
|
+
addCustomView("desktop");
|
|
4238
|
+
return;
|
|
4239
|
+
}
|
|
4240
|
+
addMenuOpen.value = !addMenuOpen.value;
|
|
4241
|
+
}
|
|
4242
|
+
function closeAddMenuOnOutsideClick(event) {
|
|
4243
|
+
if (!addMenuRef.value?.contains(event.target)) addMenuOpen.value = false;
|
|
4244
|
+
}
|
|
4245
|
+
(0, vue.watch)(addMenuOpen, (open) => {
|
|
4246
|
+
if (open) document.addEventListener("mousedown", closeAddMenuOnOutsideClick);
|
|
4247
|
+
else document.removeEventListener("mousedown", closeAddMenuOnOutsideClick);
|
|
4248
|
+
});
|
|
4124
4249
|
/** Seed a chat asking Claude to author a new custom view for this collection.
|
|
4125
4250
|
* Reuses the same chat-seed path as collection actions — the host injects a
|
|
4126
4251
|
* templated prompt; Claude asks, authors the HTML, and registers it. The
|
|
4127
4252
|
* authoring base is source-aware: a feed lives under `feeds/<slug>/`, every
|
|
4128
|
-
* other collection under the `data/skills/<slug>/` staging dir.
|
|
4129
|
-
|
|
4253
|
+
* other collection under the `data/skills/<slug>/` staging dir. The prompt
|
|
4254
|
+
* is target-aware: phone views follow the custom-view-remote contract and
|
|
4255
|
+
* register with `target: "mobile"`. */
|
|
4256
|
+
function addCustomView(target) {
|
|
4257
|
+
addMenuOpen.value = false;
|
|
4130
4258
|
const current = collection.value;
|
|
4131
4259
|
if (!current) return;
|
|
4132
4260
|
const base = current.source === "feed" ? `feeds/${current.slug}` : `data/skills/${current.slug}`;
|
|
4133
|
-
const prompt = t("collectionsView.addViewPrompt", {
|
|
4261
|
+
const prompt = t(target === "mobile" ? "collectionsView.addMobileViewPrompt" : "collectionsView.addViewPrompt", {
|
|
4134
4262
|
title: current.title,
|
|
4135
4263
|
base
|
|
4136
4264
|
});
|
|
@@ -4185,7 +4313,8 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4185
4313
|
}
|
|
4186
4314
|
/** Select a custom view by id (builds the `custom:<id>` mode key). */
|
|
4187
4315
|
function setCustomView(viewId) {
|
|
4188
|
-
|
|
4316
|
+
const mode = `custom:${viewId}`;
|
|
4317
|
+
view.value = mode;
|
|
4189
4318
|
}
|
|
4190
4319
|
/** A short, slug-safe id not already used by a loaded record. Collisions
|
|
4191
4320
|
* are astronomically unlikely (32 bits), but we still re-roll a few
|
|
@@ -4343,7 +4472,8 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4343
4472
|
viewing.value = null;
|
|
4344
4473
|
return;
|
|
4345
4474
|
}
|
|
4346
|
-
|
|
4475
|
+
const match = findItemById(selected) ?? null;
|
|
4476
|
+
viewing.value = match;
|
|
4347
4477
|
}
|
|
4348
4478
|
/** Title for the open-mode header: the record's primary-key value
|
|
4349
4479
|
* (e.g. `INV-2026-0001`), falling back to the collection title.
|
|
@@ -4437,19 +4567,17 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4437
4567
|
/** Whether a `toggle` field reads as checked: its projected enum field
|
|
4438
4568
|
* currently equals `onValue`. The toggle stores nothing itself. */
|
|
4439
4569
|
function toggleChecked(item, field) {
|
|
4440
|
-
return field.
|
|
4570
|
+
return field.type === "toggle" && String(item[field.field] ?? "") === field.onValue;
|
|
4441
4571
|
}
|
|
4442
4572
|
/** Flip a `toggle`: write the projected enum field to `offValue` when
|
|
4443
4573
|
* currently checked, else `onValue`. Reuses the inline-edit PUT path
|
|
4444
4574
|
* (optimistic + rollback) — the toggle has no value of its own. */
|
|
4445
4575
|
function commitToggle(item, field) {
|
|
4576
|
+
if (field.type !== "toggle" || !collection.value) return;
|
|
4446
4577
|
const targetKey = field.field;
|
|
4447
|
-
if (!targetKey || !collection.value) return;
|
|
4448
4578
|
const enumField = collection.value.schema.fields[targetKey];
|
|
4449
4579
|
if (!enumField) return;
|
|
4450
|
-
|
|
4451
|
-
if (next === void 0) return;
|
|
4452
|
-
commitInlineEdit(item, targetKey, enumField, next);
|
|
4580
|
+
commitInlineEdit(item, targetKey, enumField, toggleChecked(item, field) ? field.offValue : field.onValue);
|
|
4453
4581
|
}
|
|
4454
4582
|
async function confirmDelete(item) {
|
|
4455
4583
|
if (!collection.value) return;
|
|
@@ -4606,6 +4734,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4606
4734
|
view.value = slug && readCollectionViewMode(slug) || "table";
|
|
4607
4735
|
anchorOverride.value = null;
|
|
4608
4736
|
kanbanOverride.value = null;
|
|
4737
|
+
addMenuOpen.value = false;
|
|
4609
4738
|
sortState.value = storedSortFor(slug);
|
|
4610
4739
|
}
|
|
4611
4740
|
if (slug) loadCollection(slug);
|
|
@@ -4654,6 +4783,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4654
4783
|
changeUnsub = null;
|
|
4655
4784
|
clearLiveRefreshTimer();
|
|
4656
4785
|
if (refreshNoteTimer !== void 0) clearTimeout(refreshNoteTimer);
|
|
4786
|
+
document.removeEventListener("mousedown", closeAddMenuOnOutsideClick);
|
|
4657
4787
|
});
|
|
4658
4788
|
(0, vue.watch)([
|
|
4659
4789
|
activeView,
|
|
@@ -4688,7 +4818,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4688
4818
|
"aria-label": (0, vue.unref)(t)("collectionsView.backToIndex"),
|
|
4689
4819
|
"data-testid": "collections-back",
|
|
4690
4820
|
onClick: goBack
|
|
4691
|
-
}, [..._cache[
|
|
4821
|
+
}, [..._cache[25] || (_cache[25] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "arrow_back", -1)])], 8, _hoisted_3$5)) : (0, vue.createCommentVNode)("", true),
|
|
4692
4822
|
collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4$5, [(0, vue.createElementVNode)("span", _hoisted_5$5, (0, vue.toDisplayString)(collection.value.icon), 1)])) : (0, vue.createCommentVNode)("", true),
|
|
4693
4823
|
(0, vue.createElementVNode)("div", _hoisted_6$4, [(0, vue.createElementVNode)("h1", _hoisted_7$4, (0, vue.toDisplayString)(collection.value?.title ?? (0, vue.unref)(t)("collectionsView.title")), 1), collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8$4, (0, vue.toDisplayString)(collection.value.slug), 1)) : (0, vue.createCommentVNode)("", true)]),
|
|
4694
4824
|
collection.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(pinToggle)), {
|
|
@@ -4717,7 +4847,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4717
4847
|
class: "h-8 px-2.5 flex items-center gap-1 rounded border border-indigo-200 bg-white hover:bg-indigo-50 text-indigo-600 font-bold text-xs transition-colors",
|
|
4718
4848
|
"data-testid": "collections-chat",
|
|
4719
4849
|
onClick: openChat
|
|
4720
|
-
}, [_cache[
|
|
4850
|
+
}, [_cache[26] || (_cache[26] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "forum", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chat")), 1)])) : (0, vue.createCommentVNode)("", true),
|
|
4721
4851
|
((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(collectionActions.value, (action) => {
|
|
4722
4852
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
4723
4853
|
key: action.id,
|
|
@@ -4734,7 +4864,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4734
4864
|
class: "h-8 px-2.5 flex items-center gap-1 rounded bg-indigo-600 hover:bg-indigo-700 text-white font-bold text-xs transition-colors shadow-sm",
|
|
4735
4865
|
"data-testid": "collections-add-item",
|
|
4736
4866
|
onClick: openCreate
|
|
4737
|
-
}, [_cache[
|
|
4867
|
+
}, [_cache[27] || (_cache[27] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.add")), 1)])) : (0, vue.createCommentVNode)("", true),
|
|
4738
4868
|
canDeleteCollection.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
4739
4869
|
key: 6,
|
|
4740
4870
|
type: "button",
|
|
@@ -4743,7 +4873,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4743
4873
|
"aria-label": (0, vue.unref)(t)("collectionsView.deleteCollection"),
|
|
4744
4874
|
"data-testid": "collections-delete",
|
|
4745
4875
|
onClick: confirmCollectionDelete
|
|
4746
|
-
}, [..._cache[
|
|
4876
|
+
}, [..._cache[28] || (_cache[28] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_13$4)) : (0, vue.createCommentVNode)("", true),
|
|
4747
4877
|
canDeleteFeed.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
4748
4878
|
key: 7,
|
|
4749
4879
|
type: "button",
|
|
@@ -4752,11 +4882,11 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4752
4882
|
"aria-label": (0, vue.unref)(t)("collectionsView.deleteFeed"),
|
|
4753
4883
|
"data-testid": "feeds-delete",
|
|
4754
4884
|
onClick: confirmFeedDelete
|
|
4755
|
-
}, [..._cache[
|
|
4885
|
+
}, [..._cache[29] || (_cache[29] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_14$4)) : (0, vue.createCommentVNode)("", true)
|
|
4756
4886
|
])) : (0, vue.createCommentVNode)("", true),
|
|
4757
|
-
refreshNote.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_15$4, [_cache[
|
|
4887
|
+
refreshNote.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_15$4, [_cache[30] || (_cache[30] = (0, vue.createElementVNode)("span", { class: "material-icons text-base text-indigo-600" }, "hourglass_top", -1)), (0, vue.createElementVNode)("span", _hoisted_16$4, (0, vue.toDisplayString)(refreshNote.value), 1)])) : (0, vue.createCommentVNode)("", true),
|
|
4758
4888
|
collection.value && (!__props.hideSearch && items.value.length > 0 || !__props.hideViewToggle && (hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_17$4, [!__props.hideSearch && items.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_18$4, [
|
|
4759
|
-
_cache[
|
|
4889
|
+
_cache[32] || (_cache[32] = (0, vue.createElementVNode)("span", { class: "absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 pointer-events-none" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "search")], -1)),
|
|
4760
4890
|
(0, vue.withDirectives)((0, vue.createElementVNode)("input", {
|
|
4761
4891
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event),
|
|
4762
4892
|
type: "text",
|
|
@@ -4770,7 +4900,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4770
4900
|
"aria-label": (0, vue.unref)(t)("collectionsView.clearSearch"),
|
|
4771
4901
|
class: "absolute inset-y-0 right-0 flex items-center pr-2.5 text-slate-400 hover:text-slate-600",
|
|
4772
4902
|
onClick: _cache[1] || (_cache[1] = ($event) => searchQuery.value = "")
|
|
4773
|
-
}, [..._cache[
|
|
4903
|
+
}, [..._cache[31] || (_cache[31] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_20$2)) : (0, vue.createCommentVNode)("", true)
|
|
4774
4904
|
])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_21$2, [
|
|
4775
4905
|
!__props.hideViewToggle && (hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
4776
4906
|
key: 0,
|
|
@@ -4784,7 +4914,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4784
4914
|
"aria-pressed": activeView.value === "table",
|
|
4785
4915
|
"data-testid": "collection-view-toggle-table",
|
|
4786
4916
|
onClick: _cache[2] || (_cache[2] = ($event) => setView("table"))
|
|
4787
|
-
}, [_cache[
|
|
4917
|
+
}, [_cache[33] || (_cache[33] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "table_rows", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewTable")), 1)], 10, _hoisted_23$2),
|
|
4788
4918
|
hasCalendar.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
4789
4919
|
key: 0,
|
|
4790
4920
|
type: "button",
|
|
@@ -4792,7 +4922,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4792
4922
|
"aria-pressed": activeView.value === "calendar",
|
|
4793
4923
|
"data-testid": "collection-view-toggle-calendar",
|
|
4794
4924
|
onClick: _cache[3] || (_cache[3] = ($event) => setView("calendar"))
|
|
4795
|
-
}, [_cache[
|
|
4925
|
+
}, [_cache[34] || (_cache[34] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "calendar_month", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewCalendar")), 1)], 10, _hoisted_24$1)) : (0, vue.createCommentVNode)("", true),
|
|
4796
4926
|
hasKanban.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
4797
4927
|
key: 1,
|
|
4798
4928
|
type: "button",
|
|
@@ -4800,7 +4930,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4800
4930
|
"aria-pressed": activeView.value === "kanban",
|
|
4801
4931
|
"data-testid": "collection-view-toggle-kanban",
|
|
4802
4932
|
onClick: _cache[4] || (_cache[4] = ($event) => setView("kanban"))
|
|
4803
|
-
}, [_cache[
|
|
4933
|
+
}, [_cache[35] || (_cache[35] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "view_kanban", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewKanban")), 1)], 10, _hoisted_25$1)) : (0, vue.createCommentVNode)("", true),
|
|
4804
4934
|
((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(customViews.value, (cv) => {
|
|
4805
4935
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
4806
4936
|
key: cv.id,
|
|
@@ -4811,15 +4941,30 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4811
4941
|
onClick: ($event) => setCustomView(cv.id)
|
|
4812
4942
|
}, [(0, vue.createElementVNode)("span", _hoisted_27$1, (0, vue.toDisplayString)(cv.icon || (cv.target === "mobile" ? "smartphone" : "dashboard_customize")), 1), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(cv.label), 1)], 10, _hoisted_26$1);
|
|
4813
4943
|
}), 128)),
|
|
4814
|
-
canAddCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("
|
|
4944
|
+
canAddCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
4815
4945
|
key: 2,
|
|
4946
|
+
ref_key: "addMenuRef",
|
|
4947
|
+
ref: addMenuRef,
|
|
4948
|
+
class: "relative"
|
|
4949
|
+
}, [(0, vue.createElementVNode)("button", {
|
|
4816
4950
|
type: "button",
|
|
4817
4951
|
class: "h-8 w-8 flex items-center justify-center rounded bg-white text-slate-500 border border-slate-200 hover:bg-slate-50",
|
|
4818
4952
|
title: (0, vue.unref)(t)("collectionsView.addView"),
|
|
4819
4953
|
"aria-label": (0, vue.unref)(t)("collectionsView.addView"),
|
|
4954
|
+
"aria-expanded": addMenuOpen.value,
|
|
4820
4955
|
"data-testid": "collection-view-add",
|
|
4821
|
-
onClick:
|
|
4822
|
-
}, [..._cache[
|
|
4956
|
+
onClick: onAddViewClick
|
|
4957
|
+
}, [..._cache[36] || (_cache[36] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)])], 8, _hoisted_28), addMenuOpen.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_29, [(0, vue.createElementVNode)("button", {
|
|
4958
|
+
type: "button",
|
|
4959
|
+
class: "w-full h-8 px-3 flex items-center gap-2 text-xs font-bold text-slate-600 hover:bg-slate-50",
|
|
4960
|
+
"data-testid": "collection-view-add-desktop",
|
|
4961
|
+
onClick: _cache[5] || (_cache[5] = ($event) => addCustomView("desktop"))
|
|
4962
|
+
}, [_cache[37] || (_cache[37] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "dashboard_customize", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.addViewDesktop")), 1)]), (0, vue.createElementVNode)("button", {
|
|
4963
|
+
type: "button",
|
|
4964
|
+
class: "w-full h-8 px-3 flex items-center gap-2 text-xs font-bold text-slate-600 hover:bg-slate-50",
|
|
4965
|
+
"data-testid": "collection-view-add-mobile",
|
|
4966
|
+
onClick: _cache[6] || (_cache[6] = ($event) => addCustomView("mobile"))
|
|
4967
|
+
}, [_cache[38] || (_cache[38] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "smartphone", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.addViewMobile")), 1)])])) : (0, vue.createCommentVNode)("", true)], 512)) : (0, vue.createCommentVNode)("", true),
|
|
4823
4968
|
canConfigureViews.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
4824
4969
|
key: 3,
|
|
4825
4970
|
type: "button",
|
|
@@ -4827,8 +4972,8 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4827
4972
|
title: (0, vue.unref)(t)("collectionsView.config.open"),
|
|
4828
4973
|
"aria-label": (0, vue.unref)(t)("collectionsView.config.open"),
|
|
4829
4974
|
"data-testid": "collection-config-open",
|
|
4830
|
-
onClick: _cache[
|
|
4831
|
-
}, [..._cache[
|
|
4975
|
+
onClick: _cache[7] || (_cache[7] = ($event) => configOpen.value = true)
|
|
4976
|
+
}, [..._cache[39] || (_cache[39] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "settings", -1)])], 8, _hoisted_30)) : (0, vue.createCommentVNode)("", true)
|
|
4832
4977
|
], 8, _hoisted_22$2)) : (0, vue.createCommentVNode)("", true),
|
|
4833
4978
|
calendarActive.value && dateFields.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
|
|
4834
4979
|
key: 1,
|
|
@@ -4836,42 +4981,42 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4836
4981
|
class: "h-8 px-2 rounded border border-slate-200 bg-white text-xs font-semibold text-slate-600 focus:outline-none focus:border-indigo-500 cursor-pointer",
|
|
4837
4982
|
"aria-label": (0, vue.unref)(t)("collectionsView.calendarFieldLabel"),
|
|
4838
4983
|
"data-testid": "collection-calendar-field",
|
|
4839
|
-
onChange: _cache[
|
|
4984
|
+
onChange: _cache[8] || (_cache[8] = ($event) => anchorOverride.value = $event.target.value)
|
|
4840
4985
|
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(dateFields.value, (key) => {
|
|
4841
4986
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
|
|
4842
4987
|
key,
|
|
4843
4988
|
value: key
|
|
4844
|
-
}, (0, vue.toDisplayString)(collection.value?.schema.fields[key]?.label ?? key), 9,
|
|
4845
|
-
}), 128))], 40,
|
|
4989
|
+
}, (0, vue.toDisplayString)(collection.value?.schema.fields[key]?.label ?? key), 9, _hoisted_32);
|
|
4990
|
+
}), 128))], 40, _hoisted_31)) : (0, vue.createCommentVNode)("", true),
|
|
4846
4991
|
kanbanActive.value && enumFields.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
|
|
4847
4992
|
key: 2,
|
|
4848
4993
|
value: kanbanGroupField.value,
|
|
4849
4994
|
class: "h-8 px-2 rounded border border-slate-200 bg-white text-xs font-semibold text-slate-600 focus:outline-none focus:border-indigo-500 cursor-pointer",
|
|
4850
4995
|
"aria-label": (0, vue.unref)(t)("collectionsView.kanbanFieldLabel"),
|
|
4851
4996
|
"data-testid": "collection-kanban-field",
|
|
4852
|
-
onChange: _cache[
|
|
4997
|
+
onChange: _cache[9] || (_cache[9] = ($event) => kanbanOverride.value = $event.target.value)
|
|
4853
4998
|
}, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(enumFields.value, (key) => {
|
|
4854
4999
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
|
|
4855
5000
|
key,
|
|
4856
5001
|
value: key
|
|
4857
|
-
}, (0, vue.toDisplayString)(collection.value?.schema.fields[key]?.label ?? key), 9,
|
|
4858
|
-
}), 128))], 40,
|
|
4859
|
-
items.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
5002
|
+
}, (0, vue.toDisplayString)(collection.value?.schema.fields[key]?.label ?? key), 9, _hoisted_34);
|
|
5003
|
+
}), 128))], 40, _hoisted_33)) : (0, vue.createCommentVNode)("", true),
|
|
5004
|
+
items.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_35, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.searchSummary", {
|
|
4860
5005
|
shown: filteredItems.value.length,
|
|
4861
5006
|
total: items.value.length
|
|
4862
5007
|
})), 1)) : (0, vue.createCommentVNode)("", true)
|
|
4863
5008
|
])])) : (0, vue.createCommentVNode)("", true),
|
|
4864
|
-
collection.value && dataIssues.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
4865
|
-
_cache[
|
|
4866
|
-
(0, vue.createElementVNode)("span",
|
|
5009
|
+
collection.value && dataIssues.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_36, [
|
|
5010
|
+
_cache[41] || (_cache[41] = (0, vue.createElementVNode)("span", { class: "material-icons text-amber-600" }, "warning", -1)),
|
|
5011
|
+
(0, vue.createElementVNode)("span", _hoisted_37, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dataIssuesDetected", { count: dataIssues.value.length })), 1),
|
|
4867
5012
|
(0, vue.createElementVNode)("button", {
|
|
4868
5013
|
type: "button",
|
|
4869
5014
|
class: "h-8 px-2.5 flex items-center gap-1 rounded border border-amber-300 bg-white hover:bg-amber-100 text-amber-700 font-bold text-xs transition-colors",
|
|
4870
5015
|
"data-testid": "collections-repair",
|
|
4871
5016
|
onClick: repairCollection
|
|
4872
|
-
}, [_cache[
|
|
5017
|
+
}, [_cache[40] || (_cache[40] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "build", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.repair")), 1)])
|
|
4873
5018
|
])) : (0, vue.createCommentVNode)("", true),
|
|
4874
|
-
(0, vue.createElementVNode)("div",
|
|
5019
|
+
(0, vue.createElementVNode)("div", _hoisted_38, [loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_39, [_cache[42] || (_cache[42] = (0, vue.createElementVNode)("div", { class: "h-8 w-8 border-2 border-indigo-600/20 border-t-indigo-600 rounded-full animate-spin" }, null, -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.loading")), 1)])) : loadError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_40, [_cache[43] || (_cache[43] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(loadError.value === "not-found" ? (0, vue.unref)(t)("collectionsView.notFound") : `${(0, vue.unref)(t)("collectionsView.loadFailed")}: ${loadError.value}`), 1)])) : !collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_41)) : calendarActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_42, [(0, vue.createVNode)(CollectionCalendarView_default, {
|
|
4875
5020
|
schema: collection.value.schema,
|
|
4876
5021
|
items: filteredItems.value,
|
|
4877
5022
|
"anchor-field": calendarAnchorField.value,
|
|
@@ -4907,7 +5052,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4907
5052
|
}, {
|
|
4908
5053
|
detail: (0, vue.withCtx)(() => [(0, vue.createVNode)(CollectionRecordPanel_default, {
|
|
4909
5054
|
editing: editing.value,
|
|
4910
|
-
"onUpdate:editing": _cache[
|
|
5055
|
+
"onUpdate:editing": _cache[10] || (_cache[10] = ($event) => editing.value = $event),
|
|
4911
5056
|
collection: collection.value,
|
|
4912
5057
|
viewing: viewing.value,
|
|
4913
5058
|
saving: saving.value,
|
|
@@ -4925,7 +5070,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4925
5070
|
onCancel: cancelEditor,
|
|
4926
5071
|
onEdit: editFromView,
|
|
4927
5072
|
onClose: onDayClose,
|
|
4928
|
-
onDelete: _cache[
|
|
5073
|
+
onDelete: _cache[11] || (_cache[11] = ($event) => viewing.value && confirmDelete(viewing.value)),
|
|
4929
5074
|
onRunAction: runAction,
|
|
4930
5075
|
onItemChat
|
|
4931
5076
|
}, null, 8, [
|
|
@@ -4956,16 +5101,16 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4956
5101
|
"selected",
|
|
4957
5102
|
"can-create",
|
|
4958
5103
|
"show-detail"
|
|
4959
|
-
])) : (0, vue.createCommentVNode)("", true)])) : kanbanActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
4960
|
-
_cache[
|
|
4961
|
-
(0, vue.createElementVNode)("span",
|
|
5104
|
+
])) : (0, vue.createCommentVNode)("", true)])) : kanbanActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_43, [inlineError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_44, [
|
|
5105
|
+
_cache[45] || (_cache[45] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
|
|
5106
|
+
(0, vue.createElementVNode)("span", _hoisted_45, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
|
|
4962
5107
|
(0, vue.createElementVNode)("button", {
|
|
4963
5108
|
type: "button",
|
|
4964
5109
|
class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
|
|
4965
5110
|
"aria-label": (0, vue.unref)(t)("common.close"),
|
|
4966
|
-
onClick: _cache[
|
|
4967
|
-
}, [..._cache[
|
|
4968
|
-
])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div",
|
|
5111
|
+
onClick: _cache[12] || (_cache[12] = ($event) => inlineError.value = null)
|
|
5112
|
+
}, [..._cache[44] || (_cache[44] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_46)
|
|
5113
|
+
])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_47, [(0, vue.createVNode)(CollectionKanbanView_default, {
|
|
4969
5114
|
schema: collection.value.schema,
|
|
4970
5115
|
items: filteredItems.value,
|
|
4971
5116
|
"group-field": kanbanGroupField.value,
|
|
@@ -4979,7 +5124,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4979
5124
|
"group-field",
|
|
4980
5125
|
"selected",
|
|
4981
5126
|
"notified"
|
|
4982
|
-
])])])) : activeCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
5127
|
+
])])])) : activeCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_48, [activeCustomView.value.target === "mobile" ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionRemoteViewPreview_default, {
|
|
4983
5128
|
key: 0,
|
|
4984
5129
|
slug: collection.value.slug,
|
|
4985
5130
|
view: activeCustomView.value,
|
|
@@ -4990,32 +5135,32 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
4990
5135
|
view: activeCustomView.value,
|
|
4991
5136
|
onOpenItem: onCustomViewOpenItem,
|
|
4992
5137
|
onStartChat: onCustomViewStartChat
|
|
4993
|
-
}, null, 8, ["slug", "view"]))])) : items.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
4994
|
-
_cache[
|
|
4995
|
-
(0, vue.createElementVNode)("p",
|
|
5138
|
+
}, null, 8, ["slug", "view"]))])) : items.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_49, [_cache[46] || (_cache[46] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "folder_open", -1)), (0, vue.createElementVNode)("p", _hoisted_50, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.itemsEmpty")), 1)])) : filteredItems.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_51, [
|
|
5139
|
+
_cache[47] || (_cache[47] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "search_off", -1)),
|
|
5140
|
+
(0, vue.createElementVNode)("p", _hoisted_52, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noMatchingItems")), 1),
|
|
4996
5141
|
(0, vue.createElementVNode)("button", {
|
|
4997
5142
|
type: "button",
|
|
4998
5143
|
class: "text-xs text-indigo-600 font-semibold hover:underline",
|
|
4999
|
-
onClick: _cache[
|
|
5144
|
+
onClick: _cache[13] || (_cache[13] = ($event) => searchQuery.value = "")
|
|
5000
5145
|
}, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.clearSearch")), 1)
|
|
5001
|
-
])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
5002
|
-
_cache[
|
|
5003
|
-
(0, vue.createElementVNode)("span",
|
|
5146
|
+
])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_53, [inlineError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_54, [
|
|
5147
|
+
_cache[49] || (_cache[49] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
|
|
5148
|
+
(0, vue.createElementVNode)("span", _hoisted_55, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
|
|
5004
5149
|
(0, vue.createElementVNode)("button", {
|
|
5005
5150
|
type: "button",
|
|
5006
5151
|
class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
|
|
5007
5152
|
"aria-label": (0, vue.unref)(t)("common.close"),
|
|
5008
|
-
onClick: _cache[
|
|
5009
|
-
}, [..._cache[
|
|
5010
|
-
])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("table",
|
|
5153
|
+
onClick: _cache[14] || (_cache[14] = ($event) => inlineError.value = null)
|
|
5154
|
+
}, [..._cache[48] || (_cache[48] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_56)
|
|
5155
|
+
])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("table", _hoisted_57, [(0, vue.createElementVNode)("thead", null, [(0, vue.createElementVNode)("tr", _hoisted_58, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(listColumnFields.value, ([key, field]) => {
|
|
5011
5156
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("th", {
|
|
5012
5157
|
key,
|
|
5013
5158
|
"aria-sort": (0, vue.unref)(_mulmoclaude_core_collection.isSortableField)(field) ? sortAriaValue(key) : void 0,
|
|
5014
5159
|
class: "px-5 py-3 font-bold text-slate-500 text-left uppercase tracking-wider whitespace-nowrap"
|
|
5015
|
-
}, [(0, vue.createElementVNode)("div",
|
|
5160
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_60, [(0, vue.createElementVNode)("span", {
|
|
5016
5161
|
class: "truncate max-w-[14rem]",
|
|
5017
5162
|
title: field.label
|
|
5018
|
-
}, (0, vue.toDisplayString)(field.label), 9,
|
|
5163
|
+
}, (0, vue.toDisplayString)(field.label), 9, _hoisted_61), (0, vue.unref)(_mulmoclaude_core_collection.isSortableField)(field) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
5019
5164
|
key: 0,
|
|
5020
5165
|
type: "button",
|
|
5021
5166
|
class: (0, vue.normalizeClass)(["inline-flex items-center justify-center rounded p-0.5 -my-1 leading-none transition-colors", sortButtonClass(key)]),
|
|
@@ -5023,9 +5168,9 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5023
5168
|
"aria-label": (0, vue.unref)(t)("collectionsView.sortBy", { field: field.label }),
|
|
5024
5169
|
onClick: (0, vue.withModifiers)(($event) => cycleSort(key), ["stop"]),
|
|
5025
5170
|
onPointerenter: ($event) => hoveredSortKey.value = key,
|
|
5026
|
-
onPointerleave: _cache[
|
|
5027
|
-
}, [(0, vue.createElementVNode)("span",
|
|
5028
|
-
}), 128))])]), (0, vue.createElementVNode)("tbody",
|
|
5171
|
+
onPointerleave: _cache[15] || (_cache[15] = ($event) => hoveredSortKey.value = null)
|
|
5172
|
+
}, [(0, vue.createElementVNode)("span", _hoisted_63, (0, vue.toDisplayString)(sortIconName(key)), 1)], 42, _hoisted_62)) : (0, vue.createCommentVNode)("", true)])], 8, _hoisted_59);
|
|
5173
|
+
}), 128))])]), (0, vue.createElementVNode)("tbody", _hoisted_64, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(sortedItems.value, (item) => {
|
|
5029
5174
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("tr", {
|
|
5030
5175
|
key: String(item[collection.value.schema.primaryKey] ?? ""),
|
|
5031
5176
|
class: (0, vue.normalizeClass)(["hover:bg-slate-50/70 cursor-pointer transition-colors focus:outline-none focus:bg-indigo-50/30", isRowOpen(item) || isEditingRow(item) ? "bg-indigo-50/40" : ""]),
|
|
@@ -5047,9 +5192,9 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5047
5192
|
class: "h-5 w-5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500/20 cursor-pointer align-middle disabled:opacity-50 disabled:cursor-not-allowed",
|
|
5048
5193
|
"data-testid": `collections-inline-toggle-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5049
5194
|
"aria-label": field.label,
|
|
5050
|
-
onClick: _cache[
|
|
5195
|
+
onClick: _cache[16] || (_cache[16] = (0, vue.withModifiers)(() => {}, ["stop"])),
|
|
5051
5196
|
onChange: ($event) => commitToggle(item, field)
|
|
5052
|
-
}, null, 40,
|
|
5197
|
+
}, null, 40, _hoisted_66)) : field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("input", {
|
|
5053
5198
|
key: 1,
|
|
5054
5199
|
type: "checkbox",
|
|
5055
5200
|
checked: item[key] === true,
|
|
@@ -5057,9 +5202,9 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5057
5202
|
class: "h-5 w-5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500/20 cursor-pointer align-middle disabled:opacity-50 disabled:cursor-not-allowed",
|
|
5058
5203
|
"data-testid": `collections-inline-bool-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5059
5204
|
"aria-label": field.label,
|
|
5060
|
-
onClick: _cache[
|
|
5205
|
+
onClick: _cache[17] || (_cache[17] = (0, vue.withModifiers)(() => {}, ["stop"])),
|
|
5061
5206
|
onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.checked)
|
|
5062
|
-
}, null, 40,
|
|
5207
|
+
}, null, 40, _hoisted_67)) : field.type === "ref" && field.to && typeof item[key] === "string" && item[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_68, [(0, vue.createElementVNode)("a", {
|
|
5063
5208
|
href: (0, vue.unref)(cui).recordHref?.(field.to, String(item[key])),
|
|
5064
5209
|
tabindex: (0, vue.unref)(cui).recordHref?.(field.to, String(item[key])) ? void 0 : 0,
|
|
5065
5210
|
role: "link",
|
|
@@ -5067,44 +5212,44 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5067
5212
|
"data-testid": `collections-ref-link-${key}-${item[key]}`,
|
|
5068
5213
|
onClick: ($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(item[key]), true),
|
|
5069
5214
|
onKeydown: [(0, vue.withKeys)(($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(item[key]), true), ["enter"]), (0, vue.withKeys)(($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(item[key]), true), ["space"])]
|
|
5070
|
-
}, (0, vue.toDisplayString)((0, vue.unref)(refDisplay)(field.to, String(item[key]))), 41,
|
|
5215
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(refDisplay)(field.to, String(item[key]))), 41, _hoisted_69)])) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
|
|
5071
5216
|
key: 3,
|
|
5072
5217
|
value: item[key] == null ? "" : String(item[key]),
|
|
5073
5218
|
disabled: isRowInlineSaving(item),
|
|
5074
5219
|
class: (0, vue.normalizeClass)(["rounded-lg border px-2 py-0.5 text-[11px] font-semibold focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed", enumControlClass(String(key), item[key])]),
|
|
5075
5220
|
"data-testid": `collections-inline-enum-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5076
5221
|
"aria-label": field.label,
|
|
5077
|
-
onClick: _cache[
|
|
5222
|
+
onClick: _cache[18] || (_cache[18] = (0, vue.withModifiers)(() => {}, ["stop"])),
|
|
5078
5223
|
onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.value)
|
|
5079
|
-
}, [showEnumPlaceholder(item, String(key)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("option",
|
|
5224
|
+
}, [showEnumPlaceholder(item, String(key)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("option", _hoisted_71, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1)) : (0, vue.createCommentVNode)("", true), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.values, (value) => {
|
|
5080
5225
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
|
|
5081
5226
|
key: value,
|
|
5082
5227
|
value
|
|
5083
|
-
}, (0, vue.toDisplayString)(value), 9,
|
|
5084
|
-
}), 128))], 42,
|
|
5228
|
+
}, (0, vue.toDisplayString)(value), 9, _hoisted_72);
|
|
5229
|
+
}), 128))], 42, _hoisted_70)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_73, (0, vue.toDisplayString)((0, vue.unref)(formatMoney)(item[key], (0, vue.unref)(resolveCurrency)(field, item), (0, vue.unref)(locale))), 1)) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_74, [_cache[50] || (_cache[50] = (0, vue.createElementVNode)("span", { class: "material-icons text-[11px]" }, "list", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(tableSummary(item[key])), 1)])) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_75, (0, vue.toDisplayString)((0, vue.unref)(derivedDisplay)(field, (0, vue.unref)(evaluateDerivedAgainstItem)(field, String(key), item), item)), 1)) : field.type !== "file" && (0, vue.unref)(isExternalUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
|
|
5085
5230
|
key: 7,
|
|
5086
5231
|
href: String(item[key]),
|
|
5087
5232
|
target: "_blank",
|
|
5088
5233
|
rel: "noopener noreferrer",
|
|
5089
5234
|
class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
|
|
5090
5235
|
"data-testid": `collections-url-link-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5091
|
-
onClick: _cache[
|
|
5092
|
-
}, (0, vue.toDisplayString)(String(item[key])), 9,
|
|
5236
|
+
onClick: _cache[19] || (_cache[19] = (0, vue.withModifiers)(() => {}, ["stop"]))
|
|
5237
|
+
}, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_76)) : field.type === "file" && (0, vue.unref)(artifactUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
|
|
5093
5238
|
key: 8,
|
|
5094
5239
|
href: (0, vue.unref)(artifactUrl)(item[key]) ?? void 0,
|
|
5095
5240
|
target: "_blank",
|
|
5096
5241
|
rel: "noopener noreferrer",
|
|
5097
5242
|
class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
|
|
5098
5243
|
"data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5099
|
-
onClick: _cache[
|
|
5100
|
-
}, (0, vue.toDisplayString)(String(item[key])), 9,
|
|
5244
|
+
onClick: _cache[20] || (_cache[20] = (0, vue.withModifiers)(() => {}, ["stop"]))
|
|
5245
|
+
}, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_77)) : field.type === "file" && (0, vue.unref)(fileRoutePath)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
|
|
5101
5246
|
key: 9,
|
|
5102
5247
|
href: (0, vue.unref)(fileRoutePath)(item[key]) ?? void 0,
|
|
5103
5248
|
class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
|
|
5104
5249
|
"data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5105
5250
|
onClick: ($event) => (0, vue.unref)(activatePathLink)($event, (0, vue.unref)(fileRoutePath)(item[key]) ?? "", true)
|
|
5106
|
-
}, (0, vue.toDisplayString)(String(item[key])), 9,
|
|
5107
|
-
}), 128))], 42,
|
|
5251
|
+
}, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_78)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_79, (0, vue.toDisplayString)((0, vue.unref)(formatCell)(item[key], field.type)), 1))], 64)) : (0, vue.createCommentVNode)("", true)]);
|
|
5252
|
+
}), 128))], 42, _hoisted_65);
|
|
5108
5253
|
}), 128))])])]))]),
|
|
5109
5254
|
collection.value && (viewing.value || editing.value) && !(calendarActive.value && openDay.value) ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionRecordModal_default, {
|
|
5110
5255
|
key: 4,
|
|
@@ -5112,7 +5257,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5112
5257
|
}, {
|
|
5113
5258
|
default: (0, vue.withCtx)(() => [(0, vue.createVNode)(CollectionRecordPanel_default, {
|
|
5114
5259
|
editing: editing.value,
|
|
5115
|
-
"onUpdate:editing": _cache[
|
|
5260
|
+
"onUpdate:editing": _cache[21] || (_cache[21] = ($event) => editing.value = $event),
|
|
5116
5261
|
collection: collection.value,
|
|
5117
5262
|
viewing: viewing.value,
|
|
5118
5263
|
saving: saving.value,
|
|
@@ -5130,7 +5275,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5130
5275
|
onCancel: cancelEditor,
|
|
5131
5276
|
onEdit: editFromView,
|
|
5132
5277
|
onClose: closeView,
|
|
5133
|
-
onDelete: _cache[
|
|
5278
|
+
onDelete: _cache[22] || (_cache[22] = ($event) => viewing.value && confirmDelete(viewing.value)),
|
|
5134
5279
|
onRunAction: runAction,
|
|
5135
5280
|
onItemChat
|
|
5136
5281
|
}, null, 8, [
|
|
@@ -5157,7 +5302,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5157
5302
|
title: collection.value.title,
|
|
5158
5303
|
views: customViews.value,
|
|
5159
5304
|
onChanged: onViewsChanged,
|
|
5160
|
-
onClose: _cache[
|
|
5305
|
+
onClose: _cache[23] || (_cache[23] = ($event) => configOpen.value = false)
|
|
5161
5306
|
}, null, 8, [
|
|
5162
5307
|
"slug",
|
|
5163
5308
|
"title",
|
|
@@ -5172,29 +5317,29 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5172
5317
|
"data-testid": "collections-chat-modal",
|
|
5173
5318
|
onClick: (0, vue.withModifiers)(closeChat, ["self"]),
|
|
5174
5319
|
onKeydown: (0, vue.withKeys)(closeChat, ["esc"])
|
|
5175
|
-
}, [(0, vue.createElementVNode)("div",
|
|
5176
|
-
(0, vue.createElementVNode)("header",
|
|
5177
|
-
_cache[
|
|
5178
|
-
(0, vue.createElementVNode)("div",
|
|
5320
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_80, [
|
|
5321
|
+
(0, vue.createElementVNode)("header", _hoisted_81, [
|
|
5322
|
+
_cache[52] || (_cache[52] = (0, vue.createElementVNode)("div", { class: "h-9 w-9 flex items-center justify-center rounded-xl bg-indigo-50 text-indigo-600 border border-indigo-100/50" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "forum")], -1)),
|
|
5323
|
+
(0, vue.createElementVNode)("div", _hoisted_82, [(0, vue.createElementVNode)("h2", _hoisted_83, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatTitle")), 1), (0, vue.createElementVNode)("span", _hoisted_84, (0, vue.toDisplayString)(collection.value.title), 1)]),
|
|
5179
5324
|
(0, vue.createElementVNode)("button", {
|
|
5180
5325
|
type: "button",
|
|
5181
5326
|
class: "h-8 w-8 flex items-center justify-center rounded text-slate-400 hover:bg-slate-200/50 hover:text-slate-600 transition-colors",
|
|
5182
5327
|
"aria-label": (0, vue.unref)(t)("common.close"),
|
|
5183
5328
|
"data-testid": "collections-chat-close",
|
|
5184
5329
|
onClick: closeChat
|
|
5185
|
-
}, [..._cache[
|
|
5330
|
+
}, [..._cache[51] || (_cache[51] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_85)
|
|
5186
5331
|
]),
|
|
5187
|
-
(0, vue.createElementVNode)("div",
|
|
5332
|
+
(0, vue.createElementVNode)("div", _hoisted_86, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
|
|
5188
5333
|
ref_key: "chatInputEl",
|
|
5189
5334
|
ref: chatInputEl,
|
|
5190
|
-
"onUpdate:modelValue": _cache[
|
|
5335
|
+
"onUpdate:modelValue": _cache[24] || (_cache[24] = ($event) => chatMessage.value = $event),
|
|
5191
5336
|
rows: "4",
|
|
5192
5337
|
placeholder: (0, vue.unref)(t)("collectionsView.chatPlaceholder"),
|
|
5193
5338
|
class: "w-full bg-slate-50 border border-slate-200/80 rounded-xl px-3 py-2.5 text-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500 focus:bg-white transition-all resize-none",
|
|
5194
5339
|
"data-testid": "collections-chat-input",
|
|
5195
5340
|
onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)(submitChat, ["meta"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)(submitChat, ["ctrl"]), ["enter"])]
|
|
5196
|
-
}, null, 40,
|
|
5197
|
-
(0, vue.createElementVNode)("footer",
|
|
5341
|
+
}, null, 40, _hoisted_87), [[vue.vModelText, chatMessage.value]])]),
|
|
5342
|
+
(0, vue.createElementVNode)("footer", _hoisted_88, [(0, vue.createElementVNode)("button", {
|
|
5198
5343
|
type: "button",
|
|
5199
5344
|
class: "h-8 px-2.5 rounded text-xs font-bold text-slate-500 hover:bg-slate-200/50 transition-colors",
|
|
5200
5345
|
"data-testid": "collections-chat-cancel",
|
|
@@ -5205,7 +5350,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
|
5205
5350
|
disabled: !chatMessage.value.trim(),
|
|
5206
5351
|
"data-testid": "collections-chat-send",
|
|
5207
5352
|
onClick: submitChat
|
|
5208
|
-
}, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatStart")), 9,
|
|
5353
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatStart")), 9, _hoisted_89)])
|
|
5209
5354
|
])], 32)) : (0, vue.createCommentVNode)("", true)
|
|
5210
5355
|
]);
|
|
5211
5356
|
};
|