Modernize the template
11
.docker/scripts/postgres/create-databases.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
|
||||
if [ -n "$POSTGRES_DATABASES" ]; then
|
||||
for db in $(echo $POSTGRES_DATABASES | tr ',' ' '); do
|
||||
echo "Creating $db..."
|
||||
psql -v ON_ERROR_STOP=1 -c "CREATE DATABASE $db;"
|
||||
done
|
||||
fi
|
||||
22
.env
|
|
@ -1,24 +1,12 @@
|
|||
# FIXME: Configure environment variables for your project
|
||||
|
||||
# Clerk authentication
|
||||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_b3Blbi1zdGlua2J1Zy04LmNsZXJrLmFjY291bnRzLmRldiQ
|
||||
|
||||
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
|
||||
|
||||
# Stripe
|
||||
# If you need a real Stripe subscription payment with checkout page, customer portal, webhook, etc.
|
||||
# You can check out the Next.js Boilerplate Pro at: https://nextjs-boilerplate.com/pro-saas-starter-kit
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51PNk4fKOp3DEwzQle6Cx1j3IW1Lze5nFKZ4JBX0gLpNQ3hjFbMiT25gw7LEr369ge7JIsVA2qRhdKQm1NAmVehXl00FQxwRfh1
|
||||
# Use Stripe test mode price id or production price id
|
||||
BILLING_PLAN_ENV=dev
|
||||
|
||||
######## [BEGIN] SENSITIVE DATA ######## For security reason, don't update the following variables (secret key) directly in this file.
|
||||
######## Please create a new file named `.env.local`, all environment files ending with `.local` won't be tracked by Git.
|
||||
######## After creating the file, you can add the following variables.
|
||||
# Clerk authentication
|
||||
# Clerk
|
||||
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
|
||||
NEXT_PUBLIC_CLERK_FRONTEND_API=https://epic-chamois-85.clerk.accounts.dev
|
||||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_ZXBpYy1jaGFtb2lzLTg1LmNsZXJrLmFjY291bnRzLmRldiQ
|
||||
CLERK_SECRET_KEY=your_clerk_secret_key
|
||||
|
||||
# Stripe
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51PNk4fKOp3DEwzQle6Cx1j3IW1Lze5nFKZ4JBX0gLpNQ3hjFbMiT25gw7LEr369ge7JIsVA2qRhdKQm1NAmVehXl00FQxwRfh1
|
||||
STRIPE_SECRET_KEY=your_stripe_secret_key
|
||||
STRIPE_WEBHOOK_SECRET=your_stripe_webhook_secret
|
||||
######## [END] SENSITIVE DATA
|
||||
|
|
|
|||
4
.github/FUNDING.yml
vendored
|
|
@ -1,4 +0,0 @@
|
|||
github: ixartz
|
||||
custom:
|
||||
- 'https://nextjs-boilerplate.com/pro-saas-starter-kit'
|
||||
- 'https://nextlessjs.com'
|
||||
31
.github/workflows/CI.yml
vendored
|
|
@ -50,10 +50,6 @@ jobs:
|
|||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
|
||||
- if: github.event_name == 'pull_request'
|
||||
name: Validate all commits from PR
|
||||
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
|
||||
|
||||
- name: Linter
|
||||
run: npm run lint
|
||||
|
||||
|
|
@ -86,30 +82,3 @@ jobs:
|
|||
name: test-results
|
||||
path: test-results/
|
||||
retention-days: 7
|
||||
|
||||
synchronize-with-crowdin:
|
||||
name: GitHub PR synchronize with Crowdin
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs: [build, test]
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }} # Crowdin Actions needs to push commits to the PR branch, checkout HEAD commit instead of merge commit
|
||||
fetch-depth: 0
|
||||
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: true
|
||||
download_translations: true
|
||||
create_pull_request: false
|
||||
localization_branch_name: ${{ github.head_ref || github.ref_name }} # explanation here: https://stackoverflow.com/a/71158878
|
||||
commit_message: 'chore: new Crowdin translations by GitHub Action'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
|
|
|
|||
63
.github/workflows/checkly.yml
vendored
|
|
@ -1,63 +0,0 @@
|
|||
name: Checkly
|
||||
|
||||
on: [deployment_status]
|
||||
|
||||
env:
|
||||
CHECKLY_API_KEY: ${{ secrets.CHECKLY_API_KEY }}
|
||||
CHECKLY_ACCOUNT_ID: ${{ secrets.CHECKLY_ACCOUNT_ID }}
|
||||
CHECKLY_TEST_ENVIRONMENT: ${{ github.event.deployment_status.environment }}
|
||||
|
||||
jobs:
|
||||
test-e2e:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
# Only run when the deployment was successful
|
||||
if: github.event.deployment_status.state == 'success'
|
||||
|
||||
name: Test E2E on Checkly
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: '${{ github.event.deployment_status.deployment.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set branch name # workaround to detect branch name in "deployment_status" actions
|
||||
run: echo "CHECKLY_TEST_REPO_BRANCH=$(git show -s --pretty=%D HEAD | tr -s ',' '\n' | sed 's/^ //' | grep -e 'origin/' | head -1 | sed 's/\origin\///g')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
|
||||
- name: Restore or cache node_modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node-modules-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
|
||||
- name: Run checks # run the checks passing in the ENVIRONMENT_URL and recording a test session.
|
||||
id: run-checks
|
||||
run: npx checkly test --reporter=github --record
|
||||
env:
|
||||
VERCEL_BYPASS_TOKEN: ${{ secrets.VERCEL_BYPASS_TOKEN }}
|
||||
ENVIRONMENT_URL: ${{ github.event.deployment_status.environment_url }}
|
||||
|
||||
- name: Create summary # export the markdown report to the job summary.
|
||||
id: create-summary
|
||||
run: cat checkly-github-report.md > $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Deploy checks # if the test run was successful and we are on Production, deploy the checks
|
||||
id: deploy-checks
|
||||
if: steps.run-checks.outcome == 'success' && github.event.deployment_status.environment == 'Production'
|
||||
run: npx checkly deploy --force
|
||||
33
.github/workflows/crowdin.yml
vendored
|
|
@ -1,33 +0,0 @@
|
|||
name: Crowdin Action
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main] # Run on push to the main branch
|
||||
schedule:
|
||||
- cron: '0 5 * * *' # Run every day at 5am
|
||||
workflow_dispatch: # Run manually
|
||||
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
name: Synchronize with Crowdin
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: true
|
||||
download_translations: true
|
||||
localization_branch_name: l10n_crowdin_translations
|
||||
create_pull_request: true
|
||||
pull_request_title: New Crowdin Translations
|
||||
pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)'
|
||||
pull_request_base_branch_name: main
|
||||
commit_message: 'chore: new Crowdin translations by GitHub Action'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
6
.gitignore
vendored
|
|
@ -49,3 +49,9 @@ local
|
|||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# docker
|
||||
.docker/*
|
||||
!.docker/scripts
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/sh
|
||||
cd "$(dirname "$0")/.." && npx --no -- commitlint --edit $1
|
||||
4
.prettierrc.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true
|
||||
}
|
||||
1
.tool-versions
Normal file
|
|
@ -0,0 +1 @@
|
|||
nodejs 20.18.1
|
||||
6
.vscode/extensions.json
vendored
|
|
@ -5,10 +5,6 @@
|
|||
"csstools.postcss",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"vitest.explorer",
|
||||
"humao.rest-client",
|
||||
"yoavbls.pretty-ts-errors",
|
||||
"ms-playwright.playwright",
|
||||
"github.vscode-github-actions",
|
||||
"lokalise.i18n-ally"
|
||||
"yoavbls.pretty-ts-errors"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
21
.vscode/settings.json
vendored
|
|
@ -15,29 +15,22 @@
|
|||
// give priority for Link to next/link instead of lucide-react
|
||||
"lucide-react"
|
||||
],
|
||||
"typescript.preferences.preferTypeOnlyAutoImports": true, // Prefer type-only imports
|
||||
"typescript.preferences.preferTypeOnlyAutoImports": true,
|
||||
|
||||
// Vitest
|
||||
"testing.automaticallyOpenTestResults": "neverOpen", // Don't open the test results automatically
|
||||
|
||||
// I18n
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/locales"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
|
||||
// Disable the default formatter, use ESLint instead
|
||||
"prettier.enable": false,
|
||||
"prettier.enable": true,
|
||||
"editor.formatOnSave": false,
|
||||
|
||||
// Auto fix with ESLint on save
|
||||
"editor.codeActionsOnSave": [
|
||||
"source.addMissingImports",
|
||||
"source.fixAll.eslint"
|
||||
],
|
||||
// "editor.codeActionsOnSave": [
|
||||
// "source.addMissingImports",
|
||||
// "source.fixAll.eslint"
|
||||
// ],
|
||||
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.format.enable": true,
|
||||
"eslint.format.enable": false,
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
|
|
|
|||
124
CHANGELOG.md
|
|
@ -1,124 +0,0 @@
|
|||
## [1.7.6](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.7.5...v1.7.6) (2025-05-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update clerk to the latest version and update middlware to use await with auth ([2287192](https://github.com/ixartz/SaaS-Boilerplate/commit/2287192ddcf5b27a1f43ac2b7a992e065b990627))
|
||||
|
||||
## [1.7.5](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.7.4...v1.7.5) (2025-05-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* clerk integration ([a9981cd](https://github.com/ixartz/SaaS-Boilerplate/commit/a9981cddcb4a0e2365066938533cd13225ce10a9))
|
||||
|
||||
## [1.7.4](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.7.3...v1.7.4) (2024-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove custom framework configuration for i18n-ally vscode ([63f87fe](https://github.com/ixartz/SaaS-Boilerplate/commit/63f87feb3c0cb186c500ef9bed9cb50d7309224d))
|
||||
* use new vitest vscode setting for preventing automatic opening of the test results ([2a2b945](https://github.com/ixartz/SaaS-Boilerplate/commit/2a2b945050f8d19883d6f2a8a6ec5ccf8b1f4173))
|
||||
|
||||
## [1.7.3](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.7.2...v1.7.3) (2024-11-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* chnage dashboard index message button in french translation ([2f1dca8](https://github.com/ixartz/SaaS-Boilerplate/commit/2f1dca84cb05af52a959dd9630769ed661d8c69b))
|
||||
* remove update deps github workflow, add separator in dashboard header ([fcf0fb4](https://github.com/ixartz/SaaS-Boilerplate/commit/fcf0fb48304ce45f6ceefa7d7eae11692655c749))
|
||||
|
||||
## [1.7.2](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.7.1...v1.7.2) (2024-10-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* hide text in logo used in dashboard and add spacing for sign in button used in navbar ([a0eeda1](https://github.com/ixartz/SaaS-Boilerplate/commit/a0eeda12251551fd6a8e50222f46f3d47f0daad7))
|
||||
* in dashboard, make the logo smaller, display without text ([f780727](https://github.com/ixartz/SaaS-Boilerplate/commit/f780727659fa58bbe6e4250dd63b2819369b7308))
|
||||
* remove hydration error and unify with pro version 1.6.1 ([ea2d02b](https://github.com/ixartz/SaaS-Boilerplate/commit/ea2d02bd52de34c6cd2390d160ffe7f14319d5c3))
|
||||
|
||||
## [1.7.1](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.7.0...v1.7.1) (2024-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update logicalId in checkly configuration ([6e7a479](https://github.com/ixartz/SaaS-Boilerplate/commit/6e7a4795bff0b92d3681fadc36256aa957eb2613))
|
||||
|
||||
# [1.7.0](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.6.1...v1.7.0) (2024-10-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update de Next.js Boilerplate v3.58.1 ([16aea65](https://github.com/ixartz/SaaS-Boilerplate/commit/16aea651ef93ed627e3bf310412cfd3651aeb3e4))
|
||||
|
||||
## [1.6.1](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.6.0...v1.6.1) (2024-08-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add demo banner at the top of the landing page ([09bf8c8](https://github.com/ixartz/SaaS-Boilerplate/commit/09bf8c8aba06eba1405fb0c20aeec23dfb732bb7))
|
||||
* issue to build Next.js with Node.js 22.7, use 22.6 instead ([4acaef9](https://github.com/ixartz/SaaS-Boilerplate/commit/4acaef95edec3cd72a35405969ece9d55a2bb641))
|
||||
|
||||
# [1.6.0](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.5.0...v1.6.0) (2024-07-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update to Next.js Boilerpalte v3.54 ([ae80843](https://github.com/ixartz/SaaS-Boilerplate/commit/ae808433e50d6889559fff382d4b9c595d34e04f))
|
||||
|
||||
# [1.5.0](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.4.0...v1.5.0) (2024-06-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update to Drizzle Kit 0.22, Storybook 8, migrate to vitest ([c2f19cd](https://github.com/ixartz/SaaS-Boilerplate/commit/c2f19cd8e9dc983e0ad799da2474610b57b88f50))
|
||||
|
||||
# [1.4.0](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.3.0...v1.4.0) (2024-05-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* vscode jest open test result view on test fails and add unauthenticatedUrl in clerk middleware ([3cfcb6b](https://github.com/ixartz/SaaS-Boilerplate/commit/3cfcb6b00d91dabcb00cbf8eb2d8be6533ff672e))
|
||||
|
||||
# [1.3.0](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.2.1...v1.3.0) (2024-05-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add custom framework for i18n-ally and replace deprecated Jest VSCode configuration ([a9889dc](https://github.com/ixartz/SaaS-Boilerplate/commit/a9889dc129aeeba8801f4f47e54d46e9515e6a29))
|
||||
* create dashboard header component ([f3dc1da](https://github.com/ixartz/SaaS-Boilerplate/commit/f3dc1da451ab8dce90d111fe4bbc8d4bc99e4b01))
|
||||
* don't redirect to organization-selection if the user is already on this page ([87da997](https://github.com/ixartz/SaaS-Boilerplate/commit/87da997b853fd9dcb7992107d2cb206817258910))
|
||||
* make the landing page responsive and works on mobile ([27e908a](https://github.com/ixartz/SaaS-Boilerplate/commit/27e908a735ea13845a6cc42acc12e6cae3232b9b))
|
||||
* make user dashboard responsive ([f88c9dd](https://github.com/ixartz/SaaS-Boilerplate/commit/f88c9dd5ac51339d37d1d010e5b16c7776c73b8d))
|
||||
* migreate Env.mjs file to Env.ts ([2e6ff12](https://github.com/ixartz/SaaS-Boilerplate/commit/2e6ff124dcc10a3c12cac672cbb82ec4000dc60c))
|
||||
* remove next-sitemap and use the native Next.js sitemap/robots.txt ([75c9751](https://github.com/ixartz/SaaS-Boilerplate/commit/75c9751d607b8a6a269d08667f7d9900797ff38a))
|
||||
* upgrade to Clerk v5 and use Clerk's Core 2 ([a92cef0](https://github.com/ixartz/SaaS-Boilerplate/commit/a92cef026b5c85a703f707aabf42d28a16f07054))
|
||||
* use Node.js version 20 and 22 in GitHub Actions ([226b5e9](https://github.com/ixartz/SaaS-Boilerplate/commit/226b5e970f46bfcd384ca60cd63ebb15516eca21))
|
||||
|
||||
## [1.2.1](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.2.0...v1.2.1) (2024-03-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* redirect user to the landing page after signing out ([6e9f383](https://github.com/ixartz/SaaS-Boilerplate/commit/6e9f3839daaab56dd3cf3e57287ea0f3862b8588))
|
||||
|
||||
# [1.2.0](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.1.0...v1.2.0) (2024-03-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add link to the GitHub repository ([ed42176](https://github.com/ixartz/SaaS-Boilerplate/commit/ed42176bdc2776cacc2c939bac45914a1ede8e51))
|
||||
|
||||
# [1.1.0](https://github.com/ixartz/SaaS-Boilerplate/compare/v1.0.0...v1.1.0) (2024-03-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* launching SaaS boilerplate for helping developers to build SaaS quickly ([7f24661](https://github.com/ixartz/SaaS-Boilerplate/commit/7f246618791e3a731347dffc694a52fa90b1152a))
|
||||
|
||||
# 1.0.0 (2024-03-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* initial commit ([d58e1d9](https://github.com/ixartz/SaaS-Boilerplate/commit/d58e1d97e11baa0a756bd038332eb84daf5a8327))
|
||||
21
LICENSE
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Remi W.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import { defineConfig } from 'checkly';
|
||||
import { EmailAlertChannel, Frequency } from 'checkly/constructs';
|
||||
|
||||
const sendDefaults = {
|
||||
sendFailure: true,
|
||||
sendRecovery: true,
|
||||
sendDegraded: true,
|
||||
};
|
||||
|
||||
// FIXME: Add your production URL
|
||||
const productionURL = 'https://react-saas.com';
|
||||
|
||||
const emailChannel = new EmailAlertChannel('email-channel-1', {
|
||||
// FIXME: add your own email address, Checkly will send you an email notification if a check fails
|
||||
address: 'contact@creativedesignsguru.com',
|
||||
...sendDefaults,
|
||||
});
|
||||
|
||||
export const config = defineConfig({
|
||||
// FIXME: Add your own project name, logical ID, and repository URL
|
||||
projectName: 'SaaS Boilerplate',
|
||||
logicalId: 'saas-boilerplate',
|
||||
repoUrl: 'https://github.com/ixartz/Next-js-Boilerplate',
|
||||
checks: {
|
||||
locations: ['us-east-1', 'eu-west-1'],
|
||||
tags: ['website'],
|
||||
runtimeId: '2024.02',
|
||||
browserChecks: {
|
||||
frequency: Frequency.EVERY_24H,
|
||||
testMatch: '**/tests/e2e/**/*.check.e2e.ts',
|
||||
alertChannels: [emailChannel],
|
||||
},
|
||||
playwrightConfig: {
|
||||
use: {
|
||||
baseURL: process.env.ENVIRONMENT_URL || productionURL,
|
||||
extraHTTPHeaders: {
|
||||
'x-vercel-protection-bypass': process.env.VERCEL_BYPASS_TOKEN,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
cli: {
|
||||
runLocation: 'eu-west-1',
|
||||
reporters: ['list'],
|
||||
},
|
||||
});
|
||||
|
||||
export default config;
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import type { UserConfig } from '@commitlint/types';
|
||||
|
||||
const Configuration: UserConfig = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
};
|
||||
|
||||
export default Configuration;
|
||||
|
|
@ -1,17 +1,21 @@
|
|||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "src/styles/global.css",
|
||||
"baseColor": "slate",
|
||||
"config": "",
|
||||
"css": "src/styles/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/utils/Helpers"
|
||||
}
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
|
|
|
|||
32
crowdin.yml
|
|
@ -1,32 +0,0 @@
|
|||
#
|
||||
# Your Crowdin credentials
|
||||
#
|
||||
# No need modify CROWDIN_PROJECT_ID and CROWDIN_PERSONAL_TOKEN, you can set them in GitHub Actions secrets
|
||||
project_id_env: CROWDIN_PROJECT_ID
|
||||
api_token_env: CROWDIN_PERSONAL_TOKEN
|
||||
base_path: .
|
||||
base_url: 'https://api.crowdin.com' # https://{organization-name}.crowdin.com for Crowdin Enterprise
|
||||
|
||||
#
|
||||
# Choose file structure in Crowdin
|
||||
# e.g. true or false
|
||||
#
|
||||
preserve_hierarchy: true
|
||||
|
||||
#
|
||||
# Files configuration
|
||||
#
|
||||
files:
|
||||
- source: /src/locales/en.json
|
||||
|
||||
#
|
||||
# Where translations will be placed
|
||||
# e.g. "/resources/%two_letters_code%/%original_file_name%"
|
||||
#
|
||||
translation: '/src/locales/%two_letters_code%.json'
|
||||
|
||||
#
|
||||
# File type
|
||||
# e.g. "json"
|
||||
#
|
||||
type: json
|
||||
13
docker-compose.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
services:
|
||||
postgres:
|
||||
container_name: postgres
|
||||
image: postgres:15.4
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
- ./.docker/postgres:/var/lib/postgresql/data
|
||||
- ./.docker/scripts/postgres:/docker-entrypoint-initdb.d
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DATABASES=roo_code_development,roo_code_test
|
||||
|
|
@ -1,68 +1,73 @@
|
|||
import antfu from '@antfu/eslint-config';
|
||||
import nextPlugin from '@next/eslint-plugin-next';
|
||||
import jestDom from 'eslint-plugin-jest-dom';
|
||||
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
||||
import playwright from 'eslint-plugin-playwright';
|
||||
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
||||
import tailwind from 'eslint-plugin-tailwindcss';
|
||||
import testingLibrary from 'eslint-plugin-testing-library';
|
||||
import globals from 'globals';
|
||||
|
||||
export default antfu({
|
||||
react: true,
|
||||
typescript: true,
|
||||
import js from '@eslint/js';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import pluginReactHooks from 'eslint-plugin-react-hooks';
|
||||
import pluginReact from 'eslint-plugin-react';
|
||||
import pluginNext from '@next/eslint-plugin-next';
|
||||
// import turboPlugin from 'eslint-plugin-turbo';
|
||||
import onlyWarn from 'eslint-plugin-only-warn';
|
||||
|
||||
lessOpinionated: true,
|
||||
isInEditor: false,
|
||||
|
||||
stylistic: {
|
||||
semi: true,
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
...tseslint.configs.recommended,
|
||||
// {
|
||||
// plugins: {
|
||||
// turbo: turboPlugin,
|
||||
// },
|
||||
// rules: {
|
||||
// 'turbo/no-undeclared-env-vars': 'warn',
|
||||
// },
|
||||
// },
|
||||
{
|
||||
plugins: {
|
||||
onlyWarn,
|
||||
},
|
||||
},
|
||||
|
||||
formatters: {
|
||||
css: true,
|
||||
{
|
||||
ignores: ['dist/**'],
|
||||
},
|
||||
|
||||
ignores: [
|
||||
'migrations/**/*',
|
||||
'next-env.d.ts',
|
||||
],
|
||||
}, ...tailwind.configs['flat/recommended'], jsxA11y.flatConfigs.recommended, {
|
||||
plugins: {
|
||||
'@next/next': nextPlugin,
|
||||
{
|
||||
...pluginReact.configs.flat.recommended,
|
||||
languageOptions: {
|
||||
...pluginReact.configs.flat.recommended.languageOptions,
|
||||
globals: {
|
||||
...globals.serviceworker,
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...nextPlugin.configs.recommended.rules,
|
||||
...nextPlugin.configs['core-web-vitals'].rules,
|
||||
{
|
||||
plugins: {
|
||||
'@next/next': pluginNext,
|
||||
},
|
||||
rules: {
|
||||
...pluginNext.configs.recommended.rules,
|
||||
...pluginNext.configs['core-web-vitals'].rules,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
plugins: {
|
||||
'simple-import-sort': simpleImportSort,
|
||||
{
|
||||
plugins: {
|
||||
'react-hooks': pluginReactHooks,
|
||||
},
|
||||
settings: { react: { version: 'detect' } },
|
||||
rules: {
|
||||
...pluginReactHooks.configs.recommended.rules,
|
||||
// React scope no longer necessary with new JSX transform.
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error',
|
||||
{
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}, {
|
||||
files: [
|
||||
'**/*.test.ts?(x)',
|
||||
],
|
||||
...testingLibrary.configs['flat/react'],
|
||||
...jestDom.configs['flat/recommended'],
|
||||
}, {
|
||||
files: [
|
||||
'**/*.spec.ts',
|
||||
'**/*.e2e.ts',
|
||||
],
|
||||
...playwright.configs['flat/recommended'],
|
||||
}, {
|
||||
rules: {
|
||||
'import/order': 'off', // Avoid conflicts with `simple-import-sort` plugin
|
||||
'sort-imports': 'off', // Avoid conflicts with `simple-import-sort` plugin
|
||||
'style/brace-style': ['error', '1tbs'], // Use the default brace style
|
||||
'ts/consistent-type-definitions': ['error', 'type'], // Use `type` instead of `interface`
|
||||
'react/prefer-destructuring-assignment': 'off', // Vscode doesn't support automatically destructuring, it's a pain to add a new variable
|
||||
'node/prefer-global/process': 'off', // Allow using `process.env`
|
||||
'test/padding-around-all': 'error', // Add padding in test files
|
||||
'test/prefer-lowercase-title': 'off', // Allow using uppercase titles in test titles
|
||||
},
|
||||
});
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
'*': ['eslint --fix --no-warn-ignored'],
|
||||
'**/*.ts?(x)': () => 'npm run check-types',
|
||||
};
|
||||
2
next-env.d.ts
vendored
|
|
@ -2,4 +2,4 @@
|
|||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import withBundleAnalyzer from '@next/bundle-analyzer';
|
||||
import { withSentryConfig } from '@sentry/nextjs';
|
||||
import createJiti from 'jiti';
|
||||
import withNextIntl from 'next-intl/plugin';
|
||||
|
||||
const jiti = createJiti(fileURLToPath(import.meta.url));
|
||||
|
||||
jiti('./src/libs/Env');
|
||||
|
||||
const withNextIntlConfig = withNextIntl('./src/libs/i18n.ts');
|
||||
|
||||
const bundleAnalyzer = withBundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
});
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
export default withSentryConfig(
|
||||
bundleAnalyzer(
|
||||
withNextIntlConfig({
|
||||
eslint: {
|
||||
dirs: ['.'],
|
||||
},
|
||||
poweredByHeader: false,
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
serverComponentsExternalPackages: ['@electric-sql/pglite'],
|
||||
},
|
||||
}),
|
||||
),
|
||||
{
|
||||
// For all available options, see:
|
||||
// https://github.com/getsentry/sentry-webpack-plugin#options
|
||||
// FIXME: Add your Sentry organization and project names
|
||||
org: 'nextjs-boilerplate-org',
|
||||
project: 'nextjs-boilerplate',
|
||||
|
||||
// Only print logs for uploading source maps in CI
|
||||
silent: !process.env.CI,
|
||||
|
||||
// For all available options, see:
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
|
||||
|
||||
// Upload a larger set of source maps for prettier stack traces (increases build time)
|
||||
widenClientFileUpload: true,
|
||||
|
||||
// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
|
||||
// This can increase your server load as well as your hosting bill.
|
||||
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
|
||||
// side errors will fail.
|
||||
tunnelRoute: '/monitoring',
|
||||
|
||||
// Hides source maps from generated client bundles
|
||||
hideSourceMaps: true,
|
||||
|
||||
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
||||
disableLogger: true,
|
||||
|
||||
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
|
||||
// See the following for more information:
|
||||
// https://docs.sentry.io/product/crons/
|
||||
// https://vercel.com/docs/cron-jobs
|
||||
automaticVercelMonitors: true,
|
||||
|
||||
// Disable Sentry telemetry
|
||||
telemetry: false,
|
||||
},
|
||||
);
|
||||
54
next.config.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import type { NextConfig } from 'next';
|
||||
import withBundleAnalyzer from '@next/bundle-analyzer';
|
||||
import { withSentryConfig } from '@sentry/nextjs';
|
||||
import createNextIntlPlugin from 'next-intl/plugin';
|
||||
|
||||
const withNextIntl = createNextIntlPlugin();
|
||||
|
||||
const bundleAnalyzer = withBundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
});
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
eslint: { dirs: ['.'] },
|
||||
poweredByHeader: false,
|
||||
reactStrictMode: true,
|
||||
};
|
||||
|
||||
export default withSentryConfig(bundleAnalyzer(withNextIntl(nextConfig)), {
|
||||
// For all available options, see:
|
||||
// https://github.com/getsentry/sentry-webpack-plugin#options
|
||||
// FIXME: Add your Sentry organization and project names
|
||||
org: 'roo-code',
|
||||
project: 'roo-code-cloud',
|
||||
|
||||
// Only print logs for uploading source maps in CI
|
||||
silent: !process.env.CI,
|
||||
|
||||
// For all available options, see:
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
|
||||
|
||||
// Upload a larger set of source maps for prettier stack traces (increases build time)
|
||||
widenClientFileUpload: true,
|
||||
|
||||
// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
|
||||
// This can increase your server load as well as your hosting bill.
|
||||
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
|
||||
// side errors will fail.
|
||||
tunnelRoute: '/monitoring',
|
||||
|
||||
// Hides source maps from generated client bundles
|
||||
// hideSourceMaps: true,
|
||||
|
||||
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
||||
disableLogger: true,
|
||||
|
||||
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
|
||||
// See the following for more information:
|
||||
// https://docs.sentry.io/product/crons/
|
||||
// https://vercel.com/docs/cron-jobs
|
||||
automaticVercelMonitors: true,
|
||||
|
||||
// Disable Sentry telemetry
|
||||
telemetry: false,
|
||||
});
|
||||
41857
package-lock.json
generated
152
package.json
|
|
@ -1,20 +1,18 @@
|
|||
{
|
||||
"name": "saas-boilerplate",
|
||||
"version": "1.7.6",
|
||||
"name": "roo-code-cloud",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev:spotlight": "spotlight-sidecar",
|
||||
"lint": "eslint .",
|
||||
"check-types": "tsc --noEmit --pretty",
|
||||
"test": "vitest run",
|
||||
"test:e2e": "playwright test",
|
||||
"dev:spotlight": "pnpx @spotlightjs/spotlight",
|
||||
"dev:next": "next dev",
|
||||
"dev": "run-p dev:*",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"build-stats": "cross-env ANALYZE=true npm run build",
|
||||
"build-stats": "cross-env ANALYZE=true pnpm run build",
|
||||
"clean": "rimraf .next out coverage",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"check-types": "tsc --noEmit --pretty",
|
||||
"test": "vitest run",
|
||||
"test:e2e": "playwright test",
|
||||
"commit": "cz",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "dotenv -c production -- drizzle-kit migrate",
|
||||
"db:studio": "dotenv -c production -- drizzle-kit studio",
|
||||
|
|
@ -29,53 +27,46 @@
|
|||
"@clerk/localizations": "^3.14.2",
|
||||
"@clerk/nextjs": "^6.18.3",
|
||||
"@clerk/themes": "^2.1.36",
|
||||
"@electric-sql/pglite": "^0.2.12",
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@electric-sql/pglite": "^0.3.0",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@logtail/pino": "^0.5.2",
|
||||
"@radix-ui/react-accordion": "^1.2.1",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.3",
|
||||
"@sentry/nextjs": "^8.34.0",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.14",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-separator": "^1.1.6",
|
||||
"@radix-ui/react-slot": "^1.2.2",
|
||||
"@sentry/nextjs": "^9.17.0",
|
||||
"@spotlightjs/spotlight": "^2.5.0",
|
||||
"@t3-oss/env-nextjs": "^0.11.1",
|
||||
"@t3-oss/env-nextjs": "^0.13.4",
|
||||
"@tailwindcss/postcss": "^4.1.6",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"drizzle-orm": "^0.35.1",
|
||||
"lucide-react": "^0.453.0",
|
||||
"next": "^14.2.25",
|
||||
"next-intl": "^3.21.1",
|
||||
"next-themes": "^0.3.0",
|
||||
"drizzle-orm": "^0.43.1",
|
||||
"lucide-react": "^0.509.0",
|
||||
"next": "^15.3.2",
|
||||
"next-intl": "^4.1.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"pg": "^8.13.0",
|
||||
"pino": "^9.5.0",
|
||||
"pino-pretty": "^11.3.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"sharp": "^0.33.5",
|
||||
"stripe": "^16.12.0",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"sharp": "^0.34.1",
|
||||
"stripe": "^18.1.0",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.27.3",
|
||||
"@clerk/testing": "^1.3.11",
|
||||
"@commitlint/cli": "^19.5.0",
|
||||
"@commitlint/config-conventional": "^19.5.0",
|
||||
"@commitlint/cz-commitlint": "^19.5.0",
|
||||
"@eslint-react/eslint-plugin": "^1.15.0",
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@faker-js/faker": "^9.0.3",
|
||||
"@next/bundle-analyzer": "^14.2.15",
|
||||
"@next/eslint-plugin-next": "^14.2.15",
|
||||
"@percy/cli": "1.30.1",
|
||||
"@next/bundle-analyzer": "^15.3.2",
|
||||
"@next/eslint-plugin-next": "^15.3.2",
|
||||
"@percy/cli": "1.30.10",
|
||||
"@percy/playwright": "^1.0.6",
|
||||
"@playwright/test": "^1.48.1",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@storybook/addon-essentials": "^8.3.5",
|
||||
"@storybook/addon-interactions": "^8.3.5",
|
||||
"@storybook/addon-links": "^8.3.5",
|
||||
|
|
@ -84,78 +75,53 @@
|
|||
"@storybook/nextjs": "^8.3.5",
|
||||
"@storybook/react": "^8.3.5",
|
||||
"@storybook/test": "^8.3.5",
|
||||
"@storybook/test-runner": "^0.19.1",
|
||||
"@storybook/test-runner": "^0.22.0",
|
||||
"@testing-library/jest-dom": "^6.6.1",
|
||||
"@testing-library/react": "^16.0.1",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/node": "^22.7.6",
|
||||
"@types/pg": "^8.11.10",
|
||||
"@types/react": "^18.3.11",
|
||||
"@types/react": "^19.1.3",
|
||||
"@vitejs/plugin-react": "^4.3.2",
|
||||
"@vitest/coverage-v8": "^2.1.9",
|
||||
"@vitest/expect": "^2.1.9",
|
||||
"@vitest/coverage-v8": "^3.1.3",
|
||||
"@vitest/expect": "^3.1.3",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"checkly": "^4.9.0",
|
||||
"commitizen": "^4.3.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"cssnano": "^7.0.6",
|
||||
"dotenv-cli": "^7.4.2",
|
||||
"drizzle-kit": "^0.26.2",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-plugin-format": "^0.1.2",
|
||||
"eslint-plugin-jest-dom": "^5.4.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.0",
|
||||
"eslint-plugin-playwright": "^1.7.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.12",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-tailwindcss": "^3.17.5",
|
||||
"eslint-plugin-testing-library": "^6.3.0",
|
||||
"dotenv-cli": "^8.0.0",
|
||||
"drizzle-kit": "^0.31.1",
|
||||
"eslint": "^9.26.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-only-warn": "^1.1.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-turbo": "^2.5.3",
|
||||
"globals": "^16.1.0",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^9.1.6",
|
||||
"jiti": "^1.21.6",
|
||||
"jsdom": "^25.0.1",
|
||||
"lint-staged": "^15.2.10",
|
||||
"jsdom": "^26.1.0",
|
||||
"lint-staged": "^16.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss": "^8.5.3",
|
||||
"rimraf": "^6.0.1",
|
||||
"semantic-release": "^24.1.2",
|
||||
"start-server-and-test": "^2.0.8",
|
||||
"storybook": "^8.3.5",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"tailwindcss": "^4.1.6",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tsx": "^4.19.1",
|
||||
"tw-animate-css": "^1.2.9",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript-eslint": "^8.32.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"vitest": "^2.1.9",
|
||||
"vitest": "^3.1.3",
|
||||
"vitest-fail-on-console": "^0.7.1"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "@commitlint/cz-commitlint"
|
||||
}
|
||||
},
|
||||
"release": {
|
||||
"branches": [
|
||||
"main"
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,json,css,md}": [
|
||||
"prettier --write"
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@semantic-release/commit-analyzer",
|
||||
{
|
||||
"preset": "conventionalcommits"
|
||||
}
|
||||
],
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
[
|
||||
"@semantic-release/npm",
|
||||
{
|
||||
"npmPublish": false
|
||||
}
|
||||
],
|
||||
"@semantic-release/git",
|
||||
"@semantic-release/github"
|
||||
"src/**/*.{ts,tsx}": [
|
||||
"pnpx eslint --max-warnings=0 --fix"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
18679
pnpm-lock.yaml
generated
Normal file
|
|
@ -1,11 +0,0 @@
|
|||
// Please do not use the array form (like ['tailwindcss', 'postcss-preset-env'])
|
||||
// it will create an unexpected error: Invalid PostCSS Plugin found: [0]
|
||||
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
|
||||
},
|
||||
};
|
||||
14
postcss.config.mjs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// module.exports = {
|
||||
// plugins: {
|
||||
// tailwindcss: {},
|
||||
// autoprefixer: {},
|
||||
// ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
|
||||
// },
|
||||
// };
|
||||
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
plugins: ['@tailwindcss/postcss'],
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1040" height="460" fill="none" viewBox="0 0 1040 460"><path fill="#fff" d="M730.356 287.811c-23.741 0-40.79-17.686-40.79-42.064 0-24.219 17.049-41.745 40.152-41.745s40.312 17.526 40.312 41.745c0 1.274-.16 3.186-.319 4.78H707.89c1.752 13.065 10.994 22.147 23.103 22.147 8.604 0 16.571-4.621 20.554-11.472l14.34 9.241c-7.17 10.357-21.032 17.368-35.531 17.368m-21.67-50.827h42.224c-2.868-10.676-10.994-17.846-21.192-17.846-10.197 0-18.164 7.17-21.032 17.846m-54.387 50.558v-81.451h18.477v85.818c0 11.254-2.183 19.485-6.719 24.356-5.207 5.879-12.43 8.735-21.668 8.735l-5.04-16.629q8.316 0 11.591-4.536c2.183-3.191 3.359-8.566 3.359-16.293m20.811-111.867c0 6.411-5.197 11.608-11.609 11.608-6.411 0-11.608-5.197-11.608-11.608s5.197-11.609 11.608-11.609 11.609 5.197 11.609 11.609m117.102-.005h18.482v30.403h16.093v15.137h-16.093v64.529h-18.482V221.21h-13.013v-15.137h13.013z"/><path fill="#fff" fill-rule="evenodd" d="m398.096 285.739 14.944-30.251h56.808l14.761 30.251h19.276L441.442 158 379 285.739zm22.686-46.072 20.66-42.1 20.661 42.1z" clip-rule="evenodd"/><path fill="#fff" d="M636.593 233.276c-5.783-7.374-12.766-13.353-25.807-13.353-13.544 0-23.422 10.846-23.422 25.983 0 14.977 9.878 25.971 23.422 25.971 13.041 0 20.024-6.117 25.807-13.475v20.959c-6.947 5.322-15.795 8.45-25.807 8.45-24.06 0-41.427-17.686-41.427-41.905 0-24.378 17.367-41.904 41.427-41.904 9.978 0 18.872 3.034 25.807 8.324zm-103.542 52.463h-18.436v-79.627h18.436v7.629c3.552-3.979 6.508-6.305 8.909-7.437 2.842-1.339 5.983-2.203 9.67-2.203s9.914 1.508 15.11 4.693l-8.38 16.76c-3.52-2.514-6.872-3.687-10.224-3.687q-15.085 0-15.085 22.626zM284.06 229.715c11.824 3.022 21.598 5.444 41.94 9.955-20.342 4.511-30.116 6.933-41.94 9.955a11.24 11.24 0 0 0-8.106 8.105c-3.021 11.821-5.443 21.599-9.954 41.94-4.511-20.341-6.933-30.119-9.954-41.94a11.24 11.24 0 0 0-8.106-8.105c-11.824-3.022-21.598-5.444-41.94-9.955 20.342-4.511 30.116-6.933 41.94-9.955a11.24 11.24 0 0 0 8.106-8.105c3.021-11.821 5.443-21.599 9.954-41.94 4.511 20.341 6.933 30.119 9.954 41.94a11.24 11.24 0 0 0 8.106 8.105"/><path fill="#fff" fill-rule="evenodd" d="m174.651 236.37 3.349 3.3-3.349 3.3c-30.835-2.232-59.886-2.548-106.692-3.056-6.912-.075-14.21-.154-21.959-.244 7.632-.089 14.828-.167 21.646-.241 46.99-.51 76.101-.827 107.005-3.059m629.8-152.12c78.172 0 150.861 66.099 150.861 145.75 0 79.642-72.672 145.735-150.834 145.75h-.054q-.588 0-1.177-.005c79.921-.67 144.503-65.666 144.503-145.745S883.168 84.925 803.247 84.255q.603-.005 1.204-.005" clip-rule="evenodd"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1040" height="460" fill="none" viewBox="0 0 1040 460"><path fill="#000" d="M730.356 287.811c-23.741 0-40.79-17.686-40.79-42.064 0-24.219 17.049-41.745 40.152-41.745s40.312 17.526 40.312 41.745c0 1.274-.16 3.186-.319 4.78H707.89c1.752 13.065 10.994 22.147 23.103 22.147 8.604 0 16.571-4.621 20.554-11.472l14.34 9.241c-7.17 10.357-21.032 17.368-35.531 17.368m-21.67-50.827h42.224c-2.868-10.676-10.994-17.846-21.192-17.846-10.197 0-18.164 7.17-21.032 17.846m-54.387 50.558v-81.451h18.477v85.818c0 11.254-2.183 19.485-6.719 24.356-5.207 5.879-12.43 8.735-21.668 8.735l-5.04-16.629q8.316 0 11.591-4.536c2.183-3.191 3.359-8.566 3.359-16.293m20.811-111.867c0 6.411-5.197 11.608-11.609 11.608-6.411 0-11.608-5.197-11.608-11.608s5.197-11.609 11.608-11.609 11.609 5.197 11.609 11.609m117.102-.005h18.482v30.403h16.093v15.137h-16.093v64.529h-18.482V221.21h-13.013v-15.137h13.013z"/><path fill="#000" fill-rule="evenodd" d="m398.096 285.739 14.944-30.251h56.808l14.761 30.251h19.276L441.442 158 379 285.739zm22.686-46.072 20.66-42.1 20.661 42.1z" clip-rule="evenodd"/><path fill="#000" d="M636.593 233.276c-5.783-7.374-12.766-13.353-25.807-13.353-13.544 0-23.422 10.846-23.422 25.983 0 14.977 9.878 25.971 23.422 25.971 13.041 0 20.024-6.117 25.807-13.475v20.959c-6.947 5.322-15.795 8.45-25.807 8.45-24.06 0-41.427-17.686-41.427-41.905 0-24.378 17.367-41.904 41.427-41.904 9.978 0 18.872 3.034 25.807 8.324zm-103.542 52.463h-18.436v-79.627h18.436v7.629c3.552-3.979 6.508-6.305 8.909-7.437 2.842-1.339 5.983-2.203 9.67-2.203s9.914 1.508 15.11 4.693l-8.38 16.76c-3.52-2.514-6.872-3.687-10.224-3.687q-15.085 0-15.085 22.626zm-247.787-56.687c12.613 3.223 23.038 5.806 44.736 10.618-21.698 4.812-32.123 7.395-44.736 10.618a11.99 11.99 0 0 0-8.646 8.646c-3.223 12.609-5.807 23.039-10.618 44.736-4.811-21.697-7.395-32.127-10.618-44.736a11.99 11.99 0 0 0-8.646-8.646c-12.613-3.223-23.038-5.806-44.736-10.618 21.698-4.812 32.123-7.395 44.736-10.618a11.99 11.99 0 0 0 8.646-8.646c3.223-12.609 5.807-23.039 10.618-44.736 4.811 21.697 7.395 32.127 10.618 44.736a11.99 11.99 0 0 0 8.646 8.646"/><path fill="#000" fill-rule="evenodd" d="m174.499 236.17 3.501 3.501-3.501 3.501c-32.237-2.368-62.608-2.703-111.542-3.242-7.226-.08-14.856-.164-22.957-.259 7.979-.094 15.502-.177 22.63-.256 49.126-.541 79.56-.877 111.869-3.245M956 230c0 79.642-72.998 145.735-151.51 145.75h-.054q-.593 0-1.185-.005C883.17 375.073 947.75 310.078 947.75 230S883.17 84.927 803.25 84.255q.586-.005 1.172-.005h.082C883.011 84.273 956 150.363 956 230" clip-rule="evenodd"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 4 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
|
@ -1,23 +0,0 @@
|
|||
<svg width="160" height="44" viewBox="0 0 160 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2_23)">
|
||||
<g clip-path="url(#clip1_2_23)">
|
||||
<path d="M137.239 41.0207L132.798 35.2884H131.692V42.9725H132.814V37.0839L137.38 42.9725H138.361V35.2884H137.239V41.0207Z" fill="#1F1633"/>
|
||||
<path d="M126.058 39.5951H130.037V38.5967H126.056V36.284H130.546V35.2854H124.912V42.9725H130.603V41.9741H126.056L126.058 39.5951Z" fill="#1F1633"/>
|
||||
<path d="M121.378 38.6208C119.829 38.2478 119.396 37.9531 119.396 37.2374C119.396 36.5937 119.964 36.1577 120.812 36.1577C121.585 36.1802 122.33 36.4489 122.939 36.9245L123.541 36.0734C122.77 35.4691 121.813 35.1503 120.833 35.1711C119.312 35.1711 118.25 36.0734 118.25 37.3576C118.25 38.741 119.152 39.2192 120.795 39.6192C122.257 39.9561 122.704 40.2688 122.704 40.9696C122.704 41.6703 122.103 42.1034 121.173 42.1034C120.248 42.0992 119.358 41.7496 118.678 41.1229L118.001 41.932C118.873 42.6812 119.985 43.0921 121.135 43.0899C122.783 43.0899 123.842 42.2027 123.842 40.8313C123.833 39.6704 123.147 39.0478 121.378 38.6208Z" fill="#1F1633"/>
|
||||
<path d="M158.707 35.2884L156.394 38.8974L154.097 35.2884H152.755L155.789 39.935V42.9756H156.945V39.8989L160 35.2884H158.707Z" fill="#1F1633"/>
|
||||
<path d="M139.268 36.3291H141.786V42.9756H142.941V36.3291H145.458V35.2884H139.272L139.268 36.3291Z" fill="#1F1633"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M150.8 39.9742C151.962 39.6523 152.605 38.8403 152.605 37.6794C152.605 36.2028 151.524 35.2734 149.783 35.2734H146.366V42.9695H147.51V40.2087H149.45L151.399 42.9756H152.734L150.629 40.0223L150.8 39.9742ZM147.507 39.2223V36.305H149.663C150.788 36.305 151.431 36.8373 151.431 37.7606C151.431 38.6839 150.743 39.2223 149.675 39.2223H147.507Z" fill="#1F1633"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.252 42.9497C101.171 42.9497 101.109 42.9279 101.066 42.8839C101.022 42.8328 101 42.7707 101 42.6977V35.5401C101 35.4597 101.022 35.3976 101.066 35.3538C101.109 35.3027 101.171 35.2771 101.252 35.2771H104.259C104.808 35.2771 105.253 35.3683 105.597 35.5511C105.948 35.7264 106.204 35.9676 106.365 36.2745C106.533 36.5814 106.618 36.9249 106.618 37.3049C106.618 37.6044 106.566 37.8639 106.464 38.083C106.369 38.295 106.249 38.4704 106.102 38.6092C105.956 38.7408 105.814 38.8431 105.674 38.9161C105.96 39.0549 106.212 39.2815 106.431 39.5956C106.657 39.9098 106.771 40.2898 106.771 40.7356C106.771 41.1375 106.68 41.5066 106.497 41.8426C106.315 42.1789 106.04 42.4491 105.674 42.6538C105.315 42.8511 104.87 42.9497 104.336 42.9497H101.252ZM102.076 42.0619H104.193C104.668 42.0619 105.034 41.934 105.29 41.6782C105.546 41.4225 105.674 41.1083 105.674 40.7356C105.674 40.3483 105.546 40.0305 105.29 39.7819C105.034 39.5263 104.668 39.3984 104.193 39.3984H102.076V42.0619ZM102.076 38.5215H104.094C104.562 38.5215 104.917 38.4155 105.158 38.2036C105.4 37.9844 105.521 37.6885 105.521 37.3158C105.521 36.9431 105.4 36.6582 105.158 36.4608C104.917 36.2636 104.562 36.1649 104.094 36.1649H102.076V38.5215Z" fill="#1F1633"/>
|
||||
<path d="M110.221 42.8839C110.273 42.9279 110.335 42.9497 110.408 42.9497H110.979C111.06 42.9497 111.122 42.9279 111.165 42.8839C111.217 42.8328 111.242 42.7707 111.242 42.6977V40.1109L113.876 35.6387C113.89 35.6169 113.901 35.595 113.907 35.573C113.915 35.5511 113.919 35.5255 113.919 35.4962C113.919 35.4378 113.897 35.3867 113.853 35.3428C113.81 35.299 113.755 35.2771 113.689 35.2771H113.13C113.064 35.2771 113.004 35.2953 112.953 35.3318C112.909 35.3611 112.87 35.405 112.833 35.4634L110.693 39.0147L108.553 35.4634C108.532 35.405 108.495 35.3611 108.444 35.3318C108.392 35.2953 108.334 35.2771 108.269 35.2771H107.698C107.64 35.2771 107.588 35.299 107.545 35.3428C107.5 35.3867 107.479 35.4378 107.479 35.4962C107.479 35.5255 107.483 35.5511 107.489 35.573C107.493 35.5838 107.497 35.5946 107.5 35.6053C107.504 35.6165 107.508 35.6276 107.512 35.6387L110.155 40.1109V42.6977C110.155 42.7707 110.178 42.8328 110.221 42.8839Z" fill="#1F1633"/>
|
||||
<path d="M51.0821 8.70567C53.3791 8.70567 55.2164 9.46902 56.7476 11.0467L56.8497 11.1484L58.8913 9.16369L58.8403 9.0619C57.0026 7.02624 54.1956 5.90663 50.9803 5.90663C44.6515 5.90663 40.0068 10.2833 40.0068 16.3393C40.0068 22.3954 44.6004 26.7721 50.9292 26.7721C54.1445 26.7721 56.9519 25.6525 58.8403 23.5659L58.8913 23.4641L56.8497 21.4794L56.7476 21.5812C55.2164 23.1588 53.328 23.973 51.0821 23.973C46.4888 23.973 43.1714 20.7669 43.1714 16.3393C43.1714 11.9118 46.5399 8.70567 51.0821 8.70567ZM67.6189 24.1257C64.6076 24.1257 62.5149 21.9374 62.5149 18.8839C62.5149 15.7795 64.6076 13.6421 67.6189 13.6421C70.6302 13.6421 72.7226 15.7795 72.7226 18.8839C72.7226 21.9883 70.6302 24.1257 67.6189 24.1257ZM67.6189 10.9958C62.8721 10.9958 59.4525 14.3037 59.4525 18.8839C59.4525 23.4641 62.8721 26.7721 67.6189 26.7721C72.3654 26.7721 75.785 23.4641 75.785 18.8839C75.785 14.3037 72.3144 10.9958 67.6189 10.9958ZM84.9722 24.1257C81.9609 24.1257 79.8171 21.9374 79.8171 18.833C79.8171 15.7287 81.9098 13.5403 84.9722 13.5403C87.9833 13.5403 90.0759 15.7287 90.0759 18.833C90.0759 21.9374 87.9833 24.1257 84.9722 24.1257ZM90.0759 13.1332C88.8001 11.6573 86.9625 10.894 84.7679 10.894C80.0722 10.894 76.8058 14.151 76.8058 18.833C76.8058 23.515 80.0722 26.7721 84.7679 26.7721C87.0135 26.7721 88.953 25.9069 90.1781 24.3802V26.5685H93.0873V4.88882H90.0249L90.0759 13.1332ZM102.887 13.5403C105.388 13.5403 107.276 15.2197 107.633 17.6625H98.1913C98.4974 15.2197 100.335 13.5403 102.887 13.5403ZM102.887 10.9958C98.3952 10.9958 95.1799 14.3037 95.1799 18.8839C95.1799 23.515 98.5485 26.7721 103.448 26.7721C106 26.7721 108.093 25.9069 109.522 24.2784L109.573 24.2275L107.889 22.2936L107.786 22.3954C106.715 23.5659 105.286 24.1766 103.499 24.1766C100.641 24.1766 98.5485 22.5481 98.1402 19.9526H110.39V19.8509C110.441 19.5964 110.441 19.2401 110.441 18.9857C110.492 14.2528 107.327 10.9958 102.887 10.9958ZM124.425 21.9883C123.506 23.3623 122.077 24.1257 120.291 24.1257C117.178 24.1257 115.085 21.9883 115.085 18.8839C115.085 15.7795 117.229 13.6421 120.291 13.6421C122.077 13.6421 123.506 14.3546 124.425 15.7795L124.476 15.8813L126.824 14.4055L126.773 14.3037C125.548 12.2172 123.2 10.9958 120.342 10.9958C115.544 10.9958 112.023 14.3037 112.023 18.8839C112.023 23.4641 115.544 26.7721 120.342 26.7721C123.2 26.7721 125.548 25.5507 126.773 23.4132L126.824 23.3114L124.476 21.8865L124.425 21.9883ZM136.011 24.1257C133 24.1257 130.907 21.9374 130.907 18.8839C130.907 15.7795 133 13.6421 136.011 13.6421C139.022 13.6421 141.115 15.7795 141.115 18.8839C141.115 21.9883 139.022 24.1257 136.011 24.1257ZM136.011 10.9958C131.264 10.9958 127.845 14.3037 127.845 18.8839C127.845 23.4641 131.264 26.7721 136.011 26.7721C140.758 26.7721 144.177 23.4641 144.177 18.8839C144.177 14.3037 140.707 10.9958 136.011 10.9958ZM157.039 10.9958L151.833 22.9043L146.729 10.9958H143.616L150.302 26.2123L150.353 26.2631H153.313L159.999 10.9958H157.039ZM16.2354 0.910034C7.28788 0.910034 0 8.1049 0 16.8826V16.9546L2.74197 18.5374H2.81413C4.5459 17.3863 6.63848 16.9546 8.73103 17.3143C10.1742 17.6021 11.5451 18.2496 12.6275 19.2569L13.1326 19.6886L13.4213 19.113C13.7099 18.5374 13.9985 18.0338 14.2871 17.5302C14.4314 17.3143 14.5758 17.1704 14.7201 16.9546L15.0087 16.5948L14.6479 16.307C13.1326 15.0839 11.3287 14.2205 9.38043 13.8608C7.50436 13.501 5.62825 13.573 3.8965 14.1486C5.19533 8.46464 10.2463 4.4355 16.2354 4.4355C19.6268 4.4355 22.8017 5.73059 25.1829 8.1049C26.9147 9.75972 28.0692 11.8462 28.5743 14.1486C27.4919 13.7888 26.3374 13.6449 25.1829 13.6449H24.9664C24.5335 13.6449 24.1005 13.7169 23.5954 13.7169H23.5233C23.379 13.7169 23.1625 13.7888 23.0182 13.7888C22.7295 13.8608 22.513 13.8608 22.2244 13.9327L22.008 14.0047C21.7915 14.0766 21.575 14.1486 21.3585 14.2205H21.2864C20.7813 14.3644 20.3483 14.5803 19.8433 14.7961C19.6268 14.8681 19.4103 15.012 19.1938 15.1559H19.1217C18.0393 15.8034 17.0291 16.5948 16.2354 17.6021L16.1632 17.746C15.9468 18.0338 15.8024 18.2496 15.6581 18.3935C15.5138 18.5374 15.4416 18.7533 15.2973 18.9691L15.2252 19.113C15.0809 19.3289 15.0087 19.5447 14.9366 19.6886V19.7606C14.7201 20.1922 14.5036 20.6959 14.3593 21.1995V21.2715C13.9985 22.4227 13.782 23.6458 13.782 24.9408V25.0848C13.782 25.2286 13.782 25.4445 13.782 25.5884C13.782 25.6603 13.782 25.7323 13.782 25.8042C13.782 25.8762 13.782 26.0201 13.782 26.092V26.164V26.3079C13.782 26.4518 13.8542 26.6676 13.8542 26.8115C14.215 28.5383 15.0087 30.193 16.2354 31.7041L16.3075 31.776L16.3797 31.7041C16.8848 31.1286 18.0393 29.3297 18.1836 28.2505C17.6064 27.1713 17.3177 25.9481 17.3177 24.797C17.3177 20.7678 20.4927 17.3863 24.6056 17.1704H24.8942C26.5539 17.0985 28.2135 17.6021 29.5845 18.5374H29.6566L32.3986 16.9546V16.8826C32.3986 12.6377 30.7391 8.60852 27.6362 5.58671C24.6056 2.56486 20.5648 0.910034 16.2354 0.910034Z" fill="#F01F7A"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2_23">
|
||||
<rect width="160" height="44" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_2_23">
|
||||
<rect width="160" height="44" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 8.8 KiB |
|
|
@ -1,28 +0,0 @@
|
|||
<svg width="160" height="44" viewBox="0 0 160 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_943_358)">
|
||||
<mask id="mask0_943_358" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="160" height="44">
|
||||
<path d="M160 0H0V44H160V0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_943_358)">
|
||||
<mask id="mask1_943_358" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="160" height="44">
|
||||
<path d="M160 0H0V44H160V0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_943_358)">
|
||||
<path d="M137.239 41.0209L132.798 35.2886H131.692V42.9727H132.814V37.0841L137.38 42.9727H138.361V35.2886H137.239V41.0209Z" fill="white"/>
|
||||
<path d="M126.058 39.5953H130.037V38.5969H126.056V36.2842H130.546V35.2856H124.912V42.9727H130.603V41.9743H126.056L126.058 39.5953Z" fill="white"/>
|
||||
<path d="M121.378 38.6211C119.829 38.2481 119.396 37.9534 119.396 37.2377C119.396 36.594 119.964 36.158 120.812 36.158C121.585 36.1805 122.33 36.4492 122.939 36.9248L123.541 36.0737C122.77 35.4694 121.813 35.1506 120.833 35.1714C119.312 35.1714 118.25 36.0737 118.25 37.3579C118.25 38.7413 119.152 39.2195 120.795 39.6195C122.257 39.9564 122.704 40.2691 122.704 40.9699C122.704 41.6706 122.103 42.1037 121.173 42.1037C120.248 42.0995 119.358 41.7499 118.678 41.1232L118.001 41.9323C118.873 42.6815 119.985 43.0924 121.135 43.0902C122.783 43.0902 123.842 42.203 123.842 40.8316C123.833 39.6707 123.147 39.0481 121.378 38.6211Z" fill="white"/>
|
||||
<path d="M158.707 35.2886L156.394 38.8976L154.097 35.2886H152.755L155.789 39.9352V42.9758H156.945V39.8991L160 35.2886H158.707Z" fill="white"/>
|
||||
<path d="M139.268 36.3293H141.786V42.9758H142.941V36.3293H145.458V35.2886H139.272L139.268 36.3293Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M150.8 39.9742C151.962 39.6523 152.605 38.8403 152.605 37.6794C152.605 36.2028 151.524 35.2734 149.783 35.2734H146.366V42.9695H147.51V40.2087H149.45L151.399 42.9756H152.734L150.629 40.0223L150.8 39.9742ZM147.507 39.2223V36.305H149.663C150.788 36.305 151.431 36.8373 151.431 37.7606C151.431 38.6839 150.743 39.2223 149.675 39.2223H147.507Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.252 42.9499C101.171 42.9499 101.109 42.9281 101.066 42.8841C101.022 42.833 101 42.7709 101 42.6979V35.5403C101 35.4599 101.022 35.3978 101.066 35.354C101.109 35.3029 101.171 35.2773 101.252 35.2773H104.259C104.808 35.2773 105.253 35.3685 105.597 35.5513C105.948 35.7266 106.204 35.9678 106.365 36.2747C106.533 36.5816 106.618 36.9251 106.618 37.3051C106.618 37.6046 106.566 37.8641 106.464 38.0832C106.369 38.2952 106.249 38.4706 106.102 38.6094C105.956 38.741 105.814 38.8433 105.674 38.9163C105.96 39.0551 106.212 39.2817 106.431 39.5958C106.657 39.91 106.771 40.29 106.771 40.7358C106.771 41.1377 106.68 41.5068 106.497 41.8428C106.315 42.1791 106.04 42.4493 105.674 42.654C105.315 42.8513 104.87 42.9499 104.336 42.9499H101.252ZM102.076 42.0621H104.193C104.668 42.0621 105.034 41.9342 105.29 41.6784C105.546 41.4227 105.674 41.1085 105.674 40.7358C105.674 40.3485 105.546 40.0307 105.29 39.7821C105.034 39.5265 104.668 39.3986 104.193 39.3986H102.076V42.0621ZM102.076 38.5217H104.094C104.562 38.5217 104.917 38.4157 105.158 38.2038C105.4 37.9846 105.521 37.6887 105.521 37.316C105.521 36.9433 105.4 36.6584 105.158 36.461C104.917 36.2638 104.562 36.1651 104.094 36.1651H102.076V38.5217Z" fill="white"/>
|
||||
<path d="M110.221 42.8841C110.273 42.9281 110.335 42.9499 110.408 42.9499H110.979C111.06 42.9499 111.122 42.9281 111.165 42.8841C111.217 42.833 111.242 42.7709 111.242 42.6979V40.1111L113.876 35.6389C113.89 35.6171 113.901 35.5952 113.907 35.5732C113.915 35.5513 113.919 35.5257 113.919 35.4964C113.919 35.438 113.897 35.3869 113.853 35.343C113.81 35.2992 113.755 35.2773 113.689 35.2773H113.13C113.064 35.2773 113.004 35.2955 112.953 35.332C112.909 35.3613 112.87 35.4052 112.833 35.4636L110.693 39.0149L108.553 35.4636C108.532 35.4052 108.495 35.3613 108.444 35.332C108.392 35.2955 108.334 35.2773 108.269 35.2773H107.698C107.64 35.2773 107.588 35.2992 107.545 35.343C107.5 35.3869 107.479 35.438 107.479 35.4964C107.479 35.5257 107.483 35.5513 107.489 35.5732C107.493 35.584 107.497 35.5948 107.5 35.6055C107.504 35.6167 107.508 35.6278 107.512 35.6389L110.155 40.1111V42.6979C110.155 42.7709 110.178 42.833 110.221 42.8841Z" fill="white"/>
|
||||
<path d="M51.0821 8.70579C53.3791 8.70579 55.2164 9.46914 56.7476 11.0468L56.8497 11.1485L58.8913 9.16381L58.8403 9.06202C57.0026 7.02636 54.1956 5.90675 50.9803 5.90675C44.6515 5.90675 40.0068 10.2834 40.0068 16.3394C40.0068 22.3955 44.6004 26.7722 50.9292 26.7722C54.1445 26.7722 56.9519 25.6526 58.8403 23.566L58.8913 23.4642L56.8497 21.4795L56.7476 21.5813C55.2164 23.1589 53.328 23.9731 51.0821 23.9731C46.4888 23.9731 43.1714 20.767 43.1714 16.3394C43.1714 11.9119 46.5399 8.70579 51.0821 8.70579ZM67.6189 24.1258C64.6076 24.1258 62.5149 21.9375 62.5149 18.884C62.5149 15.7796 64.6076 13.6422 67.6189 13.6422C70.6302 13.6422 72.7226 15.7796 72.7226 18.884C72.7226 21.9884 70.6302 24.1258 67.6189 24.1258ZM67.6189 10.9959C62.8721 10.9959 59.4525 14.3038 59.4525 18.884C59.4525 23.4642 62.8721 26.7722 67.6189 26.7722C72.3654 26.7722 75.785 23.4642 75.785 18.884C75.785 14.3038 72.3144 10.9959 67.6189 10.9959ZM84.9722 24.1258C81.9609 24.1258 79.8171 21.9375 79.8171 18.8331C79.8171 15.7288 81.9098 13.5404 84.9722 13.5404C87.9833 13.5404 90.0759 15.7288 90.0759 18.8331C90.0759 21.9375 87.9833 24.1258 84.9722 24.1258ZM90.0759 13.1333C88.8001 11.6574 86.9625 10.8941 84.7679 10.8941C80.0722 10.8941 76.8058 14.1511 76.8058 18.8331C76.8058 23.5151 80.0722 26.7722 84.7679 26.7722C87.0135 26.7722 88.953 25.907 90.1781 24.3803V26.5686H93.0873V4.88894H90.0249L90.0759 13.1333ZM102.887 13.5404C105.388 13.5404 107.276 15.2198 107.633 17.6626H98.1913C98.4974 15.2198 100.335 13.5404 102.887 13.5404ZM102.887 10.9959C98.3952 10.9959 95.1799 14.3038 95.1799 18.884C95.1799 23.5151 98.5485 26.7722 103.448 26.7722C106 26.7722 108.093 25.907 109.522 24.2785L109.573 24.2276L107.889 22.2937L107.786 22.3955C106.715 23.566 105.286 24.1767 103.499 24.1767C100.641 24.1767 98.5485 22.5482 98.1402 19.9527H110.39V19.851C110.441 19.5965 110.441 19.2402 110.441 18.9858C110.492 14.2529 107.327 10.9959 102.887 10.9959ZM124.425 21.9884C123.506 23.3624 122.077 24.1258 120.291 24.1258C117.178 24.1258 115.085 21.9884 115.085 18.884C115.085 15.7796 117.229 13.6422 120.291 13.6422C122.077 13.6422 123.506 14.3547 124.425 15.7796L124.476 15.8814L126.824 14.4056L126.773 14.3038C125.548 12.2173 123.2 10.9959 120.342 10.9959C115.544 10.9959 112.023 14.3038 112.023 18.884C112.023 23.4642 115.544 26.7722 120.342 26.7722C123.2 26.7722 125.548 25.5508 126.773 23.4133L126.824 23.3115L124.476 21.8866L124.425 21.9884ZM136.011 24.1258C133 24.1258 130.907 21.9375 130.907 18.884C130.907 15.7796 133 13.6422 136.011 13.6422C139.022 13.6422 141.115 15.7796 141.115 18.884C141.115 21.9884 139.022 24.1258 136.011 24.1258ZM136.011 10.9959C131.264 10.9959 127.845 14.3038 127.845 18.884C127.845 23.4642 131.264 26.7722 136.011 26.7722C140.758 26.7722 144.177 23.4642 144.177 18.884C144.177 14.3038 140.707 10.9959 136.011 10.9959ZM157.039 10.9959L151.833 22.9044L146.729 10.9959H143.616L150.302 26.2124L150.353 26.2632H153.313L159.999 10.9959H157.039ZM16.2354 0.910156C7.28788 0.910156 0 8.10502 0 16.8827V16.9547L2.74197 18.5375H2.81413C4.5459 17.3864 6.63848 16.9547 8.73103 17.3144C10.1742 17.6022 11.5451 18.2497 12.6275 19.257L13.1326 19.6887L13.4213 19.1131C13.7099 18.5375 13.9985 18.0339 14.2871 17.5303C14.4314 17.3144 14.5758 17.1705 14.7201 16.9547L15.0087 16.5949L14.6479 16.3071C13.1326 15.084 11.3287 14.2206 9.38043 13.8609C7.50436 13.5011 5.62825 13.5731 3.8965 14.1487C5.19533 8.46476 10.2463 4.43562 16.2354 4.43562C19.6268 4.43562 22.8017 5.73071 25.1829 8.10502C26.9147 9.75984 28.0692 11.8463 28.5743 14.1487C27.4919 13.7889 26.3374 13.645 25.1829 13.645H24.9664C24.5335 13.645 24.1005 13.717 23.5954 13.717H23.5233C23.379 13.717 23.1625 13.7889 23.0182 13.7889C22.7295 13.8609 22.513 13.8609 22.2244 13.9328L22.008 14.0048C21.7915 14.0767 21.575 14.1487 21.3585 14.2206H21.2864C20.7813 14.3645 20.3483 14.5804 19.8433 14.7962C19.6268 14.8682 19.4103 15.0121 19.1938 15.156H19.1217C18.0393 15.8035 17.0291 16.5949 16.2354 17.6022L16.1632 17.7461C15.9468 18.0339 15.8024 18.2497 15.6581 18.3936C15.5138 18.5375 15.4416 18.7534 15.2973 18.9692L15.2252 19.1131C15.0809 19.329 15.0087 19.5448 14.9366 19.6887V19.7607C14.7201 20.1923 14.5036 20.696 14.3593 21.1996V21.2716C13.9985 22.4228 13.782 23.6459 13.782 24.9409V25.0849C13.782 25.2287 13.782 25.4446 13.782 25.5885C13.782 25.6604 13.782 25.7324 13.782 25.8043C13.782 25.8763 13.782 26.0202 13.782 26.0921V26.1641V26.308C13.782 26.4519 13.8542 26.6677 13.8542 26.8116C14.215 28.5384 15.0087 30.1931 16.2354 31.7042L16.3075 31.7761L16.3797 31.7042C16.8848 31.1287 18.0393 29.3298 18.1836 28.2506C17.6064 27.1714 17.3177 25.9482 17.3177 24.7971C17.3177 20.7679 20.4927 17.3864 24.6056 17.1705H24.8942C26.5539 17.0986 28.2135 17.6022 29.5845 18.5375H29.6566L32.3986 16.9547V16.8827C32.3986 12.6378 30.7391 8.60864 27.6362 5.58683C24.6056 2.56498 20.5648 0.910156 16.2354 0.910156Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_943_358">
|
||||
<rect width="160" height="44" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.1 KiB |
|
|
@ -1,21 +0,0 @@
|
|||
<svg width="206" height="32" viewBox="0 0 206 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_10121_47824)">
|
||||
<path d="M51.6575 13.502C52.456 12.0936 53.569 10.992 54.9965 10.2052C56.424 9.41832 58.0451 9.0249 59.8517 9.0249C62.0696 9.0249 63.9649 9.5993 65.5457 10.7402C67.1265 11.8811 68.1749 13.4391 68.7072 15.4141H63.7068C63.3358 14.6508 62.8035 14.0764 62.1261 13.6751C61.4405 13.2739 60.6663 13.0771 59.7952 13.0771C58.3919 13.0771 57.2628 13.5493 56.3918 14.5013C55.5207 15.4534 55.0852 16.7281 55.0852 18.3175C55.0852 19.9069 55.5207 21.1816 56.3918 22.1337C57.2628 23.0858 58.3919 23.5579 59.7952 23.5579C60.6663 23.5579 61.4405 23.3612 62.1261 22.9599C62.8116 22.5586 63.3358 21.9842 63.7068 21.221H68.7072C68.1749 23.196 67.1184 24.746 65.5457 25.8791C63.9649 27.0121 62.0696 27.5787 59.8517 27.5787C58.0451 27.5787 56.424 27.1853 54.9965 26.3984C53.569 25.6116 52.456 24.5179 51.6575 23.1173C50.8591 21.7167 50.4639 20.1194 50.4639 18.3175C50.4639 16.5157 50.8591 14.9105 51.6575 13.502Z" fill="white"/>
|
||||
<path d="M73.8047 26.7526C72.6433 26.1467 71.732 25.2812 71.0626 24.156C70.4012 23.0308 70.0625 21.7168 70.0625 20.2139C70.0625 18.7111 70.4012 17.4128 71.0706 16.2797C71.7481 15.1467 72.6675 14.2811 73.837 13.6753C75.0064 13.0694 76.3211 12.7704 77.7728 12.7704C79.2245 12.7704 80.5391 13.0694 81.7086 13.6753C82.878 14.2811 83.7974 15.1467 84.4749 16.2797C85.1524 17.4128 85.4831 18.7268 85.4831 20.2139C85.4831 21.7011 85.1443 23.0151 84.4588 24.1481C83.7733 25.2812 82.8458 26.1467 81.6683 26.7526C80.4907 27.3585 79.1761 27.6575 77.7163 27.6575C76.2565 27.6575 74.958 27.3585 73.7967 26.7526H73.8047ZM79.9504 22.8892C80.5633 22.2676 80.8698 21.3785 80.8698 20.2139C80.8698 19.0494 80.5714 18.1681 79.9826 17.5387C79.3858 16.9171 78.6599 16.6023 77.7889 16.6023C76.9179 16.6023 76.1678 16.9092 75.5791 17.5229C74.9903 18.1367 74.7 19.0337 74.7 20.2061C74.7 21.3785 74.9903 22.2597 75.5629 22.8813C76.1356 23.5029 76.8614 23.8177 77.7325 23.8177C78.6035 23.8177 79.3455 23.5029 79.9504 22.8813V22.8892Z" fill="white"/>
|
||||
<path d="M87.3376 16.2639C87.9102 15.1387 88.7006 14.2732 89.6926 13.6673C90.6846 13.0615 91.7895 12.7625 93.0154 12.7625C93.9913 12.7625 94.8785 12.9592 95.685 13.3605C96.4915 13.7617 97.1287 14.2968 97.5884 14.9656V8.24597H102.137V27.445H97.5884V25.3677C97.1609 26.0601 96.556 26.6109 95.7657 27.0279C94.9753 27.445 94.0559 27.6495 93.0154 27.6495C91.7895 27.6495 90.6846 27.3427 89.6926 26.7289C88.7006 26.1152 87.9183 25.2418 87.3376 24.1087C86.765 22.9757 86.4746 21.6695 86.4746 20.1745C86.4746 18.6795 86.765 17.3812 87.3376 16.256V16.2639ZM96.6448 17.5858C96.0157 16.9485 95.2495 16.6259 94.3462 16.6259C93.4429 16.6259 92.6767 16.9406 92.0476 17.5701C91.4185 18.1995 91.104 19.0729 91.104 20.1745C91.104 21.2761 91.4185 22.1574 92.0476 22.8104C92.6767 23.4557 93.4429 23.7861 94.3462 23.7861C95.2495 23.7861 96.0157 23.4635 96.6448 22.8262C97.2738 22.1888 97.5884 21.3154 97.5884 20.206C97.5884 19.0965 97.2738 18.2231 96.6448 17.5858Z" fill="white"/>
|
||||
<path d="M118.816 21.276H108.525C108.597 22.173 108.896 22.8655 109.412 23.3376C109.936 23.8097 110.581 24.0536 111.34 24.0536C112.477 24.0536 113.267 23.5894 113.711 22.653H118.55C118.3 23.6051 117.856 24.4628 117.203 25.2181C116.558 25.9814 115.743 26.5794 114.767 27.0121C113.791 27.4449 112.703 27.6573 111.493 27.6573C110.041 27.6573 108.743 27.3583 107.613 26.7525C106.476 26.1466 105.589 25.2811 104.952 24.1559C104.315 23.0307 103.992 21.7167 103.992 20.2138C103.992 18.7109 104.307 17.3969 104.936 16.2717C105.565 15.1465 106.444 14.281 107.581 13.6751C108.718 13.0693 110.017 12.7703 111.493 12.7703C112.969 12.7703 114.203 13.0614 115.324 13.6515C116.437 14.2417 117.316 15.0757 117.945 16.1694C118.574 17.2553 118.889 18.53 118.889 19.9856C118.889 20.4026 118.864 20.8354 118.808 21.2839L118.816 21.276ZM114.243 18.8132C114.243 18.05 113.977 17.4441 113.445 16.9956C112.912 16.5471 112.251 16.3189 111.452 16.3189C110.654 16.3189 110.049 16.5314 109.525 16.9641C109.001 17.3969 108.678 18.0106 108.557 18.8054H114.251L114.243 18.8132Z" fill="white"/>
|
||||
<path d="M130.542 27.4527L126.663 20.5757H125.574V27.4527H121.025V9.2373H128.655C130.131 9.2373 131.381 9.48909 132.421 9.99267C133.462 10.4963 134.236 11.1808 134.752 12.0542C135.268 12.9276 135.527 13.9033 135.527 14.9734C135.527 16.1851 135.18 17.2631 134.478 18.2152C133.776 19.1673 132.744 19.844 131.381 20.2374L135.688 27.4527H130.55H130.542ZM125.566 17.4362H128.389C129.22 17.4362 129.849 17.2395 130.26 16.8382C130.679 16.4369 130.889 15.8783 130.889 15.1544C130.889 14.4305 130.679 13.919 130.26 13.5177C129.841 13.1164 129.22 12.9197 128.389 12.9197H125.566V17.4362Z" fill="white"/>
|
||||
<path d="M137.794 16.2639C138.366 15.1387 139.157 14.2732 140.149 13.6673C141.141 13.0614 142.254 12.849 143.472 12.849H152.593V27.445H148.044V25.3992C147.601 26.0758 146.988 26.6188 146.198 27.0358C145.407 27.4528 144.488 27.6574 143.447 27.6574C142.238 27.6574 141.141 27.3505 140.149 26.7368C139.157 26.1231 138.374 25.2497 137.794 24.1166C137.221 22.9835 136.931 21.6774 136.931 20.1824C136.931 18.6874 137.221 17.3891 137.794 16.2639ZM147.101 17.5858C146.472 16.9485 145.706 16.6258 144.802 16.6258C143.899 16.6258 143.133 16.9406 142.504 17.5701C141.875 18.1995 141.56 19.0729 141.56 20.1745C141.56 21.2761 141.875 22.1574 142.504 22.8104C143.133 23.4557 143.899 23.7861 144.802 23.7861C145.706 23.7861 146.472 23.4635 147.101 22.8262C147.73 22.1888 148.044 21.3154 148.044 20.206C148.044 19.0965 147.73 18.2231 147.101 17.5858Z" fill="white"/>
|
||||
<path d="M161.747 13.3841C162.545 12.967 163.457 12.7625 164.489 12.7625C165.715 12.7625 166.82 13.0615 167.812 13.6673C168.804 14.2732 169.586 15.1387 170.167 16.2639C170.74 17.3891 171.03 18.6953 171.03 20.1824C171.03 21.6695 170.74 22.9836 170.167 24.1166C169.594 25.2497 168.804 26.1231 167.812 26.7368C166.82 27.3505 165.715 27.6574 164.489 27.6574C163.441 27.6574 162.529 27.4528 161.747 27.0515C160.965 26.6424 160.352 26.1073 159.908 25.4306V27.4528H155.359V8.24597H159.908V15.0207C160.336 14.344 160.949 13.8011 161.747 13.3841ZM165.457 17.5779C164.828 16.9485 164.054 16.6337 163.126 16.6337C162.199 16.6337 161.457 16.9563 160.828 17.5937C160.198 18.231 159.884 19.1044 159.884 20.2139C159.884 21.3233 160.198 22.1967 160.828 22.8341C161.457 23.4714 162.223 23.794 163.126 23.794C164.029 23.794 164.804 23.4714 165.441 22.8183C166.078 22.1731 166.401 21.2918 166.401 20.1824C166.401 19.0729 166.086 18.2074 165.457 17.5779Z" fill="white"/>
|
||||
<path d="M179.272 13.3841C180.071 12.967 180.982 12.7625 182.015 12.7625C183.24 12.7625 184.345 13.0615 185.337 13.6673C186.329 14.2732 187.112 15.1387 187.692 16.2639C188.265 17.3891 188.555 18.6953 188.555 20.1824C188.555 21.6695 188.265 22.9836 187.692 24.1166C187.12 25.2497 186.329 26.1231 185.337 26.7368C184.345 27.3505 183.24 27.6574 182.015 27.6574C180.966 27.6574 180.055 27.4528 179.272 27.0515C178.49 26.6424 177.877 26.1073 177.434 25.4306V27.4528H172.885V8.24597H177.434V15.0207C177.861 14.344 178.474 13.8011 179.272 13.3841ZM182.982 17.5779C182.353 16.9485 181.579 16.6337 180.652 16.6337C179.724 16.6337 178.982 16.9563 178.353 17.5937C177.724 18.231 177.409 19.1044 177.409 20.2139C177.409 21.3233 177.724 22.1967 178.353 22.8341C178.982 23.4714 179.748 23.794 180.652 23.794C181.555 23.794 182.329 23.4714 182.966 22.8183C183.603 22.1731 183.926 21.2918 183.926 20.1824C183.926 19.0729 183.611 18.2074 182.982 17.5779Z" fill="white"/>
|
||||
<path d="M190.749 10.7796C190.241 10.3232 189.991 9.75667 189.991 9.07998C189.991 8.4033 190.241 7.81316 190.749 7.3568C191.257 6.90043 191.903 6.67224 192.701 6.67224C193.5 6.67224 194.121 6.90043 194.629 7.3568C195.137 7.81316 195.387 8.38756 195.387 9.07998C195.387 9.77241 195.137 10.3232 194.629 10.7796C194.121 11.2359 193.483 11.4641 192.701 11.4641C191.919 11.4641 191.249 11.2359 190.749 10.7796ZM194.967 12.9749V27.4528H190.419V12.967H194.967V12.9749Z" fill="white"/>
|
||||
<path d="M206 23.6838V27.4449H203.685C202.04 27.4449 200.75 27.0515 199.83 26.2646C198.911 25.4778 198.451 24.1952 198.451 22.4091V16.6494H196.645V12.967H198.451V9.44189H203V12.967H205.976V16.6494H203V22.4642C203 22.8969 203.105 23.2117 203.322 23.4005C203.532 23.5893 203.887 23.6838 204.387 23.6838H206.008H206Z" fill="white"/>
|
||||
<path d="M23.7918 31.6153C22.0336 31.0488 20.3318 27.1775 18.2188 27.1775C21.1787 28.9164 19.9205 31.2219 19.6785 31.6153H23.7998H23.7918Z" fill="white"/>
|
||||
<path d="M36.5996 17.5856C36.2286 8.37956 28.599 0.873075 19.1709 0.448179C8.66197 -0.0160589 0 8.15138 0 18.2938C0 22.1651 1.25816 25.7452 3.40349 28.6644C4.63746 30.3482 6.56503 31.4105 8.6539 31.5836C5.58108 28.1844 8.19419 25.3439 8.19419 25.3439C6.97635 25.9183 6.47631 27.8539 6.47631 27.8539C4.16968 26.4691 4.70198 24.8167 4.70198 24.8167C4.84715 24.4705 5.17782 23.5184 6.95216 23.8646C10.3879 12.8252 21.7033 18.5771 21.7033 18.5771C21.5259 18.2308 20.9371 16.2637 20.9371 16.2637C13.5898 15.2251 11.1057 10.1421 11.1057 10.1421C17.9208 8.11991 22.0662 13.6121 22.0662 13.6121C20.1709 9.10346 15.6141 10.8975 14.4285 3.50113C22.3405 4.78369 23.4857 10.8345 23.647 12.1407C23.7599 11.8889 24.389 11.5505 26.986 11.7C31.0105 11.9282 34.2124 15.9175 34.2124 15.9175C34.2124 24.0063 24.0825 22.0549 24.0825 28.1687C24.0825 29.6007 25.6794 29.5142 26.2117 30.2066C26.6472 30.7731 26.4618 31.4026 26.373 31.6072H27.3328C29.6072 31.6072 31.7686 30.5843 33.1074 28.7903C35.4383 25.6665 36.7609 21.7795 36.5996 17.5778V17.5856Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_10121_47824">
|
||||
<rect width="206" height="31.1826" fill="white" transform="translate(0 0.432495)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.4 KiB |
|
|
@ -1,21 +0,0 @@
|
|||
<svg width="206" height="33" viewBox="0 0 206 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_6619_4326)">
|
||||
<path d="M51.6575 13.8313C52.456 12.3876 53.569 11.2585 54.9965 10.452C56.424 9.64544 58.0451 9.24219 59.8517 9.24219C62.0696 9.24219 63.9649 9.83094 65.5457 11.0004C67.1265 12.1698 68.1749 13.7667 68.7072 15.7911H63.7068C63.3358 15.0088 62.8035 14.42 62.1261 14.0087C61.4405 13.5974 60.6663 13.3957 59.7952 13.3957C58.3919 13.3957 57.2628 13.8796 56.3917 14.8555C55.5207 15.8314 55.0852 17.138 55.0852 18.7671C55.0852 20.3963 55.5207 21.7028 56.3917 22.6787C57.2628 23.6546 58.3919 24.1385 59.7952 24.1385C60.6663 24.1385 61.4405 23.9369 62.1261 23.5256C62.8116 23.1142 63.3358 22.5255 63.7068 21.7432H68.7072C68.1749 23.7675 67.1184 25.3564 65.5457 26.5177C63.9649 27.6791 62.0696 28.2598 59.8517 28.2598C58.0451 28.2598 56.424 27.8565 54.9965 27.05C53.569 26.2435 52.456 25.1225 51.6575 23.6869C50.8591 22.2513 50.4639 20.614 50.4639 18.7671C50.4639 16.9202 50.8591 15.2749 51.6575 13.8313Z" fill="#171717"/>
|
||||
<path d="M73.8042 27.4131C72.6429 26.7921 71.7315 25.9049 71.0621 24.7516C70.4007 23.5983 70.062 22.2514 70.062 20.7109C70.062 19.1705 70.4007 17.8397 71.0702 16.6784C71.7476 15.517 72.6671 14.6298 73.8365 14.0088C75.0059 13.3878 76.3206 13.0813 77.7723 13.0813C79.224 13.0813 80.5386 13.3878 81.7081 14.0088C82.8775 14.6298 83.797 15.517 84.4744 16.6784C85.1519 17.8397 85.4826 19.1866 85.4826 20.7109C85.4826 22.2352 85.1438 23.5821 84.4583 24.7435C83.7728 25.9049 82.8453 26.7921 81.6678 27.4131C80.4902 28.0341 79.1756 28.3406 77.7158 28.3406C76.256 28.3406 74.9576 28.0341 73.7962 27.4131H73.8042ZM79.9499 23.4531C80.5628 22.8159 80.8693 21.9046 80.8693 20.7109C80.8693 19.5173 80.5709 18.614 79.9821 17.9688C79.3853 17.3316 78.6595 17.009 77.7884 17.009C76.9174 17.009 76.1673 17.3236 75.5786 17.9526C74.9898 18.5817 74.6995 19.5012 74.6995 20.7029C74.6995 21.9046 74.9898 22.8079 75.5624 23.445C76.1351 24.0822 76.8609 24.4048 77.732 24.4048C78.603 24.4048 79.345 24.0822 79.9499 23.445V23.4531Z" fill="#171717"/>
|
||||
<path d="M87.3376 16.6621C87.9102 15.5088 88.7006 14.6216 89.6926 14.0006C90.6846 13.3796 91.7895 13.0731 93.0154 13.0731C93.9913 13.0731 94.8785 13.2747 95.685 13.6861C96.4915 14.0974 97.1287 14.6458 97.5884 15.3314V8.44373H102.137V28.1227H97.5884V25.9935C97.1609 26.7032 96.556 27.2678 95.7657 27.6952C94.9753 28.1227 94.0558 28.3324 93.0154 28.3324C91.7895 28.3324 90.6846 28.0178 89.6926 27.3888C88.7006 26.7597 87.9183 25.8644 87.3376 24.7031C86.765 23.5417 86.4746 22.2029 86.4746 20.6705C86.4746 19.1381 86.765 17.8074 87.3376 16.654V16.6621ZM96.6448 18.0171C96.0157 17.3638 95.2495 17.0331 94.3462 17.0331C93.4429 17.0331 92.6767 17.3557 92.0476 18.0009C91.4185 18.6461 91.104 19.5414 91.104 20.6705C91.104 21.7996 91.4185 22.7029 92.0476 23.3723C92.6767 24.0337 93.4429 24.3724 94.3462 24.3724C95.2495 24.3724 96.0157 24.0417 96.6448 23.3884C97.2738 22.7352 97.5884 21.8399 97.5884 20.7028C97.5884 19.5656 97.2738 18.6703 96.6448 18.0171Z" fill="#171717"/>
|
||||
<path d="M118.816 21.7996H108.525C108.597 22.719 108.896 23.4288 109.412 23.9127C109.936 24.3966 110.581 24.6466 111.34 24.6466C112.477 24.6466 113.267 24.1708 113.711 23.211H118.55C118.3 24.1869 117.856 25.066 117.203 25.8402C116.558 26.6226 115.743 27.2355 114.767 27.6791C113.791 28.1227 112.703 28.3404 111.493 28.3404C110.041 28.3404 108.743 28.034 107.613 27.4129C106.476 26.7919 105.589 25.9048 104.952 24.7514C104.315 23.5981 103.992 22.2513 103.992 20.7108C103.992 19.1704 104.307 17.8235 104.936 16.6702C105.565 15.5169 106.444 14.6297 107.581 14.0087C108.718 13.3877 110.017 13.0812 111.493 13.0812C112.969 13.0812 114.203 13.3796 115.324 13.9845C116.437 14.5894 117.316 15.4443 117.945 16.5653C118.574 17.6783 118.889 18.9849 118.889 20.4769C118.889 20.9044 118.864 21.348 118.808 21.8077L118.816 21.7996ZM114.243 19.2752C114.243 18.4929 113.977 17.8719 113.445 17.4122C112.912 16.9524 112.251 16.7186 111.452 16.7186C110.654 16.7186 110.049 16.9363 109.525 17.3799C109.001 17.8235 108.678 18.4526 108.557 19.2671H114.251L114.243 19.2752Z" fill="#171717"/>
|
||||
<path d="M130.543 28.1308L126.663 21.0818H125.575V28.1308H121.026V9.45996H128.656C130.131 9.45996 131.382 9.71805 132.422 10.2342C133.462 10.7504 134.237 11.4521 134.753 12.3473C135.269 13.2425 135.527 14.2426 135.527 15.3395C135.527 16.5815 135.18 17.6864 134.479 18.6623C133.777 19.6382 132.745 20.3318 131.382 20.735L135.688 28.1308H130.551H130.543ZM125.567 17.8638H128.389C129.22 17.8638 129.849 17.6622 130.26 17.2509C130.68 16.8396 130.89 16.2669 130.89 15.525C130.89 14.783 130.68 14.2587 130.26 13.8474C129.841 13.4361 129.22 13.2345 128.389 13.2345H125.567V17.8638Z" fill="#171717"/>
|
||||
<path d="M137.793 16.6621C138.366 15.5088 139.156 14.6217 140.148 14.0006C141.14 13.3796 142.253 13.1619 143.471 13.1619H152.593V28.1227H148.044V26.0258C147.6 26.7194 146.987 27.2759 146.197 27.7033C145.407 28.1308 144.487 28.3405 143.447 28.3405C142.237 28.3405 141.14 28.0259 140.148 27.3969C139.156 26.7678 138.374 25.8725 137.793 24.7112C137.221 23.5498 136.93 22.211 136.93 20.6786C136.93 19.1462 137.221 17.8155 137.793 16.6621ZM147.1 18.0171C146.471 17.3638 145.705 17.0331 144.802 17.0331C143.898 17.0331 143.132 17.3557 142.503 18.001C141.874 18.6462 141.56 19.5414 141.56 20.6705C141.56 21.7996 141.874 22.7029 142.503 23.3723C143.132 24.0337 143.898 24.3724 144.802 24.3724C145.705 24.3724 146.471 24.0417 147.1 23.3885C147.729 22.7352 148.044 21.84 148.044 20.7028C148.044 19.5656 147.729 18.6704 147.1 18.0171Z" fill="#171717"/>
|
||||
<path d="M161.747 13.7103C162.545 13.2828 163.457 13.0731 164.489 13.0731C165.715 13.0731 166.82 13.3796 167.812 14.0006C168.804 14.6216 169.586 15.5088 170.167 16.6621C170.74 17.8154 171.03 19.1542 171.03 20.6786C171.03 22.2029 170.74 23.5497 170.167 24.7111C169.594 25.8725 168.804 26.7677 167.812 27.3968C166.82 28.0259 165.715 28.3404 164.489 28.3404C163.441 28.3404 162.529 28.1308 161.747 27.7194C160.965 27.3 160.352 26.7516 159.908 26.058V28.1308H155.359V8.44373H159.908V15.3878C160.336 14.6942 160.949 14.1377 161.747 13.7103ZM165.457 18.009C164.828 17.3638 164.054 17.0412 163.126 17.0412C162.199 17.0412 161.457 17.3718 160.828 18.0251C160.198 18.6784 159.884 19.5736 159.884 20.7108C159.884 21.848 160.198 22.7432 160.828 23.3965C161.457 24.0498 162.223 24.3805 163.126 24.3805C164.029 24.3805 164.804 24.0498 165.441 23.3804C166.078 22.719 166.401 21.8157 166.401 20.6786C166.401 19.5414 166.086 18.6542 165.457 18.009Z" fill="#171717"/>
|
||||
<path d="M179.272 13.7104C180.071 13.2829 180.982 13.0732 182.015 13.0732C183.24 13.0732 184.345 13.3797 185.337 14.0007C186.329 14.6218 187.112 15.5089 187.692 16.6622C188.265 17.8155 188.555 19.1544 188.555 20.6787C188.555 22.203 188.265 23.5499 187.692 24.7113C187.12 25.8726 186.329 26.7679 185.337 27.3969C184.345 28.026 183.24 28.3406 182.015 28.3406C180.966 28.3406 180.055 28.1309 179.272 27.7196C178.49 27.3002 177.877 26.7517 177.434 26.0581V28.1309H172.885V8.44385H177.434V15.3879C177.861 14.6943 178.474 14.1378 179.272 13.7104ZM182.982 18.0091C182.353 17.3639 181.579 17.0413 180.652 17.0413C179.724 17.0413 178.982 17.372 178.353 18.0252C177.724 18.6785 177.409 19.5738 177.409 20.7109C177.409 21.8481 177.724 22.7434 178.353 23.3966C178.982 24.0499 179.748 24.3806 180.652 24.3806C181.555 24.3806 182.329 24.0499 182.966 23.3805C183.603 22.7192 183.926 21.8159 183.926 20.6787C183.926 19.5415 183.611 18.6543 182.982 18.0091Z" fill="#171717"/>
|
||||
<path d="M190.749 11.0407C190.241 10.5729 189.991 9.99223 189.991 9.29862C189.991 8.60502 190.241 8.00014 190.749 7.53236C191.257 7.06458 191.902 6.83069 192.701 6.83069C193.499 6.83069 194.12 7.06458 194.628 7.53236C195.136 8.00014 195.386 8.58889 195.386 9.29862C195.386 10.0084 195.136 10.5729 194.628 11.0407C194.12 11.5085 193.483 11.7424 192.701 11.7424C191.918 11.7424 191.249 11.5085 190.749 11.0407ZM194.967 13.2909V28.1307H190.418V13.2828H194.967V13.2909Z" fill="#171717"/>
|
||||
<path d="M206.001 24.2676V28.1227H203.686C202.041 28.1227 200.75 27.7195 199.831 26.913C198.911 26.1065 198.452 24.7918 198.452 22.961V17.0574H196.645V13.2829H198.452V9.66968H203V13.2829H205.976V17.0574H203V23.0175C203 23.4611 203.105 23.7837 203.323 23.9773C203.533 24.1708 203.888 24.2676 204.388 24.2676H206.009H206.001Z" fill="#171717"/>
|
||||
<path d="M23.7923 32.3973C22.0341 31.8166 20.3323 27.8485 18.2192 27.8485C21.1791 29.6309 19.921 31.994 19.679 32.3973H23.8003H23.7923Z" fill="#FF570A"/>
|
||||
<path d="M36.5996 18.017C36.2286 8.58076 28.599 0.88661 19.1709 0.451092C8.66197 -0.024752 0 8.34687 0 18.7428C0 22.7109 1.25816 26.3805 3.40349 29.3727C4.63746 31.0987 6.56503 32.1875 8.6539 32.3649C5.58108 28.8807 8.19419 25.9692 8.19419 25.9692C6.97635 26.558 6.47631 28.542 6.47631 28.542C4.16968 27.1225 4.70198 25.4289 4.70198 25.4289C4.84715 25.074 5.17782 24.0981 6.95216 24.453C10.3879 13.1376 21.7033 19.0332 21.7033 19.0332C21.5259 18.6783 20.9371 16.662 20.9371 16.662C13.5898 15.5974 11.1057 10.3874 11.1057 10.3874C17.9208 8.31461 22.0662 13.9441 22.0662 13.9441C20.1709 9.32275 15.6141 11.1616 14.4285 3.58037C22.3405 4.89499 23.4857 11.0971 23.647 12.4359C23.7599 12.1778 24.389 11.831 26.986 11.9843C31.0105 12.2181 34.2124 16.3072 34.2124 16.3072C34.2124 24.5981 24.0825 22.598 24.0825 28.8646C24.0825 30.3325 25.6794 30.2438 26.2117 30.9535C26.6472 31.5342 26.4618 32.1794 26.373 32.3891H27.3328C29.6072 32.3891 31.7686 31.3406 33.1074 29.5018C35.4383 26.2999 36.7609 22.3157 36.5996 18.0089V18.017Z" fill="#FF570A"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_6619_4326">
|
||||
<rect width="206" height="31.9622" fill="white" transform="translate(0 0.434937)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 8 KiB |
|
Before Width: | Height: | Size: 374 KiB |
|
Before Width: | Height: | Size: 370 KiB |
|
Before Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
|
@ -1,7 +1,9 @@
|
|||
import { auth } from '@clerk/nextjs/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default async function CenteredLayout(props: { children: React.ReactNode }) {
|
||||
export default async function CenteredLayout(props: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const { userId } = await auth();
|
||||
|
||||
if (userId) {
|
||||
12
src/app/(auth)/(center)/sign-in/[[...sign-in]]/page.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { SignIn } from '@clerk/nextjs';
|
||||
import { getLocale, getTranslations } from 'next-intl/server';
|
||||
|
||||
export async function generateMetadata() {
|
||||
const locale = await getLocale();
|
||||
const t = await getTranslations({ locale, namespace: 'SignIn' });
|
||||
return { title: t('meta_title'), description: t('meta_description') };
|
||||
}
|
||||
|
||||
const SignInPage = () => <SignIn path="/sign-in" />;
|
||||
|
||||
export default SignInPage;
|
||||
12
src/app/(auth)/(center)/sign-up/[[...sign-up]]/page.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { SignUp } from '@clerk/nextjs';
|
||||
import { getLocale, getTranslations } from 'next-intl/server';
|
||||
|
||||
export async function generateMetadata() {
|
||||
const locale = await getLocale();
|
||||
const t = await getTranslations({ locale, namespace: 'SignUp' });
|
||||
return { title: t('meta_title'), description: t('meta_description') };
|
||||
}
|
||||
|
||||
const SignUpPage = () => <SignUp path="/sign-up" />;
|
||||
|
||||
export default SignUpPage;
|
||||
|
|
@ -1,18 +1,12 @@
|
|||
import { useTranslations } from 'next-intl';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { getLocale, getTranslations } from 'next-intl/server';
|
||||
|
||||
import { DashboardHeader } from '@/features/dashboard/DashboardHeader';
|
||||
|
||||
export async function generateMetadata(props: { params: { locale: string } }) {
|
||||
const t = await getTranslations({
|
||||
locale: props.params.locale,
|
||||
namespace: 'Dashboard',
|
||||
});
|
||||
|
||||
return {
|
||||
title: t('meta_title'),
|
||||
description: t('meta_description'),
|
||||
};
|
||||
export async function generateMetadata() {
|
||||
const locale = await getLocale();
|
||||
const t = await getTranslations({ locale, namespace: 'Dashboard' });
|
||||
return { title: t('meta_title'), description: t('meta_description') };
|
||||
}
|
||||
|
||||
export default function DashboardLayout(props: { children: React.ReactNode }) {
|
||||
|
|
@ -28,7 +22,6 @@ export default function DashboardLayout(props: { children: React.ReactNode }) {
|
|||
href: '/dashboard',
|
||||
label: t('home'),
|
||||
},
|
||||
// PRO: Link to the /dashboard/todos page
|
||||
{
|
||||
href: '/dashboard/organization-profile/organization-members',
|
||||
label: t('members'),
|
||||
|
|
@ -37,7 +30,6 @@ export default function DashboardLayout(props: { children: React.ReactNode }) {
|
|||
href: '/dashboard/organization-profile',
|
||||
label: t('settings'),
|
||||
},
|
||||
// PRO: Link to the /dashboard/billing page
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -2,9 +2,8 @@ import { OrganizationProfile } from '@clerk/nextjs';
|
|||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { TitleBar } from '@/features/dashboard/TitleBar';
|
||||
import { getI18nPath } from '@/utils/Helpers';
|
||||
|
||||
const OrganizationProfilePage = (props: { params: { locale: string } }) => {
|
||||
const OrganizationProfilePage = () => {
|
||||
const t = useTranslations('OrganizationProfile');
|
||||
|
||||
return (
|
||||
|
|
@ -16,10 +15,7 @@ const OrganizationProfilePage = (props: { params: { locale: string } }) => {
|
|||
|
||||
<OrganizationProfile
|
||||
routing="path"
|
||||
path={getI18nPath(
|
||||
'/dashboard/organization-profile',
|
||||
props.params.locale,
|
||||
)}
|
||||
path="/dashboard/organization-profile"
|
||||
afterLeaveOrganizationUrl="/onboarding/organization-selection"
|
||||
appearance={{
|
||||
elements: {
|
||||
|
|
@ -2,7 +2,6 @@ import { useTranslations } from 'next-intl';
|
|||
|
||||
import { MessageState } from '@/features/dashboard/MessageState';
|
||||
import { TitleBar } from '@/features/dashboard/TitleBar';
|
||||
import { SponsorLogos } from '@/features/sponsors/SponsorLogos';
|
||||
|
||||
const DashboardIndexPage = () => {
|
||||
const t = useTranslations('DashboardIndex');
|
||||
|
|
@ -15,7 +14,7 @@ const DashboardIndexPage = () => {
|
|||
/>
|
||||
|
||||
<MessageState
|
||||
icon={(
|
||||
icon={
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
|
|
@ -26,35 +25,29 @@ const DashboardIndexPage = () => {
|
|||
<path d="M0 0h24v24H0z" stroke="none" />
|
||||
<path d="M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3M12 12l8-4.5M12 12v9M12 12L4 7.5" />
|
||||
</svg>
|
||||
)}
|
||||
}
|
||||
title={t('message_state_title')}
|
||||
description={t.rich('message_state_description', {
|
||||
code: chunks => (
|
||||
code: (chunks) => (
|
||||
<code className="bg-secondary text-secondary-foreground">
|
||||
{chunks}
|
||||
</code>
|
||||
),
|
||||
})}
|
||||
button={(
|
||||
<>
|
||||
<div className="mt-2 text-xs font-light text-muted-foreground">
|
||||
{t.rich('message_state_alternative', {
|
||||
url: () => (
|
||||
<a
|
||||
className="text-blue-500 hover:text-blue-600"
|
||||
href="https://nextjs-boilerplate.com/pro-saas-starter-kit"
|
||||
>
|
||||
Next.js Boilerplate SaaS
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mt-7">
|
||||
<SponsorLogos />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
button={
|
||||
<div className="mt-2 text-xs font-light text-muted-foreground">
|
||||
{t.rich('message_state_alternative', {
|
||||
url: () => (
|
||||
<a
|
||||
className="text-blue-500 hover:text-blue-600"
|
||||
href="https://nextjs-boilerplate.com/pro-saas-starter-kit"
|
||||
>
|
||||
Next.js Boilerplate SaaS
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
@ -2,9 +2,8 @@ import { UserProfile } from '@clerk/nextjs';
|
|||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { TitleBar } from '@/features/dashboard/TitleBar';
|
||||
import { getI18nPath } from '@/utils/Helpers';
|
||||
|
||||
const UserProfilePage = (props: { params: { locale: string } }) => {
|
||||
const UserProfilePage = () => {
|
||||
const t = useTranslations('UserProfile');
|
||||
|
||||
return (
|
||||
|
|
@ -16,7 +15,7 @@ const UserProfilePage = (props: { params: { locale: string } }) => {
|
|||
|
||||
<UserProfile
|
||||
routing="path"
|
||||
path={getI18nPath('/dashboard/user-profile', props.params.locale)}
|
||||
path="/dashboard/user-profile"
|
||||
appearance={{
|
||||
elements: {
|
||||
rootBox: 'w-full',
|
||||
28
src/app/(auth)/layout.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
'use client';
|
||||
|
||||
import { enUS, frFR } from '@clerk/localizations';
|
||||
import { ClerkProvider } from '@clerk/nextjs';
|
||||
|
||||
export default function AuthLayout(props: {
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}) {
|
||||
let clerkLocale = enUS;
|
||||
|
||||
if (props.params.locale === 'fr') {
|
||||
clerkLocale = frFR;
|
||||
}
|
||||
|
||||
return (
|
||||
<ClerkProvider
|
||||
localization={clerkLocale}
|
||||
signInUrl="/sign-in"
|
||||
signUpUrl="/sign-up"
|
||||
signInFallbackRedirectUrl="/dashboard"
|
||||
signUpFallbackRedirectUrl="/dashboard"
|
||||
afterSignOutUrl="/"
|
||||
>
|
||||
{props.children}
|
||||
</ClerkProvider>
|
||||
);
|
||||
}
|
||||
32
src/app/(unauth)/page.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { getLocale, getTranslations, setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { Features } from '@/templates/Features';
|
||||
import { Footer } from '@/templates/Footer';
|
||||
import { Hero } from '@/templates/Hero';
|
||||
import { Navbar } from '@/templates/Navbar';
|
||||
|
||||
export async function generateMetadata() {
|
||||
const locale = await getLocale();
|
||||
const t = await getTranslations({ locale, namespace: 'Index' });
|
||||
|
||||
return {
|
||||
title: t('meta_title'),
|
||||
description: t('meta_description'),
|
||||
};
|
||||
}
|
||||
|
||||
const IndexPage = async () => {
|
||||
const locale = await getLocale();
|
||||
setRequestLocale(locale);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<Hero />
|
||||
<Features />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexPage;
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import { SignIn } from '@clerk/nextjs';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { getI18nPath } from '@/utils/Helpers';
|
||||
|
||||
export async function generateMetadata(props: { params: { locale: string } }) {
|
||||
const t = await getTranslations({
|
||||
locale: props.params.locale,
|
||||
namespace: 'SignIn',
|
||||
});
|
||||
|
||||
return {
|
||||
title: t('meta_title'),
|
||||
description: t('meta_description'),
|
||||
};
|
||||
}
|
||||
|
||||
const SignInPage = (props: { params: { locale: string } }) => (
|
||||
<SignIn path={getI18nPath('/sign-in', props.params.locale)} />
|
||||
);
|
||||
|
||||
export default SignInPage;
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import { SignUp } from '@clerk/nextjs';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { getI18nPath } from '@/utils/Helpers';
|
||||
|
||||
export async function generateMetadata(props: { params: { locale: string } }) {
|
||||
const t = await getTranslations({
|
||||
locale: props.params.locale,
|
||||
namespace: 'SignUp',
|
||||
});
|
||||
|
||||
return {
|
||||
title: t('meta_title'),
|
||||
description: t('meta_description'),
|
||||
};
|
||||
}
|
||||
|
||||
const SignUpPage = (props: { params: { locale: string } }) => (
|
||||
<SignUp path={getI18nPath('/sign-up', props.params.locale)} />
|
||||
);
|
||||
|
||||
export default SignUpPage;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { enUS, frFR } from '@clerk/localizations';
|
||||
import { ClerkProvider } from '@clerk/nextjs';
|
||||
|
||||
import { AppConfig } from '@/utils/AppConfig';
|
||||
|
||||
export default function AuthLayout(props: {
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}) {
|
||||
let clerkLocale = enUS;
|
||||
let signInUrl = '/sign-in';
|
||||
let signUpUrl = '/sign-up';
|
||||
let dashboardUrl = '/dashboard';
|
||||
let afterSignOutUrl = '/';
|
||||
|
||||
if (props.params.locale === 'fr') {
|
||||
clerkLocale = frFR;
|
||||
}
|
||||
|
||||
if (props.params.locale !== AppConfig.defaultLocale) {
|
||||
signInUrl = `/${props.params.locale}${signInUrl}`;
|
||||
signUpUrl = `/${props.params.locale}${signUpUrl}`;
|
||||
dashboardUrl = `/${props.params.locale}${dashboardUrl}`;
|
||||
afterSignOutUrl = `/${props.params.locale}${afterSignOutUrl}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<ClerkProvider
|
||||
// PRO: Dark mode support for Clerk
|
||||
localization={clerkLocale}
|
||||
signInUrl={signInUrl}
|
||||
signUpUrl={signUpUrl}
|
||||
signInFallbackRedirectUrl={dashboardUrl}
|
||||
signUpFallbackRedirectUrl={dashboardUrl}
|
||||
afterSignOutUrl={afterSignOutUrl}
|
||||
>
|
||||
{props.children}
|
||||
</ClerkProvider>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { CTA } from '@/templates/CTA';
|
||||
import { DemoBanner } from '@/templates/DemoBanner';
|
||||
import { FAQ } from '@/templates/FAQ';
|
||||
import { Features } from '@/templates/Features';
|
||||
import { Footer } from '@/templates/Footer';
|
||||
import { Hero } from '@/templates/Hero';
|
||||
import { Navbar } from '@/templates/Navbar';
|
||||
import { Pricing } from '@/templates/Pricing';
|
||||
import { Sponsors } from '@/templates/Sponsors';
|
||||
|
||||
export async function generateMetadata(props: { params: { locale: string } }) {
|
||||
const t = await getTranslations({
|
||||
locale: props.params.locale,
|
||||
namespace: 'Index',
|
||||
});
|
||||
|
||||
return {
|
||||
title: t('meta_title'),
|
||||
description: t('meta_description'),
|
||||
};
|
||||
}
|
||||
|
||||
const IndexPage = (props: { params: { locale: string } }) => {
|
||||
unstable_setRequestLocale(props.params.locale);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DemoBanner />
|
||||
<Navbar />
|
||||
<Hero />
|
||||
<Sponsors />
|
||||
<Features />
|
||||
<Pricing />
|
||||
<FAQ />
|
||||
<CTA />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexPage;
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
import '@/styles/global.css';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
import { NextIntlClientProvider, useMessages } from 'next-intl';
|
||||
import { unstable_setRequestLocale } from 'next-intl/server';
|
||||
|
||||
import { DemoBadge } from '@/components/DemoBadge';
|
||||
import { AllLocales } from '@/utils/AppConfig';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
icons: [
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
url: '/apple-touch-icon.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '32x32',
|
||||
url: '/favicon-32x32.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '16x16',
|
||||
url: '/favicon-16x16.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
url: '/favicon.ico',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export function generateStaticParams() {
|
||||
return AllLocales.map(locale => ({ locale }));
|
||||
}
|
||||
|
||||
export default function RootLayout(props: {
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}) {
|
||||
unstable_setRequestLocale(props.params.locale);
|
||||
|
||||
// Using internationalization in Client Components
|
||||
const messages = useMessages();
|
||||
|
||||
// The `suppressHydrationWarning` in <html> is used to prevent hydration errors caused by `next-themes`.
|
||||
// Solution provided by the package itself: https://github.com/pacocoursey/next-themes?tab=readme-ov-file#with-app
|
||||
|
||||
// The `suppressHydrationWarning` attribute in <body> is used to prevent hydration errors caused by Sentry Overlay,
|
||||
// which dynamically adds a `style` attribute to the body tag.
|
||||
return (
|
||||
<html lang={props.params.locale} suppressHydrationWarning>
|
||||
<body className="bg-background text-foreground antialiased" suppressHydrationWarning>
|
||||
{/* PRO: Dark mode support for Shadcn UI */}
|
||||
<NextIntlClientProvider
|
||||
locale={props.params.locale}
|
||||
messages={messages}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
<DemoBadge />
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -4,16 +4,17 @@ import * as Sentry from '@sentry/nextjs';
|
|||
import NextError from 'next/error';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function GlobalError(props: {
|
||||
export default function GlobalError({
|
||||
error,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
params: { locale: string };
|
||||
}) {
|
||||
useEffect(() => {
|
||||
Sentry.captureException(props.error);
|
||||
}, [props.error]);
|
||||
Sentry.captureException(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<html lang={props.params.locale}>
|
||||
<html>
|
||||
<body>
|
||||
{/* `NextError` is the default Next.js error page component. Its type
|
||||
definition requires a `statusCode` prop. However, since the App Router
|
||||
|
|
|
|||
45
src/app/layout.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import '@/styles/globals.css';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
import { NextIntlClientProvider } from 'next-intl';
|
||||
import { getLocale } from 'next-intl/server';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
icons: [
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
url: '/apple-touch-icon.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '32x32',
|
||||
url: '/favicon-32x32.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '16x16',
|
||||
url: '/favicon-16x16.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
url: '/favicon.ico',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default async function RootLayout(props: { children: React.ReactNode }) {
|
||||
const locale = await getLocale();
|
||||
|
||||
return (
|
||||
<html lang={locale} suppressHydrationWarning>
|
||||
<body
|
||||
className="bg-background text-foreground antialiased"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<NextIntlClientProvider>{props.children}</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -10,6 +10,5 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
|||
changeFrequency: 'daily',
|
||||
priority: 0.7,
|
||||
},
|
||||
// Add more URLs here
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ import { usePathname } from 'next/navigation';
|
|||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
|
||||
export const ActiveLink = (props: { href: string; children: React.ReactNode }) => {
|
||||
export const ActiveLink = (props: {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
|
|
@ -13,8 +16,8 @@ export const ActiveLink = (props: { href: string; children: React.ReactNode }) =
|
|||
href={props.href}
|
||||
className={cn(
|
||||
'px-3 py-2',
|
||||
pathname.endsWith(props.href)
|
||||
&& 'rounded-md bg-primary text-primary-foreground',
|
||||
pathname.endsWith(props.href) &&
|
||||
'rounded-md bg-primary text-primary-foreground',
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useLocale } from 'next-intl';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
|
@ -10,7 +11,6 @@ import {
|
|||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { usePathname, useRouter } from '@/libs/i18nNavigation';
|
||||
import { AppConfig } from '@/utils/AppConfig';
|
||||
|
||||
export const LocaleSwitcher = () => {
|
||||
|
|
@ -18,15 +18,20 @@ export const LocaleSwitcher = () => {
|
|||
const pathname = usePathname();
|
||||
const locale = useLocale();
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
router.push(pathname, { locale: value });
|
||||
const handleChange = () => {
|
||||
router.push(pathname);
|
||||
router.refresh();
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="p-2 focus-visible:ring-offset-0" variant="ghost" size="icon" aria-label="lang-switcher">
|
||||
<Button
|
||||
className="p-2 focus-visible:ring-offset-0"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label="lang-switcher"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="size-6 stroke-current stroke-2"
|
||||
|
|
@ -43,7 +48,7 @@ export const LocaleSwitcher = () => {
|
|||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuRadioGroup value={locale} onValueChange={handleChange}>
|
||||
{AppConfig.locales.map(elt => (
|
||||
{AppConfig.locales.map((elt) => (
|
||||
<DropdownMenuRadioItem key={elt.id} value={elt.id}>
|
||||
{elt.name}
|
||||
</DropdownMenuRadioItem>
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
|
||||
const AccordionItem = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AccordionPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn('border-b', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AccordionItem.displayName = 'AccordionItem';
|
||||
|
||||
const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-1 items-center justify-between py-5 text-left text-lg font-medium transition-all hover:no-underline [&[data-state=open]>svg]:rotate-90',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="size-4 shrink-0 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||
|
||||
const AccordionContent = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className="overflow-hidden text-base text-muted-foreground transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn('pb-5 pt-0', className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||
|
||||
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
import type { VariantProps } from 'class-variance-authority';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
|
||||
import { badgeVariants } from './badgeVariants';
|
||||
|
||||
export type BadgeProps = {} & React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof badgeVariants>;
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export { Badge };
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export const badgeVariants = cva(
|
||||
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
||||
secondary:
|
||||
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
destructive:
|
||||
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
||||
outline: 'text-foreground',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
@ -1,27 +1,59 @@
|
|||
import { Slot } from '@radix-ui/react-slot';
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
import * as React from 'react';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
import { buttonVariants } from './buttonVariants';
|
||||
|
||||
export type ButtonProps = {
|
||||
asChild?: boolean;
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>;
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
||||
outline:
|
||||
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
|
||||
ghost:
|
||||
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
||||
sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
|
||||
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
||||
icon: 'size-9',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
);
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export { Button };
|
||||
function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<'button'> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean;
|
||||
}) {
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Button, buttonVariants };
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-10 px-4 py-2',
|
||||
sm: 'h-9 rounded-md px-3',
|
||||
lg: 'h-11 rounded-md px-8',
|
||||
icon: 'size-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
|
||||
type DataTableProps<TData, TValue> = {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
};
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
});
|
||||
const t = useTranslations('DataTable');
|
||||
|
||||
return (
|
||||
<div className="rounded-md border bg-card">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length
|
||||
? (
|
||||
table.getRowModel().rows.map(row => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<TableCell key={cell.id} className="whitespace-nowrap">
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
)
|
||||
: (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
{t('no_results')}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,200 +1,257 @@
|
|||
'use client';
|
||||
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||
import { Check, ChevronRight, Circle } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
function DropdownMenu({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
||||
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
||||
}
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
function DropdownMenuPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
||||
function DropdownMenuTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Trigger
|
||||
data-slot="dropdown-menu-trigger"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
||||
function DropdownMenuContent({
|
||||
className,
|
||||
sideOffset = 4,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
data-slot="dropdown-menu-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
);
|
||||
}
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||
function DropdownMenuGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="ml-auto size-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
));
|
||||
DropdownMenuSubTrigger.displayName
|
||||
= DropdownMenuPrimitive.SubTrigger.displayName;
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSubContent.displayName
|
||||
= DropdownMenuPrimitive.SubContent.displayName;
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
function DropdownMenuItem({
|
||||
className,
|
||||
inset,
|
||||
variant = 'default',
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean;
|
||||
variant?: 'default' | 'destructive';
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Item
|
||||
data-slot="dropdown-menu-item"
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
));
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
||||
);
|
||||
}
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className="size-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
));
|
||||
DropdownMenuCheckboxItem.displayName
|
||||
= DropdownMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className="size-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
));
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'px-2 py-1.5 text-sm font-semibold',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
||||
|
||||
const DropdownMenuShortcut = ({
|
||||
function DropdownMenuCheckboxItem({
|
||||
className,
|
||||
children,
|
||||
checked,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
||||
return (
|
||||
<span
|
||||
className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownMenuRadioGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioGroup
|
||||
data-slot="dropdown-menu-radio-group"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
||||
}
|
||||
|
||||
function DropdownMenuRadioItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownMenuLabel({
|
||||
className,
|
||||
inset,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Label
|
||||
data-slot="dropdown-menu-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
'px-2 py-1.5 text-sm font-medium data-[inset]:pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownMenuSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
data-slot="dropdown-menu-separator"
|
||||
className={cn('bg-border -mx-1 my-1 h-px', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownMenuShortcut({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'span'>) {
|
||||
return (
|
||||
<span
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
className={cn(
|
||||
'text-muted-foreground ml-auto text-xs tracking-widest',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownMenuSub({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
||||
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
|
||||
}
|
||||
|
||||
function DropdownMenuSubTrigger({
|
||||
className,
|
||||
inset,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto size-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownMenuSubContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuSubContent,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
import type * as LabelPrimitive from '@radix-ui/react-label';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import * as React from 'react';
|
||||
import type { ControllerProps, FieldPath, FieldValues } from 'react-hook-form';
|
||||
import { Controller, FormProvider } from 'react-hook-form';
|
||||
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { cn } from '@/utils/Helpers';
|
||||
|
||||
import { FormFieldContext, FormItemContext, useFormField } from './useFormField';
|
||||
|
||||
const Form = FormProvider;
|
||||
|
||||
const FormField = <
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
>({
|
||||
...props
|
||||
}: ControllerProps<TFieldValues, TName>) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const formFieldName = React.useMemo(() => ({ name: props.name }), []);
|
||||
|
||||
return (
|
||||
<FormFieldContext.Provider value={formFieldName}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const FormItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const id = React.useId();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const formItemId = React.useMemo(() => ({ id }), []);
|
||||
|
||||
return (
|
||||
<FormItemContext.Provider value={formItemId}>
|
||||
<div ref={ref} className={cn('space-y-2', className)} {...props} />
|
||||
</FormItemContext.Provider>
|
||||
);
|
||||
});
|
||||
FormItem.displayName = 'FormItem';
|
||||
|
||||
const FormLabel = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { error, formItemId } = useFormField();
|
||||
|
||||
return (
|
||||
<Label
|
||||
ref={ref}
|
||||
className={cn(error && 'text-destructive', className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormLabel.displayName = 'FormLabel';
|
||||
|
||||
const FormControl = React.forwardRef<
|
||||
React.ElementRef<typeof Slot>,
|
||||
React.ComponentPropsWithoutRef<typeof Slot>
|
||||
>(({ ...props }, ref) => {
|
||||
const { error, formItemId, formDescriptionId, formMessageId }
|
||||
= useFormField();
|
||||
|
||||
return (
|
||||
<Slot
|
||||
ref={ref}
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
? `${formDescriptionId}`
|
||||
: `${formDescriptionId} ${formMessageId}`
|
||||
}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormControl.displayName = 'FormControl';
|
||||
|
||||
const FormDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { formDescriptionId } = useFormField();
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formDescriptionId}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormDescription.displayName = 'FormDescription';
|
||||
|
||||
const FormMessage = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, children, ...props }, ref) => {
|
||||
const { error, formMessageId } = useFormField();
|
||||
const body = error ? String(error?.message) : children;
|
||||
|
||||
if (!body) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formMessageId}
|
||||
className={cn('text-sm font-medium text-destructive', className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
);
|
||||
});
|
||||
FormMessage.displayName = 'FormMessage';
|
||||
|
||||
export {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
};
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
|
||||
export type InputProps = {} & React.InputHTMLAttributes<HTMLInputElement>;
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
Input.displayName = 'Input';
|
||||
|
||||
export { Input };
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
|
||||
const labelVariants = cva(
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
);
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||
VariantProps<typeof labelVariants>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Label.displayName = LabelPrimitive.Root.displayName;
|
||||
|
||||
export { Label };
|
||||
|
|
@ -1,31 +1,28 @@
|
|||
'use client';
|
||||
|
||||
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
||||
import * as React from 'react';
|
||||
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Separator = React.forwardRef<
|
||||
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||
>(
|
||||
(
|
||||
{ className, orientation = 'horizontal', decorative = true, ...props },
|
||||
ref,
|
||||
) => (
|
||||
function Separator({
|
||||
className,
|
||||
orientation = 'horizontal',
|
||||
decorative = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
||||
return (
|
||||
<SeparatorPrimitive.Root
|
||||
ref={ref}
|
||||
data-slot="separator-root"
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
'shrink-0 bg-border',
|
||||
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
||||
'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
);
|
||||
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
||||
);
|
||||
}
|
||||
|
||||
export { Separator };
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
|
||||
const Table = React.forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className="relative w-full overflow-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn('w-full caption-bottom text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
Table.displayName = 'Table';
|
||||
|
||||
const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
||||
));
|
||||
TableHeader.displayName = 'TableHeader';
|
||||
|
||||
const TableBody = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={cn('[&_tr:last-child]:border-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableBody.displayName = 'TableBody';
|
||||
|
||||
const TableFooter = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableFooter.displayName = 'TableFooter';
|
||||
|
||||
const TableRow = React.forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableRow.displayName = 'TableRow';
|
||||
|
||||
const TableHead = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableHead.displayName = 'TableHead';
|
||||
|
||||
const TableCell = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCell.displayName = 'TableCell';
|
||||
|
||||
const TableCaption = React.forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={cn('mt-4 text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCaption.displayName = 'TableCaption';
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
};
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/utils/Helpers';
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider;
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root;
|
||||
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import React from 'react';
|
||||
import { type FieldPath, type FieldValues, useFormContext } from 'react-hook-form';
|
||||
|
||||
type FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
> = {
|
||||
name: TName;
|
||||
};
|
||||
|
||||
export const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||
{} as FormFieldContextValue,
|
||||
);
|
||||
|
||||
type FormItemContextValue = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const FormItemContext = React.createContext<FormItemContextValue>(
|
||||
{} as FormItemContextValue,
|
||||
);
|
||||
|
||||
export const useFormField = () => {
|
||||
const fieldContext = React.useContext(FormFieldContext);
|
||||
const itemContext = React.useContext(FormItemContext);
|
||||
const { getFieldState, formState } = useFormContext();
|
||||
|
||||
const fieldState = getFieldState(fieldContext.name, formState);
|
||||
|
||||
if (!fieldContext) {
|
||||
throw new Error('useFormField should be used within <FormField>');
|
||||
}
|
||||
|
||||
const { id } = itemContext;
|
||||
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
|
||||
export const ProtectFallback = (props: { trigger: React.ReactNode }) => {
|
||||
const t = useTranslations('ProtectFallback');
|
||||
|
||||
return (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{props.trigger}</TooltipTrigger>
|
||||
<TooltipContent align="center">
|
||||
<p>{t('not_enough_permission')}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
import { useTranslations } from 'next-intl';
|
||||
import React from 'react';
|
||||
|
||||
import type { BillingInterval } from '@/types/Subscription';
|
||||
|
||||
export const PricingCard = (props: {
|
||||
planId: string;
|
||||
price: number;
|
||||
interval: BillingInterval;
|
||||
button: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const t = useTranslations('PricingPlan');
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-border px-6 py-8 text-center">
|
||||
<div className="text-lg font-semibold">
|
||||
{t(`${props.planId}_plan_name`)}
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex items-center justify-center">
|
||||
<div className="text-5xl font-bold">
|
||||
{`$${props.price}`}
|
||||
</div>
|
||||
|
||||
<div className="ml-1 text-muted-foreground">
|
||||
{`/ ${t(`plan_interval_${props.interval}`)}`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-2 text-sm text-muted-foreground">
|
||||
{t(`${props.planId}_plan_description`)}
|
||||
</div>
|
||||
|
||||
{props.button}
|
||||
|
||||
<ul className="mt-8 space-y-3">{props.children}</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
export const PricingFeature = (props: { children: React.ReactNode }) => (
|
||||
<li className="flex items-center text-muted-foreground">
|
||||
<svg
|
||||
className="mr-1 size-6 stroke-current stroke-2 text-purple-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M0 0h24v24H0z" stroke="none" />
|
||||
<path d="M5 12l5 5L20 7" />
|
||||
</svg>
|
||||
{props.children}
|
||||
</li>
|
||||
);
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { PricingCard } from '@/features/billing/PricingCard';
|
||||
import { PricingFeature } from '@/features/billing/PricingFeature';
|
||||
import { PricingPlanList } from '@/utils/AppConfig';
|
||||
|
||||
export const PricingInformation = (props: {
|
||||
buttonList: Record<string, React.ReactNode>;
|
||||
}) => {
|
||||
const t = useTranslations('PricingPlan');
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-x-6 gap-y-8 md:grid-cols-3">
|
||||
{Object.values(PricingPlanList).map(plan => (
|
||||
<PricingCard
|
||||
key={plan.id}
|
||||
planId={plan.id}
|
||||
price={plan.price}
|
||||
interval={plan.interval}
|
||||
button={props.buttonList[plan.id]}
|
||||
>
|
||||
<PricingFeature>
|
||||
{t('feature_team_member', {
|
||||
number: plan.features.teamMember,
|
||||
})}
|
||||
</PricingFeature>
|
||||
|
||||
<PricingFeature>
|
||||
{t('feature_website', {
|
||||
number: plan.features.website,
|
||||
})}
|
||||
</PricingFeature>
|
||||
|
||||
<PricingFeature>
|
||||
{t('feature_storage', {
|
||||
number: plan.features.storage,
|
||||
})}
|
||||
</PricingFeature>
|
||||
|
||||
<PricingFeature>
|
||||
{t('feature_transfer', {
|
||||
number: plan.features.transfer,
|
||||
})}
|
||||
</PricingFeature>
|
||||
|
||||
<PricingFeature>{t('feature_email_support')}</PricingFeature>
|
||||
</PricingCard>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { OrganizationSwitcher, UserButton } from '@clerk/nextjs';
|
||||
import Link from 'next/link';
|
||||
import { useLocale } from 'next-intl';
|
||||
|
||||
import { ActiveLink } from '@/components/ActiveLink';
|
||||
import { LocaleSwitcher } from '@/components/LocaleSwitcher';
|
||||
|
|
@ -15,7 +14,6 @@ import {
|
|||
} from '@/components/ui/dropdown-menu';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Logo } from '@/templates/Logo';
|
||||
import { getI18nPath } from '@/utils/Helpers';
|
||||
|
||||
export const DashboardHeader = (props: {
|
||||
menu: {
|
||||
|
|
@ -23,8 +21,6 @@ export const DashboardHeader = (props: {
|
|||
label: string;
|
||||
}[];
|
||||
}) => {
|
||||
const locale = useLocale();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
|
|
@ -46,10 +42,7 @@ export const DashboardHeader = (props: {
|
|||
|
||||
<OrganizationSwitcher
|
||||
organizationProfileMode="navigation"
|
||||
organizationProfileUrl={getI18nPath(
|
||||
'/dashboard/organization-profile',
|
||||
locale,
|
||||
)}
|
||||
organizationProfileUrl="/dashboard/organization-profile"
|
||||
afterCreateOrganizationUrl="/dashboard"
|
||||
hidePersonal
|
||||
skipInvitationScreen
|
||||
|
|
@ -62,7 +55,7 @@ export const DashboardHeader = (props: {
|
|||
|
||||
<nav className="ml-3 max-lg:hidden">
|
||||
<ul className="flex flex-row items-center gap-x-3 text-lg font-medium [&_a:hover]:opacity-100 [&_a]:opacity-75">
|
||||
{props.menu.map(item => (
|
||||
{props.menu.map((item) => (
|
||||
<li key={item.href}>
|
||||
<ActiveLink href={item.href}>{item.label}</ActiveLink>
|
||||
</li>
|
||||
|
|
@ -80,7 +73,7 @@ export const DashboardHeader = (props: {
|
|||
<ToggleMenuButton />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{props.menu.map(item => (
|
||||
{props.menu.map((item) => (
|
||||
<DropdownMenuItem key={item.href} asChild>
|
||||
<Link href={item.href}>{item.label}</Link>
|
||||
</DropdownMenuItem>
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
export const CTABanner = (props: {
|
||||
title: string;
|
||||
description: string;
|
||||
buttons: React.ReactNode;
|
||||
}) => (
|
||||
<div className="rounded-xl bg-muted bg-gradient-to-br from-indigo-400 via-purple-400 to-pink-400 px-6 py-10 text-center">
|
||||
<div className="text-3xl font-bold text-primary-foreground">
|
||||
{props.title}
|
||||
</div>
|
||||
|
||||
<div className="mt-2 text-lg font-medium text-muted">
|
||||
{props.description}
|
||||
</div>
|
||||
|
||||
<div className="mt-6">{props.buttons}</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
export const LogoCloud = (props: { text: string; children: React.ReactNode }) => (
|
||||
<>
|
||||
<div className="text-center text-xl font-medium text-muted-foreground">
|
||||
{props.text}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-2 place-items-center gap-x-3 gap-y-6 md:grid-cols-5 md:gap-x-20 [&_a:hover]:opacity-100 [&_a]:opacity-60">
|
||||
{props.children}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
export const StickyBanner = (props: { children: React.ReactNode }) => (
|
||||
<div className="sticky top-0 z-50 bg-primary p-4 text-center text-lg font-semibold text-primary-foreground [&_a:hover]:text-indigo-500 [&_a]:text-fuchsia-500">
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
/* eslint-disable react-dom/no-unsafe-target-blank */
|
||||
import Image from 'next/image';
|
||||
|
||||
import { LogoCloud } from '@/features/landing/LogoCloud';
|
||||
|
||||
export const SponsorLogos = () => (
|
||||
<LogoCloud text="Sponsored by">
|
||||
<a
|
||||
href="https://clerk.com?utm_source=github&utm_medium=sponsorship&utm_campaign=nextjs-boilerplate"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Image
|
||||
src="/assets/images/clerk-logo-dark.png"
|
||||
alt="Clerk logo dark"
|
||||
className="dark:hidden"
|
||||
width="128"
|
||||
height="37"
|
||||
/>
|
||||
<Image
|
||||
src="/assets/images/clerk-logo-white.png"
|
||||
alt="Clerk logo light"
|
||||
className="hidden dark:block"
|
||||
width="128"
|
||||
height="37"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://l.crowdin.com/next-js"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Image
|
||||
src="/assets/images/crowdin-dark.png"
|
||||
alt="Crowdin logo dark"
|
||||
className="dark:hidden"
|
||||
width="128"
|
||||
height="26"
|
||||
/>
|
||||
<Image
|
||||
src="/assets/images/crowdin-white.png"
|
||||
alt="Crowdin logo light"
|
||||
className="hidden dark:block"
|
||||
width="128"
|
||||
height="26"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://sentry.io/for/nextjs/?utm_source=github&utm_medium=paid-community&utm_campaign=general-fy25q1-nextjs&utm_content=github-banner-nextjsboilerplate-logo"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Image
|
||||
src="/assets/images/sentry-dark.png"
|
||||
alt="Sentry logo dark"
|
||||
className="dark:hidden"
|
||||
width="128"
|
||||
height="38"
|
||||
/>
|
||||
<Image
|
||||
src="/assets/images/sentry-white.png"
|
||||
alt="Sentry logo light"
|
||||
className="hidden dark:block"
|
||||
width="128"
|
||||
height="38"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://launch.arcjet.com/Q6eLbRE"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Image
|
||||
src="/assets/images/arcjet-light.svg"
|
||||
alt="Arcjet logo dark"
|
||||
className="dark:hidden"
|
||||
width="128"
|
||||
height="38"
|
||||
/>
|
||||
<Image
|
||||
src="/assets/images/arcjet-dark.svg"
|
||||
alt="Arcjet logo light"
|
||||
className="hidden dark:block"
|
||||
width="128"
|
||||
height="38"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://nextjs-boilerplate.com/pro-saas-starter-kit"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<Image
|
||||
src="/assets/images/nextjs-boilerplate-saas.png"
|
||||
alt="Nextjs SaaS Boilerplate"
|
||||
width="128"
|
||||
height="30"
|
||||
/>
|
||||
</a>
|
||||
</LogoCloud>
|
||||
);
|
||||
12
src/i18n/request.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { getRequestConfig } from 'next-intl/server';
|
||||
|
||||
export default getRequestConfig(async () => {
|
||||
// Provide a static locale, fetch a user setting,
|
||||
// read from `cookies()`, `headers()`, etc.
|
||||
const locale = 'en';
|
||||
|
||||
return {
|
||||
locale,
|
||||
messages: (await import(`../locales/${locale}.json`)).default,
|
||||
};
|
||||
});
|
||||
|
|
@ -6,25 +6,13 @@ import * as Sentry from '@sentry/nextjs';
|
|||
import * as Spotlight from '@spotlightjs/spotlight';
|
||||
|
||||
Sentry.init({
|
||||
// Sentry DSN
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 1,
|
||||
|
||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||
debug: false,
|
||||
|
||||
replaysOnErrorSampleRate: 1.0,
|
||||
|
||||
// This sets the sample rate to be 10%. You may want this to be 100% while
|
||||
// in development and sample at a lower rate in production
|
||||
replaysSessionSampleRate: 0.1,
|
||||
|
||||
// You can remove this option if you're not planning to use the Sentry Session Replay feature:
|
||||
replaysOnErrorSampleRate: 1.0,
|
||||
integrations: [
|
||||
Sentry.replayIntegration({
|
||||
// Additional Replay configuration goes in here, for example:
|
||||
maskAllText: true,
|
||||
blockAllMedia: true,
|
||||
}),
|
||||
|
|
@ -34,3 +22,7 @@ Sentry.init({
|
|||
if (process.env.NODE_ENV === 'development') {
|
||||
Spotlight.init();
|
||||
}
|
||||
|
||||
// This export will instrument router navigations, and is only relevant if you enable tracing.
|
||||
// `captureRouterTransitionStart` is available from SDK version 9.12.0 onwards
|
||||
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import * as Sentry from '@sentry/nextjs';
|
||||
|
||||
export const onRequestError = Sentry.captureRequestError;
|
||||
|
||||
export async function register() {
|
||||
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||
// Node.js Sentry configuration
|
||||
|
|
|
|||
6
src/lib/utils.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||