feat(i18n): traducir Biblioteca de Conocimiento (help-center)
- Página principal: título, subtítulo, búsqueda, tarjetas, diálogo de eliminar, toasts - Lector de capítulo: navegación, PDF/video/documento, descargas, TOC - Editor: formulario completo (labels, placeholders), toolbar markdown, subida de archivos, toasts - ~70 claves common nuevas (help_*) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2008,6 +2008,87 @@
|
||||
"maint_desc": "MODULE NOT YET AVAILABLE!",
|
||||
"maint_back": "Back to Home",
|
||||
"sub_manifest": "Export manifests management",
|
||||
"title_invoice_report": "Invoice Report"
|
||||
"title_invoice_report": "Invoice Report",
|
||||
"title_help_library": "Knowledge Library",
|
||||
"sub_help": "System Manuals, Guides and Documentation.",
|
||||
"new_chapter": "New Chapter",
|
||||
"help_search_ph": "Search the library...",
|
||||
"help_empty": "The library is empty.",
|
||||
"help_format": "Format",
|
||||
"help_unknown": "Unknown",
|
||||
"help_size": "Size",
|
||||
"help_watch": "Watch",
|
||||
"help_read": "Read",
|
||||
"help_open": "Open",
|
||||
"help_delete_title": "Delete chapter?",
|
||||
"help_delete_desc": "\"{title}\" will be permanently deleted.",
|
||||
"help_toast_load_error": "Error loading articles",
|
||||
"help_toast_deleted": "Chapter deleted",
|
||||
"help_toast_error": "Error",
|
||||
"help_back_library": "Back to Library",
|
||||
"help_chapter_load_error": "Error loading chapter",
|
||||
"help_pdf_document": "PDF Document",
|
||||
"help_open_tab": "Open in tab",
|
||||
"help_download": "Download",
|
||||
"help_video_unsupported": "Your browser does not support videos.",
|
||||
"help_file_info": "File information",
|
||||
"help_file_download": "File to download",
|
||||
"help_no_preview": "This file has no direct preview.",
|
||||
"help_download_now": "Download now",
|
||||
"help_in_chapter": "In this chapter",
|
||||
"btn_cancel": "Cancel",
|
||||
"btn_delete": "Delete",
|
||||
"btn_edit": "Edit",
|
||||
"btn_save": "Save",
|
||||
"help_toast_article_load_error": "Error loading article",
|
||||
"help_toast_chapter_created": "Chapter created",
|
||||
"help_toast_changes_saved": "Changes saved",
|
||||
"help_toast_save_error": "Error saving",
|
||||
"help_toast_uploading_image": "Uploading image...",
|
||||
"help_toast_image_inserted": "Image inserted",
|
||||
"help_toast_upload_error": "Error uploading",
|
||||
"help_toast_uploading_file": "Uploading {name}...",
|
||||
"help_untitled": "Untitled",
|
||||
"help_toast_file_uploaded": "File uploaded successfully",
|
||||
"help_toast_file_upload_error": "Error uploading file",
|
||||
"help_back": "Back",
|
||||
"help_hide_preview": "Hide Preview",
|
||||
"help_show_preview": "Show Preview",
|
||||
"help_config": "Settings",
|
||||
"help_field_title": "Title",
|
||||
"help_ph_title": "E.g.: Introduction",
|
||||
"help_field_slug": "Identifier (Slug)",
|
||||
"help_ph_slug": "eg-article-title",
|
||||
"help_slug_hint": "Auto-generated from the title if left empty.",
|
||||
"help_field_type": "Content Type",
|
||||
"help_type_article": "Article (Markdown)",
|
||||
"help_type_document": "Other Document",
|
||||
"help_advanced": "Advanced Options",
|
||||
"help_field_category": "Category",
|
||||
"help_ph_category": "E.g.: General",
|
||||
"help_field_order": "Order",
|
||||
"help_field_context": "Context Path",
|
||||
"help_ph_context": "E.g.: /dashboard/...",
|
||||
"help_context_hint": "URL where this article will appear.",
|
||||
"help_field_tags": "Tags",
|
||||
"help_ph_tags": "E.g.: invoices, scrap",
|
||||
"help_bold": "Bold",
|
||||
"help_italic": "Italic",
|
||||
"help_h1": "Heading 1",
|
||||
"help_h2": "Heading 2",
|
||||
"help_list": "List",
|
||||
"help_link": "Link",
|
||||
"help_upload_image": "Upload Image",
|
||||
"help_ph_content": "# Start writing here...",
|
||||
"help_drag_images": "Drag images here",
|
||||
"help_config_pdf": "PDF Settings",
|
||||
"help_config_video": "Video Settings",
|
||||
"help_config_document": "Document Settings",
|
||||
"help_upload_hint": "Upload the file you want to associate with this title.",
|
||||
"help_file_loaded": "File Loaded",
|
||||
"help_view_file": "View File",
|
||||
"help_change_file": "Change File",
|
||||
"help_select_file": "Select a file",
|
||||
"help_drag_drop": "Or drag and drop here"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2008,6 +2008,87 @@
|
||||
"maint_desc": "¡MÓDULO AÚN NO DISPONIBLE!",
|
||||
"maint_back": "Volver al Inicio",
|
||||
"sub_manifest": "Gestión de manifiestos de exportación",
|
||||
"title_invoice_report": "Reporte de Facturas"
|
||||
"title_invoice_report": "Reporte de Facturas",
|
||||
"title_help_library": "Biblioteca de Conocimiento",
|
||||
"sub_help": "Manuales, Guías y Documentación del Sistema.",
|
||||
"new_chapter": "Nuevo Capítulo",
|
||||
"help_search_ph": "Buscar en la biblioteca...",
|
||||
"help_empty": "La biblioteca está vacía.",
|
||||
"help_format": "Formato",
|
||||
"help_unknown": "Desconocido",
|
||||
"help_size": "Tamaño",
|
||||
"help_watch": "Ver",
|
||||
"help_read": "Leer",
|
||||
"help_open": "Abrir",
|
||||
"help_delete_title": "¿Eliminar capítulo?",
|
||||
"help_delete_desc": "Se eliminará permanentemente \"{title}\".",
|
||||
"help_toast_load_error": "Error al cargar artículos",
|
||||
"help_toast_deleted": "Capítulo eliminado",
|
||||
"help_toast_error": "Error",
|
||||
"help_back_library": "Volver a la Biblioteca",
|
||||
"help_chapter_load_error": "Error al cargar el capítulo",
|
||||
"help_pdf_document": "Documento PDF",
|
||||
"help_open_tab": "Abrir en pestaña",
|
||||
"help_download": "Descargar",
|
||||
"help_video_unsupported": "Tu navegador no soporta videos.",
|
||||
"help_file_info": "Información del archivo",
|
||||
"help_file_download": "Archivo para descargar",
|
||||
"help_no_preview": "Este archivo no tiene vista previa directa.",
|
||||
"help_download_now": "Descargar ahora",
|
||||
"help_in_chapter": "En este capítulo",
|
||||
"btn_cancel": "Cancelar",
|
||||
"btn_delete": "Eliminar",
|
||||
"btn_edit": "Editar",
|
||||
"btn_save": "Guardar",
|
||||
"help_toast_article_load_error": "Error al cargar artículo",
|
||||
"help_toast_chapter_created": "Capítulo creado",
|
||||
"help_toast_changes_saved": "Cambios guardados",
|
||||
"help_toast_save_error": "Error al guardar",
|
||||
"help_toast_uploading_image": "Subiendo imagen...",
|
||||
"help_toast_image_inserted": "Imagen insertada",
|
||||
"help_toast_upload_error": "Error al subir",
|
||||
"help_toast_uploading_file": "Subiendo {name}...",
|
||||
"help_untitled": "Sin Título",
|
||||
"help_toast_file_uploaded": "Archivo subido correctamente",
|
||||
"help_toast_file_upload_error": "Error al subir archivo",
|
||||
"help_back": "Volver",
|
||||
"help_hide_preview": "Ocultar Vista Previa",
|
||||
"help_show_preview": "Ver Vista Previa",
|
||||
"help_config": "Configuración",
|
||||
"help_field_title": "Título",
|
||||
"help_ph_title": "Ej: Introducción",
|
||||
"help_field_slug": "Identificador (Slug)",
|
||||
"help_ph_slug": "ej-titulo-articulo",
|
||||
"help_slug_hint": "Se autogenera del título si se deja vacío.",
|
||||
"help_field_type": "Tipo de Contenido",
|
||||
"help_type_article": "Artículo (Markdown)",
|
||||
"help_type_document": "Otro Documento",
|
||||
"help_advanced": "Opciones Avanzadas",
|
||||
"help_field_category": "Categoría",
|
||||
"help_ph_category": "Ej: General",
|
||||
"help_field_order": "Orden",
|
||||
"help_field_context": "Ruta Contextual",
|
||||
"help_ph_context": "Ej: /dashboard/...",
|
||||
"help_context_hint": "URL donde aparecerá este artículo.",
|
||||
"help_field_tags": "Etiquetas",
|
||||
"help_ph_tags": "Ej: facturas, mermas",
|
||||
"help_bold": "Negrita",
|
||||
"help_italic": "Cursiva",
|
||||
"help_h1": "Título 1",
|
||||
"help_h2": "Título 2",
|
||||
"help_list": "Lista",
|
||||
"help_link": "Enlace",
|
||||
"help_upload_image": "Subir Imagen",
|
||||
"help_ph_content": "# Empieza a escribir aquí...",
|
||||
"help_drag_images": "Arrastra imágenes aquí",
|
||||
"help_config_pdf": "Configuración de PDF",
|
||||
"help_config_video": "Configuración de Video",
|
||||
"help_config_document": "Configuración de Documento",
|
||||
"help_upload_hint": "Sube el archivo que deseas asociar a este título.",
|
||||
"help_file_loaded": "Archivo Cargado",
|
||||
"help_view_file": "Ver Archivo",
|
||||
"help_change_file": "Cambiar Archivo",
|
||||
"help_select_file": "Selecciona un archivo",
|
||||
"help_drag_drop": "O arrastra y suelta aquí"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
FileCode
|
||||
} from 'lucide-svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { m } from '$lib/i18n/messages';
|
||||
|
||||
let articles: HelpArticle[] = $state([]);
|
||||
let loading = $state(true);
|
||||
@@ -92,7 +93,7 @@
|
||||
try {
|
||||
articles = await helpApi.listArticles();
|
||||
} catch (e: any) {
|
||||
toast.error('Error al cargar artículos: ' + e.message);
|
||||
toast.error(m['common.help_toast_load_error']() + ': ' + e.message);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
@@ -111,11 +112,11 @@
|
||||
processing = true;
|
||||
try {
|
||||
await helpApi.deleteArticle(selectedArticle.uuid);
|
||||
toast.success('Capítulo eliminado');
|
||||
toast.success(m['common.help_toast_deleted']());
|
||||
showDeleteDialog = false;
|
||||
await loadArticles();
|
||||
} catch (e: any) {
|
||||
toast.error('Error: ' + e.message);
|
||||
toast.error(m['common.help_toast_error']() + ': ' + e.message);
|
||||
} finally {
|
||||
processing = false;
|
||||
}
|
||||
@@ -138,15 +139,15 @@
|
||||
<div>
|
||||
<h1 class="flex items-center gap-2 text-3xl font-bold tracking-tight text-primary">
|
||||
<Book class="h-8 w-8" />
|
||||
Biblioteca de Conocimiento
|
||||
{m['common.title_help_library']()}
|
||||
</h1>
|
||||
<p class="text-muted-foreground">Manuales, Guías y Documentación del Sistema.</p>
|
||||
<p class="text-muted-foreground">{m['common.sub_help']()}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
{#if canCreate}
|
||||
<Button href="/dashboard/help-center/editor/new" class="shadow-lg">
|
||||
<Plus class="mr-2 h-4 w-4" />
|
||||
Nuevo Capítulo
|
||||
{m['common.new_chapter']()}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -157,7 +158,7 @@
|
||||
<Search class="absolute top-3 left-3 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Buscar en la biblioteca..."
|
||||
placeholder={m['common.help_search_ph']()}
|
||||
class="h-10 border-muted-foreground/20 bg-background pl-10"
|
||||
bind:value={searchTerm}
|
||||
/>
|
||||
@@ -172,7 +173,7 @@
|
||||
{:else if Object.keys(groupedArticles).length === 0}
|
||||
<div class="flex flex-col items-center justify-center py-20 text-muted-foreground">
|
||||
<Book class="mb-4 h-16 w-16 opacity-20" />
|
||||
<p>La biblioteca está vacía.</p>
|
||||
<p>{m['common.help_empty']()}</p>
|
||||
</div>
|
||||
{:else}
|
||||
{#each Object.entries(groupedArticles) as [category, groupArticles]}
|
||||
@@ -219,9 +220,9 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col gap-1 text-xs text-muted-foreground">
|
||||
<p>Formato: {article.mime_type || 'Desconocido'}</p>
|
||||
<p>{m['common.help_format']()}: {article.mime_type || m['common.help_unknown']()}</p>
|
||||
{#if article.file_size}
|
||||
<p>Tamaño: {(article.file_size / 1024 / 1024).toFixed(2)} MB</p>
|
||||
<p>{m['common.help_size']()}: {(article.file_size / 1024 / 1024).toFixed(2)} MB</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -229,11 +230,11 @@
|
||||
<Card.Footer class="flex items-center justify-between border-t bg-muted/5 pt-4">
|
||||
<Button variant="ghost" size="sm" href={`/dashboard/help-center/${article.uuid}`}>
|
||||
{#if article.content_type === 'video'}
|
||||
Ver
|
||||
{m['common.help_watch']()}
|
||||
{:else if article.content_type === 'article'}
|
||||
Leer
|
||||
{m['common.help_read']()}
|
||||
{:else}
|
||||
Abrir
|
||||
{m['common.help_open']()}
|
||||
{/if}
|
||||
</Button>
|
||||
<div class="flex gap-1 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
@@ -272,19 +273,19 @@
|
||||
<AlertDialog.Root bind:open={showDeleteDialog}>
|
||||
<AlertDialog.Content>
|
||||
<AlertDialog.Header>
|
||||
<AlertDialog.Title>¿Eliminar capítulo?</AlertDialog.Title>
|
||||
<AlertDialog.Title>{m['common.help_delete_title']()}</AlertDialog.Title>
|
||||
<AlertDialog.Description>
|
||||
Se eliminará permanentemente "{selectedArticle?.title}".
|
||||
{m['common.help_delete_desc']({ title: selectedArticle?.title ?? '' })}
|
||||
</AlertDialog.Description>
|
||||
</AlertDialog.Header>
|
||||
<AlertDialog.Footer>
|
||||
<AlertDialog.Cancel onclick={() => (showDeleteDialog = false)}>Cancelar</AlertDialog.Cancel>
|
||||
<AlertDialog.Cancel onclick={() => (showDeleteDialog = false)}>{m['common.btn_cancel']()}</AlertDialog.Cancel>
|
||||
<AlertDialog.Action
|
||||
class="text-destructive-foreground bg-destructive hover:bg-destructive/90"
|
||||
onclick={handleDelete}
|
||||
>
|
||||
{#if processing}<Loader2 class="mr-2 h-4 w-4 animate-spin" />{/if}
|
||||
Eliminar
|
||||
{m['common.btn_delete']()}
|
||||
</AlertDialog.Action>
|
||||
</AlertDialog.Footer>
|
||||
</AlertDialog.Content>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from '$lib/i18n/messages';
|
||||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { helpApi, type HelpArticle } from '$lib/api/help';
|
||||
@@ -122,7 +123,7 @@
|
||||
>
|
||||
<Button variant="ghost" size="sm" href="/dashboard/help-center" class="gap-2">
|
||||
<ArrowLeft class="h-4 w-4" />
|
||||
Volver a la Biblioteca
|
||||
{m['common.help_back_library']()}
|
||||
</Button>
|
||||
<div class="mx-2 h-4 w-px bg-border"></div>
|
||||
{#if article}
|
||||
@@ -140,7 +141,7 @@
|
||||
</div>
|
||||
{:else if error}
|
||||
<div class="flex h-full flex-col items-center justify-center gap-2 text-destructive">
|
||||
<p class="text-lg font-medium">Error al cargar el capítulo</p>
|
||||
<p class="text-lg font-medium">{m['common.help_chapter_load_error']()}</p>
|
||||
<p class="text-sm text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
{:else if article}
|
||||
@@ -180,7 +181,7 @@
|
||||
<div class="flex items-center gap-3">
|
||||
<FileText class="h-8 w-8 text-red-500" />
|
||||
<div>
|
||||
<p class="font-medium">Documento PDF</p>
|
||||
<p class="font-medium">{m['common.help_pdf_document']()}</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{article.file_size ? (article.file_size / 1024 / 1024).toFixed(2) : '??'} MB
|
||||
</p>
|
||||
@@ -194,7 +195,7 @@
|
||||
target="_blank"
|
||||
>
|
||||
<ExternalLink class="mr-2 h-4 w-4" />
|
||||
Abrir en pestaña
|
||||
{m['common.help_open_tab']()}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -202,7 +203,7 @@
|
||||
download={article.title}
|
||||
>
|
||||
<Download class="mr-2 h-4 w-4" />
|
||||
Descargar
|
||||
{m['common.help_download']()}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,10 +218,10 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<video controls class="w-full rounded-xl border shadow-lg">
|
||||
<source src={resolveAssetUrl(article.file_url)} type={article.mime_type} />
|
||||
Tu navegador no soporta videos.
|
||||
{m['common.help_video_unsupported']()}
|
||||
</video>
|
||||
<div class="rounded-lg border bg-muted/30 p-4">
|
||||
<p class="text-sm font-medium">Información del archivo</p>
|
||||
<p class="text-sm font-medium">{m['common.help_file_info']()}</p>
|
||||
<p class="text-xs text-muted-foreground">{article.mime_type}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -232,9 +233,9 @@
|
||||
<Download class="h-12 w-12 text-primary" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h3 class="text-xl font-semibold">Archivo para descargar</h3>
|
||||
<h3 class="text-xl font-semibold">{m['common.help_file_download']()}</h3>
|
||||
<p class="text-muted-foreground truncate max-w-md">
|
||||
Este archivo no tiene vista previa directa.
|
||||
{m['common.help_no_preview']()}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
@@ -243,7 +244,7 @@
|
||||
class="px-8 shadow-lg"
|
||||
>
|
||||
<Download class="mr-2 h-4 w-4" />
|
||||
Descargar ahora
|
||||
{m['common.help_download_now']()}
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -254,7 +255,7 @@
|
||||
{#if toc.length > 0}
|
||||
<div class="hidden w-72 overflow-y-auto border-l bg-muted/5 p-6 xl:block">
|
||||
<h4 class="mb-4 text-sm font-semibold tracking-wider text-muted-foreground uppercase">
|
||||
En este capitulo
|
||||
{m['common.help_in_chapter']()}
|
||||
</h4>
|
||||
<nav class="space-y-1">
|
||||
{#each toc as item}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { m } from '$lib/i18n/messages';
|
||||
import { page } from '$app/stores';
|
||||
import { browser } from '$app/environment';
|
||||
import { onMount, tick } from 'svelte';
|
||||
@@ -99,7 +100,7 @@
|
||||
tags = article.tags || '';
|
||||
}
|
||||
} catch (e: any) {
|
||||
toast.error('Error al cargar artículo: ' + e.message);
|
||||
toast.error(m['common.help_toast_article_load_error']() + ': ' + e.message);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
@@ -135,17 +136,17 @@
|
||||
const uuid = $page.params.uuid as string;
|
||||
if (uuid === 'new') {
|
||||
const newArticle = await helpApi.createArticle(data);
|
||||
toast.success('Capítulo creado');
|
||||
toast.success(m['common.help_toast_chapter_created']());
|
||||
// Redirect to edit mode or list? For now, stay here but update URL would be ideal.
|
||||
// simpler to just go back to list or reload.
|
||||
// Let's redirect to edit mode of this new UUID to avoid duplicates on re-save
|
||||
window.location.href = `/dashboard/help-center/editor/${newArticle.uuid}`;
|
||||
} else {
|
||||
await helpApi.updateArticle(uuid, data);
|
||||
toast.success('Cambios guardados');
|
||||
toast.success(m['common.help_toast_changes_saved']());
|
||||
}
|
||||
} catch (e: any) {
|
||||
toast.error('Error al guardar: ' + e.message);
|
||||
toast.error(m['common.help_toast_save_error']() + ': ' + e.message);
|
||||
} finally {
|
||||
processing = false;
|
||||
}
|
||||
@@ -173,22 +174,22 @@
|
||||
}
|
||||
|
||||
async function handleImageUpload(file: File) {
|
||||
const tid = toast.loading('Subiendo imagen...');
|
||||
const tid = toast.loading(m['common.help_toast_uploading_image']());
|
||||
processing = true;
|
||||
try {
|
||||
const result = await helpApi.uploadImage(file);
|
||||
const imageMarkdown = `\n\n`;
|
||||
insertText(imageMarkdown);
|
||||
toast.success('Imagen insertada', { id: tid });
|
||||
toast.success(m['common.help_toast_image_inserted'](), { id: tid });
|
||||
} catch (e: any) {
|
||||
toast.error('Error al subir: ' + e.message, { id: tid });
|
||||
toast.error(m['common.help_toast_upload_error']() + ': ' + e.message, { id: tid });
|
||||
} finally {
|
||||
processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAssetUpload(file: File) {
|
||||
const tid = toast.loading(`Subiendo ${file.name}...`);
|
||||
const tid = toast.loading(m['common.help_toast_uploading_file']({ name: file.name }));
|
||||
processing = true;
|
||||
try {
|
||||
const result = await helpApi.uploadAsset(file);
|
||||
@@ -198,7 +199,7 @@
|
||||
|
||||
// Auto-set title if empty
|
||||
if (!title) {
|
||||
title = file.name.split('.').shift() || 'Sin Título';
|
||||
title = file.name.split('.').shift() || m['common.help_untitled']();
|
||||
}
|
||||
if (!slug) {
|
||||
slug = title.toLowerCase().replace(/[^\w]+/g, '-');
|
||||
@@ -211,9 +212,9 @@
|
||||
else content_type = 'document';
|
||||
}
|
||||
|
||||
toast.success('Archivo subido correctamente', { id: tid });
|
||||
toast.success(m['common.help_toast_file_uploaded'](), { id: tid });
|
||||
} catch (e: any) {
|
||||
toast.error('Error al subir archivo: ' + e.message, { id: tid });
|
||||
toast.error(m['common.help_toast_file_upload_error']() + ': ' + e.message, { id: tid });
|
||||
} finally {
|
||||
processing = false;
|
||||
}
|
||||
@@ -260,20 +261,20 @@
|
||||
<div class="flex items-center gap-4">
|
||||
<Button variant="ghost" size="sm" href="/dashboard/help-center">
|
||||
<ArrowLeft class="mr-2 h-4 w-4" />
|
||||
Volver
|
||||
{m['common.help_back']()}
|
||||
</Button>
|
||||
<h1 class="max-w-md truncate text-lg font-semibold">
|
||||
{title || 'Sin Título'}
|
||||
{title || m['common.help_untitled']()}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button variant="outline" size="sm" onclick={() => (showPreview = !showPreview)}>
|
||||
{showPreview ? 'Ocultar Vista Previa' : 'Ver Vista Previa'}
|
||||
{showPreview ? m['common.help_hide_preview']() : m['common.help_show_preview']()}
|
||||
</Button>
|
||||
<Button onclick={handleSave} disabled={processing || !title}>
|
||||
{#if processing}<Loader2 class="mr-2 h-4 w-4 animate-spin" />{/if}
|
||||
<Save class="mr-2 h-4 w-4" />
|
||||
Guardar
|
||||
{m['common.btn_save']()}
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
@@ -283,56 +284,56 @@
|
||||
<!-- Metadata Sidebar (Left) - Collapsible or Fixed width -->
|
||||
<aside class="hidden w-64 overflow-y-auto border-r bg-muted/10 p-4 lg:block">
|
||||
<h3 class="mb-4 text-sm font-semibold tracking-wider text-muted-foreground uppercase">
|
||||
Configuración
|
||||
{m['common.help_config']()}
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="title">Título</Label>
|
||||
<Input id="title" bind:value={title} placeholder="Ej: Introducción" />
|
||||
<Label for="title">{m['common.help_field_title']()}</Label>
|
||||
<Input id="title" bind:value={title} placeholder={m['common.help_ph_title']()} />
|
||||
</div>
|
||||
<div class="grid gap-2 border-t pt-4">
|
||||
<Label for="slug">Identificador (Slug)</Label>
|
||||
<Input id="slug" bind:value={slug} placeholder="ej-titulo-articulo" />
|
||||
<p class="text-[10px] text-muted-foreground">Se autogenera del título si se deja vacío.</p>
|
||||
<Label for="slug">{m['common.help_field_slug']()}</Label>
|
||||
<Input id="slug" bind:value={slug} placeholder={m['common.help_ph_slug']()} />
|
||||
<p class="text-[10px] text-muted-foreground">{m['common.help_slug_hint']()}</p>
|
||||
</div>
|
||||
<div class="grid gap-2 border-t pt-4">
|
||||
<Label for="type">Tipo de Contenido</Label>
|
||||
<Label for="type">{m['common.help_field_type']()}</Label>
|
||||
<select
|
||||
id="type"
|
||||
bind:value={content_type}
|
||||
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<option value="article">Artículo (Markdown)</option>
|
||||
<option value="article">{m['common.help_type_article']()}</option>
|
||||
<option value="pdf">PDF</option>
|
||||
<option value="video">Video</option>
|
||||
<option value="document">Otro Documento</option>
|
||||
<option value="document">{m['common.help_type_document']()}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<details class="group border-t pt-4">
|
||||
<summary class="flex cursor-pointer items-center justify-between text-xs font-semibold tracking-wider text-muted-foreground uppercase hover:text-primary transition-colors">
|
||||
Opciones Avanzadas
|
||||
{m['common.help_advanced']()}
|
||||
<HelpCircle size={14} class="opacity-50 group-open:rotate-180 transition-transform" />
|
||||
</summary>
|
||||
<div class="space-y-4 pt-4">
|
||||
<div class="grid gap-2">
|
||||
<Label for="category">Categoría</Label>
|
||||
<Input id="category" bind:value={category} placeholder="Ej: General" />
|
||||
<Label for="category">{m['common.help_field_category']()}</Label>
|
||||
<Input id="category" bind:value={category} placeholder={m['common.help_ph_category']()} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="order">Orden</Label>
|
||||
<Label for="order">{m['common.help_field_order']()}</Label>
|
||||
<Input id="order" type="number" bind:value={order} />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="context_path" class="flex items-center gap-1">
|
||||
Ruta Contextual
|
||||
{m['common.help_field_context']()}
|
||||
</Label>
|
||||
<Input id="context_path" bind:value={context_path} placeholder="Ej: /dashboard/..." />
|
||||
<p class="text-[10px] text-muted-foreground">URL donde aparecerá este artículo.</p>
|
||||
<Input id="context_path" bind:value={context_path} placeholder={m['common.help_ph_context']()} />
|
||||
<p class="text-[10px] text-muted-foreground">{m['common.help_context_hint']()}</p>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="tags">Etiquetas</Label>
|
||||
<Input id="tags" bind:value={tags} placeholder="Ej: facturas, mermas" />
|
||||
<Label for="tags">{m['common.help_field_tags']()}</Label>
|
||||
<Input id="tags" bind:value={tags} placeholder={m['common.help_ph_tags']()} />
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
@@ -351,7 +352,7 @@
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
onclick={() => insertText('**', '**')}
|
||||
title="Negrita"
|
||||
title={m['common.help_bold']()}
|
||||
>
|
||||
<Bold class="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -360,7 +361,7 @@
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
onclick={() => insertText('*', '*')}
|
||||
title="Cursiva"
|
||||
title={m['common.help_italic']()}
|
||||
>
|
||||
<Italic class="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -370,7 +371,7 @@
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
onclick={() => insertText('# ')}
|
||||
title="Título 1"
|
||||
title={m['common.help_h1']()}
|
||||
>
|
||||
<Heading1 class="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -379,7 +380,7 @@
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
onclick={() => insertText('## ')}
|
||||
title="Título 2"
|
||||
title={m['common.help_h2']()}
|
||||
>
|
||||
<Heading2 class="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -389,7 +390,7 @@
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
onclick={() => insertText('- ')}
|
||||
title="Lista"
|
||||
title={m['common.help_list']()}
|
||||
>
|
||||
<List class="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -398,13 +399,13 @@
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
onclick={() => insertText('[texto](url)')}
|
||||
title="Enlace"
|
||||
title={m['common.help_link']()}
|
||||
>
|
||||
<LinkIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
<label
|
||||
class="inline-flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50"
|
||||
title="Subir Imagen"
|
||||
title={m['common.help_upload_image']()}
|
||||
>
|
||||
<ImageIcon class="h-4 w-4" />
|
||||
<input
|
||||
@@ -423,7 +424,7 @@
|
||||
id="markdown-editor"
|
||||
bind:value={content}
|
||||
class="w-full flex-1 resize-none bg-background p-8 font-mono text-sm leading-relaxed outline-none"
|
||||
placeholder="# Empieza a escribir aquí..."
|
||||
placeholder={m['common.help_ph_content']()}
|
||||
ondrop={(e: DragEvent) => handleDrop(e)}
|
||||
ondragover={(e: DragEvent) => e.preventDefault()}
|
||||
></textarea>
|
||||
@@ -432,7 +433,7 @@
|
||||
<div
|
||||
class="pointer-events-none absolute inset-0 m-4 hidden items-center justify-center rounded-lg border-2 border-dashed border-primary bg-primary/10 opacity-0 transition-opacity group-hover:flex"
|
||||
>
|
||||
<p class="font-medium text-primary">Arrastra imágenes aquí</p>
|
||||
<p class="font-medium text-primary">{m['common.help_drag_images']()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -453,19 +454,19 @@
|
||||
<div class="rounded-full bg-red-100 p-6 dark:bg-red-900/20">
|
||||
<FileText class="h-16 w-16 text-red-500" />
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold">Configuración de PDF</h2>
|
||||
<h2 class="text-2xl font-bold">{m['common.help_config_pdf']()}</h2>
|
||||
{:else if content_type === 'video'}
|
||||
<div class="rounded-full bg-blue-100 p-6 dark:bg-blue-900/20">
|
||||
<VideoIcon class="h-16 w-16 text-blue-500" />
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold">Configuración de Video</h2>
|
||||
<h2 class="text-2xl font-bold">{m['common.help_config_video']()}</h2>
|
||||
{:else}
|
||||
<div class="rounded-full bg-primary/10 p-6">
|
||||
<FileCode class="h-16 w-16 text-primary" />
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold">Configuración de Documento</h2>
|
||||
<h2 class="text-2xl font-bold">{m['common.help_config_document']()}</h2>
|
||||
{/if}
|
||||
<p class="text-muted-foreground">Sube el archivo que deseas asociar a este título.</p>
|
||||
<p class="text-muted-foreground">{m['common.help_upload_hint']()}</p>
|
||||
</div>
|
||||
|
||||
{#if file_url}
|
||||
@@ -475,7 +476,7 @@
|
||||
<FileCode class="h-6 w-6" />
|
||||
</div>
|
||||
<div class="flex-1 text-left">
|
||||
<p class="font-medium">Archivo Cargado</p>
|
||||
<p class="font-medium">{m['common.help_file_loaded']()}</p>
|
||||
<p class="truncate text-xs text-muted-foreground">
|
||||
{getFileNameFromPath(file_url)}
|
||||
</p>
|
||||
@@ -486,12 +487,12 @@
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<Button variant="outline" class="flex-1" href={resolveAssetUrl(file_url)} target="_blank">
|
||||
Ver Archivo
|
||||
{m['common.help_view_file']()}
|
||||
</Button>
|
||||
<label
|
||||
class="flex flex-1 cursor-pointer items-center justify-center rounded-md border border-input bg-background hover:bg-accent hover:text-accent-foreground"
|
||||
>
|
||||
Cambiar Archivo
|
||||
{m['common.help_change_file']()}
|
||||
<input
|
||||
type="file"
|
||||
class="hidden"
|
||||
@@ -511,8 +512,8 @@
|
||||
<div class="rounded-full bg-background p-4 shadow-sm">
|
||||
<Upload class="h-8 w-8 text-primary" />
|
||||
</div>
|
||||
<span class="font-medium">Selecciona un archivo</span>
|
||||
<span class="text-xs text-muted-foreground">O arrastra y suelta aquí</span>
|
||||
<span class="font-medium">{m['common.help_select_file']()}</span>
|
||||
<span class="text-xs text-muted-foreground">{m['common.help_drag_drop']()}</span>
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
|
||||
Reference in New Issue
Block a user