├── .npmrc ├── apps └── web │ ├── cypress │ ├── support │ │ ├── e2e.ts │ │ └── commands.ts │ └── e2e │ │ ├── signup.cy.ts │ │ ├── homepage.cy.ts │ │ └── pricing.cy.ts │ ├── modules │ ├── analytics │ │ ├── index.tsx │ │ └── provider │ │ │ ├── custom │ │ │ └── index.tsx │ │ │ ├── vercel │ │ │ └── index.tsx │ │ │ ├── mixpanel │ │ │ └── index.tsx │ │ │ ├── plausible │ │ │ └── index.tsx │ │ │ ├── umami │ │ │ └── index.tsx │ │ │ ├── pirsch │ │ │ └── index.tsx │ │ │ └── google │ │ │ └── index.tsx │ ├── saas │ │ ├── shared │ │ │ ├── constants.ts │ │ │ └── components │ │ │ │ ├── PageHeader.tsx │ │ │ │ ├── LoadingWrapper.tsx │ │ │ │ ├── TabGroup.tsx │ │ │ │ ├── CreateTeamDialog.tsx │ │ │ │ ├── Pagination.tsx │ │ │ │ └── ActionBlock.tsx │ │ ├── dashboard │ │ │ ├── state.ts │ │ │ └── components │ │ │ │ └── ProductNameGenerator.tsx │ │ ├── auth │ │ │ ├── hooks │ │ │ │ └── use-user.ts │ │ │ ├── lib │ │ │ │ └── current-team-id.tsx │ │ │ └── components │ │ │ │ ├── TeamInvitationInfo.tsx │ │ │ │ ├── SigninModeSwitch.tsx │ │ │ │ ├── SocialSigninButton.tsx │ │ │ │ └── VerifyTokenView.tsx │ │ ├── admin │ │ │ └── component │ │ │ │ ├── EmailVerified.tsx │ │ │ │ └── SideMenu.tsx │ │ └── settings │ │ │ └── components │ │ │ ├── TeamRoleSelect.tsx │ │ │ ├── SubscriptionStatusBadge.tsx │ │ │ ├── SettingsMenu.tsx │ │ │ ├── CustomerPortalButton.tsx │ │ │ ├── TeamMembersBlock.tsx │ │ │ ├── ChangePassword.tsx │ │ │ ├── PauseSubscriptionButton.tsx │ │ │ ├── ResumeSubscriptionButton.tsx │ │ │ ├── CancelSubscriptionButton.tsx │ │ │ ├── ChangeNameForm.tsx │ │ │ ├── UpgradePlan.tsx │ │ │ ├── ChangeTeamNameForm.tsx │ │ │ └── CropImageDialog.tsx │ ├── shared │ │ ├── lib │ │ │ └── api-client.ts │ │ ├── hooks │ │ │ └── locale-currency.tsx │ │ └── components │ │ │ ├── ClientProviders.tsx │ │ │ ├── UserAvatar.tsx │ │ │ ├── TeamAvatar.tsx │ │ │ ├── ApiClientProvider.tsx │ │ │ ├── ConsentBanner.tsx │ │ │ └── LocaleSwitch.tsx │ ├── ui │ │ ├── lib │ │ │ └── index.ts │ │ └── components │ │ │ ├── skeleton.tsx │ │ │ ├── label.tsx │ │ │ ├── input.tsx │ │ │ ├── badge.tsx │ │ │ ├── password-input.tsx │ │ │ ├── tooltip.tsx │ │ │ ├── avatar.tsx │ │ │ ├── toaster.tsx │ │ │ └── alert.tsx │ ├── i18n │ │ └── index.ts │ └── marketing │ │ ├── blog │ │ ├── components │ │ │ └── PostContent.tsx │ │ └── utils │ │ │ └── mdx-components.tsx │ │ ├── shared │ │ └── components │ │ │ ├── NotFound.tsx │ │ │ ├── Banner.tsx │ │ │ └── Footer.tsx │ │ └── pricing │ │ └── components │ │ └── PricingTable.tsx │ ├── .eslintrc.js │ ├── app │ ├── api │ │ ├── oauth │ │ │ ├── github │ │ │ │ ├── route.ts │ │ │ │ └── callback │ │ │ │ │ └── route.ts │ │ │ └── google │ │ │ │ ├── route.ts │ │ │ │ └── callback │ │ │ │ └── route.ts │ │ └── [trpc] │ │ │ └── route.ts │ ├── icon.png │ ├── favicon.ico │ ├── [locale] │ │ ├── not-found.tsx │ │ ├── [...rest] │ │ │ └── page.tsx │ │ ├── (saas) │ │ │ ├── auth │ │ │ │ ├── verify │ │ │ │ │ └── page.tsx │ │ │ │ ├── otp │ │ │ │ │ └── page.tsx │ │ │ │ ├── login │ │ │ │ │ └── page.tsx │ │ │ │ ├── signup │ │ │ │ │ └── page.tsx │ │ │ │ ├── forgot-password │ │ │ │ │ └── page.tsx │ │ │ │ └── layout.tsx │ │ │ ├── app │ │ │ │ ├── admin │ │ │ │ │ ├── users │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── layout.tsx │ │ │ │ ├── settings │ │ │ │ │ ├── loading.tsx │ │ │ │ │ ├── account │ │ │ │ │ │ └── general │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── team │ │ │ │ │ │ ├── general │ │ │ │ │ │ └── page.tsx │ │ │ │ │ │ ├── members │ │ │ │ │ │ └── page.tsx │ │ │ │ │ │ └── billing │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── ai-demo │ │ │ │ │ └── page.tsx │ │ │ │ ├── dashboard │ │ │ │ │ └── page.tsx │ │ │ │ └── layout.tsx │ │ │ ├── loading.tsx │ │ │ └── team │ │ │ │ └── invitation │ │ │ │ └── route.tsx │ │ ├── globals.css │ │ ├── (marketing) │ │ │ ├── (home) │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── pricing │ │ │ │ └── page.tsx │ │ │ └── blog │ │ │ │ └── page.tsx │ │ └── layout.tsx │ └── robots.ts │ ├── postcss.config.cjs │ ├── public │ └── images │ │ ├── blog │ │ ├── cover.png │ │ ├── author.jpg │ │ └── author2.jpg │ │ └── hero-dark.svg │ ├── cypress.config.js │ ├── global.d.ts │ ├── tailwind.config.ts │ ├── content │ └── posts │ │ ├── first-post.mdx │ │ └── second-post.mdx │ ├── components.json │ ├── middleware.ts │ ├── .gitignore │ ├── tsconfig.json │ ├── i18n.ts │ ├── config.ts │ ├── contentlayer.config.ts │ └── next.config.js ├── packages ├── mail │ ├── .gitignore │ ├── provider │ │ ├── index.ts │ │ ├── custom.ts │ │ ├── plunk.ts │ │ ├── nodemailer.ts │ │ ├── resend.ts │ │ └── postmark.ts │ ├── index.ts │ ├── config.ts │ ├── tsconfig.json │ ├── types.ts │ ├── emails │ │ ├── components │ │ │ └── PrimaryButton.tsx │ │ ├── NewsletterSignup.tsx │ │ ├── EmailChange.tsx │ │ ├── TeamInvitation.tsx │ │ ├── MagicLink.tsx │ │ ├── NewUser.tsx │ │ └── ForgotPassword.tsx │ ├── util │ │ ├── send.ts │ │ └── templates.ts │ └── package.json ├── storage │ ├── index.ts │ ├── provider │ │ ├── index.ts │ │ ├── supabase │ │ │ └── index.ts │ │ └── s3 │ │ │ └── index.ts │ ├── tsconfig.json │ ├── types.ts │ └── package.json ├── utils │ ├── index.ts │ ├── tsconfig.json │ ├── lib │ │ └── base-url.ts │ └── package.json ├── api │ ├── modules │ │ ├── billing │ │ │ ├── provider │ │ │ │ ├── index.ts │ │ │ │ ├── stripe │ │ │ │ │ ├── index.ts │ │ │ │ │ └── api.ts │ │ │ │ └── lemonsqueezy │ │ │ │ │ ├── index.ts │ │ │ │ │ └── api.ts │ │ │ ├── procedures │ │ │ │ ├── index.ts │ │ │ │ ├── plans.ts │ │ │ │ ├── create-checkout-link.ts │ │ │ │ ├── pause-subscription.ts │ │ │ │ ├── cancel-subscription.ts │ │ │ │ ├── resume-subscription.ts │ │ │ │ ├── sync-subscription.ts │ │ │ │ └── create-customer-portal-link.ts │ │ │ └── types.ts │ │ ├── newsletter │ │ │ └── procedures │ │ │ │ ├── index.ts │ │ │ │ └── signup.ts │ │ ├── ai │ │ │ └── procedures │ │ │ │ ├── index.ts │ │ │ │ └── generate-product-names.ts │ │ ├── uploads │ │ │ └── procedures │ │ │ │ ├── index.ts │ │ │ │ └── signed-upload-url.ts │ │ ├── admin │ │ │ └── procedures │ │ │ │ ├── index.ts │ │ │ │ ├── delete-user.ts │ │ │ │ ├── impersonate.ts │ │ │ │ └── unimpersonate.ts │ │ ├── auth │ │ │ ├── procedures │ │ │ │ ├── index.ts │ │ │ │ ├── update.ts │ │ │ │ ├── change-password.ts │ │ │ │ ├── logout.ts │ │ │ │ ├── delete-account.ts │ │ │ │ ├── verify-token.ts │ │ │ │ ├── change-email.ts │ │ │ │ ├── login-with-email.ts │ │ │ │ ├── forgot-password.ts │ │ │ │ ├── verify-otp.ts │ │ │ │ ├── user.ts │ │ │ │ └── login-with-password.ts │ │ │ └── abilities.ts │ │ └── team │ │ │ └── procedures │ │ │ ├── index.ts │ │ │ ├── subscription.ts │ │ │ ├── invitations.ts │ │ │ ├── invitation-by-id.ts │ │ │ ├── by-id.ts │ │ │ ├── update.ts │ │ │ ├── delete.ts │ │ │ ├── create.ts │ │ │ ├── revoke-invitation.ts │ │ │ ├── memberships.ts │ │ │ ├── remove-member.ts │ │ │ ├── update-membership.ts │ │ │ ├── accept-invite.ts │ │ │ └── invite-member.ts │ ├── tsconfig.json │ ├── trpc │ │ ├── router-handler.ts │ │ ├── caller.ts │ │ ├── router.ts │ │ ├── base.ts │ │ └── context.ts │ └── package.json ├── auth │ ├── index.ts │ ├── tsconfig.json │ ├── types.ts │ ├── lib │ │ ├── password.ts │ │ └── lucia.ts │ └── package.json ├── database │ ├── index.ts │ ├── tsconfig.json │ ├── src │ │ └── client.ts │ └── package.json └── config │ ├── tsconfig │ ├── package.json │ ├── react-library.json │ ├── base.json │ └── nextjs.json │ ├── eslint-config-custom │ ├── index.js │ └── package.json │ └── tailwind │ └── package.json ├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ └── main.yml ├── .vscode ├── extensions.json └── settings.json ├── .prettierrc ├── next-env.d.ts ├── pnpm-workspace.yaml ├── README.md ├── .gitignore ├── package.json ├── .env └── turbo.json /.npmrc: -------------------------------------------------------------------------------- 1 | public-hoist-pattern[]=*prisma* -------------------------------------------------------------------------------- /apps/web/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | import "./commands"; 2 | -------------------------------------------------------------------------------- /packages/mail/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | .react-email 3 | out -------------------------------------------------------------------------------- /packages/storage/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./provider"; 2 | -------------------------------------------------------------------------------- /packages/mail/provider/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./plunk"; 2 | -------------------------------------------------------------------------------- /packages/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./lib/base-url"; 2 | -------------------------------------------------------------------------------- /packages/mail/index.ts: -------------------------------------------------------------------------------- 1 | export { sendEmail } from "./util/send"; 2 | -------------------------------------------------------------------------------- /packages/storage/provider/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./supabase"; 2 | -------------------------------------------------------------------------------- /apps/web/cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/web/modules/analytics/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./provider/custom"; 2 | -------------------------------------------------------------------------------- /packages/api/modules/billing/provider/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./stripe"; 2 | -------------------------------------------------------------------------------- /packages/api/modules/newsletter/procedures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./signup"; 2 | -------------------------------------------------------------------------------- /packages/api/modules/ai/procedures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./generate-product-names"; 2 | -------------------------------------------------------------------------------- /packages/api/modules/billing/provider/stripe/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./subscriptions"; 2 | -------------------------------------------------------------------------------- /packages/api/modules/uploads/procedures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./signed-upload-url"; 2 | -------------------------------------------------------------------------------- /packages/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./lib/lucia"; 2 | export * from "./lib/tokens"; 3 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ["custom"], 4 | }; 5 | -------------------------------------------------------------------------------- /packages/api/modules/billing/provider/lemonsqueezy/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./subscriptions"; 2 | -------------------------------------------------------------------------------- /packages/mail/config.ts: -------------------------------------------------------------------------------- 1 | export const config = { 2 | from: "", 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/app/api/oauth/github/route.ts: -------------------------------------------------------------------------------- 1 | export { githubRouteHandler as GET } from "auth/oauth/github"; 2 | -------------------------------------------------------------------------------- /apps/web/app/api/oauth/google/route.ts: -------------------------------------------------------------------------------- 1 | export { googleRouteHandler as GET } from "auth/oauth/google"; 2 | -------------------------------------------------------------------------------- /apps/web/app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/284247028/supastarter-nextjs/HEAD/apps/web/app/icon.png -------------------------------------------------------------------------------- /apps/web/modules/saas/shared/constants.ts: -------------------------------------------------------------------------------- 1 | export const CURRENT_TEAM_ID_COOKIE_NAME = "current-team-id"; 2 | -------------------------------------------------------------------------------- /apps/web/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/web/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/284247028/supastarter-nextjs/HEAD/apps/web/app/favicon.ico -------------------------------------------------------------------------------- /apps/web/app/api/oauth/github/callback/route.ts: -------------------------------------------------------------------------------- 1 | export { githubCallbackRouteHandler as GET } from "auth/oauth/github"; 2 | -------------------------------------------------------------------------------- /apps/web/app/api/oauth/google/callback/route.ts: -------------------------------------------------------------------------------- 1 | export { googleCallbackRouteHandler as GET } from "auth/oauth/google"; 2 | -------------------------------------------------------------------------------- /apps/web/public/images/blog/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/284247028/supastarter-nextjs/HEAD/apps/web/public/images/blog/cover.png -------------------------------------------------------------------------------- /apps/web/public/images/blog/author.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/284247028/supastarter-nextjs/HEAD/apps/web/public/images/blog/author.jpg -------------------------------------------------------------------------------- /apps/web/public/images/blog/author2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/284247028/supastarter-nextjs/HEAD/apps/web/public/images/blog/author2.jpg -------------------------------------------------------------------------------- /packages/database/index.ts: -------------------------------------------------------------------------------- 1 | export { PrismaClient } from "@prisma/client"; 2 | export * from "./src/client"; 3 | export * from "./src/zod"; 4 | -------------------------------------------------------------------------------- /packages/auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/base.json", 3 | "include": ["**/*.ts"], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/mail/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/storage/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/base.json", 3 | "include": ["**/*.ts"], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/base.json", 3 | "include": ["**/*.ts"], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "next/navigation"; 2 | 3 | export default function NotFoundPage() { 4 | return redirect("/404"); 5 | } 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['custom'], 4 | settings: { 5 | next: { 6 | rootDir: ['apps/*/'], 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/api/modules/admin/procedures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./delete-user"; 2 | export * from "./impersonate"; 3 | export * from "./unimpersonate"; 4 | export * from "./users"; 5 | -------------------------------------------------------------------------------- /apps/web/cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require("cypress"); 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | baseUrl: "http://localhost:3000", 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /apps/web/global.d.ts: -------------------------------------------------------------------------------- 1 | // Use type safe message keys with `next-intl` 2 | type Messages = typeof import("./locales/en.json"); 3 | 4 | declare interface IntlMessages extends Messages {} 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | open-pull-requests-limit: 25 8 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/[...rest]/page.tsx: -------------------------------------------------------------------------------- 1 | import { NotFound } from "@marketing/shared/components/NotFound"; 2 | 3 | export default function NotFoundPage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /packages/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["**/*.ts", "../auth/lucia.d.ts"], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/modules/saas/dashboard/state.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai"; 2 | 3 | export const sidebarExpanded = atom(false); 4 | 5 | export const createTeamDialogOpen = atom(false); 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "dbaeumer.vscode-eslint", 5 | "lokalise.i18n-ally", 6 | "bradlc.vscode-tailwindcss" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /apps/web/modules/shared/lib/api-client.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCReact } from "@trpc/react-query"; 2 | import { ApiRouter } from "api/trpc/router"; 3 | 4 | export const apiClient = createTRPCReact({}); 5 | -------------------------------------------------------------------------------- /packages/config/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsconfig", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "publishConfig": { 7 | "access": "public" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(saas)/auth/verify/page.tsx: -------------------------------------------------------------------------------- 1 | import { VerifyTokenView } from "@saas/auth/components/VerifyTokenView"; 2 | 3 | export default function VerifyTokenPage() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/modules/ui/lib/index.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /packages/database/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"], 5 | "compilerOptions": { 6 | "baseUrl": "." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/mail/types.ts: -------------------------------------------------------------------------------- 1 | export interface SendEmailParams { 2 | to: string; 3 | subject: string; 4 | text: string; 5 | html?: string; 6 | } 7 | 8 | export type SendEmailHandler = (params: SendEmailParams) => Promise; 9 | -------------------------------------------------------------------------------- /apps/web/modules/saas/auth/hooks/use-user.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { userContext } from "../lib/user-context"; 3 | 4 | export function useUser() { 5 | const context = useContext(userContext); 6 | return context; 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/app/api/[trpc]/route.ts: -------------------------------------------------------------------------------- 1 | import { trpcApiRouteHandler } from "api/trpc/router-handler"; 2 | 3 | export const dynamic = "force-dynamic"; 4 | export const revalidate = 0; 5 | 6 | export { trpcApiRouteHandler as GET, trpcApiRouteHandler as POST }; 7 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(saas)/app/admin/users/page.tsx: -------------------------------------------------------------------------------- 1 | import { UserList } from "@saas/admin/component/UserList"; 2 | 3 | export default function AdminUserPage() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "printWidth": 80, 4 | "singleQuote": false, 5 | "jsxSingleQuote": false, 6 | "semi": true, 7 | "trailingComma": "all", 8 | "tabWidth": 2, 9 | "plugins": ["prettier-plugin-tailwindcss"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import baseConfig from "tailwind-config/tailwind.config"; 2 | import { Config } from "tailwindcss"; 3 | 4 | export default { 5 | presets: [baseConfig], 6 | content: ["./app/**/*.tsx", "./modules/**/*.tsx"], 7 | } satisfies Config; 8 | -------------------------------------------------------------------------------- /apps/web/app/robots.ts: -------------------------------------------------------------------------------- 1 | import { MetadataRoute } from "next"; 2 | 3 | export default function robots(): MetadataRoute.Robots { 4 | return { 5 | rules: { 6 | userAgent: "*", 7 | allow: "/", 8 | }, 9 | // sitemap: `${BASE_URL}/sitemap.xml`, 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /packages/utils/lib/base-url.ts: -------------------------------------------------------------------------------- 1 | export function getBaseUrl() { 2 | if (process.env.NEXT_PUBLIC_SITE_URL) return process.env.NEXT_PUBLIC_SITE_URL; 3 | if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; 4 | return `http://localhost:${process.env.PORT ?? 3000}`; 5 | } 6 | -------------------------------------------------------------------------------- /packages/mail/provider/custom.ts: -------------------------------------------------------------------------------- 1 | import { config } from "../config"; 2 | import { SendEmailHandler } from "../types"; 3 | 4 | const { from } = config; 5 | 6 | export const send: SendEmailHandler = async ({ to, subject, text, html }) => { 7 | // handle your custom email sending logic here 8 | }; 9 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/basic-features/typescript for more information. 7 | -------------------------------------------------------------------------------- /packages/auth/types.ts: -------------------------------------------------------------------------------- 1 | import { User } from "database"; 2 | 3 | export type PartialBy = Omit & Partial>; 4 | 5 | export type DatabaseSessionAttributes = { 6 | impersonatorId?: string; 7 | }; 8 | export type DatabaseUserAttributes = PartialBy; 9 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - apps/web 3 | - packages/api 4 | - packages/auth 5 | - packages/database 6 | - packages/mail 7 | - packages/config/eslint-config-custom 8 | - packages/config/tsconfig 9 | - packages/config/tailwind 10 | - packages/storage 11 | - packages/utils 12 | -------------------------------------------------------------------------------- /packages/config/eslint-config-custom/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["next", "turbo", "prettier"], 3 | rules: { 4 | "@next/next/no-html-link-for-pages": "off", 5 | }, 6 | parserOptions: { 7 | babelOptions: { 8 | presets: [require.resolve("next/babel")], 9 | }, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /apps/web/modules/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import { appConfig } from "@config"; 2 | import { createSharedPathnamesNavigation } from "next-intl/navigation"; 3 | 4 | export const { Link, redirect, usePathname, useRouter } = 5 | createSharedPathnamesNavigation({ 6 | locales: appConfig.i18n.locales, 7 | localePrefix: "never", 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/modules/billing/procedures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./cancel-subscription"; 2 | export * from "./create-checkout-link"; 3 | export * from "./create-customer-portal-link"; 4 | export * from "./pause-subscription"; 5 | export * from "./plans"; 6 | export * from "./resume-subscription"; 7 | export * from "./sync-subscription"; 8 | -------------------------------------------------------------------------------- /packages/config/tsconfig/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "lib": ["ES2021", "DOM"], 7 | "module": "ESNext", 8 | "target": "ES6", 9 | "jsx": "preserve" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(saas)/app/settings/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Icon } from "@ui/components/icon"; 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/modules/saas/auth/lib/current-team-id.tsx: -------------------------------------------------------------------------------- 1 | import { CURRENT_TEAM_ID_COOKIE_NAME } from "@saas/shared/constants"; 2 | import Cookies from "js-cookie"; 3 | 4 | export function updateCurrentTeamIdCookie(teamId: string) { 5 | Cookies.set(CURRENT_TEAM_ID_COOKIE_NAME, teamId, { 6 | path: "/", 7 | expires: 30, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(saas)/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Logo } from "@shared/components/Logo"; 2 | 3 | export default function Loading() { 4 | return ( 5 |
6 |
7 | 8 |
9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/auth/lib/password.ts: -------------------------------------------------------------------------------- 1 | import { Argon2id } from "oslo/password"; 2 | 3 | export async function hashPassword(password: string) { 4 | return await new Argon2id().hash(password); 5 | } 6 | 7 | export async function verifyPassword(hashedPassword: string, password: string) { 8 | return new Argon2id().verify(hashedPassword, password); 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/modules/ui/components/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@ui/lib" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /apps/web/cypress/e2e/signup.cy.ts: -------------------------------------------------------------------------------- 1 | describe("signup", () => { 2 | beforeEach(() => { 3 | cy.visit("/auth/signup"); 4 | }); 5 | 6 | it("should show all form fields", () => { 7 | cy.get('input[name="name"]').should("exist"); 8 | cy.get('input[name="email"]').should("exist"); 9 | cy.get('input[name="password"]').should("exist"); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/api/trpc/router-handler.ts: -------------------------------------------------------------------------------- 1 | import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; 2 | import { createContext } from "./context"; 3 | import { apiRouter } from "./router"; 4 | 5 | export const trpcApiRouteHandler = (req: Request) => 6 | fetchRequestHandler({ 7 | endpoint: "/api", 8 | req, 9 | router: apiRouter, 10 | createContext, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@types/node": "^20.11.24", 4 | "eslint-config-custom": "workspace:*", 5 | "tsconfig": "workspace:*" 6 | }, 7 | "license": "MIT", 8 | "main": "./index.tsx", 9 | "name": "utils", 10 | "scripts": { 11 | "lint": "eslint \"**/*.ts*\"" 12 | }, 13 | "types": "./**/.tsx", 14 | "version": "0.0.0" 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | * { 7 | @apply border-border; 8 | } 9 | } 10 | 11 | @layer utilities { 12 | .no-scrollbar::-webkit-scrollbar { 13 | display: none; 14 | } 15 | 16 | .no-scrollbar { 17 | -ms-overflow-style: none; 18 | scrollbar-width: none; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/api/modules/billing/procedures/plans.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { publicProcedure } from "../../../trpc/base"; 3 | import { getAllPlans } from "../provider"; 4 | import { SubscriptionPlanModel } from "../types"; 5 | 6 | export const plans = publicProcedure 7 | .output(z.array(SubscriptionPlanModel)) 8 | .query(async () => { 9 | return await getAllPlans(); 10 | }); 11 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(marketing)/(home)/page.tsx: -------------------------------------------------------------------------------- 1 | import { Features } from "@marketing/home/components/Features"; 2 | import { Hero } from "@marketing/home/components/Hero"; 3 | import { Newsletter } from "@marketing/home/components/Newsletter"; 4 | 5 | export default function Home() { 6 | return ( 7 | <> 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(saas)/auth/otp/page.tsx: -------------------------------------------------------------------------------- 1 | import { OtpForm } from "@saas/auth/components/OtpForm"; 2 | import { getTranslations } from "next-intl/server"; 3 | 4 | export async function generateMetadata() { 5 | const t = await getTranslations(); 6 | 7 | return { 8 | title: t("auth.verifyOtp.title"), 9 | }; 10 | } 11 | 12 | export default function OtpPage() { 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/modules/shared/hooks/locale-currency.tsx: -------------------------------------------------------------------------------- 1 | import { appConfig } from "@config"; 2 | import { useLocale } from "next-intl"; 3 | 4 | export function useLocaleCurrency() { 5 | const locale = useLocale(); 6 | const localeCurrency = 7 | Object.entries(appConfig.i18n.localeCurrencies).find( 8 | ([key, value]) => key === locale, 9 | )?.[1] || "USD"; 10 | 11 | return localeCurrency; 12 | } 13 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(saas)/auth/login/page.tsx: -------------------------------------------------------------------------------- 1 | import { LoginForm } from "@saas/auth/components/LoginForm"; 2 | import { getTranslations } from "next-intl/server"; 3 | 4 | export async function generateMetadata() { 5 | const t = await getTranslations(); 6 | 7 | return { 8 | title: t("auth.login.title"), 9 | }; 10 | } 11 | 12 | export default function LoginPage() { 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(saas)/auth/signup/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignupForm } from "@saas/auth/components/SignupForm"; 2 | import { getTranslations } from "next-intl/server"; 3 | 4 | export async function generateMetadata() { 5 | const t = await getTranslations(); 6 | 7 | return { 8 | title: t("auth.signup.title"), 9 | }; 10 | } 11 | 12 | export default function SignupPage() { 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/content/posts/first-post.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Favorite Things 3 | date: 2023-02-28 4 | image: /images/blog/cover.png 5 | authorName: Elon Musk 6 | authorImage: /images/blog/author2.jpg 7 | excerpt: In this post I'm going to tell you about my favorite things. 8 | tags: [first, post] 9 | published: true 10 | --- 11 | 12 | ## This is a heading 13 | 14 | And we also have some great content here. What do you think? 15 | -------------------------------------------------------------------------------- /apps/web/modules/saas/shared/components/PageHeader.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export function PageHeader({ 4 | title, 5 | subtitle, 6 | }: { 7 | title: string; 8 | subtitle?: string; 9 | }) { 10 | return ( 11 |
12 |

{title}

13 |

{subtitle}

14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/database/src/client.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | declare let global: { prisma: PrismaClient }; 3 | 4 | let prisma: PrismaClient; 5 | 6 | if (process.env.NODE_ENV === "production") { 7 | prisma = new PrismaClient(); 8 | } else { 9 | if (!global.prisma) { 10 | global.prisma = new PrismaClient(); 11 | } 12 | 13 | prisma = global.prisma; 14 | } 15 | 16 | export { prisma as db }; 17 | -------------------------------------------------------------------------------- /packages/config/eslint-config-custom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-custom", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "eslint-config-next": "^14.1.0", 8 | "eslint-config-prettier": "^9.1.0", 9 | "eslint-config-turbo": "^1.12.4", 10 | "eslint-plugin-react": "7.33.2" 11 | }, 12 | "publishConfig": { 13 | "access": "public" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/api/trpc/caller.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "./context"; 2 | import { apiRouter } from "./router"; 3 | 4 | export const createApiCaller = async () => { 5 | const context = await createContext(); 6 | return apiRouter.createCaller(context); 7 | }; 8 | 9 | export const createAdminApiCaller = async () => { 10 | const context = await createContext({ isAdmin: true }); 11 | return apiRouter.createCaller(context); 12 | }; 13 | -------------------------------------------------------------------------------- /apps/web/modules/shared/components/ClientProviders.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ThemeProvider } from "next-themes"; 4 | import { PropsWithChildren } from "react"; 5 | import { ApiClientProvider } from "./ApiClientProvider"; 6 | 7 | export function ClientProviders({ children }: PropsWithChildren) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/api/modules/auth/procedures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./change-email"; 2 | export * from "./change-password"; 3 | export * from "./delete-account"; 4 | export * from "./forgot-password"; 5 | export * from "./login-with-email"; 6 | export * from "./login-with-password"; 7 | export * from "./logout"; 8 | export * from "./signup"; 9 | export * from "./update"; 10 | export * from "./user"; 11 | export * from "./verify-otp"; 12 | export * from "./verify-token"; 13 | -------------------------------------------------------------------------------- /apps/web/app/[locale]/(saas)/auth/forgot-password/page.tsx: -------------------------------------------------------------------------------- 1 | import { ForgotPasswordForm } from "@saas/auth/components/ForgotPasswordForm"; 2 | import { getTranslations } from "next-intl/server"; 3 | 4 | export async function generateMetadata() { 5 | const t = await getTranslations(); 6 | 7 | return { 8 | title: t("auth.forgotPassword.title"), 9 | }; 10 | } 11 | 12 | export default function ForgotPasswordPage() { 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/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": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@ui/components", 15 | "utils": "@ui/lib", 16 | "ui": "@ui/components" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/api/modules/newsletter/procedures/signup.ts: -------------------------------------------------------------------------------- 1 | import { sendEmail } from "mail"; 2 | import { z } from "zod"; 3 | import { publicProcedure } from "../../../trpc/base"; 4 | 5 | export const signup = publicProcedure 6 | .input( 7 | z.object({ 8 | email: z.string(), 9 | }), 10 | ) 11 | .mutation(async ({ input: { email } }) => { 12 | await sendEmail({ 13 | to: email, 14 | templateId: "newsletterSignup", 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /apps/web/modules/analytics/provider/custom/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export function AnalyticsScript() { 4 | // return your script here 5 | return null; 6 | } 7 | 8 | export function useAnalytics() { 9 | const trackEvent = (event: string, data: Record) => { 10 | // call your analytics service to track a custom event here 11 | console.info("tracking event", event, data); 12 | }; 13 | 14 | return { 15 | trackEvent, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/mail/emails/components/PrimaryButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@react-email/components"; 2 | import { PropsWithChildren } from "react"; 3 | 4 | export default function PrimaryButton({ 5 | href, 6 | children, 7 | }: PropsWithChildren<{ 8 | href: string; 9 | }>) { 10 | return ( 11 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/storage/types.ts: -------------------------------------------------------------------------------- 1 | export type CreateBucketHandler = ( 2 | name: string, 3 | options?: { 4 | public?: boolean; 5 | }, 6 | ) => Promise; 7 | 8 | export type GetSignedUploadUrlHandler = ( 9 | path: string, 10 | options: { 11 | bucket: string; 12 | }, 13 | ) => Promise; 14 | 15 | export type GetSignedUrlHander = ( 16 | path: string, 17 | options: { 18 | bucket: string; 19 | expiresIn?: number; 20 | }, 21 | ) => Promise; 22 | -------------------------------------------------------------------------------- /packages/api/modules/team/procedures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./accept-invite"; 2 | export * from "./by-id"; 3 | export * from "./create"; 4 | export * from "./delete"; 5 | export * from "./invitation-by-id"; 6 | export * from "./invitations"; 7 | export * from "./invite-member"; 8 | export * from "./memberships"; 9 | export * from "./remove-member"; 10 | export * from "./revoke-invitation"; 11 | export * from "./subscription"; 12 | export * from "./update"; 13 | export * from "./update-membership"; 14 | -------------------------------------------------------------------------------- /apps/web/modules/analytics/provider/vercel/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | // @ts-ignore 4 | import { Analytics } from "@vercel/analytics/react"; 5 | // @ts-ignore 6 | import { track } from "@vercel/analytics"; 7 | 8 | export function AnalyticsScript() { 9 | return ; 10 | } 11 | 12 | export function useAnalytics() { 13 | const trackEvent = (event: string, data?: Record) => { 14 | track(event, data); 15 | }; 16 | 17 | return { 18 | trackEvent, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /packages/config/tailwind/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailwind-config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "index.ts", 6 | "devDependencies": { 7 | "@mertasan/tailwindcss-variables": "^2.7.0", 8 | "@tailwindcss/container-queries": "^0.1.1", 9 | "@tailwindcss/forms": "^0.5.6", 10 | "@tailwindcss/typography": "^0.5.10", 11 | "@types/mertasan__tailwindcss-variables": "^2.6.1", 12 | "tailwindcss": "^3.4.1", 13 | "tailwindcss-animate": "^1.0.7" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | supastarter 2 | supastarter 3 | 4 | # supastarter – next.js 5 | 6 | supastarter is the ultimate starter kit for production-ready, scalable SaaS applications. 7 | 8 | ## Helpful links 9 | 10 | - [📘 Documentation](https://docs.supastarter.dev) 11 | - [🚀 Demo](https://demo.supastarter.dev) 12 | - [💬 Discord](https://discord.gg/RUSASaAj4V) 13 | 14 | -------------------------------------------------------------------------------- /apps/web/modules/marketing/blog/components/PostContent.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { MDX } from "contentlayer/core"; 4 | import { useMDXComponent } from "next-contentlayer/hooks"; 5 | import { mdxComponents } from "../utils/mdx-components"; 6 | 7 | export function PostContent({ mdx }: { mdx: MDX }) { 8 | const MDXContent = useMDXComponent(mdx.code); 9 | 10 | return ( 11 |
12 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/middleware.ts: -------------------------------------------------------------------------------- 1 | import createMiddleware from "next-intl/middleware"; 2 | import { NextRequest } from "next/server"; 3 | import { appConfig } from "./config"; 4 | 5 | const intlMiddleware = createMiddleware({ 6 | locales: appConfig.i18n.locales, 7 | defaultLocale: appConfig.i18n.defaultLocale, 8 | localePrefix: "never", 9 | }); 10 | 11 | export default async function middleware(req: NextRequest) { 12 | return intlMiddleware(req); 13 | } 14 | 15 | export const config = { 16 | matcher: ["/((?!api|_next|.*\\..*).*)"], 17 | }; 18 | -------------------------------------------------------------------------------- /.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 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | .swc/ 16 | 17 | # misc 18 | .DS_Store 19 | *.pem 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | # .env 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # turbo 34 | .turbo 35 | 36 | # ui 37 | dist/ -------------------------------------------------------------------------------- /packages/storage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@aws-sdk/client-s3": "3.437.0", 4 | "@aws-sdk/s3-request-presigner": "3.437.0", 5 | "@supabase/supabase-js": "^2.39.7" 6 | }, 7 | "devDependencies": { 8 | "@types/node": "^20.11.24", 9 | "eslint-config-custom": "workspace:*", 10 | "tsconfig": "workspace:*" 11 | }, 12 | "license": "MIT", 13 | "main": "./index.ts", 14 | "name": "storage", 15 | "scripts": { 16 | "lint": "eslint \"**/*.ts*\"" 17 | }, 18 | "types": "./**/.ts", 19 | "version": "0.0.0" 20 | } 21 | -------------------------------------------------------------------------------- /apps/web/.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 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | 37 | # contentlayer 38 | .contentlayer/ -------------------------------------------------------------------------------- /apps/web/app/[locale]/(marketing)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Footer } from "@marketing/shared/components/Footer"; 2 | import { NavBar } from "@marketing/shared/components/NavBar"; 3 | import { UserContextProvider } from "@saas/auth/lib/user-context"; 4 | import { PropsWithChildren } from "react"; 5 | 6 | export default function MarketingLayout({ children }: PropsWithChildren) { 7 | return ( 8 | 9 | 10 |
{children}
11 |