├── .editorconfig ├── .env.example ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode ├── extensions.json └── settings.json ├── LICENSE.md ├── README.md ├── apps ├── .gitkeep ├── web-e2e │ ├── .eslintrc.json │ ├── cypress.config.ts │ ├── project.json │ ├── src │ │ ├── e2e │ │ │ └── app.cy.ts │ │ ├── fixtures │ │ │ └── example.json │ │ └── support │ │ │ ├── app.po.ts │ │ │ ├── commands.ts │ │ │ └── e2e.ts │ └── tsconfig.json └── web │ ├── .eslintrc.json │ ├── app │ ├── (authentication) │ │ ├── layout.tsx │ │ ├── sign-in │ │ │ └── page.tsx │ │ └── sign-up │ │ │ └── page.tsx │ ├── (marketing) │ │ ├── blog │ │ │ ├── [slug] │ │ │ │ ├── opengraph-image.tsx │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ └── page.tsx │ ├── @signInModal │ │ ├── (.)sign-in │ │ │ └── page.tsx │ │ └── default.tsx │ ├── @signUpModal │ │ ├── (.)sign-up │ │ │ └── page.tsx │ │ └── default.tsx │ ├── [teamSlug] │ │ ├── dashboard │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ └── settings │ │ │ ├── accounts │ │ │ └── page.tsx │ │ │ ├── aside-link.tsx │ │ │ ├── billing │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── members │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ └── profile │ │ │ └── page.tsx │ ├── api │ │ ├── accept-team-invitation │ │ │ └── [token] │ │ │ │ └── route.ts │ │ ├── lemonsqueezy │ │ │ └── route.ts │ │ ├── oauth │ │ │ ├── github │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ ├── sign-in │ │ │ └── route.ts │ │ ├── sign-out │ │ │ └── route.ts │ │ ├── sign-up │ │ │ └── route.ts │ │ ├── trpc │ │ │ └── [trpc] │ │ │ │ └── route.ts │ │ └── verify-email │ │ │ └── [token] │ │ │ └── route.ts │ ├── client-providers.tsx │ ├── error.tsx │ ├── global-error.tsx │ ├── layout.tsx │ ├── mdx.css │ ├── not-found.tsx │ ├── opengraph-image.tsx │ ├── robots.ts │ ├── sitemap.ts │ └── styles.css │ ├── index.d.ts │ ├── jest.config.ts │ ├── next-env.d.ts │ ├── next.config.js │ ├── pages │ └── api │ │ ├── email-verification.tsx │ │ └── team-invitation-email.tsx │ ├── postcss.config.js │ ├── posts │ └── blog-post-1.mdx │ ├── project.json │ ├── public │ ├── .gitkeep │ ├── fonts │ │ ├── Inter-Bold.ttf │ │ └── Inter-Regular.ttf │ ├── images │ │ ├── apple-icon-180.png │ │ ├── favicon-196.png │ │ ├── logo-mark.svg │ │ ├── logo.svg │ │ ├── manifest-icon-192.maskable.png │ │ └── manifest-icon-512.maskable.png │ └── site.webmanifest │ ├── tailwind.config.js │ ├── tsconfig.json │ └── tsconfig.spec.json ├── babel.config.json ├── contentlayer.config.ts ├── drizzle.config.json ├── jest.config.ts ├── jest.preset.js ├── libs ├── .gitkeep ├── authentication │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── authentication.ts │ │ │ └── get-authentication.ts │ ├── tsconfig.json │ └── tsconfig.lib.json ├── authorisation │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ └── authorisation.ts │ ├── tsconfig.json │ └── tsconfig.lib.json ├── configuration │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ └── web.ts │ ├── tsconfig.json │ └── tsconfig.lib.json ├── db │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── db.ts │ │ │ └── schema.ts │ ├── tsconfig.json │ └── tsconfig.lib.json ├── feature │ ├── authentication │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ ├── authentication-form │ │ │ │ │ ├── authentication-form.tsx │ │ │ │ │ ├── github-button.tsx │ │ │ │ │ └── schema.ts │ │ │ │ ├── oauth │ │ │ │ │ └── github.ts │ │ │ │ ├── sign-in │ │ │ │ │ ├── action.ts │ │ │ │ │ ├── sign-in-form.tsx │ │ │ │ │ └── sign-in.ts │ │ │ │ ├── sign-out │ │ │ │ │ └── sign-out.ts │ │ │ │ └── sign-up │ │ │ │ │ ├── action.ts │ │ │ │ │ ├── sign-up-form.tsx │ │ │ │ │ └── sign-up.ts │ │ │ └── server.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json │ ├── billing │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ ├── plan-card │ │ │ │ │ └── plan-card.tsx │ │ │ │ └── plan-form │ │ │ │ │ └── plan-form.tsx │ │ │ └── server.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json │ ├── dashboard │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ └── header │ │ │ │ │ └── header.tsx │ │ │ └── server.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json │ ├── settings │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ ├── add-connected-account │ │ │ │ │ └── add-connected-account.tsx │ │ │ │ ├── connected-accounts │ │ │ │ │ └── connected-accounts.tsx │ │ │ │ ├── profile-form │ │ │ │ │ ├── container.tsx │ │ │ │ │ └── form.tsx │ │ │ │ └── remove-connected-account │ │ │ │ │ └── remove-connected-account.tsx │ │ │ └── server.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json │ └── team │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── project.json │ │ ├── src │ │ ├── index.ts │ │ ├── lib │ │ │ ├── invite-members-form │ │ │ │ ├── container.tsx │ │ │ │ └── form.tsx │ │ │ ├── invite-table │ │ │ │ ├── container.tsx │ │ │ │ ├── row-actions.tsx │ │ │ │ └── table.tsx │ │ │ ├── team-form │ │ │ │ ├── container.tsx │ │ │ │ └── form.tsx │ │ │ ├── team-switcher │ │ │ │ └── team-switcher.tsx │ │ │ ├── team-table │ │ │ │ ├── container.tsx │ │ │ │ ├── row-actions.tsx │ │ │ │ └── table.tsx │ │ │ └── team-tabs │ │ │ │ └── team-tabs.tsx │ │ └── server.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json ├── ui │ ├── blog │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── project.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── lib │ │ │ │ ├── article-card │ │ │ │ │ └── article-card.tsx │ │ │ │ └── mdx-components │ │ │ │ │ └── mdx-components.tsx │ │ │ └── server.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json │ └── web │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── README.md │ │ ├── project.json │ │ ├── src │ │ ├── index.ts │ │ ├── lib │ │ │ ├── accordion │ │ │ │ └── accordion.tsx │ │ │ ├── alert-dialog │ │ │ │ └── alert-dialog.tsx │ │ │ ├── alert │ │ │ │ └── alert.tsx │ │ │ ├── avatar │ │ │ │ └── avatar.tsx │ │ │ ├── badge │ │ │ │ └── badge.tsx │ │ │ ├── button │ │ │ │ └── button.tsx │ │ │ ├── calendar │ │ │ │ └── calendar.tsx │ │ │ ├── card │ │ │ │ └── card.tsx │ │ │ ├── command │ │ │ │ └── command.tsx │ │ │ ├── data-table │ │ │ │ ├── data-table-pagination.tsx │ │ │ │ └── data-table.tsx │ │ │ ├── date-range-picker │ │ │ │ └── date-range-picker.tsx │ │ │ ├── dialog │ │ │ │ └── dialog.tsx │ │ │ ├── dropdown-menu │ │ │ │ └── dropdown-menu.tsx │ │ │ ├── footer │ │ │ │ └── footer.tsx │ │ │ ├── form │ │ │ │ └── form.tsx │ │ │ ├── header │ │ │ │ └── header.tsx │ │ │ ├── input │ │ │ │ └── input.tsx │ │ │ ├── intercept-dialog │ │ │ │ └── intercept-dialog.tsx │ │ │ ├── label │ │ │ │ └── label.tsx │ │ │ ├── logo │ │ │ │ └── logo.tsx │ │ │ ├── popover │ │ │ │ └── popover.tsx │ │ │ ├── radio-group │ │ │ │ └── radio-group.tsx │ │ │ ├── select │ │ │ │ └── select.tsx │ │ │ ├── sign-out-button │ │ │ │ └── sign-out-button.tsx │ │ │ ├── skeleton │ │ │ │ └── skeleton.tsx │ │ │ ├── table │ │ │ │ └── table.tsx │ │ │ ├── tabs │ │ │ │ └── tabs.tsx │ │ │ ├── theme-provider │ │ │ │ └── theme-provider.tsx │ │ │ ├── toaster │ │ │ │ └── toaster.tsx │ │ │ ├── tooltip │ │ │ │ └── tooltip.tsx │ │ │ └── user-button │ │ │ │ └── user-button.tsx │ │ └── server.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json └── utility │ ├── email │ ├── .babelrc │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── components │ │ │ ├── Button.tsx │ │ │ ├── Footer.tsx │ │ │ ├── Header.tsx │ │ │ ├── Heading.tsx │ │ │ ├── Link.tsx │ │ │ ├── Text.tsx │ │ │ ├── base-layout.tsx │ │ │ └── index.ts │ │ ├── emails │ │ │ ├── team-invitation.tsx │ │ │ └── verify-email.tsx │ │ ├── index.ts │ │ ├── mailing.config.json │ │ ├── previews │ │ │ ├── team-invitation.tsx │ │ │ └── verify-email.tsx │ │ └── theme.ts │ ├── tsconfig.json │ └── tsconfig.lib.json │ ├── payment │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── client.ts │ │ │ ├── create-checkout │ │ │ └── create-checkout.ts │ │ │ ├── get-plans │ │ │ └── get-plans.ts │ │ │ ├── get-products │ │ │ └── get-products.ts │ │ │ └── get-variants │ │ │ └── get-variants.ts │ ├── tsconfig.json │ └── tsconfig.lib.json │ ├── schema │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ └── team │ │ │ ├── index.ts │ │ │ ├── invite-members-schema.ts │ │ │ └── update-team-schema.ts │ ├── tsconfig.json │ └── tsconfig.lib.json │ ├── shared │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── classnames │ │ │ └── classnames.ts │ │ │ ├── format-date │ │ │ └── format-date.ts │ │ │ ├── generate-token │ │ │ └── generate-token.ts │ │ │ └── get-first-part-of-email │ │ │ └── get-first-part-of-email.ts │ ├── tsconfig.json │ └── tsconfig.lib.json │ ├── trpc-next-client │ ├── .babelrc │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── client.ts │ │ │ ├── createHydrateClient.tsx │ │ │ └── createTrpcNextBeta.tsx │ ├── tsconfig.json │ └── tsconfig.lib.json │ ├── trpc-next-server │ ├── .babelrc │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── createTrpcNextLayout.tsx │ │ │ ├── local-storage.ts │ │ │ └── server.ts │ ├── tsconfig.json │ └── tsconfig.lib.json │ └── trpc │ ├── .eslintrc.json │ ├── README.md │ ├── project.json │ ├── src │ ├── index.ts │ └── lib │ │ ├── createContext.ts │ │ ├── createRouter.ts │ │ └── routers │ │ ├── _app.ts │ │ ├── billing-router │ │ └── billing-router.ts │ │ ├── invitation-router │ │ └── invitation-router.tsx │ │ ├── member-router │ │ └── member-router.tsx │ │ ├── team-router │ │ └── team-router.tsx │ │ └── user-router │ │ └── user-router.tsx │ ├── tsconfig.json │ └── tsconfig.lib.json ├── mailing.config.json ├── migrations.json ├── nx.json ├── package-lock.json ├── package.json ├── tools ├── locales-sync.config.js └── tsconfig.tools.json ├── tsconfig.base.json └── types ├── lucia.d.ts └── next-intl.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | GITHUB_CLIENT_ID="" 2 | GITHUB_CLIENT_SECRET="" 3 | 4 | LEMON_SQUEEZY_API_KEY="" 5 | LEMON_SQUEEZY_STORE_ID="" 6 | LEMON_SQUEEZY_WEBHOOK_SECRET="" 7 | 8 | RESEND_API_KEY="" 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": ["**/*"], 4 | "plugins": ["@nx"], 5 | "overrides": [ 6 | { 7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 8 | "rules": { 9 | "@nx/enforce-module-boundaries": [ 10 | "error", 11 | { 12 | "enforceBuildableLibDependency": true, 13 | "allow": [], 14 | "depConstraints": [ 15 | { 16 | "sourceTag": "type:application", 17 | "onlyDependOnLibsWithTags": ["type:feature", "type:utility"] 18 | }, 19 | { 20 | "sourceTag": "type:feature", 21 | "onlyDependOnLibsWithTags": ["type:feature", "type:utility"] 22 | }, 23 | { 24 | "sourceTag": "type:utility", 25 | "onlyDependOnLibsWithTags": ["type:utility"] 26 | } 27 | ] 28 | } 29 | ] 30 | } 31 | }, 32 | { 33 | "files": ["*.ts", "*.tsx"], 34 | "extends": ["plugin:@nx/typescript"], 35 | "rules": {} 36 | }, 37 | { 38 | "files": ["*.js", "*.jsx"], 39 | "extends": ["plugin:@nx/javascript"], 40 | "rules": {} 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | dist 5 | tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | # Next.js 42 | .next 43 | 44 | .env 45 | .react-email 46 | .mailing 47 | 48 | .contentlayer 49 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | /dist 3 | /coverage -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": false, 5 | "printWidth": 120 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "nrwl.angular-console", 4 | "esbenp.prettier-vscode", 5 | "firsttris.vscode-jest-runner", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Amos Bastian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /apps/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amosbastian/template/bdc77731d4f0dfef0bd2529205c8dd5b733d5b4a/apps/.gitkeep -------------------------------------------------------------------------------- /apps/web-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/web-e2e/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | import { nxE2EPreset } from "@nx/cypress/plugins/cypress-preset"; 3 | 4 | export default defineConfig({ 5 | e2e: nxE2EPreset(__dirname), 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "apps/web-e2e/src", 5 | "projectType": "application", 6 | "targets": { 7 | "e2e": { 8 | "executor": "@nx/cypress:cypress", 9 | "options": { 10 | "cypressConfig": "apps/web-e2e/cypress.config.ts", 11 | "devServerTarget": "web:serve:development", 12 | "testingType": "e2e" 13 | }, 14 | "configurations": { 15 | "production": { 16 | "devServerTarget": "web:serve:production" 17 | } 18 | } 19 | }, 20 | "lint": { 21 | "executor": "@nx/linter:eslint", 22 | "outputs": ["{options.outputFile}"], 23 | "options": { 24 | "lintFilePatterns": ["apps/web-e2e/**/*.{js,ts}"] 25 | } 26 | } 27 | }, 28 | "tags": [], 29 | "implicitDependencies": ["web"] 30 | } 31 | -------------------------------------------------------------------------------- /apps/web-e2e/src/e2e/app.cy.ts: -------------------------------------------------------------------------------- 1 | import { getGreeting } from "../support/app.po"; 2 | 3 | describe("web", () => { 4 | beforeEach(() => cy.visit("/")); 5 | 6 | it("should display welcome message", () => { 7 | // Custom command example, see `../support/commands.ts` file 8 | cy.login("my-email@something.com", "myPassword"); 9 | 10 | // Function helper example, see `../support/app.po.ts` file 11 | getGreeting().contains("Welcome web"); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/web-e2e/src/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io" 4 | } 5 | -------------------------------------------------------------------------------- /apps/web-e2e/src/support/app.po.ts: -------------------------------------------------------------------------------- 1 | export const getGreeting = () => cy.get("h1"); 2 | -------------------------------------------------------------------------------- /apps/web-e2e/src/support/commands.ts: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-namespace 12 | declare namespace Cypress { 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | interface Chainable { 15 | login(email: string, password: string): void; 16 | } 17 | } 18 | // 19 | // -- This is a parent command -- 20 | Cypress.Commands.add("login", (email, password) => { 21 | console.log("Custom command example: Login", email, password); 22 | }); 23 | // 24 | // -- This is a child command -- 25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 26 | // 27 | // 28 | // -- This is a dual command -- 29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 30 | // 31 | // 32 | // -- This will overwrite an existing command -- 33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 34 | -------------------------------------------------------------------------------- /apps/web-e2e/src/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import "./commands"; 18 | -------------------------------------------------------------------------------- /apps/web-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "outDir": "../../dist/out-tsc", 6 | "allowJs": true, 7 | "types": ["cypress", "node"] 8 | }, 9 | "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:@nx/react-typescript", "next", "next/core-web-vitals", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*", ".next/**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": { 8 | "@nx/enforce-module-boundaries": [ 9 | "error", 10 | { 11 | "enforceBuildableLibDependency": true, 12 | "allow": [], 13 | "depConstraints": [ 14 | { 15 | "sourceTag": "*", 16 | "onlyDependOnLibsWithTags": ["*"] 17 | } 18 | ] 19 | } 20 | ] 21 | } 22 | }, 23 | { 24 | "files": ["*.ts", "*.tsx"], 25 | "rules": {} 26 | }, 27 | { 28 | "files": ["*.js", "*.jsx"], 29 | "rules": {} 30 | } 31 | ], 32 | "rules": { 33 | "@next/next/no-html-link-for-pages": "off" 34 | }, 35 | "env": { 36 | "jest": true 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /apps/web/app/(authentication)/layout.tsx: -------------------------------------------------------------------------------- 1 | export default function AuthenticationLayout({ children }: { children: React.ReactNode }) { 2 | return
{children}
; 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/app/(authentication)/sign-in/page.tsx: -------------------------------------------------------------------------------- 1 | import { BRAND_NAME } from "@template/configuration"; 2 | import { SignInForm } from "@template/feature/authentication/server"; 3 | import { buttonVariants } from "@template/ui/web"; 4 | import { Logo } from "@template/ui/web/server"; 5 | import { classnames } from "@template/utility/shared"; 6 | import { ChevronLeft } from "lucide-react"; 7 | import { type Metadata } from "next"; 8 | import Link from "next/link"; 9 | 10 | export default function SignInPage() { 11 | return ( 12 |
13 | 17 | <> 18 | 19 | Back 20 | 21 | 22 |
23 |
24 | 25 |

Sign in to {BRAND_NAME}

26 |
27 | 28 |

29 | 30 | Don't have an account? Sign up 31 | 32 |

33 |
34 |
35 | ); 36 | } 37 | 38 | export const metadata: Metadata = { 39 | title: "Sign in", 40 | }; 41 | -------------------------------------------------------------------------------- /apps/web/app/(authentication)/sign-up/page.tsx: -------------------------------------------------------------------------------- 1 | import { BRAND_NAME } from "@template/configuration"; 2 | import { SignUpForm } from "@template/feature/authentication/server"; 3 | import { buttonVariants } from "@template/ui/web"; 4 | import { Logo } from "@template/ui/web/server"; 5 | import { classnames } from "@template/utility/shared"; 6 | import { ChevronLeft } from "lucide-react"; 7 | import { type Metadata } from "next"; 8 | import Link from "next/link"; 9 | 10 | export default function SignInPage() { 11 | return ( 12 |
13 | 17 | <> 18 | 19 | Back 20 | 21 | 22 |
23 |
24 | 25 |

Sign up for {BRAND_NAME}

26 |
27 | 28 |

29 | 30 | Already have an account? Sign in 31 | 32 |

33 |
34 |
35 | ); 36 | } 37 | 38 | export const metadata: Metadata = { 39 | title: "Sign up", 40 | }; 41 | -------------------------------------------------------------------------------- /apps/web/app/(marketing)/blog/page.tsx: -------------------------------------------------------------------------------- 1 | import { BRAND_NAME } from "@template/configuration"; 2 | import { allPosts } from "@template/contentlayer"; 3 | import { ArticleCard } from "@template/ui/blog/server"; 4 | import { compareDesc } from "date-fns"; 5 | 6 | export const metadata = { 7 | title: "Blog", 8 | description: `Read all about ${BRAND_NAME}`, 9 | }; 10 | 11 | export default async function BlogPage() { 12 | const posts = allPosts 13 | .filter((post) => post.published) 14 | .sort((a, b) => { 15 | return compareDesc(new Date(a.datePublished), new Date(b.datePublished)); 16 | }); 17 | 18 | return ( 19 |
20 |
21 |
22 |

From the blog

23 |

24 | Learn how to grow your business with our expert advice. 25 |

26 |
27 | {posts.map((post) => ( 28 | 29 | ))} 30 |
31 |
32 |
33 |
34 | ); 35 | } 36 | 37 | // https://github.com/vercel/next.js/issues/50634 38 | export const dynamic = "force-static"; 39 | -------------------------------------------------------------------------------- /apps/web/app/(marketing)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Footer } from "@template/ui/web"; 2 | import { Header } from "@template/ui/web/server"; 3 | 4 | export default function MarketingLayout({ children }: { children: React.ReactNode }) { 5 | return ( 6 | <> 7 |
8 |
{children}
9 |