{p.name}
diff --git a/test_date.mjs b/test_date.mjs
new file mode 100644
index 0000000..ad4200f
--- /dev/null
+++ b/test_date.mjs
@@ -0,0 +1,30 @@
+const date = '2026-06-11';
+const slots = ['09:00', '10:00', '11:00', '12:00'];
+const bookedSlots = [];
+
+const today = new Date();
+const todayStr = today.toISOString().split('T')[0];
+const isToday = date === todayStr;
+const currentHour = today.getHours();
+const currentMinute = today.getMinutes();
+
+console.log("todayStr:", todayStr);
+console.log("date:", date);
+console.log("isToday:", isToday);
+console.log("currentHour:", currentHour);
+console.log("currentMinute:", currentMinute);
+
+const processedSlots = slots.map(time => {
+ let isPast = false;
+ if (isToday) {
+ const [h, m] = time.split(':').map(Number);
+ if (h < currentHour || (h === currentHour && m <= currentMinute)) {
+ isPast = true;
+ }
+ }
+
+ const isBooked = bookedSlots.includes(time) || isPast;
+ return { time, isBooked };
+});
+
+console.log(processedSlots);
diff --git a/test_supabase.js b/test_supabase.js
new file mode 100644
index 0000000..b423c52
--- /dev/null
+++ b/test_supabase.js
@@ -0,0 +1,24 @@
+import { createClient } from '@supabase/supabase-js';
+import dotenv from 'dotenv';
+dotenv.config({ path: './web/.env' });
+
+const supabaseUrl = process.env.VITE_SUPABASE_URL;
+const supabaseKey = process.env.VITE_SUPABASE_ANON_KEY;
+
+if (!supabaseUrl || !supabaseKey) {
+ console.log("Missing Supabase credentials in .env");
+ process.exit(1);
+}
+
+const supabase = createClient(supabaseUrl, supabaseKey);
+
+async function test() {
+ console.log("Checking notifications table...");
+ const { data, error } = await supabase.from('notifications').select('*').limit(1);
+ if (error) {
+ console.error("Error accessing notifications table:", error.message);
+ } else {
+ console.log("Notifications table exists!");
+ }
+}
+test();
diff --git a/web/check_notifications.js b/web/check_notifications.js
new file mode 100644
index 0000000..66a0c87
--- /dev/null
+++ b/web/check_notifications.js
@@ -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()
diff --git a/web/check_profiles.js b/web/check_profiles.js
new file mode 100644
index 0000000..32d3a56
--- /dev/null
+++ b/web/check_profiles.js
@@ -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();
diff --git a/web/src/components/ProductList.tsx b/web/src/components/ProductList.tsx
index a1cea14..93dddd9 100644
--- a/web/src/components/ProductList.tsx
+++ b/web/src/components/ProductList.tsx
@@ -13,7 +13,7 @@ export const ProductList = ({
onAdd?: (id: string) => void;
}) => (
- {products.map((p) => {
+ {[...products].sort((a, b) => b.price - a.price).map((p) => {
const lowStock = p.stock <= 3;
return (
diff --git a/web/src/context/AppContext.tsx b/web/src/context/AppContext.tsx
index bdb9c70..ad247ef 100644
--- a/web/src/context/AppContext.tsx
+++ b/web/src/context/AppContext.tsx
@@ -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);
diff --git a/web/src/pages/Dashboard.tsx b/web/src/pages/Dashboard.tsx
index 3416dfb..96ae45a 100644
--- a/web/src/pages/Dashboard.tsx
+++ b/web/src/pages/Dashboard.tsx
@@ -141,8 +141,8 @@ function DashboardInner({ shop }: { shop: BarberShop }) {
const [svcDuration, setSvcDuration] = useState(30);
const [prodName, setProdName] = useState('');
- const [prodPrice, setProdPrice] = useState(30);
- const [prodStock, setProdStock] = useState(10);
+ const [prodPrice, setProdPrice] = useState('30');
+ const [prodStock, setProdStock] = useState('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 }) {
)}
- {shop.products.map((p) => (
+ {[...shop.products].sort((a, b) => b.price - a.price).map((p) => (