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:
@@ -5,92 +5,116 @@ export interface PrintTemplate {
|
||||
name: string
|
||||
description?: string
|
||||
operations: Operation[]
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'printercentral-templates'
|
||||
|
||||
// Función para generar UUID compatible con todos los navegadores
|
||||
function generateId(): string {
|
||||
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
||||
return crypto.randomUUID()
|
||||
}
|
||||
// Fallback para navegadores sin crypto.randomUUID
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = Math.random() * 16 | 0
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8)
|
||||
return v.toString(16)
|
||||
})
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export function useTemplates() {
|
||||
const templates = useState<PrintTemplate[]>('templates', () => [])
|
||||
const initialized = useState('templatesInitialized', () => false)
|
||||
const loading = useState('templatesLoading', () => false)
|
||||
const error = useState<string | null>('templatesError', () => null)
|
||||
|
||||
// Cargar de localStorage al iniciar (solo cliente)
|
||||
if (import.meta.client && !initialized.value) {
|
||||
const stored = localStorage.getItem(STORAGE_KEY)
|
||||
if (stored) {
|
||||
try {
|
||||
templates.value = JSON.parse(stored)
|
||||
} catch (e) {
|
||||
console.error('Error parsing templates:', e)
|
||||
}
|
||||
}
|
||||
initialized.value = true
|
||||
}
|
||||
|
||||
// Guardar en localStorage cuando cambie
|
||||
watch(templates, (val) => {
|
||||
if (import.meta.client) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(val))
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
function saveTemplate(name: string, description: string, operations: Operation[]): PrintTemplate {
|
||||
const template: PrintTemplate = {
|
||||
id: generateId(),
|
||||
name,
|
||||
description,
|
||||
operations: JSON.parse(JSON.stringify(operations)),
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now()
|
||||
}
|
||||
templates.value = [...templates.value, template]
|
||||
return template
|
||||
}
|
||||
|
||||
function updateTemplate(id: string, updates: Partial<Omit<PrintTemplate, 'id' | 'createdAt'>>) {
|
||||
const idx = templates.value.findIndex(t => t.id === id)
|
||||
if (idx !== -1) {
|
||||
templates.value = templates.value.map((t, i) =>
|
||||
i === idx ? { ...t, ...updates, updatedAt: Date.now() } : t
|
||||
)
|
||||
// Cargar templates del servidor
|
||||
async function fetchTemplates(): Promise<void> {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await $fetch<PrintTemplate[]>('/api/templates')
|
||||
templates.value = data
|
||||
} catch (e) {
|
||||
console.error('Error fetching templates:', e)
|
||||
error.value = 'Error al cargar templates'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function deleteTemplate(id: string) {
|
||||
templates.value = templates.value.filter(t => t.id !== id)
|
||||
// Guardar nuevo template
|
||||
async function saveTemplate(
|
||||
name: string,
|
||||
description: string,
|
||||
operations: Operation[]
|
||||
): Promise<PrintTemplate | null> {
|
||||
error.value = null
|
||||
try {
|
||||
const template = await $fetch<PrintTemplate>('/api/templates', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
name,
|
||||
description,
|
||||
operations
|
||||
}
|
||||
})
|
||||
templates.value = [...templates.value, template]
|
||||
return template
|
||||
} catch (e) {
|
||||
console.error('Error saving template:', e)
|
||||
error.value = 'Error al guardar template'
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar template existente
|
||||
async function updateTemplate(
|
||||
id: string,
|
||||
updates: Partial<{ name: string; description: string; operations: Operation[] }>
|
||||
): Promise<PrintTemplate | null> {
|
||||
error.value = null
|
||||
try {
|
||||
const template = await $fetch<PrintTemplate>(`/api/templates/${id}`, {
|
||||
method: 'PUT',
|
||||
body: updates
|
||||
})
|
||||
templates.value = templates.value.map(t => t.id === id ? template : t)
|
||||
return template
|
||||
} catch (e) {
|
||||
console.error('Error updating template:', e)
|
||||
error.value = 'Error al actualizar template'
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Eliminar template
|
||||
async function deleteTemplate(id: string): Promise<boolean> {
|
||||
error.value = null
|
||||
try {
|
||||
await $fetch(`/api/templates/${id}`, { method: 'DELETE' })
|
||||
templates.value = templates.value.filter(t => t.id !== id)
|
||||
return true
|
||||
} catch (e) {
|
||||
console.error('Error deleting template:', e)
|
||||
error.value = 'Error al eliminar template'
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Cargar operaciones de un template
|
||||
function loadTemplate(id: string): Operation[] | null {
|
||||
const template = templates.value.find(t => t.id === id)
|
||||
return template ? JSON.parse(JSON.stringify(template.operations)) : null
|
||||
}
|
||||
|
||||
function duplicateTemplate(id: string): PrintTemplate | null {
|
||||
const template = templates.value.find(t => t.id === id)
|
||||
if (!template) return null
|
||||
return saveTemplate(
|
||||
`${template.name} (copia)`,
|
||||
template.description || '',
|
||||
template.operations
|
||||
)
|
||||
// Duplicar template
|
||||
async function duplicateTemplate(id: string): Promise<PrintTemplate | null> {
|
||||
error.value = null
|
||||
try {
|
||||
const template = await $fetch<PrintTemplate>(`/api/templates/${id}/duplicate`, {
|
||||
method: 'POST'
|
||||
})
|
||||
templates.value = [...templates.value, template]
|
||||
return template
|
||||
} catch (e) {
|
||||
console.error('Error duplicating template:', e)
|
||||
error.value = 'Error al duplicar template'
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
templates: readonly(templates),
|
||||
loading: readonly(loading),
|
||||
error: readonly(error),
|
||||
fetchTemplates,
|
||||
saveTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
|
||||
Reference in New Issue
Block a user