This commit is contained in:
2026-08-17 21:20:45 +02:00
parent 321f11e817
commit 6fcdc74843
157 changed files with 2895 additions and 7544 deletions
+18 -18
View File
@@ -313,15 +313,15 @@ const GAPS = {
pl: "Członkowie zarządzają produktami i feedami. Administratorzy mogą też zapraszać współpracowników i zmieniać ustawienia firmy.",
ja: "メンバーは商品とフィードを管理します。管理者は同僚の招待と会社設定の変更もできます。"
},
"Demo sandbox is empty — switch to A1 or connect a feed to see real catalog stats.": {
es: "La zona de pruebas demo está vacía — cambia a A1 o conecta un feed para ver estadísticas reales del catálogo.",
fr: "Le bac à sable démo est vide — basculez vers A1 ou connectez un flux pour voir de vraies stats catalogue.",
de: "Demo-Sandbox ist leer — wechseln Sie zu A1 oder verbinden Sie einen Feed, um echte Katalogstatistiken zu sehen.",
it: "La sandbox demo è vuota — passa ad A1 o collega un feed per vedere statistiche reali del catalogo.",
pt: "A sandbox de demonstração está vazia — mude para A1 ou ligue um feed para ver estatísticas reais do catálogo.",
nl: "Demo-sandbox is leeg — schakel over naar A1 of koppel een feed om echte catalogusstatistieken te zien.",
pl: "Piaskownica demo jest pusta — przełącz na A1 lub podłącz feed, aby zobaczyć realne statystyki katalogu.",
ja: "デモサンドボックスは空です — A1に切り替えるかフィードを接続して実際のカタログ統計を表示します。"
"Demo sandbox is empty — connect a feed to see catalog stats.": {
es: "La zona de pruebas demo está vacía — conecta un feed para ver estadísticas reales del catálogo.",
fr: "Le bac à sable démo est vide — connectez un flux pour voir de vraies stats catalogue.",
de: "Demo-Sandbox ist leer — verbinden Sie einen Feed, um echte Katalogstatistiken zu sehen.",
it: "La sandbox demo è vuota — collega un feed per vedere statistiche reali del catalogo.",
pt: "A sandbox de demonstração está vazia — ligue um feed para ver estatísticas reais do catálogo.",
nl: "Demo-sandbox is leeg — koppel een feed om echte catalogusstatistieken te zien.",
pl: "Piaskownica demo jest pusta — podłącz feed, aby zobaczyć realne statystyki katalogu.",
ja: "デモサンドボックスは空です — フィードを接続して実際のカタログ統計を表示します。"
},
"Add a feed or upload a CSV to populate this workspace.": {
es: "Añade un feed o sube un CSV para poblar este espacio de trabajo.",
@@ -353,15 +353,15 @@ const GAPS = {
pl: "Importuj → wzbogacaj → publikuj. Przejdź do następnego kroku dla {name}.",
ja: "インポート → 強化 → 公開。{name} の次のステップへ。"
},
"Switch to A1 (or another seeded company) in the header, or connect a feed here to populate this sandbox.": {
es: "Cambia a A1 (u otra empresa con datos) en el encabezado, o conecta un feed aquí para poblar esta zona de pruebas.",
fr: "Basculez vers A1 (ou une autre entreprise seedée) dans l'en-tête, ou connectez un flux ici pour remplir ce bac à sable.",
de: "Wechseln Sie in der Kopfzeile zu A1 (oder einem anderen Seed-Unternehmen) oder verbinden Sie hier einen Feed, um diese Sandbox zu füllen.",
it: "Passa ad A1 (o un'altra azienda con dati) nell'intestazione, oppure collega un feed qui per popolare questa sandbox.",
pt: "Mude para A1 (ou outra empresa com dados) no cabeçalho, ou ligue um feed aqui para preencher esta sandbox.",
nl: "Schakel in de header over naar A1 (of een ander geseeded bedrijf), of koppel hier een feed om deze sandbox te vullen.",
pl: "Przełącz na A1 (lub inną firmę z danymi) w nagłówku albo podłącz tu feed, aby wypełnić tę piaskownicę.",
ja: "ヘッダーでA1(または別のシード済み会社)に切り替えるか、ここでフィードを接続してサンドボックスにデータを入れます。"
"Connect a feed here to populate this sandbox, or switch to another company in the header.": {
es: "Conecta un feed aquí para poblar esta zona de pruebas, o cambia a otra empresa en el encabezado.",
fr: "Connectez un flux ici pour remplir ce bac à sable, ou basculez vers une autre entreprise dans l'en-tête.",
de: "Verbinden Sie hier einen Feed, um diese Sandbox zu füllen, oder wechseln Sie in der Kopfzeile zu einem anderen Unternehmen.",
it: "Collega un feed qui per popolare questa sandbox, oppure passa a un'altra azienda nell'intestazione.",
pt: "Ligue um feed aqui para preencher esta sandbox, ou mude para outra empresa no cabeçalho.",
nl: "Koppel hier een feed om deze sandbox te vullen, of schakel in de header over naar een ander bedrijf.",
pl: "Podłącz tu feed, aby wypełnić tę piaskownicę, albo przełącz na inną firmę w nagłówku.",
ja: "ここでフィードを接続してサンドボックスにデータを入れるか、ヘッダーで別の会社に切り替えます。"
},
"Connect a feed or upload a CSV to start building your catalog.": {
es: "Conecta un feed o sube un CSV para empezar a crear tu catálogo.",
+2 -2
View File
@@ -172,11 +172,11 @@ export const EXTRA = {
"Los miembros gestionan productos y feeds. Los administradores también pueden invitar compañeros y cambiar la configuración de la empresa.",
"settings.sendInvite": "Enviar invitación",
"dashboard.demoEmptyHint":
"La zona de pruebas demo está vacía — cambia a A1 o conecta un feed para ver estadísticas reales del catálogo.",
"La zona de pruebas demo está vacía — conecta un feed para ver estadísticas reales del catálogo.",
"dashboard.workflowHint": "Importar → enriquecer → publicar. Salta al siguiente paso para {name}.",
"dashboard.overviewHint": "Totales en vivo para {name}",
"dashboard.demoEmptyMessage":
"Cambia a A1 (u otra empresa con datos) en el encabezado, o conecta un feed aquí para poblar esta zona de pruebas.",
"Conecta un feed aquí para poblar esta zona de pruebas, o cambia a otra empresa en el encabezado.",
"dashboard.emptyTitle": "Aún no hay datos de catálogo",
"dashboard.emptyMessage": "Conecta un feed o sube un CSV para empezar a crear tu catálogo.",
"dashboard.connectFeedAnyway": "Conectar feed de todos modos",
+16 -16
View File
@@ -142,10 +142,10 @@ export const EXTRA = {
"settings.inviteRole": "Rol",
"settings.inviteRoleHint": "Los miembros gestionan productos y feeds. Los administradores también pueden invitar compañeros y cambiar la configuración de la empresa.",
"settings.sendInvite": "Enviar invitación",
"dashboard.demoEmptyHint": "La zona de pruebas demo está vacía — cambia a A1 o conecta un feed para ver estadísticas reales del catálogo.",
"dashboard.demoEmptyHint": "La zona de pruebas demo está vacía — conecta un feed para ver estadísticas reales del catálogo.",
"dashboard.workflowHint": "Importar → enriquecer → publicar. Salta al siguiente paso para {name}.",
"dashboard.overviewHint": "Totales en vivo para {name}",
"dashboard.demoEmptyMessage": "Cambia a A1 (u otra empresa con datos) en el encabezado, o conecta un feed aquí para poblar esta zona de pruebas.",
"dashboard.demoEmptyMessage": "Conecta un feed aquí para poblar esta zona de pruebas, o cambia a otra empresa en el encabezado.",
"dashboard.emptyTitle": "Aún no hay datos de catálogo",
"dashboard.emptyMessage": "Conecta un feed o sube un CSV para empezar a crear tu catálogo.",
"dashboard.connectFeedAnyway": "Conectar feed de todos modos",
@@ -375,10 +375,10 @@ export const EXTRA = {
"settings.inviteRole": "Rôle",
"settings.inviteRoleHint": "Les membres gèrent les produits et les flux. Les administrateurs peuvent aussi inviter des coéquipiers et modifier les paramètres de l'entreprise.",
"settings.sendInvite": "Envoyer l'invitation",
"dashboard.demoEmptyHint": "Le bac à sable démo est vide — basculez vers A1 ou connectez un flux pour voir de vraies stats catalogue.",
"dashboard.demoEmptyHint": "Le bac à sable démo est vide — connectez un flux pour voir de vraies stats catalogue.",
"dashboard.workflowHint": "Importer → enrichir → publier. Passez à l'étape suivante pour {name}.",
"dashboard.overviewHint": "Totaux en direct pour {name}",
"dashboard.demoEmptyMessage": "Basculez vers A1 (ou une autre entreprise seedée) dans l'en-tête, ou connectez un flux ici pour remplir ce bac à sable.",
"dashboard.demoEmptyMessage": "Connectez un flux ici pour remplir ce bac à sable, ou basculez vers une autre entreprise dans l'en-tête.",
"dashboard.emptyTitle": "Pas encore de données catalogue",
"dashboard.emptyMessage": "Connectez un flux ou téléversez un CSV pour commencer à construire votre catalogue.",
"dashboard.connectFeedAnyway": "Connecter un flux quand même",
@@ -606,10 +606,10 @@ export const EXTRA = {
"settings.inviteRole": "Rolle",
"settings.inviteRoleHint": "Mitglieder verwalten Produkte und Feeds. Admins können auch Teammitglieder einladen und Unternehmenseinstellungen ändern.",
"settings.sendInvite": "Einladung senden",
"dashboard.demoEmptyHint": "Demo-Sandbox ist leer — wechseln Sie zu A1 oder verbinden Sie einen Feed, um echte Katalogstatistiken zu sehen.",
"dashboard.demoEmptyHint": "Demo-Sandbox ist leer — verbinden Sie einen Feed, um echte Katalogstatistiken zu sehen.",
"dashboard.workflowHint": "Importieren → anreichern → veröffentlichen. Zum nächsten Schritt für {name}.",
"dashboard.overviewHint": "Live-Summen für {name}",
"dashboard.demoEmptyMessage": "Wechseln Sie in der Kopfzeile zu A1 (oder einem anderen Seed-Unternehmen) oder verbinden Sie hier einen Feed, um diese Sandbox zu füllen.",
"dashboard.demoEmptyMessage": "Verbinden Sie hier einen Feed, um diese Sandbox zu füllen, oder wechseln Sie in der Kopfzeile zu einem anderen Unternehmen.",
"dashboard.emptyTitle": "Noch keine Katalogdaten",
"dashboard.emptyMessage": "Verbinden Sie einen Feed oder laden Sie eine CSV hoch, um Ihren Katalog aufzubauen.",
"dashboard.connectFeedAnyway": "Feed trotzdem verbinden",
@@ -837,10 +837,10 @@ export const EXTRA = {
"settings.inviteRole": "Ruolo",
"settings.inviteRoleHint": "I membri gestiscono prodotti e feed. Gli amministratori possono anche invitare colleghi e modificare le impostazioni dell'azienda.",
"settings.sendInvite": "Invia invito",
"dashboard.demoEmptyHint": "La sandbox demo è vuota — passa ad A1 o collega un feed per vedere statistiche reali del catalogo.",
"dashboard.demoEmptyHint": "La sandbox demo è vuota — collega un feed per vedere statistiche reali del catalogo.",
"dashboard.workflowHint": "Importa → arricchisci → pubblica. Vai al passo successivo per {name}.",
"dashboard.overviewHint": "Totali in tempo reale per {name}",
"dashboard.demoEmptyMessage": "Passa ad A1 (o un'altra azienda con dati) nell'intestazione, oppure collega un feed qui per popolare questa sandbox.",
"dashboard.demoEmptyMessage": "Collega un feed qui per popolare questa sandbox, oppure passa a un'altra azienda nell'intestazione.",
"dashboard.emptyTitle": "Ancora nessun dato di catalogo",
"dashboard.emptyMessage": "Collega un feed o carica un CSV per iniziare a costruire il catalogo.",
"dashboard.connectFeedAnyway": "Collega comunque un feed",
@@ -1068,10 +1068,10 @@ export const EXTRA = {
"settings.inviteRole": "Papel",
"settings.inviteRoleHint": "Os membros gerem produtos e feeds. Os administradores também podem convidar colegas e alterar as definições da empresa.",
"settings.sendInvite": "Enviar convite",
"dashboard.demoEmptyHint": "A sandbox de demonstração está vazia — mude para A1 ou ligue um feed para ver estatísticas reais do catálogo.",
"dashboard.demoEmptyHint": "A sandbox de demonstração está vazia — ligue um feed para ver estatísticas reais do catálogo.",
"dashboard.workflowHint": "Importar → enriquecer → publicar. Salte para o passo seguinte para {name}.",
"dashboard.overviewHint": "Totais em direto para {name}",
"dashboard.demoEmptyMessage": "Mude para A1 (ou outra empresa com dados) no cabeçalho, ou ligue um feed aqui para preencher esta sandbox.",
"dashboard.demoEmptyMessage": "Ligue um feed aqui para preencher esta sandbox, ou mude para outra empresa no cabeçalho.",
"dashboard.emptyTitle": "Ainda sem dados de catálogo",
"dashboard.emptyMessage": "Ligue um feed ou carregue um CSV para começar a criar o seu catálogo.",
"dashboard.connectFeedAnyway": "Ligar feed mesmo assim",
@@ -1299,10 +1299,10 @@ export const EXTRA = {
"settings.inviteRole": "Rol",
"settings.inviteRoleHint": "Leden beheren producten en feeds. Beheerders kunnen ook teamleden uitnodigen en bedrijfsinstellingen wijzigen.",
"settings.sendInvite": "Uitnodiging verzenden",
"dashboard.demoEmptyHint": "Demo-sandbox is leeg — schakel over naar A1 of koppel een feed om echte catalogusstatistieken te zien.",
"dashboard.demoEmptyHint": "Demo-sandbox is leeg — koppel een feed om echte catalogusstatistieken te zien.",
"dashboard.workflowHint": "Importeren → verrijken → publiceren. Ga naar de volgende stap voor {name}.",
"dashboard.overviewHint": "Live totalen voor {name}",
"dashboard.demoEmptyMessage": "Schakel in de header over naar A1 (of een ander geseeded bedrijf), of koppel hier een feed om deze sandbox te vullen.",
"dashboard.demoEmptyMessage": "Koppel hier een feed om deze sandbox te vullen, of schakel in de header over naar een ander bedrijf.",
"dashboard.emptyTitle": "Nog geen catalogusgegevens",
"dashboard.emptyMessage": "Koppel een feed of upload een CSV om uw catalogus op te bouwen.",
"dashboard.connectFeedAnyway": "Feed toch koppelen",
@@ -1530,10 +1530,10 @@ export const EXTRA = {
"settings.inviteRole": "Rola",
"settings.inviteRoleHint": "Członkowie zarządzają produktami i feedami. Administratorzy mogą też zapraszać współpracowników i zmieniać ustawienia firmy.",
"settings.sendInvite": "Wyślij zaproszenie",
"dashboard.demoEmptyHint": "Piaskownica demo jest pusta — przełącz na A1 lub podłącz feed, aby zobaczyć realne statystyki katalogu.",
"dashboard.demoEmptyHint": "Piaskownica demo jest pusta — podłącz feed, aby zobaczyć realne statystyki katalogu.",
"dashboard.workflowHint": "Importuj → wzbogacaj → publikuj. Przejdź do następnego kroku dla {name}.",
"dashboard.overviewHint": "Bieżące sumy dla {name}",
"dashboard.demoEmptyMessage": "Przełącz na A1 (lub inną firmę z danymi) w nagłówku albo podłącz tu feed, aby wypełnić tę piaskownicę.",
"dashboard.demoEmptyMessage": "Podłącz tu feed, aby wypełnić tę piaskownicę, albo przełącz na inną firmę w nagłówku.",
"dashboard.emptyTitle": "Brak jeszcze danych katalogu",
"dashboard.emptyMessage": "Podłącz feed lub prześlij CSV, aby zacząć budować katalog.",
"dashboard.connectFeedAnyway": "Podłącz feed mimo to",
@@ -1761,10 +1761,10 @@ export const EXTRA = {
"settings.inviteRole": "ロール",
"settings.inviteRoleHint": "メンバーは商品とフィードを管理します。管理者は同僚の招待と会社設定の変更もできます。",
"settings.sendInvite": "招待を送信",
"dashboard.demoEmptyHint": "デモサンドボックスは空です — A1に切り替えるかフィードを接続して実際のカタログ統計を表示します。",
"dashboard.demoEmptyHint": "デモサンドボックスは空です — フィードを接続して実際のカタログ統計を表示します。",
"dashboard.workflowHint": "インポート → 強化 → 公開。{name} の次のステップへ。",
"dashboard.overviewHint": "{name} のリアルタイム合計",
"dashboard.demoEmptyMessage": "ヘッダーでA1(または別のシード済み会社)に切り替えるか、ここでフィードを接続してサンドボックスにデータを入れます。",
"dashboard.demoEmptyMessage": "ここでフィードを接続してサンドボックスにデータを入れるか、ヘッダーで別の会社に切り替えます。",
"dashboard.emptyTitle": "まだカタログデータがありません",
"dashboard.emptyMessage": "フィードを接続するかCSVをアップロードして、カタログの構築を開始します。",
"dashboard.connectFeedAnyway": "それでもフィードを接続",
+20 -20
View File
@@ -219,15 +219,15 @@
"pl": "Najpierw ustaw hasło:",
"ja": "先にパスワードを設定:"
},
"use the invite link from your email. If the link went to an old address (email drift), ask a company admin to re-issue a set-password invite to {email}.": {
"es": "usa el enlace de invitación de tu correo. Si el enlace fue a una dirección antigua (cambio de correo), pide a un administrador de la empresa que emita una nueva invitación para establecer contraseña a {email}.",
"fr": "utilisez le lien d'invitation de votre e-mail. Si le lien a été envoyé à une ancienne adresse (dérive d'e-mail), demandez à un administrateur de l'entreprise de renvoyer une invitation de définition de mot de passe à {email}.",
"de": "nutzen Sie den Einladungslink aus Ihrer E-Mail. Wenn der Link an eine alte Adresse ging (E-Mail-Drift), bitten Sie einen Unternehmens-Admin, eine neue Passwort-Einladung an {email} auszustellen.",
"it": "usa il link di invito dalla tua email. Se il link è andato a un indirizzo vecchio (deriva email), chiedi a un amministratore dell'azienda di riemettere un invito per impostare la password a {email}.",
"pt": "utilize o link do convite do seu e-mail. Se o link foi para um endereço antigo (desvio de e-mail), peça a um administrador da empresa para emitir um novo convite de definição de palavra-passe para {email}.",
"nl": "gebruik de uitnodigingslink uit uw e-mail. Als de link naar een oud adres ging (e-maildrift), vraag dan een bedrijfsbeheerder om een nieuwe set-wachtwoorduitnodiging naar {email} te sturen.",
"pl": "użyj linku zaproszenia z e-maila. Jeśli link poszedł na stary adres (dryf e-mail), poproś administratora firmy o ponowne wystawienie zaproszenia do ustawienia hasła na {email}.",
"ja": "メールの招待リンクを使用してください。リンクが古いアドレスに送られた場合(メール変更)、会社の管理者に {email} 向けのパスワード設定招待の再発行を依頼してください。"
"use the invite link from your email. If you need a new link, ask a company admin to re-issue a set-password link to {email}.": {
"es": "usa el enlace de invitación de tu correo. Si necesitas un enlace nuevo, pide a un administrador de la empresa que reemita un enlace para establecer contraseña a {email}.",
"fr": "utilisez le lien d'invitation de votre e-mail. Si vous avez besoin d'un nouveau lien, demandez à un administrateur de l'entreprise de réémettre un lien de définition de mot de passe à {email}.",
"de": "nutzen Sie den Einladungslink aus Ihrer E-Mail. Wenn Sie einen neuen Link brauchen, bitten Sie einen Unternehmens-Admin, einen Link zum Festlegen des Passworts an {email} auszustellen.",
"it": "usa il link di invito dalla tua email. Se ti serve un nuovo link, chiedi a un amministratore dell'azienda di riemettere un link per impostare la password a {email}.",
"pt": "utilize o link do convite do seu e-mail. Se precisar de um novo link, peça a um administrador da empresa para reemitir um link de definição de palavra-passe para {email}.",
"nl": "gebruik de uitnodigingslink uit uw e-mail. Als u een nieuwe link nodig hebt, vraag dan een bedrijfsbeheerder om een set-wachtwoordlink opnieuw uit te geven naar {email}.",
"pl": "użyj linku zaproszenia z e-maila. Jeśli potrzebujesz nowego linku, poproś administratora firmy o ponowne wystawienie linku do ustawienia hasła na {email}.",
"ja": "メールの招待リンクを使用してください。新しいリンクが必要な場合は、会社の管理者に {email} 向けのパスワード設定リンクの再発行を依頼してください。"
},
"your email": {
"es": "tu correo",
@@ -549,15 +549,15 @@
"pl": "To zaproszenie jest nieprawidłowe lub wygasło. Poproś administratora firmy o nowe zaproszenie, a następnie otwórz nowy link (lub wklej nowy token poniżej).",
"ja": "この招待は無効または期限切れです。会社の管理者に新しい招待の送信を依頼し、新しいリンクを開くか(下に新しいトークンを貼り付けてください)。"
},
"This set-password link is invalid or expired. Ask a company or platform admin to re-issue it, then open the new link (or paste the new token below).": {
"es": "Este enlace para establecer contraseña no es válido o ha caducado. Pide a un administrador de la empresa o de la plataforma que lo reemita y abre el nuevo enlace (o pega el nuevo token abajo).",
"fr": "Ce lien de définition de mot de passe est invalide ou expiré. Demandez à un administrateur de l'entreprise ou de la plateforme de le réémettre, puis ouvrez le nouveau lien (ou collez le nouveau jeton ci-dessous).",
"de": "Dieser Passwort-Link ist ungültig oder abgelaufen. Bitten Sie einen Unternehmens- oder Plattform-Admin um Neuausstellung und öffnen Sie den neuen Link (oder fügen Sie das neue Token unten ein).",
"it": "Questo link per impostare la password non è valido o è scaduto. Chiedi a un amministratore dell'azienda o della piattaforma di riemetterlo, poi apri il nuovo link (o incolla il nuovo token qui sotto).",
"pt": "Este link de definição de palavra-passe é inválido ou expirou. Peça a um administrador da empresa ou da plataforma para o reemitir e abra o novo link (ou cole o novo token abaixo).",
"nl": "Deze set-wachtwoordlink is ongeldig of verlopen. Vraag een bedrijfs- of platformbeheerder om hem opnieuw uit te geven en open de nieuwe link (of plak het nieuwe token hieronder).",
"pl": "Ten link do ustawienia hasła jest nieprawidłowy lub wygasł. Poproś administratora firmy lub platformy o ponowne wystawienie, a następnie otwórz nowy link (lub wklej nowy token poniżej).",
"ja": "このパスワード設定リンクは無効または期限切れです。会社またはプラットフォームの管理者に再発行を依頼し、新しいリンクを開くか(下に新しいトークンを貼り付けてください)。"
"This set-password link is invalid or expired. Ask a company admin to re-issue a set-password link to this email, then open the new link (or paste the new token below).": {
"es": "Este enlace para establecer contraseña no es válido o ha caducado. Pide a un administrador de la empresa que reemita un enlace para establecer contraseña a este correo y abre el nuevo enlace (o pega el nuevo token abajo).",
"fr": "Ce lien de définition de mot de passe est invalide ou expiré. Demandez à un administrateur de l'entreprise de réémettre un lien de définition de mot de passe vers cet e-mail, puis ouvrez le nouveau lien (ou collez le nouveau jeton ci-dessous).",
"de": "Dieser Passwort-Link ist ungültig oder abgelaufen. Bitten Sie einen Unternehmens-Admin, einen Link zum Festlegen des Passworts an diese E-Mail auszustellen, und öffnen Sie den neuen Link (oder fügen Sie das neue Token unten ein).",
"it": "Questo link per impostare la password non è valido o è scaduto. Chiedi a un amministratore dell'azienda di riemettere un link per impostare la password a questa email, poi apri il nuovo link (o incolla il nuovo token qui sotto).",
"pt": "Este link de definição de palavra-passe é inválido ou expirou. Peça a um administrador da empresa para reemitir um link de definição de palavra-passe para este e-mail e abra o novo link (ou cole o novo token abaixo).",
"nl": "Deze set-wachtwoordlink is ongeldig of verlopen. Vraag een bedrijfsbeheerder om een set-wachtwoordlink opnieuw uit te geven naar dit e-mailadres en open de nieuwe link (of plak het nieuwe token hieronder).",
"pl": "Ten link do ustawienia hasła jest nieprawidłowy lub wygasł. Poproś administratora firmy o ponowne wystawienie linku do ustawienia hasła na ten e-mail, a następnie otwórz nowy link (lub wklej nowy token poniżej).",
"ja": "このパスワード設定リンクは無効または期限切れです。会社の管理者にこのメール向けのパスワード設定リンクの再発行を依頼し、新しいリンクを開くか(下に新しいトークンを貼り付けてください)。"
},
"You're signed in as a different email than this invite.": {
"es": "Has iniciado sesión con un correo distinto al de esta invitación.",
@@ -569,7 +569,7 @@
"pl": "Jesteś zalogowany na inny e-mail niż w tym zaproszeniu.",
"ja": "この招待とは別のメールアドレスでログインしています。"
},
"Expired link? Ask an admin to re-issue — there is no self-serve resend API. Platform admins:": {
"Expired link? Ask a company admin to re-issue — there is no self-serve resend API.": {
"es": "¿Enlace caducado? Pide a un administrador que lo reemita — no hay API de reenvío autoservicio. Administradores de plataforma:",
"fr": "Lien expiré ? Demandez à un administrateur de le réémettre — il n'y a pas d'API de renvoi en libre-service. Administrateurs de plateforme :",
"de": "Abgelaufener Link? Bitten Sie einen Admin um Neuausstellung — es gibt keine Self-Service-API zum erneuten Senden. Plattform-Admins:",
@@ -729,7 +729,7 @@
"pl": "Ścieżka ponownego wystawienia:",
"ja": "再発行の手順:"
},
"if your real login email changed (email drift), ask a company admin to revoke this invite and send a new one to the email you use to sign in. Platform admins can also re-issue set-password links from Admin → Users.": {
"ask a company admin to re-issue a set-password link to this email, or to revoke this invite and send a new one to the email you use to sign in.": {
"es": "si cambió tu correo real de acceso (desfase de correo), pide a un administrador de la empresa que revoque esta invitación y envíe una nueva al correo con el que inicias sesión. Los administradores de plataforma también pueden reemitir enlaces para establecer contraseña desde Admin → Usuarios.",
"fr": "si votre vrai e-mail de connexion a changé (dérive d'e-mail), demandez à un administrateur de l'entreprise de révoquer cette invitation et d'en envoyer une nouvelle à l'e-mail que vous utilisez pour vous connecter. Les administrateurs de la plateforme peuvent aussi réémettre des liens de définition de mot de passe depuis Admin → Utilisateurs.",
"de": "wenn sich Ihre echte Anmelde-E-Mail geändert hat (E-Mail-Drift), bitten Sie einen Unternehmens-Admin, diese Einladung zu widerrufen und eine neue an die E-Mail zu senden, mit der Sie sich anmelden. Plattform-Admins können Passwort-Links auch unter Admin → Benutzer erneut ausstellen.",
+13 -8
View File
@@ -57,19 +57,24 @@ export const handle: Handle = async ({ event, resolve }) => {
}
const pathname = event.url.pathname;
const authCredentialPath =
pathname === "/login" ||
pathname === "/register" ||
pathname === "/accept-invite" ||
pathname === "/forgot-password" ||
pathname === "/reset-password";
// Never keep credentials in the query string (e.g. native GET before hydration).
if (
(pathname === "/login" ||
pathname === "/register" ||
pathname === "/accept-invite" ||
pathname === "/forgot-password" ||
pathname === "/reset-password") &&
event.url.searchParams.has("password")
) {
if (authCredentialPath && event.url.searchParams.has("password")) {
const clean = new URL(event.url);
clean.searchParams.delete("password");
clean.searchParams.delete("token");
redirect(303, `${clean.pathname}${clean.search}`);
}
// Redact invite/reset tokens from SSR page.url. Do not 303-strip: old emails
// still send ?token= and the client reads window.location once, then replaceState.
if (authCredentialPath && event.url.searchParams.has("token")) {
event.url.searchParams.delete("token");
}
const isRapiDocVendor = pathname.startsWith("/vendor/rapidoc/");
// Serve precompressed RapiDoc when client accepts gzip (copy-rapidoc-ui.mjs).
+3 -2
View File
@@ -15,6 +15,7 @@
import { browser } from "$app/environment";
import { env } from "$env/dynamic/public";
import { redactSensitiveUrl } from "./auth-link-token";
import {
CONSENT_STORAGE_KEY,
CONSENT_WAIT_FOR_UPDATE_MS,
@@ -165,9 +166,9 @@ export function trackPageview(path: string, opts?: { title?: string; location?:
if (!isAnalyticsGranted()) return;
pushDataLayer({
event: "page_view",
page_path: path,
page_path: redactSensitiveUrl(path),
page_title: opts?.title ?? document.title,
page_location: opts?.location ?? window.location.href
page_location: redactSensitiveUrl(opts?.location ?? window.location.href)
});
}
+13 -4
View File
@@ -90,13 +90,21 @@ describe("System assistant matching", () => {
["ai integrations", "open_ai_integrations"],
["email sending", "open_email_integrations"],
["usage and billing", "open_billing"],
["company settings", "open_settings"],
["platform admin", "open_admin"]
["company settings", "open_settings"]
] as const;
for (const [utterance, id] of cases) {
assert.equal(matchIntent(utterance)?.intent.id, id, utterance);
}
});
it("does not send customers to platform admin", () => {
assert.notEqual(matchIntent("platform admin")?.intent.id, "open_admin");
assert.notEqual(matchIntent("go to admin")?.intent.id, "open_admin");
});
it("matches open_admin only when staff intents are allowed", () => {
assert.equal(matchIntent("platform admin", { allowStaffIntents: true })?.intent.id, "open_admin");
});
});
describe("bot / AI identity answers", () => {
@@ -114,10 +122,11 @@ describe("bot / AI identity answers", () => {
assert.equal(matchIntent("are you an LLM chatbot")?.intent.id, "identity_system");
});
it("identity reply says System assistant and denies LLM chatbot", () => {
it("identity reply says System assistant without LLM chatbot wording", () => {
const msg = buildIdentityReply();
assert.match(msg.text, /System assistant/i);
assert.match(msg.text, /not an LLM chatbot/i);
assert.match(msg.text, /navigate Descrybe/i);
assert.doesNotMatch(msg.text, /LLM chatbot/i);
assert.doesNotMatch(msg.text, /\bno LLM\b/i);
assert.doesNotMatch(msg.text, /\bno AI\b/i);
});
+1 -1
View File
@@ -75,7 +75,7 @@ export function buildIdentityReply(): AssistantMessage {
return makeMessage({
role: "assistant",
kind: "text",
text: "I am not an LLM chatbot. I am the System assistant — I help you navigate Descrybe, run supported actions after you confirm, and guide setup using built-in workflows."
text: "I am the System assistant — I help you navigate Descrybe, run supported actions after you confirm, and guide setup using built-in workflows."
});
}
+1
View File
@@ -13,6 +13,7 @@ export type {
export { INTENT_REGISTRY, intentById, QUICK_START_REPLIES } from "./intents.ts";
export { matchIntent, matchIdentityQuestion, normalizeUtterance, extractUrl } from "./match.ts";
export type { MatchIntentOptions } from "./match.ts";
export {
makeMessage,
idleFlow,
+1
View File
@@ -481,6 +481,7 @@ export const INTENT_REGISTRY: IntentDefinition[] = [
description: "Navigate to Platform admin (staff only).",
requiresConfirm: false,
canExecute: false,
staffOnly: true,
route: "/admin",
selector: '[data-assistant-target="nav-admin"],[data-tour="nav-admin"]',
guideSteps: [{ title: "You're on Platform admin", detail: "Users, billing, support, and gates" }]
+7 -1
View File
@@ -1,6 +1,11 @@
import { INTENT_REGISTRY } from "./intents.ts";
import type { IntentMatch } from "./types.ts";
export type MatchIntentOptions = {
/** Include staffOnly intents such as open_admin. Default false so customers are not sent to /admin. */
allowStaffIntents?: boolean;
};
const URL_RE = /https?:\/\/[^\s<>"']+/i;
/** Normalize for phrase/keyword matching. */
@@ -40,7 +45,7 @@ export function matchIdentityQuestion(raw: string): boolean {
* Score intents by phrase containment and keyword hits.
* Returns null when nothing clears the confidence floor.
*/
export function matchIntent(raw: string): IntentMatch | null {
export function matchIntent(raw: string, opts?: MatchIntentOptions): IntentMatch | null {
const text = normalizeUtterance(raw);
if (!text) return null;
@@ -54,6 +59,7 @@ export function matchIntent(raw: string): IntentMatch | null {
for (const intent of INTENT_REGISTRY) {
if (intent.id === "identity_system") continue;
if (intent.staffOnly && !opts?.allowStaffIntents) continue;
let score = 0;
for (const phrase of intent.phrases) {
+4 -1
View File
@@ -1,4 +1,5 @@
import { trackEvent } from "$lib/analytics";
import { authSession } from "$lib/auth-session.svelte";
import { intentById, QUICK_START_REPLIES } from "./intents.ts";
import {
buildApiExamplesMessage,
@@ -557,7 +558,9 @@ function createAssistantController() {
return;
}
const matched = matchIntent(text);
const matched = matchIntent(text, {
allowStaffIntents: authSession.isSupportDesk
});
if (!matched) {
push(buildUnknownReply());
return;
+2
View File
@@ -100,6 +100,8 @@ export type IntentDefinition = {
/** Prefer data-assistant-target; falls back to data-tour. */
selector?: string;
guideSteps: AssistantStep[];
/** When true, omit from matchIntent unless allowStaffIntents. */
staffOnly?: boolean;
};
export type IntentMatch = {
+69
View File
@@ -0,0 +1,69 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { consumeAuthLinkSecrets, redactSensitiveUrl, tokenFromHash } from "./auth-link-token.ts";
describe("tokenFromHash", () => {
it("reads token from hash params", () => {
assert.equal(tokenFromHash("#token=abc123"), "abc123");
assert.equal(tokenFromHash("token=abc123&mode=set-password"), "abc123");
assert.equal(tokenFromHash("#mode=set-password"), "");
});
});
describe("consumeAuthLinkSecrets", () => {
it("prefers hash token over query and strips both", () => {
const got = consumeAuthLinkSecrets(
"https://app.example.com/accept-invite?token=old&mode=set-password#token=fresh&mode=set-password"
);
assert.equal(got.token, "fresh");
assert.equal(got.mode, "set-password");
assert.equal(got.cleanedPathSearch, "/accept-invite?mode=set-password");
assert.equal(got.stripped, true);
});
it("accepts legacy query token once then strips it", () => {
const got = consumeAuthLinkSecrets(
"https://app.example.com/accept-invite?token=legacyTok&mode=set-password"
);
assert.equal(got.token, "legacyTok");
assert.equal(got.mode, "set-password");
assert.equal(got.cleanedPathSearch, "/accept-invite?mode=set-password");
assert.equal(got.stripped, true);
});
it("reads invite hash without query", () => {
const got = consumeAuthLinkSecrets("https://app.example.com/accept-invite#token=invTok");
assert.equal(got.token, "invTok");
assert.equal(got.mode, "");
assert.equal(got.cleanedPathSearch, "/accept-invite");
assert.equal(got.stripped, true);
});
it("leaves the URL alone when no token is present", () => {
const got = consumeAuthLinkSecrets("https://app.example.com/accept-invite");
assert.equal(got.token, "");
assert.equal(got.stripped, false);
assert.equal(got.cleanedPathSearch, "/accept-invite");
});
});
describe("redactSensitiveUrl", () => {
it("redacts query token and password", () => {
assert.equal(
redactSensitiveUrl("/accept-invite?token=secret&mode=set-password"),
"/accept-invite?token=REDACTED&mode=set-password"
);
});
it("redacts hash token and docs API keys in page_location", () => {
const got = redactSensitiveUrl(
"https://app.example.com/accept-invite#token=secret&mode=set-password"
);
assert.equal(got.includes("secret"), false);
assert.match(got, /token=REDACTED/);
assert.equal(
redactSensitiveUrl("https://app.example.com/docs?k=dk_abcdefghijklmnop"),
"https://app.example.com/docs?k=dk_REDACTED"
);
});
});
+73
View File
@@ -0,0 +1,73 @@
/** Parse invite / reset secrets from a URL hash (`#token=` / `#token=&mode=`). */
export function paramsFromHash(hash: string): URLSearchParams {
const raw = hash.startsWith("#") ? hash.slice(1) : hash;
return new URLSearchParams(raw);
}
export function tokenFromHash(hash: string): string {
return (paramsFromHash(hash).get("token") ?? "").trim();
}
export type AuthLinkMode = "invite" | "set-password";
/**
* Read token from hash (preferred) or query (legacy emails), then strip both
* from the address bar. Mode may live in hash or query; only `set-password` is special.
*/
export function consumeAuthLinkSecrets(href: string): {
token: string;
mode: AuthLinkMode | "";
cleanedPathSearch: string;
stripped: boolean;
} {
const url = new URL(href);
const hashParams = paramsFromHash(url.hash);
const fromHash = (hashParams.get("token") ?? "").trim();
const fromQuery = (url.searchParams.get("token") ?? "").trim();
const token = fromHash || fromQuery;
const rawMode = (hashParams.get("mode") ?? url.searchParams.get("mode") ?? "")
.trim()
.toLowerCase();
const mode: AuthLinkMode | "" = rawMode === "set-password" ? "set-password" : "";
const hadQueryToken = url.searchParams.has("token");
url.searchParams.delete("token");
url.hash = "";
return {
token,
mode,
cleanedPathSearch: `${url.pathname}${url.search}`,
stripped: Boolean(fromHash) || hadQueryToken
};
}
const SENSITIVE_QUERY_KEYS = new Set(["token", "password"]);
/** Redact query/hash secrets and docs API keys from URLs sent to GTM. */
export function redactSensitiveUrl(input: string): string {
if (!input) return input;
const redactDk = (value: string) => value.replace(/\bdk_[A-Za-z0-9_-]{8,}\b/g, "dk_REDACTED");
try {
const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(input);
const u = hasScheme ? new URL(input) : new URL(input, "https://redact.invalid");
for (const key of [...u.searchParams.keys()]) {
if (SENSITIVE_QUERY_KEYS.has(key.toLowerCase())) {
u.searchParams.set(key, "REDACTED");
}
}
if (u.hash) {
const hp = new URLSearchParams(u.hash.startsWith("#") ? u.hash.slice(1) : u.hash);
let hashChanged = false;
for (const key of [...hp.keys()]) {
if (SENSITIVE_QUERY_KEYS.has(key.toLowerCase())) {
hp.set(key, "REDACTED");
hashChanged = true;
}
}
if (hashChanged) u.hash = hp.toString();
}
const out = hasScheme ? u.toString() : `${u.pathname}${u.search}${u.hash}`;
return redactDk(out);
} catch {
return redactDk(input.replace(/([?&#](?:token|password)=)[^&]*/gi, "$1REDACTED"));
}
}
@@ -28,17 +28,17 @@ export const SECTION_MARKERS: Record<
/** Canonical shared intro (same idea as CategoryEnhanceUserTemplate preamble). */
export const DEFAULT_ENHANCE_PREAMBLE =
'Your reply is parsed as JSON {"name":"string","description":"string","meta_title":"string","meta_description":"string","attrs":{}} only (system schema). Write all string fields in {{language}} (do not hardcode a language).';
"Write all product text in {{language}} (do not hardcode a language). Keep the title, description, SEO meta, and attributes consistent with the product evidence below.";
/** Minimal role bodies used when a section is empty on compose (keeps markers valid). */
export const DEFAULT_SECTION_BODIES: Record<EnhancePromptSectionId, string> = {
title:
'Role: title. Build JSON "name": short retail title from Title formula + Attrs — never brand-only. Include product type and full model when evidence exists; follow any Title formula constraints that follow; use Attrs.\nName: {{name}}',
"Write a short retail product title from the title formula and attributes — never brand-only. Include product type and full model when evidence exists; follow any title formula constraints that follow.\nName: {{name}}",
description:
'Role: description. Build JSON "description": product body HTML only (not SEO meta). When a Description formula follows, emit ONE HTML string covering each section in order; otherwise prefer 1-3 factual paragraphs as ONE string with limited HTML (<h2><p><ul><li>).\nDescription: {{description}}',
meta: 'Role: meta. Build JSON "meta_title" and "meta_description" as plain SEO text (never HTML). meta_title: 50-60 chars; meta_description: 120-155 chars; follow any SEO meta formula that follows; never copy the full description HTML into meta_description.',
"Write the product description as HTML (not SEO meta). When a description formula follows, cover each section in order as one HTML string; otherwise prefer 13 factual paragraphs with simple HTML (<h2><p><ul><li>).\nDescription: {{description}}",
meta: "Write the SEO title (5060 characters) and SEO description (120155 characters) as plain text, never HTML. Follow any SEO meta formula that follows; do not copy the full description into the SEO description.",
attributes:
'Role: attributes. Build JSON "attrs" as an object of attribute_key → value strings. Prefer Allowed attribute keys / Title formula attr slots that follow; remap near-miss labels onto those keys; fill missing keys only from Name/Description/Category/Attrs evidence; never invent specs; omit unknown keys; never invent dimensions.\nCategory: {{category}}\nAttrs: {{attrs}}'
"Fill product attributes as key/value pairs. Prefer allowed attribute keys and title-formula slots; remap near-miss labels onto those keys; fill missing keys only from name, description, category, and attributes evidence; never invent specs or dimensions; omit unknown keys.\nCategory: {{category}}\nAttrs: {{attrs}}"
};
export type SectionSchemaHint = {
@@ -31,7 +31,6 @@
const companyHint = $derived.by(() => {
const n = companyName;
if (/^platform demo$/i.test(n) || /^demo$/i.test(n)) return "Platform Demo";
if (n === "Local Demo Co" || /^a1(\s|$)/i.test(n)) return "A1 Slovenija";
return n;
});
const impersonating = $derived(Boolean(me.impersonating));
@@ -129,15 +129,9 @@
</div>
<p class="mx-auto mt-6 max-w-3xl text-center text-sm text-text-muted">
{i18n.t("pricing.section.publicBefore")}
<a href="/plans" class="font-medium text-link hover:underline"
>{i18n.t("pricing.section.plansLink")}</a
>
{i18n.t("pricing.section.publicOr")}
<a href="/billing" class="font-medium text-link hover:underline"
>{i18n.t("pricing.section.billingLink")}</a
>
{i18n.t("pricing.section.publicAfter")}
{i18n.t("pricing.section.guestPublicBefore")}
<a href="/login" class="font-medium text-link hover:underline">{i18n.t("site.logIn")}</a>
{i18n.t("pricing.section.guestPublicAfter")}
</p>
<Card class="mt-12 border-border bg-surface">
+39
View File
@@ -132,5 +132,44 @@ export function validateDocsGuideTree(tree: DocsGuideTree): string[] {
errors.push(`escapeLinks docs href must use RapiDoc ids (no braces): ${link.href}`);
}
}
collectForbiddenDocsCopy(tree, errors);
return errors;
}
function rejectForbiddenDocsCopy(where: string, text: string | undefined, errors: string[]): void {
if (!text) return;
if (/legacy/i.test(text)) {
errors.push(`${where} must not mention legacy`);
}
if (/\bA1\b/.test(text)) {
errors.push(`${where} must not mention A1`);
}
}
function collectForbiddenDocsCopy(tree: DocsGuideTree, errors: string[]): void {
rejectForbiddenDocsCopy("disclaimer", tree.disclaimer, errors);
for (const link of tree.escapeLinks) {
rejectForbiddenDocsCopy(`escapeLinks "${link.href}"`, link.label, errors);
}
for (const [id, node] of Object.entries(tree.nodes)) {
rejectForbiddenDocsCopy(`node "${id}" title`, node.title, errors);
rejectForbiddenDocsCopy(`node "${id}" body`, node.body, errors);
if (node.kind === "question") {
for (const c of node.choices) {
rejectForbiddenDocsCopy(`question "${id}" choice "${c.id}" label`, c.label, errors);
rejectForbiddenDocsCopy(`question "${id}" choice "${c.id}" description`, c.description, errors);
}
continue;
}
rejectForbiddenDocsCopy(`answer "${id}" outcome`, node.outcome, errors);
rejectForbiddenDocsCopy(`answer "${id}" tip`, node.tip, errors);
rejectForbiddenDocsCopy(`answer "${id}" warning`, node.warning, errors);
rejectForbiddenDocsCopy(`answer "${id}" authNote`, node.authNote, errors);
for (const ep of node.endpoints ?? []) {
rejectForbiddenDocsCopy(`answer "${id}" endpoint ${ep.method} ${ep.path}`, ep.summary, errors);
}
for (const link of node.links ?? []) {
rejectForbiddenDocsCopy(`answer "${id}" link "${link.href}"`, link.label, errors);
}
}
}
+26 -26
View File
@@ -145,8 +145,8 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
body: "Do not mix bodies or response envelopes between these two surfaces.",
choices: [
{
id: "legacy",
label: "Legacy EAN batch",
id: "ean",
label: "EAN batch",
description: "POST /products/process · { data: { process_id } }",
nextId: "answer.process.legacy"
},
@@ -300,7 +300,7 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
endpoints: [
endpoint("GET", "/health", "Liveness (also /healthz and /readyz on the API host)"),
endpoint("GET", "/openapi.yaml", "This OpenAPI document"),
endpoint("GET", "/products", "List products (legacy { data, meta } envelope)")
endpoint("GET", "/products", "List products ({ data, meta } envelope)")
],
links: [
{ label: "Settings → API keys", href: "/settings?tab=api-keys", kind: "app" },
@@ -325,7 +325,7 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
endpoints: [
endpoint("GET", "/health", "No key required — confirm the viewer reaches the API"),
endpoint("GET", "/products", "Authenticated smoke list (page=1&limit=1)"),
endpoint("POST", "/products/process", "Legacy process — after Authorize"),
endpoint("POST", "/products/process", "EAN batch — after Authorize"),
endpoint("POST", "/process", "Jobs process — after Authorize")
],
links: [
@@ -417,20 +417,20 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
"answer.process.legacy": {
kind: "answer",
id: "answer.process.legacy",
pathKey: "process.legacy",
pathKey: "process.ean",
synonyms: ["ean", "items[].ean", "products/process"],
title: "Legacy public process (EAN batch)",
outcome: "Start and poll enrichment using the legacy Descrybe contract.",
title: "Public process (EAN batch)",
outcome: "Start and poll enrichment using POST/GET /products/process.",
body: "POST body uses items[].ean (preferred). Handler also accepts raw_product_ids as an alternate on this same path. Responses use HTTP 200 with { data: { process_id, … } } and completed items[] when done.",
authNote: AUTH_NOTE,
endpoints: [
endpoint("POST", "/products/process", "Start processing by EAN (legacy public contract)"),
endpoint("POST", "/products/process", "Start processing by EAN"),
endpoint("GET", "/products/process/{id}", "Processing job status by process_id")
],
links: [
{ label: "Legacy start in docs", href: docsOperationHref("POST", "/products/process"), kind: "docs" },
{ label: "Start in docs", href: docsOperationHref("POST", "/products/process"), kind: "docs" },
{
label: "Legacy status in docs",
label: "Status in docs",
href: docsOperationHref("GET", "/products/process/{id}"),
kind: "docs"
},
@@ -448,7 +448,7 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
synonyms: ["raw_product_ids", "cancel job", "retry job"],
title: "Dashboard-style processing jobs",
outcome: "Start flat JSON jobs by raw_product_ids; poll, cancel, or retry.",
body: "Same body shape as dashboard POST /api/processing/jobs. Start/retry return 202 Accepted with flat JSON (no data wrapper). Prefer /products/process for legacy integrations.",
body: "Same body shape as dashboard POST /api/processing/jobs. Start/retry return 202 Accepted with flat JSON (no data wrapper). Prefer /products/process for EAN-batch integrations.",
authNote: AUTH_NOTE,
endpoints: [
endpoint("POST", "/process", "Start processing job by raw_product_ids"),
@@ -473,15 +473,15 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
pathKey: "process.which",
synonyms: ["which process", "dual contract"],
title: "Which process API should I use?",
outcome: "Pick legacy EAN for integrations; pick /process for dashboard-style jobs.",
body: "1) Legacy public: POST/GET /products/process — items[].ean, HTTP 200 { data: { process_id } }. 2) Jobs: POST/GET /process — raw_product_ids, flat JSON, 202 on start. Mixing envelopes is the most common integration bug.",
outcome: "Pick EAN batch for integrations; pick /process for dashboard-style jobs.",
body: "1) Public process: POST/GET /products/process — items[].ean, HTTP 200 { data: { process_id } }. 2) Jobs: POST/GET /process — raw_product_ids, flat JSON, 202 on start. Mixing envelopes is the most common integration bug.",
authNote: AUTH_NOTE,
endpoints: [
endpoint("POST", "/products/process", "Legacy EAN batch"),
endpoint("POST", "/products/process", "EAN batch"),
endpoint("POST", "/process", "Dashboard-style jobs")
],
links: [
{ label: "Legacy process", href: docsOperationHref("POST", "/products/process"), kind: "docs" },
{ label: "EAN process", href: docsOperationHref("POST", "/products/process"), kind: "docs" },
{ label: "Jobs process", href: docsOperationHref("POST", "/process"), kind: "docs" },
{
label: "Dual-mode overview",
@@ -575,13 +575,13 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
endpoints: [
endpoint("GET", "/categories", "List categories"),
endpoint("POST", "/categories", "Create category"),
endpoint("POST", "/categories/create", "Create category (legacy alias)"),
endpoint("POST", "/categories/create", "Create category (alias of POST /categories)"),
endpoint("GET", "/categories/{id}", "Get category by UUID"),
endpoint("PATCH", "/categories/{id}", "Update category"),
endpoint("DELETE", "/categories/{id}", "Delete category by unique_id"),
endpoint("GET", "/attributes", "List attributes"),
endpoint("POST", "/attributes", "Create attribute"),
endpoint("POST", "/attributes/create", "Create attribute (legacy alias)"),
endpoint("POST", "/attributes/create", "Create attribute (alias of POST /attributes)"),
endpoint("PATCH", "/attributes/{id}", "Update attribute"),
endpoint("DELETE", "/attributes/{id}", "Delete attribute")
],
@@ -592,7 +592,7 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
{ label: "Attributes tag", href: docsTagHref("Attributes"), kind: "docs" },
{ label: "Authorize", href: docsAuthHref(), kind: "docs" }
],
tip: "Prefer canonical POST /categories and POST /attributes; /create aliases exist for legacy clients.",
tip: "Prefer canonical POST /categories and POST /attributes; /create aliases exist for existing integrations.",
relatedIds: ["answer.feeds.mapping", "answer.products"]
},
@@ -615,7 +615,7 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
{ label: "Try GET /products", href: docsOperationHref("GET", "/products"), kind: "docs" },
{ label: "Authorize", href: docsAuthHref(), kind: "docs" }
],
tip: "List uses legacy query params: page, limit, status, search, sortBy, sortOrder, feedId.",
tip: "List uses query params: page, limit, status, search, sortBy, sortOrder, feedId.",
relatedIds: ["answer.products.quality", "answer.process.legacy"]
},
@@ -648,13 +648,13 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
synonyms: ["campaigns", "calendar", "seasonal"],
title: "Marketing calendar (API)",
outcome: "List seasonal presets and prepare a calendar export via the public API.",
body: "Public /api/v1/marketing/calendar* (and legacy /campaigns aliases) are API-key routes for the content calendar. They are separate from session email campaigns under /api/campaigns in the dashboard UI.",
body: "Public /api/v1/marketing/calendar* (and /campaigns aliases) are API-key routes for the content calendar. They are separate from session email campaigns under /api/campaigns in the dashboard UI.",
authNote: AUTH_NOTE,
endpoints: [
endpoint("GET", "/marketing/calendar", "List seasonal campaign presets"),
endpoint("POST", "/marketing/calendar/prepare", "Prepare seasonal campaign export"),
endpoint("GET", "/campaigns", "List presets (legacy alias)"),
endpoint("POST", "/campaigns/prepare", "Prepare export (legacy alias)")
endpoint("GET", "/campaigns", "List presets (alias of GET /marketing/calendar)"),
endpoint("POST", "/campaigns/prepare", "Prepare export (alias of POST /marketing/calendar/prepare)")
],
links: [
{
@@ -684,7 +684,7 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
{ label: "Settings", href: "/settings", kind: "app" },
{ label: "Contact support", href: "/support/new", kind: "support" }
],
tip: "After upgrading, retry the same process endpoint you already chose (legacy vs jobs).",
tip: "After upgrading, retry the same process endpoint you already chose (EAN batch vs jobs).",
relatedIds: ["answer.error.job", "answer.process.chooser"]
},
@@ -722,7 +722,7 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
authNote: AUTH_NOTE,
endpoints: [
endpoint("POST", "/feeds/{id}/sync", "Sync (rate-limited)"),
endpoint("POST", "/products/process", "Legacy process (rate-limited)"),
endpoint("POST", "/products/process", "EAN process (rate-limited)"),
endpoint("POST", "/process", "Jobs process (rate-limited)"),
endpoint("POST", "/export-feeds/{id}/generate", "Generate export (rate-limited)")
],
@@ -741,10 +741,10 @@ export const DOCS_GUIDE_TREE: DocsGuideTree = {
synonyms: ["job failed", "pending", "cancel", "retry"],
title: "Process job pending or failed",
outcome: "Poll the matching status endpoint; cancel/retry only on the /process job surface.",
body: "Legacy: GET /products/process/{id} (data envelope). Jobs: GET /process/{id} plus POST …/cancel|terminate|retry. Confirm you started the job on the same contract you are polling.",
body: "EAN batch: GET /products/process/{id} (data envelope). Jobs: GET /process/{id} plus POST …/cancel|terminate|retry. Confirm you started the job on the same contract you are polling.",
authNote: AUTH_NOTE,
endpoints: [
endpoint("GET", "/products/process/{id}", "Legacy poll"),
endpoint("GET", "/products/process/{id}", "EAN batch poll"),
endpoint("GET", "/process/{id}", "Jobs poll"),
endpoint("POST", "/process/{id}/retry", "Retry failed/cancelled job")
],
+33
View File
@@ -56,6 +56,7 @@ export function readStoredTryItKey(companyId: string): DocsTryItStoredKey | null
export function writeStoredTryItKey(entry: DocsTryItStoredKey): void {
if (typeof sessionStorage === "undefined") return;
try {
// Tab-scoped only. Never log the full key or send it to GTM/analytics.
sessionStorage.setItem(storageKey(entry.companyId), JSON.stringify(entry));
} catch {
/* quota / private mode */
@@ -89,6 +90,38 @@ export function clearRapiDocApiKeys(el: RapiDocAuthElement | null | undefined):
el.removeAllSecurityKeys();
}
/**
* RapiDoc labels required-but-empty credentials as "(None Applied)".
* Public /api/v1 ops use a company API key show that instead.
*/
export function rapiDocAuthEmptyLabel(text: string): string {
return text.replace(/None Applied/g, "API Key");
}
export function relabelRapiDocAuthRequired(
el: HTMLElement | null | undefined
): MutationObserver | null {
const root = el?.shadowRoot;
if (!root) return null;
const apply = () => {
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
let node: Node | null = walker.nextNode();
while (node) {
const text = node.textContent;
if (text && text.includes("None Applied")) {
node.textContent = rapiDocAuthEmptyLabel(text);
}
node = walker.nextNode();
}
};
apply();
const observer = new MutationObserver(apply);
observer.observe(root, { subtree: true, childList: true, characterData: true });
return observer;
}
export function maskKeyPrefix(prefix: string | null | undefined): string {
const p = (prefix ?? "").trim();
return p ? `${p}\u2026` : "dk_\u2026";
+22 -22
View File
@@ -78,7 +78,7 @@ export const de: MessageDict = {
"auth.login.failed": "Anmeldung fehlgeschlagen",
"auth.login.passwordNotSet": "Dieses Konto benötigt noch ein Passwort. Öffnen Sie Ihren Einladungslink oder bitten Sie einen Admin, einen neuen auszustellen.",
"auth.login.setPasswordFirstTitle": "Zuerst Passwort festlegen:",
"auth.login.setPasswordFirstBody": "nutzen Sie den Einladungslink aus Ihrer E-Mail. Wenn der Link an eine alte Adresse ging (E-Mail-Drift), bitten Sie einen Unternehmens-Admin, eine neue Passwort-Einladung an {email} auszustellen.",
"auth.login.setPasswordFirstBody": "nutzen Sie den Einladungslink aus Ihrer E-Mail. Wenn Sie einen neuen Link brauchen, bitten Sie einen Unternehmens-Admin, einen Link zum Festlegen des Passworts an {email} auszustellen.",
"auth.login.yourEmail": "Ihre E-Mail",
"auth.login.platformAdminsReissue": "Plattform-Admins können erneut ausstellen unter",
"auth.login.adminUsersLink": "Admin → Benutzer",
@@ -144,9 +144,9 @@ export const de: MessageDict = {
"auth.invite.acceptFailed": "Einladung konnte nicht angenommen werden",
"auth.invite.setPasswordFailed": "Passwort konnte nicht festgelegt werden",
"auth.invite.expired": "Diese Einladung ist ungültig oder abgelaufen. Bitten Sie Ihren Unternehmens-Admin um eine neue Einladung und öffnen Sie den neuen Link (oder fügen Sie das neue Token unten ein).",
"auth.invite.setPasswordExpired": "Dieser Passwort-Link ist ungültig oder abgelaufen. Bitten Sie einen Unternehmens- oder Plattform-Admin um Neuausstellung und öffnen Sie den neuen Link (oder fügen Sie das neue Token unten ein).",
"auth.invite.setPasswordExpired": "Dieser Passwort-Link ist ungültig oder abgelaufen. Bitten Sie einen Unternehmens-Admin, einen Link zum Festlegen des Passworts an diese E-Mail auszustellen, und öffnen Sie den neuen Link (oder fügen Sie das neue Token unten ein).",
"auth.invite.emailMismatchDefault": "Sie sind mit einer anderen E-Mail angemeldet als diese Einladung.",
"auth.invite.expiredFooter": "Abgelaufener Link? Bitten Sie einen Admin um Neuausstellung — es gibt keine Self-Service-API zum erneuten Senden. Plattform-Admins:",
"auth.invite.expiredFooter": "Abgelaufener Link? Bitten Sie einen Unternehmens-Admin um Neuausstellung es gibt keine Self-Service-API zum erneuten Senden.",
"auth.invite.adminUsersLink": "Admin → Benutzer",
"auth.invite.doneTitle": "Sie sind im Team",
"auth.invite.doneSetPasswordTitle": "Passwort gespeichert",
@@ -163,7 +163,7 @@ export const de: MessageDict = {
"auth.invite.switchAccountTitle": "Konto wechseln:",
"auth.invite.switchAccountBody": "melden Sie sich ab und schließen Sie dieses Formular mit der eingeladenen E-Mail{emailSuffix} ab.",
"auth.invite.reissueTitle": "Neuausstellungs-Pfad:",
"auth.invite.reissueBody": "wenn sich Ihre echte Anmelde-E-Mail geändert hat (E-Mail-Drift), bitten Sie einen Unternehmens-Admin, diese Einladung zu widerrufen und eine neue an die E-Mail zu senden, mit der Sie sich anmelden. Plattform-Admins können Passwort-Links auch unter Admin → Benutzer erneut ausstellen.",
"auth.invite.reissueBody": "bitten Sie einen Unternehmens-Admin, einen Link zum Festlegen des Passworts an diese E-Mail auszustellen oder diese Einladung zu widerrufen und eine neue an die E-Mail zu senden, mit der Sie sich anmelden.",
"locale.switcher": "Oberflächensprache",
"locale.menu": "Oberflächensprache wählen",
"locale.label": "Dashboard-Sprache",
@@ -276,7 +276,7 @@ export const de: MessageDict = {
"settings.table.lastUsed": "Zuletzt verwendet",
"dashboard.demoSandbox": "Demo-Sandbox",
"dashboard.demoEmptyTitle": "Demo-Sandbox ist leer",
"dashboard.demoEmptyHint": "Demo-Sandbox ist leer — wechseln Sie zu A1 oder verbinden Sie einen Feed, um echte Katalogstatistiken zu sehen.",
"dashboard.demoEmptyHint": "Demo-Sandbox ist leer verbinden Sie einen Feed, um echte Katalogstatistiken zu sehen.",
"dashboard.overviewTitle": "Übersicht",
"dashboard.quickLinks": "Schnelllinks",
"dashboard.whatsNew": "Neuigkeiten",
@@ -319,7 +319,7 @@ export const de: MessageDict = {
"dashboard.workflow.exportMap": "Zuordnen dann exportieren",
"dashboard.workflowHint": "Katalog rein → anreichern / verarbeiten → exportieren oder zu Shops pushen. Zum nächsten Schritt für {name}.",
"dashboard.overviewHint": "Live-Summen für {name}",
"dashboard.demoEmptyMessage": "Wechseln Sie in der Kopfzeile zu A1 (oder einem anderen Seed-Unternehmen) oder verbinden Sie hier einen Feed, um diese Sandbox zu füllen.",
"dashboard.demoEmptyMessage": "Verbinden Sie hier einen Feed, um diese Sandbox zu füllen, oder wechseln Sie in der Kopfzeile zu einem anderen Unternehmen.",
"dashboard.emptyTitle": "Katalog hereinholen",
"dashboard.emptyMessage": "Katalog rein → anreichern / verarbeiten → exportieren oder zu Shops pushen. Starten Sie mit Erste Schritte unten (oder Feed verbinden / CSV hochladen).",
"dashboard.connectFeedAnyway": "Feed trotzdem verbinden",
@@ -367,29 +367,29 @@ export const de: MessageDict = {
"activation.focus.sync": "Als Nächstes: Nach dem Speichern der Zuordnung eine Stichprobe synchronisieren. Sync nur bei zugeordneten Feeds — nicht bei unmapped Quellen.",
"activation.focus.dismiss": "Hinweis schließen",
"activation.dismiss": "Checkliste ausblenden",
"hypercare.report.title": "Cutover-Hypercare",
"hypercare.report.title": "Hilfe nach dem Umzug?",
"hypercare.report.message": "Fehlende oder falsche Daten nach der Migration? Melden Sie es, damit wir Ihren Workspace korrigieren können.",
"hypercare.report.cta": "Fehlende oder falsche Daten melden",
"hypercare.report.dismiss": "Verwerfen",
"hypercare.report.adminTriage": "Hypercare-Warteschlange öffnen",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is “broken” — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutover — that is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -456,9 +456,9 @@ export const de: MessageDict = {
"errors.readOnly": "Das System ist im Nur-Lesen-Modus. Änderungen sind vorübergehend deaktiviert.",
"errors.companyAdminDenied": "Nur Unternehmens-Admins können {action}. Bitten Sie einen Admin um Hilfe.",
"errors.companyAdminDenied.actionDefault": "dies tun",
"docs.tryIt.memberCannotCreate": "Nur Unternehmens-Admins können API-Schlüssel erstellen. Legacy-Schlüssel wurden nicht migriert — Schlüssel unten einfügen oder Admin um einen neuen bitten.",
"docs.tryIt.noKeysLegacyAdmin": "Legacy-API-Schlüssel wurden nicht migriert. Neuen Schlüssel über Use my API key oder Einstellungen → API-Schlüssel erstellen — Geheimnis nur einmal sichtbar.",
"docs.tryIt.noKeysLegacyMember": "Keine API-Schlüssel gelistet. Legacy-Schlüssel nicht migriert — Admin um Erstellung bitten oder dk_-Schlüssel einfügen.",
"docs.tryIt.memberCannotCreate": "Nur Unternehmens-Admins können API-Schlüssel erstellen. Schlüssel wurden nicht migriert — Schlüssel unten einfügen oder Admin um einen neuen bitten.",
"docs.tryIt.noKeysLegacyAdmin": "API-Schlüssel wurden nicht migriert. Neuen Schlüssel über Use my API key oder Einstellungen → API-Schlüssel erstellen — Geheimnis nur einmal sichtbar.",
"docs.tryIt.noKeysLegacyMember": "Keine API-Schlüssel gelistet. Schlüssel nicht migriert — Admin um Erstellung bitten oder dk_-Schlüssel einfügen.",
"errors.couldNotUpdateLanguage": "Sprache konnte nicht aktualisiert werden",
"toast.support.replyTitle": "Support-Antwort",
"toast.support.replyBody": "Das Team hat auf Ihr Support-Ticket geantwortet.",
@@ -5065,7 +5065,7 @@ export const de: MessageDict = {
"brand.uploadLogo": "Logo hochladen",
"brand.clear": "Löschen",
"brand.field.logoUrl": "Logo-URL",
"brand.placeholder.logoUrl": "https://… oder hochgeladen /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://",
"brand.logoAlt": "Vorschau des Markenlogos",
"brand.tipsTitle": "Formel- / Vorschautipps",
"brand.saving": "Wird gespeichert…",
@@ -5291,7 +5291,7 @@ export const de: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "Am beliebtesten",
"pricing.card.custom": "Individuell",
"pricing.card.forever": "für immer",
+37 -28
View File
@@ -78,7 +78,7 @@ export const en: MessageDict = {
"auth.login.failed": "Login failed",
"auth.login.passwordNotSet": "This account still needs a password. Open your invite link, or ask an admin to re-issue one.",
"auth.login.setPasswordFirstTitle": "Set password first:",
"auth.login.setPasswordFirstBody": "use the invite link from your email. If the link went to an old address (email drift), ask a company admin to re-issue a set-password invite to {email}.",
"auth.login.setPasswordFirstBody": "use the invite link from your email. If you need a new link, ask a company admin to re-issue a set-password link to {email}.",
"auth.login.yourEmail": "your email",
"auth.login.platformAdminsReissue": "Platform admins can re-issue from",
"auth.login.adminUsersLink": "Admin → Users",
@@ -144,9 +144,9 @@ export const en: MessageDict = {
"auth.invite.acceptFailed": "Could not accept invite",
"auth.invite.setPasswordFailed": "Could not set password",
"auth.invite.expired": "This invite is invalid or expired. Ask your company admin to send a new invite, then open the new link (or paste the new token below).",
"auth.invite.setPasswordExpired": "This set-password link is invalid or expired. Ask a company or platform admin to re-issue it, then open the new link (or paste the new token below).",
"auth.invite.setPasswordExpired": "This set-password link is invalid or expired. Ask a company admin to re-issue a set-password link to this email, then open the new link (or paste the new token below).",
"auth.invite.emailMismatchDefault": "You're signed in as a different email than this invite.",
"auth.invite.expiredFooter": "Expired link? Ask an admin to re-issue — there is no self-serve resend API. Platform admins:",
"auth.invite.expiredFooter": "Expired link? Ask a company admin to re-issue — there is no self-serve resend API.",
"auth.invite.adminUsersLink": "Admin → Users",
"auth.invite.doneTitle": "You're on the team",
"auth.invite.doneSetPasswordTitle": "Password saved",
@@ -163,7 +163,7 @@ export const en: MessageDict = {
"auth.invite.switchAccountTitle": "Switch account:",
"auth.invite.switchAccountBody": "sign out, then finish this form with the invited email{emailSuffix}.",
"auth.invite.reissueTitle": "Re-issue path:",
"auth.invite.reissueBody": "if your real login email changed (email drift), ask a company admin to revoke this invite and send a new one to the email you use to sign in. Platform admins can also re-issue set-password links from Admin → Users.",
"auth.invite.reissueBody": "ask a company admin to re-issue a set-password link to this email, or to revoke this invite and send a new one to the email you use to sign in.",
"locale.switcher": "Interface language",
"locale.menu": "Choose interface language",
"locale.label": "Dashboard language",
@@ -296,7 +296,7 @@ export const en: MessageDict = {
"settings.table.lastUsed": "Last Used",
"dashboard.demoSandbox": "Demo sandbox",
"dashboard.demoEmptyTitle": "Demo sandbox is empty",
"dashboard.demoEmptyHint": "Demo sandbox is empty — switch to A1 or connect a feed to see real catalog stats.",
"dashboard.demoEmptyHint": "Demo sandbox is empty — connect a feed to see catalog stats.",
"dashboard.overviewTitle": "Overview",
"dashboard.quickLinks": "Quick links",
"dashboard.whatsNew": "What's new",
@@ -339,7 +339,7 @@ export const en: MessageDict = {
"dashboard.workflow.exportMap": "Map then export",
"dashboard.workflowHint": "Catalog in → enrich / process → export or push to stores. Jump to the next step for {name}.",
"dashboard.overviewHint": "Live totals for {name}",
"dashboard.demoEmptyMessage": "Switch to A1 (or another seeded company) in the header, or connect a feed here to populate this sandbox.",
"dashboard.demoEmptyMessage": "Connect a feed here to populate this sandbox, or switch to another company in the header.",
"dashboard.emptyTitle": "Bring a catalog in",
"dashboard.emptyMessage": "Catalog in → enrich / process → export or push to stores. Start with Getting started below (or connect a feed / upload a CSV).",
"dashboard.connectFeedAnyway": "Connect feed anyway",
@@ -391,28 +391,28 @@ export const en: MessageDict = {
"activation.focus.sync": "Next: Sync a sample after mappings are saved. Use Sync on a mapped feed — avoid syncing unmapped sources.",
"activation.focus.dismiss": "Dismiss hint",
"activation.dismiss": "Dismiss checklist",
"hypercare.report.title": "Cutover hypercare",
"hypercare.report.message": "See missing or wrong data after migration? Report it so we can fix your workspace.",
"hypercare.report.title": "Need help after the move?",
"hypercare.report.message": "See missing or wrong data after the move? Report it so we can fix your workspace.",
"hypercare.report.cta": "Report missing or wrong data",
"hypercare.report.dismiss": "Dismiss",
"hypercare.report.adminTriage": "Open hypercare queue",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutoverthat is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -479,9 +479,9 @@ export const en: MessageDict = {
"errors.readOnly": "The system is in read-only mode. Changes are temporarily disabled.",
"errors.companyAdminDenied": "Only company admins can {action}. Ask a company admin for help.",
"errors.companyAdminDenied.actionDefault": "do this",
"docs.tryIt.memberCannotCreate": "Only company admins can create API keys. Legacy keys were not migrated — paste a key below, or ask an admin to create a new one.",
"docs.tryIt.noKeysLegacyAdmin": "Legacy API keys were not migrated. Create a new key via Use my API key or Settings → API keys — the secret is shown only once.",
"docs.tryIt.noKeysLegacyMember": "No API keys listed. Legacy keys were not migrated — ask a company admin to create one, or paste a dk_ key.",
"docs.tryIt.memberCannotCreate": "Only company admins can create API keys. Keys from the previous platform were not migrated — paste a key below, or ask an admin to create a new one.",
"docs.tryIt.noKeysLegacyAdmin": "API keys from the previous platform were not migrated. Create a new key via Use my API key or Settings → API keys — the secret is shown only once.",
"docs.tryIt.noKeysLegacyMember": "No API keys listed. Keys from the previous platform were not migrated — ask a company admin to create one, or paste a dk_ key.",
"errors.couldNotUpdateLanguage": "Could not update language",
"toast.support.replyTitle": "Support reply",
"toast.support.replyBody": "Staff replied to your support ticket.",
@@ -3729,6 +3729,7 @@ export const en: MessageDict = {
"processing.receipt.cancelledDesc": "The just-started job was cancelled.",
"processing.receipt.cancelFailed": "Could not cancel processing job",
"unsubscribe.title": "Unsubscribe",
"unsubscribe.useEmailLink": "Use the unsubscribe link in the email we sent you. Opening this page without that link cannot change your email preferences.",
"unsubscribe.me": "Unsubscribe me",
"unsubscribe.invalidLink": "Invalid link",
"unsubscribe.failedShort": "Unsubscribe failed",
@@ -4445,23 +4446,23 @@ export const en: MessageDict = {
"categories.promptSection.navHeading": "Sections",
"categories.promptSection.navHint": "Edit one part of the enhance reply at a time.",
"categories.promptSection.title": "Title",
"categories.promptSection.titleHelp": "Instructions for the product title (JSON name).",
"categories.promptSection.titleHelp": "Instructions for the product title.",
"categories.promptSection.titleFormulaNote": "Title formula slots (type + brand + model) are appended automatically when set on this category.",
"categories.promptSection.description": "Description",
"categories.promptSection.descriptionHelp": "Instructions for the HTML product body (JSON description - not SEO meta).",
"categories.promptSection.descriptionHelp": "Instructions for the HTML product body (not SEO meta).",
"categories.promptSection.descriptionFormulaNote": "Description formula sections are appended at render time when configured.",
"categories.promptSection.meta": "Meta",
"categories.promptSection.metaHelp": "Instructions for SEO meta_title and meta_description (plain text).",
"categories.promptSection.metaHelp": "Instructions for the SEO title and description (plain text).",
"categories.promptSection.metaFormulaNote": "SEO meta formulas from the description template are appended when present.",
"categories.promptSection.attributes": "Attributes",
"categories.promptSection.attributesHelp": "Instructions for the attrs object (allowlisted keys).",
"categories.promptSection.attributesHelp": "Instructions for product attributes (allowed keys only).",
"categories.promptSection.attributesFormulaNote": "Allowed attribute keys and title-formula attr slots are appended at render time.",
"categories.promptSection.schemaHeading": "One-shot JSON",
"categories.promptSection.schemaHelp": "Enhance returns a single JSON object. This section owns the keys below.",
"categories.promptSection.contentLabel": "Section instructions",
"categories.promptSection.contentPlaceholder": "Role guidance and placeholders for this section...",
"categories.promptSection.preambleToggle": "Shared intro (all sections)",
"categories.promptSection.preambleHelp": "Shown once above the section markers. Keep the JSON schema reminder so the model replies correctly.",
"categories.promptSection.preambleHelp": "Shown once above the section markers. Keep language and evidence reminders so the model replies correctly.",
"categories.promptSection.clearLanguage": "Clear language override",
"categories.promptSection.unstructuredHint": "This prompt is not sectioned yet. Content is shown under Description - edit the sections you need and save to store the standard Title / Description / Meta / Attributes layout.",
"categories.promptSection.categorizeNote": "Categorize (taxonomy pick) is a separate pipeline step - not part of this enhance prompt.",
@@ -4792,6 +4793,10 @@ export const en: MessageDict = {
"seo.terms.description": "Terms for using Descrybe — feed import, catalog enrichment, AI-assisted content, exports, and WooCommerce sync for your business.",
"seo.features.title": "Features — Feeds, enrichment, and export | Descrybe",
"seo.features.description": "See how Descrybe imports supplier feeds, maps fields to your taxonomy, enriches product data, and ships catalogs via export feeds, WooCommerce, or API.",
"seo.contactSales.title": "Contact sales | Descrybe",
"seo.contactSales.description": "Tell us about your catalog and capacity needs — we will prepare a custom plan you can pay online.",
"seo.unsubscribe.title": "Unsubscribe | Descrybe",
"seo.unsubscribe.description": "Stop marketing emails from Descrybe using the unsubscribe link in the message we sent you.",
"pricing.page.eyebrow": "Pricing",
"pricing.page.title": "Simple plans for growing catalogs",
"pricing.page.lead": "Start free with feed mapping and basic cleanup. Upgrade when you need AI titles and descriptions, more products, export feeds, or WooCommerce sync — Starter through Enterprise.",
@@ -4799,6 +4804,8 @@ export const en: MessageDict = {
"pricing.page.subscribedMid": "or compare plans in",
"pricing.page.plansLink": "Plans",
"pricing.page.subscribedAfter": ".",
"pricing.page.guestSubscribedBefore": "Already subscribed?",
"pricing.page.guestSubscribedAfter": "to manage billing and compare plans.",
"legal.lastUpdated": "Last updated: {date}",
"legal.backHome": "← Back to Home",
"legal.emailLabel": "Email:",
@@ -5130,7 +5137,7 @@ export const en: MessageDict = {
"brand.uploadLogo": "Upload logo",
"brand.clear": "Clear",
"brand.field.logoUrl": "Logo URL",
"brand.placeholder.logoUrl": "https://… or uploaded /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://…",
"brand.logoAlt": "Brand logo preview",
"brand.tipsTitle": "Formula / preview tips",
"brand.saving": "Saving…",
@@ -5291,6 +5298,8 @@ export const en: MessageDict = {
"pricing.section.publicBefore": "Public plans: Free, Starter, Plus, Growth, Business, Scale, and Enterprise. Create a Free account, then upgrade under",
"pricing.section.publicOr": "or",
"pricing.section.publicAfter": "via Stripe Checkout. Enterprise remains sales-led.",
"pricing.section.guestPublicBefore": "Public plans: Free, Starter, Plus, Growth, Business, Scale, and Enterprise. Create a Free account, then",
"pricing.section.guestPublicAfter": "to upgrade via Stripe Checkout. Enterprise remains sales-led.",
"pricing.section.plansLink": "Plans",
"pricing.section.billingLink": "Billing",
"pricing.section.capabilitiesTitle": "What every plan is built for",
@@ -5359,7 +5368,7 @@ export const en: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "Most Popular",
"pricing.card.custom": "Custom",
"pricing.card.forever": "forever",
+28 -19
View File
@@ -78,7 +78,7 @@ export const es: MessageDict = {
"auth.login.failed": "Error al iniciar sesión",
"auth.login.passwordNotSet": "Esta cuenta aún necesita una contraseña. Abre el enlace de invitación o pide a un administrador que emita uno nuevo.",
"auth.login.setPasswordFirstTitle": "Establece la contraseña primero:",
"auth.login.setPasswordFirstBody": "usa el enlace de invitación de tu correo. Si el enlace fue a una dirección antigua (cambio de correo), pide a un administrador de la empresa que emita una nueva invitación para establecer contraseña a {email}.",
"auth.login.setPasswordFirstBody": "usa el enlace de invitación de tu correo. Si necesitas un enlace nuevo, pide a un administrador de la empresa que reemita un enlace para establecer contraseña a {email}.",
"auth.login.yourEmail": "tu correo",
"auth.login.platformAdminsReissue": "Los administradores de plataforma pueden reemitir desde",
"auth.login.adminUsersLink": "Admin → Usuarios",
@@ -144,9 +144,9 @@ export const es: MessageDict = {
"auth.invite.acceptFailed": "No se pudo aceptar la invitación",
"auth.invite.setPasswordFailed": "No se pudo establecer la contraseña",
"auth.invite.expired": "Esta invitación no es válida o ha caducado. Pide a tu administrador de la empresa que envíe una nueva invitación y abre el nuevo enlace (o pega el nuevo token abajo).",
"auth.invite.setPasswordExpired": "Este enlace para establecer contraseña no es válido o ha caducado. Pide a un administrador de la empresa o de la plataforma que lo reemita y abre el nuevo enlace (o pega el nuevo token abajo).",
"auth.invite.setPasswordExpired": "Este enlace para establecer contraseña no es válido o ha caducado. Pide a un administrador de la empresa que reemita un enlace para establecer contraseña a este correo y abre el nuevo enlace (o pega el nuevo token abajo).",
"auth.invite.emailMismatchDefault": "Has iniciado sesión con un correo distinto al de esta invitación.",
"auth.invite.expiredFooter": "¿Enlace caducado? Pide a un administrador que lo reemita — no hay API de reenvío autoservicio. Administradores de plataforma:",
"auth.invite.expiredFooter": "¿Enlace caducado? Pide a un administrador de la empresa que lo reemita no hay API de reenvío autoservicio.",
"auth.invite.adminUsersLink": "Admin → Usuarios",
"auth.invite.doneTitle": "Ya formas parte del equipo",
"auth.invite.doneSetPasswordTitle": "Contraseña guardada",
@@ -163,7 +163,7 @@ export const es: MessageDict = {
"auth.invite.switchAccountTitle": "Cambiar de cuenta:",
"auth.invite.switchAccountBody": "cierra sesión y completa este formulario con el correo invitado{emailSuffix}.",
"auth.invite.reissueTitle": "Vía de reemisión:",
"auth.invite.reissueBody": "si cambió tu correo real de acceso (desfase de correo), pide a un administrador de la empresa que revoque esta invitación y envíe una nueva al correo con el que inicias sesión. Los administradores de plataforma también pueden reemitir enlaces para establecer contraseña desde Admin → Usuarios.",
"auth.invite.reissueBody": "pide a un administrador de la empresa que reemita un enlace para establecer contraseña a este correo, o que revoque esta invitación y envíe una nueva al correo con el que inicias sesión.",
"locale.switcher": "Idioma de la interfaz",
"locale.menu": "Elegir idioma de la interfaz",
"locale.label": "Idioma del panel",
@@ -276,7 +276,7 @@ export const es: MessageDict = {
"settings.table.lastUsed": "Último uso",
"dashboard.demoSandbox": "Zona de pruebas demo",
"dashboard.demoEmptyTitle": "La zona de pruebas demo está vacía",
"dashboard.demoEmptyHint": "La zona de pruebas demo está vacía — cambia a A1 o conecta un feed para ver estadísticas reales del catálogo.",
"dashboard.demoEmptyHint": "La zona de pruebas demo está vacía — conecta un feed para ver estadísticas reales del catálogo.",
"dashboard.overviewTitle": "Resumen",
"dashboard.quickLinks": "Enlaces rápidos",
"dashboard.whatsNew": "Novedades",
@@ -319,7 +319,7 @@ export const es: MessageDict = {
"dashboard.workflow.exportMap": "Mapear y exportar",
"dashboard.workflowHint": "Catálogo dentro → enriquecer / procesar → exportar o enviar a tiendas. Salta al siguiente paso para {name}.",
"dashboard.overviewHint": "Totales en vivo para {name}",
"dashboard.demoEmptyMessage": "Cambia a A1 (u otra empresa con datos) en el encabezado, o conecta un feed aquí para poblar esta zona de pruebas.",
"dashboard.demoEmptyMessage": "Conecta un feed aquí para poblar esta zona de pruebas, o cambia a otra empresa en el encabezado.",
"dashboard.emptyTitle": "Importa un catálogo",
"dashboard.emptyMessage": "Catálogo dentro → enriquecer / procesar → exportar o enviar a tiendas. Empieza con Primeros pasos abajo (o conecta un feed / sube un CSV).",
"dashboard.connectFeedAnyway": "Conectar feed de todos modos",
@@ -367,29 +367,29 @@ export const es: MessageDict = {
"activation.focus.sync": "Siguiente: sincroniza una muestra tras guardar el mapeo. Usa Sync en un feed mapeado — evita sincronizar orígenes sin mapear.",
"activation.focus.dismiss": "Cerrar aviso",
"activation.dismiss": "Descartar lista",
"hypercare.report.title": "Hypercare de corte",
"hypercare.report.title": "¿Necesitas ayuda tras el traslado?",
"hypercare.report.message": "¿Faltan o están incorrectos datos tras la migración? Repórtalo para que podamos corregir tu espacio de trabajo.",
"hypercare.report.cta": "Reportar datos faltantes o incorrectos",
"hypercare.report.dismiss": "Descartar",
"hypercare.report.adminTriage": "Abrir cola de hypercare",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is “broken” — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutover — that is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -3690,6 +3690,7 @@ export const es: MessageDict = {
"processing.receipt.cancelledDesc": "El trabajo recién iniciado se canceló.",
"processing.receipt.cancelFailed": "No se pudo cancelar el trabajo de procesamiento",
"unsubscribe.title": "Cancelar suscripción",
"unsubscribe.useEmailLink": "Usa el enlace de baja del correo que te enviamos. Abrir esta pagina sin ese enlace no puede cambiar tus preferencias de email.",
"unsubscribe.me": "Cancelar mi suscripción",
"unsubscribe.invalidLink": "Enlace no válido",
"unsubscribe.failedShort": "Error al cancelar la suscripción",
@@ -4727,6 +4728,10 @@ export const es: MessageDict = {
"seo.terms.description": "Términos de uso de Descrybe: importación de feeds, enriquecimiento de catálogo, contenido asistido por IA, exportaciones y sincronización con WooCommerce para tu negocio.",
"seo.features.title": "Funciones — Feeds, enriquecimiento y exportación | Descrybe",
"seo.features.description": "Descubre cómo Descrybe importa feeds de proveedores, mapea campos a tu taxonomía, enriquece datos de producto y envía catálogos mediante feeds de exportación, WooCommerce o API.",
"seo.contactSales.title": "Contacto comercial | Descrybe",
"seo.contactSales.description": "Cuentanos sobre tu catalogo y necesidades de capacidad: prepararemos un plan personalizado que puedes pagar en linea.",
"seo.unsubscribe.title": "Cancelar suscripcion | Descrybe",
"seo.unsubscribe.description": "Deja de recibir emails de marketing de Descrybe usando el enlace de baja del mensaje que te enviamos.",
"pricing.page.eyebrow": "Precios",
"pricing.page.title": "Planes sencillos para catálogos en crecimiento",
"pricing.page.lead": "Empieza gratis con mapeo de feeds y limpieza básica. Mejora cuando necesites títulos y descripciones con IA, más productos, feeds de exportación o sincronización con WooCommerce — de Starter a Enterprise.",
@@ -4734,6 +4739,8 @@ export const es: MessageDict = {
"pricing.page.subscribedMid": "o compara planes en",
"pricing.page.plansLink": "Planes",
"pricing.page.subscribedAfter": ".",
"pricing.page.guestSubscribedBefore": "Ya estas suscrito?",
"pricing.page.guestSubscribedAfter": "para gestionar la facturacion y comparar planes.",
"legal.lastUpdated": "Última actualización: {date}",
"legal.backHome": "← Volver al inicio",
"legal.emailLabel": "Correo electrónico:",
@@ -5065,7 +5072,7 @@ export const es: MessageDict = {
"brand.uploadLogo": "Subir logo",
"brand.clear": "Borrar",
"brand.field.logoUrl": "URL del logo",
"brand.placeholder.logoUrl": "https://… o subido /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://",
"brand.logoAlt": "Vista previa del logo de marca",
"brand.tipsTitle": "Consejos de fórmula / vista previa",
"brand.saving": "Guardando…",
@@ -5226,6 +5233,8 @@ export const es: MessageDict = {
"pricing.section.publicBefore": "Planes públicos: Free, Starter, Growth, Business y Enterprise. Crea una cuenta Free y luego mejora en",
"pricing.section.publicOr": "o",
"pricing.section.publicAfter": "mediante Stripe Checkout. Enterprise sigue siendo con ventas.",
"pricing.section.guestPublicBefore": "Planes publicos: Free, Starter, Plus, Growth, Business, Scale y Enterprise. Crea una cuenta Free y luego",
"pricing.section.guestPublicAfter": "para mejorar via Stripe Checkout. Enterprise sigue gestionado por ventas.",
"pricing.section.plansLink": "Planes",
"pricing.section.billingLink": "Facturación",
"pricing.section.capabilitiesTitle": "Para qué está pensado cada plan",
@@ -5291,7 +5300,7 @@ export const es: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "Más popular",
"pricing.card.custom": "Personalizado",
"pricing.card.forever": "para siempre",
+28 -19
View File
@@ -78,7 +78,7 @@ export const fr: MessageDict = {
"auth.login.failed": "Échec de la connexion",
"auth.login.passwordNotSet": "Ce compte a encore besoin d'un mot de passe. Ouvrez votre lien d'invitation, ou demandez à un administrateur d'en émettre un nouveau.",
"auth.login.setPasswordFirstTitle": "Définissez d'abord le mot de passe :",
"auth.login.setPasswordFirstBody": "utilisez le lien d'invitation de votre e-mail. Si le lien a été envoyé à une ancienne adresse (dérive d'e-mail), demandez à un administrateur de l'entreprise de renvoyer une invitation de définition de mot de passe à {email}.",
"auth.login.setPasswordFirstBody": "utilisez le lien d'invitation de votre e-mail. Si vous avez besoin d'un nouveau lien, demandez à un administrateur de l'entreprise de réémettre un lien de définition de mot de passe à {email}.",
"auth.login.yourEmail": "votre e-mail",
"auth.login.platformAdminsReissue": "Les administrateurs de la plateforme peuvent réémettre depuis",
"auth.login.adminUsersLink": "Admin → Utilisateurs",
@@ -144,9 +144,9 @@ export const fr: MessageDict = {
"auth.invite.acceptFailed": "Impossible d'accepter l'invitation",
"auth.invite.setPasswordFailed": "Impossible de définir le mot de passe",
"auth.invite.expired": "Cette invitation est invalide ou expirée. Demandez à votre administrateur d'envoyer une nouvelle invitation, puis ouvrez le nouveau lien (ou collez le nouveau jeton ci-dessous).",
"auth.invite.setPasswordExpired": "Ce lien de définition de mot de passe est invalide ou expiré. Demandez à un administrateur de l'entreprise ou de la plateforme de le réémettre, puis ouvrez le nouveau lien (ou collez le nouveau jeton ci-dessous).",
"auth.invite.setPasswordExpired": "Ce lien de définition de mot de passe est invalide ou expiré. Demandez à un administrateur de l'entreprise de réémettre un lien de définition de mot de passe vers cet e-mail, puis ouvrez le nouveau lien (ou collez le nouveau jeton ci-dessous).",
"auth.invite.emailMismatchDefault": "Vous êtes connecté avec un e-mail différent de celui de cette invitation.",
"auth.invite.expiredFooter": "Lien expiré ? Demandez à un administrateur de le réémettre — il n'y a pas d'API de renvoi en libre-service. Administrateurs de plateforme :",
"auth.invite.expiredFooter": "Lien expiré ? Demandez à un administrateur de l'entreprise de le réémettre — il n'y a pas d'API de renvoi en libre-service.",
"auth.invite.adminUsersLink": "Admin → Utilisateurs",
"auth.invite.doneTitle": "Vous faites partie de l'équipe",
"auth.invite.doneSetPasswordTitle": "Mot de passe enregistré",
@@ -163,7 +163,7 @@ export const fr: MessageDict = {
"auth.invite.switchAccountTitle": "Changer de compte :",
"auth.invite.switchAccountBody": "déconnectez-vous, puis terminez ce formulaire avec l'e-mail invité{emailSuffix}.",
"auth.invite.reissueTitle": "Chemin de réémission :",
"auth.invite.reissueBody": "si votre vrai e-mail de connexion a changé (dérive d'e-mail), demandez à un administrateur de l'entreprise de révoquer cette invitation et d'en envoyer une nouvelle à l'e-mail que vous utilisez pour vous connecter. Les administrateurs de la plateforme peuvent aussi réémettre des liens de définition de mot de passe depuis Admin → Utilisateurs.",
"auth.invite.reissueBody": "demandez à un administrateur de l'entreprise de réémettre un lien de définition de mot de passe vers cet e-mail, ou de révoquer cette invitation et d'en envoyer une nouvelle à l'e-mail que vous utilisez pour vous connecter.",
"locale.switcher": "Langue de l'interface",
"locale.menu": "Choisir la langue de l'interface",
"locale.label": "Langue du tableau de bord",
@@ -276,7 +276,7 @@ export const fr: MessageDict = {
"settings.table.lastUsed": "Dernière utilisation",
"dashboard.demoSandbox": "Bac à sable démo",
"dashboard.demoEmptyTitle": "Le bac à sable démo est vide",
"dashboard.demoEmptyHint": "Le bac à sable démo est vide — basculez vers A1 ou connectez un flux pour voir de vraies stats catalogue.",
"dashboard.demoEmptyHint": "Le bac à sable démo est vide connectez un flux pour voir de vraies stats catalogue.",
"dashboard.overviewTitle": "Vue d'ensemble",
"dashboard.quickLinks": "Liens rapides",
"dashboard.whatsNew": "Nouveautés",
@@ -319,7 +319,7 @@ export const fr: MessageDict = {
"dashboard.workflow.exportMap": "Mapper puis exporter",
"dashboard.workflowHint": "Catalogue entrant → enrichir / traiter → exporter ou pousser vers les boutiques. Passez à l’étape suivante pour {name}.",
"dashboard.overviewHint": "Totaux en direct pour {name}",
"dashboard.demoEmptyMessage": "Basculez vers A1 (ou une autre entreprise seedée) dans l'en-tête, ou connectez un flux ici pour remplir ce bac à sable.",
"dashboard.demoEmptyMessage": "Connectez un flux ici pour remplir ce bac à sable, ou basculez vers une autre entreprise dans l'en-tête.",
"dashboard.emptyTitle": "Importez un catalogue",
"dashboard.emptyMessage": "Catalogue entrant → enrichir / traiter → exporter ou pousser vers les boutiques. Commencez avec Premiers pas ci-dessous (ou connectez un flux / téléversez un CSV).",
"dashboard.connectFeedAnyway": "Connecter un flux quand même",
@@ -367,29 +367,29 @@ export const fr: MessageDict = {
"activation.focus.sync": "Ensuite : synchronisez un échantillon après l’enregistrement du mapping. Utilisez Sync sur un flux mappé — évitez les sources non mappées.",
"activation.focus.dismiss": "Fermer l’indication",
"activation.dismiss": "Masquer la checklist",
"hypercare.report.title": "Hypercare de bascule",
"hypercare.report.title": "Besoin d'aide après le transfert ?",
"hypercare.report.message": "Données manquantes ou incorrectes après la migration ? Signalez-le pour que nous corrigions votre espace de travail.",
"hypercare.report.cta": "Signaler des données manquantes ou incorrectes",
"hypercare.report.dismiss": "Ignorer",
"hypercare.report.adminTriage": "Ouvrir la file hypercare",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is “broken” — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutover — that is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -3690,6 +3690,7 @@ export const fr: MessageDict = {
"processing.receipt.cancelledDesc": "La tâche qui vient de démarrer a été annulée.",
"processing.receipt.cancelFailed": "Impossible d'annuler la tâche de traitement",
"unsubscribe.title": "Se désabonner",
"unsubscribe.useEmailLink": "Utilisez le lien de desabonnement dans l'e-mail que nous vous avons envoye. Ouvrir cette page sans ce lien ne peut pas modifier vos preferences e-mail.",
"unsubscribe.me": "Me désabonner",
"unsubscribe.invalidLink": "Lien invalide",
"unsubscribe.failedShort": "Échec du désabonnement",
@@ -4727,6 +4728,10 @@ export const fr: MessageDict = {
"seo.terms.description": "Conditions d'utilisation de Descrybe : import de flux, enrichissement de catalogue, contenu assisté par IA, exports et synchronisation WooCommerce pour votre entreprise.",
"seo.features.title": "Fonctionnalités — Flux, enrichissement et export | Descrybe",
"seo.features.description": "Découvrez comment Descrybe importe les flux fournisseurs, mappe les champs à votre taxonomie, enrichit les données produit et diffuse les catalogues via flux d'export, WooCommerce ou API.",
"seo.contactSales.title": "Contacter les ventes | Descrybe",
"seo.contactSales.description": "Parlez-nous de votre catalogue et de vos besoins de capacite: nous preparerons une offre personnalisee payable en ligne.",
"seo.unsubscribe.title": "Se desabonner | Descrybe",
"seo.unsubscribe.description": "Arretez les e-mails marketing Descrybe via le lien de desabonnement du message que nous vous avons envoye.",
"pricing.page.eyebrow": "Tarifs",
"pricing.page.title": "Des offres simples pour des catalogues en croissance",
"pricing.page.lead": "Commencez gratuitement avec le mapping de flux et un nettoyage de base. Passez à un plan supérieur quand vous avez besoin de titres et descriptions IA, de plus de produits, d’exports ou de la sync WooCommerce — de Starter à Enterprise.",
@@ -4734,6 +4739,8 @@ export const fr: MessageDict = {
"pricing.page.subscribedMid": "ou comparez les offres dans",
"pricing.page.plansLink": "Offres",
"pricing.page.subscribedAfter": ".",
"pricing.page.guestSubscribedBefore": "Deja abonne?",
"pricing.page.guestSubscribedAfter": "pour gerer la facturation et comparer les offres.",
"legal.lastUpdated": "Dernière mise à jour : {date}",
"legal.backHome": "← Retour à l’accueil",
"legal.emailLabel": "E-mail :",
@@ -5065,7 +5072,7 @@ export const fr: MessageDict = {
"brand.uploadLogo": "Téléverser le logo",
"brand.clear": "Effacer",
"brand.field.logoUrl": "URL du logo",
"brand.placeholder.logoUrl": "https://… ou téléversé /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://",
"brand.logoAlt": "Aperçu du logo de marque",
"brand.tipsTitle": "Conseils formule / aperçu",
"brand.saving": "Enregistrement…",
@@ -5226,6 +5233,8 @@ export const fr: MessageDict = {
"pricing.section.publicBefore": "Offres publiques : Free, Starter, Growth, Business et Enterprise. Créez un compte Free, puis passez à un plan supérieur sous",
"pricing.section.publicOr": "ou",
"pricing.section.publicAfter": "via Stripe Checkout. Enterprise reste géré par les ventes.",
"pricing.section.guestPublicBefore": "Offres publiques: Free, Starter, Plus, Growth, Business, Scale et Enterprise. Creez un compte Free, puis",
"pricing.section.guestPublicAfter": "pour passer a un plan superieur via Stripe Checkout. Enterprise reste gere par les ventes.",
"pricing.section.plansLink": "Offres",
"pricing.section.billingLink": "Facturation",
"pricing.section.capabilitiesTitle": "Ce pour quoi chaque offre est conçue",
@@ -5291,7 +5300,7 @@ export const fr: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "Le plus populaire",
"pricing.card.custom": "Sur mesure",
"pricing.card.forever": "pour toujours",
+31 -22
View File
@@ -78,7 +78,7 @@ export const it: MessageDict = {
"auth.login.failed": "Accesso non riuscito",
"auth.login.passwordNotSet": "Questo account richiede ancora una password. Apri il link di invito o chiedi a un amministratore di generarne uno nuovo.",
"auth.login.setPasswordFirstTitle": "Imposta prima la password:",
"auth.login.setPasswordFirstBody": "usa il link di invito dalla tua email. Se il link è andato a un indirizzo vecchio (deriva email), chiedi a un amministratore dell'azienda di riemettere un invito per impostare la password a {email}.",
"auth.login.setPasswordFirstBody": "usa il link di invito dalla tua email. Se ti serve un nuovo link, chiedi a un amministratore dell'azienda di riemettere un link per impostare la password a {email}.",
"auth.login.yourEmail": "la tua email",
"auth.login.platformAdminsReissue": "Gli amministratori della piattaforma possono riemettere da",
"auth.login.adminUsersLink": "Admin → Utenti",
@@ -144,9 +144,9 @@ export const it: MessageDict = {
"auth.invite.acceptFailed": "Impossibile accettare l'invito",
"auth.invite.setPasswordFailed": "Impossibile impostare la password",
"auth.invite.expired": "Questo invito non è valido o è scaduto. Chiedi all'amministratore dell'azienda di inviare un nuovo invito, poi apri il nuovo link (o incolla il nuovo token qui sotto).",
"auth.invite.setPasswordExpired": "Questo link per impostare la password non è valido o è scaduto. Chiedi a un amministratore dell'azienda o della piattaforma di riemetterlo, poi apri il nuovo link (o incolla il nuovo token qui sotto).",
"auth.invite.setPasswordExpired": "Questo link per impostare la password non è valido o è scaduto. Chiedi a un amministratore dell'azienda di riemettere un link per impostare la password a questa email, poi apri il nuovo link (o incolla il nuovo token qui sotto).",
"auth.invite.emailMismatchDefault": "Hai effettuato l'accesso con un'email diversa da questo invito.",
"auth.invite.expiredFooter": "Link scaduto? Chiedi a un amministratore di riemetterlo — non c'è un'API di reinvio self-service. Amministratori della piattaforma:",
"auth.invite.expiredFooter": "Link scaduto? Chiedi a un amministratore dell'azienda di riemetterlo non c'è un'API di reinvio self-service.",
"auth.invite.adminUsersLink": "Admin → Utenti",
"auth.invite.doneTitle": "Fai parte del team",
"auth.invite.doneSetPasswordTitle": "Password salvata",
@@ -163,7 +163,7 @@ export const it: MessageDict = {
"auth.invite.switchAccountTitle": "Cambia account:",
"auth.invite.switchAccountBody": "esci, poi completa questo modulo con l'email invitata{emailSuffix}.",
"auth.invite.reissueTitle": "Percorso di riemissione:",
"auth.invite.reissueBody": "se la tua email di accesso reale è cambiata (deriva email), chiedi a un amministratore dell'azienda di revocare questo invito e inviarne uno nuovo all'email con cui accedi. Gli amministratori della piattaforma possono anche riemettere link per impostare la password da Admin → Utenti.",
"auth.invite.reissueBody": "chiedi a un amministratore dell'azienda di riemettere un link per impostare la password a questa email, oppure di revocare questo invito e inviarne uno nuovo all'email con cui accedi.",
"locale.switcher": "Lingua dell'interfaccia",
"locale.menu": "Scegli la lingua dell'interfaccia",
"locale.label": "Lingua della dashboard",
@@ -276,7 +276,7 @@ export const it: MessageDict = {
"settings.table.lastUsed": "Ultimo utilizzo",
"dashboard.demoSandbox": "Sandbox demo",
"dashboard.demoEmptyTitle": "La sandbox demo è vuota",
"dashboard.demoEmptyHint": "La sandbox demo è vuota — passa ad A1 o collega un feed per vedere statistiche reali del catalogo.",
"dashboard.demoEmptyHint": "La sandbox demo è vuota collega un feed per vedere statistiche reali del catalogo.",
"dashboard.overviewTitle": "Panoramica",
"dashboard.quickLinks": "Collegamenti rapidi",
"dashboard.whatsNew": "Novità",
@@ -319,7 +319,7 @@ export const it: MessageDict = {
"dashboard.workflow.exportMap": "Mappa poi esporta",
"dashboard.workflowHint": "Catalogo in ingresso → arricchisci / elabora → esporta o invia agli store. Vai al passo successivo per {name}.",
"dashboard.overviewHint": "Totali in tempo reale per {name}",
"dashboard.demoEmptyMessage": "Passa ad A1 (o un'altra azienda con dati) nell'intestazione, oppure collega un feed qui per popolare questa sandbox.",
"dashboard.demoEmptyMessage": "Collega un feed qui per popolare questa sandbox, oppure passa a un'altra azienda nell'intestazione.",
"dashboard.emptyTitle": "Importa un catalogo",
"dashboard.emptyMessage": "Catalogo in ingresso → arricchisci / elabora → esporta o invia agli store. Inizia con Per iniziare qui sotto (o collega un feed / carica un CSV).",
"dashboard.connectFeedAnyway": "Collega comunque un feed",
@@ -367,29 +367,29 @@ export const it: MessageDict = {
"activation.focus.sync": "Avanti: sincronizza un campione dopo aver salvato la mappatura. Usa Sync su un feed mappato — evita fonti non mappate.",
"activation.focus.dismiss": "Chiudi suggerimento",
"activation.dismiss": "Nascondi checklist",
"hypercare.report.title": "Hypercare di cutover",
"hypercare.report.title": "Serve aiuto dopo il trasferimento?",
"hypercare.report.message": "Dati mancanti o errati dopo la migrazione? Segnalalo così possiamo correggere il tuo workspace.",
"hypercare.report.cta": "Segnala dati mancanti o errati",
"hypercare.report.dismiss": "Ignora",
"hypercare.report.adminTriage": "Apri coda hypercare",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is “broken” — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutover — that is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -456,9 +456,9 @@ export const it: MessageDict = {
"errors.readOnly": "Il sistema è in modalità sola lettura. Le modifiche sono temporaneamente disabilitate.",
"errors.companyAdminDenied": "Solo gli amministratori dell'azienda possono {action}. Chiedi aiuto a un amministratore.",
"errors.companyAdminDenied.actionDefault": "fare questo",
"docs.tryIt.memberCannotCreate": "Solo gli admin azienda possono creare chiavi API. Le chiavi legacy non sono state migrate — incolla una chiave sotto o chiedi a un admin di crearne una nuova.",
"docs.tryIt.noKeysLegacyAdmin": "Le chiavi API legacy non sono state migrate. Creane una con Use my API key o Impostazioni → Chiavi API — il secret è mostrato una sola volta.",
"docs.tryIt.noKeysLegacyMember": "Nessuna chiave API elencata. Le chiavi legacy non sono state migrate — chiedi a un admin di crearne una, o incolla una chiave dk_.",
"docs.tryIt.memberCannotCreate": "Solo gli admin azienda possono creare chiavi API. Le chiavi non sono state migrate — incolla una chiave sotto o chiedi a un admin di crearne una nuova.",
"docs.tryIt.noKeysLegacyAdmin": "Le chiavi API non sono state migrate. Creane una con Use my API key o Impostazioni → Chiavi API — il secret è mostrato una sola volta.",
"docs.tryIt.noKeysLegacyMember": "Nessuna chiave API elencata. Le chiavi non sono state migrate — chiedi a un admin di crearne una, o incolla una chiave dk_.",
"errors.couldNotUpdateLanguage": "Impossibile aggiornare la lingua",
"toast.support.replyTitle": "Risposta del supporto",
"toast.support.replyBody": "Lo staff ha risposto al tuo ticket di supporto.",
@@ -3690,6 +3690,7 @@ export const it: MessageDict = {
"processing.receipt.cancelledDesc": "Il processo appena avviato è stato annullato.",
"processing.receipt.cancelFailed": "Impossibile annullare il processo",
"unsubscribe.title": "Annulla iscrizione",
"unsubscribe.useEmailLink": "Usa il link di disiscrizione nell'email che ti abbiamo inviato. Aprire questa pagina senza quel link non puo modificare le preferenze email.",
"unsubscribe.me": "Annulla la mia iscrizione",
"unsubscribe.invalidLink": "Link non valido",
"unsubscribe.failedShort": "Annullamento iscrizione non riuscito",
@@ -4727,6 +4728,10 @@ export const it: MessageDict = {
"seo.terms.description": "Termini di utilizzo di Descrybe: importazione di feed, arricchimento del catalogo, contenuti assistiti da IA, esportazioni e sincronizzazione WooCommerce per la tua azienda.",
"seo.features.title": "Funzionalità — Feed, arricchimento ed esportazione | Descrybe",
"seo.features.description": "Scopri come Descrybe importa i feed dei fornitori, mappa i campi alla tua tassonomia, arricchisce i dati di prodotto e distribuisce i cataloghi tramite feed di esportazione, WooCommerce o API.",
"seo.contactSales.title": "Contatta le vendite | Descrybe",
"seo.contactSales.description": "Parlaci del tuo catalogo e delle esigenze di capacita: prepareremo un piano personalizzato che puoi pagare online.",
"seo.unsubscribe.title": "Annulla iscrizione | Descrybe",
"seo.unsubscribe.description": "Interrompi le email di marketing Descrybe usando il link di disiscrizione nel messaggio che ti abbiamo inviato.",
"pricing.page.eyebrow": "Prezzi",
"pricing.page.title": "Piani semplici per cataloghi in crescita",
"pricing.page.lead": "Inizia gratis con mappatura feed e pulizia di base. Passa a un piano superiore quando ti servono titoli e descrizioni IA, più prodotti, feed di esportazione o sync WooCommerce — da Starter a Enterprise.",
@@ -4734,6 +4739,8 @@ export const it: MessageDict = {
"pricing.page.subscribedMid": "o confronta i piani in",
"pricing.page.plansLink": "Piani",
"pricing.page.subscribedAfter": ".",
"pricing.page.guestSubscribedBefore": "Gia abbonato?",
"pricing.page.guestSubscribedAfter": "per gestire la fatturazione e confrontare i piani.",
"legal.lastUpdated": "Ultimo aggiornamento: {date}",
"legal.backHome": "← Torna alla home",
"legal.emailLabel": "Email:",
@@ -5065,7 +5072,7 @@ export const it: MessageDict = {
"brand.uploadLogo": "Carica logo",
"brand.clear": "Cancella",
"brand.field.logoUrl": "URL del logo",
"brand.placeholder.logoUrl": "https://… oppure caricato /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://",
"brand.logoAlt": "Anteprima logo del brand",
"brand.tipsTitle": "Suggerimenti formula / anteprima",
"brand.saving": "Salvataggio…",
@@ -5226,6 +5233,8 @@ export const it: MessageDict = {
"pricing.section.publicBefore": "Piani pubblici: Free, Starter, Growth, Business ed Enterprise. Crea un account Free, poi passa a un piano superiore in",
"pricing.section.publicOr": "o",
"pricing.section.publicAfter": "tramite Stripe Checkout. Enterprise resta gestito dalle vendite.",
"pricing.section.guestPublicBefore": "Piani pubblici: Free, Starter, Plus, Growth, Business, Scale ed Enterprise. Crea un account Free, poi",
"pricing.section.guestPublicAfter": "per passare a un piano superiore tramite Stripe Checkout. Enterprise resta gestito dalle vendite.",
"pricing.section.plansLink": "Piani",
"pricing.section.billingLink": "Fatturazione",
"pricing.section.capabilitiesTitle": "A cosa è pensato ogni piano",
@@ -5291,7 +5300,7 @@ export const it: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "Più popolare",
"pricing.card.custom": "Personalizzato",
"pricing.card.forever": "per sempre",
+28 -19
View File
@@ -78,7 +78,7 @@ export const ja: MessageDict = {
"auth.login.failed": "ログインに失敗しました",
"auth.login.passwordNotSet": "このアカウントにはまだパスワードが必要です。招待リンクを開くか、管理者に再発行を依頼してください。",
"auth.login.setPasswordFirstTitle": "先にパスワードを設定:",
"auth.login.setPasswordFirstBody": "メールの招待リンクを使用してください。リンクが古いアドレスに送られた場合(メール変更)、会社の管理者に {email} 向けのパスワード設定招待の再発行を依頼してください。",
"auth.login.setPasswordFirstBody": "メールの招待リンクを使用してください。新しいリンクが必要な場合は、会社の管理者に {email} 向けのパスワード設定リンクの再発行を依頼してください。",
"auth.login.yourEmail": "あなたのメール",
"auth.login.platformAdminsReissue": "プラットフォーム管理者は次から再発行できます:",
"auth.login.adminUsersLink": "管理 → ユーザー",
@@ -144,9 +144,9 @@ export const ja: MessageDict = {
"auth.invite.acceptFailed": "招待を承認できませんでした",
"auth.invite.setPasswordFailed": "パスワードを設定できませんでした",
"auth.invite.expired": "この招待は無効または期限切れです。会社の管理者に新しい招待の送信を依頼し、新しいリンクを開くか(下に新しいトークンを貼り付けてください)。",
"auth.invite.setPasswordExpired": "このパスワード設定リンクは無効または期限切れです。会社またはプラットフォームの管理者に再発行を依頼し、新しいリンクを開くか(下に新しいトークンを貼り付けてください)。",
"auth.invite.setPasswordExpired": "このパスワード設定リンクは無効または期限切れです。会社の管理者にこのメール向けのパスワード設定リンクの再発行を依頼し、新しいリンクを開くか(下に新しいトークンを貼り付けてください)。",
"auth.invite.emailMismatchDefault": "この招待とは別のメールアドレスでログインしています。",
"auth.invite.expiredFooter": "期限切れのリンクですか?管理者に再発行を依頼してください — セルフサービスの再送信APIはありません。プラットフォーム管理者:",
"auth.invite.expiredFooter": "期限切れのリンクですか?会社の管理者に再発行を依頼してください — セルフサービスの再送信APIはありません。",
"auth.invite.adminUsersLink": "管理 → ユーザー",
"auth.invite.doneTitle": "チームに参加しました",
"auth.invite.doneSetPasswordTitle": "パスワードを保存しました",
@@ -163,7 +163,7 @@ export const ja: MessageDict = {
"auth.invite.switchAccountTitle": "アカウント切替:",
"auth.invite.switchAccountBody": "ログアウトしてから、招待されたメール{emailSuffix}でこのフォームを完了してください。",
"auth.invite.reissueTitle": "再発行の手順:",
"auth.invite.reissueBody": "実際のログイン用メールが変わった場合(メール変更)、会社の管理者にこの招待の取り消しと、ログインに使うメールへの新しい招待送信を依頼してください。プラットフォーム管理者は「管理 → ユーザー」からパスワード設定リンクも再発行できます。",
"auth.invite.reissueBody": "会社の管理者にこのメール向けのパスワード設定リンクの再発行を依頼するか、この招待を取り消してログインに使うメールへ新しい招待を送るよう依頼してください。",
"locale.switcher": "インターフェース言語",
"locale.menu": "インターフェース言語を選択",
"locale.label": "ダッシュボードの言語",
@@ -276,7 +276,7 @@ export const ja: MessageDict = {
"settings.table.lastUsed": "最終使用",
"dashboard.demoSandbox": "デモサンドボックス",
"dashboard.demoEmptyTitle": "デモサンドボックスは空です",
"dashboard.demoEmptyHint": "デモサンドボックスは空です — A1に切り替えるかフィードを接続して実際のカタログ統計を表示します。",
"dashboard.demoEmptyHint": "デモサンドボックスは空です — フィードを接続して実際のカタログ統計を表示します。",
"dashboard.overviewTitle": "概要",
"dashboard.quickLinks": "クイックリンク",
"dashboard.whatsNew": "新着情報",
@@ -319,7 +319,7 @@ export const ja: MessageDict = {
"dashboard.workflow.exportMap": "マップしてエクスポート",
"dashboard.workflowHint": "カタログ取込 → 充実 / 処理 → エクスポートまたはストアへプッシュ。{name} の次のステップへ。",
"dashboard.overviewHint": "{name} のリアルタイム合計",
"dashboard.demoEmptyMessage": "ヘッダーでA1(または別のシード済み会社)に切り替えるか、ここでフィードを接続してサンドボックスにデータを入れます。",
"dashboard.demoEmptyMessage": "ここでフィードを接続してサンドボックスにデータを入れるか、ヘッダーで別の会社に切り替えます。",
"dashboard.emptyTitle": "カタログを取り込む",
"dashboard.emptyMessage": "カタログ取込 → 充実 / 処理 → エクスポートまたはストアへプッシュ。下の「はじめに」から開始(またはフィード接続 / CSVアップロード)。",
"dashboard.connectFeedAnyway": "それでもフィードを接続",
@@ -367,29 +367,29 @@ export const ja: MessageDict = {
"activation.focus.sync": "次へ: マッピング保存後にサンプルを同期します。マップ済みフィードで Sync を使い、未マップのソースは避けてください。",
"activation.focus.dismiss": "ヒントを閉じる",
"activation.dismiss": "チェックリストを閉じる",
"hypercare.report.title": "カットオーバー hypercare",
"hypercare.report.title": "移行後のサポートが必要ですか?",
"hypercare.report.message": "移行後にデータの欠落や誤りがありますか?報告いただければワークスペースを修正します。",
"hypercare.report.cta": "欠落・誤ったデータを報告",
"hypercare.report.dismiss": "閉じる",
"hypercare.report.adminTriage": "hypercareキューを開く",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is “broken” — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutover — that is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -3690,6 +3690,7 @@ export const ja: MessageDict = {
"processing.receipt.cancelledDesc": "開始直後のジョブがキャンセルされました。",
"processing.receipt.cancelFailed": "処理ジョブをキャンセルできませんでした",
"unsubscribe.title": "配信停止",
"unsubscribe.useEmailLink": "Use the unsubscribe link in the email we sent you. Opening this page without that link cannot change your email preferences.",
"unsubscribe.me": "配信を停止する",
"unsubscribe.invalidLink": "無効なリンク",
"unsubscribe.failedShort": "配信停止に失敗しました",
@@ -4727,6 +4728,10 @@ export const ja: MessageDict = {
"seo.terms.description": "Descrybe の利用条件:フィードのインポート、カタログのエンリッチメント、AI 支援コンテンツ、エクスポート、およびビジネス向け WooCommerce 同期。",
"seo.features.title": "機能 — フィード、エンリッチメント、エクスポート | Descrybe",
"seo.features.description": "Descrybe がサプライヤーフィードを取り込み、フィールドをタクソノミーにマッピングし、商品データをエンリッチし、エクスポートフィード、WooCommerce、または API でカタログを配信する仕組みをご確認ください。",
"seo.contactSales.title": "Contact sales | Descrybe",
"seo.contactSales.description": "Tell us about your catalog and capacity needs — we will prepare a custom plan you can pay online.",
"seo.unsubscribe.title": "Unsubscribe | Descrybe",
"seo.unsubscribe.description": "Stop marketing emails from Descrybe using the unsubscribe link in the message we sent you.",
"pricing.page.eyebrow": "料金",
"pricing.page.title": "成長するカタログ向けのシンプルなプラン",
"pricing.page.lead": "フィードのマッピングと基本的な整形から無料で始められます。AI によるタイトル/説明、商品数の増加、エクスポートフィード、WooCommerce 連携が必要になったらアップグレード — Starter から Enterprise まで。",
@@ -4734,6 +4739,8 @@ export const ja: MessageDict = {
"pricing.page.subscribedMid": "、プラン比較は",
"pricing.page.plansLink": "プラン",
"pricing.page.subscribedAfter": "へ。",
"pricing.page.guestSubscribedBefore": "Already subscribed?",
"pricing.page.guestSubscribedAfter": "to manage billing and compare plans.",
"legal.lastUpdated": "最終更新日: {date}",
"legal.backHome": "← ホームに戻る",
"legal.emailLabel": "メール:",
@@ -5065,7 +5072,7 @@ export const ja: MessageDict = {
"brand.uploadLogo": "ロゴをアップロード",
"brand.clear": "クリア",
"brand.field.logoUrl": "ロゴ URL",
"brand.placeholder.logoUrl": "https://… またはアップロード済み /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://",
"brand.logoAlt": "ブランドロゴのプレビュー",
"brand.tipsTitle": "数式 / プレビューのヒント",
"brand.saving": "保存中…",
@@ -5226,6 +5233,8 @@ export const ja: MessageDict = {
"pricing.section.publicBefore": "公開プラン:Free、Starter、Growth、Business、Enterprise。Freeアカウントを作成し、その後",
"pricing.section.publicOr": "または",
"pricing.section.publicAfter": "でStripe Checkout経由でアップグレード。Enterpriseはセールス主導のままです。",
"pricing.section.guestPublicBefore": "Public plans: Free, Starter, Plus, Growth, Business, Scale, and Enterprise. Create a Free account, then",
"pricing.section.guestPublicAfter": "to upgrade via Stripe Checkout. Enterprise remains sales-led.",
"pricing.section.plansLink": "プラン",
"pricing.section.billingLink": "請求",
"pricing.section.capabilitiesTitle": "各プランが想定する用途",
@@ -5291,7 +5300,7 @@ export const ja: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "一番人気",
"pricing.card.custom": "カスタム",
"pricing.card.forever": "ずっと",
+31 -22
View File
@@ -78,7 +78,7 @@ export const nl: MessageDict = {
"auth.login.failed": "Inloggen mislukt",
"auth.login.passwordNotSet": "Dit account heeft nog een wachtwoord nodig. Open uw uitnodigingslink of vraag een beheerder om een nieuwe.",
"auth.login.setPasswordFirstTitle": "Stel eerst een wachtwoord in:",
"auth.login.setPasswordFirstBody": "gebruik de uitnodigingslink uit uw e-mail. Als de link naar een oud adres ging (e-maildrift), vraag dan een bedrijfsbeheerder om een nieuwe set-wachtwoorduitnodiging naar {email} te sturen.",
"auth.login.setPasswordFirstBody": "gebruik de uitnodigingslink uit uw e-mail. Als u een nieuwe link nodig hebt, vraag dan een bedrijfsbeheerder om een set-wachtwoordlink opnieuw uit te geven naar {email}.",
"auth.login.yourEmail": "uw e-mail",
"auth.login.platformAdminsReissue": "Platformbeheerders kunnen opnieuw uitgeven via",
"auth.login.adminUsersLink": "Admin → Gebruikers",
@@ -144,9 +144,9 @@ export const nl: MessageDict = {
"auth.invite.acceptFailed": "Uitnodiging kon niet worden geaccepteerd",
"auth.invite.setPasswordFailed": "Wachtwoord kon niet worden ingesteld",
"auth.invite.expired": "Deze uitnodiging is ongeldig of verlopen. Vraag uw bedrijfsbeheerder om een nieuwe uitnodiging te sturen en open de nieuwe link (of plak het nieuwe token hieronder).",
"auth.invite.setPasswordExpired": "Deze set-wachtwoordlink is ongeldig of verlopen. Vraag een bedrijfs- of platformbeheerder om hem opnieuw uit te geven en open de nieuwe link (of plak het nieuwe token hieronder).",
"auth.invite.setPasswordExpired": "Deze set-wachtwoordlink is ongeldig of verlopen. Vraag een bedrijfsbeheerder om een set-wachtwoordlink opnieuw uit te geven naar dit e-mailadres en open de nieuwe link (of plak het nieuwe token hieronder).",
"auth.invite.emailMismatchDefault": "U bent ingelogd met een ander e-mailadres dan deze uitnodiging.",
"auth.invite.expiredFooter": "Verlopen link? Vraag een beheerder om opnieuw uit te geven — er is geen self-service-API voor opnieuw verzenden. Platformbeheerders:",
"auth.invite.expiredFooter": "Verlopen link? Vraag een bedrijfsbeheerder om opnieuw uit te geven er is geen self-service-API voor opnieuw verzenden.",
"auth.invite.adminUsersLink": "Admin → Gebruikers",
"auth.invite.doneTitle": "U bent in het team",
"auth.invite.doneSetPasswordTitle": "Wachtwoord opgeslagen",
@@ -163,7 +163,7 @@ export const nl: MessageDict = {
"auth.invite.switchAccountTitle": "Account wisselen:",
"auth.invite.switchAccountBody": "log uit en voltooi dit formulier met het uitgenodigde e-mailadres{emailSuffix}.",
"auth.invite.reissueTitle": "Pad voor opnieuw uitgeven:",
"auth.invite.reissueBody": "als uw echte login-e-mail is gewijzigd (e-maildrift), vraag dan een bedrijfsbeheerder om deze uitnodiging in te trekken en een nieuwe te sturen naar het e-mailadres waarmee u inlogt. Platformbeheerders kunnen ook set-wachtwoordlinks opnieuw uitgeven via Admin → Gebruikers.",
"auth.invite.reissueBody": "vraag een bedrijfsbeheerder om een set-wachtwoordlink opnieuw uit te geven naar dit e-mailadres, of om deze uitnodiging in te trekken en een nieuwe te sturen naar het e-mailadres waarmee u inlogt.",
"locale.switcher": "Interfacetaal",
"locale.menu": "Kies interfacetaal",
"locale.label": "Dashboardtaal",
@@ -276,7 +276,7 @@ export const nl: MessageDict = {
"settings.table.lastUsed": "Laatst gebruikt",
"dashboard.demoSandbox": "Demo-sandbox",
"dashboard.demoEmptyTitle": "Demo-sandbox is leeg",
"dashboard.demoEmptyHint": "Demo-sandbox is leeg — schakel over naar A1 of koppel een feed om echte catalogusstatistieken te zien.",
"dashboard.demoEmptyHint": "Demo-sandbox is leeg koppel een feed om echte catalogusstatistieken te zien.",
"dashboard.overviewTitle": "Overzicht",
"dashboard.quickLinks": "Snelle links",
"dashboard.whatsNew": "Wat is nieuw",
@@ -319,7 +319,7 @@ export const nl: MessageDict = {
"dashboard.workflow.exportMap": "Mappen en exporteren",
"dashboard.workflowHint": "Catalogus in → verrijken / verwerken → exporteren of pushen naar winkels. Ga naar de volgende stap voor {name}.",
"dashboard.overviewHint": "Live totalen voor {name}",
"dashboard.demoEmptyMessage": "Schakel in de header over naar A1 (of een ander geseeded bedrijf), of koppel hier een feed om deze sandbox te vullen.",
"dashboard.demoEmptyMessage": "Koppel hier een feed om deze sandbox te vullen, of schakel in de header over naar een ander bedrijf.",
"dashboard.emptyTitle": "Haal een catalogus binnen",
"dashboard.emptyMessage": "Catalogus in → verrijken / verwerken → exporteren of pushen naar winkels. Begin met Aan de slag hieronder (of koppel een feed / upload een CSV).",
"dashboard.connectFeedAnyway": "Feed toch koppelen",
@@ -367,29 +367,29 @@ export const nl: MessageDict = {
"activation.focus.sync": "Volgende: synchroniseer een steekproef na het opslaan van de mapping. Gebruik Sync op een gemapte feed — vermijd ongemapte bronnen.",
"activation.focus.dismiss": "Hint sluiten",
"activation.dismiss": "Checklist verbergen",
"hypercare.report.title": "Cutover-hypercare",
"hypercare.report.title": "Hulp nodig na de verhuizing?",
"hypercare.report.message": "Ontbrekende of verkeerde data na migratie? Meld het zodat we je workspace kunnen herstellen.",
"hypercare.report.cta": "Ontbrekende of verkeerde data melden",
"hypercare.report.dismiss": "Sluiten",
"hypercare.report.adminTriage": "Hypercare-wachtrij openen",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is “broken” — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutover — that is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -456,9 +456,9 @@ export const nl: MessageDict = {
"errors.readOnly": "Het systeem staat in alleen-lezenmodus. Wijzigingen zijn tijdelijk uitgeschakeld.",
"errors.companyAdminDenied": "Alleen bedrijfsbeheerders kunnen {action}. Vraag een beheerder om hulp.",
"errors.companyAdminDenied.actionDefault": "dit doen",
"docs.tryIt.memberCannotCreate": "Alleen bedrijfsadmins kunnen API-sleutels maken. Legacy-sleutels zijn niet gemigreerd — plak hieronder een sleutel of vraag een admin om een nieuwe.",
"docs.tryIt.noKeysLegacyAdmin": "Legacy API-sleutels zijn niet gemigreerd. Maak een nieuwe via Use my API key of Instellingen → API-sleutels — het geheim wordt slechts één keer getoond.",
"docs.tryIt.noKeysLegacyMember": "Geen API-sleutels vermeld. Legacy-sleutels niet gemigreerd — vraag een admin er een aan te maken, of plak een dk_-sleutel.",
"docs.tryIt.memberCannotCreate": "Alleen bedrijfsadmins kunnen API-sleutels maken. sleutels zijn niet gemigreerd — plak hieronder een sleutel of vraag een admin om een nieuwe.",
"docs.tryIt.noKeysLegacyAdmin": "API-sleutels zijn niet gemigreerd. Maak een nieuwe via Use my API key of Instellingen → API-sleutels — het geheim wordt slechts één keer getoond.",
"docs.tryIt.noKeysLegacyMember": "Geen API-sleutels vermeld. sleutels niet gemigreerd — vraag een admin er een aan te maken, of plak een dk_-sleutel.",
"errors.couldNotUpdateLanguage": "Taal kon niet worden bijgewerkt",
"toast.support.replyTitle": "Supportantwoord",
"toast.support.replyBody": "Medewerkers hebben gereageerd op uw supportticket.",
@@ -3690,6 +3690,7 @@ export const nl: MessageDict = {
"processing.receipt.cancelledDesc": "De zojuist gestarte taak is geannuleerd.",
"processing.receipt.cancelFailed": "Kon verwerkingstaak niet annuleren",
"unsubscribe.title": "Uitschrijven",
"unsubscribe.useEmailLink": "Gebruik de afmeldlink in de e-mail die we u hebben gestuurd. Deze pagina openen zonder die link kan uw e-mailvoorkeuren niet wijzigen.",
"unsubscribe.me": "Mij uitschrijven",
"unsubscribe.invalidLink": "Ongeldige link",
"unsubscribe.failedShort": "Uitschrijven mislukt",
@@ -4727,6 +4728,10 @@ export const nl: MessageDict = {
"seo.terms.description": "Gebruiksvoorwaarden van Descrybe: feed-import, catalogusverrijking, AI-ondersteunde content, exporten en synchronisatie met WooCommerce voor uw bedrijf.",
"seo.features.title": "Functies — Feeds, verrijking en export | Descrybe",
"seo.features.description": "Ontdek hoe Descrybe leveranciersfeeds importeert, velden toewijst aan uw taxonomie, productgegevens verrijkt en catalogi levert via exportfeeds, WooCommerce of API.",
"seo.contactSales.title": "Contact sales | Descrybe",
"seo.contactSales.description": "Vertel ons over uw catalogus en capaciteitsbehoefte: we maken een plan op maat dat u online kunt betalen.",
"seo.unsubscribe.title": "Uitschrijven | Descrybe",
"seo.unsubscribe.description": "Stop marketingmails van Descrybe via de afmeldlink in het bericht dat we u hebben gestuurd.",
"pricing.page.eyebrow": "Prijzen",
"pricing.page.title": "Eenvoudige plannen voor groeiende catalogi",
"pricing.page.lead": "Begin gratis met feed-mapping en basisopschoning. Upgrade wanneer je AI-titels en -beschrijvingen, meer producten, exportfeeds of WooCommerce-sync nodig hebt — van Starter tot Enterprise.",
@@ -4734,6 +4739,8 @@ export const nl: MessageDict = {
"pricing.page.subscribedMid": "of vergelijk plannen in",
"pricing.page.plansLink": "Plannen",
"pricing.page.subscribedAfter": ".",
"pricing.page.guestSubscribedBefore": "Al geabonneerd?",
"pricing.page.guestSubscribedAfter": "om facturatie te beheren en plannen te vergelijken.",
"legal.lastUpdated": "Laatst bijgewerkt: {date}",
"legal.backHome": "← Terug naar home",
"legal.emailLabel": "E-mail:",
@@ -5065,7 +5072,7 @@ export const nl: MessageDict = {
"brand.uploadLogo": "Logo uploaden",
"brand.clear": "Wissen",
"brand.field.logoUrl": "Logo-URL",
"brand.placeholder.logoUrl": "https://… of geüpload /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://",
"brand.logoAlt": "Voorbeeld van merklogo",
"brand.tipsTitle": "Formule- / voorbeeldtips",
"brand.saving": "Opslaan…",
@@ -5226,6 +5233,8 @@ export const nl: MessageDict = {
"pricing.section.publicBefore": "Publieke plannen: Free, Starter, Growth, Business en Enterprise. Maak een Free-account aan en upgrade daarna onder",
"pricing.section.publicOr": "of",
"pricing.section.publicAfter": "via Stripe Checkout. Enterprise blijft sales-gestuurd.",
"pricing.section.guestPublicBefore": "Publieke plannen: Free, Starter, Plus, Growth, Business, Scale en Enterprise. Maak een Free-account aan en",
"pricing.section.guestPublicAfter": "om te upgraden via Stripe Checkout. Enterprise blijft sales-gestuurd.",
"pricing.section.plansLink": "Plannen",
"pricing.section.billingLink": "Facturering",
"pricing.section.capabilitiesTitle": "Waarvoor elk plan is gebouwd",
@@ -5291,7 +5300,7 @@ export const nl: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "Meest populair",
"pricing.card.custom": "Op maat",
"pricing.card.forever": "voor altijd",
+28 -19
View File
@@ -78,7 +78,7 @@ export const pl: MessageDict = {
"auth.login.failed": "Logowanie nie powiodło się",
"auth.login.passwordNotSet": "To konto nadal wymaga hasła. Otwórz link zaproszenia lub poproś administratora o wystawienie nowego.",
"auth.login.setPasswordFirstTitle": "Najpierw ustaw hasło:",
"auth.login.setPasswordFirstBody": "użyj linku zaproszenia z e-maila. Jeśli link poszedł na stary adres (dryf e-mail), poproś administratora firmy o ponowne wystawienie zaproszenia do ustawienia hasła na {email}.",
"auth.login.setPasswordFirstBody": "użyj linku zaproszenia z e-maila. Jeśli potrzebujesz nowego linku, poproś administratora firmy o ponowne wystawienie linku do ustawienia hasła na {email}.",
"auth.login.yourEmail": "twój e-mail",
"auth.login.platformAdminsReissue": "Administratorzy platformy mogą ponownie wystawić z",
"auth.login.adminUsersLink": "Admin → Użytkownicy",
@@ -144,9 +144,9 @@ export const pl: MessageDict = {
"auth.invite.acceptFailed": "Nie można zaakceptować zaproszenia",
"auth.invite.setPasswordFailed": "Nie można ustawić hasła",
"auth.invite.expired": "To zaproszenie jest nieprawidłowe lub wygasło. Poproś administratora firmy o nowe zaproszenie, a następnie otwórz nowy link (lub wklej nowy token poniżej).",
"auth.invite.setPasswordExpired": "Ten link do ustawienia hasła jest nieprawidłowy lub wygasł. Poproś administratora firmy lub platformy o ponowne wystawienie, a następnie otwórz nowy link (lub wklej nowy token poniżej).",
"auth.invite.setPasswordExpired": "Ten link do ustawienia hasła jest nieprawidłowy lub wygasł. Poproś administratora firmy o ponowne wystawienie linku do ustawienia hasła na ten e-mail, a następnie otwórz nowy link (lub wklej nowy token poniżej).",
"auth.invite.emailMismatchDefault": "Jesteś zalogowany na inny e-mail niż w tym zaproszeniu.",
"auth.invite.expiredFooter": "Wygasły link? Poproś administratora o ponowne wystawienie — nie ma API samodzielnego ponownego wysyłania. Administratorzy platformy:",
"auth.invite.expiredFooter": "Wygasły link? Poproś administratora firmy o ponowne wystawienie nie ma API samodzielnego ponownego wysyłania.",
"auth.invite.adminUsersLink": "Admin → Użytkownicy",
"auth.invite.doneTitle": "JesteÅ› w zespole",
"auth.invite.doneSetPasswordTitle": "Hasło zapisane",
@@ -163,7 +163,7 @@ export const pl: MessageDict = {
"auth.invite.switchAccountTitle": "Zmień konto:",
"auth.invite.switchAccountBody": "wyloguj się, a następnie dokończ ten formularz zaproszonym e-mailem{emailSuffix}.",
"auth.invite.reissueTitle": "Ścieżka ponownego wystawienia:",
"auth.invite.reissueBody": "jeśli zmienił się Twój prawdziwy e-mail logowania (dryf e-mail), poproś administratora firmy o unieważnienie tego zaproszenia i wysłanie nowego na e-mail używany do logowania. Administratorzy platformy mogą też ponownie wystawiać linki ustawienia hasła w Admin → Użytkownicy.",
"auth.invite.reissueBody": "poproś administratora firmy o ponowne wystawienie linku do ustawienia hasła na ten e-mail albo o unieważnienie tego zaproszenia i wysłanie nowego na e-mail używany do logowania.",
"locale.switcher": "Język interfejsu",
"locale.menu": "Wybierz język interfejsu",
"locale.label": "Język panelu",
@@ -276,7 +276,7 @@ export const pl: MessageDict = {
"settings.table.lastUsed": "Ostatnio użyty",
"dashboard.demoSandbox": "Piaskownica demo",
"dashboard.demoEmptyTitle": "Piaskownica demo jest pusta",
"dashboard.demoEmptyHint": "Piaskownica demo jest pusta — przełącz na A1 lub podłącz feed, aby zobaczyć realne statystyki katalogu.",
"dashboard.demoEmptyHint": "Piaskownica demo jest pusta — podłącz feed, aby zobaczyć realne statystyki katalogu.",
"dashboard.overviewTitle": "PrzeglÄ…d",
"dashboard.quickLinks": "Szybkie linki",
"dashboard.whatsNew": "Co nowego",
@@ -319,7 +319,7 @@ export const pl: MessageDict = {
"dashboard.workflow.exportMap": "Mapuj, potem eksportuj",
"dashboard.workflowHint": "Katalog wejściowy → wzbogacenie / przetwarzanie → eksport lub wypchnięcie do sklepów. Przejdź do następnego kroku dla {name}.",
"dashboard.overviewHint": "Bieżące sumy dla {name}",
"dashboard.demoEmptyMessage": "Przełącz na A1 (lub inną firmę z danymi) w nagłówku albo podłącz tu feed, aby wypełnić tę piaskownicę.",
"dashboard.demoEmptyMessage": "Podłącz tu feed, aby wypełnić tę piaskownicę, albo przełącz na inną firmę w nagłówku.",
"dashboard.emptyTitle": "WciÄ…gnij katalog",
"dashboard.emptyMessage": "Katalog wejściowy → wzbogacenie / przetwarzanie → eksport lub wypchnięcie do sklepów. Zacznij od Pierwsze kroki poniżej (lub podłącz feed / prześlij CSV).",
"dashboard.connectFeedAnyway": "Podłącz feed mimo to",
@@ -367,29 +367,29 @@ export const pl: MessageDict = {
"activation.focus.sync": "Dalej: zsynchronizuj próbkę po zapisaniu mapowania. Użyj Sync na zmapowanym feedzie — unikaj niezmapowanych źródeł.",
"activation.focus.dismiss": "Zamknij podpowiedź",
"activation.dismiss": "Ukryj listÄ™",
"hypercare.report.title": "Hypercare cutover",
"hypercare.report.title": "Potrzebujesz pomocy po przeniesieniu?",
"hypercare.report.message": "Brakujące lub błędne dane po migracji? Zgłoś to, abyśmy mogli naprawić Twój workspace.",
"hypercare.report.cta": "Zgłoś brakujące lub błędne dane",
"hypercare.report.dismiss": "Odrzuć",
"hypercare.report.adminTriage": "Otwórz kolejkę hypercare",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is “broken” — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutover — that is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -3690,6 +3690,7 @@ export const pl: MessageDict = {
"processing.receipt.cancelledDesc": "Właśnie uruchomione zadanie zostało anulowane.",
"processing.receipt.cancelFailed": "Nie można anulować zadania przetwarzania",
"unsubscribe.title": "Wypisz siÄ™",
"unsubscribe.useEmailLink": "Uzyj linku wypisania w e-mailu, ktory wyslalismy. Otwarcie tej strony bez tego linku nie zmieni preferencji e-mail.",
"unsubscribe.me": "Wypisz mnie",
"unsubscribe.invalidLink": "Nieprawidłowy link",
"unsubscribe.failedShort": "Wypisanie nie powiodło się",
@@ -4727,6 +4728,10 @@ export const pl: MessageDict = {
"seo.terms.description": "Warunki korzystania z Descrybe: import feedów, wzbogacanie katalogu, treści wspierane przez AI, eksporty oraz synchronizacja z WooCommerce dla Twojej firmy.",
"seo.features.title": "Funkcje — Feedy, wzbogacanie i eksport | Descrybe",
"seo.features.description": "Dowiedz się, jak Descrybe importuje feedy dostawców, mapuje pola na Twoją taksonomię, wzbogaca dane produktów i dostarcza katalogi przez feedy eksportowe, WooCommerce lub API.",
"seo.contactSales.title": "Kontakt ze sprzedaza | Descrybe",
"seo.contactSales.description": "Opowiedz nam o katalogu i potrzebach pojemnosci: przygotujemy plan niestandardowy, ktory oplacisz online.",
"seo.unsubscribe.title": "Wypisz sie | Descrybe",
"seo.unsubscribe.description": "Zatrzymaj e-maile marketingowe Descrybe, uzywajac linku wypisania w wiadomosci, ktora wyslalismy.",
"pricing.page.eyebrow": "Cennik",
"pricing.page.title": "Proste plany dla rosnących katalogów",
"pricing.page.lead": "Zacznij za darmo od mapowania feedów i podstawowego czyszczenia. Ulepsz, gdy potrzebujesz tytułów i opisów AI, większej liczby produktów, feedów eksportu lub synchronizacji WooCommerce — od Starter do Enterprise.",
@@ -4734,6 +4739,8 @@ export const pl: MessageDict = {
"pricing.page.subscribedMid": "lub porównaj plany w",
"pricing.page.plansLink": "Plany",
"pricing.page.subscribedAfter": ".",
"pricing.page.guestSubscribedBefore": "Masz juz subskrypcje?",
"pricing.page.guestSubscribedAfter": "aby zarzadzac platnosciami i porownac plany.",
"legal.lastUpdated": "Ostatnia aktualizacja: {date}",
"legal.backHome": "← Powrót do strony głównej",
"legal.emailLabel": "E-mail:",
@@ -5065,7 +5072,7 @@ export const pl: MessageDict = {
"brand.uploadLogo": "Prześlij logo",
"brand.clear": "Wyczyść",
"brand.field.logoUrl": "URL logo",
"brand.placeholder.logoUrl": "https://… lub przesłane /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://",
"brand.logoAlt": "PodglÄ…d logo marki",
"brand.tipsTitle": "Wskazówki formuły / podglądu",
"brand.saving": "Zapisywanie…",
@@ -5226,6 +5233,8 @@ export const pl: MessageDict = {
"pricing.section.publicBefore": "Plany publiczne: Free, Starter, Growth, Business i Enterprise. Utwórz konto Free, a potem przejdź wyżej w",
"pricing.section.publicOr": "lub",
"pricing.section.publicAfter": "przez Stripe Checkout. Enterprise pozostaje obsługiwany przez sprzedaż.",
"pricing.section.guestPublicBefore": "Plany publiczne: Free, Starter, Plus, Growth, Business, Scale i Enterprise. Utworz konto Free, a potem",
"pricing.section.guestPublicAfter": "aby przejsc wyzej przez Stripe Checkout. Enterprise pozostaje obslugiwany przez sprzedaz.",
"pricing.section.plansLink": "Plany",
"pricing.section.billingLink": "Płatności",
"pricing.section.capabilitiesTitle": "Do czego jest stworzony każdy plan",
@@ -5291,7 +5300,7 @@ export const pl: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "Najpopularniejszy",
"pricing.card.custom": "Indywidualny",
"pricing.card.forever": "na zawsze",
+28 -19
View File
@@ -78,7 +78,7 @@ export const pt: MessageDict = {
"auth.login.failed": "Falha no início de sessão",
"auth.login.passwordNotSet": "Esta conta ainda precisa de uma palavra-passe. Abra o link do convite ou peça a um administrador para emitir um novo.",
"auth.login.setPasswordFirstTitle": "Defina primeiro a palavra-passe:",
"auth.login.setPasswordFirstBody": "utilize o link do convite do seu e-mail. Se o link foi para um endereço antigo (desvio de e-mail), peça a um administrador da empresa para emitir um novo convite de definição de palavra-passe para {email}.",
"auth.login.setPasswordFirstBody": "utilize o link do convite do seu e-mail. Se precisar de um novo link, peça a um administrador da empresa para reemitir um link de definição de palavra-passe para {email}.",
"auth.login.yourEmail": "o seu e-mail",
"auth.login.platformAdminsReissue": "Os administradores da plataforma podem reemitir a partir de",
"auth.login.adminUsersLink": "Admin → Utilizadores",
@@ -144,9 +144,9 @@ export const pt: MessageDict = {
"auth.invite.acceptFailed": "Não foi possível aceitar o convite",
"auth.invite.setPasswordFailed": "Não foi possível definir a palavra-passe",
"auth.invite.expired": "Este convite é inválido ou expirou. Peça ao administrador da empresa para enviar um novo convite e abra o novo link (ou cole o novo token abaixo).",
"auth.invite.setPasswordExpired": "Este link de definição de palavra-passe é inválido ou expirou. Peça a um administrador da empresa ou da plataforma para o reemitir e abra o novo link (ou cole o novo token abaixo).",
"auth.invite.setPasswordExpired": "Este link de definição de palavra-passe é inválido ou expirou. Peça a um administrador da empresa para reemitir um link de definição de palavra-passe para este e-mail e abra o novo link (ou cole o novo token abaixo).",
"auth.invite.emailMismatchDefault": "Tem sessão iniciada com um e-mail diferente deste convite.",
"auth.invite.expiredFooter": "Link expirado? Peça a um administrador para o reemitir — não há API de reenvio self-service. Administradores da plataforma:",
"auth.invite.expiredFooter": "Link expirado? Peça a um administrador da empresa para o reemitir — não há API de reenvio self-service.",
"auth.invite.adminUsersLink": "Admin → Utilizadores",
"auth.invite.doneTitle": "Já faz parte da equipa",
"auth.invite.doneSetPasswordTitle": "Palavra-passe guardada",
@@ -163,7 +163,7 @@ export const pt: MessageDict = {
"auth.invite.switchAccountTitle": "Mudar de conta:",
"auth.invite.switchAccountBody": "termine a sessão e conclua este formulário com o e-mail convidado{emailSuffix}.",
"auth.invite.reissueTitle": "Caminho de reemissão:",
"auth.invite.reissueBody": "se o seu e-mail real de início de sessão mudou (desvio de e-mail), peça a um administrador da empresa para revogar este convite e enviar um novo para o e-mail que utiliza para iniciar sessão. Os administradores da plataforma também podem reemitir links de definição de palavra-passe em Admin → Utilizadores.",
"auth.invite.reissueBody": "peça a um administrador da empresa para reemitir um link de definição de palavra-passe para este e-mail, ou para revogar este convite e enviar um novo para o e-mail que utiliza para iniciar sessão.",
"locale.switcher": "Idioma da interface",
"locale.menu": "Escolher idioma da interface",
"locale.label": "Idioma do painel",
@@ -276,7 +276,7 @@ export const pt: MessageDict = {
"settings.table.lastUsed": "Última utilização",
"dashboard.demoSandbox": "Sandbox de demonstração",
"dashboard.demoEmptyTitle": "A sandbox de demonstração está vazia",
"dashboard.demoEmptyHint": "A sandbox de demonstração está vazia — mude para A1 ou ligue um feed para ver estatísticas reais do catálogo.",
"dashboard.demoEmptyHint": "A sandbox de demonstração está vazia ligue um feed para ver estatísticas reais do catálogo.",
"dashboard.overviewTitle": "Visão geral",
"dashboard.quickLinks": "Ligações rápidas",
"dashboard.whatsNew": "Novidades",
@@ -319,7 +319,7 @@ export const pt: MessageDict = {
"dashboard.workflow.exportMap": "Mapear e exportar",
"dashboard.workflowHint": "Catálogo a entrar → enriquecer / processar → exportar ou enviar para lojas. Salte para o passo seguinte para {name}.",
"dashboard.overviewHint": "Totais em direto para {name}",
"dashboard.demoEmptyMessage": "Mude para A1 (ou outra empresa com dados) no cabeçalho, ou ligue um feed aqui para preencher esta sandbox.",
"dashboard.demoEmptyMessage": "Ligue um feed aqui para preencher esta sandbox, ou mude para outra empresa no cabeçalho.",
"dashboard.emptyTitle": "Importe um catálogo",
"dashboard.emptyMessage": "Catálogo a entrar → enriquecer / processar → exportar ou enviar para lojas. Comece com Primeiros passos abaixo (ou ligue um feed / carregue um CSV).",
"dashboard.connectFeedAnyway": "Ligar feed mesmo assim",
@@ -367,29 +367,29 @@ export const pt: MessageDict = {
"activation.focus.sync": "Seguinte: sincronize uma amostra depois de guardar o mapeamento. Use Sync num feed mapeado — evite origens sem mapa.",
"activation.focus.dismiss": "Fechar dica",
"activation.dismiss": "Dispensar lista",
"hypercare.report.title": "Hypercare de cutover",
"hypercare.report.title": "Precisa de ajuda após a mudança?",
"hypercare.report.message": "Dados em falta ou incorretos após a migração? Reporte para podermos corrigir o seu espaço de trabalho.",
"hypercare.report.cta": "Reportar dados em falta ou incorretos",
"hypercare.report.dismiss": "Dispensar",
"hypercare.report.adminTriage": "Abrir fila de hypercare",
"etl.gaps.title": "What did not migrate",
"etl.gaps.message": "These areas were intentionally left empty or partial after the Descrybe platform cutover. Nothing is “broken” — recreate or reconnect as needed.",
"etl.gaps.title": "What still needs setup",
"etl.gaps.message": "Some items weren't copied when we moved your workspace. Nothing is broken — recreate or reconnect as needed.",
"etl.gaps.dismiss": "Dismiss",
"etl.gaps.apiKeys.title": "API keys",
"etl.gaps.apiKeys.body": "Legacy API key secrets were not migrated. Create a new key to restore API access — the secret is shown only once.",
"etl.gaps.apiKeys.body": "Previous API keys weren't copied. Create a new key to restore API access the secret is shown only once.",
"etl.gaps.apiKeys.cta": "Open API keys",
"etl.gaps.blobs.title": "File contents (blobs)",
"etl.gaps.blobs.body": "Upload records may exist as metadata only. File bytes were not copied — re-upload CSVs or resync object storage if you need the originals.",
"etl.gaps.blobs.countHint": "{count} file records are metadata-only (bytes were not migrated).",
"etl.gaps.blobs.title": "Uploaded files",
"etl.gaps.blobs.body": "File names may appear even when the file itself wasn't copied. Re-upload CSVs if you need the originals.",
"etl.gaps.blobs.countHint": "{count} files are listed without their contents (they weren't copied).",
"etl.gaps.blobs.cta": "Open Files",
"etl.gaps.jobs.title": "Processing history",
"etl.gaps.jobs.body": "Legacy job and task history was not backfilled for cutover. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Optional jobs domain was not run — empty history is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were tagged migrated from an optional backfill.",
"etl.gaps.jobs.body": "Earlier job history wasn't imported. New jobs you start here will appear normally.",
"etl.gaps.jobs.emptyHint": "Processing history wasn't imported — an empty list is expected.",
"etl.gaps.jobs.migratedHint": "{count} jobs were imported from the previous workspace.",
"etl.gaps.jobs.cta": "Open Processing",
"etl.gaps.woo.title": "Store credentials",
"etl.gaps.woo.body": "WooCommerce and Shopify secrets may need to be re-entered after cutover — that is a reconnect step, not a broken sync.",
"etl.gaps.woo.body": "WooCommerce and Shopify passwords may need to be re-entered after the move — reconnect your store; sync is not broken.",
"etl.gaps.woo.cta": "Open Stores",
"etl.gaps.settings.title": "Company settings",
"etl.gaps.settings.body": "Only language and merge-products settings were imported. Re-check other company preferences in Settings.",
@@ -3690,6 +3690,7 @@ export const pt: MessageDict = {
"processing.receipt.cancelledDesc": "O trabalho recém-iniciado foi cancelado.",
"processing.receipt.cancelFailed": "Não foi possível cancelar o trabalho de processamento",
"unsubscribe.title": "Cancelar subscrição",
"unsubscribe.useEmailLink": "Use a ligacao de cancelamento no email que lhe enviamos. Abrir esta pagina sem essa ligacao nao pode alterar as suas preferencias de email.",
"unsubscribe.me": "Cancelar a minha subscrição",
"unsubscribe.invalidLink": "Ligação inválida",
"unsubscribe.failedShort": "Falha ao cancelar a subscrição",
@@ -4727,6 +4728,10 @@ export const pt: MessageDict = {
"seo.terms.description": "Termos de utilização da Descrybe: importação de feeds, enriquecimento de catálogo, conteúdo assistido por IA, exportações e sincronização com WooCommerce para o seu negócio.",
"seo.features.title": "Funcionalidades — Feeds, enriquecimento e exportação | Descrybe",
"seo.features.description": "Descubra como a Descrybe importa feeds de fornecedores, mapeia campos para a sua taxonomia, enriquece dados de produto e envia catálogos através de feeds de exportação, WooCommerce ou API.",
"seo.contactSales.title": "Contactar vendas | Descrybe",
"seo.contactSales.description": "Fale-nos do seu catalogo e necessidades de capacidade: prepararemos um plano personalizado que pode pagar online.",
"seo.unsubscribe.title": "Cancelar subscricao | Descrybe",
"seo.unsubscribe.description": "Pare os emails de marketing da Descrybe usando a ligacao de cancelamento na mensagem que lhe enviamos.",
"pricing.page.eyebrow": "Preços",
"pricing.page.title": "Planos simples para catálogos em crescimento",
"pricing.page.lead": "Comece grátis com mapeamento de feeds e limpeza básica. Faça upgrade quando precisar de títulos e descrições com IA, mais produtos, feeds de exportação ou sync WooCommerce — de Starter a Enterprise.",
@@ -4734,6 +4739,8 @@ export const pt: MessageDict = {
"pricing.page.subscribedMid": "ou comparar planos em",
"pricing.page.plansLink": "Planos",
"pricing.page.subscribedAfter": ".",
"pricing.page.guestSubscribedBefore": "Ja e subscritor?",
"pricing.page.guestSubscribedAfter": "para gerir a faturacao e comparar planos.",
"legal.lastUpdated": "Última atualização: {date}",
"legal.backHome": "← Voltar ao início",
"legal.emailLabel": "E-mail:",
@@ -5065,7 +5072,7 @@ export const pt: MessageDict = {
"brand.uploadLogo": "Carregar logótipo",
"brand.clear": "Limpar",
"brand.field.logoUrl": "URL do logótipo",
"brand.placeholder.logoUrl": "https://… ou carregado /api/brand/logo/files/…",
"brand.placeholder.logoUrl": "https://",
"brand.logoAlt": "Pré-visualização do logótipo da marca",
"brand.tipsTitle": "Dicas de fórmula / pré-visualização",
"brand.saving": "A guardar…",
@@ -5226,6 +5233,8 @@ export const pt: MessageDict = {
"pricing.section.publicBefore": "Planos públicos: Free, Starter, Growth, Business e Enterprise. Crie uma conta Free e depois faça upgrade em",
"pricing.section.publicOr": "ou",
"pricing.section.publicAfter": "via Stripe Checkout. O Enterprise continua a ser gerido pelas vendas.",
"pricing.section.guestPublicBefore": "Planos publicos: Free, Starter, Plus, Growth, Business, Scale e Enterprise. Crie uma conta Free e depois",
"pricing.section.guestPublicAfter": "para fazer upgrade via Stripe Checkout. O Enterprise continua a ser gerido pelas vendas.",
"pricing.section.plansLink": "Planos",
"pricing.section.billingLink": "Faturação",
"pricing.section.capabilitiesTitle": "Para que está pensado cada plano",
@@ -5291,7 +5300,7 @@ export const pt: MessageDict = {
"pricing.calc.enterpriseTitle": "Sounds like Enterprise",
"pricing.calc.enterpriseBody": "Your feeds or catalog size exceed self-serve caps. Talk to sales for unlimited capacity and a managed AI grant or BYOK.",
"pricing.calc.ctaPlan": "Choose {name}",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. A1 and other client deals stay admin-only.",
"pricing.calc.disclaimer": "Estimates use list prices and ~2 credits per AI product enhance. Checkout prices come from Stripe. Custom client deals stay admin-only.",
"pricing.card.mostPopular": "Mais popular",
"pricing.card.custom": "Personalizado",
"pricing.card.forever": "para sempre",
+80
View File
@@ -0,0 +1,80 @@
/**
* Merchant-facing copy: no A1 / seeded-company, cutover jargon, ETL/blobs/domain,
* or brand-logo API paths in customer-visible strings.
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { UI_LOCALES } from "./i18n/locales.ts";
import { en } from "./i18n/messages/en.ts";
import { loadAllMessages, messagesFor } from "./i18n/messages/catalog.ts";
import {
DEFAULT_ENHANCE_PREAMBLE,
DEFAULT_SECTION_BODIES
} from "./categories/prompt-sections.ts";
const CUSTOMER_DEMO_EMPTY_KEYS = ["dashboard.demoEmptyHint", "dashboard.demoEmptyMessage"] as const;
const CUSTOMER_ETL_KEYS = [
"etl.gaps.title",
"etl.gaps.message",
"etl.gaps.blobs.title",
"etl.gaps.blobs.body",
"etl.gaps.blobs.countHint",
"etl.gaps.jobs.body",
"etl.gaps.jobs.emptyHint",
"etl.gaps.jobs.migratedHint",
"etl.gaps.woo.body"
] as const;
describe("customer-visible copy leaks", () => {
it("English demo empty, hypercare, ETL, and logo placeholder stay merchant-facing", () => {
for (const key of CUSTOMER_DEMO_EMPTY_KEYS) {
assert.doesNotMatch(en[key], /\bA1\b/i, key);
assert.doesNotMatch(en[key], /seeded company/i, key);
}
assert.doesNotMatch(en["hypercare.report.title"], /cutover|hypercare/i);
assert.doesNotMatch(en["hypercare.report.message"], /cutover|hypercare/i);
for (const key of CUSTOMER_ETL_KEYS) {
assert.doesNotMatch(en[key], /\bETL\b/, key);
assert.doesNotMatch(en[key], /\bblobs?\b/i, key);
assert.doesNotMatch(en[key], /\bdomain\b/i, key);
assert.doesNotMatch(en[key], /cutover/i, key);
}
assert.doesNotMatch(en["brand.placeholder.logoUrl"], /\/api\/brand\/logo\/files/i);
});
it("every UI locale keeps demo empty, hypercare title, ETL, and logo copy merchant-facing", async () => {
await loadAllMessages();
for (const { code } of UI_LOCALES) {
const pack = code === "en" ? en : messagesFor(code);
for (const key of CUSTOMER_DEMO_EMPTY_KEYS) {
const text = String(pack[key] ?? "");
assert.doesNotMatch(text, /\bA1\b/i, `${code}:${key}`);
assert.doesNotMatch(text, /seeded company/i, `${code}:${key}`);
}
assert.doesNotMatch(
String(pack["hypercare.report.title"] ?? ""),
/cutover|hypercare/i,
`${code}:hypercare.report.title`
);
for (const key of CUSTOMER_ETL_KEYS) {
const text = String(pack[key] ?? "");
assert.doesNotMatch(text, /\bETL\b/, `${code}:${key}`);
assert.doesNotMatch(text, /\bblobs?\b/i, `${code}:${key}`);
assert.doesNotMatch(text, /\bdomain\b/i, `${code}:${key}`);
}
assert.doesNotMatch(
String(pack["brand.placeholder.logoUrl"] ?? ""),
/\/api\/brand\/logo\/files/i,
`${code}:brand.placeholder.logoUrl`
);
}
});
it("category prompt defaults hide JSON schema jargon from customer text", () => {
const blob = [DEFAULT_ENHANCE_PREAMBLE, ...Object.values(DEFAULT_SECTION_BODIES)].join("\n");
assert.doesNotMatch(blob, /system schema/i);
assert.doesNotMatch(blob, /your reply is parsed as json/i);
assert.doesNotMatch(blob, /build json/i);
});
});
@@ -3,9 +3,15 @@ import { describe, it } from "node:test";
import { isHttpError } from "@sveltejs/kit";
import type { RequestEvent } from "@sveltejs/kit";
import {
adminLoginRedirect,
assertSameOrigin,
fetchMeStaff,
isAdminBootstrapPath,
isAdminSupportPath,
isFullPlatformAdmin,
requirePlatformAdminServer
isSupportDesk,
requirePlatformAdminServer,
requireSupportDeskServer
} from "./require-platform-admin.ts";
function makeEvent(opts: {
@@ -122,6 +128,69 @@ describe("requirePlatformAdminServer", () => {
});
});
describe("fetchMeStaff", () => {
it("returns 401 without throwing when /me is unauthorized", async () => {
const event = makeEvent({
fetchImpl: async () => new Response("{}", { status: 401 })
});
assert.deepEqual(await fetchMeStaff(event), { ok: false, status: 401 });
});
});
describe("requireSupportDeskServer", () => {
it("allows support_desk without full_admin", async () => {
const me = { staff_access: { full_admin: false, support_desk: true } };
const event = makeEvent({
fetchImpl: async () =>
new Response(JSON.stringify(me), {
status: 200,
headers: { "content-type": "application/json" }
})
});
assert.deepEqual(await requireSupportDeskServer(event), me);
});
it("403 for tenant with no staff_access", async () => {
const event = makeEvent({
fetchImpl: async () =>
new Response(JSON.stringify({ user: { is_platform_admin: false } }), {
status: 200,
headers: { "content-type": "application/json" }
})
});
await expectHttpAsync(403, () => requireSupportDeskServer(event));
});
});
describe("admin path split and login next=", () => {
it("classifies support vs bootstrap vs other admin", () => {
assert.equal(isAdminSupportPath("/admin/support"), true);
assert.equal(isAdminSupportPath("/admin/support/tickets/1"), true);
assert.equal(isAdminSupportPath("/admin"), false);
assert.equal(isAdminSupportPath("/admin/logs"), false);
assert.equal(isAdminBootstrapPath("/admin/bootstrap"), true);
assert.equal(isAdminBootstrapPath("/admin/logs"), false);
});
it("builds login redirect with next=", () => {
assert.equal(
adminLoginRedirect("/admin/logs", "?x=1"),
"/login?next=%2Fadmin%2Flogs%3Fx%3D1"
);
});
it("isSupportDesk follows staff_access.support_desk", () => {
assert.equal(isSupportDesk({ staff_access: { support_desk: true } }), true);
assert.equal(
isSupportDesk({
staff_access: { full_admin: false, support_desk: false },
user: { is_platform_admin: true }
}),
false
);
});
});
describe("assertSameOrigin", () => {
it("allows matching Origin", () => {
assertSameOrigin(
@@ -7,39 +7,90 @@ import { error } from "@sveltejs/kit";
import type { RequestEvent } from "@sveltejs/kit";
import {
isFullPlatformAdmin,
isSupportDesk,
type StaffAccessMe
} from "../staff-access.ts";
export { isFullPlatformAdmin } from "../staff-access.ts";
export { isFullPlatformAdmin, isSupportDesk } from "../staff-access.ts";
type MeStaff = StaffAccessMe;
export type FetchMeStaffResult =
| { ok: true; me: MeStaff }
| { ok: false; status: 401 | 403 | 502 };
/** Absolute /api/auth/me when PUBLIC_API_URL is set (Compose/adapter-node); else Vite same-origin proxy. */
function authMeUrl(): string {
const base = (process.env.PUBLIC_API_URL ?? "").replace(/\/$/, "");
return base ? `${base}/api/auth/me` : "/api/auth/me";
}
/** `/admin/support` desk pages — full admin or support_staff. */
export function isAdminSupportPath(pathname: string): boolean {
return pathname === "/admin/support" || pathname.startsWith("/admin/support/");
}
/** First-run bootstrap UI — full platform admin only. */
export function isAdminBootstrapPath(pathname: string): boolean {
return pathname === "/admin/bootstrap" || pathname.startsWith("/admin/bootstrap/");
}
/** Open-redirect-safe login URL with next= pointing at the current admin path. */
export function adminLoginRedirect(pathname: string, search = ""): string {
const next = `${pathname}${search}`;
return `/login?next=${encodeURIComponent(next)}`;
}
/**
* Server-side platform-admin gate for SvelteKit endpoints.
* Uses event.fetch so session cookies reach the Go /api/auth/me (Vite proxy or PUBLIC_API_URL).
* Fetch /api/auth/me without throwing. Layout uses this to redirect 401 login.
* API +server.ts handlers still use requirePlatformAdminServer (error 401/403).
*/
export async function requirePlatformAdminServer(event: RequestEvent): Promise<MeStaff> {
export async function fetchMeStaff(event: RequestEvent): Promise<FetchMeStaffResult> {
const headers: Record<string, string> = { Accept: "application/json" };
const cookie = event.request.headers.get("cookie");
if (cookie) headers.cookie = cookie;
const res = await event.fetch(authMeUrl(), { headers });
if (res.status === 401) {
error(401, "Authentication required.");
return { ok: false, status: 401 };
}
if (!res.ok) {
error(res.status === 403 ? 403 : 502, "Failed to verify admin access.");
return { ok: false, status: res.status === 403 ? 403 : 502 };
}
const me = (await res.json()) as MeStaff;
if (!isFullPlatformAdmin(me)) {
return { ok: true, me };
}
/**
* Server-side platform-admin gate for SvelteKit endpoints.
* Uses event.fetch so session cookies reach the Go /api/auth/me (Vite proxy or PUBLIC_API_URL).
*/
export async function requirePlatformAdminServer(event: RequestEvent): Promise<MeStaff> {
const result = await fetchMeStaff(event);
if (!result.ok) {
if (result.status === 401) {
error(401, "Authentication required.");
}
error(result.status === 403 ? 403 : 502, "Failed to verify admin access.");
}
if (!isFullPlatformAdmin(result.me)) {
error(403, "Platform admin required.");
}
return me;
return result.me;
}
/** Server-side support-desk gate (full admin or support_staff). */
export async function requireSupportDeskServer(event: RequestEvent): Promise<MeStaff> {
const result = await fetchMeStaff(event);
if (!result.ok) {
if (result.status === 401) {
error(401, "Authentication required.");
}
error(result.status === 403 ? 403 : 502, "Failed to verify support access.");
}
if (!isSupportDesk(result.me)) {
error(403, "Support desk access required.");
}
return result.me;
}
function originOf(url: string): string | null {
+22 -1
View File
@@ -1,6 +1,6 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { isFullPlatformAdmin, shouldUnlockAllFeatures } from "./staff-access.ts";
import { isFullPlatformAdmin, isSupportDesk, shouldUnlockAllFeatures } from "./staff-access.ts";
describe("isFullPlatformAdmin", () => {
it("allows staff_access.full_admin", () => {
@@ -39,6 +39,27 @@ describe("isFullPlatformAdmin", () => {
});
});
describe("isSupportDesk", () => {
it("allows support_desk", () => {
assert.equal(isSupportDesk({ staff_access: { support_desk: true } }), true);
});
it("denies tenant even when legacy is_platform_admin is set alongside staff_access", () => {
assert.equal(
isSupportDesk({
staff_access: { full_admin: false, support_desk: false },
user: { is_platform_admin: true }
}),
false
);
});
it("legacy: allows is_platform_admin when staff_access is absent", () => {
assert.equal(isSupportDesk({ user: { is_platform_admin: true } }), true);
assert.equal(isSupportDesk({ user: { is_platform_admin: false } }), false);
});
});
describe("shouldUnlockAllFeatures", () => {
it("unlocks Demo / full admin", () => {
assert.equal(
+9
View File
@@ -24,6 +24,15 @@ export function isFullPlatformAdmin(me: StaffAccessMe | null | undefined): boole
return Boolean(me.user?.is_platform_admin);
}
/** Full admin or support_staff (legacy: is_platform_admin when staff_access is absent). */
export function isSupportDesk(me: StaffAccessMe | null | undefined): boolean {
if (me == null) return false;
if (me.staff_access) {
return Boolean(me.staff_access.support_desk);
}
return Boolean(me.user?.is_platform_admin);
}
/**
* Nav / shell preview unlock for Demo + platform staff.
* Must stay false for normal A1 (and other tenant) users even if a stale
+31 -20
View File
@@ -1,8 +1,8 @@
<script lang="ts">
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import { page } from "$app/state";
import { api, ApiError } from "$lib/api";
import { consumeAuthLinkSecrets } from "$lib/auth-link-token";
import { trackEvent } from "$lib/analytics";
import { writeActivationProgress } from "$lib/activation";
import { i18n } from "$lib/i18n";
@@ -27,12 +27,11 @@
mismatch: boolean;
};
const initialToken = page.url.searchParams.get("token") ?? "";
const initialMode = page.url.searchParams.get("mode");
let token = $state(initialToken);
/** True when the token arrived via ?token=; never show it in a visible input. */
let tokenFromLink = $state(initialToken.length > 0);
let token = $state("");
/** True when the token arrived via hash or query link; never show it in a visible input. */
let tokenFromLink = $state(false);
/** Fragment tokens are client-only; wait for mount before treating the token as missing. */
let hydrated = $state(false);
let name = $state("");
let password = $state("");
let error = $state("");
@@ -44,9 +43,7 @@
let sessionEmail = $state("");
let signingOut = $state(false);
/** Admin HMAC emails use mode=set-password; migrator invites use the invite token path. */
let mode = $state<"invite" | "set-password">(
initialMode === "set-password" ? "set-password" : "invite"
);
let mode = $state<"invite" | "set-password">("invite");
const errorId = "accept-invite-form-error";
const passwordHintId = "accept-invite-password-hint";
@@ -103,12 +100,21 @@
}
onMount(() => {
if (!tokenFromLink) return;
// Drop the secret from the address bar / history (referrer + shoulder-surf).
const cleaned = new URL(window.location.href);
cleaned.searchParams.delete("token");
history.replaceState(history.state, "", cleaned.pathname + cleaned.search + cleaned.hash);
void loadPreview(token);
const consumed = consumeAuthLinkSecrets(window.location.href);
if (consumed.token) {
token = consumed.token;
tokenFromLink = true;
}
if (consumed.mode === "set-password") {
mode = "set-password";
}
hydrated = true;
if (consumed.stripped) {
history.replaceState(history.state, "", consumed.cleanedPathSearch);
}
if (tokenFromLink) {
void loadPreview(token);
}
});
async function onSubmit(event: Event) {
@@ -179,7 +185,15 @@
</script>
<Card>
{#if done}
{#if !hydrated}
<CardHeader>
<CardTitle level={1}>{i18n.t("auth.invite.title")}</CardTitle>
<CardDescription>{i18n.t("auth.invite.description")}</CardDescription>
</CardHeader>
<CardContent>
<p class="text-sm text-text-muted" aria-live="polite">{i18n.t("auth.invite.checking")}</p>
</CardContent>
{:else if done}
<CardHeader>
<CardTitle level={1}>
{mode === "set-password"
@@ -377,9 +391,6 @@
</p>
<p class="mt-1.5 text-center text-xs text-text-muted">
{i18n.t("auth.invite.expiredFooter")}
<a href="/admin/users" class="font-medium text-link hover:underline"
>{i18n.t("auth.invite.adminUsersLink")}</a
>.
</p>
</CardContent>
{/if}
@@ -0,0 +1,46 @@
import { error, redirect } from "@sveltejs/kit";
import type { LayoutServerLoad } from "./$types";
import {
adminLoginRedirect,
fetchMeStaff,
isAdminSupportPath,
isFullPlatformAdmin,
isSupportDesk
} from "$lib/server/require-platform-admin";
/**
* SSR gate for /admin/* pages (not +server.ts endpoints).
* Unauthenticated login?next=. Non-staff /dashboard (no admin chrome).
* /admin/support* support desk. All other admin pages (including logs + bootstrap) full platform admin.
*/
export const load: LayoutServerLoad = async (event) => {
const result = await fetchMeStaff(event);
if (!result.ok) {
if (result.status === 401) {
redirect(303, adminLoginRedirect(event.url.pathname, event.url.search));
}
error(result.status, "Forbidden");
}
const me = result.me;
const path = event.url.pathname;
const supportPath = isAdminSupportPath(path);
const fullAdmin = isFullPlatformAdmin(me);
const supportDesk = isSupportDesk(me);
if (supportPath) {
if (!supportDesk) {
redirect(303, "/dashboard");
}
return { staff: { full_admin: fullAdmin, support_desk: true } };
}
if (!fullAdmin) {
if (supportDesk) {
redirect(303, "/admin/support");
}
redirect(303, "/dashboard");
}
return { staff: { full_admin: true, support_desk: supportDesk } };
};
+14 -14
View File
@@ -264,18 +264,18 @@
});
</script>
<PageShell
eyebrow={i18n.t("admin.overview.eyebrow")}
title={i18n.t("admin.overview.title")}
description={i18n.t("admin.overview.description")}
>
{#if loading}
<Spinner />
{:else if accessDenied}
<ForbiddenEmptyState kind="platform" />
{:else if error}
<Alert message={error} />
{:else}
{#if loading}
<Spinner />
{:else if accessDenied}
<ForbiddenEmptyState kind="platform" />
{:else if error}
<Alert message={error} />
{:else}
<PageShell
eyebrow={i18n.t("admin.overview.eyebrow")}
title={i18n.t("admin.overview.title")}
description={i18n.t("admin.overview.description")}
>
{#if summary}
<section class="space-y-3" aria-labelledby="admin-kpi-heading">
<div class="flex flex-wrap items-end justify-between gap-2">
@@ -517,5 +517,5 @@
</div>
</section>
{/if}
{/if}
</PageShell>
</PageShell>
{/if}
@@ -1,7 +1,8 @@
<script lang="ts">
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import { api, failureMessage, isForbidden, isUnauthorized } from "$lib/api";
import { requirePlatformAdmin } from "$lib/admin-gate";
import { isFullPlatformAdmin } from "$lib/staff-access";
import type { MeResponse } from "$lib/types";
import PageShell from "$lib/components/PageShell.svelte";
import Spinner from "$lib/components/Spinner.svelte";
@@ -23,33 +24,33 @@
let me = $state<MeResponse | null>(null);
onMount(async () => {
try {
me = await api<MeResponse>("/api/auth/me");
} catch (err) {
if (isUnauthorized(err)) {
const gate = await requirePlatformAdmin();
if (!gate.ok) {
if (gate.reason === "auth") {
await goto("/login");
return;
}
if (isForbidden(err)) {
accessDenied = true;
return;
accessDenied = true;
if (gate.reason !== "forbidden") {
error = gate.message;
}
error = failureMessage(err, i18n.t("admin.bootstrap.loadFailed"));
} finally {
loading = false;
return;
}
me = gate.me;
loading = false;
});
</script>
<PageShell
title={i18n.t("admin.bootstrap.title")}
description={i18n.t("admin.bootstrap.description")}
>
{#if loading}
<Spinner />
{:else if accessDenied}
<ForbiddenEmptyState kind="platform" />
{:else}
{#if loading}
<Spinner />
{:else if accessDenied}
<ForbiddenEmptyState kind="platform" />
{:else}
<PageShell
title={i18n.t("admin.bootstrap.title")}
description={i18n.t("admin.bootstrap.description")}
>
<Card class="mx-auto max-w-md">
<CardHeader>
<CardTitle>{i18n.t("admin.bootstrap.statusTitle")}</CardTitle>
@@ -58,11 +59,11 @@
</CardDescription>
</CardHeader>
<CardContent>
{#if me?.user?.is_platform_admin}
{#if isFullPlatformAdmin(me)}
<div class="rounded-md border border-green-500/30 bg-green-500/10 p-4 text-sm">
<p class="font-medium">{i18n.t("admin.bootstrap.alreadyAdmin")}</p>
<p class="mt-2 text-muted-foreground">
{i18n.t("admin.bootstrap.signedInAs", { email: me.user.email })}
{i18n.t("admin.bootstrap.signedInAs", { email: me?.user?.email ?? "" })}
</p>
</div>
{:else if error}
@@ -81,7 +82,7 @@
{/if}
</CardContent>
<CardFooter class="flex justify-between gap-2">
{#if me?.user?.is_platform_admin}
{#if isFullPlatformAdmin(me)}
<Button class="w-full" onclick={() => goto("/admin")}>{i18n.t("admin.bootstrap.goToPanel")}</Button>
{:else}
<Button variant="outline" class="w-full" onclick={() => goto("/dashboard")}>
@@ -90,5 +91,5 @@
{/if}
</CardFooter>
</Card>
{/if}
</PageShell>
</PageShell>
{/if}
@@ -0,0 +1,7 @@
import { redirect } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types";
/** Legacy `/admin/logs` → diagnostics. Layout.server already requires platform admin. */
export const load: PageServerLoad = () => {
redirect(307, "/admin/diagnostics");
};
+1 -1
View File
@@ -528,7 +528,7 @@
const link =
res.accept_url?.trim() ||
(res.token
? `${window.location.origin}/accept-invite?token=${encodeURIComponent(res.token)}`
? `${window.location.origin}/accept-invite#token=${encodeURIComponent(res.token)}`
: "");
if (link) {
inviteAcceptLink = link;
+19 -3
View File
@@ -5,7 +5,9 @@
import { DEMO_BOOKING_URL } from "$lib/site";
import { submitSalesContact } from "$lib/sales-contact";
import { notifySuccess, notifyApiError } from "$lib/notify";
import PageShell from "$lib/components/PageShell.svelte";
import SiteHeader from "$lib/components/site/SiteHeader.svelte";
import Footer from "$lib/components/site/Footer.svelte";
import SeoHead from "$lib/components/site/SeoHead.svelte";
import Alert from "$lib/components/Alert.svelte";
import {
Button,
@@ -65,7 +67,19 @@
}
</script>
<PageShell title={i18n.t("sales.contact.title")} description={i18n.t("sales.contact.lead")}>
<SeoHead
title={i18n.t("seo.contactSales.title")}
description={i18n.t("seo.contactSales.description")}
path="/contact-sales"
/>
<div class="flex min-h-screen flex-col bg-background">
<SiteHeader />
<main id="main-content" class="mx-auto w-full max-w-3xl flex-1 px-4 pt-28 pb-12 sm:px-6">
<div class="mb-8">
<h1 class="text-4xl font-bold tracking-tight text-text">{i18n.t("sales.contact.title")}</h1>
<p class="mt-2 text-lg text-text-muted">{i18n.t("sales.contact.lead")}</p>
</div>
{#if done}
<Card>
<CardHeader>
@@ -152,4 +166,6 @@
</CardContent>
</Card>
{/if}
</PageShell>
</main>
<Footer />
</div>
+8 -1
View File
@@ -19,6 +19,7 @@
loadDocsAuthStatus,
maskKeyPrefix,
readStoredTryItKey,
relabelRapiDocAuthRequired,
resolveDocsTryItKey,
writeStoredTryItKey,
type DocsAuthStatus,
@@ -54,6 +55,7 @@
let runtimeWarning = $state("");
/** $state so theme $effect re-runs after mount + SiteHeader toggle. */
let rapiEl = $state<RapiDocElement | undefined>(undefined);
let authLabelObserver: MutationObserver | null = null;
let askOpen = $state(false);
let mountGeneration = 0;
let hydrateCheckTimer: number | undefined;
@@ -121,6 +123,8 @@
window.clearTimeout(hydrateCheckTimer);
window.removeEventListener("error", onWindowError);
window.removeEventListener("unhandledrejection", onRejection);
authLabelObserver?.disconnect();
authLabelObserver = null;
rapiEl?.remove();
rapiEl = undefined;
};
@@ -323,6 +327,8 @@
loadError = "";
runtimeWarning = "";
window.clearTimeout(hydrateCheckTimer);
authLabelObserver?.disconnect();
authLabelObserver = null;
rapiEl?.remove();
rapiEl = undefined;
@@ -338,6 +344,7 @@
return;
}
rapiEl = instance;
authLabelObserver = relabelRapiDocAuthRequired(instance);
} catch (err) {
if (gen !== mountGeneration) return;
loadState = "error";
@@ -533,7 +540,7 @@
{/if}
</p>
{:else}
<p class="mt-1 text-xs text-text-muted" data-testid="docs-tryit-no-keys-legacy">
<p class="mt-1 text-xs text-text-muted" data-testid="docs-tryit-no-keys">
{docsAuth.canCreateKey
? i18n.t("docs.tryIt.noKeysLegacyAdmin")
: i18n.t("docs.tryIt.noKeysLegacyMember")}
@@ -470,7 +470,7 @@
</div>
<div class="space-y-2">
<Label for="custom_model">{i18n.t("ai.field.model")}</Label>
<Input id="custom_model" bind:value={model} placeholder="gpt-4o-mini" autocomplete="off" />
<Input id="custom_model" bind:value={model} placeholder={i18n.t("ai.placeholder.modelId")} autocomplete="off" />
</div>
</CardContent>
</Card>
@@ -494,7 +494,7 @@
id="api_key"
type="password"
bind:value={apiKey}
placeholder={hasApiKey ? i18n.t("ai.placeholder.keepKey") : "sk-…"}
placeholder={hasApiKey ? i18n.t("ai.placeholder.keepKey") : i18n.t("ai.field.apiKey")}
autocomplete="new-password"
/>
</div>
-6
View File
@@ -95,12 +95,6 @@
email: email || i18n.t("auth.login.yourEmail")
})}
</p>
<p>
{i18n.t("auth.login.platformAdminsReissue")}
<a href="/admin/users" class="font-medium text-link hover:underline"
>{i18n.t("auth.login.adminUsersLink")}</a
>.
</p>
<p>
<a href="/accept-invite" class="font-medium text-link hover:underline"
>{i18n.t("auth.login.haveToken")}</a
+3 -8
View File
@@ -28,14 +28,9 @@
</p>
<MarketingAuthCtas class="mt-8" />
<p class="mt-4 text-sm text-text-muted">
{i18n.t("pricing.page.subscribedBefore")}
<a href="/billing" class="font-medium text-link hover:underline"
>{i18n.t("nav.billing")}</a
>
{i18n.t("pricing.page.subscribedMid")}
<a href="/plans" class="font-medium text-link hover:underline"
>{i18n.t("pricing.page.plansLink")}</a
>{i18n.t("pricing.page.subscribedAfter")}
{i18n.t("pricing.page.guestSubscribedBefore")}
<a href="/login" class="font-medium text-link hover:underline">{i18n.t("site.logIn")}</a>
{i18n.t("pricing.page.guestSubscribedAfter")}
</p>
</section>
<PricingSection hideIntro />
@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from "svelte";
import { page } from "$app/state";
import { api, ApiError } from "$lib/api";
import { consumeAuthLinkSecrets } from "$lib/auth-link-token";
import { apiFormError, fieldDescribedBy, fieldInvalid } from "$lib/api-form-error";
import { i18n } from "$lib/i18n";
import {
@@ -17,12 +17,6 @@
Label
} from "$lib/components/ui";
function tokenFromHash(hash: string): string {
const raw = hash.startsWith("#") ? hash.slice(1) : hash;
const params = new URLSearchParams(raw);
return (params.get("token") ?? "").trim();
}
let token = $state("");
/** True when the token arrived via link; never show it in a visible input. */
let tokenFromLink = $state(false);
@@ -39,20 +33,15 @@
const passwordHintId = "reset-password-hint";
onMount(() => {
const fromHash = tokenFromHash(window.location.hash);
const fromQuery = (new URL(window.location.href).searchParams.get("token") ?? "").trim();
const fromSsrQuery = (page.url.searchParams.get("token") ?? "").trim();
const resolved = fromHash || fromQuery || fromSsrQuery;
if (resolved) {
token = resolved;
const consumed = consumeAuthLinkSecrets(window.location.href);
if (consumed.token) {
token = consumed.token;
tokenFromLink = true;
}
hydrated = true;
if (!fromHash && !fromQuery && !fromSsrQuery) return;
const cleaned = new URL(window.location.href);
cleaned.searchParams.delete("token");
cleaned.hash = "";
history.replaceState(history.state, "", cleaned.pathname + cleaned.search);
if (consumed.stripped) {
history.replaceState(history.state, "", consumed.cleanedPathSearch);
}
});
async function onSubmit(event: Event) {
+1 -1
View File
@@ -598,7 +598,7 @@
const link =
created.accept_url?.trim() ||
(created.token
? `${window.location.origin}/accept-invite?token=${encodeURIComponent(created.token)}`
? `${window.location.origin}/accept-invite#token=${encodeURIComponent(created.token)}`
: "");
trackEvent("invite_sent", {
role: roleForEvent,
+11 -1
View File
@@ -7,6 +7,7 @@
import { Button } from "$lib/components/ui";
import SiteHeader from "$lib/components/site/SiteHeader.svelte";
import Footer from "$lib/components/site/Footer.svelte";
import SeoHead from "$lib/components/site/SeoHead.svelte";
const API_BASE = (PUBLIC_API_URL ?? "").replace(/\/$/, "") || "";
@@ -16,11 +17,12 @@
let message = $state("");
let error = $state("");
let already = $state(false);
let missingToken = $state(false);
onMount(async () => {
token = page.url.searchParams.get("token") ?? "";
if (!token) {
error = i18n.t("flash.unsubscribe.missingToken");
missingToken = true;
loading = false;
return;
}
@@ -64,6 +66,12 @@
}
</script>
<SeoHead
title={i18n.t("seo.unsubscribe.title")}
description={i18n.t("seo.unsubscribe.description")}
path="/unsubscribe"
/>
<div class="flex min-h-screen flex-col bg-background">
<SiteHeader />
<main
@@ -73,6 +81,8 @@
<h1 class="text-2xl font-semibold text-text">{i18n.t("unsubscribe.title")}</h1>
{#if loading}
<p class="text-sm text-text-muted">{i18n.t("common.loading")}</p>
{:else if missingToken}
<p class="text-sm text-text-muted">{i18n.t("unsubscribe.useEmailLink")}</p>
{:else if error}
<p class="text-sm text-danger">{error}</p>
{:else}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

@@ -1,72 +0,0 @@
{
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 3 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 4 click button[type=submit] — ok",
"step 5 wait_nav — ok (http://localhost:28472/login)",
"step 6 wait_hydrate — ok",
"step 7 wait 1500ms — ok"
],
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "mobile",
"doc_status": 200,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00-post-login-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00-post-login-mobile.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 3 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 4 click button[type=submit] — ok",
"step 5 wait_nav — ok (http://localhost:28472/login)",
"step 6 wait_hydrate — ok",
"step 7 wait 1500ms — ok"
],
"generated_at": "2026-08-08T23:04:24Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-08T23:04:24Z",
"load_ms": 1210,
"outline": null,
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00-post-login-mobile.png",
"snapshot": "",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

@@ -1,72 +0,0 @@
{
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 3 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 4 click button[type=submit] — ok",
"step 5 wait_nav — ok (http://localhost:28472/login)",
"step 6 wait_hydrate — ok",
"step 7 wait 1500ms — ok"
],
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "tablet",
"doc_status": 200,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00-post-login-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00-post-login-tablet.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 3 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 4 click button[type=submit] — ok",
"step 5 wait_nav — ok (http://localhost:28472/login)",
"step 6 wait_hydrate — ok",
"step 7 wait 1500ms — ok"
],
"generated_at": "2026-08-08T23:08:42Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-08T23:08:42Z",
"load_ms": 4242,
"outline": null,
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00-post-login-tablet.png",
"snapshot": "",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

@@ -1,72 +0,0 @@
{
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 3 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 4 click button[type=submit] — ok",
"step 5 wait_nav — ok (http://localhost:28472/login)",
"step 6 wait_hydrate — ok",
"step 7 wait 1200ms — ok"
],
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "tablet",
"doc_status": 200,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00b-login-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00b-login-tablet.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 3 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 4 click button[type=submit] — ok",
"step 5 wait_nav — ok (http://localhost:28472/login)",
"step 6 wait_hydrate — ok",
"step 7 wait 1200ms — ok"
],
"generated_at": "2026-08-09T08:05:56Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-09T08:05:56Z",
"load_ms": 16697,
"outline": null,
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\00b-login-tablet.png",
"snapshot": "",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

@@ -1,444 +0,0 @@
{
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 fill \"input[type=email],input[name=email],#email\"=\"demo@descrybe.local\" — ok",
"step 3 fill \"input[type=password],input[name=password],#password\"=\"DemoPass123!\" — ok",
"step 4 click button[type=submit] — ok",
"step 5 wait_nav — ok (http://localhost:28472/login)",
"step 6 wait_hydrate — ok",
"step 7 wait 2000ms — ok"
],
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "mobile",
"doc_status": 200,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\01-dashboard-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\01-dashboard-mobile.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e7",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open products",
"type": "button"
},
{
"ref": "e8",
"selector": "a[data-testid=\"store-reconnect-link-woocommerce\"]",
"role": "link",
"name": "WooCommerce"
},
{
"ref": "e9",
"selector": "a[data-testid=\"store-reconnect-cta-woocommerce\"]",
"role": "link",
"name": "Reconnect WooCommerce"
},
{
"ref": "e10",
"selector": "div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Reconnect WooCommerce",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(1) \u003e a",
"role": "link",
"name": "1. Feeds Connect import"
},
{
"ref": "e12",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(2) \u003e a",
"role": "link",
"name": "2. Products 5 in catalog"
},
{
"ref": "e13",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(3) \u003e a",
"role": "link",
"name": "3. Process 8 done"
},
{
"ref": "e14",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(4) \u003e a",
"role": "link",
"name": "4. Export Templates \u0026 download"
},
{
"ref": "e15",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Products 5 8 processed · 0 unprocessed"
},
{
"ref": "e16",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Categories 2 Catalog tree"
},
{
"ref": "e17",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Attributes 1 Attribute library"
},
{
"ref": "e18",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Feeds 0 Import sources"
},
{
"ref": "e19",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(5)",
"role": "link",
"name": "Credits Unlimited Enterprise · Billing"
},
{
"ref": "e20",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(1) \u003e div:nth-of-type(1) \u003e a",
"role": "link",
"name": "View all"
},
{
"ref": "e21",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Feeds Import and map"
},
{
"ref": "e22",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Products Browse and process"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Jobs Monitor tasks"
},
{
"ref": "e24",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Exports Templates \u0026 download"
},
{
"ref": "e25",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e26",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e27",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e28",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e29",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 PLATFORM DEMO Platform Demo Unlimited Start \"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"PLATFORM DEMO Platform Demo Unlimited Start tutorial Open pr\"\nheading \"Platform Demo\" [level=1]\nbutton \"Start tutorial\"\nbutton \"Open products\"\nstatus \"Reconnect store after migration Store API credentials were n\" [testid=store-reconnect-banner]\nlink \"WooCommerce\" [testid=store-reconnect-link-woocommerce]\nlink \"Reconnect WooCommerce\" [testid=store-reconnect-cta-woocommerce]\nbutton \"Reconnect WooCommerce\"\nheading \"Catalog workflow\" [level=2]\nlink \"1. Feeds Connect import\"\nlink \"2. Products 5 in catalog\"\nlink \"3. Process 8 done\"\nlink \"4. Export Templates \u0026 download\"\nheading \"Overview\" [level=2]\nlink \"Products 5 8 processed · 0 unprocessed\"\nlink \"Categories 2 Catalog tree\"\nlink \"Attributes 1 Attribute library\"\nlink \"Feeds 0 Import sources\"\nlink \"Credits Unlimited Enterprise · Billing\"\nheading \"Recent activity\" [level=2]\nlink \"View all\"\nheading \"Quick links\" [level=2]\nlink \"Feeds Import and map\"\nlink \"Products Browse and process\"\nlink \"Jobs Monitor tasks\"\nlink \"Exports Templates \u0026 download\"\nheading \"WHAT'S NEW\" [level=3]\nheading \"User Experience Improvements\" [level=3]\nheading \"Key Features:\" [level=4]\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 fill \"input[type=email],input[name=email],#email\"=\"demo@descrybe.local\" — ok",
"step 3 fill \"input[type=password],input[name=password],#password\"=\"DemoPass123!\" — ok",
"step 4 click button[type=submit] — ok",
"step 5 wait_nav — ok (http://localhost:28472/login)",
"step 6 wait_hydrate — ok",
"step 7 wait 2000ms — ok"
],
"generated_at": "2026-08-08T23:03:35Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-08T23:03:35Z",
"load_ms": 1357,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e7",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open products",
"type": "button"
},
{
"ref": "e8",
"selector": "a[data-testid=\"store-reconnect-link-woocommerce\"]",
"role": "link",
"name": "WooCommerce"
},
{
"ref": "e9",
"selector": "a[data-testid=\"store-reconnect-cta-woocommerce\"]",
"role": "link",
"name": "Reconnect WooCommerce"
},
{
"ref": "e10",
"selector": "div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Reconnect WooCommerce",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(1) \u003e a",
"role": "link",
"name": "1. Feeds Connect import"
},
{
"ref": "e12",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(2) \u003e a",
"role": "link",
"name": "2. Products 5 in catalog"
},
{
"ref": "e13",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(3) \u003e a",
"role": "link",
"name": "3. Process 8 done"
},
{
"ref": "e14",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(4) \u003e a",
"role": "link",
"name": "4. Export Templates \u0026 download"
},
{
"ref": "e15",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Products 5 8 processed · 0 unprocessed"
},
{
"ref": "e16",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Categories 2 Catalog tree"
},
{
"ref": "e17",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Attributes 1 Attribute library"
},
{
"ref": "e18",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Feeds 0 Import sources"
},
{
"ref": "e19",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(5)",
"role": "link",
"name": "Credits Unlimited Enterprise · Billing"
},
{
"ref": "e20",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(1) \u003e div:nth-of-type(1) \u003e a",
"role": "link",
"name": "View all"
},
{
"ref": "e21",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Feeds Import and map"
},
{
"ref": "e22",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Products Browse and process"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Jobs Monitor tasks"
},
{
"ref": "e24",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Exports Templates \u0026 download"
},
{
"ref": "e25",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e26",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e27",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e28",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e29",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\01-dashboard-mobile.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 PLATFORM DEMO Platform Demo Unlimited Start \"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"PLATFORM DEMO Platform Demo Unlimited Start tutorial Open pr\"\nheading \"Platform Demo\" [level=1]\nbutton \"Start tutorial\"\nbutton \"Open products\"\nstatus \"Reconnect store after migration Store API credentials were n\" [testid=store-reconnect-banner]\nlink \"WooCommerce\" [testid=store-reconnect-link-woocommerce]\nlink \"Reconnect WooCommerce\" [testid=store-reconnect-cta-woocommerce]\nbutton \"Reconnect WooCommerce\"\nheading \"Catalog workflow\" [level=2]\nlink \"1. Feeds Connect import\"\nlink \"2. Products 5 in catalog\"\nlink \"3. Process 8 done\"\nlink \"4. Export Templates \u0026 download\"\nheading \"Overview\" [level=2]\nlink \"Products 5 8 processed · 0 unprocessed\"\nlink \"Categories 2 Catalog tree\"\nlink \"Attributes 1 Attribute library\"\nlink \"Feeds 0 Import sources\"\nlink \"Credits Unlimited Enterprise · Billing\"\nheading \"Recent activity\" [level=2]\nlink \"View all\"\nheading \"Quick links\" [level=2]\nlink \"Feeds Import and map\"\nlink \"Products Browse and process\"\nlink \"Jobs Monitor tasks\"\nlink \"Exports Templates \u0026 download\"\nheading \"WHAT'S NEW\" [level=3]\nheading \"User Experience Improvements\" [level=3]\nheading \"Key Features:\" [level=4]\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

@@ -1,46 +0,0 @@
{
"action_log": null,
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "mobile",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/login",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\02-login-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\02-login-mobile.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"action_log": null,
"generated_at": "2026-08-08T23:15:01Z"
},
"final_url": "http://localhost:28472/login",
"format": "png",
"generated_at": "2026-08-08T23:15:01Z",
"load_ms": 969,
"outline": null,
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\02-login-mobile.png",
"snapshot": "",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

@@ -1,46 +0,0 @@
{
"action_log": null,
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/login",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\02-login-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\02-login-tablet.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"action_log": null,
"generated_at": "2026-08-08T23:15:03Z"
},
"final_url": "http://localhost:28472/login",
"format": "png",
"generated_at": "2026-08-08T23:15:03Z",
"load_ms": 986,
"outline": null,
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\02-login-tablet.png",
"snapshot": "",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

@@ -1,492 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260809T080924Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260809T080924Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\03-dashboard-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\03-dashboard-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260809T080924Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e12",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open products",
"type": "button"
},
{
"ref": "e13",
"selector": "a[data-testid=\"store-reconnect-link-woocommerce\"]",
"role": "link",
"name": "WooCommerce"
},
{
"ref": "e14",
"selector": "a[data-testid=\"store-reconnect-cta-woocommerce\"]",
"role": "link",
"name": "Reconnect WooCommerce"
},
{
"ref": "e15",
"selector": "div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Reconnect WooCommerce",
"type": "button"
},
{
"ref": "e16",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(1) \u003e a",
"role": "link",
"name": "1. Feeds Connect import"
},
{
"ref": "e17",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(2) \u003e a",
"role": "link",
"name": "2. Products 5 in catalog"
},
{
"ref": "e18",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(3) \u003e a",
"role": "link",
"name": "3. Process 8 done"
},
{
"ref": "e19",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(4) \u003e a",
"role": "link",
"name": "4. Export Templates \u0026 download"
},
{
"ref": "e20",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Products 5 8 processed · 0 unprocessed"
},
{
"ref": "e21",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Categories 2 Catalog tree"
},
{
"ref": "e22",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Attributes 1 Attribute library"
},
{
"ref": "e23",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Feeds 0 Import sources"
},
{
"ref": "e24",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(5)",
"role": "link",
"name": "Credits Unlimited Enterprise · Billing"
},
{
"ref": "e25",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(1) \u003e div:nth-of-type(1) \u003e a",
"role": "link",
"name": "View all"
},
{
"ref": "e26",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Feeds Import and map"
},
{
"ref": "e27",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Products Browse and process"
},
{
"ref": "e28",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Jobs Monitor tasks"
},
{
"ref": "e29",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Exports Templates \u0026 download"
},
{
"ref": "e30",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e31",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e32",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e33",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e34",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D PLATFORM DEMO Platform Demo Unlimited Star\"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nbanner \"PLATFORM DEMO Platform Demo Unlimited Start tutorial Open pr\"\nheading \"Platform Demo\" [level=1]\nbutton \"Open products\"\nstatus \"Reconnect store after migration Store API credentials were n\" [testid=store-reconnect-banner]\nlink \"WooCommerce\" [testid=store-reconnect-link-woocommerce]\nlink \"Reconnect WooCommerce\" [testid=store-reconnect-cta-woocommerce]\nbutton \"Reconnect WooCommerce\"\nheading \"Catalog workflow\" [level=2]\nlink \"1. Feeds Connect import\"\nlink \"2. Products 5 in catalog\"\nlink \"3. Process 8 done\"\nlink \"4. Export Templates \u0026 download\"\nheading \"Overview\" [level=2]\nlink \"Products 5 8 processed · 0 unprocessed\"\nlink \"Categories 2 Catalog tree\"\nlink \"Attributes 1 Attribute library\"\nlink \"Feeds 0 Import sources\"\nlink \"Credits Unlimited Enterprise · Billing\"\nheading \"Recent activity\" [level=2]\nlink \"View all\"\nheading \"Quick links\" [level=2]\nlink \"Feeds Import and map\"\nlink \"Products Browse and process\"\nlink \"Jobs Monitor tasks\"\nlink \"Exports Templates \u0026 download\"\nheading \"WHAT'S NEW\" [level=3]\nheading \"User Experience Improvements\" [level=3]\nheading \"Key Features:\" [level=4]\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-09T08:09:24Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-09T08:09:24Z",
"load_ms": 3416,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e12",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open products",
"type": "button"
},
{
"ref": "e13",
"selector": "a[data-testid=\"store-reconnect-link-woocommerce\"]",
"role": "link",
"name": "WooCommerce"
},
{
"ref": "e14",
"selector": "a[data-testid=\"store-reconnect-cta-woocommerce\"]",
"role": "link",
"name": "Reconnect WooCommerce"
},
{
"ref": "e15",
"selector": "div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Reconnect WooCommerce",
"type": "button"
},
{
"ref": "e16",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(1) \u003e a",
"role": "link",
"name": "1. Feeds Connect import"
},
{
"ref": "e17",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(2) \u003e a",
"role": "link",
"name": "2. Products 5 in catalog"
},
{
"ref": "e18",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(3) \u003e a",
"role": "link",
"name": "3. Process 8 done"
},
{
"ref": "e19",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(4) \u003e a",
"role": "link",
"name": "4. Export Templates \u0026 download"
},
{
"ref": "e20",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Products 5 8 processed · 0 unprocessed"
},
{
"ref": "e21",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Categories 2 Catalog tree"
},
{
"ref": "e22",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Attributes 1 Attribute library"
},
{
"ref": "e23",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Feeds 0 Import sources"
},
{
"ref": "e24",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(5)",
"role": "link",
"name": "Credits Unlimited Enterprise · Billing"
},
{
"ref": "e25",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(1) \u003e div:nth-of-type(1) \u003e a",
"role": "link",
"name": "View all"
},
{
"ref": "e26",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Feeds Import and map"
},
{
"ref": "e27",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Products Browse and process"
},
{
"ref": "e28",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Jobs Monitor tasks"
},
{
"ref": "e29",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Exports Templates \u0026 download"
},
{
"ref": "e30",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e31",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e32",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e33",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e34",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\03-dashboard-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D PLATFORM DEMO Platform Demo Unlimited Star\"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nbanner \"PLATFORM DEMO Platform Demo Unlimited Start tutorial Open pr\"\nheading \"Platform Demo\" [level=1]\nbutton \"Open products\"\nstatus \"Reconnect store after migration Store API credentials were n\" [testid=store-reconnect-banner]\nlink \"WooCommerce\" [testid=store-reconnect-link-woocommerce]\nlink \"Reconnect WooCommerce\" [testid=store-reconnect-cta-woocommerce]\nbutton \"Reconnect WooCommerce\"\nheading \"Catalog workflow\" [level=2]\nlink \"1. Feeds Connect import\"\nlink \"2. Products 5 in catalog\"\nlink \"3. Process 8 done\"\nlink \"4. Export Templates \u0026 download\"\nheading \"Overview\" [level=2]\nlink \"Products 5 8 processed · 0 unprocessed\"\nlink \"Categories 2 Catalog tree\"\nlink \"Attributes 1 Attribute library\"\nlink \"Feeds 0 Import sources\"\nlink \"Credits Unlimited Enterprise · Billing\"\nheading \"Recent activity\" [level=2]\nlink \"View all\"\nheading \"Quick links\" [level=2]\nlink \"Feeds Import and map\"\nlink \"Products Browse and process\"\nlink \"Jobs Monitor tasks\"\nlink \"Exports Templates \u0026 download\"\nheading \"WHAT'S NEW\" [level=3]\nheading \"User Experience Improvements\" [level=3]\nheading \"Key Features:\" [level=4]\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

@@ -1,340 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230428Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230428Z\\report.json",
"device": "mobile",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/settings?tab=profile",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\04-settings-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\04-settings-mobile.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230428Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(1)",
"role": "tab",
"name": "Profile",
"type": "button"
},
{
"ref": "e7",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(2)",
"role": "tab",
"name": "Company",
"type": "button"
},
{
"ref": "e8",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "tab",
"name": "Alerts",
"type": "button"
},
{
"ref": "e9",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(4)",
"role": "tab",
"name": "API keys",
"type": "button"
},
{
"ref": "e10",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(5)",
"role": "tab",
"name": "Team",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div:nth-of-type(1) \u003e div:nth-of-type(1) \u003e div \u003e button",
"role": "button",
"name": "Save changes",
"type": "button"
},
{
"ref": "e12",
"selector": "#firstName",
"role": "textbox",
"name": "First Name",
"type": "text",
"placeholder": "Your first name",
"value": "Demo"
},
{
"ref": "e13",
"selector": "#lastName",
"role": "textbox",
"name": "Last Name",
"type": "text",
"placeholder": "Your last name",
"value": "User"
},
{
"ref": "e14",
"selector": "#email",
"role": "textbox",
"name": "Email",
"type": "email",
"value": "demo@descrybe.local"
},
{
"ref": "e15",
"selector": "#uiLocale",
"role": "select",
"name": "Dashboard language",
"value": "en"
},
{
"ref": "e16",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e17",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e19",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e20",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 Settings Account, company, API keys, and tea\"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"Settings Account, company, API keys, and team.\"\nheading \"Settings\" [level=1]\ntab \"Profile\"\ntab \"Company\"\ntab \"Alerts\"\ntab \"API keys\"\ntab \"Team\"\nheading \"Profile\" [level=2]\nbutton \"Save changes\"\nheading \"Personal Information\" [level=3]\ntextbox \"First Name\"\ntextbox \"Last Name\"\ntextbox \"Email\"\ncombobox \"Dashboard language\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:04:28Z"
},
"final_url": "http://localhost:28472/settings?tab=profile",
"format": "png",
"generated_at": "2026-08-08T23:04:28Z",
"load_ms": 1959,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(1)",
"role": "tab",
"name": "Profile",
"type": "button"
},
{
"ref": "e7",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(2)",
"role": "tab",
"name": "Company",
"type": "button"
},
{
"ref": "e8",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "tab",
"name": "Alerts",
"type": "button"
},
{
"ref": "e9",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(4)",
"role": "tab",
"name": "API keys",
"type": "button"
},
{
"ref": "e10",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(5)",
"role": "tab",
"name": "Team",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div:nth-of-type(1) \u003e div:nth-of-type(1) \u003e div \u003e button",
"role": "button",
"name": "Save changes",
"type": "button"
},
{
"ref": "e12",
"selector": "#firstName",
"role": "textbox",
"name": "First Name",
"type": "text",
"placeholder": "Your first name",
"value": "Demo"
},
{
"ref": "e13",
"selector": "#lastName",
"role": "textbox",
"name": "Last Name",
"type": "text",
"placeholder": "Your last name",
"value": "User"
},
{
"ref": "e14",
"selector": "#email",
"role": "textbox",
"name": "Email",
"type": "email",
"value": "demo@descrybe.local"
},
{
"ref": "e15",
"selector": "#uiLocale",
"role": "select",
"name": "Dashboard language",
"value": "en"
},
{
"ref": "e16",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e17",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e19",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e20",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\04-settings-mobile.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 Settings Account, company, API keys, and tea\"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"Settings Account, company, API keys, and team.\"\nheading \"Settings\" [level=1]\ntab \"Profile\"\ntab \"Company\"\ntab \"Alerts\"\ntab \"API keys\"\ntab \"Team\"\nheading \"Profile\" [level=2]\nbutton \"Save changes\"\nheading \"Personal Information\" [level=3]\ntextbox \"First Name\"\ntextbox \"Last Name\"\ntextbox \"Email\"\ncombobox \"Dashboard language\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

@@ -1,408 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230850Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230850Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/settings?tab=profile",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\04-settings-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\04-settings-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230850Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(1)",
"role": "tab",
"name": "Profile",
"type": "button"
},
{
"ref": "e12",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(2)",
"role": "tab",
"name": "Company",
"type": "button"
},
{
"ref": "e13",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "tab",
"name": "Alerts",
"type": "button"
},
{
"ref": "e14",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(4)",
"role": "tab",
"name": "API keys",
"type": "button"
},
{
"ref": "e15",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(5)",
"role": "tab",
"name": "Team",
"type": "button"
},
{
"ref": "e16",
"selector": "div \u003e div:nth-of-type(1) \u003e div:nth-of-type(1) \u003e div \u003e button",
"role": "button",
"name": "Save changes",
"type": "button"
},
{
"ref": "e17",
"selector": "#firstName",
"role": "textbox",
"name": "First Name",
"type": "text",
"placeholder": "Your first name",
"value": "Demo"
},
{
"ref": "e18",
"selector": "#lastName",
"role": "textbox",
"name": "Last Name",
"type": "text",
"placeholder": "Your last name",
"value": "User"
},
{
"ref": "e19",
"selector": "#email",
"role": "textbox",
"name": "Email",
"type": "email",
"value": "demo@descrybe.local"
},
{
"ref": "e20",
"selector": "#uiLocale",
"role": "select",
"name": "Dashboard language",
"value": "en"
},
{
"ref": "e21",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e22",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e24",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e25",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D Settings Account, company, API keys, and t\"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nbanner \"Settings Account, company, API keys, and team.\"\nheading \"Settings\" [level=1]\ntab \"Profile\"\ntab \"Company\"\ntab \"Alerts\"\ntab \"API keys\"\ntab \"Team\"\nheading \"Profile\" [level=2]\nbutton \"Save changes\"\nheading \"Personal Information\" [level=3]\ntextbox \"First Name\"\ntextbox \"Last Name\"\ntextbox \"Email\"\ncombobox \"Dashboard language\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:08:50Z"
},
"final_url": "http://localhost:28472/settings?tab=profile",
"format": "png",
"generated_at": "2026-08-08T23:08:50Z",
"load_ms": 3897,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(1)",
"role": "tab",
"name": "Profile",
"type": "button"
},
{
"ref": "e12",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(2)",
"role": "tab",
"name": "Company",
"type": "button"
},
{
"ref": "e13",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "tab",
"name": "Alerts",
"type": "button"
},
{
"ref": "e14",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(4)",
"role": "tab",
"name": "API keys",
"type": "button"
},
{
"ref": "e15",
"selector": "div \u003e aside \u003e div \u003e div \u003e button:nth-of-type(5)",
"role": "tab",
"name": "Team",
"type": "button"
},
{
"ref": "e16",
"selector": "div \u003e div:nth-of-type(1) \u003e div:nth-of-type(1) \u003e div \u003e button",
"role": "button",
"name": "Save changes",
"type": "button"
},
{
"ref": "e17",
"selector": "#firstName",
"role": "textbox",
"name": "First Name",
"type": "text",
"placeholder": "Your first name",
"value": "Demo"
},
{
"ref": "e18",
"selector": "#lastName",
"role": "textbox",
"name": "Last Name",
"type": "text",
"placeholder": "Your last name",
"value": "User"
},
{
"ref": "e19",
"selector": "#email",
"role": "textbox",
"name": "Email",
"type": "email",
"value": "demo@descrybe.local"
},
{
"ref": "e20",
"selector": "#uiLocale",
"role": "select",
"name": "Dashboard language",
"value": "en"
},
{
"ref": "e21",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e22",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e24",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e25",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\04-settings-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D Settings Account, company, API keys, and t\"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nbanner \"Settings Account, company, API keys, and team.\"\nheading \"Settings\" [level=1]\ntab \"Profile\"\ntab \"Company\"\ntab \"Alerts\"\ntab \"API keys\"\ntab \"Team\"\nheading \"Profile\" [level=2]\nbutton \"Save changes\"\nheading \"Personal Information\" [level=3]\ntextbox \"First Name\"\ntextbox \"Last Name\"\ntextbox \"Email\"\ncombobox \"Dashboard language\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

@@ -1,268 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230430Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230430Z\\report.json",
"device": "mobile",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/billing",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\05-billing-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\05-billing-mobile.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230430Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Compare plans"
},
{
"ref": "e7",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Manage payment",
"type": "button"
},
{
"ref": "e8",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Add credits",
"type": "button"
},
{
"ref": "e9",
"selector": "div \u003e div \u003e section \u003e div:nth-of-type(1) \u003e select",
"role": "select",
"name": "Last 7 days Last 30 days Current cycle All time",
"value": "30d"
},
{
"ref": "e10",
"selector": "#main-content \u003e div \u003e div \u003e div:nth-of-type(3) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Public pricing"
},
{
"ref": "e11",
"selector": "#main-content \u003e div \u003e div \u003e div:nth-of-type(3) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Manage plan on Plans"
},
{
"ref": "e12",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e13",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e14",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e15",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e16",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 Usage \u0026 Billing Platform Demo · Unlimited pr\"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nheading \"Usage \u0026 Billing\" [level=1]\nlink \"Compare plans\"\nbutton \"Manage payment\"\nbutton \"Add credits\"\nheading \"Current plan\" [level=3]\nheading \"Credits remaining\" [level=3]\nheading \"Usage\" [level=2]\ncombobox \"Last 7 days Last 30 days Current cycle All time\"\nheading \"Products by day\" [level=3]\nlink \"Public pricing\"\nlink \"Manage plan on Plans\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:04:30Z"
},
"final_url": "http://localhost:28472/billing",
"format": "png",
"generated_at": "2026-08-08T23:04:30Z",
"load_ms": 1417,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Compare plans"
},
{
"ref": "e7",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Manage payment",
"type": "button"
},
{
"ref": "e8",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Add credits",
"type": "button"
},
{
"ref": "e9",
"selector": "div \u003e div \u003e section \u003e div:nth-of-type(1) \u003e select",
"role": "select",
"name": "Last 7 days Last 30 days Current cycle All time",
"value": "30d"
},
{
"ref": "e10",
"selector": "#main-content \u003e div \u003e div \u003e div:nth-of-type(3) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Public pricing"
},
{
"ref": "e11",
"selector": "#main-content \u003e div \u003e div \u003e div:nth-of-type(3) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Manage plan on Plans"
},
{
"ref": "e12",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e13",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e14",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e15",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e16",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\05-billing-mobile.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 Usage \u0026 Billing Platform Demo · Unlimited pr\"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nheading \"Usage \u0026 Billing\" [level=1]\nlink \"Compare plans\"\nbutton \"Manage payment\"\nbutton \"Add credits\"\nheading \"Current plan\" [level=3]\nheading \"Credits remaining\" [level=3]\nheading \"Usage\" [level=2]\ncombobox \"Last 7 days Last 30 days Current cycle All time\"\nheading \"Products by day\" [level=3]\nlink \"Public pricing\"\nlink \"Manage plan on Plans\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

@@ -1,336 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230857Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230857Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/billing",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\05-billing-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\05-billing-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230857Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Compare plans"
},
{
"ref": "e12",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Manage payment",
"type": "button"
},
{
"ref": "e13",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Add credits",
"type": "button"
},
{
"ref": "e14",
"selector": "div \u003e div \u003e section \u003e div:nth-of-type(1) \u003e select",
"role": "select",
"name": "Last 7 days Last 30 days Current cycle All time",
"value": "30d"
},
{
"ref": "e15",
"selector": "#main-content \u003e div \u003e div \u003e div:nth-of-type(3) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Public pricing"
},
{
"ref": "e16",
"selector": "#main-content \u003e div \u003e div \u003e div:nth-of-type(3) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Manage plan on Plans"
},
{
"ref": "e17",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e19",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e20",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e21",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D Usage \u0026 Billing Platform Demo · Unlimited \"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nheading \"Usage \u0026 Billing\" [level=1]\nlink \"Compare plans\"\nbutton \"Manage payment\"\nbutton \"Add credits\"\nheading \"Current plan\" [level=3]\nheading \"Credits remaining\" [level=3]\nheading \"Usage\" [level=2]\ncombobox \"Last 7 days Last 30 days Current cycle All time\"\nheading \"Products by day\" [level=3]\nlink \"Public pricing\"\nlink \"Manage plan on Plans\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:08:57Z"
},
"final_url": "http://localhost:28472/billing",
"format": "png",
"generated_at": "2026-08-08T23:08:57Z",
"load_ms": 4040,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Compare plans"
},
{
"ref": "e12",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Manage payment",
"type": "button"
},
{
"ref": "e13",
"selector": "div \u003e div \u003e div:nth-of-type(1) \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Add credits",
"type": "button"
},
{
"ref": "e14",
"selector": "div \u003e div \u003e section \u003e div:nth-of-type(1) \u003e select",
"role": "select",
"name": "Last 7 days Last 30 days Current cycle All time",
"value": "30d"
},
{
"ref": "e15",
"selector": "#main-content \u003e div \u003e div \u003e div:nth-of-type(3) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Public pricing"
},
{
"ref": "e16",
"selector": "#main-content \u003e div \u003e div \u003e div:nth-of-type(3) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Manage plan on Plans"
},
{
"ref": "e17",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e19",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e20",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e21",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\05-billing-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D Usage \u0026 Billing Platform Demo · Unlimited \"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nheading \"Usage \u0026 Billing\" [level=1]\nlink \"Compare plans\"\nbutton \"Manage payment\"\nbutton \"Add credits\"\nheading \"Current plan\" [level=3]\nheading \"Credits remaining\" [level=3]\nheading \"Usage\" [level=2]\ncombobox \"Last 7 days Last 30 days Current cycle All time\"\nheading \"Products by day\" [level=3]\nlink \"Public pricing\"\nlink \"Manage plan on Plans\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

@@ -1,312 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230434Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230434Z\\report.json",
"device": "mobile",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/support",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\06-support-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\06-support-mobile.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230434Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e button",
"role": "button",
"name": "Refresh",
"type": "button"
},
{
"ref": "e7",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e a",
"role": "link",
"name": "New ticket"
},
{
"ref": "e8",
"selector": "header \u003e div:nth-of-type(2) \u003e div \u003e a \u003e button",
"role": "button",
"name": "New ticket",
"type": "button"
},
{
"ref": "e9",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "All",
"type": "button"
},
{
"ref": "e10",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Pending",
"type": "button"
},
{
"ref": "e12",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(4)",
"role": "button",
"name": "Resolved",
"type": "button"
},
{
"ref": "e13",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(5)",
"role": "button",
"name": "Closed",
"type": "button"
},
{
"ref": "e14",
"selector": "tr[aria-label=\"Open ticket API matrix smoke billing\"]",
"role": "link",
"name": "Open ticket API matrix smoke billing"
},
{
"ref": "e15",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e16",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e17",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e19",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 Support Center Open a ticket for billing, bu\"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"Support Center Open a ticket for billing, bugs, or account h\"\nheading \"Support Center\" [level=1]\nbutton \"Refresh\"\nlink \"New ticket\"\nbutton \"New ticket\"\nheading \"Your tickets\" [level=3]\nbutton \"All\"\nbutton \"Open\"\nbutton \"Pending\"\nbutton \"Resolved\"\nbutton \"Closed\"\nlink \"Open ticket API matrix smoke billing\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:04:34Z"
},
"final_url": "http://localhost:28472/support",
"format": "png",
"generated_at": "2026-08-08T23:04:34Z",
"load_ms": 2358,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e button",
"role": "button",
"name": "Refresh",
"type": "button"
},
{
"ref": "e7",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e a",
"role": "link",
"name": "New ticket"
},
{
"ref": "e8",
"selector": "header \u003e div:nth-of-type(2) \u003e div \u003e a \u003e button",
"role": "button",
"name": "New ticket",
"type": "button"
},
{
"ref": "e9",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "All",
"type": "button"
},
{
"ref": "e10",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Pending",
"type": "button"
},
{
"ref": "e12",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(4)",
"role": "button",
"name": "Resolved",
"type": "button"
},
{
"ref": "e13",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(5)",
"role": "button",
"name": "Closed",
"type": "button"
},
{
"ref": "e14",
"selector": "tr[aria-label=\"Open ticket API matrix smoke billing\"]",
"role": "link",
"name": "Open ticket API matrix smoke billing"
},
{
"ref": "e15",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e16",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e17",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e19",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\06-support-mobile.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 Support Center Open a ticket for billing, bu\"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"Support Center Open a ticket for billing, bugs, or account h\"\nheading \"Support Center\" [level=1]\nbutton \"Refresh\"\nlink \"New ticket\"\nbutton \"New ticket\"\nheading \"Your tickets\" [level=3]\nbutton \"All\"\nbutton \"Open\"\nbutton \"Pending\"\nbutton \"Resolved\"\nbutton \"Closed\"\nlink \"Open ticket API matrix smoke billing\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

@@ -1,380 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230905Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230905Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/support",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\06-support-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\06-support-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230905Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e button",
"role": "button",
"name": "Refresh",
"type": "button"
},
{
"ref": "e12",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e a",
"role": "link",
"name": "New ticket"
},
{
"ref": "e13",
"selector": "header \u003e div:nth-of-type(2) \u003e div \u003e a \u003e button",
"role": "button",
"name": "New ticket",
"type": "button"
},
{
"ref": "e14",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "All",
"type": "button"
},
{
"ref": "e15",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open",
"type": "button"
},
{
"ref": "e16",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Pending",
"type": "button"
},
{
"ref": "e17",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(4)",
"role": "button",
"name": "Resolved",
"type": "button"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(5)",
"role": "button",
"name": "Closed",
"type": "button"
},
{
"ref": "e19",
"selector": "tr[aria-label=\"Open ticket API matrix smoke billing\"]",
"role": "link",
"name": "Open ticket API matrix smoke billing"
},
{
"ref": "e20",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e21",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e22",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e24",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D Support Center Open a ticket for billing, \"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nbanner \"Support Center Open a ticket for billing, bugs, or account h\"\nheading \"Support Center\" [level=1]\nbutton \"Refresh\"\nlink \"New ticket\"\nbutton \"New ticket\"\nheading \"Your tickets\" [level=3]\nbutton \"All\"\nbutton \"Open\"\nbutton \"Pending\"\nbutton \"Resolved\"\nbutton \"Closed\"\nlink \"Open ticket API matrix smoke billing\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:09:05Z"
},
"final_url": "http://localhost:28472/support",
"format": "png",
"generated_at": "2026-08-08T23:09:05Z",
"load_ms": 2695,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e button",
"role": "button",
"name": "Refresh",
"type": "button"
},
{
"ref": "e12",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e a",
"role": "link",
"name": "New ticket"
},
{
"ref": "e13",
"selector": "header \u003e div:nth-of-type(2) \u003e div \u003e a \u003e button",
"role": "button",
"name": "New ticket",
"type": "button"
},
{
"ref": "e14",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "All",
"type": "button"
},
{
"ref": "e15",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open",
"type": "button"
},
{
"ref": "e16",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Pending",
"type": "button"
},
{
"ref": "e17",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(4)",
"role": "button",
"name": "Resolved",
"type": "button"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(5)",
"role": "button",
"name": "Closed",
"type": "button"
},
{
"ref": "e19",
"selector": "tr[aria-label=\"Open ticket API matrix smoke billing\"]",
"role": "link",
"name": "Open ticket API matrix smoke billing"
},
{
"ref": "e20",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e21",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e22",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e24",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\06-support-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D Support Center Open a ticket for billing, \"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nbanner \"Support Center Open a ticket for billing, bugs, or account h\"\nheading \"Support Center\" [level=1]\nbutton \"Refresh\"\nlink \"New ticket\"\nbutton \"New ticket\"\nheading \"Your tickets\" [level=3]\nbutton \"All\"\nbutton \"Open\"\nbutton \"Pending\"\nbutton \"Resolved\"\nbutton \"Closed\"\nlink \"Open ticket API matrix smoke billing\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

@@ -1,346 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230439Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230439Z\\report.json",
"device": "mobile",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/support/new",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\07-support-new-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\07-support-new-mobile.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230439Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Back to tickets"
},
{
"ref": "e7",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Back to tickets",
"type": "button"
},
{
"ref": "e8",
"selector": "#support-subject",
"role": "textbox",
"name": "Subject",
"type": "text",
"placeholder": "Short summary"
},
{
"ref": "e9",
"selector": "#support-category",
"role": "select",
"name": "Category",
"value": "other"
},
{
"ref": "e10",
"selector": "#support-priority",
"role": "select",
"name": "Priority",
"value": "normal"
},
{
"ref": "e11",
"selector": "#support-sku",
"role": "textbox",
"name": "Related SKU",
"type": "text",
"placeholder": "e.g. ABC-123"
},
{
"ref": "e12",
"selector": "#support-tags",
"role": "textbox",
"name": "Tags",
"type": "text",
"placeholder": "login, invoice (comma-separated)"
},
{
"ref": "e13",
"selector": "#support-body",
"role": "textbox",
"name": "Message",
"placeholder": "Describe the issue, what you expected, and anything that helps us reproduce it."
},
{
"ref": "e14",
"selector": "form \u003e div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e button",
"role": "button",
"name": "Create ticket",
"type": "submit"
},
{
"ref": "e15",
"selector": "form \u003e div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e a",
"role": "link",
"name": "Cancel"
},
{
"ref": "e16",
"selector": "div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e a \u003e button",
"role": "button",
"name": "Cancel",
"type": "button"
},
{
"ref": "e17",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e19",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e20",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e21",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 New support ticket Tell us what you need hel\"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"New support ticket Tell us what you need help with. Include \"\nheading \"New support ticket\" [level=1]\nlink \"Back to tickets\"\nbutton \"Back to tickets\"\nform \"Ticket details Status starts as open. You may get an automat\" [testid=support-create-form]\nheading \"Ticket details\" [level=3]\ntextbox \"Subject\"\ncombobox \"Category\"\ncombobox \"Priority\"\ntextbox \"Related SKU\"\ntextbox \"Tags\"\ntextbox \"Message\"\nbutton \"Create ticket\"\nlink \"Cancel\"\nbutton \"Cancel\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:04:39Z"
},
"final_url": "http://localhost:28472/support/new",
"format": "png",
"generated_at": "2026-08-08T23:04:39Z",
"load_ms": 2556,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Back to tickets"
},
{
"ref": "e7",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Back to tickets",
"type": "button"
},
{
"ref": "e8",
"selector": "#support-subject",
"role": "textbox",
"name": "Subject",
"type": "text",
"placeholder": "Short summary"
},
{
"ref": "e9",
"selector": "#support-category",
"role": "select",
"name": "Category",
"value": "other"
},
{
"ref": "e10",
"selector": "#support-priority",
"role": "select",
"name": "Priority",
"value": "normal"
},
{
"ref": "e11",
"selector": "#support-sku",
"role": "textbox",
"name": "Related SKU",
"type": "text",
"placeholder": "e.g. ABC-123"
},
{
"ref": "e12",
"selector": "#support-tags",
"role": "textbox",
"name": "Tags",
"type": "text",
"placeholder": "login, invoice (comma-separated)"
},
{
"ref": "e13",
"selector": "#support-body",
"role": "textbox",
"name": "Message",
"placeholder": "Describe the issue, what you expected, and anything that helps us reproduce it."
},
{
"ref": "e14",
"selector": "form \u003e div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e button",
"role": "button",
"name": "Create ticket",
"type": "submit"
},
{
"ref": "e15",
"selector": "form \u003e div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e a",
"role": "link",
"name": "Cancel"
},
{
"ref": "e16",
"selector": "div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e a \u003e button",
"role": "button",
"name": "Cancel",
"type": "button"
},
{
"ref": "e17",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e19",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e20",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e21",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\07-support-new-mobile.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 New support ticket Tell us what you need hel\"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"New support ticket Tell us what you need help with. Include \"\nheading \"New support ticket\" [level=1]\nlink \"Back to tickets\"\nbutton \"Back to tickets\"\nform \"Ticket details Status starts as open. You may get an automat\" [testid=support-create-form]\nheading \"Ticket details\" [level=3]\ntextbox \"Subject\"\ncombobox \"Category\"\ncombobox \"Priority\"\ntextbox \"Related SKU\"\ntextbox \"Tags\"\ntextbox \"Message\"\nbutton \"Create ticket\"\nlink \"Cancel\"\nbutton \"Cancel\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

@@ -1,414 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230911Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230911Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/support/new",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\07-support-new-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\07-support-new-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230911Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Back to tickets"
},
{
"ref": "e12",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Back to tickets",
"type": "button"
},
{
"ref": "e13",
"selector": "#support-subject",
"role": "textbox",
"name": "Subject",
"type": "text",
"placeholder": "Short summary"
},
{
"ref": "e14",
"selector": "#support-category",
"role": "select",
"name": "Category",
"value": "other"
},
{
"ref": "e15",
"selector": "#support-priority",
"role": "select",
"name": "Priority",
"value": "normal"
},
{
"ref": "e16",
"selector": "#support-sku",
"role": "textbox",
"name": "Related SKU",
"type": "text",
"placeholder": "e.g. ABC-123"
},
{
"ref": "e17",
"selector": "#support-tags",
"role": "textbox",
"name": "Tags",
"type": "text",
"placeholder": "login, invoice (comma-separated)"
},
{
"ref": "e18",
"selector": "#support-body",
"role": "textbox",
"name": "Message",
"placeholder": "Describe the issue, what you expected, and anything that helps us reproduce it."
},
{
"ref": "e19",
"selector": "form \u003e div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e button",
"role": "button",
"name": "Create ticket",
"type": "submit"
},
{
"ref": "e20",
"selector": "form \u003e div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e a",
"role": "link",
"name": "Cancel"
},
{
"ref": "e21",
"selector": "div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e a \u003e button",
"role": "button",
"name": "Cancel",
"type": "button"
},
{
"ref": "e22",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e24",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e25",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e26",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D New support ticket Tell us what you need h\"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nbanner \"New support ticket Tell us what you need help with. Include \"\nheading \"New support ticket\" [level=1]\nlink \"Back to tickets\"\nbutton \"Back to tickets\"\nform \"Ticket details Status starts as open. You may get an automat\" [testid=support-create-form]\nheading \"Ticket details\" [level=3]\ntextbox \"Subject\"\ncombobox \"Category\"\ncombobox \"Priority\"\ntextbox \"Related SKU\"\ntextbox \"Tags\"\ntextbox \"Message\"\nbutton \"Create ticket\"\nlink \"Cancel\"\nbutton \"Cancel\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:09:11Z"
},
"final_url": "http://localhost:28472/support/new",
"format": "png",
"generated_at": "2026-08-08T23:09:11Z",
"load_ms": 2657,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e6",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e7",
"selector": "header \u003e div \u003e div \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e8",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e9",
"selector": "a[aria-label=\"Account settings for demo@descrybe.local\"]",
"role": "link",
"name": "Account settings for demo@descrybe.local"
},
{
"ref": "e10",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Back to tickets"
},
{
"ref": "e12",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Back to tickets",
"type": "button"
},
{
"ref": "e13",
"selector": "#support-subject",
"role": "textbox",
"name": "Subject",
"type": "text",
"placeholder": "Short summary"
},
{
"ref": "e14",
"selector": "#support-category",
"role": "select",
"name": "Category",
"value": "other"
},
{
"ref": "e15",
"selector": "#support-priority",
"role": "select",
"name": "Priority",
"value": "normal"
},
{
"ref": "e16",
"selector": "#support-sku",
"role": "textbox",
"name": "Related SKU",
"type": "text",
"placeholder": "e.g. ABC-123"
},
{
"ref": "e17",
"selector": "#support-tags",
"role": "textbox",
"name": "Tags",
"type": "text",
"placeholder": "login, invoice (comma-separated)"
},
{
"ref": "e18",
"selector": "#support-body",
"role": "textbox",
"name": "Message",
"placeholder": "Describe the issue, what you expected, and anything that helps us reproduce it."
},
{
"ref": "e19",
"selector": "form \u003e div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e button",
"role": "button",
"name": "Create ticket",
"type": "submit"
},
{
"ref": "e20",
"selector": "form \u003e div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e a",
"role": "link",
"name": "Cancel"
},
{
"ref": "e21",
"selector": "div \u003e div:nth-of-type(2) \u003e div:nth-of-type(4) \u003e a \u003e button",
"role": "button",
"name": "Cancel",
"type": "button"
},
{
"ref": "e22",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e24",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e25",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e26",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\07-support-new-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 D New support ticket Tell us what you need h\"\nbanner \"Platform Demo 1 D\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Account settings for demo@descrybe.local\"\nbutton \"Sign out\"\nbanner \"New support ticket Tell us what you need help with. Include \"\nheading \"New support ticket\" [level=1]\nlink \"Back to tickets\"\nbutton \"Back to tickets\"\nform \"Ticket details Status starts as open. You may get an automat\" [testid=support-create-form]\nheading \"Ticket details\" [level=3]\ntextbox \"Subject\"\ncombobox \"Category\"\ncombobox \"Priority\"\ntextbox \"Related SKU\"\ntextbox \"Tags\"\ntextbox \"Message\"\nbutton \"Create ticket\"\nlink \"Cancel\"\nbutton \"Cancel\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

@@ -1,136 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231439Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231439Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/admin/support",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\10-admin-support-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\10-admin-support-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231439Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#admin-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e button",
"role": "button",
"name": "Refresh",
"type": "button"
},
{
"ref": "e3",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e4",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e5",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Support Center Staff inbox — claim from the queue, filter by\"\nbanner \"Support Center Staff inbox — claim from the queue, filter by\"\nheading \"Support Center\" [level=1]\nbutton \"Refresh\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:14:39Z"
},
"final_url": "http://localhost:28472/admin/support",
"format": "png",
"generated_at": "2026-08-08T23:14:39Z",
"load_ms": 15197,
"outline": [
{
"ref": "e1",
"selector": "#admin-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "div \u003e header \u003e div:nth-of-type(2) \u003e div \u003e button",
"role": "button",
"name": "Refresh",
"type": "button"
},
{
"ref": "e3",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e4",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e5",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\10-admin-support-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"Support Center Staff inbox — claim from the queue, filter by\"\nbanner \"Support Center Staff inbox — claim from the queue, filter by\"\nheading \"Support Center\" [level=1]\nbutton \"Refresh\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

@@ -1,136 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231441Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231441Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/admin/users",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\11-admin-users-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\11-admin-users-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231441Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#admin-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "#main-content \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Re-issue set-password invites",
"type": "button"
},
{
"ref": "e3",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e4",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e5",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"PLATFORM ADMIN Users \u0026 organizations Platform user directory\"\nbanner \"PLATFORM ADMIN Users \u0026 organizations Platform user directory\"\nheading \"Users \u0026 organizations\" [level=1]\nbutton \"Re-issue set-password invites\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:14:41Z"
},
"final_url": "http://localhost:28472/admin/users",
"format": "png",
"generated_at": "2026-08-08T23:14:41Z",
"load_ms": 1162,
"outline": [
{
"ref": "e1",
"selector": "#admin-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "#main-content \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button",
"role": "button",
"name": "Re-issue set-password invites",
"type": "button"
},
{
"ref": "e3",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e4",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e5",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\11-admin-users-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"PLATFORM ADMIN Users \u0026 organizations Platform user directory\"\nbanner \"PLATFORM ADMIN Users \u0026 organizations Platform user directory\"\nheading \"Users \u0026 organizations\" [level=1]\nbutton \"Re-issue set-password invites\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

@@ -1,122 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231457Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231457Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/admin/settings",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\12-admin-settings-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\12-admin-settings-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231457Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#admin-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e3",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e4",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e5",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform settings Platform credentials, AI roles, mail, and \"\nbanner \"Platform settings Platform credentials, AI roles, mail, and \"\nheading \"Platform settings\" [level=1]\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:14:57Z"
},
"final_url": "http://localhost:28472/admin/settings",
"format": "png",
"generated_at": "2026-08-08T23:14:57Z",
"load_ms": 15130,
"outline": [
{
"ref": "e1",
"selector": "#admin-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e3",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e4",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e5",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\12-admin-settings-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform settings Platform credentials, AI roles, mail, and \"\nbanner \"Platform settings Platform credentials, AI roles, mail, and \"\nheading \"Platform settings\" [level=1]\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

@@ -1,424 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230831Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230831Z\\report.json",
"device": "mobile",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\13-nav-open-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\13-nav-open-mobile.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T230831Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e7",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open products",
"type": "button"
},
{
"ref": "e8",
"selector": "a[data-testid=\"store-reconnect-link-woocommerce\"]",
"role": "link",
"name": "WooCommerce"
},
{
"ref": "e9",
"selector": "a[data-testid=\"store-reconnect-cta-woocommerce\"]",
"role": "link",
"name": "Reconnect WooCommerce"
},
{
"ref": "e10",
"selector": "div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Reconnect WooCommerce",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(1) \u003e a",
"role": "link",
"name": "1. Feeds Connect import"
},
{
"ref": "e12",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(2) \u003e a",
"role": "link",
"name": "2. Products 5 in catalog"
},
{
"ref": "e13",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(3) \u003e a",
"role": "link",
"name": "3. Process 8 done"
},
{
"ref": "e14",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(4) \u003e a",
"role": "link",
"name": "4. Export Templates \u0026 download"
},
{
"ref": "e15",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Products 5 8 processed · 0 unprocessed"
},
{
"ref": "e16",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Categories 2 Catalog tree"
},
{
"ref": "e17",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Attributes 1 Attribute library"
},
{
"ref": "e18",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Feeds 0 Import sources"
},
{
"ref": "e19",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(5)",
"role": "link",
"name": "Credits Unlimited Enterprise · Billing"
},
{
"ref": "e20",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(1) \u003e div:nth-of-type(1) \u003e a",
"role": "link",
"name": "View all"
},
{
"ref": "e21",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Feeds Import and map"
},
{
"ref": "e22",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Products Browse and process"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Jobs Monitor tasks"
},
{
"ref": "e24",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Exports Templates \u0026 download"
},
{
"ref": "e25",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e26",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e27",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e28",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e29",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 PLATFORM DEMO Platform Demo Unlimited Start \"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"PLATFORM DEMO Platform Demo Unlimited Start tutorial Open pr\"\nheading \"Platform Demo\" [level=1]\nbutton \"Start tutorial\"\nbutton \"Open products\"\nstatus \"Reconnect store after migration Store API credentials were n\" [testid=store-reconnect-banner]\nlink \"WooCommerce\" [testid=store-reconnect-link-woocommerce]\nlink \"Reconnect WooCommerce\" [testid=store-reconnect-cta-woocommerce]\nbutton \"Reconnect WooCommerce\"\nheading \"Catalog workflow\" [level=2]\nlink \"1. Feeds Connect import\"\nlink \"2. Products 5 in catalog\"\nlink \"3. Process 8 done\"\nlink \"4. Export Templates \u0026 download\"\nheading \"Overview\" [level=2]\nlink \"Products 5 8 processed · 0 unprocessed\"\nlink \"Categories 2 Catalog tree\"\nlink \"Attributes 1 Attribute library\"\nlink \"Feeds 0 Import sources\"\nlink \"Credits Unlimited Enterprise · Billing\"\nheading \"Recent activity\" [level=2]\nlink \"View all\"\nheading \"Quick links\" [level=2]\nlink \"Feeds Import and map\"\nlink \"Products Browse and process\"\nlink \"Jobs Monitor tasks\"\nlink \"Exports Templates \u0026 download\"\nheading \"WHAT'S NEW\" [level=3]\nheading \"User Experience Improvements\" [level=3]\nheading \"Key Features:\" [level=4]\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:08:31Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-08T23:08:31Z",
"load_ms": 3676,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Switch user: Demo User · Platform Demo\"]",
"role": "button",
"name": "Switch user: Demo User · Platform Demo",
"type": "button"
},
{
"ref": "e4",
"selector": "a[aria-label=\"Support notifications, 1 unread\"]",
"role": "link",
"name": "Support notifications, 1 unread"
},
{
"ref": "e5",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e6",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e7",
"selector": "div \u003e div \u003e header \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Open products",
"type": "button"
},
{
"ref": "e8",
"selector": "a[data-testid=\"store-reconnect-link-woocommerce\"]",
"role": "link",
"name": "WooCommerce"
},
{
"ref": "e9",
"selector": "a[data-testid=\"store-reconnect-cta-woocommerce\"]",
"role": "link",
"name": "Reconnect WooCommerce"
},
{
"ref": "e10",
"selector": "div:nth-of-type(1) \u003e div \u003e div:nth-of-type(2) \u003e a \u003e button",
"role": "button",
"name": "Reconnect WooCommerce",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(1) \u003e a",
"role": "link",
"name": "1. Feeds Connect import"
},
{
"ref": "e12",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(2) \u003e a",
"role": "link",
"name": "2. Products 5 in catalog"
},
{
"ref": "e13",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(3) \u003e a",
"role": "link",
"name": "3. Process 8 done"
},
{
"ref": "e14",
"selector": "div \u003e section:nth-of-type(1) \u003e ol \u003e li:nth-of-type(4) \u003e a",
"role": "link",
"name": "4. Export Templates \u0026 download"
},
{
"ref": "e15",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Products 5 8 processed · 0 unprocessed"
},
{
"ref": "e16",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Categories 2 Catalog tree"
},
{
"ref": "e17",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Attributes 1 Attribute library"
},
{
"ref": "e18",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Feeds 0 Import sources"
},
{
"ref": "e19",
"selector": "div \u003e div \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(5)",
"role": "link",
"name": "Credits Unlimited Enterprise · Billing"
},
{
"ref": "e20",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(1) \u003e div:nth-of-type(1) \u003e a",
"role": "link",
"name": "View all"
},
{
"ref": "e21",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(1)",
"role": "link",
"name": "Feeds Import and map"
},
{
"ref": "e22",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(2)",
"role": "link",
"name": "Products Browse and process"
},
{
"ref": "e23",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(3)",
"role": "link",
"name": "Jobs Monitor tasks"
},
{
"ref": "e24",
"selector": "div \u003e div:nth-of-type(2) \u003e section:nth-of-type(2) \u003e div:nth-of-type(2) \u003e a:nth-of-type(4)",
"role": "link",
"name": "Exports Templates \u0026 download"
},
{
"ref": "e25",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e26",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e27",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e28",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
},
{
"ref": "e29",
"selector": "button[aria-label=\"Open system assistant\"]",
"role": "button",
"name": "Open system assistant",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\13-nav-open-mobile.png",
"snapshot": "link \"Skip to content\"\nmain \"Platform Demo 1 PLATFORM DEMO Platform Demo Unlimited Start \"\nbanner \"Platform Demo 1\"\nbutton \"Open navigation\"\nbutton \"Switch user: Demo User · Platform Demo\"\nlink \"Support notifications, 1 unread\"\nbutton \"Sign out\"\nbanner \"PLATFORM DEMO Platform Demo Unlimited Start tutorial Open pr\"\nheading \"Platform Demo\" [level=1]\nbutton \"Start tutorial\"\nbutton \"Open products\"\nstatus \"Reconnect store after migration Store API credentials were n\" [testid=store-reconnect-banner]\nlink \"WooCommerce\" [testid=store-reconnect-link-woocommerce]\nlink \"Reconnect WooCommerce\" [testid=store-reconnect-cta-woocommerce]\nbutton \"Reconnect WooCommerce\"\nheading \"Catalog workflow\" [level=2]\nlink \"1. Feeds Connect import\"\nlink \"2. Products 5 in catalog\"\nlink \"3. Process 8 done\"\nlink \"4. Export Templates \u0026 download\"\nheading \"Overview\" [level=2]\nlink \"Products 5 8 processed · 0 unprocessed\"\nlink \"Categories 2 Catalog tree\"\nlink \"Attributes 1 Attribute library\"\nlink \"Feeds 0 Import sources\"\nlink \"Credits Unlimited Enterprise · Billing\"\nheading \"Recent activity\" [level=2]\nlink \"View all\"\nheading \"Quick links\" [level=2]\nlink \"Feeds Import and map\"\nlink \"Products Browse and process\"\nlink \"Jobs Monitor tasks\"\nlink \"Exports Templates \u0026 download\"\nheading \"WHAT'S NEW\" [level=3]\nheading \"User Experience Improvements\" [level=3]\nheading \"Key Features:\" [level=4]\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"\nbutton \"Open system assistant\"",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

@@ -1,206 +0,0 @@
{
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231500Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231500Z\\report.json",
"device": "tablet",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\13-nav-open-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\13-nav-open-tablet.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260808T231500Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e4",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e5",
"selector": "header \u003e div \u003e div \u003e div \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e6",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e7",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e8",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e9",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e10",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nmain \"Loading… Loading…\"\nbanner \"Start tutorial Tour topics Sign out\"\nbutton \"Open navigation\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nbutton \"Sign out\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"action_log": [
"step 1 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-08T23:15:00Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-08T23:15:00Z",
"load_ms": 913,
"outline": [
{
"ref": "e1",
"selector": "#app-shell \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "button[aria-label=\"Open navigation\"]",
"role": "button",
"name": "Open navigation",
"type": "button"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Start tutorial\"]",
"role": "button",
"name": "Start tutorial",
"type": "button"
},
{
"ref": "e4",
"selector": "button[aria-label=\"Browse tutorial sections\"]",
"role": "button",
"name": "Browse tutorial sections",
"type": "button"
},
{
"ref": "e5",
"selector": "header \u003e div \u003e div \u003e div \u003e button",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e6",
"selector": "#main-content \u003e header \u003e div \u003e div \u003e button:nth-of-type(3)",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e7",
"selector": "button[aria-label=\"Sign out\"]",
"role": "button",
"name": "Sign out",
"type": "button"
},
{
"ref": "e8",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e9",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e10",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e11",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\13-nav-open-tablet.png",
"snapshot": "link \"Skip to content\"\nmain \"Loading… Loading…\"\nbanner \"Start tutorial Tour topics Sign out\"\nbutton \"Open navigation\"\nbutton \"Start tutorial\"\nbutton \"Browse tutorial sections\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nbutton \"Sign out\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-185
View File
@@ -1,185 +0,0 @@
#!/usr/bin/env python3
"""Batch mobile/tablet screenshots with a reused codehelper browser session."""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
ROOT = Path(r"F:\laragon\www\_MY\descrybe-v2")
OUT = ROOT / "apps" / "web" / "static" / ".audit"
BASE = "http://localhost:28472"
PAGES = [
("02-login", "/login", True),
("03-dashboard", "/dashboard", False),
("04-settings", "/settings", False),
("05-billing", "/billing", False),
("06-support", "/support", False),
("07-support-new", "/support/new", False),
("08-admin", "/admin", False),
("09-admin-billing", "/admin/billing", False),
("10-admin-support", "/admin/support", False),
("11-admin-users", "/admin/users", False),
("12-admin-settings", "/admin/settings", False),
]
def run(cmd: list[str]) -> subprocess.CompletedProcess[str]:
print(">", " ".join(cmd[:8]), "...")
r = subprocess.run(cmd, capture_output=True, text=True)
if r.stdout:
print(r.stdout[-2000:] if len(r.stdout) > 2000 else r.stdout)
if r.stderr:
print(r.stderr[-2000:] if len(r.stderr) > 2000 else r.stderr, file=sys.stderr)
return r
def main() -> int:
OUT.mkdir(parents=True, exist_ok=True)
login_actions = json.dumps(
[
{"action": "wait_hydrate"},
{
"action": "fill",
"selector": "input[type=email],input[name=email],#email",
"text": "demo@descrybe.local",
},
{
"action": "fill",
"selector": "input[type=password],input[name=password],#password",
"text": "DemoPass123!",
},
{"action": "click", "selector": "button[type=submit]"},
{"action": "wait_nav"},
{"action": "wait_hydrate"},
{"action": "wait", "ms": 1500},
# dismiss cookies if present
{"action": "click", "selector": "button:has-text('Accept all'), [data-testid=cookie-consent-banner] button:nth-of-type(3)"},
{"action": "wait", "ms": 500},
]
)
for device in ("mobile", "tablet"):
session = f"audit-{device}"
# login once per device
r = run(
[
"codehelper",
"browser",
"test",
f"{BASE}/login",
"--device",
device,
"--format",
"png",
"--wait-hydrate",
"--session",
session,
"--session-clear",
"--actions",
login_actions,
"-o",
str(OUT / f"00-post-login-{device}.png"),
"--path",
str(ROOT),
]
)
if r.returncode != 0:
print(f"login failed for {device}", file=sys.stderr)
return r.returncode
for slug, path, public in PAGES:
if path == "/login":
# public unauth shot (separate short session not needed — skip for auth device)
continue
actions = json.dumps(
[
{"action": "navigate", "url": f"{BASE}{path}"},
{"action": "wait_hydrate"},
{"action": "wait", "ms": 1200},
]
)
out = OUT / f"{slug}-{device}.png"
r = run(
[
"codehelper",
"browser",
"test",
f"{BASE}{path}",
"--device",
device,
"--format",
"png",
"--wait-hydrate",
"--session",
session,
"--actions",
actions,
"-o",
str(out),
"--path",
str(ROOT),
"--snapshot",
]
)
print(f"{slug} {device} exit={r.returncode} -> {out.exists()}")
# nav open on dashboard
nav_actions = json.dumps(
[
{"action": "navigate", "url": f"{BASE}/dashboard"},
{"action": "wait_hydrate"},
{"action": "click", "selector": 'button[aria-label="Open navigation"], [data-tour=nav-mobile-toggle]'},
{"action": "wait", "ms": 800},
]
)
r = run(
[
"codehelper",
"browser",
"test",
f"{BASE}/dashboard",
"--device",
device,
"--format",
"png",
"--wait-hydrate",
"--session",
session,
"--actions",
nav_actions,
"-o",
str(OUT / f"13-nav-open-{device}.png"),
"--path",
str(ROOT),
]
)
print(f"nav-open {device} exit={r.returncode}")
# public login (no session)
for device in ("mobile", "tablet"):
run(
[
"codehelper",
"browser",
"test",
f"{BASE}/login",
"--device",
device,
"--format",
"png",
"--wait-hydrate",
"-o",
str(OUT / f"02-login-{device}.png"),
"--path",
str(ROOT),
]
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,9 +0,0 @@
[
{"action": "wait_hydrate"},
{"action": "fill", "selector": "input[type=email],input[name=email],#email", "text": "demo@descrybe.local"},
{"action": "fill", "selector": "input[type=password],input[name=password],#password", "text": "DemoPass123!"},
{"action": "click", "selector": "button[type=submit]"},
{"action": "wait_nav"},
{"action": "wait_hydrate"},
{"action": "wait", "ms": 2000}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

@@ -1,304 +0,0 @@
{
"action_log": [
"step 1 storage_set descrybe-cookie-consent — ok (descrybe-cookie-consent)",
"step 2 navigate — FAILED: navigate requires text=url"
],
"actions_failed": true,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260809T081043Z",
"debug_pack_json": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260809T081043Z\\report.json",
"device": "mobile",
"doc_status": 200,
"failed_requests": null,
"failure_pack": {
"failed": true,
"final_url": "http://localhost:28472/login",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\probe-consent.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\probe-consent.report.json",
"pack_dir": "C:\\Users\\Green Eclipse\\.codehelper\\browser\\debug-packs\\20260809T081043Z",
"console_errors": [],
"page_errors": null,
"failed_requests": null,
"outline": [
{
"ref": "e1",
"selector": "#auth-shell \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "a[aria-label=\"Descrybe home\"]",
"role": "link",
"name": "Descrybe home"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Interface language: English\"]",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e4",
"selector": "button[aria-label=\"Switch to dark theme\"]",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e5",
"selector": "header \u003e div \u003e div \u003e div \u003e a",
"role": "link",
"name": "Demo Book a Demo"
},
{
"ref": "e6",
"selector": "#email",
"role": "textbox",
"name": "Email",
"type": "email"
},
{
"ref": "e7",
"selector": "#password",
"role": "textbox",
"name": "Password",
"type": "password"
},
{
"ref": "e8",
"selector": "div \u003e div \u003e div:nth-of-type(2) \u003e form \u003e button",
"role": "button",
"name": "Sign in",
"type": "submit"
},
{
"ref": "e9",
"selector": "div \u003e div \u003e div:nth-of-type(2) \u003e p:nth-of-type(1) \u003e a",
"role": "link",
"name": "Create company"
},
{
"ref": "e10",
"selector": "div \u003e div \u003e div:nth-of-type(2) \u003e p:nth-of-type(2) \u003e a",
"role": "link",
"name": "Accept invite"
},
{
"ref": "e11",
"selector": "div \u003e div \u003e div:nth-of-type(2) \u003e p:nth-of-type(3) \u003e a",
"role": "link",
"name": "View pricing"
},
{
"ref": "e12",
"selector": "footer \u003e div \u003e div \u003e nav \u003e a:nth-of-type(1)",
"role": "link",
"name": "Privacy Policy"
},
{
"ref": "e13",
"selector": "footer \u003e div \u003e div \u003e nav \u003e a:nth-of-type(2)",
"role": "link",
"name": "Cookie Policy"
},
{
"ref": "e14",
"selector": "footer \u003e div \u003e div \u003e nav \u003e button",
"role": "button",
"name": "Cookie settings",
"type": "button"
},
{
"ref": "e15",
"selector": "footer \u003e div \u003e div \u003e nav \u003e a:nth-of-type(3)",
"role": "link",
"name": "Terms of Service"
},
{
"ref": "e16",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e17",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e19",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"snapshot": "link \"Skip to content\"\nbanner \"Descrybe Demo\"\nlink \"Descrybe home\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Demo\"\nmain \"Sign in Use your Descrybe email and password. Email Password\"\nheading \"Sign in\" [level=1]\nform \"Email Password Sign in\"\ntextbox \"Email\"\ntextbox \"Password\"\nbutton \"Sign in\"\nlink \"Create company\"\nlink \"Accept invite\"\nlink \"View pricing\"\ncontentinfo \"Descrybe © 2026 Descrybe. All rights reserved. Privacy Polic\"\nnavigation \"Legal\"\nlink \"Privacy Policy\"\nlink \"Cookie Policy\"\nbutton \"Cookie settings\"\nlink \"Terms of Service\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"action_log": [
"step 1 storage_set descrybe-cookie-consent — ok (descrybe-cookie-consent)",
"step 2 navigate — FAILED: navigate requires text=url"
],
"failure_shot": true,
"generated_at": "2026-08-09T08:10:43Z"
},
"final_url": "http://localhost:28472/login",
"format": "png",
"generated_at": "2026-08-09T08:10:43Z",
"load_ms": 1322,
"outline": [
{
"ref": "e1",
"selector": "#auth-shell \u003e div:nth-of-type(2) \u003e a",
"role": "link",
"name": "Skip to content"
},
{
"ref": "e2",
"selector": "a[aria-label=\"Descrybe home\"]",
"role": "link",
"name": "Descrybe home"
},
{
"ref": "e3",
"selector": "button[aria-label=\"Interface language: English\"]",
"role": "button",
"name": "Interface language: English",
"type": "button"
},
{
"ref": "e4",
"selector": "button[aria-label=\"Switch to dark theme\"]",
"role": "button",
"name": "Switch to dark theme",
"type": "button"
},
{
"ref": "e5",
"selector": "header \u003e div \u003e div \u003e div \u003e a",
"role": "link",
"name": "Demo Book a Demo"
},
{
"ref": "e6",
"selector": "#email",
"role": "textbox",
"name": "Email",
"type": "email"
},
{
"ref": "e7",
"selector": "#password",
"role": "textbox",
"name": "Password",
"type": "password"
},
{
"ref": "e8",
"selector": "div \u003e div \u003e div:nth-of-type(2) \u003e form \u003e button",
"role": "button",
"name": "Sign in",
"type": "submit"
},
{
"ref": "e9",
"selector": "div \u003e div \u003e div:nth-of-type(2) \u003e p:nth-of-type(1) \u003e a",
"role": "link",
"name": "Create company"
},
{
"ref": "e10",
"selector": "div \u003e div \u003e div:nth-of-type(2) \u003e p:nth-of-type(2) \u003e a",
"role": "link",
"name": "Accept invite"
},
{
"ref": "e11",
"selector": "div \u003e div \u003e div:nth-of-type(2) \u003e p:nth-of-type(3) \u003e a",
"role": "link",
"name": "View pricing"
},
{
"ref": "e12",
"selector": "footer \u003e div \u003e div \u003e nav \u003e a:nth-of-type(1)",
"role": "link",
"name": "Privacy Policy"
},
{
"ref": "e13",
"selector": "footer \u003e div \u003e div \u003e nav \u003e a:nth-of-type(2)",
"role": "link",
"name": "Cookie Policy"
},
{
"ref": "e14",
"selector": "footer \u003e div \u003e div \u003e nav \u003e button",
"role": "button",
"name": "Cookie settings",
"type": "button"
},
{
"ref": "e15",
"selector": "footer \u003e div \u003e div \u003e nav \u003e a:nth-of-type(3)",
"role": "link",
"name": "Terms of Service"
},
{
"ref": "e16",
"selector": "#cookie-consent-desc \u003e a",
"role": "link",
"name": "Read our Cookie Policy"
},
{
"ref": "e17",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(1)",
"role": "button",
"name": "Customize",
"type": "button"
},
{
"ref": "e18",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(2)",
"role": "button",
"name": "Reject non-essential",
"type": "button"
},
{
"ref": "e19",
"selector": "div \u003e div:nth-of-type(2) \u003e div \u003e div:nth-of-type(2) \u003e button:nth-of-type(3)",
"role": "button",
"name": "Accept all",
"type": "button"
}
],
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\probe-consent.png",
"snapshot": "link \"Skip to content\"\nbanner \"Descrybe Demo\"\nlink \"Descrybe home\"\nbutton \"Interface language: English\"\nbutton \"Switch to dark theme\"\nlink \"Demo\"\nmain \"Sign in Use your Descrybe email and password. Email Password\"\nheading \"Sign in\" [level=1]\nform \"Email Password Sign in\"\ntextbox \"Email\"\ntextbox \"Password\"\nbutton \"Sign in\"\nlink \"Create company\"\nlink \"Accept invite\"\nlink \"View pricing\"\ncontentinfo \"Descrybe © 2026 Descrybe. All rights reserved. Privacy Polic\"\nnavigation \"Legal\"\nlink \"Privacy Policy\"\nlink \"Cookie Policy\"\nbutton \"Cookie settings\"\nlink \"Terms of Service\"\ndialog \"Cookies and analytics We use necessary cookies to run Descry\" [testid=cookie-consent-banner]\nheading \"Cookies and analytics\" [level=2]\nlink \"Read our Cookie Policy\"\nbutton \"Customize\"\nbutton \"Reject non-essential\"\nbutton \"Accept all\"",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

@@ -1,86 +0,0 @@
{
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 storage_set descrybe-cookie-consent — ok (descrybe-cookie-consent)",
"step 3 navigate http://localhost:28472/login — ok (http://localhost:28472/login)",
"step 4 wait_hydrate — ok",
"step 5 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 6 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 7 click button[type=submit] — ok",
"step 8 wait_nav — ok (http://localhost:28472/login)",
"step 9 wait_hydrate — ok",
"step 10 wait 1500ms — ok"
],
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
},
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "mobile",
"doc_status": 200,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-logged-in-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-logged-in-mobile.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 storage_set descrybe-cookie-consent — ok (descrybe-cookie-consent)",
"step 3 navigate http://localhost:28472/login — ok (http://localhost:28472/login)",
"step 4 wait_hydrate — ok",
"step 5 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 6 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 7 click button[type=submit] — ok",
"step 8 wait_nav — ok (http://localhost:28472/login)",
"step 9 wait_hydrate — ok",
"step 10 wait 1500ms — ok"
],
"generated_at": "2026-08-09T08:11:21Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-09T08:11:21Z",
"load_ms": 1752,
"outline": null,
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-logged-in-mobile.png",
"snapshot": "",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

@@ -1,86 +0,0 @@
{
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 storage_set descrybe-cookie-consent — ok (descrybe-cookie-consent)",
"step 3 navigate http://localhost:28472/login — ok (http://localhost:28472/login)",
"step 4 wait_hydrate — ok",
"step 5 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 6 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 7 click button[type=submit] — ok",
"step 8 wait_nav — ok (http://localhost:28472/login)",
"step 9 wait_hydrate — ok",
"step 10 wait 1500ms — ok"
],
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
},
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "tablet",
"doc_status": 200,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "tablet",
"viewport": "768x1024@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-logged-in-tablet.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-logged-in-tablet.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 storage_set descrybe-cookie-consent — ok (descrybe-cookie-consent)",
"step 3 navigate http://localhost:28472/login — ok (http://localhost:28472/login)",
"step 4 wait_hydrate — ok",
"step 5 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 6 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 7 click button[type=submit] — ok",
"step 8 wait_nav — ok (http://localhost:28472/login)",
"step 9 wait_hydrate — ok",
"step 10 wait 1500ms — ok"
],
"generated_at": "2026-08-09T08:15:39Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-09T08:15:39Z",
"load_ms": 6075,
"outline": null,
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-logged-in-tablet.png",
"snapshot": "",
"title": "Descrybe",
"trace": null,
"viewport": "768x1024@2x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

@@ -1,86 +0,0 @@
{
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 storage_set descrybe-cookie-consent — ok (descrybe-cookie-consent)",
"step 3 navigate http://localhost:28472/login — ok (http://localhost:28472/login)",
"step 4 wait_hydrate — ok",
"step 5 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 6 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 7 click button[type=submit] — ok",
"step 8 wait_nav — ok (http://localhost:28472/login)",
"step 9 wait_hydrate — ok",
"step 10 wait 1200ms — ok"
],
"actions_failed": false,
"console": [
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
},
{
"level": "debug",
"text": "[vite] connecting..."
},
{
"level": "debug",
"text": "[vite] connected."
}
],
"console_errors": [],
"debug_pack_dir": "",
"debug_pack_json": "",
"device": "mobile",
"doc_status": 200,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"failure_pack": {
"failed": false,
"final_url": "http://localhost:28472/dashboard",
"title": "Descrybe",
"doc_status": 200,
"device": "mobile",
"viewport": "390x844@2x",
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-mobile.png",
"report_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-mobile.report.json",
"console_errors": [],
"page_errors": null,
"failed_requests": [
{
"url": "http://localhost:28471/api/auth/me",
"status": 401
}
],
"action_log": [
"step 1 wait_hydrate — ok",
"step 2 storage_set descrybe-cookie-consent — ok (descrybe-cookie-consent)",
"step 3 navigate http://localhost:28472/login — ok (http://localhost:28472/login)",
"step 4 wait_hydrate — ok",
"step 5 fill \"#email,input[type=email]\"=\"demo@descrybe.local\" — ok",
"step 6 fill \"#password,input[type=password]\"=\"DemoPass123!\" — ok",
"step 7 click button[type=submit] — ok",
"step 8 wait_nav — ok (http://localhost:28472/login)",
"step 9 wait_hydrate — ok",
"step 10 wait 1200ms — ok"
],
"generated_at": "2026-08-09T08:13:06Z"
},
"final_url": "http://localhost:28472/dashboard",
"format": "png",
"generated_at": "2026-08-09T08:13:06Z",
"load_ms": 1486,
"outline": null,
"page_errors": null,
"screenshot_path": "F:\\laragon\\www\\_MY\\descrybe-v2\\apps\\web\\static\\.audit\\r00-mobile.png",
"snapshot": "",
"title": "Descrybe",
"trace": null,
"viewport": "390x844@2x"
}

Some files were not shown because too many files have changed in this diff Show More