fix(products): ordenar produtos do maior para o menor preco, permitir limpar input de preco/stock sem ficar negativo, e impedir adicionar mais quantidade ao carrinho do que o stock disponivel
This commit is contained in:
26
web/check_notifications.js
Normal file
26
web/check_notifications.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
const supabaseUrl = 'https://jqklhhpyykzrktikjnmb.supabase.co'
|
||||
const supabaseAnonKey =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Impxa2xoaHB5eWt6cmt0aWtqbm1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjgzODQ0MDgsImV4cCI6MjA4Mzk2MDQwOH0.QsPuBnyUtRPSavlqKj3IGR9c8juT02LY_hSi-j3c6M0'
|
||||
|
||||
const supabase = createClient(supabaseUrl, supabaseAnonKey)
|
||||
|
||||
async function test() {
|
||||
console.log('Testing notifications table insert...')
|
||||
|
||||
const { data, error } = await supabase.from('notifications').insert([
|
||||
{
|
||||
user_id: '00000000-0000-0000-0000-000000000000',
|
||||
message: 'test message'
|
||||
}
|
||||
]).select()
|
||||
|
||||
if (error) {
|
||||
console.error('Insert error:', error)
|
||||
} else {
|
||||
console.log('Insert success:', data)
|
||||
}
|
||||
}
|
||||
|
||||
test()
|
||||
19
web/check_profiles.js
Normal file
19
web/check_profiles.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
const supabaseUrl = 'https://jqklhhpyykzrktikjnmb.supabase.co';
|
||||
const supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Impxa2xoaHB5eWt6cmt0aWtqbm1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjgzODQ0MDgsImV4cCI6MjA4Mzk2MDQwOH0.QsPuBnyUtRPSavlqKj3IGR9c8juT02LY_hSi-j3c6M0';
|
||||
|
||||
const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
||||
|
||||
async function check() {
|
||||
const { data: testData, error: testError } = await supabase.from('profiles').select('*').limit(1);
|
||||
if (testError) {
|
||||
console.log("SELECT ERROR:", testError.message);
|
||||
} else {
|
||||
console.log("COLUMNS EXIST IN PROFILES:", testData && testData.length > 0 ? Object.keys(testData[0]) : "No rows");
|
||||
console.log("SAMPLE ROW:", testData);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
check();
|
||||
@@ -13,7 +13,7 @@ export const ProductList = ({
|
||||
onAdd?: (id: string) => void;
|
||||
}) => (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3 sm:gap-6">
|
||||
{products.map((p) => {
|
||||
{[...products].sort((a, b) => b.price - a.price).map((p) => {
|
||||
const lowStock = p.stock <= 3;
|
||||
return (
|
||||
<Card key={p.id} className="relative overflow-hidden border-none glass-card rounded-2xl sm:rounded-[2rem] group hover:shadow-2xl transition-all duration-300 flex flex-col">
|
||||
|
||||
@@ -500,6 +500,34 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
};
|
||||
|
||||
const addToCart: AppContextValue['addToCart'] = (item) => {
|
||||
if (item.type === 'product') {
|
||||
const shop = state.shops.find((shp) => shp.id === item.shopId);
|
||||
const prod = shop?.products.find((p) => p.id === item.refId);
|
||||
if (prod) {
|
||||
const maxStock = prod.stock;
|
||||
const existingItem = state.cart.find((c) => c.refId === item.refId && c.type === 'product' && c.shopId === item.shopId);
|
||||
const currentQty = existingItem ? existingItem.qty : 0;
|
||||
|
||||
if (currentQty >= maxStock) {
|
||||
addToast(`Não é possível adicionar mais unidades. Apenas ${maxStock} unidades em stock.`, 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentQty + item.qty > maxStock) {
|
||||
const allowedQty = maxStock - currentQty;
|
||||
setState((s) => {
|
||||
const cart = [...s.cart];
|
||||
const idx = cart.findIndex((c) => c.refId === item.refId && c.type === item.type && c.shopId === item.shopId);
|
||||
if (idx >= 0) cart[idx].qty = maxStock;
|
||||
else cart.push({ ...item, qty: allowedQty });
|
||||
return { ...s, cart };
|
||||
});
|
||||
addToast(`Adicionado apenas ${allowedQty} unidades. Limite de stock atingido.`, 'info');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setState((s) => {
|
||||
const cart = [...s.cart];
|
||||
const idx = cart.findIndex((c) => c.refId === item.refId && c.type === item.type && c.shopId === item.shopId);
|
||||
|
||||
@@ -141,8 +141,8 @@ function DashboardInner({ shop }: { shop: BarberShop }) {
|
||||
const [svcDuration, setSvcDuration] = useState<number>(30);
|
||||
|
||||
const [prodName, setProdName] = useState('');
|
||||
const [prodPrice, setProdPrice] = useState<number>(30);
|
||||
const [prodStock, setProdStock] = useState<number>(10);
|
||||
const [prodPrice, setProdPrice] = useState<string>('30');
|
||||
const [prodStock, setProdStock] = useState<string>('10');
|
||||
|
||||
const [barberName, setBarberName] = useState('');
|
||||
const [barberSpecs, setBarberSpecs] = useState('');
|
||||
@@ -296,10 +296,20 @@ function DashboardInner({ shop }: { shop: BarberShop }) {
|
||||
|
||||
const addNewProduct = () => {
|
||||
if (!prodName.trim()) return;
|
||||
addProduct(shop.id, { name: prodName, price: Number(prodPrice) || 0, stock: Number(prodStock) || 0 });
|
||||
const priceNum = parseFloat(prodPrice);
|
||||
const stockNum = parseInt(prodStock, 10);
|
||||
if (isNaN(priceNum) || priceNum < 0) {
|
||||
alert('O preço deve ser um número positivo.');
|
||||
return;
|
||||
}
|
||||
if (isNaN(stockNum) || stockNum < 0) {
|
||||
alert('O stock deve ser um número positivo.');
|
||||
return;
|
||||
}
|
||||
addProduct(shop.id, { name: prodName, price: priceNum, stock: stockNum });
|
||||
setProdName('');
|
||||
setProdPrice(30);
|
||||
setProdStock(10);
|
||||
setProdPrice('30');
|
||||
setProdStock('10');
|
||||
};
|
||||
|
||||
const addNewBarber = () => {
|
||||
@@ -1116,7 +1126,7 @@ function DashboardInner({ shop }: { shop: BarberShop }) {
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-3 mb-6">
|
||||
{shop.products.map((p) => (
|
||||
{[...shop.products].sort((a, b) => b.price - a.price).map((p) => (
|
||||
<div
|
||||
key={p.id}
|
||||
className={`flex items-center justify-between p-4 border rounded-lg ${p.stock <= 3 ? 'border-indigo-300 bg-indigo-50' : 'border-slate-200'
|
||||
@@ -1150,8 +1160,33 @@ function DashboardInner({ shop }: { shop: BarberShop }) {
|
||||
<h3 className="text-base font-semibold text-slate-900 mb-3">Adicionar novo produto</h3>
|
||||
<div className="grid md:grid-cols-4 gap-3">
|
||||
<Input label="Nome" placeholder="Ex: Pomada" value={prodName} onChange={(e) => setProdName(e.target.value)} />
|
||||
<Input label="Preço" type="number" placeholder="30" value={prodPrice} onChange={(e) => setProdPrice(Number(e.target.value))} />
|
||||
<Input label="Stock inicial" type="number" placeholder="10" value={prodStock} onChange={(e) => setProdStock(Number(e.target.value))} />
|
||||
<Input
|
||||
label="Preço"
|
||||
type="number"
|
||||
placeholder="30"
|
||||
min="0"
|
||||
step="0.01"
|
||||
value={prodPrice}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val === '' || parseFloat(val) >= 0) {
|
||||
setProdPrice(val);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
label="Stock inicial"
|
||||
type="number"
|
||||
placeholder="10"
|
||||
min="0"
|
||||
value={prodStock}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
if (val === '' || parseInt(val, 10) >= 0) {
|
||||
setProdStock(val);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-end">
|
||||
<Button onClick={addNewProduct} className="w-full">
|
||||
<PlusIcon size={16} className="mr-2" />
|
||||
|
||||
Reference in New Issue
Block a user