-
Notifications
You must be signed in to change notification settings - Fork 0
feat: profile page #211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: profile page #211
Changes from all commits
dde886f
d30f18f
5e7eb7a
6bf47ce
4142c8e
823789e
80e22a1
39e0795
ca97a7c
1357e89
9b0a296
8e3f524
52d84dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,4 +7,13 @@ module.exports = defineConfig([ | |
| { | ||
| ignores: ["dist/*"], | ||
| }, | ||
| { | ||
| settings: { | ||
| "import/resolver": { | ||
| typescript: { | ||
| project: "./tsconfig.json", | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. allows for path aliasing in our lint checks like |
||
| ]); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,7 @@ function NavLink({ | |
| to={to} | ||
| className={`flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors ${ | ||
| isActive | ||
| ? "bg-primary/10 font-semibold text-gray-900" | ||
| ? "bg-bg-selected font-semibold text-subtle" | ||
| : "text-gray-500 hover:bg-gray-100 hover:text-gray-700" | ||
| }`} | ||
| > | ||
|
|
@@ -29,6 +29,33 @@ function NavLink({ | |
| ); | ||
| } | ||
|
|
||
| function ProfileLink({ displayName }: { displayName: string | undefined }) { | ||
| const pathname = useRouterState({ select: (s) => s.location.pathname }); | ||
| const isActive = pathname === "/profile"; | ||
| return ( | ||
| <Link | ||
| to="/profile" | ||
| className={`flex items-center gap-3 rounded-lg p-3 transition-colors ${ | ||
| isActive ? "bg-bg-selected" : "hover:bg-bg-selected" | ||
| }`} | ||
| > | ||
| <UserButton | ||
| appearance={{ | ||
| elements: { | ||
| avatarBox: "size-10", | ||
| }, | ||
| }} | ||
| /> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. clerk component :( will switch over to use an image component once we pull data from backend instead of clerk |
||
| <div className="min-w-0 flex-1"> | ||
| <p className="truncate text-sm font-semibold text-text-subtle"> | ||
| {displayName || "User"} | ||
| </p> | ||
| <p className="truncate text-xs text-primary">Hotel Chain</p> | ||
| </div> | ||
| </Link> | ||
| ); | ||
| } | ||
|
|
||
| export function Sidebar() { | ||
| const { user } = useUser(); | ||
|
|
||
|
|
@@ -69,21 +96,7 @@ export function Sidebar() { | |
| Settings | ||
| </NavLink> | ||
| <LogoutButton /> | ||
| <div className="flex items-center gap-3 pt-4"> | ||
| <UserButton | ||
| appearance={{ | ||
| elements: { | ||
| avatarBox: "size-10", | ||
| }, | ||
| }} | ||
| /> | ||
| <div className="min-w-0 flex-1"> | ||
| <p className="truncate text-sm font-medium text-gray-700"> | ||
| {displayName || "User"} | ||
| </p> | ||
| <p className="truncate text-xs text-gray-500">Hotel Chain</p> | ||
| </div> | ||
| </div> | ||
| <ProfileLink displayName={displayName} /> | ||
| </div> | ||
| </aside> | ||
| ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| import { createFileRoute } from "@tanstack/react-router"; | ||
| import { useUser } from "@clerk/clerk-react"; | ||
|
|
||
| export const Route = createFileRoute("/_protected/profile")({ | ||
| component: ProfilePage, | ||
| }); | ||
|
|
||
| function DetailRow({ label, value }: { label: string; value: string }) { | ||
| return ( | ||
| <div className="grid grid-cols-[40%_1fr] items-center py-3"> | ||
| <p className="text-sm font-medium text-text-default">{label}</p> | ||
| <p className="text-sm text-text-subtle">{value}</p> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| function ProfileInfoCard({ | ||
| displayName, | ||
| email, | ||
| phone, | ||
| }: { | ||
| displayName: string; | ||
| email: string; | ||
| phone: string; | ||
| }) { | ||
| return ( | ||
| <section className="h-56 rounded-lg border border-stroke-subtle bg-white p-6"> | ||
| <div className="divide-y divide-stroke-subtle"> | ||
| <DetailRow label="Government Name" value={displayName} /> | ||
| <DetailRow label="Date of Birth" value="—" /> | ||
| <DetailRow label="Phone" value={phone || "—"} /> | ||
| <DetailRow label="Email" value={email || "—"} /> | ||
| </div> | ||
| </section> | ||
| ); | ||
| } | ||
|
|
||
| function NotesFromManagerCard() { | ||
| return ( | ||
| <section className="h-56 rounded-lg border border-stroke-subtle bg-white p-6"> | ||
| <h2 className="text-sm font-medium text-text-default"> | ||
| Notes from Manager | ||
| </h2> | ||
| </section> | ||
| ); | ||
| } | ||
|
|
||
| function ProfilePage() { | ||
| const { user } = useUser(); | ||
|
|
||
| const displayName = | ||
| user?.fullName || | ||
| [user?.firstName, user?.lastName].filter(Boolean).join(" ") || | ||
| "User"; | ||
|
|
||
| const email = user?.primaryEmailAddress?.emailAddress ?? ""; | ||
| const phone = user?.primaryPhoneNumber?.phoneNumber ?? ""; | ||
|
|
||
| return ( | ||
| <main className="flex h-screen flex-col overflow-hidden"> | ||
| {/* Page header */} | ||
| <header className="flex-row gap-1.5 border-b border-stroke-subtle px-10 py-5"> | ||
| <h1 className="text-2xl font-semibold text-text-default">Profile</h1> | ||
| <p className="text-sm font-medium text-text-subtle"> | ||
| Overview of profile | ||
| </p> | ||
| </header> | ||
|
|
||
| {/* Scrollable content */} | ||
| <div className="flex flex-1 flex-col overflow-y-auto px-6 py-6"> | ||
| {/* User hero */} | ||
| <div className="flex items-center gap-11 mb-6 pl-4"> | ||
| {user?.imageUrl ? ( | ||
| <img | ||
| src={user.imageUrl} | ||
| alt={displayName} | ||
| className="size-30 rounded-full object-cover" | ||
| /> | ||
| ) : ( | ||
| <div className="flex size-30 items-center justify-center rounded-full border-2 border-text-default bg-gray-100"> | ||
| <span className="text-[40px] font-semibold text-text-default"> | ||
| {displayName.charAt(0)} | ||
| </span> | ||
| </div> | ||
| )} | ||
| <div> | ||
| <h2 className="text-[40px] font-bold text-text-default leading-none"> | ||
| {user?.firstName} | ||
| </h2> | ||
| <h2 className="text-[40px] font-bold text-text-default leading-none"> | ||
| {user?.lastName} | ||
| </h2> | ||
| <p className="pt-1 text-base font-bold text-primary">Hotel Chain</p> | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* Info + Notes row */} | ||
| <div className="flex flex-1 gap-4"> | ||
| <div className="flex-1"> | ||
| <ProfileInfoCard | ||
| displayName={displayName} | ||
| email={email} | ||
| phone={phone} | ||
| /> | ||
| </div> | ||
| <div className="flex-1"> | ||
| <NotesFromManagerCard /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </main> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove unused interface