From b9a1aa801b6ef64d8b50ca80c5e467563a1856b3 Mon Sep 17 00:00:00 2001 From: Tomasz Janczuk Date: Wed, 6 Dec 2023 04:47:48 -0800 Subject: [PATCH 01/10] scaffolding of tailwindcss and shadcn-ui --- apps/web/components.json | 16 + apps/web/package.json | 12 +- apps/web/postcss.config.js | 6 + .../app/(dashboard)/api/auth/[auth0]/route.ts | 4 +- apps/web/src/app/(dashboard)/api/proxy.ts | 2 +- .../(dashboard)/api/proxy/[...path]/route.ts | 2 +- .../[invitationId]/accept/layout.tsx | 2 +- .../[tenantId]/[invitationId]/accept/page.tsx | 5 +- apps/web/src/app/(dashboard)/layout.tsx | 2 +- .../(dashboard)/manage/[tenantId]/layout.tsx | 2 +- .../[tenantId]/newplan/[planId]/page.tsx | 8 +- .../manage/[tenantId]/newplan/page.tsx | 4 +- .../manage/[tenantId]/paymentmethod/page.tsx | 8 +- .../manage/[tenantId]/settings/page.tsx | 6 +- .../web/src/app/(dashboard)/manage/layout.tsx | 4 +- .../manage/newplan/[planId]/page.tsx | 2 +- apps/web/src/app/(dashboard)/manage/page.tsx | 2 +- .../app/(dashboard)/manage/settings/page.tsx | 4 +- apps/web/src/app/(site)/contact/page.tsx | 4 +- apps/web/src/app/(site)/layout.tsx | 2 +- apps/web/src/app/(site)/pricing/page.tsx | 4 +- apps/web/src/app/global.css | 76 +++++ apps/web/src/app/layout.tsx | 27 +- apps/web/src/components/utils.ts | 6 + apps/web/tailwind.config.js | 76 +++++ apps/web/tsconfig.json | 11 +- yarn.lock | 293 +++++++++++++++++- 27 files changed, 531 insertions(+), 59 deletions(-) create mode 100644 apps/web/components.json create mode 100644 apps/web/postcss.config.js create mode 100644 apps/web/src/app/global.css create mode 100644 apps/web/src/components/utils.ts create mode 100644 apps/web/tailwind.config.js diff --git a/apps/web/components.json b/apps/web/components.json new file mode 100644 index 0000000..9b9d2a7 --- /dev/null +++ b/apps/web/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/app/global.css", + "baseColor": "slate", + "cssVariables": true + }, + "aliases": { + "components": "components", + "utils": "components/utils" + } +} diff --git a/apps/web/package.json b/apps/web/package.json index f715dc6..4233d91 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -13,22 +13,30 @@ "dependencies": { "@auth0/nextjs-auth0": "^3.2.0", "@letsgo/pricing": "*", + "@letsgo/stripe": "*", "@letsgo/tenant": "*", "@letsgo/types": "*", - "@letsgo/stripe": "*", "@stripe/react-stripe-js": "^2.3.1", "@stripe/stripe-js": "^2.1.10", + "class-variance-authority": "^0.7.0", + "clsx": "^2.0.0", + "lucide-react": "^0.294.0", "next": "^13.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "swr": "^2.2.4" + "swr": "^2.2.4", + "tailwind-merge": "^2.0.0", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@types/node": "^17.0.12", "@types/react": "^18.2.8", "@types/react-dom": "^18.2.4", + "autoprefixer": "^10.4.16", "eslint": "7.32.0", "eslint-config-custom": "*", + "postcss": "^8.4.31", + "tailwindcss": "^3.3.5", "tsconfig": "*", "typescript": "^5.1.3" } diff --git a/apps/web/postcss.config.js b/apps/web/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/apps/web/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/apps/web/src/app/(dashboard)/api/auth/[auth0]/route.ts b/apps/web/src/app/(dashboard)/api/auth/[auth0]/route.ts index 391734d..b0f0bca 100644 --- a/apps/web/src/app/(dashboard)/api/auth/[auth0]/route.ts +++ b/apps/web/src/app/(dashboard)/api/auth/[auth0]/route.ts @@ -2,8 +2,8 @@ import { AfterRefetch, AppRouteHandlerFn, Session } from "@auth0/nextjs-auth0"; import { serializeIdentity } from "@letsgo/trust"; import jwt from "jsonwebtoken"; import { NextRequest } from "next/server"; -import { apiRequest, getApiUrl } from "../../../../../components/common-server"; -import { getAuth0 } from "../../../../../components/auth0"; +import { apiRequest, getApiUrl } from "components/common-server"; +import { getAuth0 } from "components/auth0"; /** * Save the OpenId profile of the user in the database so that the management portal diff --git a/apps/web/src/app/(dashboard)/api/proxy.ts b/apps/web/src/app/(dashboard)/api/proxy.ts index da979d7..62fa3b5 100644 --- a/apps/web/src/app/(dashboard)/api/proxy.ts +++ b/apps/web/src/app/(dashboard)/api/proxy.ts @@ -12,7 +12,7 @@ import { AppRouteHandlerFnContext } from "@auth0/nextjs-auth0/dist/helpers/with-api-auth-required"; import { NextRequest, NextResponse } from "next/server"; -import { getAuth0 } from "../../../components/auth0"; +import { getAuth0 } from "components/auth0"; const methods: { [method: string]: { proxyRequestBody: boolean; proxyResponseBody: boolean }; diff --git a/apps/web/src/app/(dashboard)/api/proxy/[...path]/route.ts b/apps/web/src/app/(dashboard)/api/proxy/[...path]/route.ts index fa26127..611e7a0 100644 --- a/apps/web/src/app/(dashboard)/api/proxy/[...path]/route.ts +++ b/apps/web/src/app/(dashboard)/api/proxy/[...path]/route.ts @@ -11,7 +11,7 @@ */ import { AppRouteHandlerFn } from "@auth0/nextjs-auth0"; -import { getAuth0 } from "../../../../../components/auth0"; +import { getAuth0 } from "components/auth0"; import proxyFactory from "../../proxy"; // Delay the initialization of the proxy until the first request is received. diff --git a/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/layout.tsx b/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/layout.tsx index 1762186..561e205 100644 --- a/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/layout.tsx +++ b/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/layout.tsx @@ -1,7 +1,7 @@ "use client"; import { useUser } from "@auth0/nextjs-auth0/client"; -import { TenantProvider } from "../../../../../../components/TenantProvider"; +import { TenantProvider } from "components/TenantProvider"; export default function AcceptLayout({ children, diff --git a/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/page.tsx b/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/page.tsx index 9059a72..ec5b71c 100644 --- a/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/page.tsx +++ b/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/page.tsx @@ -1,10 +1,9 @@ "use client"; import { useRouter } from "next/navigation"; -import { useApiMutate } from "../../../../../../components/common-client"; +import { useApiMutate } from "components/common-client"; import { useEffect, useState } from "react"; -import { useTenant } from "../../../../../../components/TenantProvider"; -import { getTenant } from "@letsgo/tenant"; +import { useTenant } from "components/TenantProvider"; export default function Join({ params: { tenantId, invitationId }, diff --git a/apps/web/src/app/(dashboard)/layout.tsx b/apps/web/src/app/(dashboard)/layout.tsx index e3f5b07..f319669 100644 --- a/apps/web/src/app/(dashboard)/layout.tsx +++ b/apps/web/src/app/(dashboard)/layout.tsx @@ -1,4 +1,4 @@ -import { Auth0EnsureEnvironmentVariables } from "../../components/EnsureEnvironmentVariables"; +import { Auth0EnsureEnvironmentVariables } from "components/EnsureEnvironmentVariables"; export default Auth0EnsureEnvironmentVariables; diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/layout.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/layout.tsx index 547688c..3d8bfa8 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/layout.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/layout.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect } from "react"; -import { useTenant } from "../../../../components/TenantProvider"; +import { useTenant } from "components/TenantProvider"; export default function CheckAccessToTenant({ children, diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/[planId]/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/[planId]/page.tsx index fd1af23..39e2a71 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/[planId]/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/[planId]/page.tsx @@ -5,10 +5,10 @@ import { getActivePlan, getPlan } from "@letsgo/pricing"; import { PostPlanRequest, PostPlanResponse } from "@letsgo/types"; import { notFound, useRouter } from "next/navigation"; import { useEffect, useState } from "react"; -import Checkout from "../../../../../../components/Checkout"; -import { StripeElements } from "../../../../../../components/StripeElements"; -import { useTenant } from "../../../../../../components/TenantProvider"; -import { useApiMutate } from "../../../../../../components/common-client"; +import Checkout from "components/Checkout"; +import { StripeElements } from "components/StripeElements"; +import { useTenant } from "components/TenantProvider"; +import { useApiMutate } from "components/common-client"; const EmailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/page.tsx index 83fc64c..5065fa6 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/page.tsx @@ -2,8 +2,8 @@ import { ActivePlans, Plan } from "@letsgo/pricing"; import { useRouter } from "next/navigation"; -import { PlanSelector } from "../../../../../components/PlanSelector"; -import { useTenant } from "../../../../../components/TenantProvider"; +import { PlanSelector } from "components/PlanSelector"; +import { useTenant } from "components/TenantProvider"; function ChooseNewPlan({ params }: { params: { tenantId: string } }) { const router = useRouter(); diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/page.tsx index 72b226b..a5a06d6 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/page.tsx @@ -3,10 +3,10 @@ import { useUser } from "@auth0/nextjs-auth0/client"; import { PostPaymentMethodResponse } from "@letsgo/types"; import { useEffect } from "react"; -import Checkout from "../../../../../components/Checkout"; -import { StripeElements } from "../../../../../components/StripeElements"; -import { useTenant } from "../../../../../components/TenantProvider"; -import { useApiMutate } from "../../../../../components/common-client"; +import Checkout from "components/Checkout"; +import { StripeElements } from "components/StripeElements"; +import { useTenant } from "components/TenantProvider"; +import { useApiMutate } from "components/common-client"; function PaymentMethodUpdate() { const { error: userError, user } = useUser(); diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/settings/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/settings/page.tsx index 3e4f3b7..7aa39c5 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/settings/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/settings/page.tsx @@ -1,8 +1,8 @@ "use client"; -import { Account } from "../../../../../components/Account"; -import { Team } from "../../../../../components/Team"; -import { Invitations } from "../../../../../components/Invitations"; +import { Account } from "components/Account"; +import { Team } from "components/Team"; +import { Invitations } from "components/Invitations"; export default function Tenant({ params }: { params: { tenantId: string } }) { const tenantId = params.tenantId as string; diff --git a/apps/web/src/app/(dashboard)/manage/layout.tsx b/apps/web/src/app/(dashboard)/manage/layout.tsx index 738f526..79f7327 100644 --- a/apps/web/src/app/(dashboard)/manage/layout.tsx +++ b/apps/web/src/app/(dashboard)/manage/layout.tsx @@ -2,8 +2,8 @@ import { useUser } from "@auth0/nextjs-auth0/client"; import Link from "next/link"; -import Navbar from "../../../components/Navbar"; -import { TenantSelector } from "../../../components/TenantSelector"; +import Navbar from "components/Navbar"; +import { TenantSelector } from "components/TenantSelector"; export default function ManageLayout({ children, diff --git a/apps/web/src/app/(dashboard)/manage/newplan/[planId]/page.tsx b/apps/web/src/app/(dashboard)/manage/newplan/[planId]/page.tsx index f726ccb..7c5136c 100644 --- a/apps/web/src/app/(dashboard)/manage/newplan/[planId]/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/newplan/[planId]/page.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import { useRouter, notFound } from "next/navigation"; -import { useTenant } from "../../../../../components/TenantProvider"; +import { useTenant } from "components/TenantProvider"; import { getActivePlan } from "@letsgo/pricing"; function ResolveTenantForNewPlan({ params }: { params: { planId: string } }) { diff --git a/apps/web/src/app/(dashboard)/manage/page.tsx b/apps/web/src/app/(dashboard)/manage/page.tsx index 45b171a..24791e0 100644 --- a/apps/web/src/app/(dashboard)/manage/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/page.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import { useRouter } from "next/navigation"; -import { useTenant } from "../../../components/TenantProvider"; +import { useTenant } from "components/TenantProvider"; function ResolveTenant() { const router = useRouter(); diff --git a/apps/web/src/app/(dashboard)/manage/settings/page.tsx b/apps/web/src/app/(dashboard)/manage/settings/page.tsx index 41fdcd7..4046d9a 100644 --- a/apps/web/src/app/(dashboard)/manage/settings/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/settings/page.tsx @@ -1,7 +1,7 @@ "use client"; -import { Me } from "../../../../components/Me"; -import { User } from "../../../../components/User"; +import { Me } from "components/Me"; +import { User } from "components/User"; export default function Profile() { return ( diff --git a/apps/web/src/app/(site)/contact/page.tsx b/apps/web/src/app/(site)/contact/page.tsx index 1e556f7..cde4fde 100644 --- a/apps/web/src/app/(site)/contact/page.tsx +++ b/apps/web/src/app/(site)/contact/page.tsx @@ -3,8 +3,8 @@ import { useUser } from "@auth0/nextjs-auth0/client"; import { useEffect, useState } from "react"; import { useSearchParams } from "next/navigation"; -import { useTenant } from "../../../components/TenantProvider"; -import { useApiMutate } from "../../../components/common-client"; +import { useTenant } from "components/TenantProvider"; +import { useApiMutate } from "components/common-client"; import { ContactMessagePayload } from "@letsgo/types"; interface ContactParams { diff --git a/apps/web/src/app/(site)/layout.tsx b/apps/web/src/app/(site)/layout.tsx index de4a3df..40b425d 100644 --- a/apps/web/src/app/(site)/layout.tsx +++ b/apps/web/src/app/(site)/layout.tsx @@ -1,6 +1,6 @@ "use client"; -import Navbar from "../../components/Navbar"; +import Navbar from "components/Navbar"; import Link from "next/link"; import { useUser } from "@auth0/nextjs-auth0/client"; diff --git a/apps/web/src/app/(site)/pricing/page.tsx b/apps/web/src/app/(site)/pricing/page.tsx index 0d483c6..1fae362 100644 --- a/apps/web/src/app/(site)/pricing/page.tsx +++ b/apps/web/src/app/(site)/pricing/page.tsx @@ -1,8 +1,8 @@ "use client"; import { ActivePlans, Plan } from "@letsgo/pricing"; -import { PlanSelector } from "../../../components/PlanSelector"; -import { useTenant } from "../../../components/TenantProvider"; +import { PlanSelector } from "components/PlanSelector"; +import { useTenant } from "components/TenantProvider"; import { useRouter } from "next/navigation"; export default function Pricing() { diff --git a/apps/web/src/app/global.css b/apps/web/src/app/global.css new file mode 100644 index 0000000..6a75725 --- /dev/null +++ b/apps/web/src/app/global.css @@ -0,0 +1,76 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 1bf96e4..26d3bc2 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,5 +1,13 @@ import { UserProvider } from "@auth0/nextjs-auth0/client"; -import { TenantProvider } from "../components/TenantProvider"; +import { TenantProvider } from "components/TenantProvider"; +import { Inter as FontSans } from "next/font/google"; +import { cn } from "components/utils"; +import "app/global.css"; + +export const fontSans = FontSans({ + subsets: ["latin"], + variable: "--font-sans", +}); export default function RootLayout({ children, @@ -7,12 +15,17 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - - - - {children} - - + + + + {children} + + ); } diff --git a/apps/web/src/components/utils.ts b/apps/web/src/components/utils.ts new file mode 100644 index 0000000..ec79801 --- /dev/null +++ b/apps/web/src/components/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js new file mode 100644 index 0000000..87388c2 --- /dev/null +++ b/apps/web/tailwind.config.js @@ -0,0 +1,76 @@ +const { fontFamily } = require("tailwindcss/defaultTheme"); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + content: ["./src/**/*.{ts,tsx}"], + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: 0 }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: 0 }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + fontFamily: { + sans: ["var(--font-sans)", ...fontFamily.sans], + }, + }, + }, + plugins: [require("tailwindcss-animate")], +}; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index c989945..5bf13fe 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,14 +1,9 @@ { "extends": "tsconfig/nextjs.json", "compilerOptions": { - "plugins": [{ "name": "next" }] + "plugins": [{ "name": "next" }], + "baseUrl": "./src" }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - "src/app/(dashboard)/manage/[tenantId]/layout.tsx" - ], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] } diff --git a/yarn.lock b/yarn.lock index b7e06ce..885e804 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -1095,6 +1100,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/runtime@^7.23.1": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.5.tgz#11edb98f8aeec529b82b211028177679144242db" + integrity sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.23.2": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" @@ -2372,6 +2384,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -2380,6 +2397,11 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2505,6 +2527,18 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +autoprefixer@^10.4.16: + version "10.4.16" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" + integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== + dependencies: + browserslist "^4.21.10" + caniuse-lite "^1.0.30001538" + fraction.js "^4.3.6" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" @@ -2662,7 +2696,7 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.21.9: +browserslist@^4.21.10, browserslist@^4.21.9: version "4.22.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== @@ -2722,6 +2756,11 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -2737,6 +2776,11 @@ caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001541: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz#752f21f56f96f1b1a52e97aae98c57c562d5d9da" integrity sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw== +caniuse-lite@^1.0.30001538: + version "1.0.30001565" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz#a528b253c8a2d95d2b415e11d8b9942acc100c4f" + integrity sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w== + chalk@4, chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -2759,7 +2803,7 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -chokidar@^3.5.2: +chokidar@^3.5.2, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -2784,6 +2828,13 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== +class-variance-authority@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522" + integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A== + dependencies: + clsx "2.0.0" + client-only@0.0.1, client-only@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" @@ -2798,6 +2849,11 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +clsx@2.0.0, clsx@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" + integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2844,6 +2900,11 @@ commander@^11.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + component-emitter@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -2916,6 +2977,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + csstype@^3.0.2: version "3.1.2" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" @@ -3013,6 +3079,11 @@ dezalgo@^1.0.4: asap "^2.0.0" wrappy "1" +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -3025,6 +3096,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -3710,7 +3786,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.9, fast-glob@^3.3.1: +fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -3837,6 +3913,11 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +fraction.js@^4.3.6: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -3929,11 +4010,30 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -4773,6 +4873,11 @@ jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" +jiti@^1.19.1: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + joi@^17.11.0, joi@^17.6.0: version "17.11.0" resolved "https://registry.yarnpkg.com/joi/-/joi-17.11.0.tgz#aa9da753578ec7720e6f0ca2c7046996ed04fc1a" @@ -4929,6 +5034,16 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lilconfig@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.0.0.tgz#f8067feb033b5b74dab4602a5f5029420be749bc" + integrity sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -5012,6 +5127,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lucide-react@^0.294.0: + version "0.294.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.294.0.tgz#dc406e1e7e2f722cf93218fe5b31cf3c95778817" + integrity sha512-V7o0/VECSGbLHn3/1O67FUgBwWB+hmzshrgDVRJQhMh8uj5D3HBuIvhuAmQTtlupILSplwIZg5FTc4tTKMA2SA== + lunr@^2.3.9: version "2.3.9" resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" @@ -5066,7 +5186,7 @@ methods@^1.1.2, methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -micromatch@^4.0.4: +micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -5153,6 +5273,15 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + nanoid@^3.3.6: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" @@ -5234,6 +5363,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -5246,7 +5380,7 @@ oauth4webapi@^2.3.0: resolved "https://registry.yarnpkg.com/oauth4webapi/-/oauth4webapi-2.3.0.tgz#d01aeb83b60dbe3ff9ef1c6ec4a39e29c7be7ff6" integrity sha512-JGkb5doGrwzVDuHwgrR4nHJayzN4h59VCed6EW8Tql6iHDfZIabCJvg6wtbn5q6pyB2hZruI3b77Nudvq7NmvA== -object-assign@^4, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -5256,6 +5390,11 @@ object-hash@^2.2.0: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + object-inspect@^1.13.1, object-inspect@^1.9.0: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" @@ -5474,7 +5613,12 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pirates@^4.0.4: +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.1, pirates@^4.0.4: version "4.0.6" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== @@ -5486,7 +5630,51 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -postcss@8.4.31: +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-selector-parser@^6.0.11: + version "6.0.13" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@8.4.31, postcss@^8.4.23, postcss@^8.4.31: version "8.4.31" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== @@ -5628,6 +5816,13 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -5703,7 +5898,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.20.0, resolve@^1.22.4: +resolve@^1.1.7, resolve@^1.20.0, resolve@^1.22.2, resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -6064,6 +6259,19 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" +sucrase@^3.32.0: + version "3.34.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.34.0.tgz#1e0e2d8fcf07f8b9c3569067d92fbd8690fb576f" + integrity sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "7.1.6" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + superagent@^8.0.5: version "8.1.2" resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" @@ -6133,6 +6341,46 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" +tailwind-merge@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.0.0.tgz#a0f3a8c874ebae5feec5595614d08245a5f88a39" + integrity sha512-WO8qghn9yhsldLSg80au+3/gY9E4hFxIvQ3qOmlpXnqpDKoMruKfi/56BbbMg6fHTQJ9QD3cc79PoWqlaQE4rw== + dependencies: + "@babel/runtime" "^7.23.1" + +tailwindcss-animate@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4" + integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== + +tailwindcss@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.5.tgz#22a59e2fbe0ecb6660809d9cc5f3976b077be3b8" + integrity sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.19.1" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + resolve "^1.22.2" + sucrase "^3.32.0" + tapable@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -6152,6 +6400,20 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -6186,6 +6448,11 @@ ts-api-utils@^1.0.1: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + ts-jest@^29.1.1: version "29.1.1" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" @@ -6413,6 +6680,11 @@ use-sync-external-store@^1.2.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -6571,6 +6843,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== + yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" From da34ffd7dc04ad7597a1d34f4c5d594bf433213e Mon Sep 17 00:00:00 2001 From: Tomasz Janczuk Date: Wed, 13 Dec 2023 08:36:14 -0800 Subject: [PATCH 02/10] add shadcnui and tailwindcss --- apps/api/src/routes/me.ts | 2 + apps/api/src/routes/tenant.ts | 16 +- apps/web/next.config.js | 2 +- apps/web/package.json | 36 +- apps/web/public/logo.png | Bin 0 -> 47275 bytes .../[invitationId]/accept/layout.tsx | 11 +- .../[tenantId]/[invitationId]/accept/page.tsx | 5 +- .../manage/[tenantId]/dashboard/page.tsx | 16 + .../(dashboard)/manage/[tenantId]/layout.tsx | 13 +- .../[tenantId]/newplan/[planId]/page.tsx | 116 ++- .../manage/[tenantId]/newplan/page.tsx | 31 +- .../manage/[tenantId]/paymentmethod/page.tsx | 3 +- .../paymentmethod/processing/page.tsx | 25 +- .../{settings => subscription}/page.tsx | 8 - .../manage/[tenantId]/team/page.tsx | 18 + .../web/src/app/(dashboard)/manage/layout.tsx | 91 +- .../manage/newplan/[planId]/page.tsx | 3 +- apps/web/src/app/(dashboard)/manage/page.tsx | 4 +- .../app/(dashboard)/manage/profile/page.tsx | 5 + .../app/(dashboard)/manage/settings/page.tsx | 15 - apps/web/src/app/(site)/contact/page.tsx | 104 +- apps/web/src/app/(site)/layout.tsx | 69 +- apps/web/src/app/(site)/page.tsx | 10 +- apps/web/src/app/(site)/pricing/page.tsx | 18 +- apps/web/src/app/layout.tsx | 2 + apps/web/src/components/Account.tsx | 89 +- apps/web/src/components/Billing.tsx | 72 ++ apps/web/src/components/Checkout.tsx | 51 +- apps/web/src/components/ConfirmDialog.tsx | 49 + apps/web/src/components/Contact.tsx | 118 +++ apps/web/src/components/CopyButton.tsx | 59 ++ apps/web/src/components/Dashboard.tsx | 16 + apps/web/src/components/DeleteTenant.tsx | 37 + apps/web/src/components/Invitations.tsx | 159 +-- apps/web/src/components/Landing.tsx | 16 + .../web/src/components/LoadingPlaceholder.tsx | 13 + apps/web/src/components/Me.tsx | 23 - apps/web/src/components/Navbar.tsx | 14 +- apps/web/src/components/PlanOption.tsx | 63 ++ apps/web/src/components/PlanSelector.tsx | 63 +- apps/web/src/components/Profile.tsx | 109 ++ apps/web/src/components/SidebarNav.tsx | 44 + apps/web/src/components/SuscriptionPlan.tsx | 38 + apps/web/src/components/Team.tsx | 182 +++- apps/web/src/components/TenantSelector.tsx | 149 ++- apps/web/src/components/User.tsx | 15 - apps/web/src/components/UserNav.tsx | 91 ++ apps/web/src/components/common-client.tsx | 14 + apps/web/src/components/ui/accordion.tsx | 58 ++ apps/web/src/components/ui/alert-dialog.tsx | 141 +++ apps/web/src/components/ui/alert.tsx | 59 ++ apps/web/src/components/ui/aspect-ratio.tsx | 7 + apps/web/src/components/ui/avatar.tsx | 50 + apps/web/src/components/ui/badge.tsx | 36 + apps/web/src/components/ui/button.tsx | 56 ++ apps/web/src/components/ui/calendar.tsx | 66 ++ apps/web/src/components/ui/card.tsx | 79 ++ apps/web/src/components/ui/checkbox.tsx | 30 + apps/web/src/components/ui/collapsible.tsx | 11 + apps/web/src/components/ui/command.tsx | 155 +++ apps/web/src/components/ui/context-menu.tsx | 200 ++++ apps/web/src/components/ui/dialog.tsx | 122 +++ apps/web/src/components/ui/dropdown-menu.tsx | 200 ++++ apps/web/src/components/ui/form.tsx | 176 ++++ apps/web/src/components/ui/hover-card.tsx | 29 + apps/web/src/components/ui/input.tsx | 25 + apps/web/src/components/ui/label.tsx | 26 + apps/web/src/components/ui/menubar.tsx | 236 +++++ .../web/src/components/ui/navigation-menu.tsx | 128 +++ apps/web/src/components/ui/popover.tsx | 31 + apps/web/src/components/ui/progress.tsx | 28 + apps/web/src/components/ui/radio-group.tsx | 44 + apps/web/src/components/ui/scroll-area.tsx | 48 + apps/web/src/components/ui/select.tsx | 160 +++ apps/web/src/components/ui/separator.tsx | 31 + apps/web/src/components/ui/sheet.tsx | 140 +++ apps/web/src/components/ui/skeleton.tsx | 15 + apps/web/src/components/ui/slider.tsx | 28 + apps/web/src/components/ui/switch.tsx | 29 + apps/web/src/components/ui/table.tsx | 117 +++ apps/web/src/components/ui/tabs.tsx | 55 + apps/web/src/components/ui/textarea.tsx | 24 + apps/web/src/components/ui/toast.tsx | 127 +++ apps/web/src/components/ui/toaster.tsx | 35 + apps/web/src/components/ui/toggle-group.tsx | 61 ++ apps/web/src/components/ui/toggle.tsx | 45 + apps/web/src/components/ui/tooltip.tsx | 30 + apps/web/src/components/ui/use-toast.ts | 192 ++++ packages/stripe/src/index.ts | 3 +- packages/types/src/index.ts | 6 +- yarn.lock | 945 +++++++++++++++++- 91 files changed, 5585 insertions(+), 606 deletions(-) create mode 100644 apps/web/public/logo.png create mode 100644 apps/web/src/app/(dashboard)/manage/[tenantId]/dashboard/page.tsx rename apps/web/src/app/(dashboard)/manage/[tenantId]/{settings => subscription}/page.tsx (50%) create mode 100644 apps/web/src/app/(dashboard)/manage/[tenantId]/team/page.tsx create mode 100644 apps/web/src/app/(dashboard)/manage/profile/page.tsx delete mode 100644 apps/web/src/app/(dashboard)/manage/settings/page.tsx create mode 100644 apps/web/src/components/Billing.tsx create mode 100644 apps/web/src/components/ConfirmDialog.tsx create mode 100644 apps/web/src/components/Contact.tsx create mode 100644 apps/web/src/components/CopyButton.tsx create mode 100644 apps/web/src/components/Dashboard.tsx create mode 100644 apps/web/src/components/DeleteTenant.tsx create mode 100644 apps/web/src/components/Landing.tsx create mode 100644 apps/web/src/components/LoadingPlaceholder.tsx delete mode 100644 apps/web/src/components/Me.tsx create mode 100644 apps/web/src/components/PlanOption.tsx create mode 100644 apps/web/src/components/Profile.tsx create mode 100644 apps/web/src/components/SidebarNav.tsx create mode 100644 apps/web/src/components/SuscriptionPlan.tsx delete mode 100644 apps/web/src/components/User.tsx create mode 100644 apps/web/src/components/UserNav.tsx create mode 100644 apps/web/src/components/ui/accordion.tsx create mode 100644 apps/web/src/components/ui/alert-dialog.tsx create mode 100644 apps/web/src/components/ui/alert.tsx create mode 100644 apps/web/src/components/ui/aspect-ratio.tsx create mode 100644 apps/web/src/components/ui/avatar.tsx create mode 100644 apps/web/src/components/ui/badge.tsx create mode 100644 apps/web/src/components/ui/button.tsx create mode 100644 apps/web/src/components/ui/calendar.tsx create mode 100644 apps/web/src/components/ui/card.tsx create mode 100644 apps/web/src/components/ui/checkbox.tsx create mode 100644 apps/web/src/components/ui/collapsible.tsx create mode 100644 apps/web/src/components/ui/command.tsx create mode 100644 apps/web/src/components/ui/context-menu.tsx create mode 100644 apps/web/src/components/ui/dialog.tsx create mode 100644 apps/web/src/components/ui/dropdown-menu.tsx create mode 100644 apps/web/src/components/ui/form.tsx create mode 100644 apps/web/src/components/ui/hover-card.tsx create mode 100644 apps/web/src/components/ui/input.tsx create mode 100644 apps/web/src/components/ui/label.tsx create mode 100644 apps/web/src/components/ui/menubar.tsx create mode 100644 apps/web/src/components/ui/navigation-menu.tsx create mode 100644 apps/web/src/components/ui/popover.tsx create mode 100644 apps/web/src/components/ui/progress.tsx create mode 100644 apps/web/src/components/ui/radio-group.tsx create mode 100644 apps/web/src/components/ui/scroll-area.tsx create mode 100644 apps/web/src/components/ui/select.tsx create mode 100644 apps/web/src/components/ui/separator.tsx create mode 100644 apps/web/src/components/ui/sheet.tsx create mode 100644 apps/web/src/components/ui/skeleton.tsx create mode 100644 apps/web/src/components/ui/slider.tsx create mode 100644 apps/web/src/components/ui/switch.tsx create mode 100644 apps/web/src/components/ui/table.tsx create mode 100644 apps/web/src/components/ui/tabs.tsx create mode 100644 apps/web/src/components/ui/textarea.tsx create mode 100644 apps/web/src/components/ui/toast.tsx create mode 100644 apps/web/src/components/ui/toaster.tsx create mode 100644 apps/web/src/components/ui/toggle-group.tsx create mode 100644 apps/web/src/components/ui/toggle.tsx create mode 100644 apps/web/src/components/ui/tooltip.tsx create mode 100644 apps/web/src/components/ui/use-toast.ts diff --git a/apps/api/src/routes/me.ts b/apps/api/src/routes/me.ts index c026d15..1d31490 100644 --- a/apps/api/src/routes/me.ts +++ b/apps/api/src/routes/me.ts @@ -22,6 +22,7 @@ export const meHandler: RequestHandler = async (req, res, next) => { isBuiltInIssuer( (request.user?.decodedJwt?.payload as JwtPayload).iss || "" ); + const returnAccessToken = req.query.returnAccessToken !== undefined; let tenants = await getTenantsOfIdentity({ identity: request.user.identity, }); @@ -54,6 +55,7 @@ export const meHandler: RequestHandler = async (req, res, next) => { const body: GetMeResponse = { identityId: request.user.identityId, identity: request.user.identity, + accessToken: returnAccessToken ? request.user.jwt : undefined, tenants, }; return res.json(body); diff --git a/apps/api/src/routes/tenant.ts b/apps/api/src/routes/tenant.ts index 78d0b60..193f094 100644 --- a/apps/api/src/routes/tenant.ts +++ b/apps/api/src/routes/tenant.ts @@ -212,7 +212,7 @@ router.get( // Redirect the browser back to the dashboard with appropriate next step let location: string; if (response.status === "succeeded") { - location = createAbsoluteWebUrl(`/manage/${tenantId}/settings`); + location = createAbsoluteWebUrl(`/manage/${tenantId}/subscription`); } else if (response.status === "processing") { location = createAbsoluteWebUrl( `/manage/${tenantId}/paymentmethod/processing` @@ -320,7 +320,12 @@ router.post( // No card number yet, it will be created next }; await putTenant(tenant); - res.json(response); + // Subscription is activated immediately if the Stripe user had a balance on their account. + if (response.status === "active") { + res.status(200).send(); + } else { + res.json(response); + } return; } else { // current plan is not Stripe, new plan is not Stripe @@ -424,8 +429,13 @@ router.get( const invitations = await getInvitations({ tenantId: req.params.tenantId, }); + const sortedInvitations = invitations.sort((i1, i2) => { + const e1 = new Date(i1.expiresAt).getTime(); + const e2 = new Date(i2.expiresAt).getTime(); + return e1 > e2 ? 1 : e1 < e2 ? -1 : 0; + }); const response: GetInvitationsResponse = { - invitations: invitations.map(pruneResponse), + invitations: sortedInvitations.map(pruneResponse), }; res.json(response); } catch (e) { diff --git a/apps/web/next.config.js b/apps/web/next.config.js index ce29f15..5481cc0 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -2,7 +2,7 @@ const path = require("path"); module.exports = { reactStrictMode: true, - transpilePackages: ["ui"], + transpilePackages: ["ui", "lucide-react"], output: "standalone", experimental: { outputFileTracingRoot: path.join(__dirname, "../../"), diff --git a/apps/web/package.json b/apps/web/package.json index 4233d91..749a017 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -12,21 +12,55 @@ }, "dependencies": { "@auth0/nextjs-auth0": "^3.2.0", + "@hookform/resolvers": "^3.3.2", "@letsgo/pricing": "*", "@letsgo/stripe": "*", "@letsgo/tenant": "*", "@letsgo/types": "*", + "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-aspect-ratio": "^1.0.3", + "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-context-menu": "^2.1.5", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-hover-card": "^1.0.7", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-menubar": "^1.0.4", + "@radix-ui/react-navigation-menu": "^1.1.4", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-scroll-area": "^1.0.5", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-separator": "^1.0.3", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-toast": "^1.1.5", + "@radix-ui/react-toggle": "^1.0.3", + "@radix-ui/react-toggle-group": "^1.0.4", + "@radix-ui/react-tooltip": "^1.0.7", "@stripe/react-stripe-js": "^2.3.1", "@stripe/stripe-js": "^2.1.10", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", + "cmdk": "^0.2.0", + "date-fns": "^2.30.0", "lucide-react": "^0.294.0", "next": "^13.4.1", "react": "^18.2.0", + "react-day-picker": "^8.9.1", "react-dom": "^18.2.0", + "react-hook-form": "^7.48.2", "swr": "^2.2.4", "tailwind-merge": "^2.0.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.22.4" }, "devDependencies": { "@types/node": "^17.0.12", diff --git a/apps/web/public/logo.png b/apps/web/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..93910c0a7fe1532323bb95424c81d38ee7e34d12 GIT binary patch literal 47275 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 literal 0 HcmV?d00001 diff --git a/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/layout.tsx b/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/layout.tsx index 561e205..a0787ca 100644 --- a/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/layout.tsx +++ b/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/layout.tsx @@ -1,6 +1,7 @@ "use client"; import { useUser } from "@auth0/nextjs-auth0/client"; +import { LoadingPlaceholder } from "components/LoadingPlaceholder"; import { TenantProvider } from "components/TenantProvider"; export default function AcceptLayout({ @@ -10,8 +11,16 @@ export default function AcceptLayout({ }) { const { user, error, isLoading } = useUser(); - if (isLoading) return
Loading...
; + if (isLoading) { + return ( +
+ +
+ ); + } + if (error) throw error; + if (user) { return ( diff --git a/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/page.tsx b/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/page.tsx index ec5b71c..71c8ed8 100644 --- a/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/page.tsx +++ b/apps/web/src/app/(dashboard)/join/[tenantId]/[invitationId]/accept/page.tsx @@ -28,10 +28,7 @@ export default function Join({ afterSuccess: async () => { setInvitationAccepted(true); await refreshTenants(); - // const newTenant = getTenantFromTenants(tenantId); - // setCurrentTenant(newTenant); - window.location.href = `/manage/${tenantId}/settings`; - // router.replace(`/manage/${tenantId}/settings`); + window.location.href = `/manage/${tenantId}/team`; }, }); const [invitationAccepted, setInvitationAccepted] = useState(false); diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/dashboard/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/dashboard/page.tsx new file mode 100644 index 0000000..25a7392 --- /dev/null +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/dashboard/page.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { Dashboard } from "components/Dashboard"; + +export default function DashboardView({ + params, +}: { + params: { tenantId: string }; +}) { + const tenantId = params.tenantId as string; + return ( +
+ +
+ ); +} diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/layout.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/layout.tsx index 3d8bfa8..831aef7 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/layout.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/layout.tsx @@ -10,14 +10,8 @@ export default function CheckAccessToTenant({ children: React.ReactNode; params: { tenantId: string }; }) { - const { - isLoading, - error, - tenants, - currentTenant, - getTenant, - setCurrentTenant, - } = useTenant(); + const { error, tenants, currentTenant, getTenant, setCurrentTenant } = + useTenant(); useEffect(() => { if (tenants) { @@ -29,12 +23,11 @@ export default function CheckAccessToTenant({ } }, [tenants, params.tenantId, setCurrentTenant, getTenant]); - if (isLoading) return
Loading...
; if (error) throw error; return currentTenant?.tenantId === params.tenantId ? (
{children}
) : ( -
Loading...
+
); } diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/[planId]/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/[planId]/page.tsx index 39e2a71..b9256b5 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/[planId]/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/[planId]/page.tsx @@ -9,6 +9,19 @@ import Checkout from "components/Checkout"; import { StripeElements } from "components/StripeElements"; import { useTenant } from "components/TenantProvider"; import { useApiMutate } from "components/common-client"; +import { Button } from "components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "components/ui/card"; +import { PlanOption } from "components/PlanOption"; +import { ArrowBigRight } from "lucide-react"; +import { Input } from "components/ui/input"; +import { Label } from "components/ui/label"; +import { cn } from "components/utils"; const EmailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; @@ -33,7 +46,7 @@ function SwitchPlans({ if (data === undefined) { // Plan transition is complete - return to the management page refreshTenants(); - router.replace(`/manage/${params.tenantId}/settings`); + router.replace(`/manage/${params.tenantId}/subscription`); } }, }); @@ -51,7 +64,7 @@ function SwitchPlans({ if (planResponse) { // New plan requires payment information - collect it using Stripe. - // On successful completion, Stripe will redirect the browser to /manage/:tenantId/settings. + // On successful completion, Stripe will redirect the browser to /manage/:tenantId/subscription. return ( { + router.replace(`/manage/${currentTenant.tenantId}/subscription`); + }; + if (currentPlan.planId === newPlan.planId) { - return ( -
-

- You are already subscribed to this plan:{" "} - - {currentPlan.name} ({currentPlan.price}) - -

- -
- ); + router.replace(`/manage/${currentTenant.tenantId}/subscription`); + return
; } const confirmEmail = @@ -107,42 +109,52 @@ function SwitchPlans({ const isEmailValid = !confirmEmail || EmailRegex.test(email); return ( -
-

Switching Plans

-

- Current plan:{" "} - - {currentPlan.name} ({currentPlan.price}) - -

-

- New plan:{" "} - - {newPlan.name} ({newPlan.price}) - -

- {confirmEmail && ( -

- Billing e-mail address:{" "} - setEmail(e.target.value)} - style={isEmailValid ? {} : { color: "red" }} - /> -

- )} - -
+ + + Do you want to switch plans? + + +
+ +
+ +
+ +
+ {confirmEmail && ( +
+ + setEmail(e.target.value)} + className={cn({ "border-red-500": !isEmailValid })} + value={email} + /> +
+ )} +
+ + +
+
+
); } - return
Loading...
; + return
; } export default SwitchPlans; diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/page.tsx index 5065fa6..62deb1a 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/newplan/page.tsx @@ -1,11 +1,12 @@ "use client"; import { ActivePlans, Plan } from "@letsgo/pricing"; -import { useRouter } from "next/navigation"; import { PlanSelector } from "components/PlanSelector"; import { useTenant } from "components/TenantProvider"; +import { Card, CardContent, CardHeader, CardTitle } from "components/ui/card"; +import { useRouter } from "next/navigation"; -function ChooseNewPlan({ params }: { params: { tenantId: string } }) { +function ChooseNewPlan() { const router = useRouter(); const { error, currentTenant } = useTenant(); @@ -19,26 +20,28 @@ function ChooseNewPlan({ params }: { params: { tenantId: string } }) { )}&from=/manage/${currentTenant?.tenantId}/newplan` ); } else { - window.location.href = `/manage/newplan/${encodeURIComponent( - plan.planId - )}`; + router.push(`/manage/newplan/${encodeURIComponent(plan.planId)}`); } }; if (currentTenant) { return ( -
-

Select new plan

- -
+ + + Select new plan + + + + + ); } - return
Loading...
; + return
; } export default ChooseNewPlan; diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/page.tsx index a5a06d6..07a58fa 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/page.tsx @@ -7,6 +7,7 @@ import Checkout from "components/Checkout"; import { StripeElements } from "components/StripeElements"; import { useTenant } from "components/TenantProvider"; import { useApiMutate } from "components/common-client"; +import { LoadingPlaceholder } from "components/LoadingPlaceholder"; function PaymentMethodUpdate() { const { error: userError, user } = useUser(); @@ -44,7 +45,7 @@ function PaymentMethodUpdate() { ); } - return
Loading...
; + return ; } export default PaymentMethodUpdate; diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/processing/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/processing/page.tsx index e991b43..0827baa 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/processing/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/paymentmethod/processing/page.tsx @@ -1,5 +1,7 @@ "use client"; +import { Button } from "components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "components/ui/card"; import { useRouter } from "next/navigation"; function ProcessingPaymentMethodUpdate({ @@ -10,17 +12,24 @@ function ProcessingPaymentMethodUpdate({ const router = useRouter(); const handleClick = () => { - router.push(`/manage/${params.tenantId}/settings`); + router.push(`/manage/${params.tenantId}/subscription`); }; return ( -
-

- Processing payment details. We will update you when processing is - complete. -

- -
+ + + Processing payment + + +
+ Payment processing is in progress. We will update you when processing + is complete. +
+
+ +
+
+
); } diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/settings/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/subscription/page.tsx similarity index 50% rename from apps/web/src/app/(dashboard)/manage/[tenantId]/settings/page.tsx rename to apps/web/src/app/(dashboard)/manage/[tenantId]/subscription/page.tsx index 7aa39c5..73685f0 100644 --- a/apps/web/src/app/(dashboard)/manage/[tenantId]/settings/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/subscription/page.tsx @@ -1,20 +1,12 @@ "use client"; import { Account } from "components/Account"; -import { Team } from "components/Team"; -import { Invitations } from "components/Invitations"; export default function Tenant({ params }: { params: { tenantId: string } }) { const tenantId = params.tenantId as string; return (
-

Tenant

-

Account

-

Team

- -

Invitations

-
); } diff --git a/apps/web/src/app/(dashboard)/manage/[tenantId]/team/page.tsx b/apps/web/src/app/(dashboard)/manage/[tenantId]/team/page.tsx new file mode 100644 index 0000000..1a9d861 --- /dev/null +++ b/apps/web/src/app/(dashboard)/manage/[tenantId]/team/page.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { Team } from "components/Team"; +import { Invitations } from "components/Invitations"; + +export default function ManageTeam({ + params, +}: { + params: { tenantId: string }; +}) { + const tenantId = params.tenantId as string; + return ( +
+ + +
+ ); +} diff --git a/apps/web/src/app/(dashboard)/manage/layout.tsx b/apps/web/src/app/(dashboard)/manage/layout.tsx index 79f7327..486d9f7 100644 --- a/apps/web/src/app/(dashboard)/manage/layout.tsx +++ b/apps/web/src/app/(dashboard)/manage/layout.tsx @@ -1,42 +1,87 @@ "use client"; import { useUser } from "@auth0/nextjs-auth0/client"; -import Link from "next/link"; -import Navbar from "components/Navbar"; +import { LoadingPlaceholder } from "components/LoadingPlaceholder"; +import { SidebarNav } from "components/SidebarNav"; +import { useTenant } from "components/TenantProvider"; import { TenantSelector } from "components/TenantSelector"; +import { UserNav } from "components/UserNav"; +import Image from "next/image"; +import Link from "next/link"; +import logo from "../../../../public/logo.png"; + +function getSidebarNavItems(tenantId: string) { + return [ + { + title: "Dashboard", + href: `/manage/${tenantId}/dashboard`, + }, + { + title: "Profile", + href: `/manage/profile`, + }, + { + title: "Team", + href: `/manage/${tenantId}/team`, + }, + { + title: "Subscription", + href: `/manage/${tenantId}/subscription`, + }, + ]; +} export default function ManageLayout({ children, }: { children: React.ReactNode; }) { - const { user, error, isLoading } = useUser(); + const { error: tenantError, currentTenant } = useTenant(); + const { user, error: userError, isLoading: isUserLoading } = useUser(); + + if (!isUserLoading && !user) { + // User is not logged in - redirect to the login page + window.location.href = `/api/auth/login?returnTo=${ + window.location.pathname + }${window.location.search || ""}${window.location.hash || ""}`; + } - if (isLoading) return
Loading...
; + const error = userError || tenantError; if (error) throw error; - if (user) { + if (!user || !currentTenant) { return ( -
- -
- Home • Tenant:{" "} - •{" "} - {user?.name || "Profile"} •{" "} - Tenant •{" "} - Contact •{" "} - Logout -
-
-
{children}
+
+
); } - // User is not logged in - redirect to the login page - window.location.href = `/api/auth/login?returnTo=${window.location.pathname}${ - window.location.search || "" - }${window.location.hash || ""}`; - - return <>; + return ( +
+ {/* Top navigation */} +
+
+
+ + LetsGo + +
|
+ +
+
+ +
+
+
+
+ {/* Side navigation */} + + {/* Main dashboard body */} +
{children}
+
+
+ ); } diff --git a/apps/web/src/app/(dashboard)/manage/newplan/[planId]/page.tsx b/apps/web/src/app/(dashboard)/manage/newplan/[planId]/page.tsx index 7c5136c..c815cbd 100644 --- a/apps/web/src/app/(dashboard)/manage/newplan/[planId]/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/newplan/[planId]/page.tsx @@ -4,6 +4,7 @@ import { useEffect } from "react"; import { useRouter, notFound } from "next/navigation"; import { useTenant } from "components/TenantProvider"; import { getActivePlan } from "@letsgo/pricing"; +import { LoadingPlaceholder } from "components/LoadingPlaceholder"; function ResolveTenantForNewPlan({ params }: { params: { planId: string } }) { const router = useRouter(); @@ -20,7 +21,7 @@ function ResolveTenantForNewPlan({ params }: { params: { planId: string } }) { if (error) throw error; if (!getActivePlan(params.planId)) return notFound(); - return
Loading...
; + return ; } export default ResolveTenantForNewPlan; diff --git a/apps/web/src/app/(dashboard)/manage/page.tsx b/apps/web/src/app/(dashboard)/manage/page.tsx index 24791e0..bf910d6 100644 --- a/apps/web/src/app/(dashboard)/manage/page.tsx +++ b/apps/web/src/app/(dashboard)/manage/page.tsx @@ -10,13 +10,13 @@ function ResolveTenant() { useEffect(() => { if (currentTenant) { - router.replace(`/manage/${currentTenant.tenantId}/settings`); + router.replace(`/manage/${currentTenant.tenantId}/dashboard`); } }, [currentTenant, router]); if (error) throw error; - return
Loading...
; + return
; } export default ResolveTenant; diff --git a/apps/web/src/app/(dashboard)/manage/profile/page.tsx b/apps/web/src/app/(dashboard)/manage/profile/page.tsx new file mode 100644 index 0000000..2b3a662 --- /dev/null +++ b/apps/web/src/app/(dashboard)/manage/profile/page.tsx @@ -0,0 +1,5 @@ +"use client"; + +import { Profile } from "components/Profile"; + +export default Profile; diff --git a/apps/web/src/app/(dashboard)/manage/settings/page.tsx b/apps/web/src/app/(dashboard)/manage/settings/page.tsx deleted file mode 100644 index 4046d9a..0000000 --- a/apps/web/src/app/(dashboard)/manage/settings/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -"use client"; - -import { Me } from "components/Me"; -import { User } from "components/User"; - -export default function Profile() { - return ( -
-

Your user profile:

- -

Response from HTTP GET /api/proxy/v1/me:

- -
- ); -} diff --git a/apps/web/src/app/(site)/contact/page.tsx b/apps/web/src/app/(site)/contact/page.tsx index cde4fde..1d23ceb 100644 --- a/apps/web/src/app/(site)/contact/page.tsx +++ b/apps/web/src/app/(site)/contact/page.tsx @@ -1,105 +1,5 @@ "use client"; -import { useUser } from "@auth0/nextjs-auth0/client"; -import { useEffect, useState } from "react"; -import { useSearchParams } from "next/navigation"; -import { useTenant } from "components/TenantProvider"; -import { useApiMutate } from "components/common-client"; -import { ContactMessagePayload } from "@letsgo/types"; +import { Contact } from "components/Contact"; -interface ContactParams { - email: string; - name: string; - message: string; -} - -export default function Contact() { - const query = useSearchParams(); - const { isLoading: isUserLoading, user } = useUser(); - const { currentTenant } = useTenant(); - const [submitted, setSubmitted] = useState(false); - const [params, setParams] = useState({ - email: "", - name: "", - message: "", - }); - const { - isMutating: isSubmitting, - error: errorSubmiting, - trigger: submitContact, - } = useApiMutate({ - path: `/v1/contact`, - method: "POST", - unauthenticated: true, - afterSuccess: async () => { - setSubmitted(true); - }, - }); - - useEffect(() => { - if (user) { - setParams({ - email: user.email || "", - name: user.name || "", - message: "", - }); - } - }, [user]); - - if (errorSubmiting) throw errorSubmiting; - if (isUserLoading) return
Loading...
; - if (submitted) return
Thank you for your message!
; - if (isSubmitting) return
Submitting...
; - - const handleSubmit = () => { - const payload: ContactMessagePayload = { - ...params, - query: Object.fromEntries(query.entries()), - tenantId: currentTenant?.tenantId, - identityId: user?.identityId as string, - timestamp: new Date().toISOString(), - }; - submitContact(payload); - }; - - return ( -
-

Contact Us

-
-
Name:
- setParams({ ...params, name: e.target.value })} - /> -

- Email: - setParams({ ...params, email: e.target.value })} - /> -

- Message: -