diff --git a/app/components/layout/MobileNavigation.vue b/app/components/layout/MobileNavigation.vue index e555941..dc9a7e2 100644 --- a/app/components/layout/MobileNavigation.vue +++ b/app/components/layout/MobileNavigation.vue @@ -18,6 +18,11 @@ const tabs = computed(() => [ label: 'Templates', value: 'templates', icon: 'i-heroicons-document-duplicate' + }, + { + label: 'Impresoras', + value: 'printers', + icon: 'i-heroicons-printer' } ]) diff --git a/app/components/printers/Card.vue b/app/components/printers/Card.vue new file mode 100644 index 0000000..a160b5d --- /dev/null +++ b/app/components/printers/Card.vue @@ -0,0 +1,89 @@ + + + diff --git a/app/components/printers/Form.vue b/app/components/printers/Form.vue new file mode 100644 index 0000000..c03808b --- /dev/null +++ b/app/components/printers/Form.vue @@ -0,0 +1,114 @@ + + + diff --git a/app/components/printers/List.vue b/app/components/printers/List.vue new file mode 100644 index 0000000..d0bb170 --- /dev/null +++ b/app/components/printers/List.vue @@ -0,0 +1,132 @@ + + + diff --git a/app/components/printers/Selector.vue b/app/components/printers/Selector.vue new file mode 100644 index 0000000..07de353 --- /dev/null +++ b/app/components/printers/Selector.vue @@ -0,0 +1,36 @@ + + + diff --git a/app/composables/usePrinters.ts b/app/composables/usePrinters.ts new file mode 100644 index 0000000..7900180 --- /dev/null +++ b/app/composables/usePrinters.ts @@ -0,0 +1,203 @@ +// Composable para gestión de impresoras +export interface Printer { + id: string + name: string + host: string + deviceId: string + timeout: number + isDefault: boolean + createdAt: string + updatedAt: string +} + +const printers = ref([]) +const selectedPrinterId = ref(null) +const loading = ref(false) +const error = ref(null) + +export function usePrinters() { + const selectedPrinter = computed(() => + printers.value.find(p => p.id === selectedPrinterId.value) || null + ) + + const defaultPrinter = computed(() => + printers.value.find(p => p.isDefault) || printers.value[0] || null + ) + + async function fetchPrinters() { + loading.value = true + error.value = null + + try { + const response = await $fetch<{ + ok: boolean + printers: Printer[] + selectedPrinterId: string | null + error?: string + }>('/api/printers') + + if (response.ok) { + printers.value = response.printers + selectedPrinterId.value = response.selectedPrinterId + } else { + error.value = response.error || 'Error al cargar impresoras' + } + } catch (err: any) { + error.value = err.message + } finally { + loading.value = false + } + } + + async function createPrinter(data: { + name: string + host: string + deviceId: string + timeout?: number + isDefault?: boolean + }) { + loading.value = true + error.value = null + + try { + const response = await $fetch<{ + ok: boolean + printer?: Printer + error?: string + }>('/api/printers', { + method: 'POST', + body: data + }) + + if (response.ok && response.printer) { + printers.value.push(response.printer) + // Si es la primera impresora, seleccionarla + if (printers.value.length === 1) { + selectedPrinterId.value = response.printer.id + } + return response.printer + } else { + error.value = response.error || 'Error al crear impresora' + return null + } + } catch (err: any) { + error.value = err.message + return null + } finally { + loading.value = false + } + } + + async function updatePrinter(id: string, data: Partial<{ + name: string + host: string + deviceId: string + timeout: number + isDefault: boolean + }>) { + loading.value = true + error.value = null + + try { + const response = await $fetch<{ + ok: boolean + printer?: Printer + error?: string + }>(`/api/printers/${id}`, { + method: 'PUT', + body: data + }) + + if (response.ok && response.printer) { + const index = printers.value.findIndex(p => p.id === id) + if (index !== -1) { + // Si se estableció como default, quitar default de las demás + if (data.isDefault) { + printers.value.forEach(p => p.isDefault = false) + } + printers.value[index] = response.printer + } + return response.printer + } else { + error.value = response.error || 'Error al actualizar impresora' + return null + } + } catch (err: any) { + error.value = err.message + return null + } finally { + loading.value = false + } + } + + async function deletePrinter(id: string) { + loading.value = true + error.value = null + + try { + const response = await $fetch<{ + ok: boolean + error?: string + }>(`/api/printers/${id}`, { + method: 'DELETE' + }) + + if (response.ok) { + const index = printers.value.findIndex(p => p.id === id) + if (index !== -1) { + printers.value.splice(index, 1) + } + // Si era la seleccionada, seleccionar otra + if (selectedPrinterId.value === id) { + selectedPrinterId.value = printers.value[0]?.id || null + } + return true + } else { + error.value = response.error || 'Error al eliminar impresora' + return false + } + } catch (err: any) { + error.value = err.message + return false + } finally { + loading.value = false + } + } + + async function selectPrinter(printerId: string | null) { + try { + const response = await $fetch<{ + ok: boolean + error?: string + }>('/api/printers/select', { + method: 'POST', + body: { printerId } + }) + + if (response.ok) { + selectedPrinterId.value = printerId + return true + } else { + error.value = response.error || 'Error al seleccionar impresora' + return false + } + } catch (err: any) { + error.value = err.message + return false + } + } + + return { + printers, + selectedPrinterId, + selectedPrinter, + defaultPrinter, + loading, + error, + fetchPrinters, + createPrinter, + updatePrinter, + deletePrinter, + selectPrinter + } +} diff --git a/app/pages/index.vue b/app/pages/index.vue index acb6181..9757584 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -2,6 +2,12 @@ const activeTab = ref('constructor') const isDesktop = useMediaQuery('(min-width: 768px)') const queue = usePrintQueue() +const printers = usePrinters() + +// Cargar impresoras al iniciar +onMounted(() => { + printers.fetchPrinters() +})