From 463b0eaacf52dcbf788b8c44f0af82066cec29d3 Mon Sep 17 00:00:00 2001 From: Pawan Paudel Date: Wed, 5 Mar 2025 14:53:12 +0545 Subject: [PATCH 01/10] feat: Integrate Tailwind CSS and enhance UI styling --- app/dashboard/page.tsx | 155 +++++++-- app/globals.css | 4 +- app/layout.tsx | 11 +- app/page.tsx | 95 ++++-- client/components/ProtectedApiInteraction.tsx | 133 +++++++- package.json | 3 + pnpm-lock.yaml | 303 +++++++++++++++++- postcss.config.mjs | 6 + 8 files changed, 620 insertions(+), 90 deletions(-) create mode 100644 postcss.config.mjs diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 63f33a7..354d4e9 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,11 +1,11 @@ -"use client" +"use client"; -import { useEffect, useState } from "react" -import { useRouter } from "next/navigation" -import { trpc } from "@/client/utils/trpc/trpc-client" -import { ProtectedApiInteraction } from "../../client/components/ProtectedApiInteraction" -import { useAuth } from "@/client/hooks/useAuth" -import { supabase } from "@/client/utils/supabase/supabase-client-client" +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import { trpc } from "@/client/utils/trpc/trpc-client"; +import { ProtectedApiInteraction } from "../../client/components/ProtectedApiInteraction"; +import { useAuth } from "@/client/hooks/useAuth"; +import { supabase } from "@/client/utils/supabase/supabase-client-client"; export default function DashboardPage() { const router = useRouter(); @@ -15,13 +15,13 @@ export default function DashboardPage() { useEffect(() => { if (!isAuthLoading && !user) { - router.push("/") + router.push("/"); } - }, [isAuthLoading, user, router]) + }, [isAuthLoading, user, router]); const handleRefresh = async () => { await supabase.auth.refreshSession(); - } + }; const handleLogout = async () => { try { @@ -32,34 +32,123 @@ export default function DashboardPage() { } catch (error) { setIsLoading(false); - console.error("Logout failed:", error) + console.error("Logout failed:", error); } + }; + + if (isAuthLoading || !user) { + return ( +
+
+
+
+

Loading dashboard...

+
+
+
+ ); } - if (isAuthLoading || !user) return
Loading user data...
; - if (isLoading) return
Signing out...
; + if (isLoading) { + return ( +
+
+
+
+

Signing out...

+
+
+
+ ); + } return ( -
-
-

Dashboard

- +
+ - -
-

Welcome, user with ID: {user.id}

- +
+
+
+
+ + + +
+
+

+ User Profile +

+

ID: {user.id}

+ {user.email && ( +

Email: {user.email}

+ )} +
+
+
+ +
+

+ API Interactions +

+ +
+
- ) + ); } - diff --git a/app/globals.css b/app/globals.css index ce8bf91..f1d8c73 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,3 +1 @@ -body { - font-family: Arial, Helvetica, sans-serif; -} \ No newline at end of file +@import "tailwindcss"; diff --git a/app/layout.tsx b/app/layout.tsx index 7e8db0b..d4d2b1b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,14 +1,15 @@ -"use client" +"use client"; -import { trpc } from "@/client/utils/trpc/trpc-client" +import "./globals.css"; + +import { trpc } from "@/client/utils/trpc/trpc-client"; function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} - ) + ); } -export default trpc.withTRPC(RootLayout) - +export default trpc.withTRPC(RootLayout); diff --git a/app/page.tsx b/app/page.tsx index 3fce276..e2330f3 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,9 +1,9 @@ -"use client" +"use client"; -import { useEffect, useState } from "react" -import { useRouter } from "next/navigation" -import { trpc } from "@/client/utils/trpc/trpc-client" -import { useAuth } from "@/client/hooks/useAuth" +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import { trpc } from "@/client/utils/trpc/trpc-client"; +import { useAuth } from "@/client/hooks/useAuth"; export default function Login() { const router = useRouter(); @@ -15,44 +15,87 @@ export default function Login() { try { setIsLoading(true); - const { url } = await loginMutation.mutateAsync({ authProviderType: "GOOGLE" }); + const { url } = await loginMutation.mutateAsync({ + authProviderType: "GOOGLE", + }); if (url) { // Redirect to Google's OAuth page - window.location.href = url + window.location.href = url; } else { - console.error("No URL returned from authenticate") + console.error("No URL returned from authenticate"); } } catch (error) { - console.error("Google sign-in failed:", error) + console.error("Google sign-in failed:", error); setIsLoading(false); } - } + }; useEffect(() => { if (user) { - router.push("/dashboard") + router.push("/dashboard"); } - }, [user, router]) - + }, [user, router]); - if (isAuthLoading || isLoading || user) return
Authenticating...
+ if (isAuthLoading || isLoading || user) { + return ( +
+
+
+
+

Authenticating...

+
+
+
+ ); + } return (
-

Welcome to Our App

+
+
+

+ Welcome Back +

+

Sign in to access your account

+
- - - { loginMutation.error ? (

{ loginMutation.error.message }

) : null } + + {loginMutation.error && ( +
+

+ {loginMutation.error.message} +

+
+ )} +
- ) + ); } - diff --git a/client/components/ProtectedApiInteraction.tsx b/client/components/ProtectedApiInteraction.tsx index 70c9bd0..eb70cf6 100644 --- a/client/components/ProtectedApiInteraction.tsx +++ b/client/components/ProtectedApiInteraction.tsx @@ -1,34 +1,133 @@ -"use client" +"use client"; -import { getAuthToken, trpc } from "@/client/utils/trpc/trpc-client" +import { getAuthToken, trpc } from "@/client/utils/trpc/trpc-client"; import { jwtDecode } from "jwt-decode"; +import { useState } from "react"; export function ProtectedApiInteraction() { - const { data, isLoading, error } = trpc.debugSession.useQuery() + const { data, isLoading, error, refetch } = trpc.debugSession.useQuery(); + const [activeTab, setActiveTab] = useState<"jwt" | "user" | "session">("jwt"); if (isLoading) { - return
Loading...
+ return ( +
+
+
+ ); } if (error) { - return
{ error.message }
+ return ( +
+
+ + + + {error.message} +
+
+ ); } + const TabButton = ({ + tab, + label, + }: { + tab: typeof activeTab; + label: string; + }) => ( + + ); + + const renderContent = () => { + let content; + switch (activeTab) { + case "jwt": + content = jwtDecode(getAuthToken() || ""); + break; + case "user": + content = data?.user; + break; + case "session": + content = data?.session; + break; + } + + return ( +
+
+          {JSON.stringify(content, null, 2)}
+        
+
+ ); + }; + return ( -
- {data && ( -
-

JWT Token

-
{JSON.stringify(jwtDecode(getAuthToken() || ""), null, "  ")}
+
+
+
+ + + +
-

auth.users

-
{JSON.stringify(data.user, null, "  ")}
+ +
-

Session

-
{JSON.stringify(data.session, null, "  ")}
+
+
+

+ {activeTab === "jwt" && "JWT Token Details"} + {activeTab === "user" && "User Information"} + {activeTab === "session" && "Session Details"} +

+

+ {activeTab === "jwt" && "Decoded JWT token content"} + {activeTab === "user" && "Current user data from auth.users"} + {activeTab === "session" && "Active session information"} +

- )} + + {renderContent()} +
- ) + ); } - diff --git a/package.json b/package.json index 4c65107..5da3ac9 100644 --- a/package.json +++ b/package.json @@ -37,13 +37,16 @@ "zod": "^3.24.1" }, "devDependencies": { + "@tailwindcss/postcss": "^4.0.9", "@types/jsonwebtoken": "^9.0.7", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "14.2.16", + "postcss": "^8.5.3", "prisma": "6.2.1", + "tailwindcss": "^4.0.9", "ts-node": "^10.9.2", "tsup": "^8.3.6", "typescript": "^5.7.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee97c51..a0b0a20 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,6 +60,9 @@ importers: specifier: ^3.24.1 version: 3.24.2 devDependencies: + '@tailwindcss/postcss': + specifier: ^4.0.9 + version: 4.0.9 '@types/jsonwebtoken': specifier: ^9.0.7 version: 9.0.9 @@ -78,21 +81,31 @@ importers: eslint-config-next: specifier: 14.2.16 version: 14.2.16(eslint@8.57.1)(typescript@5.8.2) + postcss: + specifier: ^8.5.3 + version: 8.5.3 prisma: specifier: 6.2.1 version: 6.2.1 + tailwindcss: + specifier: ^4.0.9 + version: 4.0.9 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.17.23)(typescript@5.8.2) tsup: specifier: ^8.3.6 - version: 8.4.0(postcss@8.4.31)(typescript@5.8.2) + version: 8.4.0(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.2) typescript: specifier: ^5.7.3 version: 5.8.2 packages: + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -541,6 +554,82 @@ packages: '@swc/helpers@0.5.5': resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + '@tailwindcss/node@4.0.9': + resolution: {integrity: sha512-tOJvdI7XfJbARYhxX+0RArAhmuDcczTC46DGCEziqxzzbIaPnfYaIyRT31n4u8lROrsO7Q6u/K9bmQHL2uL1bQ==} + + '@tailwindcss/oxide-android-arm64@4.0.9': + resolution: {integrity: sha512-YBgy6+2flE/8dbtrdotVInhMVIxnHJPbAwa7U1gX4l2ThUIaPUp18LjB9wEH8wAGMBZUb//SzLtdXXNBHPUl6Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.0.9': + resolution: {integrity: sha512-pWdl4J2dIHXALgy2jVkwKBmtEb73kqIfMpYmcgESr7oPQ+lbcQ4+tlPeVXaSAmang+vglAfFpXQCOvs/aGSqlw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.0.9': + resolution: {integrity: sha512-4Dq3lKp0/C7vrRSkNPtBGVebEyWt9QPPlQctxJ0H3MDyiQYvzVYf8jKow7h5QkWNe8hbatEqljMj/Y0M+ERYJg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.0.9': + resolution: {integrity: sha512-k7U1RwRODta8x0uealtVt3RoWAWqA+D5FAOsvVGpYoI6ObgmnzqWW6pnVwz70tL8UZ/QXjeMyiICXyjzB6OGtQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.9': + resolution: {integrity: sha512-NDDjVweHz2zo4j+oS8y3KwKL5wGCZoXGA9ruJM982uVJLdsF8/1AeKvUwKRlMBpxHt1EdWJSAh8a0Mfhl28GlQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.9': + resolution: {integrity: sha512-jk90UZ0jzJl3Dy1BhuFfRZ2KP9wVKMXPjmCtY4U6fF2LvrjP5gWFJj5VHzfzHonJexjrGe1lMzgtjriuZkxagg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.0.9': + resolution: {integrity: sha512-3eMjyTC6HBxh9nRgOHzrc96PYh1/jWOwHZ3Kk0JN0Kl25BJ80Lj9HEvvwVDNTgPg154LdICwuFLuhfgH9DULmg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.0.9': + resolution: {integrity: sha512-v0D8WqI/c3WpWH1kq/HP0J899ATLdGZmENa2/emmNjubT0sWtEke9W9+wXeEoACuGAhF9i3PO5MeyditpDCiWQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.0.9': + resolution: {integrity: sha512-Kvp0TCkfeXyeehqLJr7otsc4hd/BUPfcIGrQiwsTVCfaMfjQZCG7DjI+9/QqPZha8YapLA9UoIcUILRYO7NE1Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.9': + resolution: {integrity: sha512-m3+60T/7YvWekajNq/eexjhV8z10rswcz4BC9bioJ7YaN+7K8W2AmLmG0B79H14m6UHE571qB0XsPus4n0QVgQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.0.9': + resolution: {integrity: sha512-dpc05mSlqkwVNOUjGu/ZXd5U1XNch1kHFJ4/cHkZFvaW1RzbHmRt24gvM8/HC6IirMxNarzVw4IXVtvrOoZtxA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.0.9': + resolution: {integrity: sha512-eLizHmXFqHswJONwfqi/WZjtmWZpIalpvMlNhTM99/bkHtUs6IqgI1XQ0/W5eO2HiRQcIlXUogI2ycvKhVLNcA==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.0.9': + resolution: {integrity: sha512-BT/E+pdMqulavEAVM5NCpxmGEwHiLDPpkmg/c/X25ZBW+izTe+aZ+v1gf/HXTrihRoCxrUp5U4YyHsBTzspQKQ==} + '@tanstack/query-core@4.36.1': resolution: {integrity: sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==} @@ -936,6 +1025,11 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -1426,6 +1520,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -1482,6 +1580,70 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-darwin-arm64@1.29.1: + resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.29.1: + resolution: {integrity: sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.1: + resolution: {integrity: sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.1: + resolution: {integrity: sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.1: + resolution: {integrity: sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.1: + resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.1: + resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.1: + resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.1: + resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.1: + resolution: {integrity: sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.1: + resolution: {integrity: sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==} + engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -1713,6 +1875,10 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -1960,6 +2126,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tailwindcss@4.0.9: + resolution: {integrity: sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==} + tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -2163,6 +2332,8 @@ packages: snapshots: + '@alloc/quick-lru@5.2.0': {} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -2499,6 +2670,68 @@ snapshots: '@swc/counter': 0.1.3 tslib: 2.8.1 + '@tailwindcss/node@4.0.9': + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + tailwindcss: 4.0.9 + + '@tailwindcss/oxide-android-arm64@4.0.9': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.0.9': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.0.9': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.0.9': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.0.9': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.9': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.0.9': + optional: true + + '@tailwindcss/oxide@4.0.9': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.0.9 + '@tailwindcss/oxide-darwin-arm64': 4.0.9 + '@tailwindcss/oxide-darwin-x64': 4.0.9 + '@tailwindcss/oxide-freebsd-x64': 4.0.9 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.9 + '@tailwindcss/oxide-linux-arm64-gnu': 4.0.9 + '@tailwindcss/oxide-linux-arm64-musl': 4.0.9 + '@tailwindcss/oxide-linux-x64-gnu': 4.0.9 + '@tailwindcss/oxide-linux-x64-musl': 4.0.9 + '@tailwindcss/oxide-win32-arm64-msvc': 4.0.9 + '@tailwindcss/oxide-win32-x64-msvc': 4.0.9 + + '@tailwindcss/postcss@4.0.9': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.0.9 + '@tailwindcss/oxide': 4.0.9 + lightningcss: 1.29.1 + postcss: 8.5.3 + tailwindcss: 4.0.9 + '@tanstack/query-core@4.36.1': {} '@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -2920,6 +3153,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + detect-libc@1.0.3: {} + diff@4.0.2: {} doctrine@2.1.0: @@ -3610,6 +3845,8 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jiti@2.4.2: {} + joycon@3.1.1: {} js-tokens@4.0.0: {} @@ -3676,6 +3913,51 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-darwin-arm64@1.29.1: + optional: true + + lightningcss-darwin-x64@1.29.1: + optional: true + + lightningcss-freebsd-x64@1.29.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.1: + optional: true + + lightningcss-linux-arm64-gnu@1.29.1: + optional: true + + lightningcss-linux-arm64-musl@1.29.1: + optional: true + + lightningcss-linux-x64-gnu@1.29.1: + optional: true + + lightningcss-linux-x64-musl@1.29.1: + optional: true + + lightningcss-win32-arm64-msvc@1.29.1: + optional: true + + lightningcss-win32-x64-msvc@1.29.1: + optional: true + + lightningcss@1.29.1: + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.1 + lightningcss-darwin-x64: 1.29.1 + lightningcss-freebsd-x64: 1.29.1 + lightningcss-linux-arm-gnueabihf: 1.29.1 + lightningcss-linux-arm64-gnu: 1.29.1 + lightningcss-linux-arm64-musl: 1.29.1 + lightningcss-linux-x64-gnu: 1.29.1 + lightningcss-linux-x64-musl: 1.29.1 + lightningcss-win32-arm64-msvc: 1.29.1 + lightningcss-win32-x64-msvc: 1.29.1 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -3869,11 +4151,12 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-load-config@6.0.1(postcss@8.4.31): + postcss-load-config@6.0.1(jiti@2.4.2)(postcss@8.5.3): dependencies: lilconfig: 3.1.3 optionalDependencies: - postcss: 8.4.31 + jiti: 2.4.2 + postcss: 8.5.3 postcss@8.4.31: dependencies: @@ -3881,6 +4164,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.3: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} prisma@6.2.1: @@ -4188,6 +4477,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tailwindcss@4.0.9: {} + tapable@2.2.1: {} text-table@0.2.0: {} @@ -4252,7 +4543,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.4.0(postcss@8.4.31)(typescript@5.8.2): + tsup@8.4.0(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.2): dependencies: bundle-require: 5.1.0(esbuild@0.25.0) cac: 6.7.14 @@ -4262,7 +4553,7 @@ snapshots: esbuild: 0.25.0 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.4.31) + postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.5.3) resolve-from: 5.0.0 rollup: 4.34.9 source-map: 0.8.0-beta.0 @@ -4271,7 +4562,7 @@ snapshots: tinyglobby: 0.2.12 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.4.31 + postcss: 8.5.3 typescript: 5.8.2 transitivePeerDependencies: - jiti diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..7059fe9 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,6 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; +export default config; From 410233cc4a5026969e2efddc84ac30eb4797280c Mon Sep 17 00:00:00 2001 From: Pawan Paudel Date: Wed, 5 Mar 2025 17:58:59 +0545 Subject: [PATCH 02/10] feat: dashboard initial working version --- app/dashboard/page.tsx | 682 ++++++++++++++++++++++++++------ app/dashboard/settings/page.tsx | 52 +++ app/layout.tsx | 71 +++- client/components/Header.tsx | 107 +++++ package.json | 2 + pnpm-lock.yaml | 24 ++ server/routers/_app.ts | 2 + server/routers/dashboard.ts | 237 +++++++++++ 8 files changed, 1061 insertions(+), 116 deletions(-) create mode 100644 app/dashboard/settings/page.tsx create mode 100644 client/components/Header.tsx create mode 100644 server/routers/dashboard.ts diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 354d4e9..fbf104d 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,154 +1,606 @@ "use client"; -import { useEffect, useState } from "react"; +import { useState, useCallback } from "react"; import { useRouter } from "next/navigation"; import { trpc } from "@/client/utils/trpc/trpc-client"; -import { ProtectedApiInteraction } from "../../client/components/ProtectedApiInteraction"; -import { useAuth } from "@/client/hooks/useAuth"; -import { supabase } from "@/client/utils/supabase/supabase-client-client"; +import { toast } from "sonner"; -export default function DashboardPage() { - const router = useRouter(); - const { user, isLoading: isAuthLoading } = useAuth(); - const logoutMutation = trpc.logout.useMutation(); - const [isLoading, setIsLoading] = useState(false); +type ActiveView = "teams" | "applications"; - useEffect(() => { - if (!isAuthLoading && !user) { - router.push("/"); - } - }, [isAuthLoading, user, router]); +export default function DashboardPage() { + const [activeView, setActiveView] = useState("teams"); + const { data: stats } = trpc.getStats.useQuery(); - const handleRefresh = async () => { - await supabase.auth.refreshSession(); - }; + const NavIcon = ({ children }: { children: React.ReactNode }) => ( +
{children}
+ ); - const handleLogout = async () => { - try { - setIsLoading(true); + const SideNavItem = ({ + view, + label, + icon, + }: { + view: ActiveView; + label: string; + icon: React.ReactNode; + }) => ( + + ); - await logoutMutation.mutateAsync(); - await supabase.auth.signOut(); - } catch (error) { - setIsLoading(false); + const renderViewHeader = () => { + const headers = { + teams: "Team Management", + applications: "Application Management", + }; - console.error("Logout failed:", error); - } + return ( +
+

+ {headers[activeView]} +

+
+ ); }; - if (isAuthLoading || !user) { - return ( -
-
-
-
-

Loading dashboard...

-
+ return ( +
+
+
+ {/* Sidebar Navigation */} + + + {/* Main Content Area */} +
+
+ {renderViewHeader()} +
+ {activeView === "teams" && } + {activeView === "applications" && } +
+
+
- ); - } +
+ ); +} + +const inputStyles = + "mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-base py-2.5 px-3"; +const selectStyles = + "mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-base py-2.5 px-3"; +const textareaStyles = + "mt-1 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-base py-2.5 px-3"; + +function TeamsView() { + const router = useRouter(); + const utils = trpc.useUtils(); + const createTeamMutation = trpc.createTeam.useMutation({ + onSuccess: () => { + toast.success("Team created successfully"); + utils.listTeams.invalidate(); + }, + onError: (error) => { + toast.error(error.message || "Failed to create team"); + }, + }); - if (isLoading) { + const { data: teams, isLoading: isLoadingTeams } = trpc.listTeams.useQuery(); + const [showCreateForm, setShowCreateForm] = useState(false); + const [newTeam, setNewTeam] = useState({ + name: "", + slug: "", + }); + + const handleCreateTeam = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + // Show loading toast + const loadingToast = toast.loading("Creating team..."); + + try { + const slug = newTeam.name + .toLowerCase() + .replace(/[^a-z0-9-]/g, "-") + .replace(/-+/g, "-") + .replace(/^-|-$/g, ""); + + await createTeamMutation.mutateAsync({ + name: newTeam.name, + slug: slug, + }); + + // Dismiss loading toast and show success + toast.dismiss(loadingToast); + toast.success("Team created successfully!"); + + setNewTeam({ name: "", slug: "" }); + setShowCreateForm(false); + } catch (error) { + // Dismiss loading toast and show error + toast.dismiss(loadingToast); + toast.error(error.message || "Failed to create team"); + } + }, + [newTeam, createTeamMutation] + ); + + if (isLoadingTeams) { return ( -
-
-
-
-

Signing out...

-
-
+
+
Loading teams...
); } return ( -
- - -
-
-
-
- + ); + } + + return ( +
+
+

Applications

+ +
+ + {showCreateForm && ( +
+
+
+ + setNewApp({ ...newApp, name: e.target.value })} + className={inputStyles} + required + placeholder="Enter application name" + />
+
-

- User Profile -

-

ID: {user.id}

- {user.email && ( -

Email: {user.email}

- )} + +