repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
Fastfood QR
stars
latest
clone command
git clone gitlawb://did:key:z6Mkfh4Y...QBEi/fastfood-qrgit clone gitlawb://did:key:z6Mkfh4Y.../fastfood-qr3042a875sync from playground21h ago| #1 | import type { Restaurant, Category, Product } from './types'; |
| #2 | |
| #3 | const STORAGE_KEYS = { |
| #4 | restaurants: 'ff_restaurants', |
| #5 | categories: 'ff_categories', |
| #6 | products: 'ff_products', |
| #7 | }; |
| #8 | |
| #9 | export function generateId(): string { |
| #10 | if (typeof crypto !== 'undefined' && crypto.randomUUID) { |
| #11 | return crypto.randomUUID(); |
| #12 | } |
| #13 | return 'xxxx-xxxx-xxxx'.replace(/x/g, () => |
| #14 | Math.floor(Math.random() * 16).toString(16) |
| #15 | ); |
| #16 | } |
| #17 | |
| #18 | const MOCK_RESTAURANTS: Restaurant[] = [ |
| #19 | { |
| #20 | id: 'r1', |
| #21 | name: import.meta.env.APP_RESTAURANT_NAME || 'Burger House', |
| #22 | slug: import.meta.env.APP_RESTAURANT_SLUG || 'burger-house', |
| #23 | description: import.meta.env.APP_RESTAURANT_DESCRIPTION || 'The best burgers in town! Fresh ingredients, bold flavors.', |
| #24 | whatsappNumber: import.meta.env.APP_WHATSAPP_SHOP_NUMBER || '+10000000000', |
| #25 | address: import.meta.env.APP_RESTAURANT_ADDRESS || '123 Main Street, Downtown', |
| #26 | logoUrl: '', |
| #27 | bannerUrl: 'https://images.unsplash.com/photo-1561758033-d89a9ad46330?w=800&h=400&fit=crop', |
| #28 | openingHours: import.meta.env.APP_RESTAURANT_OPENING_HOURS || 'Mon-Sat 11:00 - 23:00', |
| #29 | instagram: import.meta.env.APP_RESTAURANT_INSTAGRAM || '@burgerhouse', |
| #30 | deliveryFee: Number(import.meta.env.APP_DELIVERY_FEE) || 3, |
| #31 | currency: import.meta.env.APP_CURRENCY || 'EUR', |
| #32 | }, |
| #33 | ]; |
| #34 | |
| #35 | const MOCK_CATEGORIES: Category[] = [ |
| #36 | { id: 'c1', restaurantId: 'r1', name: 'Burgers', sortOrder: 1 }, |
| #37 | { id: 'c2', restaurantId: 'r1', name: 'Sides', sortOrder: 2 }, |
| #38 | { id: 'c3', restaurantId: 'r1', name: 'Drinks', sortOrder: 3 }, |
| #39 | { id: 'c4', restaurantId: 'r1', name: 'Desserts', sortOrder: 4 }, |
| #40 | ]; |
| #41 | |
| #42 | const MOCK_PRODUCTS: Product[] = [ |
| #43 | { |
| #44 | id: 'p1', restaurantId: 'r1', categoryId: 'c1', |
| #45 | name: 'Classic Burger', description: 'Juicy beef patty with lettuce, tomato, and our special sauce', |
| #46 | price: 9.5, imageUrl: 'https://images.unsplash.com/photo-1568901346375-23d9450f7131?w=400&h=300&fit=crop', |
| #47 | available: true, featured: true, |
| #48 | }, |
| #49 | { |
| #50 | id: 'p2', restaurantId: 'r1', categoryId: 'c1', |
| #51 | name: 'Cheese Burger', description: 'Double cheese with crispy bacon', |
| #52 | price: 11, imageUrl: 'https://images.unsplash.com/photo-1553979459-d2229ba7433b?w=400&h=300&fit=crop', |
| #53 | available: true, featured: false, |
| #54 | }, |
| #55 | { |
| #56 | id: 'p3', restaurantId: 'r1', categoryId: 'c1', |
| #57 | name: 'Chicken Burger', description: 'Grilled chicken breast with avocado', |
| #58 | price: 10, imageUrl: 'https://images.unsplash.com/photo-1626082927389-6cd097cdc6ec?w=400&h=300&fit=crop', |
| #59 | available: true, featured: false, |
| #60 | }, |
| #61 | { |
| #62 | id: 'p4', restaurantId: 'r1', categoryId: 'c1', |
| #63 | name: 'Veggie Burger', description: 'Plant-based patty with all the fixings', |
| #64 | price: 8.5, imageUrl: 'https://images.unsplash.com/photo-1525184782190-5ac45a4e4d38?w=400&h=300&fit=crop', |
| #65 | available: true, featured: false, |
| #66 | }, |
| #67 | { |
| #68 | id: 'p5', restaurantId: 'r1', categoryId: 'c2', |
| #69 | name: 'French Fries', description: 'Crispy golden fries', |
| #70 | price: 4, imageUrl: 'https://images.unsplash.com/photo-1630384060421-cb20d0e0649d?w=400&h=300&fit=crop', |
| #71 | available: true, featured: true, |
| #72 | }, |
| #73 | { |
| #74 | id: 'p6', restaurantId: 'r1', categoryId: 'c2', |
| #75 | name: 'Onion Rings', description: 'Beer-battered onion rings', |
| #76 | price: 5, imageUrl: 'https://images.unsplash.com/photo-1641509296288-3742f8f4e533?w=400&h=300&fit=crop', |
| #77 | available: true, featured: false, |
| #78 | }, |
| #79 | { |
| #80 | id: 'p7', restaurantId: 'r1', categoryId: 'c2', |
| #81 | name: 'Chicken Nuggets', description: '6 pieces with dipping sauce', |
| #82 | price: 6, imageUrl: 'https://images.unsplash.com/photo-1562967916-eb82221dfb43?w=400&h=300&fit=crop', |
| #83 | available: true, featured: false, |
| #84 | }, |
| #85 | { |
| #86 | id: 'p8', restaurantId: 'r1', categoryId: 'c3', |
| #87 | name: 'Coca-Cola', description: '330ml', |
| #88 | price: 3, imageUrl: 'https://images.unsplash.com/photo-1625772299848-391b6a87d7b3?w=400&h=300&fit=crop', |
| #89 | available: true, featured: false, |
| #90 | }, |
| #91 | { |
| #92 | id: 'p9', restaurantId: 'r1', categoryId: 'c3', |
| #93 | name: 'Milkshake', description: 'Vanilla, chocolate or strawberry', |
| #94 | price: 6, imageUrl: 'https://images.unsplash.com/photo-1577805947697-89e18249d767?w=400&h=300&fit=crop', |
| #95 | available: true, featured: false, |
| #96 | }, |
| #97 | { |
| #98 | id: 'p10', restaurantId: 'r1', categoryId: 'c3', |
| #99 | name: 'Fresh Juice', description: 'Orange or apple', |
| #100 | price: 5, imageUrl: 'https://images.unsplash.com/photo-1621506289937-8e085a3fc9b4?w=400&h=300&fit=crop', |
| #101 | available: true, featured: false, |
| #102 | }, |
| #103 | { |
| #104 | id: 'p11', restaurantId: 'r1', categoryId: 'c4', |
| #105 | name: 'Brownie', description: 'Warm chocolate brownie with ice cream', |
| #106 | price: 5, imageUrl: 'https://images.unsplash.com/photo-1606313564200-0271994b5ce0?w=400&h=300&fit=crop', |
| #107 | available: true, featured: false, |
| #108 | }, |
| #109 | { |
| #110 | id: 'p12', restaurantId: 'r1', categoryId: 'c4', |
| #111 | name: 'Ice Cream', description: '2 scoops of your choice', |
| #112 | price: 4, imageUrl: 'https://images.unsplash.com/photo-1497034825427-c0a241c46e3a?w=400&h=300&fit=crop', |
| #113 | available: true, featured: false, |
| #114 | }, |
| #115 | ]; |
| #116 | |
| #117 | export function initData(): void { |
| #118 | if (!localStorage.getItem(STORAGE_KEYS.restaurants)) { |
| #119 | localStorage.setItem(STORAGE_KEYS.restaurants, JSON.stringify(MOCK_RESTAURANTS)); |
| #120 | } |
| #121 | if (!localStorage.getItem(STORAGE_KEYS.categories)) { |
| #122 | localStorage.setItem(STORAGE_KEYS.categories, JSON.stringify(MOCK_CATEGORIES)); |
| #123 | } |
| #124 | if (!localStorage.getItem(STORAGE_KEYS.products)) { |
| #125 | localStorage.setItem(STORAGE_KEYS.products, JSON.stringify(MOCK_PRODUCTS)); |
| #126 | } |
| #127 | } |
| #128 | |
| #129 | export function loadRestaurants(): Restaurant[] { |
| #130 | const raw = localStorage.getItem(STORAGE_KEYS.restaurants); |
| #131 | return raw ? JSON.parse(raw) : MOCK_RESTAURANTS; |
| #132 | } |
| #133 | |
| #134 | export function saveRestaurants(restaurants: Restaurant[]): void { |
| #135 | localStorage.setItem(STORAGE_KEYS.restaurants, JSON.stringify(restaurants)); |
| #136 | } |
| #137 | |
| #138 | export function loadCategories(restaurantId: string): Category[] { |
| #139 | const raw = localStorage.getItem(STORAGE_KEYS.categories); |
| #140 | const all: Category[] = raw ? JSON.parse(raw) : MOCK_CATEGORIES; |
| #141 | return all.filter((c) => c.restaurantId === restaurantId).sort((a, b) => a.sortOrder - b.sortOrder); |
| #142 | } |
| #143 | |
| #144 | export function saveCategories(categories: Category[]): void { |
| #145 | const raw = localStorage.getItem(STORAGE_KEYS.categories); |
| #146 | const all: Category[] = raw ? JSON.parse(raw) : MOCK_CATEGORIES; |
| #147 | const ids = new Set(categories.map((c) => c.id)); |
| #148 | const rest = all.filter((c) => !ids.has(c.id)); |
| #149 | localStorage.setItem(STORAGE_KEYS.categories, JSON.stringify([...rest, ...categories])); |
| #150 | } |
| #151 | |
| #152 | export function loadProducts(restaurantId: string): Product[] { |
| #153 | const raw = localStorage.getItem(STORAGE_KEYS.products); |
| #154 | const all: Product[] = raw ? JSON.parse(raw) : MOCK_PRODUCTS; |
| #155 | return all.filter((p) => p.restaurantId === restaurantId); |
| #156 | } |
| #157 | |
| #158 | export function saveProducts(products: Product[]): void { |
| #159 | const raw = localStorage.getItem(STORAGE_KEYS.products); |
| #160 | const all: Product[] = raw ? JSON.parse(raw) : MOCK_PRODUCTS; |
| #161 | const ids = new Set(products.map((p) => p.id)); |
| #162 | const rest = all.filter((p) => !ids.has(p.id)); |
| #163 | localStorage.setItem(STORAGE_KEYS.products, JSON.stringify([...rest, ...products])); |
| #164 | } |
| #165 | |
| #166 | export function getRestaurantBySlug(slug: string): Restaurant | undefined { |
| #167 | const restaurants = loadRestaurants(); |
| #168 | return restaurants.find((r) => r.slug === slug); |
| #169 | } |
| #170 | |
| #171 | const currencySymbols: Record<string, string> = { |
| #172 | EUR: '\u20ac', |
| #173 | USD: '$', |
| #174 | GBP: '\u00a3', |
| #175 | BRL: 'R$', |
| #176 | }; |
| #177 | |
| #178 | export function getCurrencySymbol(currency: string): string { |
| #179 | return currencySymbols[currency] ?? currency; |
| #180 | } |
| #181 |