Permite definir variables en templates con sintaxis {{nombre🏷️default}}
- Auto-detección de variables al guardar templates
- Drawer para completar valores al cargar template con variables
- Badge mostrando cantidad de variables en tarjeta de template
- Resolución de variables antes de cargar en cola
181 lines
4.7 KiB
TypeScript
181 lines
4.7 KiB
TypeScript
// Gestión de templates persistentes
|
|
import { readFile, writeFile, mkdir } from 'fs/promises'
|
|
import { existsSync } from 'fs'
|
|
import { join } from 'path'
|
|
|
|
export interface Operation {
|
|
op: string
|
|
[key: string]: any
|
|
}
|
|
|
|
export interface TemplateVariable {
|
|
name: string
|
|
label?: string
|
|
defaultValue?: string
|
|
}
|
|
|
|
export interface PrintTemplate {
|
|
id: string
|
|
name: string
|
|
description?: string
|
|
operations: Operation[]
|
|
variables: TemplateVariable[]
|
|
createdAt: string
|
|
updatedAt: string
|
|
}
|
|
|
|
// Regex para detectar {{nombre}}, {{nombre:label}}, {{nombre:label:default}}
|
|
const VARIABLE_REGEX = /\{\{(\w+)(?::([^:}]+))?(?::([^}]+))?\}\}/g
|
|
|
|
// Extraer variables de las operaciones
|
|
export function extractVariables(operations: Operation[]): TemplateVariable[] {
|
|
const variablesMap = new Map<string, TemplateVariable>()
|
|
|
|
for (const op of operations) {
|
|
// Buscar en campos que pueden contener variables
|
|
const searchFields = ['value', 'data']
|
|
for (const field of searchFields) {
|
|
if (typeof op[field] === 'string') {
|
|
const matches = op[field].matchAll(VARIABLE_REGEX)
|
|
for (const match of matches) {
|
|
const [, name, label, defaultValue] = match
|
|
// Solo guardar la primera definición de cada variable
|
|
if (!variablesMap.has(name)) {
|
|
variablesMap.set(name, {
|
|
name,
|
|
label: label || undefined,
|
|
defaultValue: defaultValue || undefined
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Array.from(variablesMap.values())
|
|
}
|
|
|
|
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,
|
|
variables: extractVariables(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
|
|
|
|
// Si se actualizan las operaciones, recalcular las variables
|
|
const variables = data.operations
|
|
? extractVariables(data.operations)
|
|
: store.templates[index].variables
|
|
|
|
store.templates[index] = {
|
|
...store.templates[index],
|
|
...data,
|
|
variables,
|
|
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))
|
|
})
|
|
}
|