diff --git a/assets/pasted-20260228-202230-c78e8860.png b/assets/pasted-20260228-202230-c78e8860.png new file mode 100644 index 0000000..16d229d Binary files /dev/null and b/assets/pasted-20260228-202230-c78e8860.png differ diff --git a/assets/pasted-20260302-181039-8b1c369a.png b/assets/pasted-20260302-181039-8b1c369a.png new file mode 100644 index 0000000..16d229d Binary files /dev/null and b/assets/pasted-20260302-181039-8b1c369a.png differ diff --git a/backend/src/db/migrations/20260228100000-grant-public-access.js b/backend/src/db/migrations/20260228100000-grant-public-access.js new file mode 100644 index 0000000..70b012d --- /dev/null +++ b/backend/src/db/migrations/20260228100000-grant-public-access.js @@ -0,0 +1,44 @@ + +module.exports = { + async up(queryInterface) { + const createdAt = new Date(); + const updatedAt = new Date(); + + const [publicRole] = await queryInterface.sequelize.query( + "SELECT id FROM roles WHERE name = 'Public' LIMIT 1" + ); + + if (!publicRole || publicRole.length === 0) { + console.error("Public role not found"); + return; + } + + const publicRoleId = publicRole[0].id; + + const [permissions] = await queryInterface.sequelize.query( + "SELECT id, name FROM permissions WHERE name IN ('READ_PRODUCTS', 'READ_CATEGORIES', 'CREATE_ORDERS', 'CREATE_ORDER_ITEMS', 'CREATE_PAYMENTS')" + ); + + const rolePermissions = permissions.map(p => ({ + createdAt: new Date(), + updatedAt: new Date(), + roles_permissionsId: publicRoleId, + permissionId: p.id + })).filter(rp => rp.permissionId); + + if (rolePermissions.length > 0) { + // Check if they already exist to avoid duplicates + for (const rp of rolePermissions) { + const [existing] = await queryInterface.sequelize.query( + `SELECT 1 FROM "rolesPermissionsPermissions" WHERE "roles_permissionsId" = '${rp.roles_permissionsId}' AND "permissionId" = '${rp.permissionId}'` + ); + if (existing.length === 0) { + await queryInterface.bulkInsert("rolesPermissionsPermissions", [rp]); + } + } + } + }, + + async down(queryInterface) { + } +}; diff --git a/backend/src/db/seeders/20260228100001-food-store-data.js b/backend/src/db/seeders/20260228100001-food-store-data.js new file mode 100644 index 0000000..8f2a36e --- /dev/null +++ b/backend/src/db/seeders/20260228100001-food-store-data.js @@ -0,0 +1,86 @@ + +module.exports = { + async up(queryInterface) { + const createdAt = new Date(); + const updatedAt = new Date(); + + const [categories] = await queryInterface.bulkInsert('categories', [ + { id: '10000000-0000-0000-0000-000000000001', name: 'Bebidas', slug: 'bebidas', description: 'Refrescos, jugos y más', createdAt, updatedAt }, + { id: '10000000-0000-0000-0000-000000000002', name: 'Alimentos Ligeros', slug: 'alimentos-ligeros', description: 'Snacks y comidas rápidas', createdAt, updatedAt }, + { id: '10000000-0000-0000-0000-000000000003', name: 'Otros', slug: 'otros', description: 'Productos varios', createdAt, updatedAt } + ], { returning: true }); + + await queryInterface.bulkInsert('products', [ + { + id: '20000000-0000-0000-0000-000000000001', + name: 'Coca Cola 500ml', + sku: 'BEB-001', + slug: 'coca-cola-500ml', + short_description: 'Refresco de cola', + description: 'Bebida gaseosa refrescante', + price: 1.50, + stock_quantity: 100, + is_active: true, + categoryId: '10000000-0000-0000-0000-000000000001', + createdAt, updatedAt + }, + { + id: '20000000-0000-0000-0000-000000000002', + name: 'Jugo de Naranja Natural', + sku: 'BEB-002', + slug: 'jugo-naranja', + short_description: 'Jugo 100% natural', + description: 'Exprimido al momento', + price: 2.50, + stock_quantity: 50, + is_active: true, + categoryId: '10000000-0000-0000-0000-000000000001', + createdAt, updatedAt + }, + { + id: '20000000-0000-0000-0000-000000000003', + name: 'Sándwich de Jamón y Queso', + sku: 'ALM-001', + slug: 'sandwich-jamon-queso', + short_description: 'Clásico sándwich', + description: 'Pan integral, jamón de pavo y queso gouda', + price: 3.50, + stock_quantity: 30, + is_active: true, + categoryId: '10000000-0000-0000-0000-000000000002', + createdAt, updatedAt + }, + { + id: '20000000-0000-0000-0000-000000000004', + name: 'Papas Fritas Artesanales', + sku: 'ALM-002', + slug: 'papas-fritas', + short_description: 'Crunchy snacks', + description: 'Papas fritas con sal de mar', + price: 1.80, + stock_quantity: 80, + is_active: true, + categoryId: '10000000-0000-0000-0000-000000000002', + createdAt, updatedAt + }, + { + id: '20000000-0000-0000-0000-000000000005', + name: 'Café Americano', + sku: 'BEB-003', + slug: 'cafe-americano', + short_description: 'Café caliente', + description: 'Granos recién molidos', + price: 1.20, + stock_quantity: 200, + is_active: true, + categoryId: '10000000-0000-0000-0000-000000000001', + createdAt, updatedAt + } + ]); + }, + + async down(queryInterface) { + await queryInterface.bulkDelete('products', null, {}); + await queryInterface.bulkDelete('categories', null, {}); + } +}; diff --git a/backend/src/index.js b/backend/src/index.js index 4808728..67a0932 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -1,4 +1,3 @@ - const express = require('express'); const cors = require('cors'); const app = express(); @@ -111,9 +110,9 @@ app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoute app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes); -app.use('/api/categories', passport.authenticate('jwt', {session: false}), categoriesRoutes); +app.use('/api/categories', categoriesRoutes); -app.use('/api/products', passport.authenticate('jwt', {session: false}), productsRoutes); +app.use('/api/products', productsRoutes); app.use('/api/addresses', passport.authenticate('jwt', {session: false}), addressesRoutes); @@ -121,11 +120,11 @@ app.use('/api/carts', passport.authenticate('jwt', {session: false}), cartsRoute app.use('/api/cart_items', passport.authenticate('jwt', {session: false}), cart_itemsRoutes); -app.use('/api/orders', passport.authenticate('jwt', {session: false}), ordersRoutes); +app.use('/api/orders', ordersRoutes); -app.use('/api/order_items', passport.authenticate('jwt', {session: false}), order_itemsRoutes); +app.use('/api/order_items', order_itemsRoutes); -app.use('/api/payments', passport.authenticate('jwt', {session: false}), paymentsRoutes); +app.use('/api/payments', paymentsRoutes); app.use('/api/coupons', passport.authenticate('jwt', {session: false}), couponsRoutes); @@ -175,4 +174,4 @@ db.sequelize.sync().then(function () { }); }); -module.exports = app; +module.exports = app; \ No newline at end of file diff --git a/frontend/public/logo.png b/frontend/public/logo.png new file mode 100644 index 0000000..16d229d Binary files /dev/null and b/frontend/public/logo.png differ diff --git a/frontend/src/components/AsideMenuLayer.tsx b/frontend/src/components/AsideMenuLayer.tsx index 50cc1cd..7056ebe 100644 --- a/frontend/src/components/AsideMenuLayer.tsx +++ b/frontend/src/components/AsideMenuLayer.tsx @@ -5,6 +5,7 @@ import AsideMenuList from './AsideMenuList' import { MenuAsideItem } from '../interfaces' import { useAppSelector } from '../stores/hooks' import Link from 'next/link'; +import Logo from './Logo'; type Props = { @@ -37,11 +38,9 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props
-
- - Tienda Virtual - - +
+ + Tienda Virtual