@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.js
CHANGED
|
@@ -118,6 +118,8 @@ var i18n = createI18n({
|
|
|
118
118
|
kanbanUncategorized: "Uncategorized",
|
|
119
119
|
kanbanOpenCard: "Open {label}",
|
|
120
120
|
addView: "Add view",
|
|
121
|
+
addViewDesktop: "Custom view",
|
|
122
|
+
addViewMobile: "Phone view",
|
|
121
123
|
config: {
|
|
122
124
|
open: "Collection settings",
|
|
123
125
|
title: "{title} · settings",
|
|
@@ -129,6 +131,7 @@ var i18n = createI18n({
|
|
|
129
131
|
customViewLoading: "Loading view…",
|
|
130
132
|
customViewError: "Couldn't load this view: {error}",
|
|
131
133
|
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.",
|
|
134
|
+
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.",
|
|
132
135
|
repair: "Repair",
|
|
133
136
|
dataIssuesDetected: "{count} record file(s) have data problems and may be missing from this view.",
|
|
134
137
|
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.",
|
|
@@ -251,6 +254,8 @@ var i18n = createI18n({
|
|
|
251
254
|
kanbanUncategorized: "未分類",
|
|
252
255
|
kanbanOpenCard: "{label} を開く",
|
|
253
256
|
addView: "ビューを追加",
|
|
257
|
+
addViewDesktop: "カスタムビュー",
|
|
258
|
+
addViewMobile: "スマホビュー",
|
|
254
259
|
config: {
|
|
255
260
|
open: "コレクション設定",
|
|
256
261
|
title: "{title} · 設定",
|
|
@@ -262,6 +267,7 @@ var i18n = createI18n({
|
|
|
262
267
|
customViewLoading: "ビューを読み込み中…",
|
|
263
268
|
customViewError: "このビューを読み込めませんでした: {error}",
|
|
264
269
|
addViewPrompt: "「{title}」コレクションにカスタムビューを追加したい。何を見たい/編集したいか質問してから、HTML ビューファイルを {base}/views/your-view.html に作成し、{base}/schema.json の `views[]` に登録して(読み取り専用なら capabilities [\"read\"]、レコードを編集するなら [\"read\",\"write\"])。データ契約は custom-view ヘルプに従ってください。",
|
|
270
|
+
addMobileViewPrompt: "「{title}」コレクションにスマホ(リモートアプリ)用カスタムビューを追加したい。何を見たい/編集したいか質問してから、HTML ビューファイルを {base}/views/your-view.html に作成し、{base}/schema.json の `views[]` に `target: \"mobile\"` 付きで登録して(デフォルトは読み取り専用。レコードを編集する場合のみ `editableFields` / `allowDelete` を宣言)。データ契約は custom-view-remote ヘルプに従ってください — ビュー内での fetch は一切禁止です。",
|
|
265
271
|
repair: "修復",
|
|
266
272
|
dataIssuesDetected: "{count} 件のレコードファイルにデータの問題があり、この表示に出てこない可能性があります。",
|
|
267
273
|
repairPrompt: "コレクション「{title}」に、表示されない原因となるデータの問題を持つレコードファイルが {count} 件あります。それぞれを修正してください — ファイルを Read し、修正してから Write し直します:\n{issues}\n\n完了したら presentCollection を呼び出して、レコードが読み込めることを確認してください。",
|
|
@@ -384,6 +390,8 @@ var i18n = createI18n({
|
|
|
384
390
|
kanbanUncategorized: "未分类",
|
|
385
391
|
kanbanOpenCard: "打开 {label}",
|
|
386
392
|
addView: "添加视图",
|
|
393
|
+
addViewDesktop: "自定义视图",
|
|
394
|
+
addViewMobile: "手机视图",
|
|
387
395
|
config: {
|
|
388
396
|
open: "集合设置",
|
|
389
397
|
title: "{title} · 设置",
|
|
@@ -395,6 +403,7 @@ var i18n = createI18n({
|
|
|
395
403
|
customViewLoading: "正在加载视图…",
|
|
396
404
|
customViewError: "无法加载此视图:{error}",
|
|
397
405
|
addViewPrompt: "我想为“{title}”集合添加一个自定义视图。先问我想查看或编辑什么,然后在 {base}/views/your-view.html 创建 HTML 视图文件,并在 {base}/schema.json 的 `views[]` 中注册(只读视图用 capabilities [\"read\"],需要编辑记录则用 [\"read\",\"write\"])。数据契约请遵循 custom-view 帮助文档。",
|
|
406
|
+
addMobileViewPrompt: "我想为“{title}”集合添加一个手机(远程应用)自定义视图。先问我想查看或编辑什么,然后在 {base}/views/your-view.html 创建 HTML 视图文件,并在 {base}/schema.json 的 `views[]` 中以 `target: \"mobile\"` 注册(默认只读;仅在需要编辑记录时声明 `editableFields` / `allowDelete`)。数据契约请遵循 custom-view-remote 帮助文档 — 视图内禁止任何 fetch。",
|
|
398
407
|
repair: "修复",
|
|
399
408
|
dataIssuesDetected: "有 {count} 个记录文件存在数据问题,可能未显示在此视图中。",
|
|
400
409
|
repairPrompt: "集合 {title} 有 {count} 个记录文件存在导致无法显示的数据问题。请逐一修复——用 Read 读取该文件,更正后再用 Write 写回:\n{issues}\n\n完成后,调用 presentCollection 确认记录可以正常加载。",
|
|
@@ -517,6 +526,8 @@ var i18n = createI18n({
|
|
|
517
526
|
kanbanUncategorized: "미분류",
|
|
518
527
|
kanbanOpenCard: "{label} 열기",
|
|
519
528
|
addView: "보기 추가",
|
|
529
|
+
addViewDesktop: "사용자 지정 보기",
|
|
530
|
+
addViewMobile: "스마트폰 보기",
|
|
520
531
|
config: {
|
|
521
532
|
open: "컬렉션 설정",
|
|
522
533
|
title: "{title} · 설정",
|
|
@@ -528,6 +539,7 @@ var i18n = createI18n({
|
|
|
528
539
|
customViewLoading: "보기를 불러오는 중…",
|
|
529
540
|
customViewError: "이 보기를 불러오지 못했습니다: {error}",
|
|
530
541
|
addViewPrompt: "{title} 컬렉션에 사용자 지정 보기를 추가하고 싶어요. 무엇을 보거나 편집하고 싶은지 먼저 물어본 뒤, {base}/views/your-view.html 에 HTML 보기 파일을 만들고 {base}/schema.json의 `views[]`에 등록해 주세요(읽기 전용 보기는 capabilities [\"read\"], 레코드를 편집하면 [\"read\",\"write\"]). 데이터 계약은 custom-view 도움말을 따르세요.",
|
|
542
|
+
addMobileViewPrompt: "{title} 컬렉션에 스마트폰(리모트 앱)용 사용자 지정 보기를 추가하고 싶어요. 무엇을 보거나 편집하고 싶은지 먼저 물어본 뒤, {base}/views/your-view.html 에 HTML 보기 파일을 만들고 {base}/schema.json의 `views[]`에 `target: \"mobile\"`과 함께 등록해 주세요(기본은 읽기 전용, 레코드를 편집할 때만 `editableFields` / `allowDelete` 선언). 데이터 계약은 custom-view-remote 도움말을 따르세요 — 보기 안에서 fetch는 일절 금지입니다.",
|
|
531
543
|
repair: "복구",
|
|
532
544
|
dataIssuesDetected: "{count}개의 레코드 파일에 데이터 문제가 있어 이 보기에 표시되지 않을 수 있습니다.",
|
|
533
545
|
repairPrompt: "{title} 컬렉션에 표시되지 않는 원인이 되는 데이터 문제가 있는 레코드 파일이 {count}개 있습니다. 각 파일을 수정하세요 — Read로 파일을 읽고 수정한 뒤 Write로 다시 저장하세요:\n{issues}\n\n완료되면 presentCollection을 호출하여 레코드가 로드되는지 확인하세요.",
|
|
@@ -650,6 +662,8 @@ var i18n = createI18n({
|
|
|
650
662
|
kanbanUncategorized: "Sin categoría",
|
|
651
663
|
kanbanOpenCard: "Abrir {label}",
|
|
652
664
|
addView: "Añadir vista",
|
|
665
|
+
addViewDesktop: "Vista personalizada",
|
|
666
|
+
addViewMobile: "Vista móvil",
|
|
653
667
|
config: {
|
|
654
668
|
open: "Ajustes de la colección",
|
|
655
669
|
title: "{title} · ajustes",
|
|
@@ -661,6 +675,7 @@ var i18n = createI18n({
|
|
|
661
675
|
customViewLoading: "Cargando vista…",
|
|
662
676
|
customViewError: "No se pudo cargar esta vista: {error}",
|
|
663
677
|
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.",
|
|
678
|
+
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.",
|
|
664
679
|
repair: "Reparar",
|
|
665
680
|
dataIssuesDetected: "{count} archivo(s) de registro tienen problemas de datos y podrían no aparecer en esta vista.",
|
|
666
681
|
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.",
|
|
@@ -783,6 +798,8 @@ var i18n = createI18n({
|
|
|
783
798
|
kanbanUncategorized: "Sem categoria",
|
|
784
799
|
kanbanOpenCard: "Abrir {label}",
|
|
785
800
|
addView: "Adicionar visualização",
|
|
801
|
+
addViewDesktop: "Visualização personalizada",
|
|
802
|
+
addViewMobile: "Visualização para celular",
|
|
786
803
|
config: {
|
|
787
804
|
open: "Configurações da coleção",
|
|
788
805
|
title: "{title} · configurações",
|
|
@@ -794,6 +811,7 @@ var i18n = createI18n({
|
|
|
794
811
|
customViewLoading: "Carregando visualização…",
|
|
795
812
|
customViewError: "Não foi possível carregar esta visualização: {error}",
|
|
796
813
|
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.",
|
|
814
|
+
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.",
|
|
797
815
|
repair: "Reparar",
|
|
798
816
|
dataIssuesDetected: "{count} arquivo(s) de registro têm problemas de dados e podem não aparecer nesta visualização.",
|
|
799
817
|
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.",
|
|
@@ -916,6 +934,8 @@ var i18n = createI18n({
|
|
|
916
934
|
kanbanUncategorized: "Non classé",
|
|
917
935
|
kanbanOpenCard: "Ouvrir {label}",
|
|
918
936
|
addView: "Ajouter une vue",
|
|
937
|
+
addViewDesktop: "Vue personnalisée",
|
|
938
|
+
addViewMobile: "Vue mobile",
|
|
919
939
|
config: {
|
|
920
940
|
open: "Paramètres de la collection",
|
|
921
941
|
title: "{title} · paramètres",
|
|
@@ -927,6 +947,7 @@ var i18n = createI18n({
|
|
|
927
947
|
customViewLoading: "Chargement de la vue…",
|
|
928
948
|
customViewError: "Impossible de charger cette vue : {error}",
|
|
929
949
|
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.",
|
|
950
|
+
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.",
|
|
930
951
|
repair: "Réparer",
|
|
931
952
|
dataIssuesDetected: "{count} fichier(s) d'enregistrement présentent des problèmes de données et peuvent être absents de cette vue.",
|
|
932
953
|
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.",
|
|
@@ -1049,6 +1070,8 @@ var i18n = createI18n({
|
|
|
1049
1070
|
kanbanUncategorized: "Nicht kategorisiert",
|
|
1050
1071
|
kanbanOpenCard: "{label} öffnen",
|
|
1051
1072
|
addView: "Ansicht hinzufügen",
|
|
1073
|
+
addViewDesktop: "Benutzerdefinierte Ansicht",
|
|
1074
|
+
addViewMobile: "Smartphone-Ansicht",
|
|
1052
1075
|
config: {
|
|
1053
1076
|
open: "Sammlungseinstellungen",
|
|
1054
1077
|
title: "{title} · Einstellungen",
|
|
@@ -1060,6 +1083,7 @@ var i18n = createI18n({
|
|
|
1060
1083
|
customViewLoading: "Ansicht wird geladen…",
|
|
1061
1084
|
customViewError: "Diese Ansicht konnte nicht geladen werden: {error}",
|
|
1062
1085
|
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.",
|
|
1086
|
+
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.",
|
|
1063
1087
|
repair: "Reparieren",
|
|
1064
1088
|
dataIssuesDetected: "{count} Datensatzdatei(en) haben Datenprobleme und fehlen möglicherweise in dieser Ansicht.",
|
|
1065
1089
|
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.",
|
|
@@ -1693,7 +1717,10 @@ var CollectionKanbanView_default = /* @__PURE__ */ defineComponent({
|
|
|
1693
1717
|
const emit = __emit;
|
|
1694
1718
|
const { t } = useCollectionI18n();
|
|
1695
1719
|
/** The Uncategorized column uses the empty string as its sentinel value. */
|
|
1696
|
-
const groupSpec = computed(() =>
|
|
1720
|
+
const groupSpec = computed(() => {
|
|
1721
|
+
const spec = props.schema.fields[props.groupField];
|
|
1722
|
+
return spec?.type === "enum" ? spec : void 0;
|
|
1723
|
+
});
|
|
1697
1724
|
/** Declared enum values become columns in order, with a trailing
|
|
1698
1725
|
* Uncategorized column for empty/unknown values (also a drop target that
|
|
1699
1726
|
* clears the field). The Uncategorized column is omitted when the group
|
|
@@ -1740,7 +1767,10 @@ var CollectionKanbanView_default = /* @__PURE__ */ defineComponent({
|
|
|
1740
1767
|
const value = String(raw);
|
|
1741
1768
|
return (groupSpec.value?.values ?? []).includes(value) ? value : UNCATEGORIZED;
|
|
1742
1769
|
}
|
|
1743
|
-
const visibleItems = computed(() =>
|
|
1770
|
+
const visibleItems = computed(() => {
|
|
1771
|
+
const spec = groupSpec.value;
|
|
1772
|
+
return spec ? props.items.filter((item) => fieldVisible(spec, item)) : [];
|
|
1773
|
+
});
|
|
1744
1774
|
const itemsByColumnMap = computed(() => {
|
|
1745
1775
|
const map = /* @__PURE__ */ new Map();
|
|
1746
1776
|
for (const column of columns.value) map.set(column.value, []);
|
|
@@ -2267,7 +2297,8 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
|
|
|
2267
2297
|
/** Required flag for an embed's per-record picker — read off the storage
|
|
2268
2298
|
* field it writes (`idField`), since the embed itself stores nothing. */
|
|
2269
2299
|
function embedPickerRequired(field) {
|
|
2270
|
-
const
|
|
2300
|
+
const idField = field.type === "embed" ? field.idField : void 0;
|
|
2301
|
+
const target = idField ? props.collection.schema.fields[idField] : void 0;
|
|
2271
2302
|
return target ? isFieldRequiredInUi(target) : false;
|
|
2272
2303
|
}
|
|
2273
2304
|
/** Tailwind fill/text/border classes tinting an enum `<select>` by its current
|
|
@@ -2988,284 +3019,342 @@ var CollectionRemoteViewPreview_default = /*#__PURE__*/ _plugin_vue_export_helpe
|
|
|
2988
3019
|
}
|
|
2989
3020
|
}), [["__scopeId", "data-v-965ade6c"]]);
|
|
2990
3021
|
//#endregion
|
|
2991
|
-
//#region src/vue/useCollectionRendering.ts
|
|
3022
|
+
//#region src/vue/useCollectionRendering.helpers.ts
|
|
3023
|
+
var EM_DASH = "—";
|
|
3024
|
+
var DEFAULT_CURRENCY = "USD";
|
|
3025
|
+
var MARKDOWN_CELL_PREVIEW_MAX = 80;
|
|
2992
3026
|
function stepForFieldType(type) {
|
|
2993
3027
|
if (type === "money") return "0.01";
|
|
2994
3028
|
if (type === "number") return "any";
|
|
2995
3029
|
}
|
|
2996
|
-
function
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3030
|
+
function inputTypeFor(type) {
|
|
3031
|
+
if (type === "email") return "email";
|
|
3032
|
+
if (type === "number") return "number";
|
|
3033
|
+
if (type === "money") return "number";
|
|
3034
|
+
if (type === "date") return "date";
|
|
3035
|
+
if (type === "datetime") return "datetime-local";
|
|
3036
|
+
return "text";
|
|
3037
|
+
}
|
|
3038
|
+
function isExternalUrl(value) {
|
|
3039
|
+
return typeof value === "string" && /^https?:\/\//i.test(value);
|
|
3040
|
+
}
|
|
3041
|
+
function detailText(value) {
|
|
3042
|
+
if (value === void 0 || value === null || value === "") return EM_DASH;
|
|
3043
|
+
return String(value);
|
|
3044
|
+
}
|
|
3045
|
+
function formatCell(value, type) {
|
|
3046
|
+
if (value === void 0 || value === null || value === "") return EM_DASH;
|
|
3047
|
+
if (type === "markdown" && typeof value === "string") return value.length > MARKDOWN_CELL_PREVIEW_MAX ? `${value.slice(0, MARKDOWN_CELL_PREVIEW_MAX)}…` : value;
|
|
3048
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
3049
|
+
return JSON.stringify(value);
|
|
3050
|
+
}
|
|
3051
|
+
/** Resolve the ISO 4217 code for a money field: a per-record
|
|
3052
|
+
* `currencyField` (when present and non-blank) wins over the field's
|
|
3053
|
+
* literal `currency`. Only `money` / `derived` variants carry currency
|
|
3054
|
+
* keys; any other field resolves to undefined (the formatter's USD
|
|
3055
|
+
* fallback), as before. */
|
|
3056
|
+
function resolveCurrency(field, record) {
|
|
3057
|
+
if (field.type !== "money" && field.type !== "derived") return void 0;
|
|
3058
|
+
if (field.currencyField && record) {
|
|
3059
|
+
const code = record[field.currencyField];
|
|
3060
|
+
if (typeof code === "string" && code.trim().length > 0) return code;
|
|
3020
3061
|
}
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3062
|
+
return field.currency;
|
|
3063
|
+
}
|
|
3064
|
+
function formatMoney(value, currency, displayLocale) {
|
|
3065
|
+
if (value === void 0 || value === "") return EM_DASH;
|
|
3066
|
+
const amount = typeof value === "number" ? value : Number(value);
|
|
3067
|
+
if (!Number.isFinite(amount)) return String(value);
|
|
3068
|
+
const currencyCode = currency && currency.length > 0 ? currency : DEFAULT_CURRENCY;
|
|
3069
|
+
try {
|
|
3070
|
+
return new Intl.NumberFormat(displayLocale, {
|
|
3071
|
+
style: "currency",
|
|
3072
|
+
currency: currencyCode
|
|
3073
|
+
}).format(amount);
|
|
3074
|
+
} catch {
|
|
3075
|
+
return String(amount);
|
|
3032
3076
|
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3077
|
+
}
|
|
3078
|
+
function currencySymbolForLocale(currency, locale) {
|
|
3079
|
+
const code = currency && currency.length > 0 ? currency : DEFAULT_CURRENCY;
|
|
3080
|
+
try {
|
|
3081
|
+
return new Intl.NumberFormat(locale, {
|
|
3082
|
+
style: "currency",
|
|
3083
|
+
currency: code
|
|
3084
|
+
}).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
|
|
3085
|
+
} catch {
|
|
3086
|
+
return code;
|
|
3041
3087
|
}
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
const nextRefRecords = {};
|
|
3064
|
-
const nextEmbed = {};
|
|
3065
|
-
for (const { target, result } of results) {
|
|
3066
|
-
if (!result.ok) continue;
|
|
3067
|
-
if (refTargets.has(target)) {
|
|
3068
|
-
nextRef[target] = buildRefDisplayMap(result.data);
|
|
3069
|
-
nextRefRecords[target] = buildRefRecordMap(result.data);
|
|
3070
|
-
}
|
|
3071
|
-
if (embedTargets.has(target)) nextEmbed[target] = {
|
|
3072
|
-
schema: result.data.collection.schema,
|
|
3073
|
-
items: result.data.items
|
|
3074
|
-
};
|
|
3088
|
+
}
|
|
3089
|
+
function tableRows(value) {
|
|
3090
|
+
if (!Array.isArray(value)) return [];
|
|
3091
|
+
return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
|
|
3092
|
+
}
|
|
3093
|
+
function hasTableRows(value) {
|
|
3094
|
+
return tableRows(value).length > 0;
|
|
3095
|
+
}
|
|
3096
|
+
/** Pick the field used to label a referenced/embedded record: prefer a
|
|
3097
|
+
* `name` field, then `title`, else fall back to the primary key. */
|
|
3098
|
+
function displayFieldFor(fields, primaryKey) {
|
|
3099
|
+
if ("name" in fields) return "name";
|
|
3100
|
+
if ("title" in fields) return "title";
|
|
3101
|
+
return primaryKey;
|
|
3102
|
+
}
|
|
3103
|
+
function uniqueRefTargets(schema) {
|
|
3104
|
+
const targets = /* @__PURE__ */ new Set();
|
|
3105
|
+
const walk = (fields) => {
|
|
3106
|
+
for (const field of Object.values(fields)) {
|
|
3107
|
+
if (field.type === "ref" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
|
|
3108
|
+
if (field.type === "table" && field.of) walk(field.of);
|
|
3075
3109
|
}
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3110
|
+
};
|
|
3111
|
+
walk(schema.fields);
|
|
3112
|
+
return [...targets];
|
|
3113
|
+
}
|
|
3114
|
+
function uniqueEmbedTargets(schema) {
|
|
3115
|
+
const targets = /* @__PURE__ */ new Set();
|
|
3116
|
+
for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
|
|
3117
|
+
return [...targets];
|
|
3118
|
+
}
|
|
3119
|
+
function buildRefDisplayMap(detail) {
|
|
3120
|
+
const { fields, primaryKey } = detail.collection.schema;
|
|
3121
|
+
const displayField = displayFieldFor(fields, primaryKey);
|
|
3122
|
+
const map = {};
|
|
3123
|
+
for (const item of detail.items) {
|
|
3124
|
+
const slugRaw = item[primaryKey];
|
|
3125
|
+
if (typeof slugRaw !== "string" || slugRaw.length === 0) continue;
|
|
3126
|
+
const displayRaw = item[displayField];
|
|
3127
|
+
map[slugRaw] = typeof displayRaw === "string" && displayRaw.length > 0 ? displayRaw : slugRaw;
|
|
3091
3128
|
}
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
|
|
3101
|
-
return data.items.map((item) => {
|
|
3102
|
-
const slug = String(item[primaryKey] ?? "");
|
|
3103
|
-
const labelRaw = item[displayField];
|
|
3104
|
-
return {
|
|
3105
|
-
slug,
|
|
3106
|
-
display: typeof labelRaw === "string" && labelRaw.length > 0 ? labelRaw : slug
|
|
3107
|
-
};
|
|
3108
|
-
}).filter((opt) => opt.slug.length > 0).sort((left, right) => left.display.localeCompare(right.display));
|
|
3129
|
+
return map;
|
|
3130
|
+
}
|
|
3131
|
+
function buildRefRecordMap(detail) {
|
|
3132
|
+
const { schema } = detail.collection;
|
|
3133
|
+
const map = {};
|
|
3134
|
+
for (const item of detail.items) {
|
|
3135
|
+
const slugRaw = item[schema.primaryKey];
|
|
3136
|
+
if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = deriveAll(schema, item, {});
|
|
3109
3137
|
}
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3138
|
+
return map;
|
|
3139
|
+
}
|
|
3140
|
+
function sortedRefOptions(map) {
|
|
3141
|
+
return Object.entries(map).map(([slug, display]) => ({
|
|
3142
|
+
slug,
|
|
3143
|
+
display
|
|
3144
|
+
})).sort((left, right) => left.display.localeCompare(right.display));
|
|
3145
|
+
}
|
|
3146
|
+
/** Dropdown options for an `embed` field's per-record picker: every
|
|
3147
|
+
* record in the target collection, labelled by its name/title (or
|
|
3148
|
+
* primary key), skipping records without a slug and sorted by label. */
|
|
3149
|
+
function buildEmbedOptions(schema, items) {
|
|
3150
|
+
const { fields, primaryKey } = schema;
|
|
3151
|
+
const displayField = displayFieldFor(fields, primaryKey);
|
|
3152
|
+
return items.map((item) => {
|
|
3153
|
+
const slug = String(item[primaryKey] ?? "");
|
|
3154
|
+
const labelRaw = item[displayField];
|
|
3122
3155
|
return {
|
|
3123
|
-
|
|
3124
|
-
|
|
3156
|
+
slug,
|
|
3157
|
+
display: typeof labelRaw === "string" && labelRaw.length > 0 ? labelRaw : slug
|
|
3125
3158
|
};
|
|
3126
|
-
}
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
display: embedValue(subField, value, item)
|
|
3150
|
-
});
|
|
3151
|
-
}
|
|
3152
|
-
out[key] = {
|
|
3153
|
-
found: Boolean(item),
|
|
3154
|
-
rows,
|
|
3155
|
-
targetSlug: field.to ?? "",
|
|
3156
|
-
recordId: embedTargetId(field, record)
|
|
3157
|
-
};
|
|
3158
|
-
}
|
|
3159
|
-
return out;
|
|
3160
|
-
}
|
|
3161
|
-
function resolveCurrency(field, record) {
|
|
3162
|
-
if (field.currencyField && record) {
|
|
3163
|
-
const code = record[field.currencyField];
|
|
3164
|
-
if (typeof code === "string" && code.trim().length > 0) return code;
|
|
3165
|
-
}
|
|
3166
|
-
return field.currency;
|
|
3167
|
-
}
|
|
3168
|
-
function currencySymbol(currency) {
|
|
3169
|
-
const code = currency && currency.length > 0 ? currency : "USD";
|
|
3170
|
-
try {
|
|
3171
|
-
return new Intl.NumberFormat(locale.value, {
|
|
3172
|
-
style: "currency",
|
|
3173
|
-
currency: code
|
|
3174
|
-
}).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
|
|
3175
|
-
} catch {
|
|
3176
|
-
return code;
|
|
3177
|
-
}
|
|
3178
|
-
}
|
|
3179
|
-
function formatMoney(value, currency, displayLocale) {
|
|
3180
|
-
if (value === void 0 || value === "") return "—";
|
|
3181
|
-
const amount = typeof value === "number" ? value : Number(value);
|
|
3182
|
-
if (!Number.isFinite(amount)) return String(value);
|
|
3183
|
-
const currencyCode = currency && currency.length > 0 ? currency : "USD";
|
|
3159
|
+
}).filter((opt) => opt.slug.length > 0).sort((left, right) => left.display.localeCompare(right.display));
|
|
3160
|
+
}
|
|
3161
|
+
//#endregion
|
|
3162
|
+
//#region src/vue/useLinkedCollectionCaches.ts
|
|
3163
|
+
/** The de-duplicated ref + embed target slugs a schema links to. `allTargets`
|
|
3164
|
+
* is the union (each target fetched once even when both ref'd and embedded). */
|
|
3165
|
+
function linkedTargets(schema) {
|
|
3166
|
+
const refTargets = new Set(uniqueRefTargets(schema));
|
|
3167
|
+
const embedTargets = new Set(uniqueEmbedTargets(schema));
|
|
3168
|
+
return {
|
|
3169
|
+
refTargets,
|
|
3170
|
+
embedTargets,
|
|
3171
|
+
allTargets: [.../* @__PURE__ */ new Set([...refTargets, ...embedTargets])]
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
/** Fan-out fetch that hydrates the linked-collection caches. Best-effort: a
|
|
3175
|
+
* target whose fetch *rejects* (vs. resolving `{ ok: false }`) is coerced to a
|
|
3176
|
+
* skip and must not abort the others. Returns null when a quicker subsequent
|
|
3177
|
+
* load has already moved on (stale-write guard) so the caller drops the write.
|
|
3178
|
+
* Pure + injectable (`fetchDetail`, `currentSlug`) so both paths are testable. */
|
|
3179
|
+
async function fetchLinkedCaches(targets, fetchDetail, currentSlug, expectedSlug) {
|
|
3180
|
+
const { refTargets, embedTargets, allTargets } = targets;
|
|
3181
|
+
const results = await Promise.all(allTargets.map(async (target) => {
|
|
3184
3182
|
try {
|
|
3185
|
-
return
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
}
|
|
3183
|
+
return {
|
|
3184
|
+
target,
|
|
3185
|
+
result: await fetchDetail(target)
|
|
3186
|
+
};
|
|
3189
3187
|
} catch {
|
|
3190
|
-
return
|
|
3188
|
+
return {
|
|
3189
|
+
target,
|
|
3190
|
+
result: { ok: false }
|
|
3191
|
+
};
|
|
3191
3192
|
}
|
|
3193
|
+
}));
|
|
3194
|
+
if (currentSlug() !== expectedSlug) return null;
|
|
3195
|
+
const refCache = {};
|
|
3196
|
+
const refRecordCache = {};
|
|
3197
|
+
const embedCache = {};
|
|
3198
|
+
for (const { target, result } of results) {
|
|
3199
|
+
if (!result.ok) continue;
|
|
3200
|
+
if (refTargets.has(target)) {
|
|
3201
|
+
refCache[target] = buildRefDisplayMap(result.data);
|
|
3202
|
+
refRecordCache[target] = buildRefRecordMap(result.data);
|
|
3203
|
+
}
|
|
3204
|
+
if (embedTargets.has(target)) embedCache[target] = {
|
|
3205
|
+
schema: result.data.collection.schema,
|
|
3206
|
+
items: result.data.items
|
|
3207
|
+
};
|
|
3192
3208
|
}
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
}
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
}
|
|
3208
|
-
function detailText(value) {
|
|
3209
|
-
if (value === void 0 || value === null || value === "") return "—";
|
|
3210
|
-
return String(value);
|
|
3211
|
-
}
|
|
3212
|
-
function tableRows(value) {
|
|
3213
|
-
if (!Array.isArray(value)) return [];
|
|
3214
|
-
return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
|
|
3215
|
-
}
|
|
3216
|
-
function hasTableRows(value) {
|
|
3217
|
-
return tableRows(value).length > 0;
|
|
3218
|
-
}
|
|
3219
|
-
function formatSubCell(subField, value, record) {
|
|
3220
|
-
if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale.value);
|
|
3221
|
-
if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return refDisplay(subField.to, value);
|
|
3222
|
-
return formatCell(value, subField.type);
|
|
3223
|
-
}
|
|
3224
|
-
function inputTypeFor(type) {
|
|
3225
|
-
if (type === "email") return "email";
|
|
3226
|
-
if (type === "number") return "number";
|
|
3227
|
-
if (type === "money") return "number";
|
|
3228
|
-
if (type === "date") return "date";
|
|
3229
|
-
if (type === "datetime") return "datetime-local";
|
|
3230
|
-
return "text";
|
|
3231
|
-
}
|
|
3232
|
-
const stepFor = stepForFieldType;
|
|
3233
|
-
function evaluateDerivedAgainstItem(field, fieldKey, item) {
|
|
3234
|
-
if (!field.formula || !collection.value) return null;
|
|
3235
|
-
const result = deriveAll(collection.value.schema, item, refRecordCache.value)[fieldKey];
|
|
3236
|
-
return typeof result === "number" && Number.isFinite(result) ? result : null;
|
|
3209
|
+
return {
|
|
3210
|
+
refCache,
|
|
3211
|
+
refRecordCache,
|
|
3212
|
+
embedCache
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
function useLinkedCollectionCaches(collection) {
|
|
3216
|
+
const refCache = ref({});
|
|
3217
|
+
const refRecordCache = ref({});
|
|
3218
|
+
const embedCache = ref({});
|
|
3219
|
+
function resetLinkedCaches() {
|
|
3220
|
+
refCache.value = {};
|
|
3221
|
+
refRecordCache.value = {};
|
|
3222
|
+
embedCache.value = {};
|
|
3237
3223
|
}
|
|
3238
|
-
function
|
|
3239
|
-
|
|
3240
|
-
if (
|
|
3241
|
-
|
|
3224
|
+
async function loadLinkedCollections(schema, expectedSlug) {
|
|
3225
|
+
const targets = linkedTargets(schema);
|
|
3226
|
+
if (targets.allTargets.length === 0) return;
|
|
3227
|
+
const binding = collectionUi();
|
|
3228
|
+
const snapshot = await fetchLinkedCaches(targets, (slug) => binding.fetchCollectionDetail(slug), () => collection.value?.slug, expectedSlug);
|
|
3229
|
+
if (!snapshot) return;
|
|
3230
|
+
refCache.value = snapshot.refCache;
|
|
3231
|
+
refRecordCache.value = snapshot.refRecordCache;
|
|
3232
|
+
embedCache.value = snapshot.embedCache;
|
|
3242
3233
|
}
|
|
3243
3234
|
return {
|
|
3244
3235
|
refCache,
|
|
3245
3236
|
refRecordCache,
|
|
3246
3237
|
embedCache,
|
|
3247
3238
|
resetLinkedCaches,
|
|
3248
|
-
loadLinkedCollections
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3239
|
+
loadLinkedCollections
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
//#endregion
|
|
3243
|
+
//#region src/vue/useCollectionRendering.renderers.ts
|
|
3244
|
+
function lookupRefDisplay(refCache, targetSlug, itemSlug) {
|
|
3245
|
+
const map = refCache[targetSlug];
|
|
3246
|
+
return map && map[itemSlug] || itemSlug;
|
|
3247
|
+
}
|
|
3248
|
+
function refOptionsFor(refCache, targetSlug) {
|
|
3249
|
+
const map = refCache[targetSlug];
|
|
3250
|
+
return map ? sortedRefOptions(map) : [];
|
|
3251
|
+
}
|
|
3252
|
+
/** Dropdown options for an `embed` field's per-record picker (`idField`):
|
|
3253
|
+
* every record in the target collection, labelled by its name/title (or
|
|
3254
|
+
* primary key). Built from `embedCache` so it works for embed targets
|
|
3255
|
+
* that aren't also `ref` targets (the profile collection, say). */
|
|
3256
|
+
function embedOptionsFor(embedCache, targetSlug) {
|
|
3257
|
+
const data = embedCache[targetSlug];
|
|
3258
|
+
return data ? buildEmbedOptions(data.schema, data.items) : [];
|
|
3259
|
+
}
|
|
3260
|
+
function resolveEmbed(field, record, embedCache) {
|
|
3261
|
+
if (field.type !== "embed" || !field.to) return {
|
|
3262
|
+
schema: null,
|
|
3263
|
+
item: null
|
|
3264
|
+
};
|
|
3265
|
+
const targetId = embedTargetId(field, record);
|
|
3266
|
+
const data = targetId ? embedCache[field.to] : void 0;
|
|
3267
|
+
if (!data) return {
|
|
3268
|
+
schema: null,
|
|
3269
|
+
item: null
|
|
3270
|
+
};
|
|
3271
|
+
const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === targetId) ?? null;
|
|
3272
|
+
return {
|
|
3273
|
+
schema: data.schema,
|
|
3274
|
+
item
|
|
3275
|
+
};
|
|
3276
|
+
}
|
|
3277
|
+
function formatEmbedValue(field, value, record, locale) {
|
|
3278
|
+
if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale);
|
|
3279
|
+
return detailText(value);
|
|
3280
|
+
}
|
|
3281
|
+
/** Build the read-only embed view-models for one record. A function of the
|
|
3282
|
+
* open record (not a bare computed) because a per-record `idField` embed
|
|
3283
|
+
* resolves a different target per row. `schema` is the OPEN collection's
|
|
3284
|
+
* schema (null when no collection is loaded → no views). */
|
|
3285
|
+
function buildEmbedViews(schema, embedCache, record, locale) {
|
|
3286
|
+
const out = {};
|
|
3287
|
+
if (!schema) return out;
|
|
3288
|
+
for (const [key, field] of Object.entries(schema.fields)) {
|
|
3289
|
+
if (field.type !== "embed") continue;
|
|
3290
|
+
const { schema: targetSchema, item } = resolveEmbed(field, record, embedCache);
|
|
3291
|
+
const rows = [];
|
|
3292
|
+
if (targetSchema && item) for (const [subKey, subField] of Object.entries(targetSchema.fields)) {
|
|
3293
|
+
const value = item[subKey];
|
|
3294
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
3295
|
+
rows.push({
|
|
3296
|
+
key: subKey,
|
|
3297
|
+
label: subField.label,
|
|
3298
|
+
type: subField.type,
|
|
3299
|
+
value,
|
|
3300
|
+
display: formatEmbedValue(subField, value, item, locale)
|
|
3301
|
+
});
|
|
3302
|
+
}
|
|
3303
|
+
out[key] = {
|
|
3304
|
+
found: Boolean(item),
|
|
3305
|
+
rows,
|
|
3306
|
+
targetSlug: field.to ?? "",
|
|
3307
|
+
recordId: embedTargetId(field, record)
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
return out;
|
|
3311
|
+
}
|
|
3312
|
+
function renderSubCell(subField, value, record, refCache, locale) {
|
|
3313
|
+
if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale);
|
|
3314
|
+
if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return lookupRefDisplay(refCache, subField.to, value);
|
|
3315
|
+
return formatCell(value, subField.type);
|
|
3316
|
+
}
|
|
3317
|
+
function evaluateDerived(field, fieldKey, item, schema, refRecords) {
|
|
3318
|
+
if (field.type !== "derived" || !schema) return null;
|
|
3319
|
+
const result = deriveAll(schema, item, refRecords)[fieldKey];
|
|
3320
|
+
return typeof result === "number" && Number.isFinite(result) ? result : null;
|
|
3321
|
+
}
|
|
3322
|
+
function renderDerived(field, computedValue, record, locale) {
|
|
3323
|
+
if (computedValue === null || computedValue === void 0) return "—";
|
|
3324
|
+
const display = field.type === "derived" ? field.display : void 0;
|
|
3325
|
+
if (display === "money") return formatMoney(computedValue, resolveCurrency(field, record), locale);
|
|
3326
|
+
return formatCell(computedValue, display ?? "number");
|
|
3327
|
+
}
|
|
3328
|
+
//#endregion
|
|
3329
|
+
//#region src/vue/useCollectionRendering.ts
|
|
3330
|
+
var STATELESS_RENDERERS = {
|
|
3331
|
+
resolveCurrency,
|
|
3332
|
+
formatMoney,
|
|
3333
|
+
formatCell,
|
|
3334
|
+
detailText,
|
|
3335
|
+
isExternalUrl,
|
|
3336
|
+
tableRows,
|
|
3337
|
+
hasTableRows,
|
|
3338
|
+
inputTypeFor,
|
|
3339
|
+
stepFor: stepForFieldType,
|
|
3340
|
+
deriveAll
|
|
3341
|
+
};
|
|
3342
|
+
function useCollectionRendering(collection, locale) {
|
|
3343
|
+
const caches = useLinkedCollectionCaches(collection);
|
|
3344
|
+
const { refCache, refRecordCache, embedCache } = caches;
|
|
3345
|
+
return {
|
|
3346
|
+
...caches,
|
|
3347
|
+
...STATELESS_RENDERERS,
|
|
3348
|
+
refDisplay: (targetSlug, itemSlug) => lookupRefDisplay(refCache.value, targetSlug, itemSlug),
|
|
3349
|
+
refOptions: (targetSlug) => refOptionsFor(refCache.value, targetSlug),
|
|
3350
|
+
embedOptions: (targetSlug) => embedOptionsFor(embedCache.value, targetSlug),
|
|
3351
|
+
embedViewsFor: (record) => buildEmbedViews(collection.value?.schema ?? null, embedCache.value, record, locale.value),
|
|
3352
|
+
currencySymbol: (currency) => currencySymbolForLocale(currency, locale.value),
|
|
3353
|
+
artifactUrl: (value) => collectionUi().fileAssetUrl(value),
|
|
3354
|
+
fileRoutePath: (value) => collectionUi().fileRoutePath(value),
|
|
3355
|
+
formatSubCell: (subField, value, record) => renderSubCell(subField, value, record, refCache.value, locale.value),
|
|
3356
|
+
evaluateDerivedAgainstItem: (field, fieldKey, item) => evaluateDerived(field, fieldKey, item, collection.value?.schema ?? null, refRecordCache.value),
|
|
3357
|
+
derivedDisplay: (field, computedValue, record) => renderDerived(field, computedValue, record, locale.value)
|
|
3269
3358
|
};
|
|
3270
3359
|
}
|
|
3271
3360
|
//#endregion
|
|
@@ -3412,166 +3501,175 @@ var _hoisted_26$1 = [
|
|
|
3412
3501
|
"onClick"
|
|
3413
3502
|
];
|
|
3414
3503
|
var _hoisted_27$1 = { class: "material-icons text-sm" };
|
|
3415
|
-
var _hoisted_28 = [
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
var
|
|
3421
|
-
|
|
3504
|
+
var _hoisted_28 = [
|
|
3505
|
+
"title",
|
|
3506
|
+
"aria-label",
|
|
3507
|
+
"aria-expanded"
|
|
3508
|
+
];
|
|
3509
|
+
var _hoisted_29 = {
|
|
3510
|
+
key: 0,
|
|
3511
|
+
class: "absolute left-0 top-full mt-1 z-20 min-w-max rounded border border-slate-200 bg-white shadow-lg py-1",
|
|
3512
|
+
"data-testid": "collection-view-add-menu"
|
|
3513
|
+
};
|
|
3514
|
+
var _hoisted_30 = ["title", "aria-label"];
|
|
3515
|
+
var _hoisted_31 = ["value", "aria-label"];
|
|
3516
|
+
var _hoisted_32 = ["value"];
|
|
3517
|
+
var _hoisted_33 = ["value", "aria-label"];
|
|
3518
|
+
var _hoisted_34 = ["value"];
|
|
3519
|
+
var _hoisted_35 = {
|
|
3422
3520
|
key: 3,
|
|
3423
3521
|
class: "text-[10px] text-slate-400 font-bold uppercase tracking-wider select-none"
|
|
3424
3522
|
};
|
|
3425
|
-
var
|
|
3523
|
+
var _hoisted_36 = {
|
|
3426
3524
|
key: 3,
|
|
3427
3525
|
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",
|
|
3428
3526
|
"data-testid": "collections-data-issues"
|
|
3429
3527
|
};
|
|
3430
|
-
var
|
|
3431
|
-
var
|
|
3432
|
-
var
|
|
3528
|
+
var _hoisted_37 = { class: "flex-1" };
|
|
3529
|
+
var _hoisted_38 = { class: "flex-1 overflow-auto" };
|
|
3530
|
+
var _hoisted_39 = {
|
|
3433
3531
|
key: 0,
|
|
3434
3532
|
class: "flex flex-col items-center justify-center py-20 text-sm text-slate-500 gap-3"
|
|
3435
3533
|
};
|
|
3436
|
-
var
|
|
3534
|
+
var _hoisted_40 = {
|
|
3437
3535
|
key: 1,
|
|
3438
3536
|
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"
|
|
3439
3537
|
};
|
|
3440
|
-
var
|
|
3441
|
-
var
|
|
3538
|
+
var _hoisted_41 = { key: 2 };
|
|
3539
|
+
var _hoisted_42 = {
|
|
3442
3540
|
key: 3,
|
|
3443
3541
|
class: "p-4"
|
|
3444
3542
|
};
|
|
3445
|
-
var
|
|
3543
|
+
var _hoisted_43 = {
|
|
3446
3544
|
key: 4,
|
|
3447
3545
|
class: "h-full flex flex-col"
|
|
3448
3546
|
};
|
|
3449
|
-
var
|
|
3547
|
+
var _hoisted_44 = {
|
|
3450
3548
|
key: 0,
|
|
3451
3549
|
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",
|
|
3452
3550
|
"data-testid": "collections-inline-error"
|
|
3453
3551
|
};
|
|
3454
|
-
var
|
|
3455
|
-
var
|
|
3456
|
-
var
|
|
3457
|
-
var
|
|
3552
|
+
var _hoisted_45 = { class: "flex-1" };
|
|
3553
|
+
var _hoisted_46 = ["aria-label"];
|
|
3554
|
+
var _hoisted_47 = { class: "flex-1 min-h-0 px-3 py-2" };
|
|
3555
|
+
var _hoisted_48 = {
|
|
3458
3556
|
key: 5,
|
|
3459
3557
|
class: "h-full",
|
|
3460
3558
|
"data-testid": "collection-custom-view-body"
|
|
3461
3559
|
};
|
|
3462
|
-
var
|
|
3560
|
+
var _hoisted_49 = {
|
|
3463
3561
|
key: 6,
|
|
3464
3562
|
class: "flex flex-col items-center justify-center py-20 text-sm text-slate-400 gap-2"
|
|
3465
3563
|
};
|
|
3466
|
-
var
|
|
3467
|
-
var
|
|
3564
|
+
var _hoisted_50 = { class: "font-semibold text-slate-600" };
|
|
3565
|
+
var _hoisted_51 = {
|
|
3468
3566
|
key: 7,
|
|
3469
3567
|
class: "flex flex-col items-center justify-center py-20 text-sm text-slate-400 gap-2"
|
|
3470
3568
|
};
|
|
3471
|
-
var
|
|
3472
|
-
var
|
|
3569
|
+
var _hoisted_52 = { class: "font-semibold text-slate-600" };
|
|
3570
|
+
var _hoisted_53 = {
|
|
3473
3571
|
key: 8,
|
|
3474
3572
|
class: "overflow-x-auto [container-type:inline-size]"
|
|
3475
3573
|
};
|
|
3476
|
-
var
|
|
3574
|
+
var _hoisted_54 = {
|
|
3477
3575
|
key: 0,
|
|
3478
3576
|
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",
|
|
3479
3577
|
"data-testid": "collections-inline-error"
|
|
3480
3578
|
};
|
|
3481
|
-
var
|
|
3482
|
-
var
|
|
3483
|
-
var
|
|
3484
|
-
var
|
|
3485
|
-
var
|
|
3486
|
-
var
|
|
3487
|
-
var
|
|
3488
|
-
var
|
|
3579
|
+
var _hoisted_55 = { class: "flex-1" };
|
|
3580
|
+
var _hoisted_56 = ["aria-label"];
|
|
3581
|
+
var _hoisted_57 = { class: "min-w-full text-xs" };
|
|
3582
|
+
var _hoisted_58 = { class: "bg-slate-50 border-b border-slate-200" };
|
|
3583
|
+
var _hoisted_59 = ["aria-sort"];
|
|
3584
|
+
var _hoisted_60 = { class: "flex items-center gap-1" };
|
|
3585
|
+
var _hoisted_61 = ["title"];
|
|
3586
|
+
var _hoisted_62 = [
|
|
3489
3587
|
"data-testid",
|
|
3490
3588
|
"aria-label",
|
|
3491
3589
|
"onClick",
|
|
3492
3590
|
"onPointerenter"
|
|
3493
3591
|
];
|
|
3494
|
-
var
|
|
3495
|
-
var
|
|
3496
|
-
var
|
|
3592
|
+
var _hoisted_63 = { class: "material-icons text-base align-middle" };
|
|
3593
|
+
var _hoisted_64 = { class: "divide-y divide-slate-100 bg-white" };
|
|
3594
|
+
var _hoisted_65 = [
|
|
3497
3595
|
"aria-label",
|
|
3498
3596
|
"data-testid",
|
|
3499
3597
|
"onClick",
|
|
3500
3598
|
"onKeydown"
|
|
3501
3599
|
];
|
|
3502
|
-
var
|
|
3600
|
+
var _hoisted_66 = [
|
|
3503
3601
|
"checked",
|
|
3504
3602
|
"disabled",
|
|
3505
3603
|
"data-testid",
|
|
3506
3604
|
"aria-label",
|
|
3507
3605
|
"onChange"
|
|
3508
3606
|
];
|
|
3509
|
-
var
|
|
3607
|
+
var _hoisted_67 = [
|
|
3510
3608
|
"checked",
|
|
3511
3609
|
"disabled",
|
|
3512
3610
|
"data-testid",
|
|
3513
3611
|
"aria-label",
|
|
3514
3612
|
"onChange"
|
|
3515
3613
|
];
|
|
3516
|
-
var
|
|
3614
|
+
var _hoisted_68 = {
|
|
3517
3615
|
key: 2,
|
|
3518
3616
|
class: "block truncate"
|
|
3519
3617
|
};
|
|
3520
|
-
var
|
|
3618
|
+
var _hoisted_69 = [
|
|
3521
3619
|
"href",
|
|
3522
3620
|
"tabindex",
|
|
3523
3621
|
"data-testid",
|
|
3524
3622
|
"onClick",
|
|
3525
3623
|
"onKeydown"
|
|
3526
3624
|
];
|
|
3527
|
-
var
|
|
3625
|
+
var _hoisted_70 = [
|
|
3528
3626
|
"value",
|
|
3529
3627
|
"disabled",
|
|
3530
3628
|
"data-testid",
|
|
3531
3629
|
"aria-label",
|
|
3532
3630
|
"onChange"
|
|
3533
3631
|
];
|
|
3534
|
-
var
|
|
3632
|
+
var _hoisted_71 = {
|
|
3535
3633
|
key: 0,
|
|
3536
3634
|
value: ""
|
|
3537
3635
|
};
|
|
3538
|
-
var
|
|
3539
|
-
var
|
|
3636
|
+
var _hoisted_72 = ["value"];
|
|
3637
|
+
var _hoisted_73 = {
|
|
3540
3638
|
key: 4,
|
|
3541
3639
|
class: "block truncate tabular-nums font-semibold text-slate-900"
|
|
3542
3640
|
};
|
|
3543
|
-
var
|
|
3641
|
+
var _hoisted_74 = {
|
|
3544
3642
|
key: 5,
|
|
3545
3643
|
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"
|
|
3546
3644
|
};
|
|
3547
|
-
var
|
|
3645
|
+
var _hoisted_75 = {
|
|
3548
3646
|
key: 6,
|
|
3549
3647
|
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"
|
|
3550
3648
|
};
|
|
3551
|
-
var _hoisted_75 = ["href", "data-testid"];
|
|
3552
3649
|
var _hoisted_76 = ["href", "data-testid"];
|
|
3553
|
-
var _hoisted_77 = [
|
|
3650
|
+
var _hoisted_77 = ["href", "data-testid"];
|
|
3651
|
+
var _hoisted_78 = [
|
|
3554
3652
|
"href",
|
|
3555
3653
|
"data-testid",
|
|
3556
3654
|
"onClick"
|
|
3557
3655
|
];
|
|
3558
|
-
var
|
|
3656
|
+
var _hoisted_79 = {
|
|
3559
3657
|
key: 10,
|
|
3560
3658
|
class: "block truncate text-slate-600"
|
|
3561
3659
|
};
|
|
3562
|
-
var
|
|
3563
|
-
var
|
|
3564
|
-
var
|
|
3565
|
-
var
|
|
3660
|
+
var _hoisted_80 = { class: "bg-white rounded-2xl shadow-2xl w-full max-w-xl flex flex-col border border-slate-200 overflow-hidden" };
|
|
3661
|
+
var _hoisted_81 = { class: "px-6 py-4 border-b border-slate-100 flex items-center gap-3 bg-slate-50/50" };
|
|
3662
|
+
var _hoisted_82 = { class: "flex-1" };
|
|
3663
|
+
var _hoisted_83 = {
|
|
3566
3664
|
id: "collections-chat-title",
|
|
3567
3665
|
class: "text-sm font-bold text-slate-800 uppercase tracking-wide"
|
|
3568
3666
|
};
|
|
3569
|
-
var
|
|
3570
|
-
var
|
|
3571
|
-
var
|
|
3572
|
-
var
|
|
3573
|
-
var
|
|
3574
|
-
var
|
|
3667
|
+
var _hoisted_84 = { class: "text-xs text-slate-400 font-semibold" };
|
|
3668
|
+
var _hoisted_85 = ["aria-label"];
|
|
3669
|
+
var _hoisted_86 = { class: "px-6 py-5" };
|
|
3670
|
+
var _hoisted_87 = ["placeholder", "onKeydown"];
|
|
3671
|
+
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" };
|
|
3672
|
+
var _hoisted_89 = ["disabled"];
|
|
3575
3673
|
/** `slug` / `selected` are supplied only in EMBEDDED mode (the
|
|
3576
3674
|
* `presentCollection` chat card mounts this component and drives both
|
|
3577
3675
|
* from the tool result). In standalone route mode (the
|
|
@@ -3747,12 +3845,12 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
3747
3845
|
return scalarSortValue(field, item[key]);
|
|
3748
3846
|
}
|
|
3749
3847
|
/** Derived rows sort by their display type: money/number → numeric,
|
|
3750
|
-
* date
|
|
3848
|
+
* date → epoch, anything else → the enriched value as a string. */
|
|
3751
3849
|
function derivedSortValue(field, key, item) {
|
|
3752
|
-
const
|
|
3850
|
+
const display = field.type === "derived" ? field.display : void 0;
|
|
3753
3851
|
if (display === void 0 || display === "number" || display === "money") return numericSortValue(evaluateDerivedAgainstItem(field, key, item));
|
|
3754
3852
|
const enriched = collection.value ? render.deriveAll(collection.value.schema, item, render.refRecordCache.value) : item;
|
|
3755
|
-
if (display === "date"
|
|
3853
|
+
if (display === "date") return dateSortValue(enriched[key]);
|
|
3756
3854
|
return stringSortValue(enriched[key]);
|
|
3757
3855
|
}
|
|
3758
3856
|
const sortedItems = computed(() => {
|
|
@@ -3930,10 +4028,12 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
3930
4028
|
function buildChatSeed(slug, message, itemId) {
|
|
3931
4029
|
const current = collection.value;
|
|
3932
4030
|
if (current?.source !== "feed") return itemId ? `/${slug} id=${itemId} ${message}` : `/${slug} ${message}`;
|
|
4031
|
+
const dataPath = current.schema.dataPath ?? `data/feeds/${slug}`;
|
|
4032
|
+
const scoped = itemId ? `(for record \`${itemId}\`) ${message}` : message;
|
|
3933
4033
|
return t("collectionsView.feedChatSeed", {
|
|
3934
4034
|
slug,
|
|
3935
|
-
dataPath
|
|
3936
|
-
message:
|
|
4035
|
+
dataPath,
|
|
4036
|
+
message: scoped
|
|
3937
4037
|
});
|
|
3938
4038
|
}
|
|
3939
4039
|
/** Start a new general-role chat seeded from the current view. */
|
|
@@ -4008,7 +4108,10 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4008
4108
|
enumOriginallyEmpty.value = snapshotEmptyEnums(result.data.collection.schema, result.data.items);
|
|
4009
4109
|
await render.loadLinkedCollections(result.data.collection.schema, slug);
|
|
4010
4110
|
if (activeSlug.value !== slug) return;
|
|
4011
|
-
if (viewing.value)
|
|
4111
|
+
if (viewing.value) {
|
|
4112
|
+
const openId = String(viewing.value[result.data.collection.schema.primaryKey] ?? "");
|
|
4113
|
+
viewing.value = findItemById(openId) ?? null;
|
|
4114
|
+
}
|
|
4012
4115
|
}
|
|
4013
4116
|
function maybeAutoRefreshFeed(slug) {
|
|
4014
4117
|
if (embedded.value) return;
|
|
@@ -4097,16 +4200,41 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4097
4200
|
* page only (the seed starts a chat). Feeds qualify too — their views are
|
|
4098
4201
|
* authored under feeds/<slug>/ and the seed prompt points there. */
|
|
4099
4202
|
const canAddCustomView = computed(() => Boolean(collection.value) && !embedded.value);
|
|
4203
|
+
const addMenuOpen = ref(false);
|
|
4204
|
+
const addMenuRef = ref(null);
|
|
4205
|
+
/** Whether authoring a phone (remote app) view is worth offering — mirrors
|
|
4206
|
+
* the selector filter above: without the host's `fetchRemoteView` binding a
|
|
4207
|
+
* mobile view could be authored but never shown here. */
|
|
4208
|
+
const canAddMobileView = computed(() => Boolean(cui.fetchRemoteView));
|
|
4209
|
+
/** "+" click: open the target chooser, or skip the one-item menu and seed
|
|
4210
|
+
* the desktop prompt directly when mobile views aren't available. */
|
|
4211
|
+
function onAddViewClick() {
|
|
4212
|
+
if (!canAddMobileView.value) {
|
|
4213
|
+
addCustomView("desktop");
|
|
4214
|
+
return;
|
|
4215
|
+
}
|
|
4216
|
+
addMenuOpen.value = !addMenuOpen.value;
|
|
4217
|
+
}
|
|
4218
|
+
function closeAddMenuOnOutsideClick(event) {
|
|
4219
|
+
if (!addMenuRef.value?.contains(event.target)) addMenuOpen.value = false;
|
|
4220
|
+
}
|
|
4221
|
+
watch(addMenuOpen, (open) => {
|
|
4222
|
+
if (open) document.addEventListener("mousedown", closeAddMenuOnOutsideClick);
|
|
4223
|
+
else document.removeEventListener("mousedown", closeAddMenuOnOutsideClick);
|
|
4224
|
+
});
|
|
4100
4225
|
/** Seed a chat asking Claude to author a new custom view for this collection.
|
|
4101
4226
|
* Reuses the same chat-seed path as collection actions — the host injects a
|
|
4102
4227
|
* templated prompt; Claude asks, authors the HTML, and registers it. The
|
|
4103
4228
|
* authoring base is source-aware: a feed lives under `feeds/<slug>/`, every
|
|
4104
|
-
* other collection under the `data/skills/<slug>/` staging dir.
|
|
4105
|
-
|
|
4229
|
+
* other collection under the `data/skills/<slug>/` staging dir. The prompt
|
|
4230
|
+
* is target-aware: phone views follow the custom-view-remote contract and
|
|
4231
|
+
* register with `target: "mobile"`. */
|
|
4232
|
+
function addCustomView(target) {
|
|
4233
|
+
addMenuOpen.value = false;
|
|
4106
4234
|
const current = collection.value;
|
|
4107
4235
|
if (!current) return;
|
|
4108
4236
|
const base = current.source === "feed" ? `feeds/${current.slug}` : `data/skills/${current.slug}`;
|
|
4109
|
-
const prompt = t("collectionsView.addViewPrompt", {
|
|
4237
|
+
const prompt = t(target === "mobile" ? "collectionsView.addMobileViewPrompt" : "collectionsView.addViewPrompt", {
|
|
4110
4238
|
title: current.title,
|
|
4111
4239
|
base
|
|
4112
4240
|
});
|
|
@@ -4161,7 +4289,8 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4161
4289
|
}
|
|
4162
4290
|
/** Select a custom view by id (builds the `custom:<id>` mode key). */
|
|
4163
4291
|
function setCustomView(viewId) {
|
|
4164
|
-
|
|
4292
|
+
const mode = `custom:${viewId}`;
|
|
4293
|
+
view.value = mode;
|
|
4165
4294
|
}
|
|
4166
4295
|
/** A short, slug-safe id not already used by a loaded record. Collisions
|
|
4167
4296
|
* are astronomically unlikely (32 bits), but we still re-roll a few
|
|
@@ -4319,7 +4448,8 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4319
4448
|
viewing.value = null;
|
|
4320
4449
|
return;
|
|
4321
4450
|
}
|
|
4322
|
-
|
|
4451
|
+
const match = findItemById(selected) ?? null;
|
|
4452
|
+
viewing.value = match;
|
|
4323
4453
|
}
|
|
4324
4454
|
/** Title for the open-mode header: the record's primary-key value
|
|
4325
4455
|
* (e.g. `INV-2026-0001`), falling back to the collection title.
|
|
@@ -4413,19 +4543,17 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4413
4543
|
/** Whether a `toggle` field reads as checked: its projected enum field
|
|
4414
4544
|
* currently equals `onValue`. The toggle stores nothing itself. */
|
|
4415
4545
|
function toggleChecked(item, field) {
|
|
4416
|
-
return field.
|
|
4546
|
+
return field.type === "toggle" && String(item[field.field] ?? "") === field.onValue;
|
|
4417
4547
|
}
|
|
4418
4548
|
/** Flip a `toggle`: write the projected enum field to `offValue` when
|
|
4419
4549
|
* currently checked, else `onValue`. Reuses the inline-edit PUT path
|
|
4420
4550
|
* (optimistic + rollback) — the toggle has no value of its own. */
|
|
4421
4551
|
function commitToggle(item, field) {
|
|
4552
|
+
if (field.type !== "toggle" || !collection.value) return;
|
|
4422
4553
|
const targetKey = field.field;
|
|
4423
|
-
if (!targetKey || !collection.value) return;
|
|
4424
4554
|
const enumField = collection.value.schema.fields[targetKey];
|
|
4425
4555
|
if (!enumField) return;
|
|
4426
|
-
|
|
4427
|
-
if (next === void 0) return;
|
|
4428
|
-
commitInlineEdit(item, targetKey, enumField, next);
|
|
4556
|
+
commitInlineEdit(item, targetKey, enumField, toggleChecked(item, field) ? field.offValue : field.onValue);
|
|
4429
4557
|
}
|
|
4430
4558
|
async function confirmDelete(item) {
|
|
4431
4559
|
if (!collection.value) return;
|
|
@@ -4582,6 +4710,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4582
4710
|
view.value = slug && readCollectionViewMode(slug) || "table";
|
|
4583
4711
|
anchorOverride.value = null;
|
|
4584
4712
|
kanbanOverride.value = null;
|
|
4713
|
+
addMenuOpen.value = false;
|
|
4585
4714
|
sortState.value = storedSortFor(slug);
|
|
4586
4715
|
}
|
|
4587
4716
|
if (slug) loadCollection(slug);
|
|
@@ -4630,6 +4759,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4630
4759
|
changeUnsub = null;
|
|
4631
4760
|
clearLiveRefreshTimer();
|
|
4632
4761
|
if (refreshNoteTimer !== void 0) clearTimeout(refreshNoteTimer);
|
|
4762
|
+
document.removeEventListener("mousedown", closeAddMenuOnOutsideClick);
|
|
4633
4763
|
});
|
|
4634
4764
|
watch([
|
|
4635
4765
|
activeView,
|
|
@@ -4664,7 +4794,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4664
4794
|
"aria-label": unref(t)("collectionsView.backToIndex"),
|
|
4665
4795
|
"data-testid": "collections-back",
|
|
4666
4796
|
onClick: goBack
|
|
4667
|
-
}, [..._cache[
|
|
4797
|
+
}, [..._cache[25] || (_cache[25] = [createElementVNode("span", { class: "material-icons text-lg" }, "arrow_back", -1)])], 8, _hoisted_3$5)) : createCommentVNode("", true),
|
|
4668
4798
|
collection.value ? (openBlock(), createElementBlock("div", _hoisted_4$5, [createElementVNode("span", _hoisted_5$5, toDisplayString(collection.value.icon), 1)])) : createCommentVNode("", true),
|
|
4669
4799
|
createElementVNode("div", _hoisted_6$4, [createElementVNode("h1", _hoisted_7$4, toDisplayString(collection.value?.title ?? unref(t)("collectionsView.title")), 1), collection.value ? (openBlock(), createElementBlock("span", _hoisted_8$4, toDisplayString(collection.value.slug), 1)) : createCommentVNode("", true)]),
|
|
4670
4800
|
collection.value && !embedded.value ? (openBlock(), createBlock(resolveDynamicComponent(unref(pinToggle)), {
|
|
@@ -4693,7 +4823,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4693
4823
|
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",
|
|
4694
4824
|
"data-testid": "collections-chat",
|
|
4695
4825
|
onClick: openChat
|
|
4696
|
-
}, [_cache[
|
|
4826
|
+
}, [_cache[26] || (_cache[26] = createElementVNode("span", { class: "material-icons text-sm" }, "forum", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.chat")), 1)])) : createCommentVNode("", true),
|
|
4697
4827
|
(openBlock(true), createElementBlock(Fragment, null, renderList(collectionActions.value, (action) => {
|
|
4698
4828
|
return openBlock(), createElementBlock("button", {
|
|
4699
4829
|
key: action.id,
|
|
@@ -4710,7 +4840,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4710
4840
|
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",
|
|
4711
4841
|
"data-testid": "collections-add-item",
|
|
4712
4842
|
onClick: openCreate
|
|
4713
|
-
}, [_cache[
|
|
4843
|
+
}, [_cache[27] || (_cache[27] = createElementVNode("span", { class: "material-icons text-sm" }, "add", -1)), createElementVNode("span", null, toDisplayString(unref(t)("common.add")), 1)])) : createCommentVNode("", true),
|
|
4714
4844
|
canDeleteCollection.value && !embedded.value ? (openBlock(), createElementBlock("button", {
|
|
4715
4845
|
key: 6,
|
|
4716
4846
|
type: "button",
|
|
@@ -4719,7 +4849,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4719
4849
|
"aria-label": unref(t)("collectionsView.deleteCollection"),
|
|
4720
4850
|
"data-testid": "collections-delete",
|
|
4721
4851
|
onClick: confirmCollectionDelete
|
|
4722
|
-
}, [..._cache[
|
|
4852
|
+
}, [..._cache[28] || (_cache[28] = [createElementVNode("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_13$4)) : createCommentVNode("", true),
|
|
4723
4853
|
canDeleteFeed.value && !embedded.value ? (openBlock(), createElementBlock("button", {
|
|
4724
4854
|
key: 7,
|
|
4725
4855
|
type: "button",
|
|
@@ -4728,11 +4858,11 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4728
4858
|
"aria-label": unref(t)("collectionsView.deleteFeed"),
|
|
4729
4859
|
"data-testid": "feeds-delete",
|
|
4730
4860
|
onClick: confirmFeedDelete
|
|
4731
|
-
}, [..._cache[
|
|
4861
|
+
}, [..._cache[29] || (_cache[29] = [createElementVNode("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_14$4)) : createCommentVNode("", true)
|
|
4732
4862
|
])) : createCommentVNode("", true),
|
|
4733
|
-
refreshNote.value ? (openBlock(), createElementBlock("div", _hoisted_15$4, [_cache[
|
|
4863
|
+
refreshNote.value ? (openBlock(), createElementBlock("div", _hoisted_15$4, [_cache[30] || (_cache[30] = createElementVNode("span", { class: "material-icons text-base text-indigo-600" }, "hourglass_top", -1)), createElementVNode("span", _hoisted_16$4, toDisplayString(refreshNote.value), 1)])) : createCommentVNode("", true),
|
|
4734
4864
|
collection.value && (!__props.hideSearch && items.value.length > 0 || !__props.hideViewToggle && (hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value)) ? (openBlock(), createElementBlock("div", _hoisted_17$4, [!__props.hideSearch && items.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_18$4, [
|
|
4735
|
-
_cache[
|
|
4865
|
+
_cache[32] || (_cache[32] = createElementVNode("span", { class: "absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 pointer-events-none" }, [createElementVNode("span", { class: "material-icons text-lg" }, "search")], -1)),
|
|
4736
4866
|
withDirectives(createElementVNode("input", {
|
|
4737
4867
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event),
|
|
4738
4868
|
type: "text",
|
|
@@ -4746,7 +4876,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4746
4876
|
"aria-label": unref(t)("collectionsView.clearSearch"),
|
|
4747
4877
|
class: "absolute inset-y-0 right-0 flex items-center pr-2.5 text-slate-400 hover:text-slate-600",
|
|
4748
4878
|
onClick: _cache[1] || (_cache[1] = ($event) => searchQuery.value = "")
|
|
4749
|
-
}, [..._cache[
|
|
4879
|
+
}, [..._cache[31] || (_cache[31] = [createElementVNode("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_20$2)) : createCommentVNode("", true)
|
|
4750
4880
|
])) : createCommentVNode("", true), createElementVNode("div", _hoisted_21$2, [
|
|
4751
4881
|
!__props.hideViewToggle && (hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value) ? (openBlock(), createElementBlock("div", {
|
|
4752
4882
|
key: 0,
|
|
@@ -4760,7 +4890,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4760
4890
|
"aria-pressed": activeView.value === "table",
|
|
4761
4891
|
"data-testid": "collection-view-toggle-table",
|
|
4762
4892
|
onClick: _cache[2] || (_cache[2] = ($event) => setView("table"))
|
|
4763
|
-
}, [_cache[
|
|
4893
|
+
}, [_cache[33] || (_cache[33] = createElementVNode("span", { class: "material-icons text-sm" }, "table_rows", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewTable")), 1)], 10, _hoisted_23$2),
|
|
4764
4894
|
hasCalendar.value ? (openBlock(), createElementBlock("button", {
|
|
4765
4895
|
key: 0,
|
|
4766
4896
|
type: "button",
|
|
@@ -4768,7 +4898,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4768
4898
|
"aria-pressed": activeView.value === "calendar",
|
|
4769
4899
|
"data-testid": "collection-view-toggle-calendar",
|
|
4770
4900
|
onClick: _cache[3] || (_cache[3] = ($event) => setView("calendar"))
|
|
4771
|
-
}, [_cache[
|
|
4901
|
+
}, [_cache[34] || (_cache[34] = createElementVNode("span", { class: "material-icons text-sm" }, "calendar_month", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewCalendar")), 1)], 10, _hoisted_24$1)) : createCommentVNode("", true),
|
|
4772
4902
|
hasKanban.value ? (openBlock(), createElementBlock("button", {
|
|
4773
4903
|
key: 1,
|
|
4774
4904
|
type: "button",
|
|
@@ -4776,7 +4906,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4776
4906
|
"aria-pressed": activeView.value === "kanban",
|
|
4777
4907
|
"data-testid": "collection-view-toggle-kanban",
|
|
4778
4908
|
onClick: _cache[4] || (_cache[4] = ($event) => setView("kanban"))
|
|
4779
|
-
}, [_cache[
|
|
4909
|
+
}, [_cache[35] || (_cache[35] = createElementVNode("span", { class: "material-icons text-sm" }, "view_kanban", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewKanban")), 1)], 10, _hoisted_25$1)) : createCommentVNode("", true),
|
|
4780
4910
|
(openBlock(true), createElementBlock(Fragment, null, renderList(customViews.value, (cv) => {
|
|
4781
4911
|
return openBlock(), createElementBlock("button", {
|
|
4782
4912
|
key: cv.id,
|
|
@@ -4787,15 +4917,30 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4787
4917
|
onClick: ($event) => setCustomView(cv.id)
|
|
4788
4918
|
}, [createElementVNode("span", _hoisted_27$1, toDisplayString(cv.icon || (cv.target === "mobile" ? "smartphone" : "dashboard_customize")), 1), createElementVNode("span", null, toDisplayString(cv.label), 1)], 10, _hoisted_26$1);
|
|
4789
4919
|
}), 128)),
|
|
4790
|
-
canAddCustomView.value ? (openBlock(), createElementBlock("
|
|
4920
|
+
canAddCustomView.value ? (openBlock(), createElementBlock("div", {
|
|
4791
4921
|
key: 2,
|
|
4922
|
+
ref_key: "addMenuRef",
|
|
4923
|
+
ref: addMenuRef,
|
|
4924
|
+
class: "relative"
|
|
4925
|
+
}, [createElementVNode("button", {
|
|
4792
4926
|
type: "button",
|
|
4793
4927
|
class: "h-8 w-8 flex items-center justify-center rounded bg-white text-slate-500 border border-slate-200 hover:bg-slate-50",
|
|
4794
4928
|
title: unref(t)("collectionsView.addView"),
|
|
4795
4929
|
"aria-label": unref(t)("collectionsView.addView"),
|
|
4930
|
+
"aria-expanded": addMenuOpen.value,
|
|
4796
4931
|
"data-testid": "collection-view-add",
|
|
4797
|
-
onClick:
|
|
4798
|
-
}, [..._cache[
|
|
4932
|
+
onClick: onAddViewClick
|
|
4933
|
+
}, [..._cache[36] || (_cache[36] = [createElementVNode("span", { class: "material-icons text-sm" }, "add", -1)])], 8, _hoisted_28), addMenuOpen.value ? (openBlock(), createElementBlock("div", _hoisted_29, [createElementVNode("button", {
|
|
4934
|
+
type: "button",
|
|
4935
|
+
class: "w-full h-8 px-3 flex items-center gap-2 text-xs font-bold text-slate-600 hover:bg-slate-50",
|
|
4936
|
+
"data-testid": "collection-view-add-desktop",
|
|
4937
|
+
onClick: _cache[5] || (_cache[5] = ($event) => addCustomView("desktop"))
|
|
4938
|
+
}, [_cache[37] || (_cache[37] = createElementVNode("span", { class: "material-icons text-sm" }, "dashboard_customize", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.addViewDesktop")), 1)]), createElementVNode("button", {
|
|
4939
|
+
type: "button",
|
|
4940
|
+
class: "w-full h-8 px-3 flex items-center gap-2 text-xs font-bold text-slate-600 hover:bg-slate-50",
|
|
4941
|
+
"data-testid": "collection-view-add-mobile",
|
|
4942
|
+
onClick: _cache[6] || (_cache[6] = ($event) => addCustomView("mobile"))
|
|
4943
|
+
}, [_cache[38] || (_cache[38] = createElementVNode("span", { class: "material-icons text-sm" }, "smartphone", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.addViewMobile")), 1)])])) : createCommentVNode("", true)], 512)) : createCommentVNode("", true),
|
|
4799
4944
|
canConfigureViews.value ? (openBlock(), createElementBlock("button", {
|
|
4800
4945
|
key: 3,
|
|
4801
4946
|
type: "button",
|
|
@@ -4803,8 +4948,8 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4803
4948
|
title: unref(t)("collectionsView.config.open"),
|
|
4804
4949
|
"aria-label": unref(t)("collectionsView.config.open"),
|
|
4805
4950
|
"data-testid": "collection-config-open",
|
|
4806
|
-
onClick: _cache[
|
|
4807
|
-
}, [..._cache[
|
|
4951
|
+
onClick: _cache[7] || (_cache[7] = ($event) => configOpen.value = true)
|
|
4952
|
+
}, [..._cache[39] || (_cache[39] = [createElementVNode("span", { class: "material-icons text-sm" }, "settings", -1)])], 8, _hoisted_30)) : createCommentVNode("", true)
|
|
4808
4953
|
], 8, _hoisted_22$2)) : createCommentVNode("", true),
|
|
4809
4954
|
calendarActive.value && dateFields.value.length > 1 ? (openBlock(), createElementBlock("select", {
|
|
4810
4955
|
key: 1,
|
|
@@ -4812,42 +4957,42 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4812
4957
|
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",
|
|
4813
4958
|
"aria-label": unref(t)("collectionsView.calendarFieldLabel"),
|
|
4814
4959
|
"data-testid": "collection-calendar-field",
|
|
4815
|
-
onChange: _cache[
|
|
4960
|
+
onChange: _cache[8] || (_cache[8] = ($event) => anchorOverride.value = $event.target.value)
|
|
4816
4961
|
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(dateFields.value, (key) => {
|
|
4817
4962
|
return openBlock(), createElementBlock("option", {
|
|
4818
4963
|
key,
|
|
4819
4964
|
value: key
|
|
4820
|
-
}, toDisplayString(collection.value?.schema.fields[key]?.label ?? key), 9,
|
|
4821
|
-
}), 128))], 40,
|
|
4965
|
+
}, toDisplayString(collection.value?.schema.fields[key]?.label ?? key), 9, _hoisted_32);
|
|
4966
|
+
}), 128))], 40, _hoisted_31)) : createCommentVNode("", true),
|
|
4822
4967
|
kanbanActive.value && enumFields.value.length > 1 ? (openBlock(), createElementBlock("select", {
|
|
4823
4968
|
key: 2,
|
|
4824
4969
|
value: kanbanGroupField.value,
|
|
4825
4970
|
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",
|
|
4826
4971
|
"aria-label": unref(t)("collectionsView.kanbanFieldLabel"),
|
|
4827
4972
|
"data-testid": "collection-kanban-field",
|
|
4828
|
-
onChange: _cache[
|
|
4973
|
+
onChange: _cache[9] || (_cache[9] = ($event) => kanbanOverride.value = $event.target.value)
|
|
4829
4974
|
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(enumFields.value, (key) => {
|
|
4830
4975
|
return openBlock(), createElementBlock("option", {
|
|
4831
4976
|
key,
|
|
4832
4977
|
value: key
|
|
4833
|
-
}, toDisplayString(collection.value?.schema.fields[key]?.label ?? key), 9,
|
|
4834
|
-
}), 128))], 40,
|
|
4835
|
-
items.value.length > 0 ? (openBlock(), createElementBlock("div",
|
|
4978
|
+
}, toDisplayString(collection.value?.schema.fields[key]?.label ?? key), 9, _hoisted_34);
|
|
4979
|
+
}), 128))], 40, _hoisted_33)) : createCommentVNode("", true),
|
|
4980
|
+
items.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_35, toDisplayString(unref(t)("collectionsView.searchSummary", {
|
|
4836
4981
|
shown: filteredItems.value.length,
|
|
4837
4982
|
total: items.value.length
|
|
4838
4983
|
})), 1)) : createCommentVNode("", true)
|
|
4839
4984
|
])])) : createCommentVNode("", true),
|
|
4840
|
-
collection.value && dataIssues.value.length > 0 ? (openBlock(), createElementBlock("div",
|
|
4841
|
-
_cache[
|
|
4842
|
-
createElementVNode("span",
|
|
4985
|
+
collection.value && dataIssues.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_36, [
|
|
4986
|
+
_cache[41] || (_cache[41] = createElementVNode("span", { class: "material-icons text-amber-600" }, "warning", -1)),
|
|
4987
|
+
createElementVNode("span", _hoisted_37, toDisplayString(unref(t)("collectionsView.dataIssuesDetected", { count: dataIssues.value.length })), 1),
|
|
4843
4988
|
createElementVNode("button", {
|
|
4844
4989
|
type: "button",
|
|
4845
4990
|
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",
|
|
4846
4991
|
"data-testid": "collections-repair",
|
|
4847
4992
|
onClick: repairCollection
|
|
4848
|
-
}, [_cache[
|
|
4993
|
+
}, [_cache[40] || (_cache[40] = createElementVNode("span", { class: "material-icons text-sm" }, "build", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.repair")), 1)])
|
|
4849
4994
|
])) : createCommentVNode("", true),
|
|
4850
|
-
createElementVNode("div",
|
|
4995
|
+
createElementVNode("div", _hoisted_38, [loading.value ? (openBlock(), createElementBlock("div", _hoisted_39, [_cache[42] || (_cache[42] = createElementVNode("div", { class: "h-8 w-8 border-2 border-indigo-600/20 border-t-indigo-600 rounded-full animate-spin" }, null, -1)), createElementVNode("span", null, toDisplayString(unref(t)("common.loading")), 1)])) : loadError.value ? (openBlock(), createElementBlock("div", _hoisted_40, [_cache[43] || (_cache[43] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)), createElementVNode("span", null, toDisplayString(loadError.value === "not-found" ? unref(t)("collectionsView.notFound") : `${unref(t)("collectionsView.loadFailed")}: ${loadError.value}`), 1)])) : !collection.value ? (openBlock(), createElementBlock("div", _hoisted_41)) : calendarActive.value ? (openBlock(), createElementBlock("div", _hoisted_42, [createVNode(CollectionCalendarView_default, {
|
|
4851
4996
|
schema: collection.value.schema,
|
|
4852
4997
|
items: filteredItems.value,
|
|
4853
4998
|
"anchor-field": calendarAnchorField.value,
|
|
@@ -4883,7 +5028,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4883
5028
|
}, {
|
|
4884
5029
|
detail: withCtx(() => [createVNode(CollectionRecordPanel_default, {
|
|
4885
5030
|
editing: editing.value,
|
|
4886
|
-
"onUpdate:editing": _cache[
|
|
5031
|
+
"onUpdate:editing": _cache[10] || (_cache[10] = ($event) => editing.value = $event),
|
|
4887
5032
|
collection: collection.value,
|
|
4888
5033
|
viewing: viewing.value,
|
|
4889
5034
|
saving: saving.value,
|
|
@@ -4901,7 +5046,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4901
5046
|
onCancel: cancelEditor,
|
|
4902
5047
|
onEdit: editFromView,
|
|
4903
5048
|
onClose: onDayClose,
|
|
4904
|
-
onDelete: _cache[
|
|
5049
|
+
onDelete: _cache[11] || (_cache[11] = ($event) => viewing.value && confirmDelete(viewing.value)),
|
|
4905
5050
|
onRunAction: runAction,
|
|
4906
5051
|
onItemChat
|
|
4907
5052
|
}, null, 8, [
|
|
@@ -4932,16 +5077,16 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4932
5077
|
"selected",
|
|
4933
5078
|
"can-create",
|
|
4934
5079
|
"show-detail"
|
|
4935
|
-
])) : createCommentVNode("", true)])) : kanbanActive.value ? (openBlock(), createElementBlock("div",
|
|
4936
|
-
_cache[
|
|
4937
|
-
createElementVNode("span",
|
|
5080
|
+
])) : createCommentVNode("", true)])) : kanbanActive.value ? (openBlock(), createElementBlock("div", _hoisted_43, [inlineError.value ? (openBlock(), createElementBlock("div", _hoisted_44, [
|
|
5081
|
+
_cache[45] || (_cache[45] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)),
|
|
5082
|
+
createElementVNode("span", _hoisted_45, toDisplayString(unref(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
|
|
4938
5083
|
createElementVNode("button", {
|
|
4939
5084
|
type: "button",
|
|
4940
5085
|
class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
|
|
4941
5086
|
"aria-label": unref(t)("common.close"),
|
|
4942
|
-
onClick: _cache[
|
|
4943
|
-
}, [..._cache[
|
|
4944
|
-
])) : createCommentVNode("", true), createElementVNode("div",
|
|
5087
|
+
onClick: _cache[12] || (_cache[12] = ($event) => inlineError.value = null)
|
|
5088
|
+
}, [..._cache[44] || (_cache[44] = [createElementVNode("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_46)
|
|
5089
|
+
])) : createCommentVNode("", true), createElementVNode("div", _hoisted_47, [createVNode(CollectionKanbanView_default, {
|
|
4945
5090
|
schema: collection.value.schema,
|
|
4946
5091
|
items: filteredItems.value,
|
|
4947
5092
|
"group-field": kanbanGroupField.value,
|
|
@@ -4955,7 +5100,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4955
5100
|
"group-field",
|
|
4956
5101
|
"selected",
|
|
4957
5102
|
"notified"
|
|
4958
|
-
])])])) : activeCustomView.value ? (openBlock(), createElementBlock("div",
|
|
5103
|
+
])])])) : activeCustomView.value ? (openBlock(), createElementBlock("div", _hoisted_48, [activeCustomView.value.target === "mobile" ? (openBlock(), createBlock(CollectionRemoteViewPreview_default, {
|
|
4959
5104
|
key: 0,
|
|
4960
5105
|
slug: collection.value.slug,
|
|
4961
5106
|
view: activeCustomView.value,
|
|
@@ -4966,32 +5111,32 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4966
5111
|
view: activeCustomView.value,
|
|
4967
5112
|
onOpenItem: onCustomViewOpenItem,
|
|
4968
5113
|
onStartChat: onCustomViewStartChat
|
|
4969
|
-
}, null, 8, ["slug", "view"]))])) : items.value.length === 0 && editing.value?.mode !== "create" ? (openBlock(), createElementBlock("div",
|
|
4970
|
-
_cache[
|
|
4971
|
-
createElementVNode("p",
|
|
5114
|
+
}, null, 8, ["slug", "view"]))])) : items.value.length === 0 && editing.value?.mode !== "create" ? (openBlock(), createElementBlock("div", _hoisted_49, [_cache[46] || (_cache[46] = createElementVNode("span", { class: "material-icons text-4xl text-slate-300" }, "folder_open", -1)), createElementVNode("p", _hoisted_50, toDisplayString(unref(t)("collectionsView.itemsEmpty")), 1)])) : filteredItems.value.length === 0 && editing.value?.mode !== "create" ? (openBlock(), createElementBlock("div", _hoisted_51, [
|
|
5115
|
+
_cache[47] || (_cache[47] = createElementVNode("span", { class: "material-icons text-4xl text-slate-300" }, "search_off", -1)),
|
|
5116
|
+
createElementVNode("p", _hoisted_52, toDisplayString(unref(t)("collectionsView.noMatchingItems")), 1),
|
|
4972
5117
|
createElementVNode("button", {
|
|
4973
5118
|
type: "button",
|
|
4974
5119
|
class: "text-xs text-indigo-600 font-semibold hover:underline",
|
|
4975
|
-
onClick: _cache[
|
|
5120
|
+
onClick: _cache[13] || (_cache[13] = ($event) => searchQuery.value = "")
|
|
4976
5121
|
}, toDisplayString(unref(t)("collectionsView.clearSearch")), 1)
|
|
4977
|
-
])) : (openBlock(), createElementBlock("div",
|
|
4978
|
-
_cache[
|
|
4979
|
-
createElementVNode("span",
|
|
5122
|
+
])) : (openBlock(), createElementBlock("div", _hoisted_53, [inlineError.value ? (openBlock(), createElementBlock("div", _hoisted_54, [
|
|
5123
|
+
_cache[49] || (_cache[49] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)),
|
|
5124
|
+
createElementVNode("span", _hoisted_55, toDisplayString(unref(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
|
|
4980
5125
|
createElementVNode("button", {
|
|
4981
5126
|
type: "button",
|
|
4982
5127
|
class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
|
|
4983
5128
|
"aria-label": unref(t)("common.close"),
|
|
4984
|
-
onClick: _cache[
|
|
4985
|
-
}, [..._cache[
|
|
4986
|
-
])) : createCommentVNode("", true), createElementVNode("table",
|
|
5129
|
+
onClick: _cache[14] || (_cache[14] = ($event) => inlineError.value = null)
|
|
5130
|
+
}, [..._cache[48] || (_cache[48] = [createElementVNode("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_56)
|
|
5131
|
+
])) : createCommentVNode("", true), createElementVNode("table", _hoisted_57, [createElementVNode("thead", null, [createElementVNode("tr", _hoisted_58, [(openBlock(true), createElementBlock(Fragment, null, renderList(listColumnFields.value, ([key, field]) => {
|
|
4987
5132
|
return openBlock(), createElementBlock("th", {
|
|
4988
5133
|
key,
|
|
4989
5134
|
"aria-sort": unref(isSortableField)(field) ? sortAriaValue(key) : void 0,
|
|
4990
5135
|
class: "px-5 py-3 font-bold text-slate-500 text-left uppercase tracking-wider whitespace-nowrap"
|
|
4991
|
-
}, [createElementVNode("div",
|
|
5136
|
+
}, [createElementVNode("div", _hoisted_60, [createElementVNode("span", {
|
|
4992
5137
|
class: "truncate max-w-[14rem]",
|
|
4993
5138
|
title: field.label
|
|
4994
|
-
}, toDisplayString(field.label), 9,
|
|
5139
|
+
}, toDisplayString(field.label), 9, _hoisted_61), unref(isSortableField)(field) ? (openBlock(), createElementBlock("button", {
|
|
4995
5140
|
key: 0,
|
|
4996
5141
|
type: "button",
|
|
4997
5142
|
class: normalizeClass(["inline-flex items-center justify-center rounded p-0.5 -my-1 leading-none transition-colors", sortButtonClass(key)]),
|
|
@@ -4999,9 +5144,9 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
4999
5144
|
"aria-label": unref(t)("collectionsView.sortBy", { field: field.label }),
|
|
5000
5145
|
onClick: withModifiers(($event) => cycleSort(key), ["stop"]),
|
|
5001
5146
|
onPointerenter: ($event) => hoveredSortKey.value = key,
|
|
5002
|
-
onPointerleave: _cache[
|
|
5003
|
-
}, [createElementVNode("span",
|
|
5004
|
-
}), 128))])]), createElementVNode("tbody",
|
|
5147
|
+
onPointerleave: _cache[15] || (_cache[15] = ($event) => hoveredSortKey.value = null)
|
|
5148
|
+
}, [createElementVNode("span", _hoisted_63, toDisplayString(sortIconName(key)), 1)], 42, _hoisted_62)) : createCommentVNode("", true)])], 8, _hoisted_59);
|
|
5149
|
+
}), 128))])]), createElementVNode("tbody", _hoisted_64, [(openBlock(true), createElementBlock(Fragment, null, renderList(sortedItems.value, (item) => {
|
|
5005
5150
|
return openBlock(), createElementBlock("tr", {
|
|
5006
5151
|
key: String(item[collection.value.schema.primaryKey] ?? ""),
|
|
5007
5152
|
class: 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" : ""]),
|
|
@@ -5023,9 +5168,9 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
5023
5168
|
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",
|
|
5024
5169
|
"data-testid": `collections-inline-toggle-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5025
5170
|
"aria-label": field.label,
|
|
5026
|
-
onClick: _cache[
|
|
5171
|
+
onClick: _cache[16] || (_cache[16] = withModifiers(() => {}, ["stop"])),
|
|
5027
5172
|
onChange: ($event) => commitToggle(item, field)
|
|
5028
|
-
}, null, 40,
|
|
5173
|
+
}, null, 40, _hoisted_66)) : field.type === "boolean" ? (openBlock(), createElementBlock("input", {
|
|
5029
5174
|
key: 1,
|
|
5030
5175
|
type: "checkbox",
|
|
5031
5176
|
checked: item[key] === true,
|
|
@@ -5033,9 +5178,9 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
5033
5178
|
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",
|
|
5034
5179
|
"data-testid": `collections-inline-bool-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5035
5180
|
"aria-label": field.label,
|
|
5036
|
-
onClick: _cache[
|
|
5181
|
+
onClick: _cache[17] || (_cache[17] = withModifiers(() => {}, ["stop"])),
|
|
5037
5182
|
onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.checked)
|
|
5038
|
-
}, null, 40,
|
|
5183
|
+
}, null, 40, _hoisted_67)) : field.type === "ref" && field.to && typeof item[key] === "string" && item[key] ? (openBlock(), createElementBlock("span", _hoisted_68, [createElementVNode("a", {
|
|
5039
5184
|
href: unref(cui).recordHref?.(field.to, String(item[key])),
|
|
5040
5185
|
tabindex: unref(cui).recordHref?.(field.to, String(item[key])) ? void 0 : 0,
|
|
5041
5186
|
role: "link",
|
|
@@ -5043,44 +5188,44 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
5043
5188
|
"data-testid": `collections-ref-link-${key}-${item[key]}`,
|
|
5044
5189
|
onClick: ($event) => unref(activateRefLink)($event, field.to, String(item[key]), true),
|
|
5045
5190
|
onKeydown: [withKeys(($event) => unref(activateRefLink)($event, field.to, String(item[key]), true), ["enter"]), withKeys(($event) => unref(activateRefLink)($event, field.to, String(item[key]), true), ["space"])]
|
|
5046
|
-
}, toDisplayString(unref(refDisplay)(field.to, String(item[key]))), 41,
|
|
5191
|
+
}, toDisplayString(unref(refDisplay)(field.to, String(item[key]))), 41, _hoisted_69)])) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? (openBlock(), createElementBlock("select", {
|
|
5047
5192
|
key: 3,
|
|
5048
5193
|
value: item[key] == null ? "" : String(item[key]),
|
|
5049
5194
|
disabled: isRowInlineSaving(item),
|
|
5050
5195
|
class: 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])]),
|
|
5051
5196
|
"data-testid": `collections-inline-enum-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5052
5197
|
"aria-label": field.label,
|
|
5053
|
-
onClick: _cache[
|
|
5198
|
+
onClick: _cache[18] || (_cache[18] = withModifiers(() => {}, ["stop"])),
|
|
5054
5199
|
onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.value)
|
|
5055
|
-
}, [showEnumPlaceholder(item, String(key)) ? (openBlock(), createElementBlock("option",
|
|
5200
|
+
}, [showEnumPlaceholder(item, String(key)) ? (openBlock(), createElementBlock("option", _hoisted_71, toDisplayString(unref(t)("collectionsView.selectPlaceholder")), 1)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(field.values, (value) => {
|
|
5056
5201
|
return openBlock(), createElementBlock("option", {
|
|
5057
5202
|
key: value,
|
|
5058
5203
|
value
|
|
5059
|
-
}, toDisplayString(value), 9,
|
|
5060
|
-
}), 128))], 42,
|
|
5204
|
+
}, toDisplayString(value), 9, _hoisted_72);
|
|
5205
|
+
}), 128))], 42, _hoisted_70)) : field.type === "money" ? (openBlock(), createElementBlock("span", _hoisted_73, toDisplayString(unref(formatMoney)(item[key], unref(resolveCurrency)(field, item), unref(locale))), 1)) : field.type === "table" ? (openBlock(), createElementBlock("span", _hoisted_74, [_cache[50] || (_cache[50] = createElementVNode("span", { class: "material-icons text-[11px]" }, "list", -1)), createElementVNode("span", null, toDisplayString(tableSummary(item[key])), 1)])) : field.type === "derived" ? (openBlock(), createElementBlock("span", _hoisted_75, toDisplayString(unref(derivedDisplay)(field, unref(evaluateDerivedAgainstItem)(field, String(key), item), item)), 1)) : field.type !== "file" && unref(isExternalUrl)(item[key]) ? (openBlock(), createElementBlock("a", {
|
|
5061
5206
|
key: 7,
|
|
5062
5207
|
href: String(item[key]),
|
|
5063
5208
|
target: "_blank",
|
|
5064
5209
|
rel: "noopener noreferrer",
|
|
5065
5210
|
class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
|
|
5066
5211
|
"data-testid": `collections-url-link-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5067
|
-
onClick: _cache[
|
|
5068
|
-
}, toDisplayString(String(item[key])), 9,
|
|
5212
|
+
onClick: _cache[19] || (_cache[19] = withModifiers(() => {}, ["stop"]))
|
|
5213
|
+
}, toDisplayString(String(item[key])), 9, _hoisted_76)) : field.type === "file" && unref(artifactUrl)(item[key]) ? (openBlock(), createElementBlock("a", {
|
|
5069
5214
|
key: 8,
|
|
5070
5215
|
href: unref(artifactUrl)(item[key]) ?? void 0,
|
|
5071
5216
|
target: "_blank",
|
|
5072
5217
|
rel: "noopener noreferrer",
|
|
5073
5218
|
class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
|
|
5074
5219
|
"data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5075
|
-
onClick: _cache[
|
|
5076
|
-
}, toDisplayString(String(item[key])), 9,
|
|
5220
|
+
onClick: _cache[20] || (_cache[20] = withModifiers(() => {}, ["stop"]))
|
|
5221
|
+
}, toDisplayString(String(item[key])), 9, _hoisted_77)) : field.type === "file" && unref(fileRoutePath)(item[key]) ? (openBlock(), createElementBlock("a", {
|
|
5077
5222
|
key: 9,
|
|
5078
5223
|
href: unref(fileRoutePath)(item[key]) ?? void 0,
|
|
5079
5224
|
class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
|
|
5080
5225
|
"data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
|
|
5081
5226
|
onClick: ($event) => unref(activatePathLink)($event, unref(fileRoutePath)(item[key]) ?? "", true)
|
|
5082
|
-
}, toDisplayString(String(item[key])), 9,
|
|
5083
|
-
}), 128))], 42,
|
|
5227
|
+
}, toDisplayString(String(item[key])), 9, _hoisted_78)) : (openBlock(), createElementBlock("span", _hoisted_79, toDisplayString(unref(formatCell)(item[key], field.type)), 1))], 64)) : createCommentVNode("", true)]);
|
|
5228
|
+
}), 128))], 42, _hoisted_65);
|
|
5084
5229
|
}), 128))])])]))]),
|
|
5085
5230
|
collection.value && (viewing.value || editing.value) && !(calendarActive.value && openDay.value) ? (openBlock(), createBlock(CollectionRecordModal_default, {
|
|
5086
5231
|
key: 4,
|
|
@@ -5088,7 +5233,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
5088
5233
|
}, {
|
|
5089
5234
|
default: withCtx(() => [createVNode(CollectionRecordPanel_default, {
|
|
5090
5235
|
editing: editing.value,
|
|
5091
|
-
"onUpdate:editing": _cache[
|
|
5236
|
+
"onUpdate:editing": _cache[21] || (_cache[21] = ($event) => editing.value = $event),
|
|
5092
5237
|
collection: collection.value,
|
|
5093
5238
|
viewing: viewing.value,
|
|
5094
5239
|
saving: saving.value,
|
|
@@ -5106,7 +5251,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
5106
5251
|
onCancel: cancelEditor,
|
|
5107
5252
|
onEdit: editFromView,
|
|
5108
5253
|
onClose: closeView,
|
|
5109
|
-
onDelete: _cache[
|
|
5254
|
+
onDelete: _cache[22] || (_cache[22] = ($event) => viewing.value && confirmDelete(viewing.value)),
|
|
5110
5255
|
onRunAction: runAction,
|
|
5111
5256
|
onItemChat
|
|
5112
5257
|
}, null, 8, [
|
|
@@ -5133,7 +5278,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
5133
5278
|
title: collection.value.title,
|
|
5134
5279
|
views: customViews.value,
|
|
5135
5280
|
onChanged: onViewsChanged,
|
|
5136
|
-
onClose: _cache[
|
|
5281
|
+
onClose: _cache[23] || (_cache[23] = ($event) => configOpen.value = false)
|
|
5137
5282
|
}, null, 8, [
|
|
5138
5283
|
"slug",
|
|
5139
5284
|
"title",
|
|
@@ -5148,29 +5293,29 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
5148
5293
|
"data-testid": "collections-chat-modal",
|
|
5149
5294
|
onClick: withModifiers(closeChat, ["self"]),
|
|
5150
5295
|
onKeydown: withKeys(closeChat, ["esc"])
|
|
5151
|
-
}, [createElementVNode("div",
|
|
5152
|
-
createElementVNode("header",
|
|
5153
|
-
_cache[
|
|
5154
|
-
createElementVNode("div",
|
|
5296
|
+
}, [createElementVNode("div", _hoisted_80, [
|
|
5297
|
+
createElementVNode("header", _hoisted_81, [
|
|
5298
|
+
_cache[52] || (_cache[52] = 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" }, [createElementVNode("span", { class: "material-icons text-lg" }, "forum")], -1)),
|
|
5299
|
+
createElementVNode("div", _hoisted_82, [createElementVNode("h2", _hoisted_83, toDisplayString(unref(t)("collectionsView.chatTitle")), 1), createElementVNode("span", _hoisted_84, toDisplayString(collection.value.title), 1)]),
|
|
5155
5300
|
createElementVNode("button", {
|
|
5156
5301
|
type: "button",
|
|
5157
5302
|
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",
|
|
5158
5303
|
"aria-label": unref(t)("common.close"),
|
|
5159
5304
|
"data-testid": "collections-chat-close",
|
|
5160
5305
|
onClick: closeChat
|
|
5161
|
-
}, [..._cache[
|
|
5306
|
+
}, [..._cache[51] || (_cache[51] = [createElementVNode("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_85)
|
|
5162
5307
|
]),
|
|
5163
|
-
createElementVNode("div",
|
|
5308
|
+
createElementVNode("div", _hoisted_86, [withDirectives(createElementVNode("textarea", {
|
|
5164
5309
|
ref_key: "chatInputEl",
|
|
5165
5310
|
ref: chatInputEl,
|
|
5166
|
-
"onUpdate:modelValue": _cache[
|
|
5311
|
+
"onUpdate:modelValue": _cache[24] || (_cache[24] = ($event) => chatMessage.value = $event),
|
|
5167
5312
|
rows: "4",
|
|
5168
5313
|
placeholder: unref(t)("collectionsView.chatPlaceholder"),
|
|
5169
5314
|
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",
|
|
5170
5315
|
"data-testid": "collections-chat-input",
|
|
5171
5316
|
onKeydown: [withKeys(withModifiers(submitChat, ["meta"]), ["enter"]), withKeys(withModifiers(submitChat, ["ctrl"]), ["enter"])]
|
|
5172
|
-
}, null, 40,
|
|
5173
|
-
createElementVNode("footer",
|
|
5317
|
+
}, null, 40, _hoisted_87), [[vModelText, chatMessage.value]])]),
|
|
5318
|
+
createElementVNode("footer", _hoisted_88, [createElementVNode("button", {
|
|
5174
5319
|
type: "button",
|
|
5175
5320
|
class: "h-8 px-2.5 rounded text-xs font-bold text-slate-500 hover:bg-slate-200/50 transition-colors",
|
|
5176
5321
|
"data-testid": "collections-chat-cancel",
|
|
@@ -5181,7 +5326,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
|
|
|
5181
5326
|
disabled: !chatMessage.value.trim(),
|
|
5182
5327
|
"data-testid": "collections-chat-send",
|
|
5183
5328
|
onClick: submitChat
|
|
5184
|
-
}, toDisplayString(unref(t)("collectionsView.chatStart")), 9,
|
|
5329
|
+
}, toDisplayString(unref(t)("collectionsView.chatStart")), 9, _hoisted_89)])
|
|
5185
5330
|
])], 32)) : createCommentVNode("", true)
|
|
5186
5331
|
]);
|
|
5187
5332
|
};
|