diff --git a/backend/src/db/db.config.js b/backend/src/db/db.config.js index e28f9a2..30ded43 100644 --- a/backend/src/db/db.config.js +++ b/backend/src/db/db.config.js @@ -1,4 +1,4 @@ - +require('dotenv').config(); module.exports = { production: { @@ -12,22 +12,23 @@ module.exports = { seederStorage: 'sequelize', }, development: { - username: 'postgres', dialect: 'postgres', - password: '', - database: 'db_diego_coffee___cocktail_studio', - host: process.env.DB_HOST || 'localhost', + username: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + host: process.env.DB_HOST, + port: process.env.DB_PORT, logging: console.log, seederStorage: 'sequelize', }, - dev_stage: { - dialect: 'postgres', - username: process.env.DB_USER, - password: process.env.DB_PASS, - database: process.env.DB_NAME, - host: process.env.DB_HOST, - port: process.env.DB_PORT, - logging: console.log, - seederStorage: 'sequelize', - } -}; + dev_stage: { + dialect: 'postgres', + username: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + host: process.env.DB_HOST, + port: process.env.DB_PORT, + logging: console.log, + seederStorage: 'sequelize', + } +}; \ No newline at end of file diff --git a/backend/src/db/migrations/20260130000000-grant-public-permissions.js b/backend/src/db/migrations/20260130000000-grant-public-permissions.js new file mode 100644 index 0000000..88ecc77 --- /dev/null +++ b/backend/src/db/migrations/20260130000000-grant-public-permissions.js @@ -0,0 +1,59 @@ + +module.exports = { + up: async (queryInterface, Sequelize) => { + const createdAt = new Date(); + const updatedAt = new Date(); + + // Get Public role ID + const [publicRole] = await queryInterface.sequelize.query( + `SELECT id FROM roles WHERE name = 'Public' LIMIT 1;`, + { type: queryInterface.sequelize.QueryTypes.SELECT } + ); + + if (!publicRole) return; + + const publicRoleId = publicRole.id; + + // Permissions to grant to Public role + const permissionsToGrant = [ + 'READ_MENU_ITEMS', + 'READ_CATEGORIES', + 'READ_PROMOTIONS', + 'READ_LOCATIONS', + 'CREATE_PITCHES', + 'CREATE_RESERVATIONS' + ]; + + // Find permission IDs + const permissions = await queryInterface.sequelize.query( + `SELECT id FROM permissions WHERE name IN (:permissionsToGrant);`, + { + replacements: { permissionsToGrant }, + type: queryInterface.sequelize.QueryTypes.SELECT + } + ); + + const rolesPermissionsPermissions = permissions.map(p => ({ + createdAt, + updatedAt, + roles_permissionsId: publicRoleId, + permissionId: p.id + })); + + await queryInterface.bulkInsert('rolesPermissionsPermissions', rolesPermissionsPermissions); + }, + + down: async (queryInterface, Sequelize) => { + const [publicRole] = await queryInterface.sequelize.query( + `SELECT id FROM roles WHERE name = 'Public' LIMIT 1;`, + { type: queryInterface.sequelize.QueryTypes.SELECT } + ); + + if (!publicRole) return; + + await queryInterface.sequelize.query( + `DELETE FROM "rolesPermissionsPermissions" WHERE "roles_permissionsId" = :publicRoleId;`, + { replacements: { publicRoleId: publicRole.id } } + ); + } +}; diff --git a/backend/src/db/seeders/20260130000000-diego-menu-items.js b/backend/src/db/seeders/20260130000000-diego-menu-items.js new file mode 100644 index 0000000..a65f0a0 --- /dev/null +++ b/backend/src/db/seeders/20260130000000-diego-menu-items.js @@ -0,0 +1,154 @@ +const { v4: uuid } = require('uuid'); + +module.exports = { + up: async (queryInterface, Sequelize) => { + const createdAt = new Date(); + const updatedAt = new Date(); + + // Clear existing items to avoid duplicates and ensure clean state + await queryInterface.bulkDelete('menu_items', null, {}); + await queryInterface.bulkDelete('categories', null, {}); + + const categories = [ + { + id: uuid(), + name: 'Coffee', + description: 'Specialty coffee and lattes.', + createdAt, + updatedAt + }, + { + id: uuid(), + name: 'Cocktail', + description: 'Creative cocktails and "Drink the feelings".', + createdAt, + updatedAt + }, + { + id: uuid(), + name: 'Food', + description: 'Cozy and chaotic food pairings.', + createdAt, + updatedAt + } + ]; + + await queryInterface.bulkInsert('categories', categories); + + const getCatId = (name) => categories.find(c => c.name === name).id; + + const menuItems = [ + { + id: uuid(), + title: 'Orange Coffee', + description: 'Bright orange, smooth coffee, sneaky dark chocolate finish.', + price: 150.00, + item_type: 'Coffee', + categoryId: getCatId('Coffee'), + ingredients: 'Espresso, Fresh Orange Juice, Dark Chocolate Syrup', + is_featured: true, + is_available: true, + createdAt, + updatedAt + }, + { + id: uuid(), + title: 'Spanish Latté', + description: 'Creamy and sweet, a classic favorite. Available iced or hot.', + price: 180.00, + item_type: 'Coffee', + categoryId: getCatId('Coffee'), + ingredients: 'Espresso, Condensed Milk, Fresh Milk', + is_featured: true, + is_available: true, + createdAt, + updatedAt + }, + { + id: uuid(), + title: 'CEO Latté', + description: 'A strong, bold latte for the boss in you. Served hot.', + price: 170.00, + item_type: 'Coffee', + categoryId: getCatId('Coffee'), + ingredients: 'Extra Shot Espresso, Steamed Milk, Secret Sweetener', + is_featured: true, + is_available: true, + createdAt, + updatedAt + }, + { + id: uuid(), + title: 'Spice & Star', + description: 'Buttery, cinnamon-spiced, sparkling with warm star anise.', + price: 250.00, + item_type: 'Cocktail', + categoryId: getCatId('Cocktail'), + ingredients: 'Spiced Rum, Cinnamon Syrup, Star Anise, Butter-washed Bourbon', + is_featured: true, + is_available: true, + createdAt, + updatedAt + }, + { + id: uuid(), + title: 'Spania Salada', + description: 'A salty-sweet twist on the classic Spanish Latté.', + price: 220.00, + item_type: 'Cocktail', + categoryId: getCatId('Cocktail'), + ingredients: 'Espresso, Salted Caramel, Condensed Milk, Sea Salt', + is_featured: true, + is_available: true, + createdAt, + updatedAt + }, + { + id: uuid(), + title: 'Honey Chai Matcha Latté', + description: 'One-in-a-million matcha. A soothing blend of matcha, honey, and chai spices.', + price: 190.00, + item_type: 'Coffee', + categoryId: getCatId('Coffee'), + ingredients: 'Ceremonial Grade Matcha, Honey, Chai Spice Blend, Oat Milk', + is_featured: true, + is_available: true, + createdAt, + updatedAt + }, + { + id: uuid(), + title: 'Cinnamon Oat', + description: 'Cozy oat-based coffee with a hint of cinnamon.', + price: 180.00, + item_type: 'Coffee', + categoryId: getCatId('Coffee'), + ingredients: 'Espresso, Oat Milk, Cinnamon, Maple Syrup', + is_featured: true, + is_available: true, + createdAt, + updatedAt + }, + { + id: uuid(), + title: 'Diego Burger', + description: 'Our signature chaotic burger. Collab with ohso.socialph.', + price: 220.00, + item_type: 'Food', + categoryId: getCatId('Food'), + ingredients: 'Beef Patty, Secret Chaos Sauce, Brioche Bun, Caramelized Onions', + is_featured: true, + is_available: true, + createdAt, + updatedAt + } + ]; + + return queryInterface.bulkInsert('menu_items', menuItems); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete('menu_items', null, {}); + await queryInterface.bulkDelete('categories', null, {}); + } +}; \ No newline at end of file diff --git a/backend/src/db/seeders/20260130000001-grant-public-permissions.js b/backend/src/db/seeders/20260130000001-grant-public-permissions.js new file mode 100644 index 0000000..ec7841a --- /dev/null +++ b/backend/src/db/seeders/20260130000001-grant-public-permissions.js @@ -0,0 +1,55 @@ + +module.exports = { + up: async (queryInterface, Sequelize) => { + 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[0]) { + console.error("Public role not found"); + return; + } + + const publicRoleId = publicRole[0].id; + + const permissionsToGrant = [ + 'READ_MENU_ITEMS', + 'CREATE_RESERVATIONS', + 'READ_LOCATIONS', + 'READ_CATEGORIES', + 'CREATE_PITCHES', + 'READ_MEDIA' + ]; + + const [permissions] = await queryInterface.sequelize.query( + `SELECT id, name FROM permissions WHERE name IN (${permissionsToGrant.map(p => `'${p}'`).join(',')});` + ); + + const rolePermissions = permissions.map(permission => ({ + createdAt, + updatedAt, + roles_permissionsId: publicRoleId, + permissionId: permission.id + })); + + return queryInterface.bulkInsert('rolesPermissionsPermissions', rolePermissions, { + ignoreDuplicates: true + }); + }, + + down: async (queryInterface, Sequelize) => { + // Usually we don't want to remove these in down as it might break things, + // but for completeness: + const [publicRole] = await queryInterface.sequelize.query( + `SELECT id FROM roles WHERE name = 'Public' LIMIT 1;` + ); + if (publicRole && publicRole[0]) { + return queryInterface.bulkDelete('rolesPermissionsPermissions', { + roles_permissionsId: publicRole[0].id + }); + } + } +}; diff --git a/backend/src/db/seeders/20260130000002-diego-locations.js b/backend/src/db/seeders/20260130000002-diego-locations.js new file mode 100644 index 0000000..1005f3c --- /dev/null +++ b/backend/src/db/seeders/20260130000002-diego-locations.js @@ -0,0 +1,52 @@ +const { v4: uuid } = require('uuid'); + +module.exports = { + up: async (queryInterface, Sequelize) => { + const createdAt = new Date(); + const updatedAt = new Date(); + + // Clear reservations first to avoid foreign key constraints + await queryInterface.bulkDelete('reservations', null, {}); + await queryInterface.bulkDelete('locations', null, {}); + + const locations = [ + { + id: uuid(), + name: 'Lucena (Main Playground)', + address: 'Bonifacio Dr., Pleasantville, Lucena', + hours: '3PM - 11PM', + city: 'Lucena', + is_open: true, + createdAt, + updatedAt + }, + { + id: uuid(), + name: 'Lucban', + address: 'Town Center, Lucban, Quezon', + hours: 'Closing Soon', + city: 'Lucban', + is_open: true, + createdAt, + updatedAt + }, + { + id: uuid(), + name: 'Sariaya', + address: 'Quiminiano St., Brgy 3, Arellano Subd., Sariaya', + hours: '3PM - 11PM', + city: 'Sariaya', + is_open: true, + createdAt, + updatedAt + } + ]; + + return queryInterface.bulkInsert('locations', locations); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete('reservations', null, {}); + await queryInterface.bulkDelete('locations', null, {}); + } +}; diff --git a/backend/src/index.js b/backend/src/index.js index 990752d..b1d9c19 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -109,21 +109,21 @@ app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoute app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes); -app.use('/api/menu_items', passport.authenticate('jwt', {session: false}), menu_itemsRoutes); +app.use('/api/menu_items', menu_itemsRoutes); -app.use('/api/categories', passport.authenticate('jwt', {session: false}), categoriesRoutes); +app.use('/api/categories', categoriesRoutes); app.use('/api/promotions', passport.authenticate('jwt', {session: false}), promotionsRoutes); -app.use('/api/reservations', passport.authenticate('jwt', {session: false}), reservationsRoutes); +app.use('/api/reservations', reservationsRoutes); -app.use('/api/pitches', passport.authenticate('jwt', {session: false}), pitchesRoutes); +app.use('/api/pitches', pitchesRoutes); -app.use('/api/locations', passport.authenticate('jwt', {session: false}), locationsRoutes); +app.use('/api/locations', locationsRoutes); app.use('/api/orders', passport.authenticate('jwt', {session: false}), ordersRoutes); -app.use('/api/media', passport.authenticate('jwt', {session: false}), mediaRoutes); +app.use('/api/media', mediaRoutes); app.use('/api/pages', passport.authenticate('jwt', {session: false}), pagesRoutes); diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx index eb155e3..1986306 100644 --- a/frontend/src/components/NavBarItem.tsx +++ b/frontend/src/components/NavBarItem.tsx @@ -1,6 +1,5 @@ -import React, {useEffect, useRef} from 'react' +import React, {useEffect, useRef, useState} from 'react' import Link from 'next/link' -import { useState } from 'react' import { mdiChevronUp, mdiChevronDown } from '@mdi/js' import BaseDivider from './BaseDivider' import BaseIcon from './BaseIcon' @@ -129,4 +128,4 @@ export default function NavBarItem({ item }: Props) { } return
This is a React.js/Node.js app generated by the Flatlogic Web App Generator
-For guides and documentation please check - your local README.md and the Flatlogic documentation
-+ Coffee • Cocktails • Chaos 💙 +
+This is your sign
++ Mabuhay to Diego! From beachfront brews to cozy haunts, we bring fun to every sip. + Diego is more than just a shop; it's your playground in Quezon. +
++ "Making specialty coffee and cocktails accessible, fun, and beautifully chaotic for everyone." +
+Our main playground in Pleasantville, Lucena is still the heart of the chaos. Join the party! #PlayWithDiego
+
+
+
+
+ 100%
UNHINGED
© 2026 {title}. All rights reserved
- - Privacy Policy - ++ Planning a birthday, wedding, or corporate event? Or maybe you have a wild collaboration idea? + "This is your sign" +
+ +Pitch your idea
+hello@diego.ph
+The Playgrounds
+Lucena • Sariaya
+Play with Diego
+#DiegoByAteLorie #PlayWithDiego
+The chaos is being organized. We'll get back to you soon!
+ +Locate the Playground
+ +{loc.address}
+Dec 23 & 26–30
+3PM – 11PM
+Dec 24–25 & 31
+Closed for the Chaos
+