├── with-platform-supabase-tailwind-prisma ├── supabase │ └── .gitignore ├── src │ ├── app │ │ ├── api │ │ │ ├── auth │ │ │ │ └── [...nextauth] │ │ │ │ │ └── route.ts │ │ │ ├── supabase │ │ │ │ └── storage │ │ │ │ │ └── route.ts │ │ │ └── cal │ │ │ │ └── refresh │ │ │ │ └── route.ts │ │ ├── dashboard │ │ │ ├── settings │ │ │ │ ├── page.tsx │ │ │ │ ├── availability │ │ │ │ │ └── page.tsx │ │ │ │ ├── layout.tsx │ │ │ │ ├── booking-events │ │ │ │ │ ├── event-type-delete.tsx │ │ │ │ │ ├── event-type-create.tsx │ │ │ │ │ └── _actions.ts │ │ │ │ ├── _components │ │ │ │ │ ├── settings-content.tsx │ │ │ │ │ ├── expert-edit.tsx │ │ │ │ │ └── supabase-react-dropzone.tsx │ │ │ │ └── profile │ │ │ │ │ └── page.tsx │ │ │ ├── getting-started │ │ │ │ └── page.tsx │ │ │ ├── @breadcrumbs │ │ │ │ ├── page.tsx │ │ │ │ └── [...dashboardSegments] │ │ │ │ │ └── page.tsx │ │ │ ├── data.tsx │ │ │ ├── @dashboardNavigationDesktop │ │ │ │ ├── page.tsx │ │ │ │ └── [...dashboardSegments] │ │ │ │ │ └── page.tsx │ │ │ ├── @dashboardNavigationMobile │ │ │ │ ├── page.tsx │ │ │ │ └── [...dashboardSegments] │ │ │ │ │ └── page.tsx │ │ │ ├── _components │ │ │ │ ├── connect-calendar-step.tsx │ │ │ │ ├── getting-started-steps.tsx │ │ │ │ ├── user-details-step.tsx │ │ │ │ └── user-filters-step.tsx │ │ │ └── layout.tsx │ │ ├── login │ │ │ ├── page.tsx │ │ │ ├── _components │ │ │ │ ├── input.tsx │ │ │ │ └── login.tsx │ │ │ └── layout.tsx │ │ ├── signup │ │ │ ├── page.tsx │ │ │ ├── _components │ │ │ │ ├── input.tsx │ │ │ │ └── signup.tsx │ │ │ └── layout.tsx │ │ ├── [expertUsername] │ │ │ ├── booking │ │ │ │ └── [bookingUid] │ │ │ │ │ └── page.tsx │ │ │ ├── _components │ │ │ │ ├── Container.tsx │ │ │ │ ├── AboutSection.tsx │ │ │ │ └── expert-booker.tsx │ │ │ ├── layout.tsx │ │ │ └── [eventSlug] │ │ │ │ └── page.tsx │ │ ├── _components │ │ │ ├── universal │ │ │ │ ├── logo.tsx │ │ │ │ ├── hero.tsx │ │ │ │ └── layout.tsx │ │ │ ├── search-bar.tsx │ │ │ ├── submit-button.tsx │ │ │ ├── home │ │ │ │ ├── signup-card.tsx │ │ │ │ └── sidebar-item.tsx │ │ │ ├── use-cal.tsx │ │ │ ├── autocomplete.tsx │ │ │ ├── multi-select.tsx │ │ │ ├── banner.tsx │ │ │ └── navigation.tsx │ │ ├── providers.tsx │ │ ├── tailwind-indicator.tsx │ │ ├── _searchParams.ts │ │ ├── page.tsx │ │ └── layout.tsx │ ├── fonts │ │ ├── CalSans-SemiBold.ttf │ │ └── CalSans-SemiBold.woff2 │ ├── components │ │ └── ui │ │ │ ├── skeleton.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── label.tsx │ │ │ ├── textarea.tsx │ │ │ ├── separator.tsx │ │ │ ├── input.tsx │ │ │ ├── progress.tsx │ │ │ ├── toaster.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── tooltip.tsx │ │ │ ├── badge.tsx │ │ │ ├── popover.tsx │ │ │ ├── avatar.tsx │ │ │ ├── button.tsx │ │ │ ├── card.tsx │ │ │ ├── tabs.tsx │ │ │ ├── accordion.tsx │ │ │ ├── table.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── pagination.tsx │ │ │ ├── dialog.tsx │ │ │ ├── sheet.tsx │ │ │ ├── form.tsx │ │ │ ├── use-toast.ts │ │ │ ├── toast.tsx │ │ │ ├── navigation-menu.tsx │ │ │ └── command.tsx │ ├── lib │ │ ├── supabase-image-loader.ts │ │ ├── constants.ts │ │ └── utils.ts │ ├── middleware.ts │ ├── cal │ │ ├── utils.ts │ │ └── auth.ts │ ├── styles │ │ └── globals.css │ ├── env.js │ └── auth │ │ └── config.edge.ts ├── postcss.config.cjs ├── public │ ├── hero.jpg │ └── favicon.ico ├── components.json ├── prettier.config.mjs ├── prisma │ ├── client.ts │ ├── seed.ts │ └── schema.prisma ├── next.config.js ├── tsconfig.json ├── LICENSE ├── .eslintrc.cjs ├── .env.example ├── tailwind.config.ts └── package.json ├── README.md └── .gitignore /with-platform-supabase-tailwind-prisma/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | .env 5 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | export { GET, POST } from "@/auth"; 2 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/dashboard/settings/page.tsx: -------------------------------------------------------------------------------- 1 | export default function SettingsOutlet() { 2 | return null; 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The platform starter kit has been archived and is no longer maintained. 2 | 3 | Find more, smaller examples here: https://github.com/calcom/examples 4 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | tailwindcss: {}, 4 | }, 5 | }; 6 | 7 | module.exports = config; 8 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/public/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calcom/platform-starter-kit/HEAD/with-platform-supabase-tailwind-prisma/public/hero.jpg -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calcom/platform-starter-kit/HEAD/with-platform-supabase-tailwind-prisma/public/favicon.ico -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/fonts/CalSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calcom/platform-starter-kit/HEAD/with-platform-supabase-tailwind-prisma/src/fonts/CalSans-SemiBold.ttf -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/login/page.tsx: -------------------------------------------------------------------------------- 1 | import { LoginForm } from "@/app/login/_components/login"; 2 | 3 | export default function LoginPage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/fonts/CalSans-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calcom/platform-starter-kit/HEAD/with-platform-supabase-tailwind-prisma/src/fonts/CalSans-SemiBold.woff2 -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/dashboard/settings/availability/page.tsx: -------------------------------------------------------------------------------- 1 | import SettingsContent from "../_components/settings-content"; 2 | 3 | export default async function DashboardSettingsAvailability() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/signup/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignupForm } from "@/app/signup/_components/signup"; 2 | 3 | export default async function SignupPage() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | function Skeleton({ className, ...props }: React.HTMLAttributes) { 4 | return
; 5 | } 6 | 7 | export { Skeleton }; 8 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/dashboard/settings/layout.tsx: -------------------------------------------------------------------------------- 1 | export default function SettingsLayout(props: { children: React.ReactNode }) { 2 | return ( 3 |
4 | {props.children} 5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/lib/supabase-image-loader.ts: -------------------------------------------------------------------------------- 1 | import { env } from "@/env"; 2 | 3 | export default function supabaseLoader({ 4 | src, 5 | width, 6 | quality, 7 | }: { 8 | src: string; 9 | width: number; 10 | quality?: number; 11 | }) { 12 | return `${env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/${src}?width=${width}&quality=${quality || 75}`; 13 | } 14 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/[expertUsername]/booking/[bookingUid]/page.tsx: -------------------------------------------------------------------------------- 1 | import { BookingResult } from "@/app/_components/booking-result"; 2 | import { Suspense } from "react"; 3 | 4 | export default function Booking() { 5 | return ( 6 |
7 | 8 | 9 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/_components/universal/logo.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import Link from "next/link"; 3 | 4 | export const Logo = ({ href, className }: { href?: string; className?: string }) => { 5 | return ( 6 | 7 | Cal.com ® 8 | 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/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 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/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": "src/styles/globals.css", 9 | "baseColor": "stone", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/[expertUsername]/_components/Container.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | export function Container({ className, children, ...props }: React.ComponentPropsWithoutRef<"div">) { 4 | return ( 5 |
6 |
7 |
{children}
8 |
9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/prettier.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */ 2 | const config = { 3 | bracketSpacing: true, 4 | bracketSameLine: true, 5 | singleQuote: false, 6 | jsxSingleQuote: false, 7 | trailingComma: "es5", 8 | semi: true, 9 | printWidth: 110, 10 | arrowParens: "always", 11 | endOfLine: "auto", 12 | plugins: ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"], 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/dashboard/getting-started/page.tsx: -------------------------------------------------------------------------------- 1 | import GettingStarted from "../_components/getting-started-steps"; 2 | import { auth } from "@/auth"; 3 | import { db } from "prisma/client"; 4 | 5 | export default async function Dashboard() { 6 | const sesh = await auth(); 7 | const filterOptions = await db.filterOption.findMany(); 8 | 9 | if (!sesh?.user?.id) { 10 | return
Not logged in
; 11 | } 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/providers.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { TooltipProvider } from "@radix-ui/react-tooltip"; 4 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 5 | import type { ThemeProviderProps } from "next-themes/dist/types"; 6 | import * as React from "react"; 7 | 8 | export function Providers({ children, ...props }: ThemeProviderProps) { 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/prisma/client.ts: -------------------------------------------------------------------------------- 1 | import { env } from "@/env"; 2 | import { PrismaClient } from "@prisma/client"; 3 | 4 | const createPrismaClient = () => 5 | new PrismaClient({ 6 | log: env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"], 7 | }); 8 | 9 | const globalForPrisma = globalThis as unknown as { 10 | prisma: ReturnType | undefined; 11 | }; 12 | 13 | export const db = globalForPrisma.prisma ?? createPrismaClient(); 14 | 15 | if (env.NODE_ENV !== "production") globalForPrisma.prisma = db; 16 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/dashboard/@breadcrumbs/page.tsx: -------------------------------------------------------------------------------- 1 | import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from "@/components/ui/breadcrumb"; 2 | 3 | // Note: The root breadcrum is required since optional catch-all routes aren't supported yet by Nextjs parallel routes 4 | export default function BreadcrumbsSlot() { 5 | return ( 6 | 7 | 8 | 9 | Dashboard 10 | 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { authConfig } from "./auth/config.edge"; 2 | import NextAuth from "next-auth"; 3 | import { NextResponse, type MiddlewareConfig } from "next/server"; 4 | 5 | export default NextAuth(authConfig).auth((req) => { 6 | if (req.nextUrl.pathname === "/dashboard/settings") { 7 | return NextResponse.redirect(new URL("/dashboard/settings/profile", req.url)); 8 | } 9 | }); 10 | 11 | export const config: MiddlewareConfig = { 12 | // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher 13 | matcher: ["/((?!api|_next/static|_next/image|.*\\.png$).*)"], 14 | }; 15 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/cal/utils.ts: -------------------------------------------------------------------------------- 1 | import { env } from "@/env"; 2 | 3 | export const stripCalOAuthClientIdFromText = (str: string) => { 4 | if (str === "") return str; 5 | return str.split(`-${env.NEXT_PUBLIC_CAL_OAUTH_CLIENT_ID}`)?.[0]?.replace(".", " "); 6 | }; 7 | 8 | export const stripCalOAuthClientIdFromEmail = (str: string) => { 9 | if (str === "") return str; 10 | return str.replace(`+${env.NEXT_PUBLIC_CAL_OAUTH_CLIENT_ID}`, ""); 11 | }; 12 | 13 | export const isCalSandbox = 14 | env.NEXT_PUBLIC_CAL_OAUTH_CLIENT_ID === "cluwyp9yb0001p61n2dkqdmo1" && 15 | "https://api.cal.dev/v2" === env.NEXT_PUBLIC_CAL_API_URL; 16 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/next.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | 3 | /** 4 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful 5 | * for Docker builds. 6 | */ 7 | await import("./src/env.js"); 8 | 9 | /** @type {import("next").NextConfig} */ 10 | const config = { 11 | experimental: { 12 | ppr: true, 13 | }, 14 | images: { 15 | formats: ["image/avif", "image/webp"], 16 | remotePatterns: [ 17 | { 18 | protocol: "https", 19 | hostname: "picsum.photos", 20 | }, 21 | ], 22 | loader: "custom", 23 | loaderFile: "./src/lib/supabase-image-loader.ts", 24 | }, 25 | }; 26 | 27 | export default config; 28 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/_components/search-bar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Input } from "@/components/ui/input"; 4 | import { useQueryState, parseAsString } from "nuqs"; 5 | 6 | export const SearchBar = () => { 7 | const [query, setQuery] = useQueryState("q", parseAsString); 8 | 9 | return ( 10 |
11 | { 16 | // append the query to the URL 17 | await setQuery(e.target.value); 18 | }} 19 | /> 20 | {/* */} 21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/_components/universal/hero.tsx: -------------------------------------------------------------------------------- 1 | import { Balancer } from "react-wrap-balancer"; 2 | 3 | interface HeroProps { 4 | title: string; 5 | children?: React.ReactNode; 6 | } 7 | 8 | export const Hero = ({ title, children }: HeroProps) => { 9 | return ( 10 |
13 |
14 |

15 | {title} 16 |

17 | {children} 18 |
19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # database 12 | /prisma/db.sqlite 13 | /prisma/db.sqlite-journal 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | next-env.d.ts 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # local env files 34 | # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables 35 | .env 36 | .env.legacy 37 | .env*.local 38 | 39 | # vercel 40 | .vercel 41 | 42 | # typescript 43 | *.tsbuildinfo 44 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/prisma/seed.ts: -------------------------------------------------------------------------------- 1 | import { filterOptions } from "@/app/_hardcoded"; 2 | import { PrismaClient } from "@prisma/client"; 3 | 4 | const devDb = new PrismaClient(); 5 | 6 | async function main() { 7 | for (const filterOption of filterOptions) { 8 | console.log(`attempting to upsert ${filterOption.fieldId}`); 9 | await devDb.filterOption.upsert({ 10 | where: { fieldId: filterOption.fieldId }, 11 | create: filterOption, 12 | update: filterOption, 13 | }); 14 | console.log(`✅ {filterOption.fieldId} upserted`); 15 | } 16 | } 17 | 18 | main() 19 | .then(async () => { 20 | await devDb.$disconnect(); 21 | }) 22 | .catch(async (e) => { 23 | console.error(e); 24 | await devDb.$disconnect(); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/app/tailwind-indicator.tsx: -------------------------------------------------------------------------------- 1 | import { IS_PRODUCTION } from "@/lib/constants"; 2 | 3 | export function TailwindIndicator() { 4 | if (IS_PRODUCTION) return null; 5 | 6 | return ( 7 |
8 |
xs
9 |
sm
10 |
md
11 |
lg
12 |
xl
13 |
2xl
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | import * as LabelPrimitive from "@radix-ui/react-label"; 5 | import { cva, type VariantProps } from "class-variance-authority"; 6 | import * as React from "react"; 7 | 8 | const labelVariants = cva( 9 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 10 | ); 11 | 12 | const Label = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef & VariantProps 15 | >(({ className, ...props }, ref) => ( 16 | 17 | )); 18 | Label.displayName = LabelPrimitive.Root.displayName; 19 | 20 | export { Label }; 21 | -------------------------------------------------------------------------------- /with-platform-supabase-tailwind-prisma/src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import * as React from "react"; 3 | 4 | export type TextareaProps = React.TextareaHTMLAttributes; 5 | 6 | const Textarea = React.forwardRef(({ className, ...props }, ref) => { 7 | return ( 8 |