Przeglądaj źródła

add the new system caisher/waiter

gdias 4 miesięcy temu
rodzic
commit
3b5e99077f

+ 11 - 2
src/lib/component/Commands.svelte

@@ -105,7 +105,7 @@
 
 
 	function handleAddProduct(order_id) {
 	function handleAddProduct(order_id) {
 		localStorage.setItem('order', order_id);
 		localStorage.setItem('order', order_id);
-		goto('/dashboard/addproducts');
+		goto('/dashboard/endcommands');
 	}
 	}
 
 
 	onMount(() => {
 	onMount(() => {
@@ -127,8 +127,9 @@
 			<div
 			<div
 				class="w-full max-w-xs rounded-lg bg-gray-900 shadow-md transition-transform hover:scale-105"
 				class="w-full max-w-xs rounded-lg bg-gray-900 shadow-md transition-transform hover:scale-105"
 			>
 			>
-				<div class="flex items-center justify-between rounded-t-lg bg-emerald-700 p-4">
+				<div class="flex flex-col items-center justify-between rounded-t-lg bg-emerald-700 p-4">
 					<span class="font-medium">{order.name}</span>
 					<span class="font-medium">{order.name}</span>
+					<span class="font-medium">{order.phone}</span>
 				</div>
 				</div>
 				<div class="p-4">
 				<div class="p-4">
 					<div class="mb-2 text-sm text-gray-400">
 					<div class="mb-2 text-sm text-gray-400">
@@ -142,6 +143,14 @@
 						Adicionar items
 						Adicionar items
 					</button>
 					</button>
 					{#if flag == 'admin'}
 					{#if flag == 'admin'}
+						<button
+							class="mb-2 w-full rounded bg-yellow-600 py-2 hover:bg-yellow-700"
+							on:click={handleAddProduct(order.id)}
+						>
+							Finalizar Comanda
+						</button>
+					{/if}
+					{#if flag == 'admin' || flag == 'cashier'}
 						<button
 						<button
 							class="w-full rounded bg-red-600 py-2 hover:bg-red-700"
 							class="w-full rounded bg-red-600 py-2 hover:bg-red-700"
 							on:click={() => {
 							on:click={() => {

+ 348 - 0
src/lib/component/EndCommand.svelte

@@ -0,0 +1,348 @@
+<script>
+	import { onMount } from 'svelte';
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+	import { browser } from '$app/environment';
+
+	import trash_icon from '$lib/assets/trash_white.svg';
+	import arrow_back from '$lib/assets/arrow_back.svg';
+	import add_green from '$lib/assets/add_green.svg';
+	import cart_icon from '$lib/assets/cart_icon.svg';
+
+	let token = null;
+	let company = null;
+	let orderId = null;
+	let tableIdNum = null;
+
+	if (browser) {
+		token = localStorage.getItem('token');
+		company = Number(localStorage.getItem('company'));
+		orderId = Number(localStorage.getItem('order'));
+		tableIdNum = localStorage.getItem('table');
+	}
+
+	let selectedCategory = null;
+	let isPaymentModalOpen = false;
+	let selectedPaymentMethod = null;
+
+	let orderItems = [];
+	let products = [];
+	let categories = [];
+
+	$: totalAmount = orderItems.reduce(
+		(sum, item) => sum + Number(item.product_details?.product_price ?? 0) * (item.quantity ?? 1),
+		0
+	);
+
+	async function fetchOrderItems() {
+		try {
+			const myHeaders = new Headers();
+			myHeaders.append('Authorization', `Bearer ${token}`);
+			myHeaders.append('Content-Type', 'application/json');
+
+			if (orderId) {
+				const rawItems = JSON.stringify({
+					order_id: orderId,
+					company_id: company
+				});
+
+				const resItems = await fetch('https://dev2.mixtech.dev.br/order_item/get', {
+					method: 'POST',
+					headers: myHeaders,
+					body: rawItems
+				});
+				const itemsResult = await resItems.json();
+				orderItems = itemsResult.data || [];
+				orderItems = orderItems.map((item) => ({ ...item, quantity: item.quantity ?? 1 }));
+			} else {
+				orderItems = [];
+			}
+		} catch (error) {
+			console.error('Erro ao buscar itens do pedido:', error);
+		}
+	}
+
+	async function fetchProductsAndCategories() {
+		try {
+			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 resCategories = await fetch('https://dev2.mixtech.dev.br/category/get', requestOptions);
+			const categoriesResult = await resCategories.json();
+			categories = categoriesResult.data || [];
+
+			const resProducts = await fetch('https://dev2.mixtech.dev.br/product/get', requestOptions);
+			const productsResult = await resProducts.json();
+			products = productsResult.data || [];
+		} catch (error) {
+			console.error('Erro ao buscar produtos e categorias:', error);
+		}
+	}
+
+	onMount(async () => {
+		await fetchOrderItems();
+		await fetchProductsAndCategories();
+	});
+
+	$: if (categories.length > 0 && !selectedCategory) {
+		selectedCategory = categories[0].category_name;
+	}
+
+	$: filteredProducts = selectedCategory
+		? products.filter((p) => {
+				const cat = categories.find((c) => c.category_id === p.category_id);
+				return cat?.category_name === selectedCategory;
+			})
+		: products;
+
+	async function handleAddItem(product) {
+		const myHeaders = new Headers();
+		myHeaders.append('Authorization', `Bearer ${token}`);
+		myHeaders.append('Content-Type', 'application/json');
+		const payload = {
+			company_id: company,
+			order_id: orderId,
+			product_id: product.product_id
+		};
+		await fetch('https://dev2.mixtech.dev.br/order_item/create', {
+			method: 'POST',
+			headers: myHeaders,
+			body: JSON.stringify(payload)
+		});
+		await fetchOrderItems();
+	}
+
+	async function removeItemFromOrder(tableId, itemId) {
+		const myHeaders = new Headers();
+		myHeaders.append('Authorization', `Bearer ${token}`);
+		myHeaders.append('Content-Type', 'application/json');
+		await fetch('https://dev2.mixtech.dev.br/order_item/delete', {
+			method: 'DELETE',
+			headers: myHeaders,
+			body: JSON.stringify({
+				company_id: company,
+				item_id: itemId
+			})
+		});
+		await fetchOrderItems();
+	}
+
+	async function handlePaymentConfirm() {
+		if (!selectedPaymentMethod) {
+			console.warn('Selecione uma forma de pagamento');
+			return;
+		}
+		const myHeaders = new Headers();
+		myHeaders.append('Authorization', `Bearer ${token}`);
+		myHeaders.append('Content-Type', 'application/json');
+		await fetch('https://dev2.mixtech.dev.br/order/close', {
+			method: 'POST',
+			headers: myHeaders,
+			body: JSON.stringify({
+				company_id: company,
+				order_id: orderId,
+				payment_method: selectedPaymentMethod
+			})
+		});
+		isPaymentModalOpen = false;
+		goto('/tables');
+	}
+</script>
+
+<div class="container mx-auto">
+	<div class="mb-6 flex items-center">
+		<button
+			on:click={() => goto('/dashboard/commands')}
+			class="mr-4 rounded-lg bg-gray-800 p-2 hover:bg-gray-700"
+		>
+			<img src={arrow_back} alt="Voltar" class="h-5 w-5" />
+		</button>
+		<h1 class="text-2xl font-bold">Mesa {tableIdNum}</h1>
+	</div>
+
+	<div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
+		<div class="overflow-hidden rounded-lg bg-gray-800 shadow-lg lg:col-span-2">
+			<div class="bg-gray-700 p-4">
+				<h2 class="text-lg font-semibold">Adicionar Produtos</h2>
+			</div>
+
+			<div class="bg-gray-750 flex overflow-x-auto border-b border-gray-700 p-2">
+				{#each categories as category}
+					<button
+						class="mx-1 whitespace-nowrap rounded-md px-4 py-2 text-sm font-medium {selectedCategory ===
+						category.category_name
+							? 'bg-indigo-600 text-white'
+							: 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
+						on:click={() => (selectedCategory = category.category_name)}
+					>
+						{category.category_name}
+					</button>
+				{/each}
+			</div>
+
+			<div
+				class="grid max-h-[calc(100vh-400px)] grid-cols-2 gap-4 overflow-y-auto p-4 sm:grid-cols-3 md:grid-cols-4"
+			>
+				{#each filteredProducts as product}
+					<button
+						class="hover:bg-gray-650 rounded-lg bg-gray-700 p-4 text-left"
+						on:click={() => handleAddItem(product)}
+					>
+						<div class="flex h-full flex-col">
+							<h3 class="mb-2 line-clamp-2 font-medium">
+								{product.product_name ?? 'Nome Indisponível'}
+							</h3>
+							<div class="mt-auto flex items-center justify-between">
+								<span class="font-semibold text-emerald-400"
+									>R$ {Number(product.product_price ?? 0).toFixed(2)}</span
+								>
+								<img src={add_green} alt="Adicionar" class="h-5 w-5 text-emerald-400" />
+							</div>
+							{#if product.product_is_kitchen}
+								<span class="mt-2 rounded-full bg-yellow-800 px-2 py-0.5 text-xs text-yellow-300"
+									>Cozinha</span
+								>
+							{/if}
+						</div>
+					</button>
+				{/each}
+			</div>
+		</div>
+
+		<div
+			class="flex h-full flex-col overflow-hidden rounded-lg bg-gray-800 shadow-lg md:h-[calc(100vh-200px)]"
+		>
+			<div class="bg-gray-700 p-4">
+				<h2 class="text-lg font-semibold">Comanda</h2>
+			</div>
+
+			<div class="flex-1 overflow-y-auto p-4">
+				{#if orderItems.length === 0}
+					<div class="py-6 text-center text-gray-400">
+						<img src={cart_icon} class="mx-auto mb-2 h-10 w-10 opacity-50" alt="Nenhum item" />
+						<p>Nenhum item adicionado</p>
+					</div>
+				{:else}
+					{#each orderItems as item, index}
+						<div class="bg-gray-750 flex items-center justify-between rounded-lg p-3">
+							<div class="flex-1">
+								<h3 class="font-medium">
+									{item.product_details?.product_name ?? 'Nome Indisponível'}
+								</h3>
+								<p class="text-sm text-gray-400">
+									R$ {Number(item.product_details?.product_price ?? 0).toFixed(2)} x {item.quantity ??
+										1}
+								</p>
+							</div>
+							<div class="flex items-center space-x-2">
+								<button
+									on:click={() => removeItemFromOrder(tableIdNum, item.order_item_id)}
+									class="ml-2 rounded bg-red-700 p-1 hover:bg-red-600"
+								>
+									<img src={trash_icon} class="h-4 w-4" alt="Remover" />
+								</button>
+							</div>
+						</div>
+					{/each}
+				{/if}
+			</div>
+
+			<div class="bg-gray-750 border-t border-gray-700 p-4">
+				<div class="mb-2 flex justify-between">
+					<span class="font-medium">Subtotal:</span>
+					<span>R$ {totalAmount.toFixed(2)}</span>
+				</div>
+
+				<button
+					on:click={() => (isPaymentModalOpen = true)}
+					disabled={orderItems.length === 0}
+					class="flex w-full items-center justify-center rounded-lg bg-emerald-600 py-3 font-medium hover:bg-emerald-700 disabled:cursor-not-allowed disabled:opacity-50"
+				>
+					Fechar Atendimento
+				</button>
+			</div>
+		</div>
+	</div>
+
+	{#if isPaymentModalOpen}
+		<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-4">
+			<div class="w-full max-w-lg rounded-lg bg-gray-800 p-6 shadow-xl">
+				<h2 class="mb-4 text-xl font-bold">Finalizar Pedido</h2>
+
+				<div class="mb-6">
+					<h3 class="mb-3 text-lg font-medium">Resumo do Pedido</h3>
+					<div class="mb-4 max-h-60 overflow-y-auto sm:max-h-80">
+						{#each orderItems as item}
+							<div class="flex justify-between border-b border-gray-700 py-2">
+								<span
+									>{item.quantity ?? 1}x {item.product_details?.product_name ??
+										'Nome Indisponível'}</span
+								>
+								<span
+									>R$ {(
+										Number(item.product_details?.product_price ?? 0) * (item.quantity ?? 1)
+									).toFixed(2)}</span
+								>
+							</div>
+						{/each}
+					</div>
+					<div class="flex justify-between text-lg font-semibold">
+						<span>Total:</span>
+						<span>R$ {totalAmount.toFixed(2)}</span>
+					</div>
+				</div>
+
+				<h3 class="mb-3 text-lg font-medium">Forma de Pagamento</h3>
+				<div class="mb-6 grid grid-cols-1 gap-3 sm:grid-cols-2">
+					{#each ['CASH', 'PIX', 'DEBIT', 'CREDIT'] as method}
+						<button
+							class="flex flex-col items-center justify-center rounded-lg border-2 p-4 transition-colors {selectedPaymentMethod ===
+							method
+								? 'border-emerald-500 bg-emerald-900/20'
+								: 'border-gray-700 hover:border-gray-600'}"
+							on:click={() => (selectedPaymentMethod = method)}
+						>
+							<span
+								>{method === 'CASH'
+									? 'Dinheiro'
+									: method === 'PIX'
+										? 'Pix'
+										: method === 'DEBIT'
+											? 'Cart\u00e3o de D\u00e9bito'
+											: 'Cart\u00e3o de Cr\u00e9dito'}</span
+							>
+						</button>
+					{/each}
+				</div>
+
+				<div class="flex space-x-3">
+					<button
+						on:click={() => (isPaymentModalOpen = false)}
+						class="flex-1 rounded-lg bg-gray-700 py-3 font-medium hover:bg-gray-600"
+					>
+						Cancelar
+					</button>
+					<button
+						on:click={handlePaymentConfirm}
+						disabled={!selectedPaymentMethod}
+						class="flex-1 rounded-lg bg-emerald-600 py-3 font-medium hover:bg-emerald-700 disabled:cursor-not-allowed disabled:opacity-50"
+					>
+						Confirmar Pagamento
+					</button>
+				</div>
+			</div>
+		</div>
+	{/if}
+</div>

+ 11 - 0
src/routes/dashboard/endcommand/+page.svelte

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