diff --git a/apps/builder/__tests__/category-resource-list-selection.test.tsx b/apps/builder/__tests__/category-resource-list-selection.test.tsx new file mode 100644 index 000000000..3ad24fbf6 --- /dev/null +++ b/apps/builder/__tests__/category-resource-list-selection.test.tsx @@ -0,0 +1,178 @@ +// @vitest-environment jsdom + +import { act } from "react" +import { createRoot, type Root } from "react-dom/client" +import { afterEach, describe, expect, test, vi } from "vitest" +import { CategoryResourceList } from "@/features/templates/components/category-resource-list" + +vi.mock("next-intl", () => ({ + useTranslations: () => (key: string) => key, +})) + +// jsdom has no PointerEvent constructor; Base UI's Checkbox reads pointer +// event fields (`pointerType`) inside its own click handler, so a plain +// MouseEvent throws there. Polyfill with MouseEvent's fields, matching the +// common jsdom + Base UI/Radix test workaround. +class PointerEventPolyfill extends MouseEvent { + pointerType: string + constructor(type: string, params: PointerEventInit = {}) { + super(type, params) + this.pointerType = params.pointerType ?? "mouse" + } +} +;( + window as unknown as { PointerEvent: typeof PointerEventPolyfill } +).PointerEvent ??= PointerEventPolyfill + +const listSelectableResourcesAPI = vi.fn() + +vi.mock("@/lib/orpc/orpc", () => ({ + client: { + templatesAPI: { + listSelectableResourcesAPI: ( + ...args: Parameters + ) => listSelectableResourcesAPI(...args), + }, + }, +})) + +let container: HTMLDivElement | null = null +let root: Root | null = null + +async function renderComponent(ui: React.ReactElement) { + Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true }) + container = document.createElement("div") + document.body.appendChild(container) + root = createRoot(container) + await act(() => { + root?.render(ui) + }) + return container +} + +afterEach(() => { + if (root) { + act(() => { + root?.unmount() + }) + } + container?.remove() + container = null + root = null + vi.clearAllMocks() + vi.useRealTimers() +}) + +const FULL_SET = ["id-1", "id-2", "id-3"] + +const clickCheckbox = async (node: Element) => { + await act(() => { + node.dispatchEvent(new window.PointerEvent("click", { bubbles: true })) + }) +} + +describe("CategoryResourceList selection", () => { + test("select-all after a search still selects the full unfiltered set, not just the matches", async () => { + vi.useFakeTimers() + listSelectableResourcesAPI.mockImplementation( + (input: { keyword?: string }) => { + if (input.keyword) { + // A search only ever returns the matching subset — must never + // become the new `allIds`. + return { + items: [{ id: "id-2", name: "Matching Flow" }], + nextCursor: null, + total: 1, + allIds: undefined, + } + } + return { + items: FULL_SET.map((id) => ({ id, name: id })), + nextCursor: null, + total: FULL_SET.length, + allIds: FULL_SET, + } + }, + ) + + const onChange = vi.fn() + const el = await renderComponent( + , + ) + + // Initial unfiltered load captured allIds = FULL_SET. + expect(listSelectableResourcesAPI).toHaveBeenCalledWith( + expect.objectContaining({ keyword: undefined, cursor: undefined }), + ) + + const searchInput = el.querySelector( + "input[placeholder]", + ) as HTMLInputElement + + const nativeInputValueSetter = Object.getOwnPropertyDescriptor( + window.HTMLInputElement.prototype, + "value", + )?.set + await act(() => { + nativeInputValueSetter?.call(searchInput, "Matching") + searchInput.dispatchEvent(new Event("input", { bubbles: true })) + }) + + // Drive the real debounce timer so `fetchPage(null, "Matching")` runs + // through the component's own effect, not a re-invoked mock. + await act(() => { + vi.advanceTimersByTime(500) + }) + await act(async () => { + await Promise.resolve() + }) + + const selectAllCheckbox = el.querySelector( + '[role="checkbox"][aria-labelledby="flows-select-all-label"]', + ) as HTMLElement + await clickCheckbox(selectAllCheckbox) + + // Because `allIds` was captured from the unfiltered fetch and a search + // must never overwrite it, "select all" resolves to the full set, not + // just the single search match. + expect(onChange).toHaveBeenCalledWith({ mode: "ids", ids: FULL_SET }) + }) + + test("unchecking a row while mode:all downgrades to the exact allIds set, not just the loaded page", async () => { + listSelectableResourcesAPI.mockResolvedValue({ + items: FULL_SET.map((id) => ({ id, name: id })), + nextCursor: null, + total: FULL_SET.length, + allIds: FULL_SET, + }) + + const onChange = vi.fn() + const el = await renderComponent( + , + ) + + const allCheckboxes = Array.from(el.querySelectorAll('[role="checkbox"]')) + const rowCheckboxes = allCheckboxes.filter( + (node) => + node.getAttribute("aria-labelledby") !== "flows-select-all-label", + ) + expect(rowCheckboxes.length).toBe(FULL_SET.length) + + await clickCheckbox(rowCheckboxes[1]) + + expect(onChange).toHaveBeenCalledWith({ + mode: "ids", + ids: FULL_SET.filter((id) => id !== "id-2"), + }) + }) +}) diff --git a/apps/builder/messages/ar.json b/apps/builder/messages/ar.json index a4b24d0d1..7ff072026 100644 --- a/apps/builder/messages/ar.json +++ b/apps/builder/messages/ar.json @@ -145,7 +145,8 @@ "onSuccess": "عند النجاح", "clone": "استنساخ", "remove": "إزالة", - "restore": "استعادة" + "restore": "استعادة", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "يجب أن تكون مسؤولًا فائقًا لمساحة العمل للقيام بذلك." @@ -1220,6 +1221,9 @@ "mcpServer": { "label": "خادم MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "وظيفة النظام", "names": { @@ -1418,9 +1422,11 @@ "label": "الحالة", "pending": "قيد الانتظار", "processing": "قيد المعالجة", + "installing": "Installing", "success": "نجاح", "error": "خطأ", "completed": "مكتمل", + "partial": "Partial", "failed": "فاشل" }, "subtitle": { @@ -3986,7 +3992,59 @@ }, "templates": { "title": "القوالب", - "description": "إنشاء قوالبك وإدارتها." + "description": "أنشئ قوالبك وأدرها.", + "share": { + "label": "مشاركة", + "enabled": "تمت المشاركة", + "disabled": "غير مُشارَك", + "dialogTitle": "مشاركة القالب", + "dialogDescription": "يمكن لأي شخص لديه هذا الرابط تثبيت نسخة من هذا القالب في مساحة عمل ضمن مؤسستك.", + "enableLabel": "تفعيل رابط المشاركة", + "linkLabel": "رابط المشاركة" + }, + "installs": { + "title": "عمليات التثبيت", + "resources": "الموارد", + "warnings": "التحذيرات", + "completedAt": "اكتمل في", + "autoUpdate": "التحديث التلقائي" + }, + "form": { + "contents": "محتوى القالب", + "comingSoon": "قريبًا", + "selectAll": "تحديد الكل", + "selectedCount": "تم تحديد {count}", + "details": "التفاصيل", + "permissions": "الأذونات الافتراضية", + "allowEdit": "السماح بالتعديل بعد التثبيت", + "allowDelete": "السماح بالحذف بعد التثبيت", + "publisherName": "اسم الناشر", + "youtubeVideoId": "معرّف فيديو يوتيوب", + "youtubeVideoIdDescription": "المعرّف المكوَّن من 11 حرفًا من رابط الفيديو، مثل \"dQw4w9WgXcQ\" في youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "رابط الاختبار", + "imageUrl": "صورة الغلاف", + "installBehavior": "سلوك التثبيت", + "createInstallFolder": "التثبيت في مجلد مخصص", + "createInstallFolderDescription": "يضع كل مورد يتم تثبيته داخل مجلد جديد واحد يحمل اسم هذا القالب.", + "defaultAutoUpdate": "تمييز عمليات التثبيت للتحديث بشكل افتراضي", + "defaultAutoUpdateDescription": "ستعرض عمليات التثبيت الجديدة إشعارًا بتوفر تحديث في كل مرة تعيد فيها نشر هذا القالب." + }, + "categories": { + "flows": "المسارات", + "customFields": "الحقول المخصصة", + "tags": "الوسوم", + "products": "المنتجات", + "productCategories": "فئات المنتجات", + "aiFunctions": "وظائف الذكاء الاصطناعي", + "aiAgents": "وكلاء الذكاء الاصطناعي", + "calendars": "تقويمات المواعيد", + "webchats": "دردشات الويب", + "keywords": "الكلمات المفتاحية", + "entryPointLinks": "روابط نقاط الدخول", + "triggers": "المشغِّلات", + "fbCommentAutomations": "أتمتة تعليقات فيسبوك", + "settings": "الإعدادات" + } }, "qrCodeGenerator": { "title": "مولّد رمز QR", @@ -4102,6 +4160,18 @@ "unavailableTitle": "إلغاء الاشتراك غير متاح.", "unavailableDescription": "مساحة العمل هذه لم تعد متاحة." }, + "templatesPublicPage": { + "invalidTitle": "هذا الرابط لم يعد متاحًا.", + "invalidDescription": "رابط مشاركة هذا القالب غير صالح أو معطَّل أو منتهي الصلاحية.", + "byPublisher": "بواسطة {publisherName}", + "includes": "يتضمن هذا القالب", + "tryItOut": "جرّبه", + "signInToInstall": "سجّل الدخول للتثبيت", + "selectWorkspace": "اختر مساحة عمل", + "install": "تثبيت", + "installStarted": "بدأ التثبيت", + "noEligibleWorkspaces": "ليس لديك مساحة عمل مؤهلة لتثبيت هذا القالب." + }, "extensionsMe": { "actions": { "delete": "حذف بياناتي", diff --git a/apps/builder/messages/da.json b/apps/builder/messages/da.json index 49638c773..5eb89ae9f 100644 --- a/apps/builder/messages/da.json +++ b/apps/builder/messages/da.json @@ -145,7 +145,8 @@ "onSuccess": "På succes", "clone": "Klon", "remove": "Fjern", - "restore": "Gendan" + "restore": "Gendan", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Du skal være super admin for arbejdsområdet for at gøre dette." @@ -1146,6 +1147,9 @@ "mcpServer": { "label": "MCP Server" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "System Function", "names": { @@ -1344,7 +1348,9 @@ "label": "Status", "pending": "Pending", "processing": "Processing", + "installing": "Installing", "completed": "Completed", + "partial": "Partial", "failed": "Failed", "success": "Succes", "error": "Fejl" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Skabeloner", - "description": "Opret og administrer din skabeloner." + "description": "Opret og administrer dine skabeloner.", + "share": { + "label": "Del", + "enabled": "Delt", + "disabled": "Ikke delt", + "dialogTitle": "Del skabelon", + "dialogDescription": "Alle med dette link kan installere en kopi af denne skabelon i et arbejdsområde i din tenant.", + "enableLabel": "Aktivér dellink", + "linkLabel": "Dellink" + }, + "installs": { + "title": "Installationer", + "resources": "Ressourcer", + "warnings": "Advarsler", + "completedAt": "Afsluttet", + "autoUpdate": "Automatisk opdatering" + }, + "form": { + "contents": "Skabelonindhold", + "comingSoon": "Kommer snart", + "selectAll": "Vælg alle", + "selectedCount": "{count} valgt", + "details": "Detaljer", + "permissions": "Standardtilladelser", + "allowEdit": "Tillad redigering efter installation", + "allowDelete": "Tillad sletning efter installation", + "publisherName": "Udgivernavn", + "youtubeVideoId": "YouTube-video-id", + "youtubeVideoIdDescription": "Det 11-tegns id fra videoens URL, f.eks. \"dQw4w9WgXcQ\" i youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Testlink", + "imageUrl": "Forsidebillede", + "installBehavior": "Installationsadfærd", + "createInstallFolder": "Installer i en dedikeret mappe", + "createInstallFolderDescription": "Placer alle installerede ressourcer i én ny mappe opkaldt efter denne skabelon.", + "defaultAutoUpdate": "Markér installationer til opdatering som standard", + "defaultAutoUpdateDescription": "Nye installationer viser en besked om tilgængelig opdatering, hver gang du genudgiver denne skabelon." + }, + "categories": { + "flows": "Flows", + "customFields": "Brugerdefinerede felter", + "tags": "Tags", + "products": "Produkter", + "productCategories": "Produktkategorier", + "aiFunctions": "AI-funktioner", + "aiAgents": "AI-agenter", + "calendars": "Aftalekalendere", + "webchats": "Webchats", + "keywords": "Nøgleord", + "entryPointLinks": "Entry Point Links", + "triggers": "Triggers", + "fbCommentAutomations": "Facebook-kommentarautomatiseringer", + "settings": "Indstillinger" + } }, "qrCodeGenerator": { "title": "QR Kode Generator", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Afmeld unavailable.", "unavailableDescription": "Denne arbejdsområde er ingen longer tilgængelig." }, + "templatesPublicPage": { + "invalidTitle": "Dette link er ikke længere tilgængeligt.", + "invalidDescription": "Dette skabelon-dellink er ugyldigt, deaktiveret eller udløbet.", + "byPublisher": "Af {publisherName}", + "includes": "Denne skabelon indeholder", + "tryItOut": "Prøv den", + "signInToInstall": "Log ind for at installere", + "selectWorkspace": "Vælg et arbejdsområde", + "install": "Installer", + "installStarted": "Installation startet", + "noEligibleWorkspaces": "Du har ikke et arbejdsområde, der er egnet til at installere denne skabelon." + }, "extensionsMe": { "actions": { "delete": "Slet My Data", diff --git a/apps/builder/messages/de.json b/apps/builder/messages/de.json index b559606eb..011ee6fd7 100644 --- a/apps/builder/messages/de.json +++ b/apps/builder/messages/de.json @@ -145,7 +145,8 @@ "onSuccess": "Bei Erfolg", "clone": "Klonen", "remove": "Entfernen", - "restore": "Wiederherstellen" + "restore": "Wiederherstellen", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Sie müssen Super-Administrator des Arbeitsbereichs sein, um dies zu tun." @@ -1146,6 +1147,9 @@ "mcpServer": { "label": "MCP-Server" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Systemfunktion", "names": { @@ -1344,7 +1348,9 @@ "label": "Status", "pending": "Ausstehend", "processing": "In Verarbeitung", + "installing": "Installing", "completed": "Abgeschlossen", + "partial": "Partial", "failed": "Fehlgeschlagen", "success": "Erfolg", "error": "Fehler" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Vorlagen", - "description": "Erstellen und verwalten Sie Ihre Vorlagen." + "description": "Erstellen und verwalten Sie Ihre Vorlagen.", + "share": { + "label": "Teilen", + "enabled": "Geteilt", + "disabled": "Nicht geteilt", + "dialogTitle": "Vorlage teilen", + "dialogDescription": "Jeder mit diesem Link kann eine Kopie dieser Vorlage in einem Arbeitsbereich Ihres Mandanten installieren.", + "enableLabel": "Freigabelink aktivieren", + "linkLabel": "Freigabelink" + }, + "installs": { + "title": "Installationen", + "resources": "Ressourcen", + "warnings": "Warnungen", + "completedAt": "Abgeschlossen am", + "autoUpdate": "Automatische Aktualisierung" + }, + "form": { + "contents": "Vorlageninhalt", + "comingSoon": "Demnächst verfügbar", + "selectAll": "Alle auswählen", + "selectedCount": "{count} ausgewählt", + "details": "Details", + "permissions": "Standardberechtigungen", + "allowEdit": "Bearbeitung nach Installation erlauben", + "allowDelete": "Löschen nach Installation erlauben", + "publisherName": "Name des Herausgebers", + "youtubeVideoId": "YouTube-Video-ID", + "youtubeVideoIdDescription": "Die 11-stellige ID aus der Video-URL, z. B. \"dQw4w9WgXcQ\" in youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Testlink", + "imageUrl": "Titelbild", + "installBehavior": "Installationsverhalten", + "createInstallFolder": "In einem eigenen Ordner installieren", + "createInstallFolderDescription": "Verschachtelt jede installierte Ressource in einem neuen, nach dieser Vorlage benannten Ordner.", + "defaultAutoUpdate": "Installationen standardmäßig für Updates markieren", + "defaultAutoUpdateDescription": "Neue Installationen zeigen einen Update-Hinweis, sobald Sie diese Vorlage erneut veröffentlichen." + }, + "categories": { + "flows": "Flows", + "customFields": "Benutzerdefinierte Felder", + "tags": "Tags", + "products": "Produkte", + "productCategories": "Produktkategorien", + "aiFunctions": "KI-Funktionen", + "aiAgents": "KI-Agenten", + "calendars": "Terminkalender", + "webchats": "Webchats", + "keywords": "Schlüsselwörter", + "entryPointLinks": "Einstiegspunkt-Links", + "triggers": "Trigger", + "fbCommentAutomations": "Facebook-Kommentarautomatisierungen", + "settings": "Einstellungen" + } }, "qrCodeGenerator": { "title": "QR-Code-Generator", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Abmeldung nicht verfügbar.", "unavailableDescription": "Dieser Arbeitsbereich ist nicht mehr verfügbar." }, + "templatesPublicPage": { + "invalidTitle": "Dieser Link ist nicht mehr verfügbar.", + "invalidDescription": "Dieser Vorlagen-Freigabelink ist ungültig, deaktiviert oder abgelaufen.", + "byPublisher": "Von {publisherName}", + "includes": "Diese Vorlage enthält", + "tryItOut": "Ausprobieren", + "signInToInstall": "Zum Installieren anmelden", + "selectWorkspace": "Arbeitsbereich auswählen", + "install": "Installieren", + "installStarted": "Installation gestartet", + "noEligibleWorkspaces": "Sie haben keinen Arbeitsbereich, der für die Installation dieser Vorlage geeignet ist." + }, "extensionsMe": { "actions": { "delete": "Meine Daten löschen", diff --git a/apps/builder/messages/en.json b/apps/builder/messages/en.json index 637f4f244..742d8fcbe 100644 --- a/apps/builder/messages/en.json +++ b/apps/builder/messages/en.json @@ -145,7 +145,8 @@ "onSuccess": "On success", "clone": "Clone", "remove": "Remove", - "restore": "Restore" + "restore": "Restore", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "You need to be a workspace super admin to do this." @@ -1226,6 +1227,9 @@ "mcpServer": { "label": "MCP Server" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "System Function", "names": { @@ -1424,7 +1428,9 @@ "label": "Status", "pending": "Pending", "processing": "Processing", + "installing": "Installing", "completed": "Completed", + "partial": "Partial", "failed": "Failed", "success": "Success", "error": "Error" @@ -4424,7 +4430,59 @@ }, "templates": { "title": "Templates", - "description": "Create and manage your templates." + "description": "Create and manage your templates.", + "share": { + "label": "Share", + "enabled": "Shared", + "disabled": "Not shared", + "dialogTitle": "Share template", + "dialogDescription": "Anyone with this link can install a copy of this template into a workspace in your tenant.", + "enableLabel": "Enable share link", + "linkLabel": "Share link" + }, + "installs": { + "title": "Installs", + "resources": "Resources", + "warnings": "Warnings", + "completedAt": "Completed at", + "autoUpdate": "Auto-update" + }, + "form": { + "contents": "Template contents", + "comingSoon": "Coming soon", + "selectAll": "Select all", + "selectedCount": "{count} selected", + "details": "Details", + "permissions": "Default permissions", + "allowEdit": "Allow editing after install", + "allowDelete": "Allow deleting after install", + "publisherName": "Publisher name", + "youtubeVideoId": "YouTube video ID", + "youtubeVideoIdDescription": "The 11-character ID from the video's URL, e.g. the \"dQw4w9WgXcQ\" in youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Test link", + "imageUrl": "Cover image", + "installBehavior": "Install behavior", + "createInstallFolder": "Install into a dedicated folder", + "createInstallFolderDescription": "Nest every installed resource under one new folder named after this template.", + "defaultAutoUpdate": "Flag installs for updates by default", + "defaultAutoUpdateDescription": "New installs will show an update available notice whenever you republish this template." + }, + "categories": { + "flows": "Flows", + "customFields": "Custom fields", + "tags": "Tags", + "products": "Products", + "productCategories": "Product categories", + "aiFunctions": "AI functions", + "aiAgents": "AI agents", + "calendars": "Calendars", + "webchats": "Webchats", + "keywords": "Keywords", + "entryPointLinks": "Entry point links", + "triggers": "Triggers", + "fbCommentAutomations": "Facebook comment automations", + "settings": "Settings" + } }, "qrCodeGenerator": { "title": "QR Code Generator", @@ -4540,6 +4598,18 @@ "unavailableTitle": "Unsubscribe unavailable.", "unavailableDescription": "This workspace is no longer available." }, + "templatesPublicPage": { + "invalidTitle": "This link is no longer available.", + "invalidDescription": "This template share link is invalid, disabled, or has expired.", + "byPublisher": "By {publisherName}", + "includes": "This template includes", + "tryItOut": "Try it out", + "signInToInstall": "Sign in to install", + "selectWorkspace": "Select a workspace", + "install": "Install", + "installStarted": "Install started", + "noEligibleWorkspaces": "You don't have a workspace eligible to install this template." + }, "bookingPage": { "metadataTitle": "Booking", "comingSoonTitle": "Booking page coming soon", diff --git a/apps/builder/messages/es.json b/apps/builder/messages/es.json index 85ef11e6d..b0f464af2 100644 --- a/apps/builder/messages/es.json +++ b/apps/builder/messages/es.json @@ -145,7 +145,8 @@ "onSuccess": "Al completarse", "clone": "Clonar", "remove": "Quitar", - "restore": "Restaurar" + "restore": "Restaurar", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Debes ser superadministrador del espacio de trabajo para hacer esto." @@ -1220,6 +1221,9 @@ "mcpServer": { "label": "Servidor MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Función del sistema", "names": { @@ -1418,7 +1422,9 @@ "label": "Estado", "pending": "Pendiente", "processing": "Procesando", + "installing": "Installing", "completed": "Completado", + "partial": "Partial", "failed": "Fallido", "success": "Éxito", "error": "Error" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Plantillas", - "description": "Crear y Gestionar tu plantillas." + "description": "Crea y gestiona tus plantillas.", + "share": { + "label": "Compartir", + "enabled": "Compartida", + "disabled": "No compartida", + "dialogTitle": "Compartir plantilla", + "dialogDescription": "Cualquiera con este enlace puede instalar una copia de esta plantilla en un espacio de trabajo de tu organización.", + "enableLabel": "Activar enlace para compartir", + "linkLabel": "Enlace para compartir" + }, + "installs": { + "title": "Instalaciones", + "resources": "Recursos", + "warnings": "Advertencias", + "completedAt": "Completado el", + "autoUpdate": "Actualización automática" + }, + "form": { + "contents": "Contenido de la plantilla", + "comingSoon": "Próximamente", + "selectAll": "Seleccionar todo", + "selectedCount": "{count} seleccionados", + "details": "Detalles", + "permissions": "Permisos predeterminados", + "allowEdit": "Permitir edición después de instalar", + "allowDelete": "Permitir eliminación después de instalar", + "publisherName": "Nombre del publicador", + "youtubeVideoId": "ID del vídeo de YouTube", + "youtubeVideoIdDescription": "El ID de 11 caracteres de la URL del vídeo, por ejemplo \"dQw4w9WgXcQ\" en youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Enlace de prueba", + "imageUrl": "Imagen de portada", + "installBehavior": "Comportamiento de instalación", + "createInstallFolder": "Instalar en una carpeta dedicada", + "createInstallFolderDescription": "Anida todos los recursos instalados dentro de una nueva carpeta con el nombre de esta plantilla.", + "defaultAutoUpdate": "Marcar instalaciones para actualizaciones de forma predeterminada", + "defaultAutoUpdateDescription": "Las nuevas instalaciones mostrarán un aviso de actualización disponible cada vez que vuelvas a publicar esta plantilla." + }, + "categories": { + "flows": "Flujos", + "customFields": "Campos personalizados", + "tags": "Etiquetas", + "products": "Productos", + "productCategories": "Categorías de productos", + "aiFunctions": "Funciones de IA", + "aiAgents": "Agentes de IA", + "calendars": "Calendarios de citas", + "webchats": "Webchats", + "keywords": "Palabras clave", + "entryPointLinks": "Entry Point Enlaces", + "triggers": "Desencadenadores", + "fbCommentAutomations": "Automatizaciones de comentarios de Facebook", + "settings": "Configuración" + } }, "qrCodeGenerator": { "title": "Generador de códigos QR", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Unsubscribe undisponible.", "unavailableDescription": "This workspace es no longer disponible." }, + "templatesPublicPage": { + "invalidTitle": "Este enlace ya no está disponible.", + "invalidDescription": "Este enlace de plantilla no es válido, está deshabilitado o ha caducado.", + "byPublisher": "Por {publisherName}", + "includes": "Esta plantilla incluye", + "tryItOut": "Pruébala", + "signInToInstall": "Inicia sesión para instalar", + "selectWorkspace": "Selecciona un espacio de trabajo", + "install": "Instalar", + "installStarted": "Instalación iniciada", + "noEligibleWorkspaces": "No tienes ningún espacio de trabajo apto para instalar esta plantilla." + }, "extensionsMe": { "actions": { "delete": "Eliminar My Datos", diff --git a/apps/builder/messages/fi.json b/apps/builder/messages/fi.json index a02cd6336..93acadcb6 100644 --- a/apps/builder/messages/fi.json +++ b/apps/builder/messages/fi.json @@ -145,7 +145,8 @@ "onSuccess": "Onnistuessa", "clone": "Kloonaa", "remove": "Poista", - "restore": "Palauta" + "restore": "Palauta", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Sinun täytyy olla työtilan pääylläpitäjä tehdäksesi tämän." @@ -1146,6 +1147,9 @@ "mcpServer": { "label": "MCP-palvelin" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Järjestelmätoiminto", "names": { @@ -1344,7 +1348,9 @@ "label": "Tila", "pending": "Odottaa", "processing": "Käsitellään", + "installing": "Installing", "completed": "Valmis", + "partial": "Partial", "failed": "Epäonnistui", "success": "Onnistui", "error": "Virhe" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Mallit", - "description": "Luo ja hallitse mallejasi." + "description": "Luo ja hallitse mallejasi.", + "share": { + "label": "Jaa", + "enabled": "Jaettu", + "disabled": "Ei jaettu", + "dialogTitle": "Jaa malli", + "dialogDescription": "Kuka tahansa, jolla on tämä linkki, voi asentaa kopion tästä mallista organisaatiosi työtilaan.", + "enableLabel": "Ota jakolinkki käyttöön", + "linkLabel": "Jakolinkki" + }, + "installs": { + "title": "Asennukset", + "resources": "Resurssit", + "warnings": "Varoitukset", + "completedAt": "Valmistui", + "autoUpdate": "Automaattinen päivitys" + }, + "form": { + "contents": "Mallin sisältö", + "comingSoon": "Tulossa pian", + "selectAll": "Valitse kaikki", + "selectedCount": "{count} valittu", + "details": "Tiedot", + "permissions": "Oletusoikeudet", + "allowEdit": "Salli muokkaus asennuksen jälkeen", + "allowDelete": "Salli poistaminen asennuksen jälkeen", + "publisherName": "Julkaisijan nimi", + "youtubeVideoId": "YouTube-videon tunnus", + "youtubeVideoIdDescription": "11-merkkinen tunnus videon URL-osoitteesta, esim. \"dQw4w9WgXcQ\" osoitteessa youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Testilinkki", + "imageUrl": "Kansikuva", + "installBehavior": "Asennuksen toiminta", + "createInstallFolder": "Asenna omaan kansioon", + "createInstallFolderDescription": "Sijoittaa jokaisen asennetun resurssin uuteen, tämän mallin mukaan nimettyyn kansioon.", + "defaultAutoUpdate": "Merkitse asennukset oletuksena päivitettäviksi", + "defaultAutoUpdateDescription": "Uudet asennukset näyttävät päivitysilmoituksen aina, kun julkaiset tämän mallin uudelleen." + }, + "categories": { + "flows": "Flowt", + "customFields": "Mukautetut kentät", + "tags": "Tunnisteet", + "products": "Tuotteet", + "productCategories": "Tuotekategoriat", + "aiFunctions": "AI-toiminnot", + "aiAgents": "AI-agentit", + "calendars": "Ajanvarauskalenterit", + "webchats": "Verkkokeskustelut", + "keywords": "Avainsanat", + "entryPointLinks": "Aloituspisteiden linkit", + "triggers": "Käynnistimet", + "fbCommentAutomations": "Facebook-kommenttiautomaatiot", + "settings": "Asetukset" + } }, "qrCodeGenerator": { "title": "QR-koodigeneraattori", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Tilauksen peruuttaminen ei ole käytettävissä.", "unavailableDescription": "Tämä työtila ei ole enää käytettävissä." }, + "templatesPublicPage": { + "invalidTitle": "Tämä linkki ei ole enää käytettävissä.", + "invalidDescription": "Tämä mallin jakolinkki on virheellinen, poistettu käytöstä tai vanhentunut.", + "byPublisher": "Julkaisija: {publisherName}", + "includes": "Tämä malli sisältää", + "tryItOut": "Kokeile sitä", + "signInToInstall": "Kirjaudu sisään asentaaksesi", + "selectWorkspace": "Valitse työtila", + "install": "Asenna", + "installStarted": "Asennus aloitettu", + "noEligibleWorkspaces": "Sinulla ei ole työtilaa, johon tämän mallin voi asentaa." + }, "extensionsMe": { "actions": { "delete": "Poista tietoni", diff --git a/apps/builder/messages/fr.json b/apps/builder/messages/fr.json index 2428b2005..c0dfc3046 100644 --- a/apps/builder/messages/fr.json +++ b/apps/builder/messages/fr.json @@ -145,7 +145,8 @@ "onSuccess": "En cas de réussite", "clone": "Cloner", "remove": "Retirer", - "restore": "Restaurer" + "restore": "Restaurer", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Vous devez être super-administrateur de l’espace de travail pour effectuer cette action." @@ -1146,6 +1147,9 @@ "mcpServer": { "label": "Serveur MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Fonction système", "names": { @@ -1344,7 +1348,9 @@ "label": "État", "pending": "En attente", "processing": "Traitement en cours", + "installing": "Installing", "completed": "Terminé", + "partial": "Partial", "failed": "Échec", "success": "Réussite", "error": "Erreur" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Modèles", - "description": "Créez et gérez vos modèles." + "description": "Créez et gérez vos modèles.", + "share": { + "label": "Partager", + "enabled": "Partagé", + "disabled": "Non partagé", + "dialogTitle": "Partager le modèle", + "dialogDescription": "Toute personne disposant de ce lien peut installer une copie de ce modèle dans un espace de travail de votre organisation.", + "enableLabel": "Activer le lien de partage", + "linkLabel": "Lien de partage" + }, + "installs": { + "title": "Installations", + "resources": "Ressources", + "warnings": "Avertissements", + "completedAt": "Terminé le", + "autoUpdate": "Mise à jour automatique" + }, + "form": { + "contents": "Contenu du modèle", + "comingSoon": "Bientôt disponible", + "selectAll": "Tout sélectionner", + "selectedCount": "{count} sélectionné(s)", + "details": "Détails", + "permissions": "Autorisations par défaut", + "allowEdit": "Autoriser la modification après installation", + "allowDelete": "Autoriser la suppression après installation", + "publisherName": "Nom de l'éditeur", + "youtubeVideoId": "ID de la vidéo YouTube", + "youtubeVideoIdDescription": "L'identifiant de 11 caractères présent dans l'URL de la vidéo, par ex. \"dQw4w9WgXcQ\" dans youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Lien de test", + "imageUrl": "Image de couverture", + "installBehavior": "Comportement d'installation", + "createInstallFolder": "Installer dans un dossier dédié", + "createInstallFolderDescription": "Regroupe chaque ressource installée dans un nouveau dossier portant le nom de ce modèle.", + "defaultAutoUpdate": "Marquer les installations comme à mettre à jour par défaut", + "defaultAutoUpdateDescription": "Les nouvelles installations afficheront un avis de mise à jour disponible chaque fois que vous republierez ce modèle." + }, + "categories": { + "flows": "Flux", + "customFields": "Champs personnalisés", + "tags": "Étiquettes", + "products": "Produits", + "productCategories": "Catégories de produits", + "aiFunctions": "Fonctions IA", + "aiAgents": "Agents IA", + "calendars": "Calendriers de rendez-vous", + "webchats": "Webchats", + "keywords": "Mots-clés", + "entryPointLinks": "Liens de point d’entrée", + "triggers": "Déclencheurs", + "fbCommentAutomations": "Automatisations de commentaires Facebook", + "settings": "Paramètres" + } }, "qrCodeGenerator": { "title": "Générateur de codes QR", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Désabonnement indisponible.", "unavailableDescription": "Cet espace de travail n’est plus disponible." }, + "templatesPublicPage": { + "invalidTitle": "Ce lien n'est plus disponible.", + "invalidDescription": "Ce lien de partage de modèle est invalide, désactivé ou a expiré.", + "byPublisher": "Par {publisherName}", + "includes": "Ce modèle inclut", + "tryItOut": "Essayer", + "signInToInstall": "Se connecter pour installer", + "selectWorkspace": "Sélectionner un espace de travail", + "install": "Installer", + "installStarted": "Installation démarrée", + "noEligibleWorkspaces": "Vous n'avez aucun espace de travail éligible pour installer ce modèle." + }, "extensionsMe": { "actions": { "delete": "Supprimer mes données", diff --git a/apps/builder/messages/he.json b/apps/builder/messages/he.json index 5bef2bb0f..e7640df22 100644 --- a/apps/builder/messages/he.json +++ b/apps/builder/messages/he.json @@ -145,7 +145,8 @@ "onSuccess": "במקרה של הצלחה", "clone": "שכפול", "remove": "הסרה", - "restore": "שחזור" + "restore": "שחזור", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "עליכם להיות מנהל-על של סביבת העבודה כדי לבצע פעולה זו." @@ -2440,7 +2441,59 @@ }, "templates": { "title": "תבניות", - "description": "יצירה וניהול של התבניות שלכם." + "description": "צור ונהל את התבניות שלך.", + "share": { + "label": "שיתוף", + "enabled": "משותפת", + "disabled": "לא משותפת", + "dialogTitle": "שיתוף תבנית", + "dialogDescription": "כל מי שיש לו את הקישור הזה יכול להתקין עותק של התבנית הזו בסביבת עבודה בארגון שלך.", + "enableLabel": "הפעלת קישור שיתוף", + "linkLabel": "קישור שיתוף" + }, + "installs": { + "title": "התקנות", + "resources": "משאבים", + "warnings": "אזהרות", + "completedAt": "הושלם בתאריך", + "autoUpdate": "עדכון אוטומטי" + }, + "form": { + "contents": "תוכן התבנית", + "comingSoon": "בקרוב", + "selectAll": "בחירת הכול", + "selectedCount": "{count} נבחרו", + "details": "פרטים", + "permissions": "הרשאות ברירת מחדל", + "allowEdit": "אפשר עריכה לאחר ההתקנה", + "allowDelete": "אפשר מחיקה לאחר ההתקנה", + "publisherName": "שם המפרסם", + "youtubeVideoId": "מזהה סרטון YouTube", + "youtubeVideoIdDescription": "המזהה בן 11 התווים מכתובת ה-URL של הסרטון, למשל \"dQw4w9WgXcQ\" בכתובת youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "קישור בדיקה", + "imageUrl": "תמונת שער", + "installBehavior": "התנהגות ההתקנה", + "createInstallFolder": "התקנה בתיקייה ייעודית", + "createInstallFolderDescription": "מקנן כל משאב שהותקן בתוך תיקייה חדשה אחת הנקראת על שם התבנית הזו.", + "defaultAutoUpdate": "סימון התקנות לעדכון כברירת מחדל", + "defaultAutoUpdateDescription": "התקנות חדשות יציגו הודעת עדכון זמין בכל פעם שתפרסם מחדש את התבנית הזו." + }, + "categories": { + "flows": "תהליכים", + "customFields": "שדות מותאמים אישית", + "tags": "תגיות", + "products": "מוצרים", + "productCategories": "קטגוריות מוצרים", + "aiFunctions": "פונקציות AI", + "aiAgents": "סוכני AI", + "calendars": "יומני פגישות", + "webchats": "צ'אטים אינטרנטיים", + "keywords": "מילות מפתח", + "entryPointLinks": "קישורים לנקודות כניסה", + "triggers": "גורמים מפעילים", + "fbCommentAutomations": "אוטומציות תגובות פייסבוק", + "settings": "הגדרות" + } }, "qrCodeGenerator": { "title": "מחולל קודי QR", @@ -2556,6 +2609,18 @@ "unavailableTitle": "ביטול ההרשמה אינו זמין.", "unavailableDescription": "סביבת עבודה זו אינה זמינה עוד." }, + "templatesPublicPage": { + "invalidTitle": "קישור זה אינו זמין עוד.", + "invalidDescription": "קישור השיתוף של התבנית הזו אינו תקין, מושבת או שפג תוקפו.", + "byPublisher": "מאת {publisherName}", + "includes": "התבנית הזו כוללת", + "tryItOut": "נסה עכשיו", + "signInToInstall": "התחבר כדי להתקין", + "selectWorkspace": "בחר סביבת עבודה", + "install": "התקן", + "installStarted": "ההתקנה החלה", + "noEligibleWorkspaces": "אין לך סביבת עבודה מתאימה להתקנת התבנית הזו." + }, "extensionsMe": { "actions": { "delete": "מחיקת הנתונים שלי", @@ -3537,6 +3602,9 @@ "mcpServer": { "label": "שרת MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "פונקציית מערכת", "names": { @@ -3735,7 +3803,9 @@ "label": "מצב", "pending": "ממתין", "processing": "בעיבוד", + "installing": "Installing", "completed": "הושלם", + "partial": "Partial", "failed": "נכשל", "success": "הצלחה", "error": "שגיאה" diff --git a/apps/builder/messages/id.json b/apps/builder/messages/id.json index 5598f79a4..e51653e7f 100644 --- a/apps/builder/messages/id.json +++ b/apps/builder/messages/id.json @@ -145,7 +145,8 @@ "onSuccess": "Saat berhasil", "clone": "Klon", "remove": "Hapus", - "restore": "Pulihkan" + "restore": "Pulihkan", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Anda harus menjadi super admin workspace untuk melakukan ini." @@ -1146,6 +1147,9 @@ "mcpServer": { "label": "Server MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Fungsi Sistem", "names": { @@ -1344,7 +1348,9 @@ "label": "Status", "pending": "Menunggu", "processing": "Sedang diproses", + "installing": "Installing", "completed": "Selesai", + "partial": "Partial", "failed": "Gagal", "success": "Berhasil", "error": "Kesalahan" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Templat", - "description": "Buat dan kelola templat Anda." + "description": "Buat dan kelola templat Anda.", + "share": { + "label": "Bagikan", + "enabled": "Dibagikan", + "disabled": "Tidak dibagikan", + "dialogTitle": "Bagikan templat", + "dialogDescription": "Siapa pun yang memiliki tautan ini dapat menginstal salinan templat ini ke workspace di tenant Anda.", + "enableLabel": "Aktifkan tautan berbagi", + "linkLabel": "Tautan berbagi" + }, + "installs": { + "title": "Instalasi", + "resources": "Sumber daya", + "warnings": "Peringatan", + "completedAt": "Selesai pada", + "autoUpdate": "Pembaruan otomatis" + }, + "form": { + "contents": "Isi templat", + "comingSoon": "Segera hadir", + "selectAll": "Pilih semua", + "selectedCount": "{count} dipilih", + "details": "Detail", + "permissions": "Izin default", + "allowEdit": "Izinkan pengeditan setelah instalasi", + "allowDelete": "Izinkan penghapusan setelah instalasi", + "publisherName": "Nama penerbit", + "youtubeVideoId": "ID video YouTube", + "youtubeVideoIdDescription": "ID 11 karakter dari URL video, misalnya \"dQw4w9WgXcQ\" pada youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Tautan uji", + "imageUrl": "Gambar sampul", + "installBehavior": "Perilaku instalasi", + "createInstallFolder": "Instal ke folder khusus", + "createInstallFolderDescription": "Menempatkan setiap sumber daya yang diinstal ke dalam satu folder baru yang diberi nama sesuai templat ini.", + "defaultAutoUpdate": "Tandai instalasi untuk pembaruan secara default", + "defaultAutoUpdateDescription": "Instalasi baru akan menampilkan pemberitahuan pembaruan tersedia setiap kali Anda menerbitkan ulang templat ini." + }, + "categories": { + "flows": "Flow", + "customFields": "Kustom Field", + "tags": "Tag", + "products": "Produk", + "productCategories": "Kategori produk", + "aiFunctions": "Fungsi AI", + "aiAgents": "Agen AI", + "calendars": "Kalender Janji Temu", + "webchats": "Webchat", + "keywords": "Kata Kunci", + "entryPointLinks": "Tautan Titik Masuk", + "triggers": "Trigger", + "fbCommentAutomations": "Otomatisasi komentar Facebook", + "settings": "Pengaturan" + } }, "qrCodeGenerator": { "title": "Pembuat Kode QR", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Berhenti berlangganan tidak tersedia.", "unavailableDescription": "Ruang kerja ini tidak lagi tersedia." }, + "templatesPublicPage": { + "invalidTitle": "Tautan ini sudah tidak tersedia lagi.", + "invalidDescription": "Tautan berbagi templat ini tidak valid, dinonaktifkan, atau telah kedaluwarsa.", + "byPublisher": "Oleh {publisherName}", + "includes": "Templat ini mencakup", + "tryItOut": "Coba sekarang", + "signInToInstall": "Masuk untuk menginstal", + "selectWorkspace": "Pilih workspace", + "install": "Instal", + "installStarted": "Instalasi dimulai", + "noEligibleWorkspaces": "Anda tidak memiliki workspace yang memenuhi syarat untuk menginstal templat ini." + }, "extensionsMe": { "actions": { "delete": "Hapus Data Saya", diff --git a/apps/builder/messages/it.json b/apps/builder/messages/it.json index 442a5a5b5..70c9c7303 100644 --- a/apps/builder/messages/it.json +++ b/apps/builder/messages/it.json @@ -528,6 +528,9 @@ "mcpServer": { "label": "Server MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Funzione di sistema", "names": { @@ -726,7 +729,9 @@ "label": "Stato", "pending": "In attesa", "processing": "In elaborazione", + "installing": "Installing", "completed": "Completato", + "partial": "Partial", "failed": "Non riuscito", "success": "Operazione riuscita", "error": "Errore" @@ -1510,7 +1515,8 @@ "onSuccess": "In caso di successo", "clone": "Clona", "remove": "Rimuovi", - "restore": "Ripristina" + "restore": "Ripristina", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Devi essere super admin dello spazio di lavoro per farlo." @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Modelli", - "description": "Crea e gestisci i tuoi modelli." + "description": "Crea e gestisci i tuoi modelli.", + "share": { + "label": "Condividi", + "enabled": "Condiviso", + "disabled": "Non condiviso", + "dialogTitle": "Condividi modello", + "dialogDescription": "Chiunque abbia questo link può installare una copia di questo modello in uno spazio di lavoro del tuo tenant.", + "enableLabel": "Attiva link di condivisione", + "linkLabel": "Link di condivisione" + }, + "installs": { + "title": "Installazioni", + "resources": "Risorse", + "warnings": "Avvisi", + "completedAt": "Completato il", + "autoUpdate": "Aggiornamento automatico" + }, + "form": { + "contents": "Contenuto del modello", + "comingSoon": "Prossimamente", + "selectAll": "Seleziona tutto", + "selectedCount": "{count} selezionati", + "details": "Dettagli", + "permissions": "Autorizzazioni predefinite", + "allowEdit": "Consenti la modifica dopo l'installazione", + "allowDelete": "Consenti l'eliminazione dopo l'installazione", + "publisherName": "Nome dell'editore", + "youtubeVideoId": "ID video di YouTube", + "youtubeVideoIdDescription": "L'ID di 11 caratteri dall'URL del video, ad es. \"dQw4w9WgXcQ\" in youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Link di prova", + "imageUrl": "Immagine di copertina", + "installBehavior": "Comportamento di installazione", + "createInstallFolder": "Installa in una cartella dedicata", + "createInstallFolderDescription": "Raggruppa ogni risorsa installata in una nuova cartella con il nome di questo modello.", + "defaultAutoUpdate": "Contrassegna le installazioni per gli aggiornamenti per impostazione predefinita", + "defaultAutoUpdateDescription": "Le nuove installazioni mostreranno un avviso di aggiornamento disponibile ogni volta che ripubblichi questo modello." + }, + "categories": { + "flows": "Flussi", + "customFields": "Campi personalizzati", + "tags": "Tag", + "products": "Prodotti", + "productCategories": "Categorie di prodotti", + "aiFunctions": "Funzioni IA", + "aiAgents": "Agenti IA", + "calendars": "Calendari degli appuntamenti", + "webchats": "Webchat", + "keywords": "Parole chiave", + "entryPointLinks": "Link dei punti di ingresso", + "triggers": "Trigger", + "fbCommentAutomations": "Automazioni dei commenti Facebook", + "settings": "Impostazioni" + } }, "qrCodeGenerator": { "title": "Generatore di codici QR", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Annullamento dell'iscrizione non disponibile.", "unavailableDescription": "Questa area di lavoro non è più disponibile." }, + "templatesPublicPage": { + "invalidTitle": "Questo link non è più disponibile.", + "invalidDescription": "Questo link di condivisione del modello non è valido, è disattivato o è scaduto.", + "byPublisher": "Di {publisherName}", + "includes": "Questo modello include", + "tryItOut": "Provalo", + "signInToInstall": "Accedi per installare", + "selectWorkspace": "Seleziona uno spazio di lavoro", + "install": "Installa", + "installStarted": "Installazione avviata", + "noEligibleWorkspaces": "Non hai uno spazio di lavoro idoneo per installare questo modello." + }, "extensionsMe": { "actions": { "delete": "Elimina i miei dati", diff --git a/apps/builder/messages/ja.json b/apps/builder/messages/ja.json index 553002ff0..31077e304 100644 --- a/apps/builder/messages/ja.json +++ b/apps/builder/messages/ja.json @@ -145,7 +145,8 @@ "onSuccess": "成功時", "clone": "複製", "remove": "削除", - "restore": "復元" + "restore": "復元", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "この操作を行うには、ワークスペースのスーパー管理者である必要があります。" @@ -1146,6 +1147,9 @@ "mcpServer": { "label": "MCPサーバー" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "システム関数", "names": { @@ -1344,7 +1348,9 @@ "label": "ステータス", "pending": "保留中", "processing": "処理中", + "installing": "Installing", "completed": "完了", + "partial": "Partial", "failed": "失敗", "success": "成功", "error": "エラー" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "テンプレート", - "description": "テンプレートを作成・管理します。" + "description": "テンプレートを作成・管理します。", + "share": { + "label": "共有", + "enabled": "共有中", + "disabled": "未共有", + "dialogTitle": "テンプレートを共有", + "dialogDescription": "このリンクを持つ人は誰でも、あなたのテナント内のワークスペースにこのテンプレートのコピーをインストールできます。", + "enableLabel": "共有リンクを有効にする", + "linkLabel": "共有リンク" + }, + "installs": { + "title": "インストール", + "resources": "リソース", + "warnings": "警告", + "completedAt": "完了日時", + "autoUpdate": "自動更新" + }, + "form": { + "contents": "テンプレートの内容", + "comingSoon": "近日公開", + "selectAll": "すべて選択", + "selectedCount": "{count}件選択中", + "details": "詳細", + "permissions": "デフォルトの権限", + "allowEdit": "インストール後の編集を許可", + "allowDelete": "インストール後の削除を許可", + "publisherName": "公開者名", + "youtubeVideoId": "YouTube動画ID", + "youtubeVideoIdDescription": "動画URLに含まれる11文字のID(例: youtube.com/watch?v=dQw4w9WgXcQ の \"dQw4w9WgXcQ\")", + "testLink": "テストリンク", + "imageUrl": "カバー画像", + "installBehavior": "インストール時の動作", + "createInstallFolder": "専用フォルダーにインストールする", + "createInstallFolderDescription": "インストールされたすべてのリソースを、このテンプレートの名前を付けた新しい1つのフォルダーにまとめます。", + "defaultAutoUpdate": "インストールをデフォルトで更新対象としてマークする", + "defaultAutoUpdateDescription": "このテンプレートを再公開するたびに、新規インストールに更新通知が表示されます。" + }, + "categories": { + "flows": "フロー", + "customFields": "カスタムフィールド", + "tags": "タグ", + "products": "商品", + "productCategories": "商品カテゴリー", + "aiFunctions": "AI関数", + "aiAgents": "AIエージェント", + "calendars": "予約カレンダー", + "webchats": "Webチャット", + "keywords": "キーワード", + "entryPointLinks": "エントリーポイントリンク", + "triggers": "トリガー", + "fbCommentAutomations": "Facebookコメント自動化", + "settings": "設定" + } }, "qrCodeGenerator": { "title": "QRコード生成", @@ -4267,6 +4325,18 @@ "unavailableTitle": "配信を停止できません。", "unavailableDescription": "このワークスペースは利用できなくなりました。" }, + "templatesPublicPage": { + "invalidTitle": "このリンクはご利用いただけません。", + "invalidDescription": "このテンプレート共有リンクは無効、無効化済み、または期限切れです。", + "byPublisher": "{publisherName}による", + "includes": "このテンプレートに含まれるもの", + "tryItOut": "試してみる", + "signInToInstall": "サインインしてインストール", + "selectWorkspace": "ワークスペースを選択", + "install": "インストール", + "installStarted": "インストールを開始しました", + "noEligibleWorkspaces": "このテンプレートをインストールできるワークスペースがありません。" + }, "extensionsMe": { "actions": { "delete": "自分のデータを削除", diff --git a/apps/builder/messages/nl.json b/apps/builder/messages/nl.json index f53ca23bc..94a191e18 100644 --- a/apps/builder/messages/nl.json +++ b/apps/builder/messages/nl.json @@ -145,7 +145,8 @@ "onSuccess": "Bij succes", "clone": "Klonen", "remove": "Verwijderen", - "restore": "Herstellen" + "restore": "Herstellen", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Je moet superbeheerder van de werkruimte zijn om dit te doen." @@ -960,6 +961,9 @@ "mcpServer": { "label": "MCP-server" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Systeemfunctie", "names": { @@ -1158,7 +1162,9 @@ "label": "Status", "pending": "In behandeling", "processing": "Wordt verwerkt", + "installing": "Installing", "completed": "Voltooid", + "partial": "Partial", "failed": "Mislukt", "success": "Geslaagd", "error": "Fout" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Sjablonen", - "description": "Maak en beheer je sjablonen." + "description": "Maak en beheer je sjablonen.", + "share": { + "label": "Delen", + "enabled": "Gedeeld", + "disabled": "Niet gedeeld", + "dialogTitle": "Sjabloon delen", + "dialogDescription": "Iedereen met deze link kan een kopie van dit sjabloon installeren in een werkruimte binnen jouw tenant.", + "enableLabel": "Deellink inschakelen", + "linkLabel": "Deellink" + }, + "installs": { + "title": "Installaties", + "resources": "Bronnen", + "warnings": "Waarschuwingen", + "completedAt": "Voltooid op", + "autoUpdate": "Automatische update" + }, + "form": { + "contents": "Sjabloon-inhoud", + "comingSoon": "Binnenkort beschikbaar", + "selectAll": "Alles selecteren", + "selectedCount": "{count} geselecteerd", + "details": "Details", + "permissions": "Standaardrechten", + "allowEdit": "Bewerken na installatie toestaan", + "allowDelete": "Verwijderen na installatie toestaan", + "publisherName": "Naam uitgever", + "youtubeVideoId": "YouTube-video-ID", + "youtubeVideoIdDescription": "De 11-tekens-ID uit de video-URL, bijv. \"dQw4w9WgXcQ\" in youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Testlink", + "imageUrl": "Omslagafbeelding", + "installBehavior": "Installatiegedrag", + "createInstallFolder": "Installeren in een aparte map", + "createInstallFolderDescription": "Plaatst elke geïnstalleerde bron in één nieuwe map die naar dit sjabloon is vernoemd.", + "defaultAutoUpdate": "Installaties standaard markeren voor updates", + "defaultAutoUpdateDescription": "Nieuwe installaties tonen een melding over een beschikbare update zodra je dit sjabloon opnieuw publiceert." + }, + "categories": { + "flows": "Flows", + "customFields": "Aangepaste velden", + "tags": "Tags", + "products": "Producten", + "productCategories": "Productcategorieën", + "aiFunctions": "AI-functies", + "aiAgents": "AI-agents", + "calendars": "Afspraakkalenders", + "webchats": "Webchats", + "keywords": "Trefwoorden", + "entryPointLinks": "Toegangspuntlinks", + "triggers": "Triggers", + "fbCommentAutomations": "Facebook-reactieautomatiseringen", + "settings": "Instellingen" + } }, "qrCodeGenerator": { "title": "QR-codegenerator", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Afmelden niet beschikbaar.", "unavailableDescription": "Deze werkruimte is niet meer beschikbaar." }, + "templatesPublicPage": { + "invalidTitle": "Deze link is niet meer beschikbaar.", + "invalidDescription": "Deze sjabloon-deellink is ongeldig, uitgeschakeld of verlopen.", + "byPublisher": "Door {publisherName}", + "includes": "Dit sjabloon bevat", + "tryItOut": "Probeer het", + "signInToInstall": "Log in om te installeren", + "selectWorkspace": "Selecteer een werkruimte", + "install": "Installeren", + "installStarted": "Installatie gestart", + "noEligibleWorkspaces": "Je hebt geen werkruimte die geschikt is om dit sjabloon te installeren." + }, "extensionsMe": { "actions": { "delete": "Mijn gegevens verwijderen", diff --git a/apps/builder/messages/pt-BR.json b/apps/builder/messages/pt-BR.json index 22bf314b1..48f6d09e0 100644 --- a/apps/builder/messages/pt-BR.json +++ b/apps/builder/messages/pt-BR.json @@ -145,7 +145,8 @@ "onSuccess": "Em caso de sucesso", "clone": "Clonar", "remove": "Remover", - "restore": "Restaurar" + "restore": "Restaurar", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Você precisa ser um superadministrador do espaço de trabalho para fazer isso." @@ -1146,6 +1147,9 @@ "mcpServer": { "label": "Servidor MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Função do sistema", "names": { @@ -1344,7 +1348,9 @@ "label": "Status", "pending": "Pendente", "processing": "Processando", + "installing": "Installing", "completed": "Concluído", + "partial": "Partial", "failed": "Falhou", "success": "Sucesso", "error": "Erro" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Modelos", - "description": "Crie e gerencie seus modelos." + "description": "Crie e gerencie seus modelos.", + "share": { + "label": "Compartilhar", + "enabled": "Compartilhado", + "disabled": "Não compartilhado", + "dialogTitle": "Compartilhar modelo", + "dialogDescription": "Qualquer pessoa com este link pode instalar uma cópia deste modelo em um espaço de trabalho da sua organização.", + "enableLabel": "Ativar link de compartilhamento", + "linkLabel": "Link de compartilhamento" + }, + "installs": { + "title": "Instalações", + "resources": "Recursos", + "warnings": "Avisos", + "completedAt": "Concluído em", + "autoUpdate": "Atualização automática" + }, + "form": { + "contents": "Conteúdo do modelo", + "comingSoon": "Em breve", + "selectAll": "Selecionar tudo", + "selectedCount": "{count} selecionado(s)", + "details": "Detalhes", + "permissions": "Permissões padrão", + "allowEdit": "Permitir edição após a instalação", + "allowDelete": "Permitir exclusão após a instalação", + "publisherName": "Nome do publicador", + "youtubeVideoId": "ID do vídeo do YouTube", + "youtubeVideoIdDescription": "O ID de 11 caracteres da URL do vídeo, ex.: \"dQw4w9WgXcQ\" em youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Link de teste", + "imageUrl": "Imagem de capa", + "installBehavior": "Comportamento de instalação", + "createInstallFolder": "Instalar em uma pasta dedicada", + "createInstallFolderDescription": "Agrupa todos os recursos instalados em uma nova pasta com o nome deste modelo.", + "defaultAutoUpdate": "Marcar instalações para atualização por padrão", + "defaultAutoUpdateDescription": "Novas instalações mostrarão um aviso de atualização disponível sempre que você republicar este modelo." + }, + "categories": { + "flows": "Fluxos", + "customFields": "Campos personalizados", + "tags": "Tags", + "products": "Produtos", + "productCategories": "Categorias de produtos", + "aiFunctions": "Funções de IA", + "aiAgents": "Agentes de IA", + "calendars": "Calendários de compromissos", + "webchats": "Webchats", + "keywords": "Palavras-chave", + "entryPointLinks": "Links de ponto de entrada", + "triggers": "Gatilhos", + "fbCommentAutomations": "Automações de comentários do Facebook", + "settings": "Configurações" + } }, "qrCodeGenerator": { "title": "Gerador de códigos QR", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Cancelamento de inscrição indisponível.", "unavailableDescription": "Este espaço de trabalho não está mais disponível." }, + "templatesPublicPage": { + "invalidTitle": "Este link não está mais disponível.", + "invalidDescription": "Este link de compartilhamento do modelo é inválido, está desativado ou expirou.", + "byPublisher": "Por {publisherName}", + "includes": "Este modelo inclui", + "tryItOut": "Experimente", + "signInToInstall": "Faça login para instalar", + "selectWorkspace": "Selecione um espaço de trabalho", + "install": "Instalar", + "installStarted": "Instalação iniciada", + "noEligibleWorkspaces": "Você não tem um espaço de trabalho elegível para instalar este modelo." + }, "extensionsMe": { "actions": { "delete": "Excluir meus dados", diff --git a/apps/builder/messages/pt-PT.json b/apps/builder/messages/pt-PT.json index e2b1605ae..f9fcbabaf 100644 --- a/apps/builder/messages/pt-PT.json +++ b/apps/builder/messages/pt-PT.json @@ -145,7 +145,8 @@ "onSuccess": "Em caso de sucesso", "clone": "Clonar", "remove": "Remover", - "restore": "Restaurar" + "restore": "Restaurar", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Tem de ser um superadministrador do espaço de trabalho para fazer isto." @@ -1146,6 +1147,9 @@ "mcpServer": { "label": "Servidor MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Função do sistema", "names": { @@ -1344,7 +1348,9 @@ "label": "Estado", "pending": "Pendente", "processing": "Em processamento", + "installing": "Installing", "completed": "Concluído", + "partial": "Partial", "failed": "Falhou", "success": "Sucesso", "error": "Erro" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Modelos", - "description": "Crie e faça a gestão dos seus modelos." + "description": "Crie e faça a gestão dos seus modelos.", + "share": { + "label": "Partilhar", + "enabled": "Partilhado", + "disabled": "Não partilhado", + "dialogTitle": "Partilhar modelo", + "dialogDescription": "Qualquer pessoa com esta ligação pode instalar uma cópia deste modelo num espaço de trabalho da sua organização.", + "enableLabel": "Ativar ligação de partilha", + "linkLabel": "Ligação de partilha" + }, + "installs": { + "title": "Instalações", + "resources": "Recursos", + "warnings": "Avisos", + "completedAt": "Concluído em", + "autoUpdate": "Atualização automática" + }, + "form": { + "contents": "Conteúdo do modelo", + "comingSoon": "Brevemente", + "selectAll": "Selecionar tudo", + "selectedCount": "{count} selecionado(s)", + "details": "Detalhes", + "permissions": "Permissões predefinidas", + "allowEdit": "Permitir edição após a instalação", + "allowDelete": "Permitir eliminação após a instalação", + "publisherName": "Nome do publicador", + "youtubeVideoId": "ID do vídeo do YouTube", + "youtubeVideoIdDescription": "O ID de 11 caracteres do URL do vídeo, por ex. \"dQw4w9WgXcQ\" em youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Ligação de teste", + "imageUrl": "Imagem de capa", + "installBehavior": "Comportamento de instalação", + "createInstallFolder": "Instalar numa pasta dedicada", + "createInstallFolderDescription": "Agrupa todos os recursos instalados numa nova pasta com o nome deste modelo.", + "defaultAutoUpdate": "Marcar instalações para atualização por predefinição", + "defaultAutoUpdateDescription": "As novas instalações mostrarão um aviso de atualização disponível sempre que republicar este modelo." + }, + "categories": { + "flows": "Fluxos", + "customFields": "Campos personalizados", + "tags": "Etiquetas", + "products": "Produtos", + "productCategories": "Categorias de produtos", + "aiFunctions": "Funções de IA", + "aiAgents": "Agentes de IA", + "calendars": "Calendários de marcações", + "webchats": "Webchats", + "keywords": "Palavras-chave", + "entryPointLinks": "Ligações de ponto de entrada", + "triggers": "Acionadores", + "fbCommentAutomations": "Automatizações de comentários do Facebook", + "settings": "Definições" + } }, "qrCodeGenerator": { "title": "Gerador de códigos QR", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Cancelamento de subscrição indisponível.", "unavailableDescription": "Este espaço de trabalho já não está disponível." }, + "templatesPublicPage": { + "invalidTitle": "Esta ligação já não está disponível.", + "invalidDescription": "Esta ligação de partilha de modelo é inválida, está desativada ou expirou.", + "byPublisher": "Por {publisherName}", + "includes": "Este modelo inclui", + "tryItOut": "Experimente", + "signInToInstall": "Inicie sessão para instalar", + "selectWorkspace": "Selecione um espaço de trabalho", + "install": "Instalar", + "installStarted": "Instalação iniciada", + "noEligibleWorkspaces": "Não tem nenhum espaço de trabalho elegível para instalar este modelo." + }, "extensionsMe": { "actions": { "delete": "Eliminar os meus dados", diff --git a/apps/builder/messages/ro.json b/apps/builder/messages/ro.json index 218995afc..c423bcb9d 100644 --- a/apps/builder/messages/ro.json +++ b/apps/builder/messages/ro.json @@ -892,7 +892,8 @@ "onSuccess": "La reușită", "clone": "Clonează", "remove": "Elimină", - "restore": "Restaurează" + "restore": "Restaurează", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Trebuie să fii superadministrator al spațiului de lucru pentru a face acest lucru." @@ -1893,6 +1894,9 @@ "mcpServer": { "label": "Server MCP" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Funcție de sistem", "names": { @@ -2091,7 +2095,9 @@ "label": "Stare", "pending": "În așteptare", "processing": "În curs de procesare", + "installing": "Installing", "completed": "Finalizat", + "partial": "Partial", "failed": "Eșuat", "success": "Succes", "error": "Eroare" @@ -4686,7 +4692,59 @@ }, "templates": { "description": "Creează și gestionează șabloanele tale.", - "title": "Șabloane" + "title": "Șabloane", + "share": { + "label": "Distribuie", + "enabled": "Distribuit", + "disabled": "Nedistribuit", + "dialogTitle": "Distribuie șablonul", + "dialogDescription": "Oricine are acest link poate instala o copie a acestui șablon într-un spațiu de lucru din organizația ta.", + "enableLabel": "Activează linkul de distribuire", + "linkLabel": "Link de distribuire" + }, + "installs": { + "title": "Instalări", + "resources": "Resurse", + "warnings": "Avertismente", + "completedAt": "Finalizat la", + "autoUpdate": "Actualizare automată" + }, + "form": { + "contents": "Conținutul șablonului", + "comingSoon": "În curând", + "selectAll": "Selectează tot", + "selectedCount": "{count} selectate", + "details": "Detalii", + "permissions": "Permisiuni implicite", + "allowEdit": "Permite editarea după instalare", + "allowDelete": "Permite ștergerea după instalare", + "publisherName": "Numele editorului", + "youtubeVideoId": "ID-ul videoclipului YouTube", + "youtubeVideoIdDescription": "ID-ul de 11 caractere din URL-ul videoclipului, de ex. \"dQw4w9WgXcQ\" în youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Link de test", + "imageUrl": "Imagine de copertă", + "installBehavior": "Comportament la instalare", + "createInstallFolder": "Instalează într-un folder dedicat", + "createInstallFolderDescription": "Grupează fiecare resursă instalată într-un singur folder nou, denumit după acest șablon.", + "defaultAutoUpdate": "Marchează instalările pentru actualizări în mod implicit", + "defaultAutoUpdateDescription": "Instalările noi vor afișa o notificare de actualizare disponibilă de fiecare dată când republici acest șablon." + }, + "categories": { + "flows": "Fluxuri", + "customFields": "Câmpuri personalizate", + "tags": "Etichete", + "products": "Produse", + "productCategories": "Categorii de produse", + "aiFunctions": "Funcții AI", + "aiAgents": "Agenți AI", + "calendars": "Calendare de programări", + "webchats": "Chat-uri web", + "keywords": "Cuvinte cheie", + "entryPointLinks": "Linkuri de intrare", + "triggers": "Declanșatoare", + "fbCommentAutomations": "Automatizări comentarii Facebook", + "settings": "Setări" + } }, "tiktok": { "description": "Conectează-ți contul TikTok pentru a răspunde la mesajele directe.", @@ -4701,6 +4759,18 @@ "unavailableDescription": "Acest spațiu de lucru nu mai este disponibil.", "unavailableTitle": "Dezabonare indisponibilă." }, + "templatesPublicPage": { + "invalidTitle": "Acest link nu mai este disponibil.", + "invalidDescription": "Acest link de distribuire a șablonului este invalid, dezactivat sau expirat.", + "byPublisher": "De {publisherName}", + "includes": "Acest șablon include", + "tryItOut": "Încearcă-l", + "signInToInstall": "Autentifică-te pentru a instala", + "selectWorkspace": "Selectează un spațiu de lucru", + "install": "Instalează", + "installStarted": "Instalarea a început", + "noEligibleWorkspaces": "Nu ai niciun spațiu de lucru eligibil pentru a instala acest șablon." + }, "bookingWebview": { "metadataTitle": "Programează o întâlnire", "badge": "Programare", diff --git a/apps/builder/messages/sv.json b/apps/builder/messages/sv.json index ed3f4b918..e8747c72d 100644 --- a/apps/builder/messages/sv.json +++ b/apps/builder/messages/sv.json @@ -149,7 +149,8 @@ "uploadFile": "Ladda upp fil", "view": "Visa", "viewAnalytics": "Visa analys", - "wait": "Vänta" + "wait": "Vänta", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Du måste vara superadministratör för arbetsytan för att göra detta." @@ -2018,6 +2019,9 @@ "mcpServer": { "label": "MCP-server" }, + "template": { + "label": "Template" + }, "me": { "label": "Länk till mina data" }, @@ -2379,11 +2383,13 @@ }, "status": { "completed": "Slutförd", + "partial": "Partial", "error": "Fel", "failed": "Misslyckades", "label": "Status", "pending": "Väntande", "processing": "Bearbetas", + "installing": "Installing", "success": "Lyckades" }, "statusCode": { @@ -4234,7 +4240,59 @@ }, "templates": { "description": "Skapa och hantera dina mallar.", - "title": "Mallar" + "title": "Mallar", + "share": { + "label": "Dela", + "enabled": "Delad", + "disabled": "Inte delad", + "dialogTitle": "Dela mall", + "dialogDescription": "Alla med den här länken kan installera en kopia av den här mallen i en arbetsyta i din organisation.", + "enableLabel": "Aktivera dellänk", + "linkLabel": "Dellänk" + }, + "installs": { + "title": "Installationer", + "resources": "Resurser", + "warnings": "Varningar", + "completedAt": "Slutförd", + "autoUpdate": "Automatisk uppdatering" + }, + "form": { + "contents": "Mallens innehåll", + "comingSoon": "Kommer snart", + "selectAll": "Markera alla", + "selectedCount": "{count} markerade", + "details": "Detaljer", + "permissions": "Standardbehörigheter", + "allowEdit": "Tillåt redigering efter installation", + "allowDelete": "Tillåt borttagning efter installation", + "publisherName": "Utgivarens namn", + "youtubeVideoId": "YouTube-video-id", + "youtubeVideoIdDescription": "Det 11-teckens id från videons URL, t.ex. \"dQw4w9WgXcQ\" i youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Testlänk", + "imageUrl": "Omslagsbild", + "installBehavior": "Installationsbeteende", + "createInstallFolder": "Installera i en dedikerad mapp", + "createInstallFolderDescription": "Placerar varje installerad resurs i en ny mapp uppkallad efter den här mallen.", + "defaultAutoUpdate": "Markera installationer för uppdatering som standard", + "defaultAutoUpdateDescription": "Nya installationer visar ett meddelande om tillgänglig uppdatering varje gång du publicerar om den här mallen." + }, + "categories": { + "flows": "Flöden", + "customFields": "Anpassade fält", + "tags": "Taggar", + "products": "Produkter", + "productCategories": "Produktkategorier", + "aiFunctions": "AI-funktioner", + "aiAgents": "AI-agenter", + "calendars": "Bokningskalendrar", + "webchats": "Webbchattar", + "keywords": "Nyckelord", + "entryPointLinks": "Länkar till startpunkter", + "triggers": "Utlösare", + "fbCommentAutomations": "Facebook-kommentarautomatiseringar", + "settings": "Inställningar" + } }, "texts": { "or": "eller" @@ -4323,6 +4381,18 @@ "unavailableDescription": "Den här arbetsytan är inte längre tillgänglig.", "unavailableTitle": "Avregistrering är inte tillgänglig." }, + "templatesPublicPage": { + "invalidTitle": "Den här länken är inte längre tillgänglig.", + "invalidDescription": "Den här mallens dellänk är ogiltig, inaktiverad eller har gått ut.", + "byPublisher": "Av {publisherName}", + "includes": "Den här mallen innehåller", + "tryItOut": "Prova den", + "signInToInstall": "Logga in för att installera", + "selectWorkspace": "Välj en arbetsyta", + "install": "Installera", + "installStarted": "Installationen har startat", + "noEligibleWorkspaces": "Du har ingen arbetsyta som är berättigad att installera den här mallen." + }, "userPersistentMenus": { "title": "Beständiga användarmenyer" }, diff --git a/apps/builder/messages/tr.json b/apps/builder/messages/tr.json index b79bcd3db..187d79f3c 100644 --- a/apps/builder/messages/tr.json +++ b/apps/builder/messages/tr.json @@ -145,7 +145,8 @@ "onSuccess": "Başarı durumunda", "clone": "Klonla", "remove": "Kaldır", - "restore": "Geri Yükle" + "restore": "Geri Yükle", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Bunu yapmak için çalışma alanı süper yöneticisi olmanız gerekir." @@ -1220,6 +1221,9 @@ "mcpServer": { "label": "MCP Sunucusu" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Sistem İşlevi", "names": { @@ -1418,7 +1422,9 @@ "label": "Durum", "pending": "Bekliyor", "processing": "İşleniyor", + "installing": "Installing", "completed": "Tamamlandı", + "partial": "Partial", "failed": "Başarısız", "success": "Başarılı", "error": "Hata" @@ -4151,7 +4157,59 @@ }, "templates": { "title": "Şablonlar", - "description": "Şablonlarınızı oluşturun ve yönetin." + "description": "Şablonlarınızı oluşturun ve yönetin.", + "share": { + "label": "Paylaş", + "enabled": "Paylaşıldı", + "disabled": "Paylaşılmadı", + "dialogTitle": "Şablonu paylaş", + "dialogDescription": "Bu bağlantıya sahip herkes, bu şablonun bir kopyasını kendi kuruluşunuzdaki bir çalışma alanına yükleyebilir.", + "enableLabel": "Paylaşım bağlantısını etkinleştir", + "linkLabel": "Paylaşım bağlantısı" + }, + "installs": { + "title": "Yüklemeler", + "resources": "Kaynaklar", + "warnings": "Uyarılar", + "completedAt": "Tamamlanma tarihi", + "autoUpdate": "Otomatik güncelleme" + }, + "form": { + "contents": "Şablon içeriği", + "comingSoon": "Yakında", + "selectAll": "Tümünü seç", + "selectedCount": "{count} seçildi", + "details": "Ayrıntılar", + "permissions": "Varsayılan izinler", + "allowEdit": "Yüklemeden sonra düzenlemeye izin ver", + "allowDelete": "Yüklemeden sonra silmeye izin ver", + "publisherName": "Yayıncı adı", + "youtubeVideoId": "YouTube video kimliği", + "youtubeVideoIdDescription": "Video URL'sindeki 11 karakterli kimlik, ör. youtube.com/watch?v=dQw4w9WgXcQ içindeki \"dQw4w9WgXcQ\"", + "testLink": "Test bağlantısı", + "imageUrl": "Kapak görseli", + "installBehavior": "Yükleme davranışı", + "createInstallFolder": "Ayrılmış bir klasöre yükle", + "createInstallFolderDescription": "Yüklenen her kaynağı bu şablonun adını taşıyan tek bir yeni klasörün altına yerleştirir.", + "defaultAutoUpdate": "Yüklemeleri varsayılan olarak güncelleme için işaretle", + "defaultAutoUpdateDescription": "Bu şablonu her yeniden yayınladığınızda, yeni yüklemelerde güncelleme bildirimi gösterilir." + }, + "categories": { + "flows": "Akışlar", + "customFields": "Özel Alanlar", + "tags": "Etiketler", + "products": "Ürünler", + "productCategories": "Ürün kategorileri", + "aiFunctions": "Yapay Zekâ İşlevleri", + "aiAgents": "Yapay Zekâ Temsilcileri", + "calendars": "Randevu Takvimleri", + "webchats": "Web Sohbetleri", + "keywords": "Anahtar Kelimeler", + "entryPointLinks": "Giriş Noktası Bağlantıları", + "triggers": "Tetikleyiciler", + "fbCommentAutomations": "Facebook yorum otomasyonları", + "settings": "Ayarlar" + } }, "qrCodeGenerator": { "title": "QR Kod Oluşturucu", @@ -4267,6 +4325,18 @@ "unavailableTitle": "Abonelikten çıkma kullanılamıyor.", "unavailableDescription": "Bu çalışma alanı artık kullanılamıyor." }, + "templatesPublicPage": { + "invalidTitle": "Bu bağlantı artık kullanılamıyor.", + "invalidDescription": "Bu şablon paylaşım bağlantısı geçersiz, devre dışı bırakılmış veya süresi dolmuş.", + "byPublisher": "{publisherName} tarafından", + "includes": "Bu şablon şunları içerir", + "tryItOut": "Deneyin", + "signInToInstall": "Yüklemek için oturum açın", + "selectWorkspace": "Bir çalışma alanı seçin", + "install": "Yükle", + "installStarted": "Yükleme başladı", + "noEligibleWorkspaces": "Bu şablonu yüklemeye uygun bir çalışma alanınız yok." + }, "extensionsMe": { "actions": { "delete": "Verilerimi Sil", diff --git a/apps/builder/messages/vi.json b/apps/builder/messages/vi.json index 2a7c959ee..4363ab46e 100644 --- a/apps/builder/messages/vi.json +++ b/apps/builder/messages/vi.json @@ -145,7 +145,8 @@ "onSuccess": "Khi thành công", "clone": "Nhân bản", "remove": "Xóa", - "restore": "Khôi phục" + "restore": "Khôi phục", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "Bạn cần là super admin của workspace để thực hiện thao tác này." @@ -1190,6 +1191,9 @@ "mcpServer": { "label": "MCP Server" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "Hàm hệ thống", "names": { @@ -1388,7 +1392,9 @@ "label": "Trạng thái", "pending": "Đang chờ", "processing": "Đang xử lý", + "installing": "Installing", "completed": "Hoàn tất", + "partial": "Partial", "failed": "Thất bại", "success": "Thành công", "error": "Lỗi" @@ -4324,7 +4330,59 @@ }, "templates": { "title": "Mẫu", - "description": "Tạo và quản lý các mẫu của bạn." + "description": "Tạo và quản lý các mẫu của bạn.", + "share": { + "label": "Chia sẻ", + "enabled": "Đã chia sẻ", + "disabled": "Chưa chia sẻ", + "dialogTitle": "Chia sẻ mẫu", + "dialogDescription": "Bất kỳ ai có liên kết này đều có thể cài đặt một bản sao của mẫu này vào một workspace trong tổ chức của bạn.", + "enableLabel": "Bật liên kết chia sẻ", + "linkLabel": "Liên kết chia sẻ" + }, + "installs": { + "title": "Lượt cài đặt", + "resources": "Tài nguyên", + "warnings": "Cảnh báo", + "completedAt": "Hoàn tất lúc", + "autoUpdate": "Tự động cập nhật" + }, + "form": { + "contents": "Nội dung mẫu", + "comingSoon": "Sắp ra mắt", + "selectAll": "Chọn tất cả", + "selectedCount": "Đã chọn {count}", + "details": "Chi tiết", + "permissions": "Quyền mặc định", + "allowEdit": "Cho phép chỉnh sửa sau khi cài đặt", + "allowDelete": "Cho phép xóa sau khi cài đặt", + "publisherName": "Tên nhà xuất bản", + "youtubeVideoId": "ID video YouTube", + "youtubeVideoIdDescription": "ID gồm 11 ký tự lấy từ URL video, ví dụ \"dQw4w9WgXcQ\" trong youtube.com/watch?v=dQw4w9WgXcQ", + "testLink": "Liên kết thử nghiệm", + "imageUrl": "Ảnh bìa", + "installBehavior": "Hành vi khi cài đặt", + "createInstallFolder": "Cài đặt vào một thư mục riêng", + "createInstallFolderDescription": "Đưa mọi tài nguyên đã cài đặt vào một thư mục mới duy nhất, được đặt tên theo mẫu này.", + "defaultAutoUpdate": "Đánh dấu các lượt cài đặt cần cập nhật theo mặc định", + "defaultAutoUpdateDescription": "Các lượt cài đặt mới sẽ hiển thị thông báo có bản cập nhật mỗi khi bạn xuất bản lại mẫu này." + }, + "categories": { + "flows": "Luồng", + "customFields": "Trường tùy chỉnh", + "tags": "Thẻ", + "products": "Sản phẩm", + "productCategories": "Danh mục sản phẩm", + "aiFunctions": "AI Functions", + "aiAgents": "AI Agent", + "calendars": "Lịch hẹn", + "webchats": "Webchat", + "keywords": "Từ khóa", + "entryPointLinks": "Liên kết giới thiệu", + "triggers": "Trigger", + "fbCommentAutomations": "Tự động hóa bình luận Facebook", + "settings": "Cài đặt" + } }, "qrCodeGenerator": { "title": "Trình tạo mã QR", @@ -4444,6 +4502,18 @@ "unavailableTitle": "Không thể hủy đăng ký.", "unavailableDescription": "Không gian làm việc này không còn khả dụng." }, + "templatesPublicPage": { + "invalidTitle": "Liên kết này không còn khả dụng.", + "invalidDescription": "Liên kết chia sẻ mẫu này không hợp lệ, đã bị vô hiệu hóa hoặc đã hết hạn.", + "byPublisher": "Bởi {publisherName}", + "includes": "Mẫu này bao gồm", + "tryItOut": "Dùng thử", + "signInToInstall": "Đăng nhập để cài đặt", + "selectWorkspace": "Chọn một workspace", + "install": "Cài đặt", + "installStarted": "Đã bắt đầu cài đặt", + "noEligibleWorkspaces": "Bạn không có workspace nào đủ điều kiện để cài đặt mẫu này." + }, "bookingPage": { "metadataTitle": "Đặt lịch", "comingSoonTitle": "Trang đặt lịch sắp ra mắt", diff --git a/apps/builder/messages/zh-CN.json b/apps/builder/messages/zh-CN.json index a875cf8eb..fc707492b 100644 --- a/apps/builder/messages/zh-CN.json +++ b/apps/builder/messages/zh-CN.json @@ -149,7 +149,8 @@ "uploadFile": "上传文件", "view": "查看", "viewAnalytics": "查看分析", - "wait": "等等" + "wait": "等等", + "loadMore": "Load more" }, "activeCampaign": { "automations": { @@ -2026,6 +2027,9 @@ "mcpServer": { "label": "MCP服务器" }, + "template": { + "label": "Template" + }, "me": { "label": "我的数据链接" }, @@ -2382,11 +2386,13 @@ }, "status": { "completed": "已完成", + "partial": "Partial", "error": "错误", "failed": "失败", "label": "状态", "pending": "待定", "processing": "处理中", + "installing": "Installing", "success": "成功" }, "statusCode": { @@ -4233,8 +4239,60 @@ "title": "Telegram" }, "templates": { - "description": "创建并管理您的范本。", - "title": "范本" + "description": "创建和管理您的范本。", + "title": "范本", + "share": { + "label": "分享", + "enabled": "已分享", + "disabled": "未分享", + "dialogTitle": "分享范本", + "dialogDescription": "任何拥有此链接的人都可以将此范本的副本安装到您所在租户的工作区中。", + "enableLabel": "启用分享链接", + "linkLabel": "分享链接" + }, + "installs": { + "title": "安装记录", + "resources": "资源", + "warnings": "警告", + "completedAt": "完成时间", + "autoUpdate": "自动更新" + }, + "form": { + "contents": "范本内容", + "comingSoon": "即将推出", + "selectAll": "全选", + "selectedCount": "已选择 {count} 项", + "details": "详情", + "permissions": "默认权限", + "allowEdit": "允许安装后编辑", + "allowDelete": "允许安装后删除", + "publisherName": "发布者名称", + "youtubeVideoId": "YouTube 视频 ID", + "youtubeVideoIdDescription": "视频 URL 中的 11 位字符 ID,例如 youtube.com/watch?v=dQw4w9WgXcQ 中的 \"dQw4w9WgXcQ\"", + "testLink": "测试链接", + "imageUrl": "封面图片", + "installBehavior": "安装行为", + "createInstallFolder": "安装到专属文件夹", + "createInstallFolderDescription": "将每个已安装的资源都归入一个以此范本命名的新文件夹中。", + "defaultAutoUpdate": "默认将安装标记为需要更新", + "defaultAutoUpdateDescription": "每次重新发布此范本时,新安装都会显示可用更新的提示。" + }, + "categories": { + "flows": "流程", + "customFields": "自订字段", + "tags": "标签", + "products": "产品", + "productCategories": "产品分类", + "aiFunctions": "人工智能功能", + "aiAgents": "AI Agent", + "calendars": "预约日历", + "webchats": "网络聊天", + "keywords": "关键字", + "entryPointLinks": "入口点链接", + "triggers": "触发器", + "fbCommentAutomations": "Facebook 评论自动化", + "settings": "设置" + } }, "texts": { "or": "或" @@ -4323,6 +4381,18 @@ "unavailableDescription": "此工作区不再可用。", "unavailableTitle": "无法取消订阅。" }, + "templatesPublicPage": { + "invalidTitle": "此链接已不再可用。", + "invalidDescription": "此范本分享链接无效、已禁用或已过期。", + "byPublisher": "发布者:{publisherName}", + "includes": "此范本包含", + "tryItOut": "立即体验", + "signInToInstall": "登录以安装", + "selectWorkspace": "选择一个工作区", + "install": "安装", + "installStarted": "安装已开始", + "noEligibleWorkspaces": "您没有可安装此范本的合适工作区。" + }, "userPersistentMenus": { "title": "用户常驻菜单" }, diff --git a/apps/builder/messages/zh-TW.json b/apps/builder/messages/zh-TW.json index 22229e7ab..fecaef01f 100644 --- a/apps/builder/messages/zh-TW.json +++ b/apps/builder/messages/zh-TW.json @@ -536,6 +536,9 @@ "mcpServer": { "label": "MCP伺服器" }, + "template": { + "label": "Template" + }, "systemFunction": { "label": "系統功能", "names": { @@ -726,7 +729,9 @@ "label": "狀態", "pending": "待定", "processing": "處理中", + "installing": "Installing", "completed": "已完成", + "partial": "Partial", "failed": "失敗", "success": "成功", "error": "錯誤" @@ -1661,7 +1666,8 @@ "onSuccess": "成功", "clone": "複製", "remove": "移除", - "restore": "還原" + "restore": "還原", + "loadMore": "Load more" }, "errors": { "superAdminRequired": "您需要是工作區的超級管理員才能執行此操作。" @@ -4184,7 +4190,59 @@ }, "templates": { "title": "範本", - "description": "建立並管理您的範本。" + "description": "建立並管理您的範本。", + "share": { + "label": "分享", + "enabled": "已分享", + "disabled": "未分享", + "dialogTitle": "分享範本", + "dialogDescription": "任何擁有此連結的人都可以將此範本的副本安裝到您所在租戶的工作區中。", + "enableLabel": "啟用分享連結", + "linkLabel": "分享連結" + }, + "installs": { + "title": "安裝記錄", + "resources": "資源", + "warnings": "警告", + "completedAt": "完成時間", + "autoUpdate": "自動更新" + }, + "form": { + "contents": "範本內容", + "comingSoon": "即將推出", + "selectAll": "全選", + "selectedCount": "已選取 {count} 項", + "details": "詳細資料", + "permissions": "預設權限", + "allowEdit": "允許安裝後編輯", + "allowDelete": "允許安裝後刪除", + "publisherName": "發布者名稱", + "youtubeVideoId": "YouTube 影片 ID", + "youtubeVideoIdDescription": "影片網址中的 11 位字元 ID,例如 youtube.com/watch?v=dQw4w9WgXcQ 中的 \"dQw4w9WgXcQ\"", + "testLink": "測試連結", + "imageUrl": "封面圖片", + "installBehavior": "安裝行為", + "createInstallFolder": "安裝到專屬資料夾", + "createInstallFolderDescription": "將每個已安裝的資源都歸入一個以此範本命名的新資料夾中。", + "defaultAutoUpdate": "預設將安裝標記為需要更新", + "defaultAutoUpdateDescription": "每次重新發布此範本時,新安裝都會顯示可用更新的提示。" + }, + "categories": { + "flows": "流程", + "customFields": "自訂欄位", + "tags": "標籤", + "products": "產品", + "productCategories": "產品分類", + "aiFunctions": "人工智慧功能", + "aiAgents": "AI Agent", + "calendars": "預約日曆", + "webchats": "網路聊天", + "keywords": "關鍵字", + "entryPointLinks": "入口點連結", + "triggers": "觸發器", + "fbCommentAutomations": "Facebook 留言自動化", + "settings": "設定" + } }, "qrCodeGenerator": { "title": "QR Code 產生器", @@ -4300,6 +4358,18 @@ "unavailableTitle": "無法取消訂閱。", "unavailableDescription": "此工作區不再可用。" }, + "templatesPublicPage": { + "invalidTitle": "此連結已不再可用。", + "invalidDescription": "此範本分享連結無效、已停用或已過期。", + "byPublisher": "發布者:{publisherName}", + "includes": "此範本包含", + "tryItOut": "立即體驗", + "signInToInstall": "登入以安裝", + "selectWorkspace": "選擇一個工作區", + "install": "安裝", + "installStarted": "安裝已開始", + "noEligibleWorkspaces": "您沒有適合安裝此範本的工作區。" + }, "extensionsMe": { "actions": { "delete": "刪除我的資料", diff --git a/apps/builder/src/app/space/[workspaceId]/templates/[id]/edit/page.tsx b/apps/builder/src/app/space/[workspaceId]/templates/[id]/edit/page.tsx new file mode 100644 index 000000000..11958ddda --- /dev/null +++ b/apps/builder/src/app/space/[workspaceId]/templates/[id]/edit/page.tsx @@ -0,0 +1,59 @@ +import { templateService } from "@chatbotx.io/business" +import { getIdFromParams } from "@chatbotx.io/utils" +import { notFound } from "next/navigation" +import { getTranslations } from "next-intl/server" +import { AppBreadcrumb } from "@/components/app-breadcrumb" +import { TemplateForm } from "@/features/templates/components/template-form" +import { hasWorkspacePermission } from "@/lib/auth/permission-routes" +import { getCurrentUserAndTargetWorkspace } from "@/lib/auth/utils" + +export default async function EditTemplatePage({ + params, +}: { + params: Promise<{ workspaceId: string; id: string }> +}) { + const resolvedParams = await params + const workspaceId = getIdFromParams(resolvedParams, "workspaceId") + const id = getIdFromParams(resolvedParams, "id") + if (!(workspaceId && id)) { + return notFound() + } + + const userAndWorkspace = await getCurrentUserAndTargetWorkspace(workspaceId) + if ( + !( + userAndWorkspace && + hasWorkspacePermission( + userAndWorkspace.targetWorkspaceMember.permissions, + "superAdmin", + ) + ) + ) { + return notFound() + } + + const template = await templateService + .findByIdOrFail({ workspaceId, templateId: id }) + .catch(() => null) + if (!template) { + return notFound() + } + + const t = await getTranslations() + + return ( +
+ + +
+ ) +} diff --git a/apps/builder/src/app/space/[workspaceId]/templates/create/page.tsx b/apps/builder/src/app/space/[workspaceId]/templates/create/page.tsx new file mode 100644 index 000000000..42dbf12c7 --- /dev/null +++ b/apps/builder/src/app/space/[workspaceId]/templates/create/page.tsx @@ -0,0 +1,49 @@ +import { getIdFromParams } from "@chatbotx.io/utils" +import { notFound } from "next/navigation" +import { getTranslations } from "next-intl/server" +import { AppBreadcrumb } from "@/components/app-breadcrumb" +import { TemplateForm } from "@/features/templates/components/template-form" +import { hasWorkspacePermission } from "@/lib/auth/permission-routes" +import { getCurrentUserAndTargetWorkspace } from "@/lib/auth/utils" + +export default async function CreateTemplatePage({ + params, +}: { + params: Promise<{ workspaceId: string }> +}) { + const workspaceId = getIdFromParams(await params, "workspaceId") + if (!workspaceId) { + return notFound() + } + + const userAndWorkspace = await getCurrentUserAndTargetWorkspace(workspaceId) + if ( + !( + userAndWorkspace && + hasWorkspacePermission( + userAndWorkspace.targetWorkspaceMember.permissions, + "superAdmin", + ) + ) + ) { + return notFound() + } + + const t = await getTranslations() + + return ( +
+ + +
+ ) +} diff --git a/apps/builder/src/app/space/[workspaceId]/templates/installs/page.tsx b/apps/builder/src/app/space/[workspaceId]/templates/installs/page.tsx new file mode 100644 index 000000000..d57fa7178 --- /dev/null +++ b/apps/builder/src/app/space/[workspaceId]/templates/installs/page.tsx @@ -0,0 +1,39 @@ +import { templateService } from "@chatbotx.io/business" +import { getIdFromParams } from "@chatbotx.io/utils" +import { notFound } from "next/navigation" +import { getTranslations } from "next-intl/server" +import { AppBreadcrumb } from "@/components/app-breadcrumb" +import { TemplateInstallsTable } from "@/features/templates/components/template-installs-table" + +export default async function TemplateInstallsPage({ + params, +}: { + params: Promise<{ workspaceId: string }> +}) { + const workspaceId = getIdFromParams(await params, "workspaceId") + if (!workspaceId) { + return notFound() + } + + const t = await getTranslations() + const installations = await templateService.listInstallations(workspaceId) + + return ( +
+ + +
+ ) +} diff --git a/apps/builder/src/app/space/[workspaceId]/templates/page.tsx b/apps/builder/src/app/space/[workspaceId]/templates/page.tsx new file mode 100644 index 000000000..732d7c6c4 --- /dev/null +++ b/apps/builder/src/app/space/[workspaceId]/templates/page.tsx @@ -0,0 +1,75 @@ +import { templateService } from "@chatbotx.io/business" +import { Button } from "@chatbotx.io/ui/components/ui/button" +import { getIdFromParams } from "@chatbotx.io/utils" +import { PlusIcon } from "lucide-react" +import Link from "next/link" +import { notFound } from "next/navigation" +import { getTranslations } from "next-intl/server" +import { AppBreadcrumb } from "@/components/app-breadcrumb" +import { TemplatesTable } from "@/features/templates/components/templates-table" +import { hasWorkspacePermission } from "@/lib/auth/permission-routes" +import { getCurrentUserAndTargetWorkspace } from "@/lib/auth/utils" + +export default async function TemplatesPage({ + params, +}: { + params: Promise<{ workspaceId: string }> +}) { + const workspaceId = getIdFromParams(await params, "workspaceId") + if (!workspaceId) { + return notFound() + } + + const userAndWorkspace = await getCurrentUserAndTargetWorkspace(workspaceId) + const isSuperAdmin = Boolean( + userAndWorkspace && + hasWorkspacePermission( + userAndWorkspace.targetWorkspaceMember.permissions, + "superAdmin", + ), + ) + + const t = await getTranslations() + const templates = await templateService.list(workspaceId) + + return ( +
+ +
+

+ {t("templates.description")} +

+
+
+
+ +
+ ) +} diff --git a/apps/builder/src/app/t/[shareToken]/page.tsx b/apps/builder/src/app/t/[shareToken]/page.tsx new file mode 100644 index 000000000..740b83825 --- /dev/null +++ b/apps/builder/src/app/t/[shareToken]/page.tsx @@ -0,0 +1,244 @@ +import { templateService, workspaceMemberService } from "@chatbotx.io/business" +import { getPublicFileUrl } from "@chatbotx.io/utils" +import type { Metadata } from "next" +import { getTranslations } from "next-intl/server" +import { WorkspacePicker } from "@/features/templates/components/workspace-picker" +import { getTenantSettings } from "@/features/tenant/utils" +import { hasWorkspacePermission } from "@/lib/auth/permission-routes" +import { getCurrentUser } from "@/lib/auth/utils" + +type PublicTemplatePageProps = { + params: Promise<{ shareToken: string }> +} + +/** + * Runs the same lookup as the page body so a shared link gets a real OG + * preview — a disabled/expired/unknown token falls back to a generic title + * rather than leaking whether the token ever existed. + */ +export async function generateMetadata( + props: PublicTemplatePageProps, +): Promise { + const { shareToken } = await props.params + const [t, template, { storageUrl }] = await Promise.all([ + getTranslations("templatesPublicPage"), + templateService.findPublicByShareToken(shareToken), + getTenantSettings(), + ]) + if (!template) { + return { title: t("invalidTitle") } + } + const imageUrl = template.imageUrl + ? getPublicFileUrl(template.imageUrl, storageUrl) + : null + return { + title: template.name, + description: template.description ?? undefined, + openGraph: { + title: template.name, + description: template.description ?? undefined, + images: imageUrl ? [imageUrl] : undefined, + }, + } +} + +/** + * Every failure mode — bad token, disabled share, expired — collapses into + * the same generic invalid-link message via one try/catch, mirroring + * `/unsubscribe`. The page must never be a token-existence oracle. + */ +export default async function PublicTemplatePage( + props: PublicTemplatePageProps, +) { + const { shareToken } = await props.params + const [t, tCategories] = await Promise.all([ + getTranslations("templatesPublicPage"), + getTranslations("templates.categories"), + ]) + + const template = await templateService.findPublicByShareToken(shareToken) + if (!template) { + return ( + + ) + } + + const [user, { storageUrl }] = await Promise.all([ + getCurrentUser(), + getTenantSettings(), + ]) + const imageUrl = template.imageUrl + ? getPublicFileUrl(template.imageUrl, storageUrl) + : null + + return ( +
+ {imageUrl ? ( + // biome-ignore lint/performance/noImgElement: external, unoptimized template thumbnail resolved from publisher-uploaded storage + {template.name} + ) : null} +
+

{template.name}

+ {template.publisherName ? ( +

+ {t("byPublisher", { publisherName: template.publisherName })} +

+ ) : null} +
+ {template.description ? ( +

{template.description}

+ ) : null} + {template.youtubeVideoId ? ( +
+