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:
136
server/utils/templates.ts
Normal file
136
server/utils/templates.ts
Normal 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))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user