From 5b0c7a6c217261ec44711ffcd0b4d39e9bf2d731 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Yusuf=20Akg=C3=BCl?= <s86116@bht-berlin.de>
Date: Fri, 7 Jul 2023 05:06:34 +0200
Subject: [PATCH] profile ui and editing, needs still optimization

---
 .../[userid]/(profile)/followers/page.tsx     |  62 ---
 .../(user)/[userid]/(profile)/layout.tsx      |  20 -
 .../(profile)/followers}/page.tsx             |   0
 .../[username]/(profile)/following/page.tsx   |   7 +
 .../(user)/[username]/(profile)/layout.tsx    |  37 ++
 .../(profile)/likes/page.tsx                  |   0
 .../(profile)/media/page.tsx                  |   0
 .../(profile)/page.tsx                        |   4 +-
 .../(profile)/with_replies/page.tsx           |   0
 .../status/[id]/page.tsx                      |   0
 app/api/users/[username]/route.ts             |   1 +
 components/follow-button.tsx                  |   2 +
 .../profile/components/edit-profile-modal.tsx | 396 ++++++++++--------
 .../profile/components/profile-user-info.tsx  |  98 +++--
 components/profile/components/profile.tsx     |  47 ---
 .../profile/components/user-join-date.tsx     |   8 +-
 16 files changed, 339 insertions(+), 343 deletions(-)
 delete mode 100644 app/(content)/(user)/[userid]/(profile)/followers/page.tsx
 delete mode 100644 app/(content)/(user)/[userid]/(profile)/layout.tsx
 rename app/(content)/(user)/{[userid]/(profile)/following => [username]/(profile)/followers}/page.tsx (100%)
 create mode 100644 app/(content)/(user)/[username]/(profile)/following/page.tsx
 create mode 100644 app/(content)/(user)/[username]/(profile)/layout.tsx
 rename app/(content)/(user)/{[userid] => [username]}/(profile)/likes/page.tsx (100%)
 rename app/(content)/(user)/{[userid] => [username]}/(profile)/media/page.tsx (100%)
 rename app/(content)/(user)/{[userid] => [username]}/(profile)/page.tsx (52%)
 rename app/(content)/(user)/{[userid] => [username]}/(profile)/with_replies/page.tsx (100%)
 rename app/(content)/(user)/{[userid] => [username]}/status/[id]/page.tsx (100%)
 delete mode 100644 components/profile/components/profile.tsx

diff --git a/app/(content)/(user)/[userid]/(profile)/followers/page.tsx b/app/(content)/(user)/[userid]/(profile)/followers/page.tsx
deleted file mode 100644
index 4c4cc45..0000000
--- a/app/(content)/(user)/[userid]/(profile)/followers/page.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-
-import { GlobalLayout } from "@/components/global-layout"
-import { UserAvatar } from "@/components/user-avatar"
-import { db } from "@/lib/db"
-import { User } from "@prisma/client"
-import Link from "next/link"
-
-export default async function UserFollowers({ params }: { params: { userid: string } }) {
-
-    const fullUser = await db.user.findFirst({
-        where: {
-            username: params.userid
-        },
-        include: {
-            following: true,
-            followers: true
-        }
-    })
-
-    const followers = await db.user.findMany({
-        where: {
-            following: {
-                every: {
-                    followerId: { not: undefined }
-                }
-            },
-            followers: {
-                every: {
-                    followingId: fullUser?.id
-                }
-            }
-        }
-    })
-
-    return (
-        <GlobalLayout
-            mainContent={
-                <div className="flex flex-col w-full">
-                    {followers?.map((follower: User) => (
-                        <div className="flex items-center space-y-6" key={follower.id}>
-                            <Link href={`/${follower.username}`} className="flex flex-row">
-                                <UserAvatar
-                                    user={{ username: follower.username, image: follower.image }}
-                                    className="h-10 w-10"
-                                />
-
-                                <div className="flex flex-col ml-3">
-                                    <span className="font-bold">{follower.name}</span>
-                                    <span className="text-sky-500 text-sm">@{follower.username}</span>
-                                </div>
-                            </Link>
-
-                            <div className="ml-auto">
-                                {/* Followbutton */}
-                            </div>
-                        </div>
-                    ))}
-                </div>
-            }
-        />
-    )
-}
\ No newline at end of file
diff --git a/app/(content)/(user)/[userid]/(profile)/layout.tsx b/app/(content)/(user)/[userid]/(profile)/layout.tsx
deleted file mode 100644
index 5df26b9..0000000
--- a/app/(content)/(user)/[userid]/(profile)/layout.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { GlobalLayout } from "@/components/global-layout"
-import { Profile } from "@/components/profile/components/profile"
-import { ProfileSideContent } from "@/components/profile/components/profile-side-content"
-
-export default async function ProfileLayout({
-    children,
-}: {
-    children: React.ReactNode
-}) {
-    return (
-        <GlobalLayout
-            mainContent={
-                <Profile >
-                    {children}
-                </Profile>
-            }
-            sideContent={<ProfileSideContent />}
-        />
-    )
-}
\ No newline at end of file
diff --git a/app/(content)/(user)/[userid]/(profile)/following/page.tsx b/app/(content)/(user)/[username]/(profile)/followers/page.tsx
similarity index 100%
rename from app/(content)/(user)/[userid]/(profile)/following/page.tsx
rename to app/(content)/(user)/[username]/(profile)/followers/page.tsx
diff --git a/app/(content)/(user)/[username]/(profile)/following/page.tsx b/app/(content)/(user)/[username]/(profile)/following/page.tsx
new file mode 100644
index 0000000..5ea8f4d
--- /dev/null
+++ b/app/(content)/(user)/[username]/(profile)/following/page.tsx
@@ -0,0 +1,7 @@
+export default async function Following() {
+    return (
+        <div>
+            <h1>Following Page WIP</h1>
+        </div>
+    )
+}
\ No newline at end of file
diff --git a/app/(content)/(user)/[username]/(profile)/layout.tsx b/app/(content)/(user)/[username]/(profile)/layout.tsx
new file mode 100644
index 0000000..9595b88
--- /dev/null
+++ b/app/(content)/(user)/[username]/(profile)/layout.tsx
@@ -0,0 +1,37 @@
+import { GlobalLayout } from "@/components/global-layout"
+import { ProfileSideContent } from "@/components/profile/components/profile-side-content"
+import { ProfileUserInfo } from "@/components/profile/components/profile-user-info"
+import { Card } from "@/components/ui/card"
+import { UserNotFound } from "@/components/user-not-found"
+import getURL from "@/lib/utils"
+
+export const dynamic = 'force-dynamic'
+export const fetchCache = 'force-no-store'
+
+export default async function ProfileLayout({
+    params,
+    children,
+}: {
+    params: { username: string }
+    children: React.ReactNode
+}) {
+    const user = await fetch(getURL(`/api/users/${params.username}`)).then((result) => result.json())
+
+    return (
+        <GlobalLayout
+            mainContent={
+                <Card className="overflow-hidden h-full w-full">
+                    {!user ?
+                        <UserNotFound />
+                        :
+                        <>
+                            <ProfileUserInfo user={user} />
+                            {children}
+                        </>
+                    }
+                </Card>
+            }
+            sideContent={<ProfileSideContent />}
+        />
+    )
+}
\ No newline at end of file
diff --git a/app/(content)/(user)/[userid]/(profile)/likes/page.tsx b/app/(content)/(user)/[username]/(profile)/likes/page.tsx
similarity index 100%
rename from app/(content)/(user)/[userid]/(profile)/likes/page.tsx
rename to app/(content)/(user)/[username]/(profile)/likes/page.tsx
diff --git a/app/(content)/(user)/[userid]/(profile)/media/page.tsx b/app/(content)/(user)/[username]/(profile)/media/page.tsx
similarity index 100%
rename from app/(content)/(user)/[userid]/(profile)/media/page.tsx
rename to app/(content)/(user)/[username]/(profile)/media/page.tsx
diff --git a/app/(content)/(user)/[userid]/(profile)/page.tsx b/app/(content)/(user)/[username]/(profile)/page.tsx
similarity index 52%
rename from app/(content)/(user)/[userid]/(profile)/page.tsx
rename to app/(content)/(user)/[username]/(profile)/page.tsx
index 14bfa34..b418baa 100644
--- a/app/(content)/(user)/[userid]/(profile)/page.tsx
+++ b/app/(content)/(user)/[username]/(profile)/page.tsx
@@ -1,9 +1,9 @@
 import { ProfileUserContent } from "@/components/profile/components/profile-user-content"
 
-export default async function User({ params }: { params: { userid: string } }) {
+export default async function User({ params }: { params: { username: string } }) {
     return (
         <div className="space-y-6 w-full">
-            <ProfileUserContent userid={params.userid} />
+            {/* <ProfileUserContent userid={params.username} /> */}
         </div>
     )
 }
\ No newline at end of file
diff --git a/app/(content)/(user)/[userid]/(profile)/with_replies/page.tsx b/app/(content)/(user)/[username]/(profile)/with_replies/page.tsx
similarity index 100%
rename from app/(content)/(user)/[userid]/(profile)/with_replies/page.tsx
rename to app/(content)/(user)/[username]/(profile)/with_replies/page.tsx
diff --git a/app/(content)/(user)/[userid]/status/[id]/page.tsx b/app/(content)/(user)/[username]/status/[id]/page.tsx
similarity index 100%
rename from app/(content)/(user)/[userid]/status/[id]/page.tsx
rename to app/(content)/(user)/[username]/status/[id]/page.tsx
diff --git a/app/api/users/[username]/route.ts b/app/api/users/[username]/route.ts
index ce7122b..fde9970 100644
--- a/app/api/users/[username]/route.ts
+++ b/app/api/users/[username]/route.ts
@@ -29,6 +29,7 @@ export async function GET(request: Request, context: { params: { username: strin
                 createdAt: true,
                 bio: true,
                 location: true,
+                website: true,
                 followers: true,
                 following: true,
 
diff --git a/components/follow-button.tsx b/components/follow-button.tsx
index fd3e645..552c4e9 100644
--- a/components/follow-button.tsx
+++ b/components/follow-button.tsx
@@ -1,3 +1,5 @@
+"use client"
+
 import { useFollow } from "./profile/hooks/use-follow"
 import { Button } from "./ui/button"
 import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog"
diff --git a/components/profile/components/edit-profile-modal.tsx b/components/profile/components/edit-profile-modal.tsx
index 2555865..ba74fcc 100644
--- a/components/profile/components/edit-profile-modal.tsx
+++ b/components/profile/components/edit-profile-modal.tsx
@@ -20,41 +20,52 @@ import { Textarea } from "@/components/ui/textarea"
 import { toast } from "@/components/ui/use-toast"
 import { UserAvatar } from "@/components/user-avatar"
 import { zodResolver } from "@hookform/resolvers/zod"
-import { useSession } from "next-auth/react"
 import Image from "next/image"
-import { useRef, useState } from "react"
+import { useRouter } from "next/navigation"
+import { useEffect, useRef, useState } from "react"
 import { useForm } from "react-hook-form"
 import { z } from "zod"
 import { useUpdateProfile } from "../hooks/use-update-profile"
 import { IProfile, IUser } from "../types"
 
 const FormSchema = z.object({
-    name: z.string().min(1, "Name can't be blank.").max(50, "Name can't be more than 50 characters"),
+    name: z.string().min(1, "Name can't be blank").max(50, "Name can't be more than 50 characters"),
     bio: z.string().max(160, "Bio can't be more than 160 characters").optional(),
     location: z.string().max(30, "Location can't be more than 30 characters").optional(),
     website: z.string().max(100, "Website can't be more than 100 characters").optional(),
 })
 
+const ImagesSchema = z.custom<File>().refine((file) => file instanceof File, { message: "Expected a file" })
+    .refine((file) => file?.size < 4000000, { message: "Images must be less than 4MB" })
+    .optional()
+
 export const EditProfileModal = ({ user }: { user: IUser }) => {
-    const { isLoading, mutate } = useUpdateProfile()
+    const { isLoading, isSuccess, mutate } = useUpdateProfile()
 
     const form = useForm<z.infer<typeof FormSchema>>({
         resolver: zodResolver(FormSchema),
+        defaultValues: {
+            name: user.name,
+            bio: user.bio || "",
+            location: user.location || "",
+            website: user.website || "",
+        },
     })
 
-    form.setValue('name', user.name)
-    form.setValue('bio', user.bio || "")
-    form.setValue('location', user.location || "")
-    form.setValue('website', user.website || "")
-
-    const [chosenImages, setChosenImages] = useState<Partial<IProfile>>({
+    const [open, setOpen] = useState(false)
+    const [imageErrorMessage, setImageErrorMessage] = useState("")
+    const [chosenBanner, setChosenBanner] = useState<Partial<IProfile>>({
         banner: { url: user?.banner || "", file: undefined },
+    })
+    const [chosenImages, setChosenImages] = useState<Partial<IProfile>>({
         image: { url: user?.image || "", file: undefined },
     })
 
     const bannerInputRef = useRef<HTMLInputElement>(null)
     const imageInputRef = useRef<HTMLInputElement>(null)
 
+    const router = useRouter()
+
     async function onSave(formData: z.infer<typeof FormSchema>) {
         if (!user) return null
 
@@ -63,36 +74,56 @@ export const EditProfileModal = ({ user }: { user: IUser }) => {
             bio: formData.bio,
             location: formData.location,
             website: formData.website,
-            banner: { url: user?.banner || "", file: chosenImages.banner?.file },
+            banner: { url: user?.banner || "", file: chosenBanner.banner?.file },
             image: { url: user?.image || "", file: chosenImages.image?.file },
         }
-
+        // TODO dont send everything, only send what has changed
         mutate({
             profile,
             userId: user.id,
         })
-
-        toast({
-            description: "Successfully updated profile.",
-        })
     }
 
+    useEffect(() => {
+        if (isSuccess) {
+            toast({
+                description: "Successfully updated profile.",
+            })
+            setOpen(false)
+            router.refresh()
+        }
+    }, [isSuccess, router])
+
     const chooseImage = async (event: any, type: string) => {
         const file = event.target.files[0]
         if (!file) return
+        try {
+            ImagesSchema.parse(file)
+            setImageErrorMessage("")
+        } catch (error: any) {
+            const err = error as z.ZodError
+            setImageErrorMessage(err.issues[0].message)
+            return
+        }
 
-        if (type === "banner" && bannerInputRef.current)
+        const reader = new FileReader()
+
+        if (type === "banner" && bannerInputRef.current) {
             bannerInputRef.current.value = ""
+            reader.onloadend = () => {
+                setChosenBanner({
+                    ["banner"]: { url: reader.result as string, file },
+                })
+            }
+        }
 
-        if (type === "avatar" && imageInputRef.current)
+        if (type === "image" && imageInputRef.current) {
             imageInputRef.current.value = ""
-
-        const reader = new FileReader()
-
-        reader.onloadend = () => {
-            setChosenImages({
-                [type]: { url: reader.result as string, file },
-            })
+            reader.onloadend = () => {
+                setChosenImages({
+                    ["image"]: { url: reader.result as string, file },
+                })
+            }
         }
 
         reader.readAsDataURL(file)
@@ -101,7 +132,7 @@ export const EditProfileModal = ({ user }: { user: IUser }) => {
     if (!user) return null
 
     return (
-        <Dialog>
+        <Dialog open={open} onOpenChange={setOpen}>
             <DialogTrigger asChild>
                 <Button variant="outline">Edit Profile</Button>
             </DialogTrigger>
@@ -113,156 +144,191 @@ export const EditProfileModal = ({ user }: { user: IUser }) => {
                     </DialogDescription>
                 </DialogHeader>
                 <Form {...form}>
-                    <form onSubmit={form.handleSubmit(onSave)} className="grid gap-3 py-3">
-                        <Card className="relative w-full overflow-hidden">
-                            <AspectRatio ratio={889 / 500} className="bg-slate-600  dark:bg-slate-400">
-                                {chosenImages.banner?.url &&
-                                    <Image
-                                        src={chosenImages.banner?.url}
-                                        alt={"user banner image"}
-                                        fill
-                                        priority
-                                        className="object-center"
-                                    />
+                    <form onSubmit={form.handleSubmit(onSave)}>
+                        <div className="grid gap-3 py-3">
+
+                            <div className="space-y-2">
+                                <div className="grid grid-cols-4 items-center gap-4">
+                                    <div className="col-span-1 relative w-full overflow-hidden items-center">
+                                        <div className="object-center object-cover w-full h-full">
+                                            {!chosenImages.image?.url ?
+                                                <UserAvatar
+                                                    user={{ username: user.username, image: null }}
+                                                    className="object-center object-cover w-full h-full aspect-square"
+                                                />
+                                                :
+                                                <AspectRatio ratio={1 / 1} className="overflow-hidden rounded-full">
+                                                    <Image
+                                                        src={chosenImages.image?.url}
+                                                        alt={"user image"}
+                                                        fill
+                                                        priority
+                                                        className="object-center object-cover w-full h-full "
+                                                    />
+                                                </AspectRatio>
+                                            }
+                                        </div>
+                                        <input
+                                            className="hidden resize-none"
+                                            type="file"
+                                            accept="image/*"
+                                            ref={imageInputRef}
+                                            onChange={(e) => chooseImage(e, "image")}
+                                            disabled={isLoading}
+                                        />
+                                        <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex space-x-3">
+                                            <Button
+                                                type="button"
+                                                variant="outline"
+                                                size="icon"
+                                                className="bg-opacity-50 dark:bg-opacity-50"
+                                                onClick={() => imageInputRef?.current?.click()}
+                                                disabled={isLoading}
+                                            >
+                                                <Icons.camera />
+                                            </Button>
+                                        </div>
+                                    </div>
+
+                                    <Card className="col-span-3 relative w-full overflow-hidden border-none">
+
+                                        <AspectRatio ratio={3 / 1} className="bg-muted overflow-hidden">
+                                            {chosenBanner.banner?.url &&
+                                                <Image
+                                                    src={chosenBanner.banner?.url}
+                                                    alt={"user banner image"}
+                                                    fill
+                                                    priority
+                                                    className="object-center object-cover w-full h-full"
+                                                />
+                                            }
+                                        </AspectRatio>
+                                        <input
+                                            className="hidden w-full resize-none"
+                                            type="file"
+                                            accept="image/*"
+                                            ref={bannerInputRef}
+                                            onChange={(e) => chooseImage(e, "banner")}
+                                            disabled={isLoading}
+                                        />
+                                        <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex space-x-3">
+                                            <Button
+                                                type="button"
+                                                variant="outline"
+                                                size="icon"
+                                                className="bg-opacity-50 dark:bg-opacity-50"
+                                                onClick={() => bannerInputRef.current?.click()}
+                                                disabled={isLoading}
+                                            >
+                                                <Icons.camera />
+                                            </Button>
+                                            {chosenBanner.banner?.url && (
+                                                <Button
+                                                    type="button"
+                                                    variant="outline"
+                                                    size="icon"
+                                                    className="bg-opacity-50 dark:bg-opacity-50"
+                                                    onClick={() => setChosenBanner({ banner: { url: "", file: undefined } })}
+                                                    disabled={isLoading}
+                                                >
+                                                    <Icons.close />
+                                                </Button>
+                                            )}
+                                        </div>
+                                    </Card>
+                                </div>
+                                {imageErrorMessage &&
+                                    <div className="grid grid-cols-4 items-center gap-4">
+                                        <div className="col-span-1"></div>
+                                        <div className="col-span-3">
+                                            <p className="text-sm font-medium text-destructive">{imageErrorMessage}</p>
+                                        </div>
+                                    </div>
                                 }
-                            </AspectRatio>
-                            <input
-                                type="file"
-                                accept="image/*"
-                                ref={bannerInputRef}
-                                onChange={(e) => chooseImage(e, "banner")}
-                                disabled={isLoading}
-                            />
-                            <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex space-x-3">
-                                <Button
-                                    variant="outline"
-                                    size="icon"
-                                    className="bg-opacity-50 dark:bg-opacity-50"
-                                    onClick={() => bannerInputRef.current?.click()}
-                                    disabled={isLoading}
-                                >
-                                    <Icons.camera />
-                                </Button>
-                                {chosenImages.banner?.url && (
-                                    <Button
-                                        variant="outline"
-                                        size="icon"
-                                        className="bg-opacity-50 dark:bg-opacity-50"
-                                        onClick={() => setChosenImages({ banner: { url: "", file: undefined } })}
-                                        disabled={isLoading}
-                                    >
-                                        <Icons.close />
-                                    </Button>
-                                )}
                             </div>
-                        </Card>
 
-                        <div className="absolute w-24 h-24 -mt-12 overflow-hidden">
-                            <UserAvatar
-                                user={{ username: user.username, image: chosenImages.banner?.url || null }}
-                                className="object-cover"
-                            />
-                            <input
-                                type="file"
-                                accept="image/*"
-                                ref={imageInputRef}
-                                onChange={(e) => chooseImage(e, "image")}
-                                disabled={isLoading}
-                            />
-                            <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex space-x-3">
-                                <Button
-                                    variant="outline"
-                                    size="icon"
-                                    className="bg-opacity-50 dark:bg-opacity-50"
-                                    onClick={() => imageInputRef?.current?.click()}
-                                    disabled={isLoading}
-                                >
-                                    <Icons.camera />
-                                </Button>
+                            <div className="grid grid-cols-4 items-center gap-4">
+                                <Label htmlFor="name" className="text-right">
+                                    Name
+                                </Label>
+                                <FormField
+                                    control={form.control}
+                                    name="name"
+                                    render={({ field }) => (
+                                        <FormItem className="col-span-3">
+                                            <FormControl>
+                                                <Input id="name" disabled={isLoading} {...field} />
+                                            </FormControl>
+                                            <FormMessage />
+                                        </FormItem>
+                                    )}
+                                />
                             </div>
-                        </div>
 
-                        <div className="grid grid-cols-4 items-center gap-4">
-                            <Label htmlFor="name" className="text-right">
-                                Name
-                            </Label>
-                            <FormField
-                                control={form.control}
-                                name="name"
-                                render={({ field }) => (
-                                    <FormItem>
-                                        <FormControl>
-                                            <Input id="name" className="col-span-3" disabled={isLoading} {...field} />
-                                        </FormControl>
-                                        <FormMessage />
-                                    </FormItem>
-                                )}
-                            />
-                        </div>
+                            <div className="grid grid-cols-4 items-center gap-4">
+                                <Label htmlFor="bio" className="text-right">
+                                    Bio
+                                </Label>
+                                <FormField
+                                    control={form.control}
+                                    name="bio"
+                                    render={({ field }) => (
+                                        <FormItem className="col-span-3">
+                                            <FormControl>
+                                                <Textarea id="bio" className="resize-none" disabled={isLoading} {...field} />
+                                            </FormControl>
+                                            <FormMessage />
+                                        </FormItem>
+                                    )}
+                                />
+                            </div>
 
-                        <div className="grid grid-cols-4 items-center gap-4">
-                            <Label htmlFor="bio" className="text-right">
-                                Bio
-                            </Label>
-                            <FormField
-                                control={form.control}
-                                name="bio"
-                                render={({ field }) => (
-                                    <FormItem>
-                                        <FormControl>
-                                            <Textarea id="bio" className="col-span-3 resize-none" disabled={isLoading} {...field} />
-                                        </FormControl>
-                                        <FormMessage />
-                                    </FormItem>
-                                )}
-                            />
-                        </div>
+                            <div className="grid grid-cols-4 items-center gap-4">
+                                <Label htmlFor="location" className="text-right">
+                                    Location
+                                </Label>
+                                <FormField
+                                    control={form.control}
+                                    name="location"
+                                    render={({ field }) => (
+                                        <FormItem className="col-span-3">
+                                            <FormControl>
+                                                <Input id="location" disabled={isLoading} {...field} />
+                                            </FormControl>
+                                            <FormMessage />
+                                        </FormItem>
+                                    )}
+                                />
+                            </div>
 
-                        <div className="grid grid-cols-4 items-center gap-4">
-                            <Label htmlFor="location" className="text-right">
-                                Location
-                            </Label>
-                            <FormField
-                                control={form.control}
-                                name="location"
-                                render={({ field }) => (
-                                    <FormItem>
-                                        <FormControl>
-                                            <Input id="location" className="col-span-3" disabled={isLoading} {...field} />
-                                        </FormControl>
-                                        <FormMessage />
-                                    </FormItem>
-                                )}
-                            />
+                            <div className="grid grid-cols-4 items-center gap-4">
+                                <Label htmlFor="website" className="text-right">
+                                    Website
+                                </Label>
+                                <FormField
+                                    control={form.control}
+                                    name="website"
+                                    render={({ field }) => (
+                                        <FormItem className="col-span-3">
+                                            <FormControl>
+                                                <Input id="website" disabled={isLoading} {...field} />
+                                            </FormControl>
+                                            <FormMessage />
+                                        </FormItem>
+                                    )}
+                                />
+                            </div>
                         </div>
-
-                        <div className="grid grid-cols-4 items-center gap-4">
-                            <Label htmlFor="website" className="text-right">
-                                Website
-                            </Label>
-                            <FormField
-                                control={form.control}
-                                name="website"
-                                render={({ field }) => (
-                                    <FormItem>
-                                        <FormControl>
-                                            <Input id="website" className="col-span-3" disabled={isLoading} {...field} />
-                                        </FormControl>
-                                        <FormMessage />
-                                    </FormItem>
+                        <DialogFooter>
+                            <Button type="submit" disabled={isLoading || !form.formState.isDirty}>
+                                {isLoading && (
+                                    <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
                                 )}
-                            />
-                        </div>
+                                Save changes
+                            </Button>
+                        </DialogFooter>
                     </form>
                 </Form>
-                <DialogFooter>
-                    <Button type="submit" disabled={isLoading}>
-                        {isLoading && (
-                            <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
-                        )}
-                        Save changes
-                    </Button>
-                </DialogFooter>
             </DialogContent>
         </Dialog>
     )
diff --git a/components/profile/components/profile-user-info.tsx b/components/profile/components/profile-user-info.tsx
index c1837db..e2e6924 100644
--- a/components/profile/components/profile-user-info.tsx
+++ b/components/profile/components/profile-user-info.tsx
@@ -13,71 +13,83 @@ import { UserJoinDate } from "./user-join-date"
 export const ProfileUserInfo = async ({ user }: { user: IUser }) => {
     const session = await getCurrentUser()
 
-    // const isFollowing = following({
-    //     user,
-    //     sessionUserId: session ? session.id : "",
-    // })
+    const isFollowing = following({
+        user,
+        sessionUserId: session ? session.id : "",
+    })
 
     return (
         <>
-            {/* <div className="h-64 overflow-hidden">
-                <AspectRatio ratio={889 / 500} className="bg-slate-600  dark:bg-slate-400">
-                    {user.banner &&
-                        <Image
-                            src={user.banner}
-                            alt={"user banner image"}
-                            fill
-                            priority
-                            className="object-center"
-                        />
-                    }
-                </AspectRatio>
+            <AspectRatio ratio={3 / 1} className="bg-muted w-full overflow-hidden">
+                {user.banner &&
+                    <Image
+                        src={user.banner}
+                        alt={"user banner image"}
+                        fill
+                        priority
+                        className="object-center object-cover w-full h-full"
+                    />
+                }
+            </AspectRatio>
+            <div className="relative">
+                <div className="flex items-end justify-between p-6 md:px-12">
+                    <div>
+                        <div className="absolute bottom-6 md:bottom-3">
+                            <UserAvatar
+                                user={{ username: user.username, image: user.image || null }}
+                                className="h-20 md:h-40 w-20 md:w-40"
+                            />
+                        </div>
+                    </div>
+                    <div>
+                        {session?.id === user.id ? (
+                            <EditProfileModal user={user} />
+                        ) : (
+                            <FollowButton
+                                userId={user.id}
+                                username={user.username ? user.username : ""}
+                                isFollowing={isFollowing}
+                            />
+                        )}
+                    </div>
+                </div>
             </div>
-            <div className="p-6 md:p-12 ss:flex">
-                <UserAvatar
-                    user={{ username: user.username, image: user.image || null }}
-                    className="h-52 w-52 -mt-36"
-                />
 
-                <div className="ml-6 md:ml-12 space-y-3">
+            <div className="px-6 md:px-12 flex flex-col space-y-3 w-full">
+                <div className="pb-3 whitespace-nowrap items-center">
                     <h1 className="text-2xl font-bold">{user.name}</h1>
                     <h1 className="text-md text-sky-500">@{user.username}</h1>
-                    {user.bio && <h1 className="">{user.bio}</h1>}
+                </div>
+                {user.bio && <h1 className="">{user.bio}</h1>}
+
+                <div className="flex whitespace-nowrap items-center space-x-6 text-muted-foreground">
                     {user.location &&
-                        <div className="space-x-2">
-                            <Icons.location />
-                            {user.location}
+                        <div className="flex items-center">
+                            <Icons.location className="mr-1" />
+                            <div>{user.location}</div>
                         </div>
                     }
                     {user.website &&
-                        <div className="space-x-2">
-                            <Icons.website />
-                            {user.website}
+                        <div className="flex items-center">
+                            <Icons.website className="mr-1" />
+                            <div>{user.website}</div>
                         </div>
                     }
                     {user.createdAt && <UserJoinDate date={user.createdAt} />}
                 </div>
 
-                <div className="flex justify-end ml-6 md:ml-12">
-                    {session?.id === user.id ? (
-                        <EditProfileModal user={user} />
-                    ) : (
-                        <FollowButton userId={user.id} username={user.username ? user.username : ""} isFollowing={isFollowing} />
-                    )}
-                </div>
-
-                <div className="flex justify-between w-full mt-6 md:mt-12">
+                <div className="flex whitespace-nowrap items-center space-x-6">
                     <Link href={`/${user.username}/following`}>
-                        <span className="text-sm">{user._count?.following}</span>
-                        <span className="text-sm">Following</span>
+                        <span className="text-sm font-bold">{user._count?.following}</span>
+                        <span className="text-sm text-muted-foreground"> Following</span>
                     </Link>
 
                     <Link href={`/${user.username}/followers`}>
-                        <span className="text-sm">{user._count?.followers}</span>
-                        <span className="text-sm">Followers</span>
+                        <span className="text-sm font-bold">{user._count?.followers}</span>
+                        <span className="text-sm text-muted-foreground"> Followers</span>
                     </Link>
                 </div>
-            </div> */}
+            </div>
         </ >
     )
 }
\ No newline at end of file
diff --git a/components/profile/components/profile.tsx b/components/profile/components/profile.tsx
deleted file mode 100644
index 46cd550..0000000
--- a/components/profile/components/profile.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-"use client"
-
-import LoadingItem from "@/components/loading-item"
-import { TryAgain } from "@/components/try-again"
-import { Card } from "@/components/ui/card"
-import { UserNotFound } from "@/components/user-not-found"
-import { usePathname } from "next/navigation"
-import { useUser } from "../hooks/use-user"
-import { ProfileUserInfo } from "./profile-user-info"
-
-export const Profile = ({ children }: { children: React.ReactNode }) => {
-    const pathname = usePathname()
-    const username = pathname?.split("/")[1] || ""
-    // TODO: Fix this client / server side rendering issue
-    const { data: user, isLoading, isError } = useUser(username)
-
-    if (isLoading) {
-        return (
-            <>
-                <LoadingItem />
-            </>
-        )
-    }
-
-    if (isError) {
-        return (
-            <>
-                <TryAgain />
-            </>
-        )
-    }
-
-    if (!isLoading && !isError && !user) {
-        return (
-            <>
-                <UserNotFound />
-            </>
-        )
-    }
-
-    return (
-        <Card className="overflow-hidden h-full w-full">
-            {/* <ProfileUserInfo user={user} /> */}
-            <div>{children}</div>
-        </Card>
-    )
-}
diff --git a/components/profile/components/user-join-date.tsx b/components/profile/components/user-join-date.tsx
index 35b7738..16706d9 100644
--- a/components/profile/components/user-join-date.tsx
+++ b/components/profile/components/user-join-date.tsx
@@ -9,11 +9,11 @@ export const UserJoinDate = ({
     showIcon?: boolean
 }) => {
     return (
-        <div className="">
-            {showIcon && (<Icons.calendar />)}
-            <span className="">
+        <div className="flex items-center">
+            {showIcon && (<Icons.calendar className="mr-1" />)}
+            <div>
                 Joined {dayjs(date).format("MMMM YYYY")}
-            </span>
+            </div>
         </div>
     )
 }
\ No newline at end of file
-- 
GitLab