From 0b82ca7de7473a01c880969c20168d930e8cace3 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 30 Jan 2026 13:43:22 +0000 Subject: [PATCH] Diego Coffee PH --- backend/src/db/db.config.js | 33 +- ...20260130000000-grant-public-permissions.js | 59 ++ .../20260130000000-diego-menu-items.js | 154 ++++ ...20260130000001-grant-public-permissions.js | 55 ++ .../seeders/20260130000002-diego-locations.js | 52 ++ backend/src/index.js | 12 +- frontend/src/components/NavBarItem.tsx | 5 +- frontend/src/layouts/Authenticated.tsx | 5 +- frontend/src/menuNavBar.ts | 27 +- frontend/src/pages/_app.tsx | 35 +- frontend/src/pages/_document.tsx | 17 + frontend/src/pages/index.tsx | 826 +++++++++++++++--- frontend/tailwind.config.js | 52 +- 13 files changed, 1120 insertions(+), 212 deletions(-) create mode 100644 backend/src/db/migrations/20260130000000-grant-public-permissions.js create mode 100644 backend/src/db/seeders/20260130000000-diego-menu-items.js create mode 100644 backend/src/db/seeders/20260130000001-grant-public-permissions.js create mode 100644 backend/src/db/seeders/20260130000002-diego-locations.js create mode 100644 frontend/src/pages/_document.tsx 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
{NavBarItemComponentContents}
-} +} \ No newline at end of file diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx index 1b9907d..26c3572 100644 --- a/frontend/src/layouts/Authenticated.tsx +++ b/frontend/src/layouts/Authenticated.tsx @@ -1,5 +1,4 @@ -import React, { ReactNode, useEffect } from 'react' -import { useState } from 'react' +import React, { ReactNode, useEffect, useState } from 'react' import jwt from 'jsonwebtoken'; import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js' import menuAside from '../menuAside' @@ -126,4 +125,4 @@ export default function LayoutAuthenticated({ ) -} +} \ No newline at end of file diff --git a/frontend/src/menuNavBar.ts b/frontend/src/menuNavBar.ts index a5dd956..63e3a98 100644 --- a/frontend/src/menuNavBar.ts +++ b/frontend/src/menuNavBar.ts @@ -47,7 +47,30 @@ const menuNavBar: MenuNavBarItem[] = [ ] export const webPagesNavBar = [ - + { + href: '#home', + label: 'Home', + }, + { + href: '#about', + label: 'About', + }, + { + href: '#menu', + label: 'Menu', + }, + { + href: '#locations', + label: 'Locations', + }, + { + href: '#contact', + label: 'Contact', + }, + { + href: '/login', + label: 'Admin', + } ]; -export default menuNavBar +export default menuNavBar \ No newline at end of file diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index 70499f7..5ec368f 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -114,35 +114,6 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { React.useEffect(() => { // Tour is disabled by default in generated projects. return; - const isCompleted = (stepKey: string) => { - return localStorage.getItem(`completed_${stepKey}`) === 'true'; - }; - if (router.pathname === '/login' && !isCompleted('loginSteps')) { - setSteps(loginSteps); - setStepName('loginSteps'); - setStepsEnabled(true); - }else if (router.pathname === '/dashboard' && !isCompleted('appSteps')) { - setTimeout(() => { - setSteps(appSteps); - setStepName('appSteps'); - setStepsEnabled(true); - }, 1000); - } else if (router.pathname === '/users/users-list' && !isCompleted('usersSteps')) { - setTimeout(() => { - setSteps(usersSteps); - setStepName('usersSteps'); - setStepsEnabled(true); - }, 1000); - } else if (router.pathname === '/roles/roles-list' && !isCompleted('rolesSteps')) { - setTimeout(() => { - setSteps(rolesSteps); - setStepName('rolesSteps'); - setStepsEnabled(true); - }, 1000); - } else { - setSteps([]); - setStepsEnabled(false); - } }, [router.pathname]); const handleExit = () => { @@ -161,6 +132,10 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { {getLayout( <> + + + + @@ -198,4 +173,4 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ) } -export default appWithTranslation(MyApp); +export default appWithTranslation(MyApp); \ No newline at end of file diff --git a/frontend/src/pages/_document.tsx b/frontend/src/pages/_document.tsx new file mode 100644 index 0000000..ae73541 --- /dev/null +++ b/frontend/src/pages/_document.tsx @@ -0,0 +1,17 @@ +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + + + + + +
+ + + + ) +} diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index bf29374..c036cc4 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,166 +1,706 @@ - import React, { useEffect, useState } from 'react'; import type { ReactElement } from 'react'; import Head from 'next/head'; import Link from 'next/link'; -import BaseButton from '../components/BaseButton'; -import CardBox from '../components/CardBox'; -import SectionFullScreen from '../components/SectionFullScreen'; +import axios from 'axios'; import LayoutGuest from '../layouts/Guest'; -import BaseDivider from '../components/BaseDivider'; -import BaseButtons from '../components/BaseButtons'; import { getPageTitle } from '../config'; -import { useAppSelector } from '../stores/hooks'; -import CardBoxComponentTitle from "../components/CardBoxComponentTitle"; -import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'; +import SectionMain from '../components/SectionMain'; +import BaseButton from '../components/BaseButton'; +import { + mdiCoffee, + mdiGlassCocktail, + mdiHamburger, + mdiMapMarker, + mdiEmail, + mdiCreation, + mdiCalendarClock, + mdiInstagram, + mdiArrowDown, + mdiBottleWine +} from '@mdi/js'; +import BaseIcon from '../components/BaseIcon'; +import CardBoxModal from '../components/CardBoxModal'; +const DecorativeElements = () => ( +
+
+ +
+
+ +
+
+ +
+
+); -export default function Starter() { - const [illustrationImage, setIllustrationImage] = useState({ - src: undefined, - photographer: undefined, - photographer_url: undefined, - }) - const [illustrationVideo, setIllustrationVideo] = useState({video_files: []}) - const [contentType, setContentType] = useState('video'); - const [contentPosition, setContentPosition] = useState('left'); - const textColor = useAppSelector((state) => state.style.linkColor); +const Navbar = () => { + const [scrolled, setScrolled] = useState(false); - const title = 'Diego Coffee & Cocktail Studio' - - // Fetch Pexels image/video - useEffect(() => { - async function fetchData() { - const image = await getPexelsImage(); - const video = await getPexelsVideo(); - setIllustrationImage(image); - setIllustrationVideo(video); - } - fetchData(); - }, []); - - const imageBlock = (image) => ( -
-
- - Photo by {image?.photographer} on Pexels - -
-
- ); - - const videoBlock = (video) => { - if (video?.video_files?.length > 0) { - return ( -
- -
- - Video by {video.user.name} on Pexels - -
-
) - } + useEffect(() => { + const handleScroll = () => { + setScrolled(window.scrollY > 50); }; + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); return ( -
- - {getPageTitle('Starter Page')} - + + ); +}; - -
- {contentType === 'image' && contentPosition !== 'background' - ? imageBlock(illustrationImage) - : null} - {contentType === 'video' && contentPosition !== 'background' - ? videoBlock(illustrationVideo) - : null} -
- - - -
-

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

-
- - - +const DiegoHero = () => ( +
+
+
+
+
+
+
+ +
+

+ Diego +

+

+ Coffee • Cocktails • Chaos 💙 +

+

+ A little playground in Lucena.
+ Beautifully unhinged. +

+ +
+

This is your sign

+
+
+
+); -
-
+const BrandStory = () => ( +
+
+
+
+
+
The Brand Story
+

Our Story

+

+ 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." +

+
+
+
+ +
+

Celebrating 2 Years!

+

Our main playground in Pleasantville, Lucena is still the heart of the chaos. Join the party! #PlayWithDiego

+
+
+
+
+
+
+ Drink +
+
+ Vibe +
+
+
+
+ Shop +
+
+ Details +
+
+
+
+

100%
UNHINGED

+
- -
-

© 2026 {title}. All rights reserved

- - Privacy Policy - +
+
+); + +const MenuPreview = () => { + const [activeTab, setActiveTab] = useState('Coffee'); + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(true); + const [selectedItem, setSelectedItem] = useState(null); + + const categories = [ + { name: 'Coffee', icon: mdiCoffee }, + { name: 'Cocktail', icon: mdiGlassCocktail }, + { name: 'Food', icon: mdiHamburger } + ]; + + useEffect(() => { + const fetchItems = async () => { + try { + setLoading(true); + const response = await axios.get('/menu_items'); + setItems(response.data.rows || []); + } catch (error) { + console.error('Error fetching menu items:', error); + } finally { + setLoading(false); + } + }; + fetchItems(); + }, []); + + const filteredItems = items.filter(item => item.item_type === activeTab); + + return ( + + ); +}; + +const ContactBooking = ({ locations }: { locations: any[] }) => { + const [activeForm, setActiveForm] = useState<'reservation' | 'pitch'>('reservation'); + const [formData, setFormData] = useState({}); + const [submitting, setSubmitting] = useState(false); + const [success, setSuccess] = useState(false); + const [error, setError] = useState(''); + + const handleInputChange = (e: React.ChangeEvent) => { + setFormData({ ...formData, [e.target.name]: e.target.value }); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setSubmitting(true); + setSuccess(false); + setError(''); + + try { + if (activeForm === 'reservation') { + await axios.post('/reservations', { + data: { + ...formData, + reservation_start: new Date(formData.date + 'T' + formData.time), + status: 'pending' + } + }); + } else { + await axios.post('/pitches', { + data: { + ...formData, + submitted_at: new Date(), + notified: false + } + }); + } + setSuccess(true); + setFormData({}); + } catch (err: any) { + console.error('Form submission error:', err); + setError(err.response?.data?.message || 'Something went wrong. Chaos in the server!'); + } finally { + setSubmitting(false); + } + }; + + return ( +
+
+
+
+
+

Let's Play

+

+ 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

+
+
+ +
+
+
+ + +
+ + {success ? ( +
+
+ +
+

Message Received!

+

The chaos is being organized. We'll get back to you soon!

+ +
+ ) : ( +
+
+
+ + +
+
+ + +
+
+ + {activeForm === 'reservation' ? ( + <> +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + ) : ( +
+ + +
+ )} + +
+ + +
+ + {error &&

{error}

} + + +
+ )} +
+
+
+
+
+ ); +}; + +const Locations = ({ locations }: { locations: any[] }) => { + return ( +
+
+

Find the Chaos

+

Locate the Playground

+ +
+ {locations.map((loc) => ( +
+
+
+ {loc.name} +
+
+ +
+
+
+

{loc.name}

+

{loc.address}

+
+ + {loc.hours} +
+ {loc.name.includes('Lucban') && ( +
+ ⚠️ Closing soon - Still open til Jan 24! +
+ )} +
+ ))} +
+ +
+
+

Special Holiday Hours

+
+
+

Dec 23 & 26–30

+

3PM – 11PM

+
+
+

Dec 24–25 & 31

+

Closed for the Chaos

+
+
+
+
+
+ ); +}; + +export default function DiegoApp() { + const title = 'Diego Coffee & Cocktail Studio'; + const [locations, setLocations] = useState([]); + + useEffect(() => { + axios.get('/locations').then(res => setLocations(res.data.rows || [])); + }, []); + + return ( +
+ + {getPageTitle('Diego')} + + + + + + +
+ + + + + +
+ +
+
+
+
+
+

Diego

+

+ Your little playground in Lucena and beyond. Drink the feelings, embrace the chaos, and play with Diego. +

+
+ {['instagram', 'facebook', 'tiktok'].map(social => ( + + + + ))} +
+
+
+
+

Navigation

+ +
+
+

Admin & Legal

+
    +
  • Privacy Chaos
  • +
  • Rules of the Playground
  • +
+ + Admin Portal + +
+
+
+
+
+

© 2026 {title}. All feelings reserved.

+

Beautifully unhinged

+
+
+

#PlayWithDiego

+
+
+
+
); } -Starter.getLayout = function getLayout(page: ReactElement) { +DiegoApp.getLayout = function getLayout(page: ReactElement) { return {page}; -}; - +}; \ No newline at end of file diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 1378cf6..0e3b11c 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -6,7 +6,7 @@ module.exports = { content: [ './src/**/*.{js,ts,jsx,tsx}', ], - darkMode: 'class', // or 'media' or 'class' + darkMode: 'class', theme: { asideScrollbars: { light: 'light', @@ -35,13 +35,46 @@ module.exports = { 'fade-in': { from: { opacity: 0 }, to: { opacity: 1 } + }, + 'chaos-shake': { + '0%, 100%': { transform: 'translateX(0) rotate(0)' }, + '10%': { transform: 'translateX(-2px) rotate(-1deg)' }, + '20%': { transform: 'translateX(2px) rotate(1deg)' }, + '30%': { transform: 'translateX(-2px) rotate(-1deg)' }, + '40%': { transform: 'translateX(2px) rotate(1deg)' }, + '50%': { transform: 'translateX(-2px) rotate(-1deg)' }, + '60%': { transform: 'translateX(2px) rotate(1deg)' }, + '70%': { transform: 'translateX(-2px) rotate(-1deg)' }, + '80%': { transform: 'translateX(2px) rotate(1deg)' }, + '90%': { transform: 'translateX(-2px) rotate(-1deg)' } + }, + 'sparkle': { + '0%, 100%': { opacity: 1, transform: 'scale(1)' }, + '50%': { opacity: 0.5, transform: 'scale(1.2)' } + }, + 'float': { + '0%, 100%': { transform: 'translateY(0)' }, + '50%': { transform: 'translateY(-20px)' } + }, + 'spin-slow': { + from: { transform: 'rotate(0deg)' }, + to: { transform: 'rotate(360deg)' } } }, animation: { 'fade-out': 'fade-out 250ms ease-in-out', - 'fade-in': 'fade-in 250ms ease-in-out' + 'fade-in': 'fade-in 250ms ease-in-out', + 'chaos-shake': 'chaos-shake 0.5s ease-in-out infinite', + 'sparkle': 'sparkle 1.5s ease-in-out infinite', + 'float': 'float 3s ease-in-out infinite', + 'spin-slow': 'spin-slow 8s linear infinite', }, colors: { + diego: { + orange: '#FF7518', + navy: '#001F3F', + accent: '#FF7518', + }, dark: { 900: '#131618', 800: '#21242A', @@ -65,9 +98,7 @@ module.exports = { 400: '#EFF0F6', 300: '#F7F7FC' }, - - - midnightBlueTheme: { + midnightBlueTheme: { text: '#D4D7E2', iconsColor: '#5A7BEB', mainBG: '#1E2338', @@ -87,10 +118,13 @@ module.exports = { }, fontFamily: { sans: ['Ubuntu', 'sans-serif'], - - }, - borderRadius: { - '3xl': '2rem', + cursive: ['Pacifico', 'cursive'], + heading: ['Pacifico', 'cursive'], + }, + borderRadius: { + '3xl': '2rem', + '4xl': '3rem', + '5xl': '4rem', }, } },