diff --git a/TODO.md b/TODO.md index 6452813..e73af00 100644 --- a/TODO.md +++ b/TODO.md @@ -21,4 +21,8 @@ Storybook snapshot testing + pipeline integration Zod 4 Shadcn registry GLobal open orders checker in Admin -> show toast, count badge over open orders menu icon -Server actions errors - show friendly error message in modal / parallel route - \ No newline at end of file +Server actions errors - show friendly error message in modal / parallel route - +language switch in menu - non-free tiers +360 view? +pricing - nm scans a month +info icon on Price card \ No newline at end of file diff --git a/package.json b/package.json index 3adbbc1..e1a1c95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-menu", - "version": "0.0.71", + "version": "0.0.72", "private": true, "type": "module", "scripts": { diff --git a/src/app/(landing)/_components/LandingFeatures.tsx b/src/app/(landing)/_components/LandingFeatures.tsx index 3d6c7c7..2b1030b 100644 --- a/src/app/(landing)/_components/LandingFeatures.tsx +++ b/src/app/(landing)/_components/LandingFeatures.tsx @@ -1,4 +1,4 @@ -import { Airplay, Clock, Globe, ScanQrCode, Users, Utensils } from 'lucide-react'; +import { Airplay, ChartPieIcon, Clock, PiggyBankIcon, ScanQrCode, Utensils } from 'lucide-react'; import React from 'react'; import { LandingSectionTitle } from '~/app/(landing)/_components/LandingSectionTitle'; import { sections } from '~/domain/landing-content'; @@ -12,35 +12,36 @@ export interface Feature { export const LandingFeatures: React.FC = () => { const features: Feature[] = [ { - title: 'Step into the QR age', - description: 'Create and manage QR-powered menus for your customers', + title: `Your own site - on us`, + description: `Once you set up your menus, you get a public link that hosts them - always online, ready to be shared, no web hosting required.`, icon: , }, { - title: 'Handle take-out orders with ease', - description: 'Nunc eget tincidunt libero. Pellentesque fringilla congue nisi id lobortis.', + title: 'Always in sync', + description: `Publish updates to your menu items and prices any time - they're instantly reflected in your menus.`, icon: , }, { - title: 'Set up and manage kiosk screens', - description: 'Mauris turpis lectus, finibus eget gravida a, sollicitudin sit amet risus.', + title: 'No app required', + description: + 'Customers that scan your QR codes are taken to your public menu page, that simply works on any mobile device.', icon: , }, { - title: 'See open orders in real time, anytime', - description: 'Donec ac lobortis enim, id pellentesque massa. Nulla quis enim ut elit consequat malesuada.', + title: 'Fully-automated ordering system', + description: `In interactive mode, guests can order items directly from your menu and can see instant updates for items marked as received at the table by your staff.`, icon: , }, { - title: 'Team Collaboration', - description: 'Quisque tincidunt aliquam malesuada. Maecenas maximus purus ac metus congue viverra. ', - icon: , + title: 'No hidden costs', + description: + 'No purchases of expensive terminals or app installs are required. Your guests and your staff only need a device with a browser - be it a phone, a tablet, a laptop or a desktop PC.', + icon: , }, { - title: 'Global Accessibility', - description: - 'Suspendisse tincidunt diam non risus venenatis, ac efficitur velit sodales. Aenean blandit consequat elit in pellentesque.', - icon: , + title: 'In-depth reports', + description: `How many guests opened your menus? What's your best selling menu item? What is never ordered? Easy - learn all these and much more from our reports page, included in all price plans!`, + icon: , }, ]; diff --git a/src/app/(landing)/_components/LandingHero.tsx b/src/app/(landing)/_components/LandingHero.tsx index 38614cc..dcfa399 100644 --- a/src/app/(landing)/_components/LandingHero.tsx +++ b/src/app/(landing)/_components/LandingHero.tsx @@ -8,14 +8,16 @@ export const LandingHero: React.FC = () => {
-

- Lorem ipsum your - dolor today! +

+ QR menus, + super-powered!

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean vel sem leo. Nunc eget - tincidunt libero. Pellentesque fringilla congue nisi id lobortis. Nullam pharetra orci eros, - id interdum ipsum sagittis ac. + TheMenu is the ultimate QR code menu platform for restaurants, bars, cafes, food trucks and + hotels. +

+

+ Ready to start accepting orders for dine-in, takeaway and delivery?

- // - // - // - //
- // ); + return ( +
+ + + +
+ ); } return ( diff --git a/src/domain/price-tier-flags.ts b/src/domain/price-tier-flags.ts index 39c70f6..c6f0b0c 100644 --- a/src/domain/price-tier-flags.ts +++ b/src/domain/price-tier-flags.ts @@ -1,6 +1,12 @@ import { z } from 'zod'; +import { MENU_MODES } from '~/domain/menu-modes'; -export const PriceTierFlagIdSchema = z.union([z.literal('reports'), z.literal('publicSite')]); +export const PriceTierFlagIdSchema = z.union([ + z.literal('reports'), + z.literal('publicSite'), + z.literal('nonInteractiveMode'), + z.literal('interactiveMode'), +]); export type PriceTierFlagId = z.infer; @@ -31,4 +37,16 @@ export const priceTierFlags: Record = { resourcePluralName: 'public site', description: 'A public-facing professional-looking web site that your customers can use to browse your menus.', }, + nonInteractiveMode: { + id: 'nonInteractiveMode', + resourceSingularName: 'non-interactive mode', + resourcePluralName: 'non-interactive mode', + description: MENU_MODES.noninteractive.description, + }, + interactiveMode: { + id: 'interactiveMode', + resourceSingularName: 'interactive mode', + resourcePluralName: 'interactive mode', + description: MENU_MODES.interactive.description, + }, }; diff --git a/src/domain/price-tiers.ts b/src/domain/price-tiers.ts index c329739..e828a73 100644 --- a/src/domain/price-tiers.ts +++ b/src/domain/price-tiers.ts @@ -49,6 +49,8 @@ export const priceTiers: Record = { flags: [ { id: 'reports', isEnabled: true }, { id: 'publicSite', isEnabled: true }, + { id: 'nonInteractiveMode', isEnabled: true }, + { id: 'interactiveMode', isEnabled: true }, ], }, start2: { @@ -57,7 +59,7 @@ export const priceTiers: Record = { description: 'Takes two minutes to get started', monthlyUsdPrice: 0, yearlyUsdPrice: 0, - isPublic: true, + isPublic: false, isPopular: false, features: [ { id: 'locations', quota: 1 }, @@ -67,6 +69,8 @@ export const priceTiers: Record = { flags: [ { id: 'reports', isEnabled: true }, { id: 'publicSite', isEnabled: true }, + { id: 'nonInteractiveMode', isEnabled: true }, + { id: 'interactiveMode', isEnabled: true }, ], }, pro: { @@ -80,12 +84,14 @@ export const priceTiers: Record = { isPopular: true, features: [ { id: 'locations', quota: 1 }, - { id: 'menus', quota: 1 }, - { id: 'menuItems', quota: 0 }, + { id: 'menus', quota: 10 }, + { id: 'menuItems', quota: 100 }, ], flags: [ { id: 'reports', isEnabled: false }, { id: 'publicSite', isEnabled: true }, + { id: 'nonInteractiveMode', isEnabled: true }, + { id: 'interactiveMode', isEnabled: true }, ], }, enterprise: { @@ -99,12 +105,14 @@ export const priceTiers: Record = { isPopular: false, features: [ { id: 'locations', quota: 1 }, - { id: 'menus', quota: 100 }, - { id: 'menuItems', quota: 14 }, + { id: 'menus', quota: 50 }, + { id: 'menuItems', quota: 1000 }, ], flags: [ { id: 'reports', isEnabled: true }, { id: 'publicSite', isEnabled: true }, + { id: 'nonInteractiveMode', isEnabled: true }, + { id: 'interactiveMode', isEnabled: true }, ], }, custom1: { diff --git a/src/middleware.ts b/src/middleware.ts index 1772556..bbae72e 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -75,16 +75,6 @@ export default clerkMiddleware( } } - // For auth-protected routes, ensure we have a last location cookie - if (isAuthProtectedRoute(req)) { - const machineId = req.cookies.get(CookieKey.CurrentLocationId)?.value; - if (!machineId) { - const anonResponse = NextResponse.next(); - anonResponse.cookies.set(CookieKey.MachineId, crypto.randomUUID(), cookieOptions); - return anonResponse; - } - } - // For public routes, ensure we have a machineId cookie if (!isAuthProtectedRoute(req)) { const machineId = req.cookies.get(CookieKey.MachineId);