diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json new file mode 100644 index 0000000..9e5a22b --- /dev/null +++ b/frontend/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "WillLuke.nextjs.addTypesOnSave": true, + "WillLuke.nextjs.hasPrompted": true +} diff --git a/frontend/app/api/auth/[...nextauth]/authOptions.ts b/frontend/app/api/auth/[...nextauth]/authOptions.ts index 5062f56..44b72bc 100644 --- a/frontend/app/api/auth/[...nextauth]/authOptions.ts +++ b/frontend/app/api/auth/[...nextauth]/authOptions.ts @@ -39,9 +39,9 @@ export const authOptions: NextAuthOptions = { params: { prompt: "consent", access_type: "offline", - response_type: "code" - } - } + response_type: "code", + }, + }, }), DiscordProvider({ clientId: process.env.DISCORD_CLIENT_ID as string, @@ -69,13 +69,13 @@ export const authOptions: NextAuthOptions = { // }; // }, authorization: { - params: { scope: 'openid profile email' }, + params: { scope: "openid profile email" }, }, - issuer: 'https://www.linkedin.com', - jwks_endpoint: 'https://www.linkedin.com/oauth/openid/jwks', + issuer: "https://www.linkedin.com", + jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks", profile(profile, tokens) { const defaultImage = - 'https://cdn-icons-png.flaticon.com/512/174/174857.png'; + "https://cdn-icons-png.flaticon.com/512/174/174857.png"; return { id: profile.sub, name: profile.name, @@ -94,8 +94,8 @@ export const authOptions: NextAuthOptions = { // // Find your user in the database using MongoDBAdapter // const client = await clientPromise; // const users = client.db("auth").collection("users"); - - // // Find user with the email + + // // Find user with the email // const result = await users.findOne({ // email: credentials?.email, // }); @@ -148,4 +148,4 @@ export const authOptions: NextAuthOptions = { return session; }, }, -}; \ No newline at end of file +}; diff --git a/frontend/app/api/auth/[...nextauth]/route.ts b/frontend/app/api/auth/[...nextauth]/route.ts index 966a375..a4735df 100644 --- a/frontend/app/api/auth/[...nextauth]/route.ts +++ b/frontend/app/api/auth/[...nextauth]/route.ts @@ -4,4 +4,3 @@ import { authOptions } from "./authOptions"; const handler = NextAuth(authOptions); export { handler as GET, handler as POST }; - diff --git a/frontend/app/api/user/route.ts b/frontend/app/api/user/route.ts index 946098e..22b4d35 100644 --- a/frontend/app/api/user/route.ts +++ b/frontend/app/api/user/route.ts @@ -1,28 +1,35 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import prisma from '@/lib/prisma'; // adjust the import path as needed -import { getSession, useSession } from 'next-auth/react' -import { NextRequest } from 'next/server'; -import useSWR from 'swr'; +import type { NextApiRequest, NextApiResponse } from "next"; +import prisma from "@/lib/prisma"; // adjust the import path as needed +import { getSession, useSession } from "next-auth/react"; +import { NextRequest } from "next/server"; +import useSWR from "swr"; -const fetcher = (url: string) => fetch(url, { next: { revalidate: 60 }}).then((res) => res.json()) +const fetcher = (url: string) => + fetch(url, { next: { revalidate: 60 } }).then((res) => res.json()); export async function GET(request: NextRequest) { - const session = request.nextUrl.searchParams.get('userId') - const { data } = useSWR(`/api/auth/session`, fetcher); - console.log(data) - + const userId = request.nextUrl.searchParams.get("userId"); + const email = request.nextUrl.searchParams.get("email"); + const name = request.nextUrl.searchParams.get("name"); + + const session = { userId: userId, name: name, email: email }; + try { if (session) { - const provRaw = await prisma.account.findMany({ select: { provider: true } }, { where: {id: session }}) - - const provArray = provRaw.map((prov: {provider: string}) => { - return prov.provider - }) - return Response.json(provArray); + const provRaw = await prisma.account.findMany( + { select: { provider: true } }, + { where: { id: session.userId } } + ); + + const provArray = provRaw.map((prov: { provider: string }) => { + return prov.provider; + }); + + return Response.json({ name: name, providers: provArray, email: email }); } else { - return Response.json({ message: 'Unauthorized' }) + return Response.json(session); } } catch (error) { - return Response.json({ message: 'Server error' }); + return Response.json({ message: "Server error" }); } -} \ No newline at end of file +} diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx index 6717a5c..2ffd9fb 100644 --- a/frontend/app/dashboard/page.tsx +++ b/frontend/app/dashboard/page.tsx @@ -14,28 +14,34 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, -} from "@/components/ui/tabs"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { redirect } from "next/navigation"; -import { Activity, Book, CalendarDays, Clock4, FileText, ShieldAlert, TableProperties } from "lucide-react"; +import { + Activity, + Book, + CalendarDays, + Clock4, + FileText, + ShieldAlert, + TableProperties, +} from "lucide-react"; export const metadata: Metadata = { title: "Dashboard", description: "Example dashboard app built using the components.", -} +}; export default async function DashboardPage() { const session = await getServerSession(authOptions); - if (!session) { redirect('/api/auth/signin') } + if (!session) { + redirect("/api/auth/signin"); + } - return (session && ( -
-
- {/*
+ return ( + session && ( +
+
+ {/*
@@ -45,112 +51,120 @@ export default async function DashboardPage() {
*/} -
-
-

Marketplace Listings Dashboard

-
- - +
+
+

+ Marketplace Listings Dashboard +

+
+ + +
+ + + Overview + + Analytics + + + Reports + + + Notifications + + + Search + + + +
+ + + + Total Listings + + + + +
286
+

+ +1098% from last month +

+
+
+ + + + Flagged Listings + + + + +
+0
+

+ +0% from last month +

+
+
+ + + + New Listings + + + + +
+243
+

+ +876% from last month +

+
+
+ + + + Listings Today + + + + +
+36
+

+ +36 from last 24 hours +

+
+
+
+
+ + + Overview + + + + + + + + Recent Listings + + 8 listings flagged in the last 24 hours + + + + + + +
+
+
- - - Overview - - Analytics - - - Reports - - - Notifications - - - Search - - - -
- - - - Total Listings - - - - -
286
-

- +1098% from last month -

-
-
- - - - Flagged Listings - - - - -
+0
-

- +0% from last month -

-
-
- - - New Listings - - - -
+243
-

- +876% from last month -

-
-
- - - - Listings Today - - - - -
+36
-

- +36 from last 24 hours -

-
-
-
-
- - - Overview - - - - - - - - Recent Listings - - 8 listings flagged in the last 24 hours - - - - - - -
-
-
-
- )) -} \ No newline at end of file + ) + ); +} diff --git a/frontend/app/error.tsx b/frontend/app/error.tsx index dcb5f93..189192f 100644 --- a/frontend/app/error.tsx +++ b/frontend/app/error.tsx @@ -1,4 +1,4 @@ -'use client' // Error components must be Client Components +"use client"; // Error components must be Client Components import Meta, { defaultMetaProps } from "@/components/layout/meta"; import { useEffect } from "react"; diff --git a/frontend/app/globals.css b/frontend/app/globals.css index f994691..7c1aa0a 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -1,7 +1,7 @@ @tailwind base; @tailwind components; @tailwind utilities; - + @layer base { :root { --background: 0 0% 100%; @@ -9,87 +9,89 @@ --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; - font-feature-settings: "rlig" 1, "calt" 1; - } - } - - @layer utilities { - .step { - counter-increment: step; - } - - .step:before { - @apply absolute w-9 h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 border-background; - @apply ml-[-50px] mt-[-4px]; - content: counter(step); - } - } - - @media (max-width: 640px) { - .container { - @apply px-4; + font-feature-settings: + "rlig" 1, + "calt" 1; + } +} + +@layer utilities { + .step { + counter-increment: step; } -} \ No newline at end of file + + .step:before { + @apply absolute w-9 h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 border-background; + @apply ml-[-50px] mt-[-4px]; + content: counter(step); + } +} + +@media (max-width: 640px) { + .container { + @apply px-4; + } +} diff --git a/frontend/app/middleware.ts b/frontend/app/middleware.ts index a43667a..51c0aec 100644 --- a/frontend/app/middleware.ts +++ b/frontend/app/middleware.ts @@ -1,14 +1,14 @@ -import type { NextRequest } from 'next/server'; +import type { NextRequest } from "next/server"; export function middleware(request: NextRequest) { - if (request.nextUrl.pathname.startsWith('/profile')) { - // Add /profile specific logics + if (request.nextUrl.pathname.startsWith("/settings")) { + // Add /settings specific logics } - if (request.nextUrl.pathname.startsWith('/dashboard')) { + if (request.nextUrl.pathname.startsWith("/dashboard")) { // Add /dashboard specific logics } } export const config = { - matcher: ['/profile/:path*', '/dashboard/:path*'], -} \ No newline at end of file + matcher: ["/settings/:path*", "/dashboard/:path*"], +}; diff --git a/frontend/app/not-found.tsx b/frontend/app/not-found.tsx index bed838b..7d81d66 100644 --- a/frontend/app/not-found.tsx +++ b/frontend/app/not-found.tsx @@ -1,8 +1,8 @@ -"use client" +"use client"; import Meta, { defaultMetaProps } from "@/components/layout/meta"; import { ArrowBigLeftDash, ArrowLeft } from "lucide-react"; -import { useRouter } from 'next/navigation'; +import { useRouter } from "next/navigation"; export default function NotFound() { const router = useRouter(); @@ -17,13 +17,20 @@ export default function NotFound() { }} />

- Not Found | Could not find requested resource + Not Found | Could not find + requested resource

- -
+
); } diff --git a/frontend/app/opengraph-image.tsx b/frontend/app/opengraph-image.tsx index 767e051..caa0198 100644 --- a/frontend/app/opengraph-image.tsx +++ b/frontend/app/opengraph-image.tsx @@ -7,8 +7,8 @@ export const contentType = "image/png"; export default async function OG() { const sfPro = await fetch( - new URL("./fonts/SF-Pro-Display-Medium.otf", import.meta.url), - ).then((res) => res.arrayBuffer()) + new URL("./fonts/SF-Pro-Display-Medium.otf", import.meta.url) + ).then((res) => res.arrayBuffer()); return new ImageResponse( ( @@ -26,7 +26,9 @@ export default async function OG() { }} > SMARE Logo @@ -55,6 +57,6 @@ export default async function OG() { data: sfPro, }, ], - }, + } ); } diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index cf43c0a..b1110e4 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -30,7 +30,11 @@ export default async function Home() { className="group flex max-w-fit items-center justify-center space-x-2 rounded-full border border-statefarm bg-statefarm px-5 py-2 text-sm text-white transition-colors hover:bg-white hover:text-statefarm" href="/dashboard" > - +

View Dashboard

+type AccountFormValues = z.infer; // This can come from your database or API. const defaultValues: Partial = { // name: "Your name", // dob: new Date("2023-01-23"), -} +}; export function AccountForm() { const form = useForm({ resolver: zodResolver(accountFormSchema), defaultValues, - }) + }); function onSubmit(data: AccountFormValues) { toast({ @@ -84,7 +84,7 @@ export function AccountForm() { {JSON.stringify(data, null, 2)} ), - }) + }); } return ( @@ -187,7 +187,7 @@ export function AccountForm() { value={language.label} key={language.value} onSelect={() => { - form.setValue("language", language.value) + form.setValue("language", language.value); }} > Update account - ) + ); } diff --git a/frontend/app/settings/account/page.tsx b/frontend/app/settings/account/page.tsx index 40521ab..9ac8512 100644 --- a/frontend/app/settings/account/page.tsx +++ b/frontend/app/settings/account/page.tsx @@ -1,5 +1,5 @@ -import { Separator } from "@/components/ui/separator" -import { AccountForm } from "@/app/settings/account/account-form" +import { Separator } from "@/components/ui/separator"; +import { AccountForm } from "@/app/settings/account/account-form"; export default function SettingsAccountPage() { return ( @@ -14,5 +14,5 @@ export default function SettingsAccountPage() {
- ) + ); } diff --git a/frontend/app/settings/appearance/appearance-form.tsx b/frontend/app/settings/appearance/appearance-form.tsx index 2bf99ac..a0f22e1 100644 --- a/frontend/app/settings/appearance/appearance-form.tsx +++ b/frontend/app/settings/appearance/appearance-form.tsx @@ -1,12 +1,12 @@ -"use client" +"use client"; -import { zodResolver } from "@hookform/resolvers/zod" -import { ChevronDownIcon } from "@radix-ui/react-icons" -import { useForm } from "react-hook-form" -import * as z from "zod" +import { zodResolver } from "@hookform/resolvers/zod"; +import { ChevronDownIcon } from "@radix-ui/react-icons"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; -import { cn } from "@/lib/utils" -import { Button, buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils"; +import { Button, buttonVariants } from "@/components/ui/button"; import { Form, FormControl, @@ -15,9 +15,9 @@ import { FormItem, FormLabel, FormMessage, -} from "@/components/ui/form" -import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" -import { toast } from "@/components/ui/use-toast" +} from "@/components/ui/form"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { toast } from "@/components/ui/use-toast"; const appearanceFormSchema = z.object({ theme: z.enum(["light", "dark"], { @@ -27,20 +27,20 @@ const appearanceFormSchema = z.object({ invalid_type_error: "Select a font", required_error: "Please select a font.", }), -}) +}); -type AppearanceFormValues = z.infer +type AppearanceFormValues = z.infer; // This can come from your database or API. const defaultValues: Partial = { theme: "light", -} +}; export function AppearanceForm() { const form = useForm({ resolver: zodResolver(appearanceFormSchema), defaultValues, - }) + }); function onSubmit(data: AppearanceFormValues) { toast({ @@ -50,7 +50,7 @@ export function AppearanceForm() { {JSON.stringify(data, null, 2)} ), - }) + }); } return ( @@ -160,5 +160,5 @@ export function AppearanceForm() { - ) + ); } diff --git a/frontend/app/settings/appearance/page.tsx b/frontend/app/settings/appearance/page.tsx index 72a7e09..7a46b4d 100644 --- a/frontend/app/settings/appearance/page.tsx +++ b/frontend/app/settings/appearance/page.tsx @@ -1,5 +1,5 @@ -import { Separator } from "@/components/ui/separator" -import { AppearanceForm } from "@/app/settings/appearance/appearance-form" +import { Separator } from "@/components/ui/separator"; +import { AppearanceForm } from "@/app/settings/appearance/appearance-form"; export default function SettingsAppearancePage() { return ( @@ -14,5 +14,5 @@ export default function SettingsAppearancePage() {
- ) + ); } diff --git a/frontend/app/settings/components/sidebar-nav.tsx b/frontend/app/settings/components/sidebar-nav.tsx index addcfef..1348309 100644 --- a/frontend/app/settings/components/sidebar-nav.tsx +++ b/frontend/app/settings/components/sidebar-nav.tsx @@ -1,20 +1,20 @@ -"use client" +"use client"; -import Link from "next/link" -import { usePathname } from "next/navigation" +import Link from "next/link"; +import { usePathname } from "next/navigation"; -import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils"; +import { buttonVariants } from "@/components/ui/button"; interface SidebarNavProps extends React.HTMLAttributes { items: { - href: string - title: string - }[] + href: string; + title: string; + }[]; } export function SidebarNav({ className, items, ...props }: SidebarNavProps) { - const pathname = usePathname() + const pathname = usePathname(); return ( - ) + ); } diff --git a/frontend/app/settings/layout.tsx b/frontend/app/settings/layout.tsx index b169b51..bc8b507 100644 --- a/frontend/app/settings/layout.tsx +++ b/frontend/app/settings/layout.tsx @@ -1,13 +1,13 @@ -import { Metadata } from "next" -import Image from "next/image" +import { Metadata } from "next"; +import Image from "next/image"; -import { Separator } from "@/components/ui/separator" -import { SidebarNav } from "@/app/settings/components/sidebar-nav" +import { Separator } from "@/components/ui/separator"; +import { SidebarNav } from "@/app/settings/components/sidebar-nav"; export const metadata: Metadata = { title: "Forms", description: "Advanced form example using react-hook-form and Zod.", -} +}; const sidebarNavItems = [ { @@ -22,31 +22,15 @@ const sidebarNavItems = [ title: "Notifications", href: "/settings/notifications", }, -] +]; interface SettingsLayoutProps { - children: React.ReactNode + children: React.ReactNode; } export default function SettingsLayout({ children }: SettingsLayoutProps) { return (
-
- Forms - Forms -

Settings

@@ -63,5 +47,5 @@ export default function SettingsLayout({ children }: SettingsLayoutProps) {
- ) + ); } diff --git a/frontend/app/settings/notifications/notifications-form.tsx b/frontend/app/settings/notifications/notifications-form.tsx index ec81554..1e2fea1 100644 --- a/frontend/app/settings/notifications/notifications-form.tsx +++ b/frontend/app/settings/notifications/notifications-form.tsx @@ -1,12 +1,12 @@ -"use client" +"use client"; -import Link from "next/link" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import * as z from "zod" +import Link from "next/link"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; -import { Button } from "@/components/ui/button" -import { Checkbox } from "@/components/ui/checkbox" +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; import { Form, FormControl, @@ -15,10 +15,10 @@ import { FormItem, FormLabel, FormMessage, -} from "@/components/ui/form" -import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" -import { Switch } from "@/components/ui/switch" -import { toast } from "@/components/ui/use-toast" +} from "@/components/ui/form"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { Switch } from "@/components/ui/switch"; +import { toast } from "@/components/ui/use-toast"; const notificationsFormSchema = z.object({ type: z.enum(["all", "mentions", "none"], { @@ -29,9 +29,9 @@ const notificationsFormSchema = z.object({ social_emails: z.boolean().default(false).optional(), marketing_emails: z.boolean().default(false).optional(), security_emails: z.boolean(), -}) +}); -type NotificationsFormValues = z.infer +type NotificationsFormValues = z.infer; // This can come from your database or API. const defaultValues: Partial = { @@ -39,13 +39,13 @@ const defaultValues: Partial = { marketing_emails: false, social_emails: true, security_emails: true, -} +}; export function NotificationsForm() { const form = useForm({ resolver: zodResolver(notificationsFormSchema), defaultValues, - }) + }); function onSubmit(data: NotificationsFormValues) { toast({ @@ -55,7 +55,7 @@ export function NotificationsForm() { {JSON.stringify(data, null, 2)} ), - }) + }); } return ( @@ -209,7 +209,7 @@ export function NotificationsForm() { You can manage your mobile notifications in the{" "} - mobile settings page. + mobile settings page. @@ -218,5 +218,5 @@ export function NotificationsForm() { - ) + ); } diff --git a/frontend/app/settings/notifications/page.tsx b/frontend/app/settings/notifications/page.tsx index 7cecd4e..d850cde 100644 --- a/frontend/app/settings/notifications/page.tsx +++ b/frontend/app/settings/notifications/page.tsx @@ -1,5 +1,5 @@ -import { Separator } from "@/components/ui/separator" -import { NotificationsForm } from "@/app/settings/notifications/notifications-form" +import { Separator } from "@/components/ui/separator"; +import { NotificationsForm } from "@/app/settings/notifications/notifications-form"; export default function SettingsNotificationsPage() { return ( @@ -13,5 +13,5 @@ export default function SettingsNotificationsPage() { - ) + ); } diff --git a/frontend/app/settings/page.tsx b/frontend/app/settings/page.tsx index f85793b..fe57fec 100644 --- a/frontend/app/settings/page.tsx +++ b/frontend/app/settings/page.tsx @@ -1,5 +1,5 @@ -import { Separator } from "@/components/ui/separator" -import { ProfileForm } from "@/app/settings/profile-form" +import { Separator } from "@/components/ui/separator"; +import { ProfileForm } from "@/app/settings/profile-form"; export default function SettingsProfilePage() { return ( @@ -13,5 +13,5 @@ export default function SettingsProfilePage() { - ) + ); } diff --git a/frontend/app/settings/profile-form.tsx b/frontend/app/settings/profile-form.tsx index a5b5320..2298d78 100644 --- a/frontend/app/settings/profile-form.tsx +++ b/frontend/app/settings/profile-form.tsx @@ -3,13 +3,12 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import * as z from "zod"; -import { FaLinkedin } from "react-icons/fa"; -import { FaGoogle } from "react-icons/fa"; -import { FaDiscord } from "react-icons/fa"; -import { FaGithub } from "react-icons/fa"; +import { FaLinkedin, FaGoogle, FaDiscord, FaGithub } from "react-icons/fa"; +import { redirect } from "next/navigation"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; +import { Skeleton } from "@/components/ui/skeleton"; import { Form, FormControl, @@ -20,13 +19,6 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; import { Dialog, DialogClose, @@ -38,12 +30,12 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { toast } from "@/components/ui/use-toast"; -import { getProviders, getSession, signIn, useSession } from "next-auth/react"; +import { getProviders, getSession, signIn } from "next-auth/react"; import { useEffect, useState } from "react"; -import { authOptions } from "../api/auth/[...nextauth]/authOptions"; -import { redirect } from "next/navigation"; import { Session } from "next-auth"; import useSWR from "swr"; +import { is } from "date-fns/locale"; +import { Check, SendHorizontal } from "lucide-react"; const providers = [ { type: "github", icon: }, @@ -87,35 +79,64 @@ const profileFormSchema = z.object({ type ProfileFormValues = z.infer; -const fetcher = (url: string) => fetch(url, { next: { revalidate: 60 }}).then((res) => res.json()); +const fetcher = (url: string) => + fetch(url, { next: { revalidate: 60 } }).then((res) => res.json()); export function ProfileForm() { - // if (!session) { redirect('/api/auth/signin') } - const [defaultValues, setDefaultValues] = useState>({ name: "", email: "", connected_accounts: [""] }) + const { data: session, isValidating: isSessionLoading } = useSWR( + "/api/auth/session", + fetcher + ); + const { data: userData, isValidating: isUserDataLoading } = useSWR( + () => + `/api/user?userId=${session.user.id}&email=${encodeURI( + session.user.email + )}&name=${encodeURI(session.user.name)}`, + fetcher + ); + + // exit if session doesnt have a valid email if session is not loading + useEffect(() => { + if (!session?.user?.email && !isSessionLoading) { + redirect("/api/auth/signin"); + } + }, [session, isSessionLoading]); + + const [isDataReady, setIsDataReady] = useState(false); + const [defaultValues, setDefaultValues] = useState< + Partial + >({ + name: "", + email: "", + connected_accounts: [], + }); + + // set default values once userData is loaded + useEffect(() => { + if (userData && !isUserDataLoading) { + const newDefaultValues = { + name: userData.name, + email: userData.email, + connected_accounts: userData.providers, + }; + setDefaultValues(newDefaultValues); + setIsDataReady(true); + } + }, [userData, isUserDataLoading]); + // set default values once userData is loaded const form = useForm({ resolver: zodResolver(profileFormSchema), defaultValues, mode: "onChange", }); - // const { data } = useSWR(`/api/user?userId=${session}`, fetcher); useEffect(() => { - // const { data } = useSWR(`/api/user?userId=${session}`, fetcher); - const getSesh = async () => { - const provs = Object.keys(await getProviders() as Object); - const session = await getSession() as Session; - console.log(provs) - const newDefaultValues = { - name: session?.user?.name || '', - email: session?.user?.email || '', - connected_accounts: provs || [''] - }; - setDefaultValues(newDefaultValues); - form.reset(newDefaultValues); // Reset the form with new default values + if (isDataReady) { + form.reset(defaultValues); // Reset the form with new default values } - getSesh(); - }, [form]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isDataReady, defaultValues]); function onSubmit(data: ProfileFormValues) { toast({ @@ -128,10 +149,10 @@ export function ProfileForm() { }); } - return (
+ {/* NAME FIELD */} Name - + {!isDataReady ? ( + // Skeleton Loader + ) : ( + + )} This is should be your real name. This data is only used for @@ -149,6 +174,8 @@ export function ProfileForm() { )} /> + + {/* EMAIL FIELD */} Email - + {!isDataReady ? ( + // Skeleton Loader + ) : ( + + )} Your email is linked to the provider you signed in with. To sign @@ -167,85 +202,91 @@ export function ProfileForm() { )} /> + + {/* CONNECTED ACCOUNTS FIELD */}
{providers.map((provfield, index) => ( ( - URLs + Connected Accounts Add links to your website, blog, or social media profiles. - - - - - - - - Continue to{" "} - {provfield.type.charAt(0).toUpperCase() + - provfield.type.slice(1)} - ? - - - Any unsaved changes will be lost. Are you sure you - want to continue? - - - -
- + + + + + Continue to{" "} {provfield.type.charAt(0).toUpperCase() + provfield.type.slice(1)} - + ? + + + Any unsaved changes will be lost. Are you sure you + want to continue? + + + +
+ - - - -
-
-
-
+ + + +
+ + + + )} @@ -253,7 +294,7 @@ export function ProfileForm() { /> ))} - + {/* */} ); diff --git a/frontend/app/sitemap.ts b/frontend/app/sitemap.ts index ca9a9c2..2ed89e2 100644 --- a/frontend/app/sitemap.ts +++ b/frontend/app/sitemap.ts @@ -14,7 +14,7 @@ export default async function sitemap(): Promise { url: "https://smare.lryanle.com", lastModified: new Date(), }, - ...users.map((user: { id: any; }) => ({ + ...users.map((user: { id: any }) => ({ url: `https://smare.lryanle.com/${user.id}`, lastModified: new Date(), })), diff --git a/frontend/components.json b/frontend/components.json index 7681c2f..fe9dede 100644 --- a/frontend/components.json +++ b/frontend/components.json @@ -13,4 +13,4 @@ "components": "@/components", "utils": "@/lib/utils" } -} \ No newline at end of file +} diff --git a/frontend/components/dashboard/date-range-picker.tsx b/frontend/components/dashboard/date-range-picker.tsx index 832a9af..e9fb2ac 100644 --- a/frontend/components/dashboard/date-range-picker.tsx +++ b/frontend/components/dashboard/date-range-picker.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/prop-types */ -"use client" +"use client"; import { CalendarIcon } from "@radix-ui/react-icons"; import { addDays, format } from "date-fns"; @@ -21,7 +21,7 @@ export function CalendarDateRangePicker({ const [date, setDate] = React.useState({ from: new Date(2023, 9, 27), to: addDays(new Date(2023, 9, 27), 31), - }) + }); return (
@@ -62,5 +62,5 @@ export function CalendarDateRangePicker({
- ) -} \ No newline at end of file + ); +} diff --git a/frontend/components/dashboard/main-nav.tsx b/frontend/components/dashboard/main-nav.tsx index 96b2cbc..7593982 100644 --- a/frontend/components/dashboard/main-nav.tsx +++ b/frontend/components/dashboard/main-nav.tsx @@ -12,29 +12,29 @@ export function MainNav({ {...props} > Overview Customers Products Settings - ) -} \ No newline at end of file + ); +} diff --git a/frontend/components/dashboard/overview.tsx b/frontend/components/dashboard/overview.tsx index f80b188..73b940a 100644 --- a/frontend/components/dashboard/overview.tsx +++ b/frontend/components/dashboard/overview.tsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from "recharts"; @@ -11,7 +11,7 @@ const data = [ name: "Nov", total: 153, }, -] +]; export function Overview() { return ( @@ -34,5 +34,5 @@ export function Overview() { - ) -} \ No newline at end of file + ); +} diff --git a/frontend/components/dashboard/recent-listings.tsx b/frontend/components/dashboard/recent-listings.tsx index 544d12f..52fe965 100644 --- a/frontend/components/dashboard/recent-listings.tsx +++ b/frontend/components/dashboard/recent-listings.tsx @@ -1,8 +1,4 @@ -import { - Avatar, - AvatarFallback, - AvatarImage, -} from "@/components/ui/avatar"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; export function RecentListings() { return ( @@ -14,9 +10,7 @@ export function RecentListings() {

Toyota Camry 2010

-

- Date -

+

Date

0.0/100
@@ -38,9 +32,7 @@ export function RecentListings() {

Honda Civic 2015

-

- Date -

+

Date

0.0/100
@@ -50,7 +42,9 @@ export function RecentListings() { CL
-

Chevrolet Silverado 2020

+

+ Chevrolet Silverado 2020 +

Date

0.0/100
@@ -67,5 +61,5 @@ export function RecentListings() {
0.0/100
- ) -} \ No newline at end of file + ); +} diff --git a/frontend/components/dashboard/search.tsx b/frontend/components/dashboard/search.tsx index 53da55e..69c09cf 100644 --- a/frontend/components/dashboard/search.tsx +++ b/frontend/components/dashboard/search.tsx @@ -9,5 +9,5 @@ export function Search() { className="md:w-[100px] lg:w-[300px]" /> - ) -} \ No newline at end of file + ); +} diff --git a/frontend/components/dashboard/team-switcher.tsx b/frontend/components/dashboard/team-switcher.tsx index a3c01de..27536c1 100644 --- a/frontend/components/dashboard/team-switcher.tsx +++ b/frontend/components/dashboard/team-switcher.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/prop-types */ -"use client" +"use client"; import { CaretSortIcon, @@ -8,11 +8,7 @@ import { } from "@radix-ui/react-icons"; import * as React from "react"; -import { - Avatar, - AvatarFallback, - AvatarImage, -} from "@/components/ui/avatar"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { Command, @@ -71,20 +67,22 @@ const groups = [ }, ], }, -] +]; -type Team = (typeof groups)[number]["teams"][number] +type Team = (typeof groups)[number]["teams"][number]; -type PopoverTriggerProps = React.ComponentPropsWithoutRef +type PopoverTriggerProps = React.ComponentPropsWithoutRef< + typeof PopoverTrigger +>; interface TeamSwitcherProps extends PopoverTriggerProps {} export default function TeamSwitcher({ className }: TeamSwitcherProps) { - const [open, setOpen] = React.useState(false) - const [showNewTeamDialog, setShowNewTeamDialog] = React.useState(false) + const [open, setOpen] = React.useState(false); + const [showNewTeamDialog, setShowNewTeamDialog] = React.useState(false); const [selectedTeam, setSelectedTeam] = React.useState( groups[0].teams[0] - ) + ); return ( @@ -119,8 +117,8 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) { { - setSelectedTeam(team) - setOpen(false) + setSelectedTeam(team); + setOpen(false); }} className="text-sm" > @@ -152,8 +150,8 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) { { - setOpen(false) - setShowNewTeamDialog(true) + setOpen(false); + setShowNewTeamDialog(true); }} > @@ -210,5 +208,5 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) { - ) -} \ No newline at end of file + ); +} diff --git a/frontend/components/dashboard/user-nav.tsx b/frontend/components/dashboard/user-nav.tsx index fe1b6b1..acc0b3a 100644 --- a/frontend/components/dashboard/user-nav.tsx +++ b/frontend/components/dashboard/user-nav.tsx @@ -1,8 +1,4 @@ -import { - Avatar, - AvatarFallback, - AvatarImage, -} from "@/components/ui/avatar"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { DropdownMenu, @@ -58,5 +54,5 @@ export function UserNav() { - ) -} \ No newline at end of file + ); +} diff --git a/frontend/components/home/component-grid.tsx b/frontend/components/home/component-grid.tsx index 91e3cae..957db3f 100644 --- a/frontend/components/home/component-grid.tsx +++ b/frontend/components/home/component-grid.tsx @@ -41,8 +41,9 @@ export default function ComponentGrid() { >

Popover

diff --git a/frontend/components/home/demo-modal.tsx b/frontend/components/home/demo-modal.tsx index b36dbc9..8635644 100644 --- a/frontend/components/home/demo-modal.tsx +++ b/frontend/components/home/demo-modal.tsx @@ -52,6 +52,6 @@ export function useDemoModal() { return useMemo( () => ({ setShowDemoModal, DemoModal: DemoModalCallback }), - [setShowDemoModal, DemoModalCallback], + [setShowDemoModal, DemoModalCallback] ); } diff --git a/frontend/components/layout/footer.tsx b/frontend/components/layout/footer.tsx index 7bd79a3..accf970 100644 --- a/frontend/components/layout/footer.tsx +++ b/frontend/components/layout/footer.tsx @@ -11,18 +11,17 @@ export default function Footer() { target="_blank" rel="noopener noreferrer" > - {" "} - University of Texas at Arlington CSE - - {" "}×{" "} + University of Texas at Arlington + CSE + {" "} + ×{" "} - {" "} - Statefarm + Statefarm

diff --git a/frontend/components/layout/meta.tsx b/frontend/components/layout/meta.tsx index ea34dc2..4c9df1b 100644 --- a/frontend/components/layout/meta.tsx +++ b/frontend/components/layout/meta.tsx @@ -45,4 +45,4 @@ export default function Meta({ props }: { props: MetaProps }) { ); -} \ No newline at end of file +} diff --git a/frontend/components/layout/navbar.tsx b/frontend/components/layout/navbar.tsx index 18092e8..8e5cc7b 100644 --- a/frontend/components/layout/navbar.tsx +++ b/frontend/components/layout/navbar.tsx @@ -28,27 +28,33 @@ export default function NavBar({ session }: { session: Session | null }) { alt="SMARE logo" width="156" height="50" - className="mr-2 rounded-sm" + className="mr-2 rounded-sm w-auto h-auto" >
- + Home - + Dashboard <> - {session ? ( - - ) : ( - - )} + {session ? ( + + ) : ( + + )}
diff --git a/frontend/components/layout/sign-in-modal.tsx b/frontend/components/layout/sign-in-modal.tsx index cfd3f67..2908172 100644 --- a/frontend/components/layout/sign-in-modal.tsx +++ b/frontend/components/layout/sign-in-modal.tsx @@ -37,12 +37,14 @@ const SignInModal = ({

Log In

- Log in or create an account to view your social marketplace risky listings dashboard - only your email and profile picture will be stored. + Log in or create an account to view your social marketplace risky + listings dashboard - only your email and profile picture will be + stored.

-