瀏覽代碼

add the kitchen page

gdias 4 月之前
父節點
當前提交
95ece4ba25

+ 47 - 38
src/lib/component/HistoricModal.svelte

@@ -1,46 +1,55 @@
 <script>
-  export let pedidos = [];
-  import { createEventDispatcher } from 'svelte';
-  const dispatch = createEventDispatcher();
+	export let pedidos = [];
+	import { createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
 
-  const fechar = () => dispatch('close');
+	const fechar = () => dispatch('close');
 
-  // Dispara evento para o pedido específico voltar para andamento
-  function voltarParaAndamento(pedido) {
-    dispatch('voltarAndamento', pedido);
-  }
+	function voltarParaAndamento(id) {
+		dispatch('voltarAndamento', id);
+	}
 </script>
 
-<div class="fixed inset-0 bg-black bg-opacity-60 flex justify-center items-center z-50">
-  <div class="bg-gray-700 text-white p-6 rounded-md w-96 max-h-[80vh] overflow-auto">
-    <div class="flex justify-between items-center mb-4">
-      <h2 class="font-bold text-lg">Histórico de Pedidos</h2>
-      <button on:click={fechar} class="text-red-600 text-xl font-bold" aria-label="Fechar modal">✖</button>
-    </div>
+<div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-60">
+	<div class="max-h-[80vh] w-96 overflow-auto rounded-md bg-gray-700 p-6 text-white">
+		<div class="mb-4 flex items-center justify-between">
+			<h2 class="text-lg font-bold">Histórico de Itens Prontos</h2>
+			<button on:click={fechar} class="text-xl font-bold text-red-600" aria-label="Fechar modal"
+				>X</button
+			>
+		</div>
 
-    {#each pedidos as pedido (pedido.mesa)}
-      <div class="mb-4 border-b border-gray-300 pb-2">
-        <p><strong>Mesa:</strong> {pedido.mesa}</p>
-        <p><strong>Status:</strong> {pedido.status}</p>
-        <div class="mt-2">
-          <strong>Itens:</strong>
-          <ul class="list-disc list-inside">
-            {#each pedido.itens as item}
-              <li>{item}</li>
-            {/each}
-          </ul>
-        </div>
-        <!-- Botão voltar andamento individual -->
-        {#if pedido.status !== 'andamento'}
-          <button
-            class="mt-2 bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded transition"
-            on:click={() => voltarParaAndamento(pedido)}
-          >
-            Voltar para andamento
-          </button>
-        {/if}
-      </div>
-    {/each}
+		{#if pedidos.length === 0}
+			<p class="text-gray-400">Nenhum item pronto ainda.</p>
+		{:else}
+			{#each pedidos as pedido (pedido.id)}
+				<div class="mb-4 border-b border-gray-300 pb-2">
+					<p><strong>Mesa:</strong> {pedido.mesa}</p>
+					<p><strong>Comanda:</strong> {pedido.comanda_nome}</p>
 
-  </div>
+					<div class="mt-2">
+						<strong>Itens prontos:</strong>
+						<ul class="list-inside list-disc">
+							{#each pedido.items as item}
+								<li class="flex items-center justify-between">
+									<div>
+										{item.nome}
+										{#if item.observacao}
+											<span class="italic text-yellow-300"> — {item.observacao}</span>
+										{/if}
+									</div>
+									<button
+										class="ml-4 rounded bg-blue-600 px-2 py-1 text-sm hover:bg-blue-500"
+										on:click={() => voltarParaAndamento(item.id)}
+									>
+										Voltar
+									</button>
+								</li>
+							{/each}
+						</ul>
+					</div>
+				</div>
+			{/each}
+		{/if}
+	</div>
 </div>

+ 132 - 50
src/lib/component/Kitchen.svelte

@@ -2,82 +2,164 @@
 	import { onMount } from 'svelte';
 	import OrderCard from '$lib/component/OrderCard.svelte';
 	import HistoricModal from '$lib/component/HistoricModal.svelte';
+	import { browser } from '$app/environment';
 
-	let pedidos = [];
-
+	let pedidosAtivos = [];
+	let pedidosConcluidos = [];
+	let company = null;
+	let token = null;
 	let mostrarHistorico = false;
+	let oldData = 0;
 
-	const abrirHistorico = () => {
-		mostrarHistorico = true;
-	};
+	if (browser) {
+		token = localStorage.getItem('token');
+		company = Number(localStorage.getItem('company'));
+	}
 
-	const fecharHistorico = () => {
-		mostrarHistorico = false;
-	};
+	const abrirHistorico = () => (mostrarHistorico = true);
+	const fecharHistorico = () => (mostrarHistorico = false);
 
-	// Marca pedido como concluído (vai para o histórico)
-	function handlePedidoPronto(event) {
-		const pedidoAtualizado = event.detail;
-		pedidos = pedidos.map((pedido) =>
-			pedido.mesa === pedidoAtualizado.mesa ? { ...pedido, status: 'concluido' } : pedido
-		);
-	}
+	async function atualizarFlagItem(order_item_id) {
+		try {
+			const myHeaders = new Headers();
+			myHeaders.append('Authorization', `Bearer ${token}`);
+			myHeaders.append('Content-Type', 'application/json');
+
+			const raw = JSON.stringify({
+				company_id: company,
+				order_item_id
+			});
+
+			const requestOptions = {
+				method: 'POST',
+				headers: myHeaders,
+				body: raw,
+				redirect: 'follow'
+			};
+
+			const resp = await fetch('https://dev2.mixtech.dev.br/order_item/update', requestOptions);
+			const data = await resp.text();
 
-	// Volta pedido do histórico para andamento (volta pra lista)
-	function handleVoltarAndamento(event) {
-		const pedidoAlvo = event.detail;
-		pedidos = pedidos.map((pedido) =>
-			pedido.mesa === pedidoAlvo.mesa ? { ...pedido, status: 'andamento' } : pedido
-		);
+			await fetchItems();
+		} catch (err) {
+			console.error('Erro ao atualizar item:', err);
+		}
 	}
 
-	// ⚡ Carrega pedidos do "backend"
-	onMount(async () => {
+	async function fetchItems() {
 		try {
-			const response = await fetch('https://counter.mixtech.dev.br/dashboard/kitchen');
-			const data = await response.json();
-			pedidos = data;
+			const myHeaders = new Headers();
+			myHeaders.append('Authorization', `Bearer ${token}`);
+			myHeaders.append('Content-Type', 'application/json');
+
+			const raw = JSON.stringify({ company_id: company });
+
+			const requestOptions = {
+				method: 'POST',
+				headers: myHeaders,
+				body: raw,
+				redirect: 'follow'
+			};
+
+			const response = await fetch('https://dev2.mixtech.dev.br/kitchen/get', requestOptions);
+			if (!response.ok) {
+				console.error('Erro na resposta do servidor:', response.status, response.statusText);
+				return;
+			}
+
+			const ordersResult = await response.json();
+
+			const totalItems = (ordersResult.data || []).reduce((acc, pedido) => {
+				return acc + (pedido.items ? pedido.items.length : 0);
+			}, 0);
+
+			if (totalItems > oldData) {
+				const audio = new Audio('/som.mp3');
+				audio.play().catch((e) => console.warn('Erro ao tocar som:', e));
+			}
+			oldData = totalItems;
+
+			pedidosAtivos = [];
+			pedidosConcluidos = [];
+
+			(ordersResult.data || []).forEach((p) => {
+				const itensAndamento = p.items
+					.filter((i) => i.order_item_flag === 'a')
+					.map((i) => ({
+						id: i.order_item_id,
+						nome: i.product_name,
+						comanda_nome: i.order_name,
+						observacao: i.kitchen_note?.trim() || null
+					}));
+
+				const itensProntos = p.items
+					.filter((i) => i.order_item_flag === 'p')
+					.map((i) => ({
+						id: i.order_item_id,
+						nome: i.product_name,
+						comanda_nome: i.order_name,
+						observacao: i.kitchen_note?.trim() || null
+					}));
+
+				if (itensAndamento.length > 0) {
+					pedidosAtivos.push({
+						id: p.order_id,
+						mesa: p.table_id,
+						comanda_nome: p.order_name,
+						items: itensAndamento
+					});
+				}
+
+				if (itensProntos.length > 0) {
+					pedidosConcluidos.push({
+						id: p.order_id,
+						mesa: p.table_id,
+						comanda_nome: p.order_name,
+						items: itensProntos
+					});
+				}
+			});
 		} catch (error) {
 			console.error('Erro ao carregar pedidos:', error);
-
-			// Fallback local caso falhe
-			pedidos = [
-				{ mesa: '01', itens: ['Coca', 'X-Burguer'], status: 'pendente' },
-				{ mesa: '02', itens: ['Água', 'Batata'], status: 'andamento' },
-				{ mesa: '03', itens: ['Cerveja', 'Sopa'], status: 'pendente' },
-				{ mesa: '04', itens: ['Suco', 'Lasanha'], status: 'concluido' }
-			];
 		}
-	});
-
-	// Lista apenas pedidos não concluídos
-	$: pedidosAtivos = pedidos.filter((p) => p.status !== 'concluido');
+	}
 
-	// Lista de pedidos concluídos para o histórico
-	$: pedidosConcluidos = pedidos.filter((p) => p.status === 'concluido');
+	onMount(() => {
+		fetchItems();
+		setInterval(fetchItems, 5000);
+	});
 </script>
 
-<main class="relative rounded-md bg-gray-800 p-4 text-white">
-	<!-- Botão para abrir histórico -->
-	<div class="absolute right-4 top-4">
-		<button class="text-md rounded-md bg-yellow-600 p-1 text-black" on:click={abrirHistorico}>
+<main class="relative rounded-xl bg-gray-800 p-6 text-white shadow-lg">
+	<h1 class="mb-6 text-3xl font-bold">Cozinha ao vivo</h1>
+
+	<div class="absolute right-6 top-6">
+		<button
+			class="h-12 w-40 rounded-lg bg-yellow-400 font-semibold text-black shadow-md transition-colors duration-200 hover:bg-yellow-300"
+			on:click={abrirHistorico}
+		>
 			Ver histórico
 		</button>
 	</div>
 
-	<!-- Lista de pedidos ativos -->
-	<div class="mt-10 space-y-4">
-		{#each pedidosAtivos as pedido (pedido.mesa)}
-			<OrderCard {pedido} on:pedidoPronto={handlePedidoPronto} />
+	<div class="mt-20 space-y-6">
+		{#each pedidosAtivos as pedido (pedido.id)}
+			<OrderCard
+				{pedido}
+				class="rounded-lg border border-gray-700 bg-gray-800 p-4 shadow-lg transition-colors duration-200 hover:bg-gray-700"
+				on:marcarPronto={(e) => {
+					e.detail.forEach((id) => atualizarFlagItem(id));
+				}}
+			/>
 		{/each}
 	</div>
 
-	<!-- Modal de histórico -->
 	{#if mostrarHistorico}
 		<HistoricModal
 			pedidos={pedidosConcluidos}
 			on:close={fecharHistorico}
-			on:voltarAndamento={handleVoltarAndamento}
+			on:voltarAndamento={(e) => atualizarFlagItem(e.detail)}
+			class="rounded-xl shadow-2xl"
 		/>
 	{/if}
 </main>

+ 3 - 3
src/lib/component/Mananger.svelte

@@ -140,7 +140,7 @@
 	});
 </script>
 
-<div class="container mx-auto h-screen">
+<div class="container mx-auto">
 	<div class="flex flex-col">
 		<div class="mb-6 flex items-center justify-between">
 			<h1 class="text-2xl font-bold">Gerenciar Usuários</h1>
@@ -152,8 +152,8 @@
 			</button>
 		</div>
 
-		<div class="overflow-hidden rounded-lg bg-gray-800 shadow-lg">
-			<table class="w-full divide-y divide-gray-700">
+		<div class="overflow-x-auto rounded-lg bg-gray-800 shadow-lg">
+			<table class="w-full min-w-[500px] divide-y divide-gray-700">
 				<thead class="bg-gray-700">
 					<tr>
 						<th class="px-6 py-3 text-left text-sm font-semibold text-gray-300">Username</th>

+ 44 - 33
src/lib/component/OrderCard.svelte

@@ -1,41 +1,52 @@
 <script>
-  export let pedido;
-  import { createEventDispatcher } from 'svelte';
+	export let pedido;
+	import { createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
 
-  const dispatch = createEventDispatcher();
+	function marcarItemComoPronto(id) {
+		dispatch('marcarPronto', [id]);
+	}
 
-  function marcarComoPronto() {
-    // Atualiza status local (caso precise refletir no próprio componente)
-    pedido.status = 'pronto';
-
-    // Emite evento para o componente pai
-    dispatch('pedidoPronto', pedido);
-  }
+	function marcarTodosComoProntos() {
+		const ids = pedido.items.map((item) => item.id);
+		dispatch('marcarPronto', ids);
+	}
 </script>
 
-<div class="bg-gray-700 p-4 rounded-md shadow-md text-white flex justify-between">
-  <div class="flex flex-col">
-    <div class="font-semibold mb-2">
-      Mesa {pedido.mesa}
-    </div>
+<div class="flex flex-col rounded-md bg-gray-700 p-4 text-white shadow-md">
+	<div class="flex justify-between">
+		<div>
+			<div class="mb-2 font-semibold">Mesa {pedido.mesa}</div>
+			<div class="mb-2 font-semibold">Comanda {pedido.comanda_nome}</div>
 
-    <div>
-      <strong>Itens:</strong>
-      <ul class="list-disc list-inside">
-        {#each pedido.itens as item}
-          <li>{item}</li>
-        {/each}
-      </ul>
-    </div>
-  </div>
+			<strong>Itens:</strong>
+			<ul class="list-inside list-disc text-lg">
+				{#each pedido.items as item}
+					<li class="mb-1 flex items-center justify-between">
+						<div>
+							{item.nome}
+							{#if item.observacao}
+								<span class="italic text-yellow-300"> — {item.observacao}</span>
+							{/if}
+						</div>
+						<button
+							class="ml-4 mt-2 h-10 rounded-md bg-blue-600 px-4 py-1 text-sm font-bold hover:bg-blue-500"
+							on:click={() => marcarItemComoPronto(item.id)}
+						>
+							Item pronto
+						</button>
+					</li>
+				{/each}
+			</ul>
+		</div>
 
-  <div class="flex flex-col items-end gap-2 justify-between">
-    <button
-      class="bg-green-600 px-4 py-2 rounded-xl text-sm hover:bg-green-500"
-      style="min-height: 100px;"
-      on:click={marcarComoPronto}
-    >
-      Pedido pronto
-    </button>
-  </div>
+		<div class="flex items-center">
+			<button
+				class="h-36 w-36 rounded-xl bg-green-600 px-8 text-lg font-bold hover:bg-green-500"
+				on:click={marcarTodosComoProntos}
+			>
+				Pedido pronto
+			</button>
+		</div>
+	</div>
 </div>

+ 7 - 9
src/routes/dashboard/kitchen/+page.svelte

@@ -1,13 +1,11 @@
 <script>
-    import DashBoardGuard from '$lib/component/DashBoardGuard.svelte';
-    import SideBar from '$lib/layout/SideBar.svelte';
-    import Kitchen from '$lib/component/Kitchen.svelte';
-
+	import DashBoardGuard from '$lib/component/DashBoardGuard.svelte';
+	import SideBar from '$lib/layout/SideBar.svelte';
+	import Kitchen from '$lib/component/Kitchen.svelte';
 </script>
 
 <DashBoardGuard>
-<SideBar>
-<Kitchen />
-
-</SideBar>
-</DashBoardGuard>
+	<SideBar>
+		<Kitchen />
+	</SideBar>
+</DashBoardGuard>

二進制
static/som.mp3