gdias преди 5 месеца
родител
ревизия
a30eddcb49
променени са 3 файла, в които са добавени 329 реда и са изтрити 0 реда
  1. 320 0
      src/lib/component/Commands.svelte
  2. 1 0
      src/lib/component/Tables.svelte
  3. 8 0
      src/routes/dashboard/commands/+page.svelte

+ 320 - 0
src/lib/component/Commands.svelte

@@ -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}

+ 1 - 0
src/lib/component/Tables.svelte

@@ -119,6 +119,7 @@
 
 		if (table.status === 'FREE') {
 			await startOrder(tableId, tableNumber);
+			goto('/dashboard/commands');
 		} else {
 			await goto(`/pos/${tableId}`);
 		}

+ 8 - 0
src/routes/dashboard/commands/+page.svelte

@@ -0,0 +1,8 @@
+<script>
+	import Commands from '$lib/component/Commands.svelte';
+	import SideBar from '$lib/layout/SideBar.svelte';
+</script>
+
+<SideBar>
+	<Commands />
+</SideBar>