Forráskód Böngészése

better the validation and make the tables screen and connection de mananger users

gdias 5 hónapja
szülő
commit
cd0ade95cd

+ 2 - 1
src/lib/component/DashBoardGuard.svelte

@@ -10,7 +10,8 @@
 	}
 
 	onMount(async () => {
-		if (flag === 'blocked' || flag === '') {
+		console.log(flag);
+		if (flag !== 'admin' && flag !== 'waiter' && flag !== 'kitchen' && flag !== 'cashier') {
 			goto('/login');
 		} else {
 			autorizado = true;

+ 2 - 9
src/lib/component/Mananger.svelte

@@ -37,7 +37,7 @@
 	async function handleAddUser(event) {
 		event.preventDefault();
 
-		if (!newUserForm.name || !newUserForm.username || !newUserForm.email || !newUserForm.password) {
+		if (!newUserForm.username || !newUserForm.email || !newUserForm.password) {
 			console.error('Preencha todos os campos corretamente');
 			return;
 		}
@@ -202,14 +202,6 @@
 		<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
-						bind:value={newUserForm.name}
-						class="w-full rounded-md border border-gray-600 bg-gray-700 px-3 py-2 focus:ring-emerald-500"
-						placeholder="Ex: John Doe"
-					/>
-				</div>
 				<div>
 					<p class="mb-1 block text-sm text-gray-400">Username</p>
 					<input
@@ -244,6 +236,7 @@
 						<option value={1}>Admin</option>
 						<option value={2}>Garçom</option>
 						<option value={3}>Cozinha</option>
+						<option value={4}>Caixa</option>
 					</select>
 				</div>
 				<div class="flex space-x-2">

+ 253 - 83
src/lib/component/Tables.svelte

@@ -1,40 +1,77 @@
 <script>
 	import { goto } from '$app/navigation';
+	import { onMount } from 'svelte';
 	import { browser } from '$app/environment';
 
 	let token = null;
+	let company = null;
+	let flag = null;
 
 	if (browser) {
 		token = localStorage.getItem('token');
+		company = Number(localStorage.getItem('company'));
+		flag = localStorage.getItem('flag');
 	}
 
-	// Ícones (substitua os caminhos pelas suas imagens)
-	//import coffeeIcon from '$lib/assets/coffee.svg';
-	//import clockIcon from '$lib/assets/clock.svg';
-	//import creditCardIcon from '$lib/assets/credit-card.svg';
-	//import plusIcon from '$lib/assets/plus.svg';
-	//import trashIcon from '$lib/assets/trash.svg';
-	//import printerIcon from '$lib/assets/printer.svg';
-
-	let order;
-
-	// Dados simulados — substitua depois pela sua API
-	let tables = [
-		{ id: 1, status: 'FREE', startTime: null },
-		{ id: 2, status: 'OCCUPIED', startTime: '2025-07-19T10:00:00' },
-		{ id: 3, status: 'ALERT', startTime: '2025-07-19T08:45:00' }
-	];
-
-	let orders = {
-		2: { items: [{}, {}], totalAmount: 42.5 },
-		3: { items: [{}], totalAmount: 19.99 }
+	const statusMap = {
+		1: 'FREE',
+		2: 'OCCUPIED',
+		3: 'ALERT'
 	};
 
-	function getTableOrder(tableId) {
+	const statusToApiMap = {
+		FREE: 'Livre',
+		OCCUPIED: 'Ocupado',
+		ALERT: 'Alerta'
+	};
+
+	let tables = [];
+
+	let orders = {};
+
+	let newTableNumber = '';
+	let showConfirmDelete = false;
+	let tableToDelete = null;
+
+	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]
+			})
+		};
 
-	function getStatusColor(status) {
+		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';
@@ -45,9 +82,9 @@
 			default:
 				return 'bg-gray-700 hover:bg-gray-800';
 		}
-	}
+	};
 
-	function getStatusText(status) {
+	const getStatusText = (status) => {
 		switch (status) {
 			case 'FREE':
 				return 'Livre';
@@ -58,115 +95,225 @@
 			default:
 				return 'Desconhecido';
 		}
-	}
+	};
 
-	function getOrderTime(startTime) {
+	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`;
-		const hours = Math.floor(diff / 60);
-		const mins = diff % 60;
-		return `${hours}h ${mins}m`;
-	}
 
-	async function handleTableClick(tableId) {
+		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') {
-			// 🔁 Aqui você pode fazer o fetch para iniciar o pedido
-			// await fetch('/start-order', { method: 'POST', body: JSON.stringify({ tableId }) });
+			await startOrder(tableId, tableNumber);
+		} else {
+			await goto(`/pos/${tableId}`);
 		}
+	};
 
-		goto(`/pos/${tableId}`);
-	}
+	const openDeleteConfirm = (tableId, tableNumber) => {
+		tableToDelete = { id: tableId, number: tableNumber };
+		showConfirmDelete = true;
+	};
 
-	function printKitchenOrder(tableId) {
-		// 🔁 Substitua com sua lógica de impressão
-		alert(`Imprimir pedido da mesa ${tableId}`);
-	}
+	const cancelDelete = () => {
+		showConfirmDelete = false;
+		tableToDelete = null;
+	};
 
-	function cancelOrder(tableId) {
-		// 🔁 Substitua com sua lógica de cancelamento
-		alert(`Cancelar pedido da mesa ${tableId}`);
-	}
+	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 createTable = async () => {
+		if (!newTableNumber) return;
+
+		const payload = {
+			company_id: company,
+			table_number: newTableNumber,
+			status_id: 1
+		};
+
+		const requestOptions = {
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json',
+				Authorization: `Bearer ${token}`
+			},
+			redirect: 'follow',
+			body: JSON.stringify(payload)
+		};
+
+		try {
+			const response = await fetch('https://dev2.mixtech.dev.br/table/create', requestOptions);
+			const result = await response.json();
+			if (result.status === 'ok') {
+				await fetchTables();
+				newTableNumber = '';
+			} else {
+				console.error('Erro ao criar mesa:', result.msg);
+			}
+		} catch (error) {
+			console.error(error);
+		}
+	};
+
+	onMount(() => {
+		fetchTables();
+	});
 </script>
 
 <div class="container mx-auto">
 	<div class="flex flex-col">
 		<h1 class="mb-6 text-2xl font-bold">Mesas</h1>
 
+		{#if flag === 'admin'}
+			<div class="mb-4 flex">
+				<input
+					bind:value={newTableNumber}
+					placeholder="Número da nova mesa"
+					class="flex-grow rounded-l-md bg-gray-700 px-4 py-2 text-white focus:outline-none"
+				/>
+				<button
+					on:click={createTable}
+					class="rounded-r-md bg-blue-600 px-4 py-2 text-white transition-colors hover:bg-blue-700"
+				>
+					Criar Mesa
+				</button>
+			</div>
+		{/if}
+
 		<div class="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
 			{#each tables as table}
-				{(order = getTableOrder(table.id))}
+				{@const order = getTableOrder(table.id)}
 
 				<div
 					class="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)}`}>
-						<div class="flex items-center">
-							<!--<img src={coffeeIcon} alt="Mesa" class="mr-2 h-5 w-5" />-->
-							<span class="font-medium">Mesa {table.id}</span>
-						</div>
+						<span class="font-medium">Mesa {table.number}</span>
 						<span class="text-sm font-medium">{getStatusText(table.status)}</span>
 					</div>
 
 					<div class="p-4">
 						{#if table.status !== 'FREE' && order}
 							<div class="space-y-3">
-								<div class="flex justify-between text-sm">
-									<div class="flex items-center text-gray-400">
-										<!--<img src={clockIcon} alt="Relógio" class="mr-1 h-4 w-4" />-->
-										<span>{getOrderTime(table.startTime)}</span>
-									</div>
+								<!-- <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> -->
 
-								<div class="flex justify-between text-sm">
+								<!-- <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 class="mt-2 grid grid-cols-2 gap-2">
-									<button
-										on:click|stopPropagation={() => printKitchenOrder(table.id)}
-										class="flex items-center justify-center rounded bg-blue-600 px-2 py-1.5 text-xs transition-colors hover:bg-blue-700"
-									>
-										<!--<img src={printerIcon} alt="Cozinha" class="mr-1 h-3.5 w-3.5" />-->
-										Cozinha
-									</button>
-
-									<button
-										on:click|stopPropagation={() => cancelOrder(table.id)}
-										class="flex items-center justify-center rounded bg-red-600 px-2 py-1.5 text-xs transition-colors hover:bg-red-700"
-									>
-										<!--<img src={trashIcon} alt="Cancelar" class="mr-1 h-3.5 w-3.5" />-->
-										Cancelar
-									</button>
-								</div>
+									<span class="font-medium">
+										R$ {order.totalAmount.toFixed(2)}
+									</span>
+								</div> -->
 
 								<button
-									on:click={() => handleTableClick(table.id)}
+									on:click={() => handleTableClick(table.id, table.number)}
 									class="mt-2 flex w-full items-center justify-center rounded bg-indigo-600 px-3 py-2 text-sm transition-colors hover:bg-indigo-700"
 								>
-									<!--<img src={creditCardIcon} alt="Comanda" class="mr-2 h-4 w-4" />-->
-									Ver Comanda
+									Detalhes Mesa
 								</button>
 							</div>
 						{:else}
-							<div class="flex flex-col items-center justify-center py-4">
+							<div class="flex flex-col items-center justify-center space-y-2 py-4">
 								<button
-									on:click={() => handleTableClick(table.id)}
+									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"
 								>
-									<!--<img src={plusIcon} alt="Abrir Mesa" class="mr-2 h-5 w-5" />-->
 									Abrir Mesa
 								</button>
+								{#if flag === 'admin'}
+									<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"
+									>
+										Excluir Mesa
+									</button>
+								{/if}
 							</div>
 						{/if}
 					</div>
@@ -175,3 +322,26 @@
 		</div>
 	</div>
 </div>
+
+{#if showConfirmDelete}
+	<div class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
+		<div class="w-full max-w-sm rounded-lg bg-gray-800 p-6">
+			<h2 class="mb-4 text-lg font-bold">Confirmar Exclusão</h2>
+			<p class="mb-4">Deseja realmente excluir a Mesa {tableToDelete?.number}?</p>
+			<div class="flex justify-end gap-2">
+				<button
+					on:click={cancelDelete}
+					class="rounded bg-gray-600 px-4 py-2 text-white transition-colors hover:bg-gray-700"
+				>
+					Cancelar
+				</button>
+				<button
+					on:click={deleteTable}
+					class="rounded bg-red-600 px-4 py-2 text-white transition-colors hover:bg-red-700"
+				>
+					Excluir
+				</button>
+			</div>
+		</div>
+	</div>
+{/if}

+ 6 - 1
src/lib/layout/SideBar.svelte

@@ -17,7 +17,6 @@
 
 	let isMobileMenuOpen = false;
 	onMount(() => {
-		console.log(flag);
 		if (flag == 'waiter') {
 			navItems = [{ name: 'Mesas', path: '/dashboard/tables', icon: 'table_bar' }];
 		} else if (flag == 'admin') {
@@ -30,6 +29,12 @@
 			];
 		} else if (flag == 'kitchen') {
 			navItems = [{ name: 'Cozinha', path: '/dashboard/cozinha', icon: 'kitchen_icon' }];
+		} else if (flag == 'cashier') {
+			navItems = [
+				{ name: 'Mesas', path: '/dashboard/tables', icon: 'table_bar' },
+				{ name: 'Relatórios', path: '/dashboard/reports', icon: 'report_icon' },
+				{ name: 'Cozinha', path: '/dashboard/cozinha', icon: 'kitchen_icon' }
+			];
 		}
 	});