diff --git a/gestaoRaul/balcao/__pycache__/htmx_views.cpython-312.pyc b/gestaoRaul/balcao/__pycache__/htmx_views.cpython-312.pyc index 2762430..47e2733 100644 Binary files a/gestaoRaul/balcao/__pycache__/htmx_views.cpython-312.pyc and b/gestaoRaul/balcao/__pycache__/htmx_views.cpython-312.pyc differ diff --git a/gestaoRaul/balcao/__pycache__/views.cpython-312.pyc b/gestaoRaul/balcao/__pycache__/views.cpython-312.pyc index e5fe050..718e315 100644 Binary files a/gestaoRaul/balcao/__pycache__/views.cpython-312.pyc and b/gestaoRaul/balcao/__pycache__/views.cpython-312.pyc differ diff --git a/gestaoRaul/balcao/htmx_views.py b/gestaoRaul/balcao/htmx_views.py index 9b608e0..5ab531a 100644 --- a/gestaoRaul/balcao/htmx_views.py +++ b/gestaoRaul/balcao/htmx_views.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import User -from comandas.models import Comanda, ProductComanda +from comandas.models import Comanda, ProductComanda, StockMovementType, StockMovement from mesas.models import Mesa from products.models import Product from payments.models import Payments @@ -30,6 +30,20 @@ def addProductBalcao(request, product_id, comanda_id, qtd): for i in range(qtd): product_comanda = ProductComanda(comanda_id=comanda_id, product_id=product_id) product_comanda.save() + product = Product.objects.get(id=product_id) + comanda = Comanda.objects.get(id=comanda_id) + user = User.objects.get(id=request.user.id) + typeMovement = StockMovementType.objects.get(name="Venda - Balcao") + + StockMovement.subTransactionStock( + product=product, + movement_type=typeMovement, + comanda=comanda, + user=user, + qtd=1, + obs= "Vendido no balcão" + ) + consumo = ProductComanda.objects.filter(comanda=comanda_id) total = 0 for produto in consumo: @@ -42,6 +56,20 @@ def addProductBalcaoTeclado(request, product_id, comanda_id, qtd): for i in range(qtd): product_comanda = ProductComanda(comanda_id=comanda_id, product_id=product_id) product_comanda.save() + product = Product.objects.get(id=product_id) + comanda = Comanda.objects.get(id=comanda_id) + user = User.objects.get(id=request.user.id) + typeMovement = StockMovementType.objects.get(name="Venda - Balcao") + + StockMovement.subTransactionStock( + product=product, + movement_type=typeMovement, + comanda=comanda, + user=user, + qtd=1, + obs= "Vendido no balcão" + ) + consumo = ProductComanda.objects.filter(comanda=comanda_id) total = 0 for produto in consumo: @@ -51,8 +79,24 @@ def addProductBalcaoTeclado(request, product_id, comanda_id, qtd): @group_required(groupName='Garçom') def removeProductBalcao(request, productComanda_id): product_comanda = ProductComanda.objects.get(id=productComanda_id) + comanda = product_comanda.comanda + product = product_comanda.product + user = User.objects.get(id=request.user.id) + typeMovement = StockMovementType.objects.get(name="Estorno") + consumo = ProductComanda.objects.filter(comanda=product_comanda.comanda) + StockMovement.sumTransactionStock( + product=product, + movement_type=typeMovement, + comanda=comanda, + user=user, + qtd=1, + obs= "Excluido do balcão" + ) + + product_comanda.delete() + total = 0 for produto in consumo: total += produto.product.price diff --git a/gestaoRaul/comandas/__pycache__/admin.cpython-312.pyc b/gestaoRaul/comandas/__pycache__/admin.cpython-312.pyc index ac25a4d..30aaef4 100644 Binary files a/gestaoRaul/comandas/__pycache__/admin.cpython-312.pyc and b/gestaoRaul/comandas/__pycache__/admin.cpython-312.pyc differ diff --git a/gestaoRaul/comandas/__pycache__/htmx_views.cpython-312.pyc b/gestaoRaul/comandas/__pycache__/htmx_views.cpython-312.pyc index d3dc32f..c57a7db 100644 Binary files a/gestaoRaul/comandas/__pycache__/htmx_views.cpython-312.pyc and b/gestaoRaul/comandas/__pycache__/htmx_views.cpython-312.pyc differ diff --git a/gestaoRaul/comandas/__pycache__/models.cpython-312.pyc b/gestaoRaul/comandas/__pycache__/models.cpython-312.pyc index f02c04a..793c12e 100644 Binary files a/gestaoRaul/comandas/__pycache__/models.cpython-312.pyc and b/gestaoRaul/comandas/__pycache__/models.cpython-312.pyc differ diff --git a/gestaoRaul/comandas/__pycache__/urls.cpython-312.pyc b/gestaoRaul/comandas/__pycache__/urls.cpython-312.pyc index 60191b4..706f463 100644 Binary files a/gestaoRaul/comandas/__pycache__/urls.cpython-312.pyc and b/gestaoRaul/comandas/__pycache__/urls.cpython-312.pyc differ diff --git a/gestaoRaul/comandas/__pycache__/views.cpython-312.pyc b/gestaoRaul/comandas/__pycache__/views.cpython-312.pyc index b055aa7..53182cc 100644 Binary files a/gestaoRaul/comandas/__pycache__/views.cpython-312.pyc and b/gestaoRaul/comandas/__pycache__/views.cpython-312.pyc differ diff --git a/gestaoRaul/comandas/admin.py b/gestaoRaul/comandas/admin.py index c0dd1c6..d27670a 100644 --- a/gestaoRaul/comandas/admin.py +++ b/gestaoRaul/comandas/admin.py @@ -1,7 +1,9 @@ from django.contrib import admin -from comandas.models import Comanda, ProductComanda +from comandas.models import Comanda, ProductComanda, StockMovement, StockMovementType admin.site.register(Comanda) admin.site.register(ProductComanda) +admin.site.register(StockMovementType) +admin.site.register(StockMovement) diff --git a/gestaoRaul/comandas/htmx_views.py b/gestaoRaul/comandas/htmx_views.py index 1787fee..392b63c 100644 --- a/gestaoRaul/comandas/htmx_views.py +++ b/gestaoRaul/comandas/htmx_views.py @@ -2,8 +2,10 @@ from datetime import date from decimal import Decimal from django.http import JsonResponse from django.shortcuts import render, redirect +from django.contrib.auth.models import User -from comandas.models import Comanda, ProductComanda + +from comandas.models import Comanda, ProductComanda, StockMovementType, StockMovement from orders.models import Order from products.models import Product from payments.models import Payments, somar @@ -37,14 +39,28 @@ def removeProductComanda(request, productComanda_id): 'taxa': False } product_comanda = ProductComanda.objects.get(id=productComanda_id) - comanda = Comanda.objects.get(id= product_comanda.comanda.id) + comanda = product_comanda.comanda + product = product_comanda.product + user = User.objects.get(id=request.user.id) + typeMovement = StockMovementType.objects.get(name="Estorno") + if comanda.status == 'OPEN': parcial = Payments.objects.filter(comanda=comanda) consumo = ProductComanda.objects.filter(comanda=comanda) valores = somar(consumo,comanda) if product_comanda.product.cuisine == True: order = Order.objects.get(productComanda=product_comanda) + + StockMovement.sumTransactionStock( + product=product, + movement_type=typeMovement, + comanda=comanda, + user=user, + qtd=1, + obs= "Excluido da comanda" + ) product_comanda.delete() + msg = JsonResponse({ 'type': 'broadcast', 'message': 'Atenção! Pedido cancelado', @@ -58,6 +74,14 @@ def removeProductComanda(request, productComanda_id): consumo = ProductComanda.objects.filter(comanda=comanda) valores = somar(consumo,comanda) else: + StockMovement.sumTransactionStock( + product=product, + movement_type=typeMovement, + comanda=comanda, + user=user, + qtd=1, + obs= "Excluido da comanda" + ) product_comanda.delete() consumo = ProductComanda.objects.filter(comanda=comanda) valores = somar(consumo,comanda) diff --git a/gestaoRaul/comandas/migrations/0005_stockmovementtype_stockmovement.py b/gestaoRaul/comandas/migrations/0005_stockmovementtype_stockmovement.py new file mode 100644 index 0000000..1dd6d62 --- /dev/null +++ b/gestaoRaul/comandas/migrations/0005_stockmovementtype_stockmovement.py @@ -0,0 +1,42 @@ +# Generated by Django 5.1.4 on 2025-07-22 18:36 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('comandas', '0004_comanda_user'), + ('products', '0004_unitofmeasure_productcomponent_product_components_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='StockMovementType', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('name', models.CharField(help_text="Ex: 'Entrada por Compra', 'Saída por Venda', 'Ajuste de Estoque'", max_length=100, unique=True)), + ('observation', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.CreateModel( + name='StockMovement', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('quantity', models.IntegerField(help_text='Quantidade movimentada. Use valores negativos para saídas.')), + ('observation', models.TextField(blank=True, null=True)), + ('movement_date', models.DateTimeField(auto_now_add=True)), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stock_movements', to='products.product')), + ('related_comanda', models.ForeignKey(blank=True, help_text='Comanda relacionada à movimentação (opcional).', null=True, on_delete=django.db.models.deletion.SET_NULL, to='comandas.comanda')), + ('user', models.ForeignKey(blank=True, help_text='Usuário que realizou a movimentação.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ('movement_type', models.ForeignKey(help_text='Tipo de movimentação (entrada, saída, ajuste).', on_delete=django.db.models.deletion.PROTECT, to='comandas.stockmovementtype')), + ], + options={ + 'ordering': ['-movement_date'], + }, + ), + ] diff --git a/gestaoRaul/comandas/migrations/__pycache__/0005_stockmovementtype_stockmovement.cpython-312.pyc b/gestaoRaul/comandas/migrations/__pycache__/0005_stockmovementtype_stockmovement.cpython-312.pyc new file mode 100644 index 0000000..a1c63ed Binary files /dev/null and b/gestaoRaul/comandas/migrations/__pycache__/0005_stockmovementtype_stockmovement.cpython-312.pyc differ diff --git a/gestaoRaul/comandas/models.py b/gestaoRaul/comandas/models.py index 6e6bf59..b1919c7 100644 --- a/gestaoRaul/comandas/models.py +++ b/gestaoRaul/comandas/models.py @@ -5,7 +5,7 @@ from django.db.models import Count, F from clients.models import Client -from products.models import Product +from products.models import Product, ProductComponent from mesas.models import Mesa from typePay.models import TypePay @@ -43,4 +43,109 @@ class ProductComanda(models.Model): for p in products: if p.name == produto['nome'] and p.active == True: products_ordenados.append(p) - return products_ordenados[:15] \ No newline at end of file + return products_ordenados[:15] + + +class StockMovementType(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=100, unique=True, help_text="Ex: 'Entrada por Compra', 'Saída por Venda', 'Ajuste de Estoque'") + observation = models.TextField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.name + +class StockMovement(models.Model): + id = models.AutoField(primary_key=True) + product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='stock_movements') + user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, help_text="Usuário que realizou a movimentação.") + related_comanda = models.ForeignKey( + Comanda, + on_delete=models.SET_NULL, + null=True, + blank=True, + help_text="Comanda relacionada à movimentação (opcional)." + ) + movement_type = models.ForeignKey(StockMovementType, on_delete=models.PROTECT, help_text="Tipo de movimentação (entrada, saída, ajuste).") + quantity = models.IntegerField(help_text="Quantidade movimentada. Use valores negativos para saídas.") + observation = models.TextField(null=True, blank=True) + movement_date = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return ( + f"Movimentação de {self.quantity} de {self.product.name} " + f"({self.movement_type.name}) em {self.movement_date.strftime('%d/%m/%Y %H:%M')}" + ) + + + def subTransactionStock(product:Product, + movement_type:StockMovementType, + comanda:Comanda, + obs:str, + user:User=None, + qtd:int=1): + + components = ProductComponent.objects.filter(composite_product=product) + if components.exists(): + for component in components: + movi = StockMovement.objects.create( + product=component.component_product , + related_comanda=comanda, + user=user, + movement_type=movement_type, + quantity=-component.quantity_required, + observation=obs + ) + movi.save() + component.component_product.quantity -= component.quantity_required + component.component_product.save() + + movi = StockMovement.objects.create( + product=product , + related_comanda=comanda, + user=user, + movement_type=movement_type, + quantity=-qtd, + observation=obs + ) + movi.save() + product.quantity -= qtd + product.save() + + + def sumTransactionStock(product:Product, + movement_type:StockMovementType, + comanda:Comanda, + obs:str, + user:User=None, + qtd:int=1): + + components = ProductComponent.objects.filter(composite_product=product) + if components.exists(): + for component in components: + movi = StockMovement.objects.create( + product=component.component_product , + related_comanda=comanda, + user=user, + movement_type=movement_type, + quantity=component.quantity_required, + observation=obs + ) + movi.save() + component.component_product.quantity += component.quantity_required + component.component_product.save() + + movi = StockMovement.objects.create( + product=product , + related_comanda=comanda, + user=user, + movement_type=movement_type, + quantity=qtd, + observation=obs + ) + movi.save() + product.quantity += qtd + product.save() + + class Meta: + ordering = ['-movement_date'] \ No newline at end of file diff --git a/gestaoRaul/comandas/templates/viewcomanda.html b/gestaoRaul/comandas/templates/viewcomanda.html index 8d6ff2b..920a66a 100644 --- a/gestaoRaul/comandas/templates/viewcomanda.html +++ b/gestaoRaul/comandas/templates/viewcomanda.html @@ -50,7 +50,7 @@ Detalhes {{comanda.name}}
- - + Cardápio Digital
- - - - - - - - - - {% for product in products %} +
ProdutoPreçoQuantidadeCategoriaAções
+ + + + + + + + {% for product in products %} + + + - - - - - - - - + {% else %} + + {% endif %} + + + - - - {% endfor %} - -
ProdutoPreçoQuantidadeCategoriaAções
{{product.name}}R$ {{product.price}}
{{product.name}}R$ {{product.price}}{{product.quantity}}{{product.category.name}} -
- - + {% if product.quantity > 20 %} +
{{product.quantity}}{{product.quantity}}{{product.category.name}} +
+ + -
+ {% csrf_token %} - + - {% if product.active == True %} - - {% else %} - - - {% endif %} - -
-
-
+ {% if product.active == True %} + + {% else %} + + {% endif %} + +
+ + + {% endfor %} + + diff --git a/gestaoRaul/products/views.py b/gestaoRaul/products/views.py index d06cff5..1746789 100644 --- a/gestaoRaul/products/views.py +++ b/gestaoRaul/products/views.py @@ -1,9 +1,11 @@ from django.shortcuts import render, redirect from django.http import HttpResponse import json -from django.db.models import Q +from django.db.models import Q, Count, F + from categories.models import Categories +from comandas.models import ProductComanda from products.models import Product from gestaoRaul.decorators import group_required @@ -65,6 +67,10 @@ def editProduct(request, productId): return render(request, "htmx_components/products/htmx_search_products.html", {"products": products}) def createJson(request): + produtos_mais_vendidos = list(ProductComanda.objects.values('product').annotate( + quantidade=Count('product'), + nome=F('product__name') ).order_by('-quantidade')) + products = Product.objects.filter(active=True).exclude( category__name__in=['Insumos', 'Adicional', @@ -76,8 +82,21 @@ def createJson(request): 'Cigarros', 'Outros'] ) + + + + + products_ordenados = [] + for produto in produtos_mais_vendidos: + for p in products: + if p.name == produto['nome']: + products_ordenados.append(p) + + + + product_list = [] - for product in products: + for product in products_ordenados: product_data = { "id": product.id, "name": product.name, diff --git a/gestaoRaul/templates/htmx_components/products/htmx_search_products.html b/gestaoRaul/templates/htmx_components/products/htmx_search_products.html index 8fe4717..b94740d 100644 --- a/gestaoRaul/templates/htmx_components/products/htmx_search_products.html +++ b/gestaoRaul/templates/htmx_components/products/htmx_search_products.html @@ -1,64 +1,160 @@ {% load static %} - - Produto - Preço - Quantidade - Categoria - Ações - + + Produto + Preço + Quantidade + Categoria + Ações + + {% for product in products %} + + {{product.name}} + R$ {{product.price}} -{% for product in products %} + {% if product.quantity > 20 %} + {{product.quantity}} + {% else %} + {{product.quantity}} + {% endif %} + {{product.image}} + {{product.category.name}} + +
+ + - -{{product.name}} -R$ {{product.price}} -{{product.quantity}} -{{product.category.name}} -{{product.image}} + + + - -
- - - - +
+ {% csrf_token %} + + + {% if product.active == True %} + + {% else %} + + {% endif %} +
+
+ + + {% endfor %} + + + + \ No newline at end of file diff --git a/gestaoRaul/templates/static/base.css b/gestaoRaul/templates/static/base.css index 8bf7652..20f51c7 100644 --- a/gestaoRaul/templates/static/base.css +++ b/gestaoRaul/templates/static/base.css @@ -330,9 +330,9 @@ white-space: nowrap; .toast { position: fixed; - top: 40px; + top: 4px; width: 30%; - left: 50%; + left: 30%; justify-items: center; align-items: center; transform: translateX(-50%); @@ -344,7 +344,7 @@ white-space: nowrap; opacity: 0; visibility: hidden; transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out; - z-index: 999 ; + z-index: 999999999999 !important; } .toast p { @@ -462,4 +462,11 @@ white-space: nowrap; body { margin: 8px 0px 0px 0px; } + + .toast { + position: fixed; + top: 5px; + width: 70%; + left: 40%; +} } diff --git a/gestaoRaul/templates/static/base.js b/gestaoRaul/templates/static/base.js index 04c9f03..3f2441a 100644 --- a/gestaoRaul/templates/static/base.js +++ b/gestaoRaul/templates/static/base.js @@ -19,115 +19,86 @@ document.addEventListener('DOMContentLoaded', function() { -function verificarCookieNotificacao() { - var iconNotify = document.getElementById('icon-notify'); - if (document.cookie.indexOf('notificacao=') === -1) { - document.cookie = 'notificacao=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/'; - iconNotify.style.backgroundColor = 'green'; - iconNotify.textContent = '🔊'; - }else{ - let valorAtual = document.cookie.replace(/(?:(?:^|.*;\s*)notificacao\s*\=\s*([^;]*).*$)|^.*$/, "$1"); - iconNotify.style.backgroundColor = valorAtual === 'true' ? 'green' : 'red'; - iconNotify.textContent = valorAtual === 'true' ? '🔊' : '🔇'; - } -} -verificarCookieNotificacao(); - -function cookieNotificacao() { - if (document.cookie.indexOf('notificacao=') !== -1) { - let valorAtual = document.cookie.replace(/(?:(?:^|.*;\s*)notificacao\s*\=\s*([^;]*).*$)|^.*$/, "$1"); - var iconNotify = document.getElementById('icon-notify'); - let novoValor = valorAtual === 'true' ? 'false' : 'true'; - if (novoValor === 'true') { - iconNotify.style.backgroundColor = 'green'; - iconNotify.textContent = '🔊'; - }else{ - iconNotify.style.backgroundColor = 'red'; - iconNotify.textContent = '🔇'; - } - document.cookie = 'notificacao=' + novoValor + '; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/'; - } else { - document.cookie = 'notificacao=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/'; - } -} - -const websocket = new WebSocket('ws://192.168.0.150:8765'); -const nomeUsuario = document.getElementById('user-info').textContent; - -websocket.addEventListener('open', (event) => { - console.log('Conectado ao servidor WebSocket'); -}); - -websocket.addEventListener('message', (event) => { - const data = JSON.parse(event.data); - - switch (data.local) { - case 'cozinha': - - if (document.getElementById('Fila') !== null && data.tipo === 'add'){ - var novoElemento = document.createElement('div'); - novoElemento.innerHTML = data.message; - document.getElementById('Fila').appendChild(novoElemento); - let valorAtual = document.cookie.replace(/(?:(?:^|.*;\s*)notificacao\s*\=\s*([^;]*).*$)|^.*$/, "$1"); - if (valorAtual === 'true') { - texto = new SpeechSynthesisUtterance(data.speak); - window.speechSynthesis.speak(texto); - // setTimeout(function() { - // location.reload(); - // }, 6000); - } - } - else if (document.getElementById('obs-'+data.id) !== null && data.tipo === 'edit'){ - const obs = document.getElementById('obs-'+data.id) - const card = obs.parentNode; - card.style.backgroundColor = 'rgb(243, 165, 75)'; - obs.innerHTML = data.message; - let valorAtual = document.cookie.replace(/(?:(?:^|.*;\s*)notificacao\s*\=\s*([^;]*).*$)|^.*$/, "$1"); - if (valorAtual === 'true') { - texto = new SpeechSynthesisUtterance(data.speak); - window.speechSynthesis.speak(texto); - // setTimeout(function() { - // location.reload(); - // }, 6000); - } - } - else if (document.getElementById('m-card-'+data.id) !== null && data.tipo === 'delete'){ - // const card = document.getElementById('m-card-'+data.id) - // card.style.backgroundColor = 'rgb(253, 69, 69)'; - let valorAtual = document.cookie.replace(/(?:(?:^|.*;\s*)notificacao\s*\=\s*([^;]*).*$)|^.*$/, "$1"); - if (valorAtual === 'true') { - texto = new SpeechSynthesisUtterance(data.speak); - window.speechSynthesis.speak(texto); - setTimeout(function() { - location.reload(); - }, 6000); - } - } +// const websocket = new WebSocket('ws://192.168.0.150:8765'); +// const nomeUsuario = document.getElementById('user-info').textContent; + +// websocket.addEventListener('open', (event) => { +// console.log('Conectado ao servidor WebSocket'); +// }); + +// websocket.addEventListener('message', (event) => { +// const data = JSON.parse(event.data); + +// switch (data.local) { +// case 'cozinha': + +// if (document.getElementById('Fila') !== null && data.tipo === 'add'){ + + +// var novoElemento = document.createElement('div'); +// novoElemento.innerHTML = data.message; +// document.getElementById('Fila').appendChild(novoElemento); +// let valorAtual = document.cookie.replace(/(?:(?:^|.*;\s*)notificacao\s*\=\s*([^;]*).*$)|^.*$/, "$1"); +// if (valorAtual === 'true') { +// texto = new SpeechSynthesisUtterance(data.speak); +// window.speechSynthesis.speak(texto); +// // setTimeout(function() { +// // location.reload(); +// // }, 6000); +// } +// } +// else if (document.getElementById('obs-'+data.id) !== null && data.tipo === 'edit'){ +// const obs = document.getElementById('obs-'+data.id) +// const card = obs.parentNode; +// card.style.backgroundColor = 'rgb(243, 165, 75)'; +// obs.innerHTML = data.message; +// let valorAtual = document.cookie.replace(/(?:(?:^|.*;\s*)notificacao\s*\=\s*([^;]*).*$)|^.*$/, "$1"); + +// if (valorAtual === 'true') { +// texto = new SpeechSynthesisUtterance(data.speak); +// window.speechSynthesis.speak(texto); +// // setTimeout(function() { +// // location.reload(); +// // }, 6000); +// } +// } +// else if (document.getElementById('m-card-'+data.id) !== null && data.tipo === 'delete'){ +// // const card = document.getElementById('m-card-'+data.id) +// // card.style.backgroundColor = 'rgb(253, 69, 69)'; +// let valorAtual = document.cookie.replace(/(?:(?:^|.*;\s*)notificacao\s*\=\s*([^;]*).*$)|^.*$/, "$1"); +// if (valorAtual === 'true') { +// texto = new SpeechSynthesisUtterance(data.speak); +// window.speechSynthesis.speak(texto); +// setTimeout(function() { +// location.reload(); +// }, 6000); +// } +// } - break; - case 'praca': - console.log('Mensagem para a praca:', data); - break; - case 'guarita': - console.log('Mensagem para a guarita:', data); - break; - case 'balcao': - console.log('Mensagem para a balcao:', data); - break; - default: - console.log('Local desconhecido:', data); - } +// break; +// case 'praca': +// console.log('Mensagem para a praca:', data); +// break; +// case 'guarita': +// console.log('Mensagem para a guarita:', data); +// break; +// case 'balcao': +// console.log('Mensagem para a balcao:', data); +// break; +// default: +// console.log('Local desconhecido:', data); +// } -}); -websocket.addEventListener('error', (event) => { - console.error('Erro no WebSocket:', event); -}); -websocket.addEventListener('close', (event) => { - console.log("conexão fechada"); -}); +// }); +// websocket.addEventListener('error', (event) => { +// console.error('Erro no WebSocket:', event); +// }); +// websocket.addEventListener('close', (event) => { +// console.log("conexão fechada"); +// }); @@ -154,7 +125,7 @@ function openFullscreen() { } } - function showToast(message, type ,duration = 3000) { + function showToast(message, type ,duration = 2500) { const toast = document.getElementById('toast'); if (type === 'success') { diff --git a/gestaoRaul/templates/static/comandas/js/viewcomanda.js b/gestaoRaul/templates/static/comandas/js/viewcomanda.js index a9cc624..611bad9 100644 --- a/gestaoRaul/templates/static/comandas/js/viewcomanda.js +++ b/gestaoRaul/templates/static/comandas/js/viewcomanda.js @@ -1,5 +1,5 @@ -async function openModal() { +async function modalAddProduct() { var htmlModal = document.getElementById('addProduct').innerHTML htmlModal = htmlModal.replace('search-product','search-product-modal') htmlModal = htmlModal.replace('product-list','product-list-modal') @@ -364,15 +364,7 @@ async function addProductComanda(productId, comandaId, cuisine) { throw new Error('Token de segurança não encontrado'); } - // if (cuisine === 'ggg') { - // openModalObs(); - // return; - // } - // Mostra estado de carregamento - Swal.update({ - title: 'Adicionando produto...', - }); // Requisição POST const response = await fetch(`/comandas/product=${productId}/comanda=${comandaId}/`, { @@ -403,6 +395,8 @@ async function addProductComanda(productId, comandaId, cuisine) { } const result = await response.text(); + console.log(response); + showToast('Produto adicionado com sucesso!', 'success'); // Atualiza a lista de produtos const listElement = document.getElementById("list-products-comanda"); @@ -410,17 +404,11 @@ async function addProductComanda(productId, comandaId, cuisine) { listElement.innerHTML = result; } + // if (cuisine === 'True') { + // openModalObs(); + // return; + // } - Swal.update({ - title: '✅ Produto adicionado!', - }); - - - setTimeout(() => { - Swal.update({ - title: 'Adicionar Produto' - }); - }, 2500); } catch (error) { console.error('Erro:', error); diff --git a/gestaoRaul/templates/static/products/css/products.css b/gestaoRaul/templates/static/products/css/products.css index 38dbe28..80ab60b 100644 --- a/gestaoRaul/templates/static/products/css/products.css +++ b/gestaoRaul/templates/static/products/css/products.css @@ -50,6 +50,39 @@ background-color: rgba(0, 0, 0, 0.6); } + th { + background-color: #170e4f; + cursor: pointer; + position: relative; + } + + th.sorted-asc::after { + content: " ▲"; + font-size: 0.8em; + vertical-align: super; + } + th.sorted-desc::after { + content: " ▼"; + font-size: 0.8em; + vertical-align: super; + } + + #product-list { + width: 100%; + border-collapse: collapse; +} + +#product-list th, +#product-list td { + padding: 8px; + text-align: left; +} + +#product-list th { + position: sticky; + top: 60px; + z-index: 10; +} @media (max-width: 768px) { .hide-on-mobile { @@ -62,4 +95,9 @@ max-width: 100px; width: 80px; } + #product-list th { + position: sticky; + top: 0px; + z-index: 10; +} } \ No newline at end of file diff --git a/gestaoRaul/templates/static/products/js/products.js b/gestaoRaul/templates/static/products/js/products.js index 0a18682..f9fd4e6 100644 --- a/gestaoRaul/templates/static/products/js/products.js +++ b/gestaoRaul/templates/static/products/js/products.js @@ -81,16 +81,96 @@ function editProduct(id) { // }) } -// document.getElementById('openModal').addEventListener('click', openModal); -// document.getElementById('productForm').addEventListener('submit', function(event) { -// event.preventDefault(); + +function listerSortTeable(){ + - // const productName = document.getElementById('productName').value; - // const productPrice = document.getElementById('productPrice').value; - // const productDescription = document.getElementById('productDescription').value; - // closeModal(); -// } -// ); - \ No newline at end of file +// document.addEventListener('DOMContentLoaded', function() { + const table = document.getElementById('product-list'); + const headers = table.querySelectorAll('th'); + const tbody = table.querySelector('tbody'); // Seleciona o corpo da tabela + + let currentSortColumn = -1; // Armazena o índice da coluna atualmente ordenada + let sortDirection = 'asc'; // 'asc' para ascendente, 'desc' para descendente + + // Adiciona um ouvinte de evento de clique a cada cabeçalho de coluna + headers.forEach((header, index) => { + if (header.textContent.trim() !== 'Ações') { + header.addEventListener('click', () => { + // Se a mesma coluna for clicada, inverte a direção da ordenação + if (currentSortColumn === index) { + sortDirection = (sortDirection === 'asc') ? 'desc' : 'asc'; + } else { + // Se uma nova coluna for clicada, define como a coluna atual + // e inicia a ordenação ascendente + currentSortColumn = index; + sortDirection = 'asc'; + } + + // Remove as classes de ordenação de todos os cabeçalhos + headers.forEach(h => { + h.classList.remove('sorted-asc', 'sorted-desc'); + }); + + // Adiciona a classe de ordenação ao cabeçalho clicado para visualização + header.classList.add(`sorted-${sortDirection}`); + + // Chama a função de ordenação + sortColumn(index, sortDirection, header.dataset.colType); + }); + } + }) + + function sortColumn(columnIndex, direction, columnType) { + // Converte os NodeList de linhas em um Array para poder usar sort() + const rows = Array.from(tbody.querySelectorAll('tr')); + + rows.sort((rowA, rowB) => { + // Obtém o texto da célula na coluna clicada para ambas as linhas + let valueA = rowA.children[columnIndex].textContent.trim(); + let valueB = rowB.children[columnIndex].textContent.trim(); + + // Trata valores específicos para colunas como "Preço" que têm "R$" + if (columnIndex === 1) { // Índice da coluna "Preço" + valueA = valueA.replace('R$', '').trim(); + valueB = valueB.replace('R$', '').trim(); + } + + // Converte para número se o tipo da coluna for "number" + // ou se for a coluna de "Preço" após remover "R$" + if (columnType === 'number' || columnIndex === 1) { + valueA = parseFloat(valueA); + valueB = parseFloat(valueB); + // Garante que NaN (Not a Number) sejam tratados para evitar problemas de ordenação + valueA = isNaN(valueA) ? -Infinity : valueA; + valueB = isNaN(valueB) ? -Infinity : valueB; + } else { + // Para texto, use localeCompare para ordenação correta com caracteres acentuados + // e torna minúsculo para ordenação case-insensitive + valueA = valueA.toLowerCase(); + valueB = valueB.toLowerCase(); + } + + let comparison = 0; + if (valueA > valueB) { + comparison = 1; + } else if (valueA < valueB) { + comparison = -1; + } + + // Aplica a direção da ordenação + return (direction === 'asc') ? comparison : -comparison; + }); + + // Remove todas as linhas existentes e adiciona as linhas ordenadas de volta ao tbody + while (tbody.firstChild) { + tbody.removeChild(tbody.firstChild); + } + rows.forEach(row => tbody.appendChild(row)); + } +// }); +} + +listerSortTeable(); \ No newline at end of file