├── .husky ├── pre-commit └── commit-msg ├── .prettierignore ├── public ├── logo.png ├── dkast.jpg ├── editor.png ├── favicon.ico ├── menu-back.png ├── og-image.jpg ├── products.png ├── Sora-Medium.ttf ├── biztro-hero.png ├── editor-dark.png ├── iphone-hero.png ├── menu-front.png ├── qr-example.png ├── Inter-SemiBold.ttf ├── configuration.png ├── editor-light.png ├── favicon-16x16.png ├── favicon-32x32.png ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── mstile-70x70.png ├── apple-touch-icon.png ├── bg │ ├── bg-center-cafe-1.jpg │ ├── bg-center-cafe-2.jpg │ ├── bg-top-bakery-1.jpg │ ├── bg-top-bakery-2.jpg │ ├── bg-top-burger-1.jpg │ ├── bg-top-fusion-1.jpg │ ├── bg-top-fusion-2.jpg │ ├── bg-top-salad-1.jpg │ ├── bg-top-tacos-1.jpg │ ├── bg-top-tacos-2.jpg │ ├── bg-top-tomates-1.jpg │ ├── bg-center-pizza-1.jpg │ ├── bg-center-sushi-1.jpg │ ├── bg-center-sushi-2.jpg │ ├── bg-center-tacos-3.jpg │ ├── bg-top-breakfast-1.jpg │ ├── bg-top-breakfast-2.jpg │ ├── bg-top-ice-cream-1.jpg │ ├── bg-top-ice-cream-2.jpg │ ├── bg-top-mariscos-1.jpg │ ├── bg-top-mariscos-2.jpg │ ├── bg-center-ice-cream-3.jpg │ ├── bg-center-molcajete-1.jpg │ ├── bg-center-parrilla-1.jpg │ ├── leaf.svg │ ├── noise.svg │ └── clouds.svg ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── browserconfig.xml ├── twitter-mono.svg ├── twitter.svg ├── site.webmanifest ├── facebook-mono.svg ├── tiktok-mono.svg ├── google.svg ├── facebook.svg ├── whatsapp-mono.svg ├── safari-pinned-tab.svg ├── tiktok.svg ├── instagram-mono.svg └── logo-bistro.svg ├── .commitlintrc.json ├── .vscode ├── settings.json ├── tasks.json └── mcp.json ├── src ├── app │ ├── opengraph-image.png │ ├── dashboard │ │ ├── menu-items │ │ │ ├── @modal │ │ │ │ └── default.tsx │ │ │ ├── loading.tsx │ │ │ ├── categories │ │ │ │ ├── loading.tsx │ │ │ │ ├── category-table.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── columns.tsx │ │ │ ├── [action] │ │ │ │ └── [id] │ │ │ │ │ ├── loading.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── item-table.tsx │ │ │ ├── filter-toolbar.tsx │ │ │ └── page.tsx │ │ ├── loading.tsx │ │ ├── settings │ │ │ ├── loading.tsx │ │ │ ├── layout.tsx │ │ │ ├── members │ │ │ │ └── member-table.tsx │ │ │ ├── billing │ │ │ │ ├── revalidate-status.tsx │ │ │ │ └── customer-portal-button.tsx │ │ │ └── locations │ │ │ │ └── page.tsx │ │ ├── create-org │ │ │ └── page.tsx │ │ └── layout.tsx │ ├── config.ts │ ├── menu-editor │ │ └── [id] │ │ │ ├── layout.tsx │ │ │ ├── loading.tsx │ │ │ └── preview │ │ │ └── page.tsx │ ├── api │ │ ├── auth │ │ │ └── [...all] │ │ │ │ └── route.ts │ │ └── org │ │ │ └── route.ts │ ├── (auth) │ │ ├── auth-error │ │ │ └── loading.tsx │ │ ├── invite │ │ │ └── [id] │ │ │ │ └── loading.tsx │ │ └── new-org │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ ├── .well-known │ │ └── vercel │ │ │ └── flags │ │ │ └── route.ts │ ├── (content) │ │ └── layout.tsx │ ├── global-error.tsx │ ├── not-found.tsx │ ├── [subdomain] │ │ └── resolve-editor.tsx │ ├── page.tsx │ ├── manifest.ts │ └── analytics.tsx ├── emails │ └── static │ │ └── logo.png ├── components │ ├── ui │ │ ├── aspect-ratio.tsx │ │ ├── skeleton.tsx │ │ ├── collapsible.tsx │ │ ├── date-time-picker │ │ │ ├── time-picker.tsx │ │ │ ├── date-segment.tsx │ │ │ ├── date-field.tsx │ │ │ └── time-field.tsx │ │ ├── image-viewer.tsx │ │ ├── textarea.tsx │ │ ├── separator.tsx │ │ ├── progress.tsx │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── checkbox.tsx │ │ ├── switch.tsx │ │ ├── tooltip.tsx │ │ ├── slider.tsx │ │ ├── popover.tsx │ │ ├── spinner.tsx │ │ ├── password-input.tsx │ │ ├── toggle.tsx │ │ ├── hover-card.tsx │ │ ├── ripple.tsx │ │ ├── scroll-area.tsx │ │ ├── radio-group.tsx │ │ ├── resizable.tsx │ │ ├── toggle-group.tsx │ │ ├── tabs.tsx │ │ └── button.tsx │ ├── flare-ui │ │ ├── gradient-blur.tsx │ │ ├── animated-shiny-text.tsx │ │ └── border-beam.tsx │ ├── confetti-on-mount.tsx │ ├── marketing │ │ ├── mdx.tsx │ │ ├── title-section.tsx │ │ ├── cta-banner.tsx │ │ └── footer.tsx │ ├── dashboard │ │ ├── tooltip-helper.tsx │ │ ├── info-helper.tsx │ │ ├── onboarding-progress.tsx │ │ ├── empty-state.tsx │ │ ├── header.tsx │ │ ├── onboarding-status.tsx │ │ ├── page-header.tsx │ │ ├── page-subtitle.tsx │ │ ├── empty-image-field.tsx │ │ ├── page-panel.tsx │ │ ├── secondary-nav.tsx │ │ └── upgrade-dialog.tsx │ ├── icons │ │ ├── twitter-icon.tsx │ │ ├── facebook-icon.tsx │ │ ├── tiktok-icon.tsx │ │ └── whatsapp-icon.tsx │ ├── tailwind-indicator.tsx │ └── menu-editor │ │ ├── toolbar.tsx │ │ ├── settings-panel.tsx │ │ ├── side-section.tsx │ │ ├── blocks │ │ ├── featured-settings.tsx │ │ ├── text-element.tsx │ │ └── heading-element.tsx │ │ ├── block-icons.tsx │ │ ├── font-wrapper.tsx │ │ ├── layers │ │ ├── layer-name.tsx │ │ └── default-layer.tsx │ │ └── css-styles.tsx ├── lib │ ├── stripe.ts │ ├── stripe-client.ts │ ├── session.ts │ ├── prisma.ts │ ├── auth-client.ts │ ├── atoms.ts │ ├── export-as-image.ts │ ├── currency.ts │ ├── safe-actions.ts │ └── difference.ts ├── flags.ts ├── hooks │ ├── use-forwarded-ref.tsx │ ├── use-mobile.ts │ ├── use-rect.ts │ ├── use-data-table.ts │ └── use-local-storage.ts ├── instrumentation.ts ├── proxy.ts └── server │ └── actions │ ├── location │ └── queries.ts │ └── subscriptions │ ├── mutations.ts │ └── queries.ts ├── prisma └── migrations │ ├── 20251016003645_remove_name_unique_prop │ └── migration.sql │ ├── 20240417054412_add_draft │ └── migration.sql │ ├── 20241205013525_add_allergens │ └── migration.sql │ ├── migration_lock.toml │ ├── 20241113053647_add_indexes │ └── migration.sql │ ├── 20241106062214_add_unique_to_membership │ └── migration.sql │ ├── 20240525004038_add_open_hours │ └── migration.sql │ ├── 20251009054308_remove_deprecated_tables │ └── migration.sql │ ├── 20251008005305_stripe_plugin │ └── migration.sql │ ├── 20240427172632_invite_flag │ └── migration.sql │ ├── 20251018174951_rename_invite_to_waitlist │ └── migration.sql │ ├── 20240509195503_custom_themes │ └── migration.sql │ ├── 20241113055815_delete_on_cascade │ └── migration.sql │ ├── 20240525011153_hours_optional │ └── migration.sql │ ├── 20250815193739_email_verified │ └── migration.sql │ ├── 20241105053242_stripe │ └── migration.sql │ ├── 20240503001713_menu_theme │ └── migration.sql │ ├── 20240926052957_add_team_invite │ └── migration.sql │ ├── 20240520040502_published_date │ └── migration.sql │ ├── 20241204063109_add_featured │ └── migration.sql │ ├── 20250820015141_org_plugin │ └── migration.sql │ ├── 20251210070459_add_currency_to_menuitem │ └── migration.sql │ ├── 20251213000523_add_location_services │ └── migration.sql │ ├── 20250825044935_remove_subdomain │ └── migration.sql │ ├── 20241113055408_delete_on_cascade │ └── migration.sql │ ├── 20241108020315_add_org_suscription │ └── migration.sql │ └── 20250815194210_remove_unused_auth_fields │ └── migration.sql ├── postcss.config.js ├── .deepsource.toml ├── next-env.d.ts ├── prisma.config.ts ├── sentry.server.config.ts ├── .github └── dependabot.yml ├── components.json ├── sentry.edge.config.ts ├── .gitignore ├── prettier.config.mjs ├── content-collections.ts ├── tsconfig.json ├── content └── blog │ └── beta-biztro.mdx ├── eslint.config.mjs └── sentry.client.config.ts /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | bun lint-staged 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | npx --no --commitlint --edit ${1} 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .next 4 | build 5 | next-env.d.ts -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/logo.png -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /public/dkast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/dkast.jpg -------------------------------------------------------------------------------- /public/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/editor.png -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/menu-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/menu-back.png -------------------------------------------------------------------------------- /public/og-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/og-image.jpg -------------------------------------------------------------------------------- /public/products.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/products.png -------------------------------------------------------------------------------- /public/Sora-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/Sora-Medium.ttf -------------------------------------------------------------------------------- /public/biztro-hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/biztro-hero.png -------------------------------------------------------------------------------- /public/editor-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/editor-dark.png -------------------------------------------------------------------------------- /public/iphone-hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/iphone-hero.png -------------------------------------------------------------------------------- /public/menu-front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/menu-front.png -------------------------------------------------------------------------------- /public/qr-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/qr-example.png -------------------------------------------------------------------------------- /public/Inter-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/Inter-SemiBold.ttf -------------------------------------------------------------------------------- /public/configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/configuration.png -------------------------------------------------------------------------------- /public/editor-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/editor-light.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/mstile-144x144.png -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/mstile-150x150.png -------------------------------------------------------------------------------- /public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/mstile-310x150.png -------------------------------------------------------------------------------- /public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/mstile-310x310.png -------------------------------------------------------------------------------- /public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/mstile-70x70.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /src/app/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/src/app/opengraph-image.png -------------------------------------------------------------------------------- /src/emails/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/src/emails/static/logo.png -------------------------------------------------------------------------------- /public/bg/bg-center-cafe-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-cafe-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-center-cafe-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-cafe-2.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-bakery-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-bakery-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-bakery-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-bakery-2.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-burger-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-burger-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-fusion-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-fusion-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-fusion-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-fusion-2.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-salad-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-salad-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-tacos-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-tacos-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-tacos-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-tacos-2.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-tomates-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-tomates-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-center-pizza-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-pizza-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-center-sushi-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-sushi-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-center-sushi-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-sushi-2.jpg -------------------------------------------------------------------------------- /public/bg/bg-center-tacos-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-tacos-3.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-breakfast-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-breakfast-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-breakfast-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-breakfast-2.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-ice-cream-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-ice-cream-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-ice-cream-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-ice-cream-2.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-mariscos-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-mariscos-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-top-mariscos-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-top-mariscos-2.jpg -------------------------------------------------------------------------------- /src/app/dashboard/menu-items/@modal/default.tsx: -------------------------------------------------------------------------------- 1 | export default function Default() { 2 | return null 3 | } 4 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/bg/bg-center-ice-cream-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-ice-cream-3.jpg -------------------------------------------------------------------------------- /public/bg/bg-center-molcajete-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-molcajete-1.jpg -------------------------------------------------------------------------------- /public/bg/bg-center-parrilla-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkast/biztro/HEAD/public/bg/bg-center-parrilla-1.jpg -------------------------------------------------------------------------------- /prisma/migrations/20251016003645_remove_name_unique_prop/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "Organization_name_key"; 3 | -------------------------------------------------------------------------------- /prisma/migrations/20240417054412_add_draft/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Menu" 3 | ADD COLUMN "publishedData" TEXT; -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | "@tailwindcss/postcss": {} 4 | } 5 | } 6 | 7 | module.exports = config 8 | -------------------------------------------------------------------------------- /prisma/migrations/20241205013525_add_allergens/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MenuItem" ADD COLUMN "allergens" TEXT; 3 | -------------------------------------------------------------------------------- /src/app/config.ts: -------------------------------------------------------------------------------- 1 | export const appConfig = { 2 | cookieOrg: "current-organization", 3 | itemLimit: 10, 4 | menuLimit: 1 5 | } as const 6 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "sqlite" 4 | -------------------------------------------------------------------------------- /src/app/menu-editor/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | export default function Layout({ children }: { children: React.ReactNode }) { 2 | return
{children}
3 | } 4 | -------------------------------------------------------------------------------- /src/app/api/auth/[...all]/route.ts: -------------------------------------------------------------------------------- 1 | import { toNextJsHandler } from "better-auth/next-js" 2 | 3 | import { auth } from "@/lib/auth" 4 | 5 | export const { GET, POST } = toNextJsHandler(auth) 6 | -------------------------------------------------------------------------------- /src/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "secrets" 5 | 6 | [[analyzers]] 7 | name = "javascript" 8 | 9 | [analyzers.meta] 10 | plugins = ["react"] 11 | environment = ["nodejs", "browser"] 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "eslint", 6 | "problemMatcher": ["$eslint-stylish"], 7 | "label": "eslint: lint whole folder" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/stripe.ts: -------------------------------------------------------------------------------- 1 | import Stripe from "stripe" 2 | 3 | import { env } from "@/env.mjs" 4 | 5 | export const stripe = new Stripe(env.STRIPE_SECRET_KEY, { 6 | //@ts-expect-error - This is a valid option 7 | apiVersion: "2020-08-27" 8 | }) 9 | -------------------------------------------------------------------------------- /src/app/dashboard/loading.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from "@/components/ui/spinner" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/app/(auth)/auth-error/loading.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from "@/components/ui/spinner" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/app/(auth)/invite/[id]/loading.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from "@/components/ui/spinner" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/app/(auth)/new-org/loading.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from "@/components/ui/spinner" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/app/dashboard/settings/loading.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from "@/components/ui/spinner" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/app/menu-editor/[id]/loading.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from "@/components/ui/spinner" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/app/dashboard/menu-items/loading.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from "@/components/ui/spinner" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | import "./.next/dev/types/routes.d.ts"; 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 7 | -------------------------------------------------------------------------------- /src/app/dashboard/menu-items/categories/loading.tsx: -------------------------------------------------------------------------------- 1 | import Spinner from "@/components/ui/spinner" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/app/.well-known/vercel/flags/route.ts: -------------------------------------------------------------------------------- 1 | import { createFlagsDiscoveryEndpoint, getProviderData } from "flags/next" 2 | 3 | import * as flags from "../../../../flags" 4 | 5 | export const GET = createFlagsDiscoveryEndpoint(async () => { 6 | return await getProviderData(flags) 7 | }) 8 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #603cba 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /prisma/migrations/20241113053647_add_indexes/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateIndex 2 | CREATE INDEX "Customer_membershipId_idx" ON "Customer"("membershipId"); 3 | 4 | -- CreateIndex 5 | CREATE INDEX "Organization_subdomain_idx" ON "Organization"("subdomain"); 6 | 7 | -- CreateIndex 8 | CREATE INDEX "Subscription_membershipId_idx" ON "Subscription"("membershipId"); 9 | -------------------------------------------------------------------------------- /src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /src/flags.ts: -------------------------------------------------------------------------------- 1 | import { flag } from "flags/next" 2 | 3 | export const subscriptionsEnabled = flag({ 4 | key: "enable-suscriptions", 5 | description: "Enable suscriptions feature", 6 | decide: () => process.env.FLAGS_ENABLE_SUBSCRIPTIONS === "1", 7 | defaultValue: false 8 | }) 9 | 10 | export const precomputedFlags = [subscriptionsEnabled] as const 11 | -------------------------------------------------------------------------------- /prisma.config.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { defineConfig } from "prisma/config" 3 | 4 | export default defineConfig({ 5 | schema: path.join("prisma"), 6 | datasource: { 7 | // Use process.env with fallback for prisma generate (which doesn't need a DB connection) 8 | url: process.env.LOCAL_DATABASE_URL ?? "file:./local.db" 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /prisma/migrations/20241106062214_add_unique_to_membership/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[membershipId]` on the table `Customer` will be added. If there are existing duplicate values, this will fail. 5 | 6 | */ 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "Customer_membershipId_key" ON "Customer"("membershipId"); 9 | -------------------------------------------------------------------------------- /public/twitter-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.vscode/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": { 3 | "shadcn": { 4 | "command": "npx", 5 | "args": ["shadcn@latest", "mcp"] 6 | }, 7 | "next-devtools": { 8 | "command": "npx", 9 | "args": ["-y", "next-devtools-mcp@latest"] 10 | }, 11 | "motion": { 12 | "command": "npx", 13 | "args": ["-y", "motion-ai"] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/stripe-client.ts: -------------------------------------------------------------------------------- 1 | import { loadStripe, type Stripe } from "@stripe/stripe-js" 2 | 3 | import { env } from "@/env.mjs" 4 | 5 | let stripePromise: Promise 6 | 7 | export const getStripeClient = () => { 8 | if (!stripePromise) { 9 | stripePromise = loadStripe(env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY) 10 | } 11 | 12 | return stripePromise 13 | } 14 | -------------------------------------------------------------------------------- /src/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /src/lib/session.ts: -------------------------------------------------------------------------------- 1 | import { headers } from "next/headers" 2 | 3 | import { auth } from "@/lib/auth" 4 | 5 | export async function getCurrentUser() { 6 | try { 7 | const session = await auth.api.getSession({ headers: await headers() }) 8 | return session?.user ?? null 9 | } catch (error) { 10 | console.error("Error fetching current user:", error) 11 | return null 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /public/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/hooks/use-forwarded-ref.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export function useForwardedRef(ref: React.ForwardedRef) { 4 | const innerRef = React.useRef(null) 5 | 6 | React.useEffect(() => { 7 | if (!ref) return 8 | if (typeof ref === "function") { 9 | ref(innerRef.current) 10 | } else { 11 | ref.current = innerRef.current 12 | } 13 | }) 14 | 15 | return innerRef 16 | } 17 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/facebook-mono.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /src/app/(content)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Footer from "@/components/marketing/footer" 2 | import Navbar from "@/components/marketing/nav-bar" 3 | 4 | export default function BlogLayout({ 5 | children 6 | }: { 7 | children?: React.ReactNode 8 | }) { 9 | return ( 10 |
11 | 12 |
13 | {children} 14 |
15 |
16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/components/flare-ui/gradient-blur.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | export default function GradientBlur({ className }: { className?: string }) { 4 | return ( 5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/instrumentation.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from "@sentry/nextjs" 2 | 3 | export async function register() { 4 | // Don't register Sentry in TurboPack 5 | if (process.env.TURBOPACK) { 6 | return 7 | } 8 | 9 | if (process.env.NEXT_RUNTIME === "nodejs") { 10 | await import("../sentry.server.config") 11 | } 12 | 13 | if (process.env.NEXT_RUNTIME === "edge") { 14 | await import("../sentry.edge.config") 15 | } 16 | } 17 | 18 | export const onRequestError = Sentry.captureRequestError 19 | -------------------------------------------------------------------------------- /src/components/confetti-on-mount.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useEffect } from "react" 4 | import confetti from "canvas-confetti" 5 | 6 | export default function ConfettiOnMount() { 7 | useEffect(() => { 8 | confetti({ 9 | particleCount: 200, 10 | spread: 180, 11 | origin: { y: 0.1 }, 12 | ticks: 150 13 | }) 14 | }, []) 15 | 16 | // This component only triggers the confetti effect on mount and 17 | // doesn't render any visible DOM nodes. 18 | return null 19 | } 20 | -------------------------------------------------------------------------------- /src/components/marketing/mdx.tsx: -------------------------------------------------------------------------------- 1 | import { MDXContent } from "@content-collections/mdx/react" 2 | 3 | interface MdxProps { 4 | code: string 5 | } 6 | 7 | const Mdx = ({ code }: MdxProps) => { 8 | return ( 9 |
10 | 11 |
12 | ) 13 | } 14 | 15 | export default Mdx 16 | -------------------------------------------------------------------------------- /src/components/ui/date-time-picker/time-picker.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import React from "react" 4 | import { type TimeValue } from "react-aria" 5 | import { type TimeFieldStateOptions } from "react-stately" 6 | 7 | import { TimeField } from "./time-field" 8 | 9 | const TimePicker = React.forwardRef< 10 | HTMLDivElement, 11 | Omit, "locale"> 12 | >(props => { 13 | return 14 | }) 15 | 16 | TimePicker.displayName = "TimePicker" 17 | 18 | export { TimePicker } 19 | -------------------------------------------------------------------------------- /src/proxy.ts: -------------------------------------------------------------------------------- 1 | import { getSessionCookie } from "better-auth/cookies" 2 | import type { NextRequest } from "next/server" 3 | import { NextResponse } from "next/server" 4 | 5 | export async function proxy(request: NextRequest) { 6 | const sessionCookie = getSessionCookie(request) 7 | if (!sessionCookie) { 8 | return NextResponse.redirect(new URL("/login", request.url)) 9 | } 10 | return NextResponse.next() 11 | } 12 | 13 | // Update matcher to the routes you want to protect 14 | export const config = { 15 | matcher: ["/dashboard/:path*"] 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/prisma.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@/generated/prisma-client/client" 2 | import { PrismaLibSql } from "@prisma/adapter-libsql" 3 | 4 | import { env } from "@/env.mjs" 5 | 6 | // Cast to any to avoid a type mismatch between different @libsql/client copies. 7 | const adapter = new PrismaLibSql({ 8 | url: env.TURSO_DATABASE_URL, 9 | authToken: env.TURSO_AUTH_TOKEN 10 | }) 11 | const prisma = new PrismaClient({ 12 | adapter, 13 | log: env.NODE_ENV === "development" ? ["info", "warn", "error"] : ["error"] 14 | }) 15 | 16 | export default prisma 17 | -------------------------------------------------------------------------------- /prisma/migrations/20240525004038_add_open_hours/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "OpeningHours" ( 3 | "id" TEXT NOT NULL PRIMARY KEY, 4 | "day" TEXT NOT NULL, 5 | "startTime" TEXT NOT NULL, 6 | "endTime" TEXT NOT NULL, 7 | "allDay" BOOLEAN NOT NULL, 8 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | "updatedAt" DATETIME NOT NULL, 10 | "locationId" TEXT NOT NULL, 11 | CONSTRAINT "OpeningHours_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location" ("id") ON DELETE CASCADE ON UPDATE CASCADE 12 | ); 13 | -------------------------------------------------------------------------------- /src/app/dashboard/menu-items/[action]/[id]/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/components/ui/skeleton" 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /prisma/migrations/20251009054308_remove_deprecated_tables/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `Customer_Removed` table. If the table is not empty, all the data it contains will be lost. 5 | - You are about to drop the `Subscription_Removed` table. If the table is not empty, all the data it contains will be lost. 6 | 7 | */ 8 | -- DropTable 9 | PRAGMA foreign_keys=off; 10 | DROP TABLE "Customer_Removed"; 11 | PRAGMA foreign_keys=on; 12 | 13 | -- DropTable 14 | PRAGMA foreign_keys=off; 15 | DROP TABLE "Subscription_Removed"; 16 | PRAGMA foreign_keys=on; 17 | -------------------------------------------------------------------------------- /src/server/actions/location/queries.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { cacheTag } from "next/cache" 4 | 5 | import prisma from "@/lib/prisma" 6 | 7 | export async function getDefaultLocation(organizationId: string) { 8 | "use cache" 9 | 10 | if (!organizationId) { 11 | return null 12 | } 13 | 14 | cacheTag(`locations-${organizationId}`) 15 | return await prisma.location.findFirst({ 16 | where: { 17 | organizationId 18 | }, 19 | include: { 20 | openingHours: true 21 | }, 22 | orderBy: { 23 | createdAt: "asc" 24 | } 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /prisma/migrations/20251008005305_stripe_plugin/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "stripeCustomerId" TEXT; 3 | 4 | -- CreateTable 5 | CREATE TABLE "Subscription" ( 6 | "id" TEXT NOT NULL PRIMARY KEY, 7 | "plan" TEXT NOT NULL, 8 | "referenceId" TEXT NOT NULL, 9 | "stripeCustomerId" TEXT, 10 | "stripeSubscriptionId" TEXT, 11 | "status" TEXT, 12 | "periodStart" DATETIME, 13 | "periodEnd" DATETIME, 14 | "trialStart" DATETIME, 15 | "trialEnd" DATETIME, 16 | "cancelAtPeriodEnd" BOOLEAN DEFAULT false, 17 | "seats" INTEGER 18 | ); 19 | -------------------------------------------------------------------------------- /prisma/migrations/20240427172632_invite_flag/migration.sql: -------------------------------------------------------------------------------- 1 | -- RedefineTables 2 | PRAGMA foreign_keys=OFF; 3 | CREATE TABLE "new_Invite" ( 4 | "id" TEXT NOT NULL PRIMARY KEY, 5 | "email" TEXT NOT NULL, 6 | "enabled" BOOLEAN NOT NULL DEFAULT false 7 | ); 8 | INSERT INTO "new_Invite" ("email", "id") SELECT "email", "id" FROM "Invite"; 9 | DROP TABLE "Invite"; 10 | ALTER TABLE "new_Invite" RENAME TO "Invite"; 11 | CREATE UNIQUE INDEX "Invite_email_key" ON "Invite"("email"); 12 | CREATE INDEX "Invite_email_idx" ON "Invite"("email"); 13 | PRAGMA foreign_key_check; 14 | PRAGMA foreign_keys=ON; 15 | -------------------------------------------------------------------------------- /public/tiktok-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/components/dashboard/tooltip-helper.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Tooltip, 3 | TooltipContent, 4 | TooltipProvider, 5 | TooltipTrigger 6 | } from "@/components/ui/tooltip" 7 | 8 | export const TooltipHelper = ({ 9 | children, 10 | content 11 | }: { 12 | children: React.ReactNode 13 | content: React.ReactNode 14 | }) => ( 15 | 16 | 17 | {children} 18 | 19 | {content} 20 | 21 | 22 | 23 | ) 24 | -------------------------------------------------------------------------------- /src/components/icons/twitter-icon.tsx: -------------------------------------------------------------------------------- 1 | import { type ComponentProps } from "react" 2 | 3 | export function TwitterIcon(props: ComponentProps<"svg">) { 4 | return ( 5 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/components/ui/image-viewer.tsx: -------------------------------------------------------------------------------- 1 | import { PhotoView } from "react-photo-view" 2 | import Image from "next/image" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | export default function ImageViewer({ 7 | src, 8 | className 9 | }: { 10 | src: string 11 | className?: string 12 | }) { 13 | return ( 14 | 15 | 26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /sentry.server.config.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the server. 2 | // The config you add here will be used whenever the server handles a request. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import * as Sentry from "@sentry/nextjs" 6 | 7 | Sentry.init({ 8 | dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, 9 | 10 | // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. 11 | tracesSampleRate: 1, 12 | 13 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 14 | debug: false 15 | }) 16 | -------------------------------------------------------------------------------- /src/lib/auth-client.ts: -------------------------------------------------------------------------------- 1 | import { stripeClient } from "@better-auth/stripe/client" 2 | import { 3 | inferOrgAdditionalFields, 4 | organizationClient 5 | } from "better-auth/client/plugins" 6 | import { createAuthClient } from "better-auth/react" 7 | 8 | import type { auth } from "@/lib/auth" 9 | 10 | export const authClient = createAuthClient({ 11 | plugins: [ 12 | // Add any necessary plugins here 13 | organizationClient({ 14 | schema: inferOrgAdditionalFields() 15 | }), 16 | stripeClient({ 17 | subscription: true 18 | }) 19 | ] 20 | }) 21 | export const { signIn, signUp, useSession, signOut } = authClient 22 | -------------------------------------------------------------------------------- /src/hooks/use-mobile.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | const MOBILE_BREAKPOINT = 768 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState(undefined) 7 | 8 | React.useEffect(() => { 9 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) 10 | const onChange = () => { 11 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 12 | } 13 | mql.addEventListener("change", onChange) 14 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 15 | return () => mql.removeEventListener("change", onChange) 16 | }, []) 17 | 18 | return !!isMobile 19 | } 20 | -------------------------------------------------------------------------------- /src/components/dashboard/info-helper.tsx: -------------------------------------------------------------------------------- 1 | import { Info } from "lucide-react" 2 | 3 | import { 4 | HoverCard, 5 | HoverCardContent, 6 | HoverCardTrigger 7 | } from "@/components/ui/hover-card" 8 | 9 | export default function InfoHelper({ 10 | children 11 | }: { 12 | children: React.ReactNode 13 | }) { 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | {children} 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /prisma/migrations/20251018174951_rename_invite_to_waitlist/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `Invite` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropTable 8 | PRAGMA foreign_keys=off; 9 | DROP TABLE "Invite"; 10 | PRAGMA foreign_keys=on; 11 | 12 | -- CreateTable 13 | CREATE TABLE "Waitlist" ( 14 | "id" TEXT NOT NULL PRIMARY KEY, 15 | "email" TEXT NOT NULL, 16 | "enabled" BOOLEAN NOT NULL DEFAULT false 17 | ); 18 | 19 | -- CreateIndex 20 | CREATE UNIQUE INDEX "Waitlist_email_key" ON "Waitlist"("email"); 21 | 22 | -- CreateIndex 23 | CREATE INDEX "Waitlist_email_idx" ON "Waitlist"("email"); 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | commit-message: 13 | prefix: "chore(deps):" 14 | versioning-strategy: "increase" 15 | open-pull-requests-limit: 0 16 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "styles/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "iconLibrary": "lucide", 14 | "aliases": { 15 | "components": "@/components", 16 | "utils": "@/lib/utils", 17 | "ui": "@/components/ui", 18 | "lib": "@/lib", 19 | "hooks": "@/hooks" 20 | }, 21 | "registries": { 22 | "@magicui": "https://magicui.design/r/{name}.json", 23 | "@kibo-ui": "https://www.kibo-ui.com/r/{name}.json" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/dashboard/menu-items/layout.tsx: -------------------------------------------------------------------------------- 1 | import SecondaryNav from "@/components/dashboard/secondary-nav" 2 | 3 | const SecondaryNavItems = [ 4 | { 5 | title: "Productos", 6 | href: "dashboard/menu-items" 7 | }, 8 | { 9 | title: "Categorías", 10 | href: "dashboard/menu-items/categories" 11 | } 12 | ] 13 | 14 | export default function Layout({ 15 | children, 16 | modal 17 | }: { 18 | children: React.ReactNode 19 | modal: React.ReactNode 20 | }) { 21 | return ( 22 | <> 23 | 24 |
{modal}
25 |
{children}
26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/atoms.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai" 2 | import { atomWithStorage } from "jotai/utils" 3 | 4 | import { FrameSize, type colorThemes } from "@/lib/types" 5 | 6 | export const elementPropsAtom = atom<{ [x: string]: unknown }>({}) 7 | 8 | export const frameSizeAtom = atomWithStorage("frameSize", FrameSize.MOBILE) 9 | 10 | export const fontThemeAtom = atom("DEFAULT") 11 | 12 | export const colorThemeAtom = atom("DEFAULT") 13 | 14 | export const tourModeAtom = atomWithStorage("tourMode", true) 15 | 16 | export const onboardingCardsCollapsedAtom = atomWithStorage( 17 | "onboardingCardsCollapsed", 18 | false 19 | ) 20 | 21 | export const colorListAtom = atom<(typeof colorThemes)[0][]>([]) 22 | -------------------------------------------------------------------------------- /src/app/dashboard/menu-items/categories/category-table.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useDataTable } from "@/hooks/use-data-table" 4 | import type { Category } from "@/generated/prisma-client/client" 5 | 6 | import { DataTable } from "@/components/data-table/data-table" 7 | import { columns } from "./columns" 8 | 9 | export default function CategoryTable({ data }: { data: Category[] }) { 10 | const { table, globalFilter, setGlobalFilter } = useDataTable({ 11 | data, 12 | columns 13 | }) 14 | 15 | return ( 16 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/dashboard/onboarding-progress.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { Gauge } from "@suyalcinkaya/gauge" 4 | import { useTheme } from "next-themes" 5 | 6 | export default function OnboardingProgress({ progres }: { progres: number }) { 7 | const theme = useTheme() 8 | return ( 9 |
10 | Progreso 11 | 19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/components/icons/facebook-icon.tsx: -------------------------------------------------------------------------------- 1 | import { type ComponentProps } from "react" 2 | 3 | export function FacebookIcon(props: ComponentProps<"svg">) { 4 | return ( 5 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /prisma/migrations/20240509195503_custom_themes/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Theme" ( 3 | "id" TEXT NOT NULL PRIMARY KEY, 4 | "name" TEXT NOT NULL, 5 | "scope" TEXT NOT NULL DEFAULT 'GLOBAL', 6 | "themeType" TEXT NOT NULL DEFAULT 'FONT', 7 | "themeJSON" TEXT NOT NULL DEFAULT '{}', 8 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | "updatedAt" DATETIME NOT NULL, 10 | "organizationId" TEXT, 11 | CONSTRAINT "Theme_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE 12 | ); 13 | 14 | -- CreateIndex 15 | CREATE UNIQUE INDEX "Theme_organizationId_name_scope_key" ON "Theme"("organizationId", "name", "scope"); 16 | -------------------------------------------------------------------------------- /src/app/dashboard/settings/layout.tsx: -------------------------------------------------------------------------------- 1 | import SecondaryNav from "@/components/dashboard/secondary-nav" 2 | 3 | const SecondaryNavItems = [ 4 | { 5 | title: "General", 6 | href: "dashboard/settings" 7 | }, 8 | { 9 | title: "Sucursal", 10 | href: "dashboard/settings/locations" 11 | }, 12 | { 13 | title: "Miembros", 14 | href: "dashboard/settings/members" 15 | }, 16 | { 17 | title: "Suscripción", 18 | href: "dashboard/settings/billing" 19 | } 20 | ] 21 | 22 | export default function Layout({ children }: { children: React.ReactNode }) { 23 | return ( 24 | <> 25 | 26 |
{children}
27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/components/marketing/title-section.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | export default function TitleSection({ 4 | eyebrow, 5 | title, 6 | className 7 | }: { 8 | eyebrow: string 9 | title: string 10 | className?: string 11 | }) { 12 | return ( 13 |
19 | 20 | {eyebrow} 21 | 22 |

23 | {title} 24 |

25 |
26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/app/dashboard/create-org/page.tsx: -------------------------------------------------------------------------------- 1 | import { Store } from "lucide-react" 2 | 3 | import PageSubtitle from "@/components/dashboard/page-subtitle" 4 | import NewOrgForm from "../../(auth)/new-org/new-org-form" 5 | 6 | export const metadata = { 7 | title: "Crear Organización", 8 | description: "Crea y configura tu negocio en Biztro" 9 | } 10 | 11 | export default function Page() { 12 | return ( 13 |
14 |
15 | 20 | 21 |
22 |
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/components/tailwind-indicator.tsx: -------------------------------------------------------------------------------- 1 | export function TailwindIndicator() { 2 | if (process.env.NODE_ENV === "production") return null 3 | 4 | return ( 5 |
6 |
xs
7 |
8 | sm 9 |
10 |
md
11 |
lg
12 |
xl
13 |
2xl
14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/components/dashboard/empty-state.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | // Empty state 3 | 4 | export function EmptyState({ 5 | icon, 6 | title 7 | }: { 8 | icon?: React.ReactNode 9 | title: string 10 | }) { 11 | return ( 12 |
13 | {icon && ( 14 |
15 |
16 | {icon} 17 |
18 |
19 | )} 20 | {title} 21 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/export-as-image.ts: -------------------------------------------------------------------------------- 1 | import html2canvas from "html2canvas" 2 | 3 | const exportAsImage = async ( 4 | element: HTMLElement, 5 | imageFilename: string 6 | ): Promise => { 7 | const canvas = await html2canvas(element) 8 | const image = canvas.toDataURL("image/png", 1.0) 9 | 10 | downloadImage(image, imageFilename) 11 | } 12 | 13 | const downloadImage = (blob: string, filename: string): void => { 14 | const fakeLink: HTMLAnchorElement = window.document.createElement("a") 15 | 16 | fakeLink.className = "hidden" 17 | fakeLink.download = filename 18 | fakeLink.href = blob 19 | 20 | document.body.appendChild(fakeLink) 21 | fakeLink.click() 22 | document.body.removeChild(fakeLink) 23 | 24 | fakeLink.remove() 25 | } 26 | 27 | export default exportAsImage 28 | -------------------------------------------------------------------------------- /src/server/actions/subscriptions/mutations.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { updateTag } from "next/cache" 4 | import { headers } from "next/headers" 5 | 6 | import { auth } from "@/lib/auth" 7 | import { getBaseUrl } from "@/lib/utils" 8 | 9 | export const createStripePortal = async (referenceId: string) => { 10 | try { 11 | const data = await auth.api.createBillingPortal({ 12 | body: { 13 | referenceId, 14 | returnUrl: `${getBaseUrl()}/dashboard/settings/billing` 15 | }, 16 | headers: await headers() 17 | }) 18 | 19 | updateTag(`organization-${referenceId}-subscription`) 20 | updateTag("subscription-current") 21 | 22 | return data.url 23 | } catch (error) { 24 | console.error(error) 25 | return null 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /public/bg/leaf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/server/actions/subscriptions/queries.ts: -------------------------------------------------------------------------------- 1 | import { cacheLife, cacheTag } from "next/cache" 2 | import { headers } from "next/headers" 3 | 4 | import { auth } from "@/lib/auth" 5 | 6 | // Get the current active subscription in the organization 7 | export const getCurrentSubscription = async (organizationId: string) => { 8 | "use cache: private" 9 | cacheTag(`organization-${organizationId}-subscription`) 10 | cacheLife({ stale: 60 }) 11 | 12 | const subscriptions = await auth.api.listActiveSubscriptions({ 13 | query: { 14 | referenceId: organizationId 15 | }, 16 | headers: await headers() 17 | }) 18 | 19 | const activeSubscription = subscriptions.find( 20 | sub => sub.status === "active" || sub.status === "trialing" 21 | ) 22 | 23 | return activeSubscription 24 | } 25 | -------------------------------------------------------------------------------- /sentry.edge.config.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on). 2 | // The config you add here will be used whenever one of the edge features is loaded. 3 | // Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally. 4 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 5 | 6 | import * as Sentry from "@sentry/nextjs" 7 | 8 | Sentry.init({ 9 | dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, 10 | 11 | // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. 12 | tracesSampleRate: 1, 13 | 14 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 15 | debug: false 16 | }) 17 | -------------------------------------------------------------------------------- /src/app/global-error.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useEffect } from "react" 4 | import * as Sentry from "@sentry/nextjs" 5 | import NextError from "next/error" 6 | 7 | export default function GlobalError({ 8 | error 9 | }: { 10 | error: Error & { digest?: string } 11 | }) { 12 | useEffect(() => { 13 | Sentry.captureException(error) 14 | }, [error]) 15 | 16 | return ( 17 | 18 | 19 | {/* `NextError` is the default Next.js error page component. Its type 20 | definition requires a `statusCode` prop. However, since the App Router 21 | does not expose status codes for errors, we simply pass 0 to render a 22 | generic error message. */} 23 | 24 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/app/dashboard/settings/members/member-table.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { DataTable } from "@/components/data-table/data-table" 4 | import { useDataTable } from "@/hooks/use-data-table" 5 | import type { AuthMember } from "@/lib/auth" 6 | import { getColumns } from "./columns" 7 | 8 | export default function MemberTable({ 9 | data, 10 | canDeleteMember 11 | }: { 12 | data: AuthMember[] 13 | canDeleteMember: boolean 14 | }) { 15 | 16 | const cols = getColumns(canDeleteMember) 17 | 18 | const { table, globalFilter, setGlobalFilter } = useDataTable({ 19 | data, 20 | columns: cols 21 | }) 22 | 23 | return ( 24 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /src/hooks/use-rect.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | 3 | export const useRect = ( // skipcq: JS-0356 4 | dom: HTMLElement | null 5 | ): DOMRect | undefined => { 6 | // const ref = useRef(null) 7 | const [rect, setRect] = useState() 8 | 9 | const set = () => setRect(dom?.getBoundingClientRect()) 10 | 11 | const useEffectInEvent = ( 12 | event: "resize" | "scroll", 13 | useCapture?: boolean 14 | ) => { 15 | useEffect(() => { 16 | set() 17 | window.addEventListener(event, set, useCapture) 18 | return () => window.removeEventListener(event, set, useCapture) 19 | }, []) // eslint-disable-line react-hooks/exhaustive-deps 20 | } 21 | 22 | useEffectInEvent("resize") 23 | useEffectInEvent("scroll", true) 24 | 25 | return rect 26 | } 27 | -------------------------------------------------------------------------------- /prisma/migrations/20241113055815_delete_on_cascade/migration.sql: -------------------------------------------------------------------------------- 1 | -- RedefineTables 2 | PRAGMA defer_foreign_keys=ON; 3 | PRAGMA foreign_keys=OFF; 4 | CREATE TABLE "new_Customer" ( 5 | "stripeCustomerId" TEXT NOT NULL PRIMARY KEY, 6 | "membershipId" TEXT NOT NULL, 7 | CONSTRAINT "Customer_membershipId_fkey" FOREIGN KEY ("membershipId") REFERENCES "Membership" ("id") ON DELETE CASCADE ON UPDATE CASCADE 8 | ); 9 | INSERT INTO "new_Customer" ("membershipId", "stripeCustomerId") SELECT "membershipId", "stripeCustomerId" FROM "Customer"; 10 | DROP TABLE "Customer"; 11 | ALTER TABLE "new_Customer" RENAME TO "Customer"; 12 | CREATE UNIQUE INDEX "Customer_membershipId_key" ON "Customer"("membershipId"); 13 | CREATE INDEX "Customer_membershipId_idx" ON "Customer"("membershipId"); 14 | PRAGMA foreign_keys=ON; 15 | PRAGMA defer_foreign_keys=OFF; 16 | -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { 6 | return ( 7 |