|
|
@@ -0,0 +1,320 @@
|
|
|
+<script>
|
|
|
+ import { goto } from '$app/navigation';
|
|
|
+ import { onMount } from 'svelte';
|
|
|
+ import { browser } from '$app/environment';
|
|
|
+ import add_icon from '$lib/assets/add_icon.svg';
|
|
|
+ import save_icon from '$lib/assets/save_icon.svg';
|
|
|
+ import trash_icon from '$lib/assets/trash_icon.svg';
|
|
|
+
|
|
|
+ let token = null;
|
|
|
+ let company = null;
|
|
|
+ let flag = null;
|
|
|
+
|
|
|
+ if (browser) {
|
|
|
+ token = localStorage.getItem('token');
|
|
|
+ company = Number(localStorage.getItem('company'));
|
|
|
+ flag = localStorage.getItem('flag');
|
|
|
+ }
|
|
|
+
|
|
|
+ const statusMap = {
|
|
|
+ 1: 'FREE',
|
|
|
+ 2: 'OCCUPIED',
|
|
|
+ 3: 'ALERT'
|
|
|
+ };
|
|
|
+
|
|
|
+ const statusToApiMap = {
|
|
|
+ FREE: 'Livre',
|
|
|
+ OCCUPIED: 'Ocupado',
|
|
|
+ ALERT: 'Alerta'
|
|
|
+ };
|
|
|
+
|
|
|
+ let tables = [];
|
|
|
+
|
|
|
+ let orders = {};
|
|
|
+
|
|
|
+ let newTableNumber = '';
|
|
|
+ let tableToDelete = null;
|
|
|
+ let isAddingCommand = false;
|
|
|
+
|
|
|
+ const getTableOrder = (tableId) => {
|
|
|
+ return orders[tableId];
|
|
|
+ };
|
|
|
+
|
|
|
+ const updateTableStatus = async (tableNumber, newStatus) => {
|
|
|
+ const requestOptions = {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: `Bearer ${token}`
|
|
|
+ },
|
|
|
+ redirect: 'follow',
|
|
|
+ body: JSON.stringify({
|
|
|
+ table_number: tableNumber,
|
|
|
+ company_id: company,
|
|
|
+ status_status: statusToApiMap[newStatus]
|
|
|
+ })
|
|
|
+ };
|
|
|
+
|
|
|
+ const response = await fetch('https://dev2.mixtech.dev.br/table/update', requestOptions);
|
|
|
+ const result = await response.json();
|
|
|
+ if (result.status !== 'ok') {
|
|
|
+ throw new Error('Falha ao atualizar status: ' + result.msg);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const startOrder = async (tableId, tableNumber) => {
|
|
|
+ await updateTableStatus(tableNumber, 'OCCUPIED');
|
|
|
+ tables = tables.map((t) => {
|
|
|
+ if (t.id === tableId) {
|
|
|
+ return { ...t, status: 'OCCUPIED', startTime: new Date().toISOString() };
|
|
|
+ }
|
|
|
+ return t;
|
|
|
+ });
|
|
|
+ orders = { ...orders, [tableId]: { items: [], totalAmount: 0 } };
|
|
|
+ console.log(`Order started for table ${tableId}`);
|
|
|
+ };
|
|
|
+
|
|
|
+ const getStatusColor = (status) => {
|
|
|
+ switch (status) {
|
|
|
+ case 'FREE':
|
|
|
+ return 'bg-green-600 hover:bg-green-700';
|
|
|
+ case 'OCCUPIED':
|
|
|
+ return 'bg-yellow-500 hover:bg-yellow-600';
|
|
|
+ case 'ALERT':
|
|
|
+ return 'bg-red-600 hover:bg-red-700';
|
|
|
+ default:
|
|
|
+ return 'bg-gray-700 hover:bg-gray-800';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const getStatusText = (status) => {
|
|
|
+ switch (status) {
|
|
|
+ case 'FREE':
|
|
|
+ return 'Livre';
|
|
|
+ case 'OCCUPIED':
|
|
|
+ return 'Ocupada';
|
|
|
+ case 'ALERT':
|
|
|
+ return 'Alerta';
|
|
|
+ default:
|
|
|
+ return 'Desconhecido';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const getOrderTime = (startTime) => {
|
|
|
+ if (!startTime) return '';
|
|
|
+
|
|
|
+ const startDateTime = new Date(startTime);
|
|
|
+ const now = new Date();
|
|
|
+ const diff = Math.floor((now.getTime() - startDateTime.getTime()) / 1000 / 60);
|
|
|
+
|
|
|
+ if (diff < 60) {
|
|
|
+ return `${diff} min`;
|
|
|
+ } else {
|
|
|
+ const hours = Math.floor(diff / 60);
|
|
|
+ const mins = diff % 60;
|
|
|
+ return `${hours}h ${mins}m`;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleTableClick = async (tableId, tableNumber) => {
|
|
|
+ const table = tables.find((t) => t.id === tableId);
|
|
|
+ if (!table) return;
|
|
|
+
|
|
|
+ if (table.status === 'FREE') {
|
|
|
+ await startOrder(tableId, tableNumber);
|
|
|
+ } else {
|
|
|
+ await goto(`/pos/${tableId}`);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const openDeleteConfirm = (tableId, tableNumber) => {
|
|
|
+ tableToDelete = { id: tableId, number: tableNumber };
|
|
|
+ showConfirmDelete = true;
|
|
|
+ };
|
|
|
+
|
|
|
+ const resetCommandForm = async () => {
|
|
|
+ isAddingCommand = false;
|
|
|
+ };
|
|
|
+ const deleteTable = async () => {
|
|
|
+ if (!tableToDelete) return;
|
|
|
+
|
|
|
+ const requestOptions = {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: `Bearer ${token}`
|
|
|
+ },
|
|
|
+ redirect: 'follow',
|
|
|
+ body: JSON.stringify({
|
|
|
+ table_number: tableToDelete.number,
|
|
|
+ company_id: company
|
|
|
+ })
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ const response = await fetch('https://dev2.mixtech.dev.br/table/delete', requestOptions);
|
|
|
+ const result = await response.json();
|
|
|
+ if (result.status === 'ok') {
|
|
|
+ tables = tables.filter((t) => t.id !== tableToDelete.id);
|
|
|
+ const newOrders = { ...orders };
|
|
|
+ delete newOrders[tableToDelete.id];
|
|
|
+ orders = newOrders;
|
|
|
+ showConfirmDelete = false;
|
|
|
+ tableToDelete = null;
|
|
|
+ } else {
|
|
|
+ console.error('Erro ao excluir mesa:', result.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const fetchTables = async () => {
|
|
|
+ const requestOptions = {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: `Bearer ${token}`
|
|
|
+ },
|
|
|
+ redirect: 'follow',
|
|
|
+ body: JSON.stringify({ company_id: company })
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ const response = await fetch('https://dev2.mixtech.dev.br/table/get', requestOptions);
|
|
|
+ const result = await response.json();
|
|
|
+ if (result.status === 'ok') {
|
|
|
+ tables = result.data.map((d) => ({
|
|
|
+ id: d.table_id,
|
|
|
+ number: d.table_number,
|
|
|
+ status: statusMap[d.status_id] || 'Desconhecido',
|
|
|
+ startTime: statusMap[d.status_id] === 'FREE' ? null : new Date().toISOString()
|
|
|
+ }));
|
|
|
+ tables.forEach((t) => {
|
|
|
+ if (t.status !== 'FREE' && !orders[t.id]) {
|
|
|
+ orders[t.id] = { items: [], totalAmount: 0 };
|
|
|
+ }
|
|
|
+ });
|
|
|
+ orders = { ...orders };
|
|
|
+ } else {
|
|
|
+ console.error('Erro ao buscar mesas:', result.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const createCommand = async () => {
|
|
|
+ isAddingCommand = true;
|
|
|
+ };
|
|
|
+
|
|
|
+ onMount(() => {
|
|
|
+ fetchTables();
|
|
|
+ });
|
|
|
+</script>
|
|
|
+
|
|
|
+<div class="container mx-auto h-full w-full rounded-md bg-gray-700">
|
|
|
+ <div class="flex flex-col">
|
|
|
+ <h1 class="mb-6 text-center text-2xl font-bold">Comandas</h1>
|
|
|
+ <div class="mx-auto max-w-7xl px-4">
|
|
|
+ <div
|
|
|
+ class="grid grid-cols-2 justify-items-center gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5"
|
|
|
+ >
|
|
|
+ {#each tables as table}
|
|
|
+ {@const order = getTableOrder(table.id)}
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="w-full max-w-xs transform overflow-hidden rounded-lg bg-gray-800 shadow-md transition-transform duration-200 hover:scale-105"
|
|
|
+ >
|
|
|
+ <div class={`flex items-center justify-between p-4 ${getStatusColor(table.status)}`}>
|
|
|
+ <span class="font-medium">Mesa {table.number}</span>
|
|
|
+ <span class="text-sm font-medium">{getStatusText(table.status)}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="p-4">
|
|
|
+ <div class="flex flex-col items-center justify-center space-y-2 py-4">
|
|
|
+ <div class="space-y-3">
|
|
|
+ <div class="flex justify-between text-sm">
|
|
|
+ <span class="text-gray-400">{getOrderTime(table.startTime)}</span>
|
|
|
+ <!-- <div class="font-medium">
|
|
|
+ {order.items.length}
|
|
|
+ {order.items.length === 1 ? 'item' : 'itens'}
|
|
|
+ </div> -->
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex justify-between text-sm">
|
|
|
+ <span class="text-gray-400">Total:</span>
|
|
|
+ <!-- <span class="font-medium">
|
|
|
+ R$ {order.totalAmount.toFixed(2)}
|
|
|
+ </span> -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <button
|
|
|
+ on:click={() => handleTableClick(table.id, table.number)}
|
|
|
+ class="flex items-center justify-center rounded bg-emerald-600 px-4 py-2 transition-colors hover:bg-emerald-700"
|
|
|
+ >
|
|
|
+ Comanda
|
|
|
+ </button>
|
|
|
+ <div>
|
|
|
+ <button
|
|
|
+ on:click={() => openDeleteConfirm(table.id, table.number)}
|
|
|
+ class="flex items-center justify-center rounded bg-red-600 px-4 py-2 text-sm transition-colors hover:bg-red-700"
|
|
|
+ >
|
|
|
+ Cancelar
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {/each}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="mb-4 mt-4 flex justify-center">
|
|
|
+ <button
|
|
|
+ on:click={createCommand}
|
|
|
+ class="rounded-md bg-green-600 px-4 py-2 text-white transition-colors hover:bg-blue-700"
|
|
|
+ >
|
|
|
+ Adicionar Comanda
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+{#if isAddingCommand}
|
|
|
+ <div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
|
|
|
+ <div class="w-full max-w-md rounded-lg bg-gray-800 p-6 shadow-lg">
|
|
|
+ <h2 class="mb-4 text-lg font-semibold">Adicionar Novo Usuário</h2>
|
|
|
+ <form on:submit={handleAddUser} class="space-y-4">
|
|
|
+ <div>
|
|
|
+ <p class="mb-1 block text-sm text-gray-400">Nome</p>
|
|
|
+ <input
|
|
|
+ class="w-full rounded-md border border-gray-600 bg-gray-700 px-3 py-2 focus:ring-emerald-500"
|
|
|
+ placeholder="Ex: johndoe"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <p class="mb-1 block text-sm text-gray-400">(Opcional) Celular</p>
|
|
|
+ <input
|
|
|
+ class="w-full rounded-md border border-gray-600 bg-gray-700 px-3 py-2 focus:ring-emerald-500"
|
|
|
+ placeholder="(11) 99999-9999"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex space-x-2">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ on:click={resetCommandForm}
|
|
|
+ class="rounded-lg bg-gray-700 px-4 py-2 hover:bg-gray-600"
|
|
|
+ >
|
|
|
+ Cancelar
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ type="submit"
|
|
|
+ class="flex items-center rounded-lg bg-emerald-600 px-4 py-2 hover:bg-emerald-700"
|
|
|
+ >
|
|
|
+ <img src={save_icon} alt="Salvar" class="mr-2 h-4 w-4" /> Salvar
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+{/if}
|