feat: Templates persistentes en servidor + Constructor con tabs por tipo de comando

- Templates ahora se guardan en servidor (data/templates.json) disponibles para todos
- API CRUD para templates: GET/POST /api/templates, GET/PUT/DELETE /api/templates/[id]
- Constructor de comandos rediseñado con tabs: Texto, Feed, Cortar, Pulse, QR, Barcode
- Cada tipo de comando tiene su formulario específico con campos relevantes
- Eliminado QuickActions (integrado en tabs del constructor)
- Mejorada UI de lista de impresoras con renderizado condicional
- Agregado data/ a .gitignore (datos de runtime)
This commit is contained in:
2025-11-25 01:18:36 -06:00
parent 09f5b81067
commit 155995c773
15 changed files with 825 additions and 293 deletions

136
server/utils/templates.ts Normal file
View File

@@ -0,0 +1,136 @@
// Gestión de templates persistentes
import { readFile, writeFile, mkdir } from 'fs/promises'
import { existsSync } from 'fs'
import { join } from 'path'
export interface Operation {
id: string
type: 'text' | 'feed' | 'cut' | 'pulse' | 'image' | 'barcode' | 'qrcode'
params: Record<string, unknown>
}
export interface PrintTemplate {
id: string
name: string
description?: string
operations: Operation[]
createdAt: string
updatedAt: string
}
export interface TemplatesStore {
templates: PrintTemplate[]
}
// Directorio de datos persistentes
const DATA_DIR = join(process.cwd(), 'data')
const TEMPLATES_FILE = join(DATA_DIR, 'templates.json')
// Asegurar que el directorio existe
async function ensureDataDir(): Promise<void> {
if (!existsSync(DATA_DIR)) {
await mkdir(DATA_DIR, { recursive: true })
}
}
// Leer store de templates
export async function readTemplatesStore(): Promise<TemplatesStore> {
await ensureDataDir()
try {
const data = await readFile(TEMPLATES_FILE, 'utf-8')
return JSON.parse(data)
} catch {
// Si no existe el archivo, retornar store vacío
return {
templates: []
}
}
}
// Guardar store de templates
export async function writeTemplatesStore(store: TemplatesStore): Promise<void> {
await ensureDataDir()
await writeFile(TEMPLATES_FILE, JSON.stringify(store, null, 2), 'utf-8')
}
// Generar ID único
function generateId(): string {
return `template_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
}
// CRUD Operations
export async function getAllTemplates(): Promise<PrintTemplate[]> {
const store = await readTemplatesStore()
return store.templates
}
export async function getTemplateById(id: string): Promise<PrintTemplate | null> {
const store = await readTemplatesStore()
return store.templates.find(t => t.id === id) || null
}
export async function createTemplate(data: {
name: string
description?: string
operations: Operation[]
}): Promise<PrintTemplate> {
const store = await readTemplatesStore()
const now = new Date().toISOString()
const template: PrintTemplate = {
id: generateId(),
name: data.name,
description: data.description || '',
operations: data.operations,
createdAt: now,
updatedAt: now
}
store.templates.push(template)
await writeTemplatesStore(store)
return template
}
export async function updateTemplate(id: string, data: Partial<{
name: string
description: string
operations: Operation[]
}>): Promise<PrintTemplate | null> {
const store = await readTemplatesStore()
const index = store.templates.findIndex(t => t.id === id)
if (index === -1) return null
store.templates[index] = {
...store.templates[index],
...data,
updatedAt: new Date().toISOString()
}
await writeTemplatesStore(store)
return store.templates[index]
}
export async function deleteTemplate(id: string): Promise<boolean> {
const store = await readTemplatesStore()
const index = store.templates.findIndex(t => t.id === id)
if (index === -1) return false
store.templates.splice(index, 1)
await writeTemplatesStore(store)
return true
}
export async function duplicateTemplate(id: string): Promise<PrintTemplate | null> {
const template = await getTemplateById(id)
if (!template) return null
return createTemplate({
name: `${template.name} (copia)`,
description: template.description,
operations: JSON.parse(JSON.stringify(template.operations))
})
}