From a634bd39c52fcc26528f1f9947123d6bc13145a7 Mon Sep 17 00:00:00 2001 From: pheobeayo Date: Mon, 6 Oct 2025 17:54:56 +0100 Subject: [PATCH 1/6] feature: added a github handle to profile in frontend --- .../action-buttons/EditProfileDialog.tsx | 79 +++++++++++++------ .../profiles/profile-page/ProfileHeader.tsx | 22 +++++- frontend/src/lib/types/api.d.ts | 7 +- frontend/src/lib/types/profiles.d.ts | 3 +- 4 files changed, 83 insertions(+), 28 deletions(-) diff --git a/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx b/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx index f4619c6..9c41ba6 100644 --- a/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx +++ b/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx @@ -28,44 +28,56 @@ interface EditProfileDialogProps { address: string; name?: string; description?: string; + githubHandle?: string; children: React.ReactNode; } +const formSchema = z.object({ + name: z + .string() + .min(2, { message: "Name must be at least 2 characters." }), + description: z.string().optional(), + githubHandle: z.string().optional(), +}); + +type FormValues = z.infer; + export function EditProfileDialog({ address, name, description, + githubHandle, children, }: EditProfileDialogProps) { const [open, setOpen] = useState(false); const updateProfile = useUpdateProfile(); const queryClient = useQueryClient(); - const form = useForm<{ - name: string; - description?: string; - }>({ - resolver: zodResolver( - z.object({ - name: z - .string() - .min(2, { message: "Name must be at least 2 characters." }), - description: z.string().optional(), - }) - ), - defaultValues: { name: name || "", description: description || "" }, + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + name: name || "", + description: description || "", + githubHandle: githubHandle || "", + }, }); - const onSubmit = async (values: { name: string; description?: string }) => { - await updateProfile.mutateAsync({ - input: { - name: values.name, - description: values.description || "", - siweMessage: "LOGIN_NONCE", - }, - }); - await queryClient.invalidateQueries({ queryKey: ["profiles"] }); - setOpen(false); + const onSubmit = async (values: FormValues) => { + try { + await updateProfile.mutateAsync({ + input: { + name: values.name, + description: values.description || "", + github_handle: values.githubHandle || "", + siweMessage: "LOGIN_NONCE", + }, + }); + await queryClient.invalidateQueries({ queryKey: ["profiles"] }); + setOpen(false); + form.reset(values); + } catch (error) { + console.error("Failed to update profile:", error); + } }; return ( @@ -109,6 +121,25 @@ export function EditProfileDialog({ )} /> + ( + + GitHub Handle + +
+ @ + +
+
+ +
+ )} + />
); } -export default ProfileHeader; +export default ProfileHeader; \ No newline at end of file diff --git a/frontend/src/lib/types/api.d.ts b/frontend/src/lib/types/api.d.ts index 77b1911..13532a3 100644 --- a/frontend/src/lib/types/api.d.ts +++ b/frontend/src/lib/types/api.d.ts @@ -1,14 +1,17 @@ + export type CreateProfileInput = { name: string; description?: string; avatar_url?: string; - siweMessage: string; // message to sign for auth + github_handle?: string; + siweMessage: string; }; export type UpdateProfileInput = { name?: string; description?: string; avatar_url?: string; + github_handle?: string; siweMessage: string; }; @@ -21,4 +24,4 @@ export type DeleteProfileInput = { siweMessage: string; }; -export type DeleteProfileResponse = unknown; +export type DeleteProfileResponse = unknown; \ No newline at end of file diff --git a/frontend/src/lib/types/profiles.d.ts b/frontend/src/lib/types/profiles.d.ts index 36e71c7..8f8f3a8 100644 --- a/frontend/src/lib/types/profiles.d.ts +++ b/frontend/src/lib/types/profiles.d.ts @@ -18,6 +18,7 @@ export type ProfileFromAPI = { name?: string; description?: string; avatar_url?: string; + github_handle?: string; created_at?: string; updated_at?: string; -}; +}; \ No newline at end of file From bb22529dbc1169f52fa4384e57e45a2bac8e4c8f Mon Sep 17 00:00:00 2001 From: pheobeayo Date: Tue, 7 Oct 2025 09:46:18 +0100 Subject: [PATCH 2/6] fix changes requested --- .../action-buttons/EditProfileDialog.tsx | 12 ++++++------ .../profiles/profile-page/ProfileHeader.tsx | 16 +++++----------- frontend/src/components/ui/GithubIcon.tsx | 16 ++++++++++++++++ frontend/src/lib/types/api.d.ts | 4 ++-- frontend/src/lib/types/profiles.d.ts | 2 +- 5 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 frontend/src/components/ui/GithubIcon.tsx diff --git a/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx b/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx index 9c41ba6..0263b1d 100644 --- a/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx +++ b/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx @@ -28,7 +28,7 @@ interface EditProfileDialogProps { address: string; name?: string; description?: string; - githubHandle?: string; + githubLogin?: string; children: React.ReactNode; } @@ -37,7 +37,7 @@ const formSchema = z.object({ .string() .min(2, { message: "Name must be at least 2 characters." }), description: z.string().optional(), - githubHandle: z.string().optional(), + githubLogin: z.string().optional(), }); type FormValues = z.infer; @@ -46,7 +46,7 @@ export function EditProfileDialog({ address, name, description, - githubHandle, + githubLogin, children, }: EditProfileDialogProps) { const [open, setOpen] = useState(false); @@ -58,7 +58,7 @@ export function EditProfileDialog({ defaultValues: { name: name || "", description: description || "", - githubHandle: githubHandle || "", + githubLogin: githubLogin || "", }, }); @@ -68,7 +68,7 @@ export function EditProfileDialog({ input: { name: values.name, description: values.description || "", - github_handle: values.githubHandle || "", + github_login: values.githubLogin || "", siweMessage: "LOGIN_NONCE", }, }); @@ -123,7 +123,7 @@ export function EditProfileDialog({ /> ( GitHub Handle diff --git a/frontend/src/components/profiles/profile-page/ProfileHeader.tsx b/frontend/src/components/profiles/profile-page/ProfileHeader.tsx index 06ec21b..51af304 100644 --- a/frontend/src/components/profiles/profile-page/ProfileHeader.tsx +++ b/frontend/src/components/profiles/profile-page/ProfileHeader.tsx @@ -4,6 +4,7 @@ import { useMemo } from "react"; import { useGetProfiles } from "@/hooks/profiles/use-get-profiles"; import AddressTokenBalance from "@/components/AddressTokenBalance"; import CopyAddressToClipboard from "@/components/CopyAddressToClipboard"; +import { GithubIcon } from "@/components/ui/GithubIcon"; export function ProfileHeader({ address }: { address: string }) { const profilesQuery = useGetProfiles(); @@ -51,23 +52,16 @@ export function ProfileHeader({ address }: { address: string }) { iconSize="sm" /> ) : null} - {profile?.github_handle && ( + {profile?.github_login && ( )} diff --git a/frontend/src/components/ui/GithubIcon.tsx b/frontend/src/components/ui/GithubIcon.tsx new file mode 100644 index 0000000..f1f9840 --- /dev/null +++ b/frontend/src/components/ui/GithubIcon.tsx @@ -0,0 +1,16 @@ +interface GithubIconProps { + className?: string; +} + +export function GithubIcon({ className = "h-4 w-4" }: GithubIconProps) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/lib/types/api.d.ts b/frontend/src/lib/types/api.d.ts index 13532a3..292c658 100644 --- a/frontend/src/lib/types/api.d.ts +++ b/frontend/src/lib/types/api.d.ts @@ -3,7 +3,7 @@ export type CreateProfileInput = { name: string; description?: string; avatar_url?: string; - github_handle?: string; + github_login?: string; siweMessage: string; }; @@ -11,7 +11,7 @@ export type UpdateProfileInput = { name?: string; description?: string; avatar_url?: string; - github_handle?: string; + github_login?: string; siweMessage: string; }; diff --git a/frontend/src/lib/types/profiles.d.ts b/frontend/src/lib/types/profiles.d.ts index 8f8f3a8..3504fd0 100644 --- a/frontend/src/lib/types/profiles.d.ts +++ b/frontend/src/lib/types/profiles.d.ts @@ -18,7 +18,7 @@ export type ProfileFromAPI = { name?: string; description?: string; avatar_url?: string; - github_handle?: string; + github_login?: string; created_at?: string; updated_at?: string; }; \ No newline at end of file From cedecda79b96d23b7891f3d0abd0dbc39bbad1ea Mon Sep 17 00:00:00 2001 From: pheobeayo Date: Tue, 7 Oct 2025 13:24:38 +0100 Subject: [PATCH 3/6] fix prefilling --- .../profiles/action-buttons/EditProfileDialog.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx b/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx index 0263b1d..e9b73a3 100644 --- a/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx +++ b/frontend/src/components/profiles/action-buttons/EditProfileDialog.tsx @@ -22,7 +22,7 @@ import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useUpdateProfile } from "@/hooks/profiles/use-update-profile"; import { useQueryClient } from "@tanstack/react-query"; -import { useState } from "react"; +import { useState, useEffect } from "react"; interface EditProfileDialogProps { address: string; @@ -62,6 +62,17 @@ export function EditProfileDialog({ }, }); + + useEffect(() => { + if (open) { + form.reset({ + name: name || "", + description: description || "", + githubLogin: githubLogin || "", + }); + } + }, [open, name, description, githubLogin, form]); + const onSubmit = async (values: FormValues) => { try { await updateProfile.mutateAsync({ @@ -74,7 +85,6 @@ export function EditProfileDialog({ }); await queryClient.invalidateQueries({ queryKey: ["profiles"] }); setOpen(false); - form.reset(values); } catch (error) { console.error("Failed to update profile:", error); } From f3cd30cd9eb2deaa1e206e40ef319244d14eb96e Mon Sep 17 00:00:00 2001 From: pheobeayo Date: Tue, 7 Oct 2025 18:20:08 +0100 Subject: [PATCH 4/6] fix props --- frontend/src/components/pages/ProfilePage.tsx | 29 +++++++++++++++---- .../components/profiles/list/ProfileCard.tsx | 3 ++ .../components/profiles/list/ProfilesList.tsx | 19 ++++++------ .../profiles/profile-page/ProfileActions.tsx | 3 ++ .../src/lib/constants/profileConstants.ts | 7 ++++- frontend/src/lib/types/profiles.d.ts | 7 +++-- 6 files changed, 49 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/pages/ProfilePage.tsx b/frontend/src/components/pages/ProfilePage.tsx index bf61232..8018e7b 100644 --- a/frontend/src/components/pages/ProfilePage.tsx +++ b/frontend/src/components/pages/ProfilePage.tsx @@ -2,22 +2,41 @@ import { AppWrapper } from "@/components/AppWrapper"; import ProfileHeader from "@/components/profiles/profile-page/ProfileHeader"; import ProfileActions from "@/components/profiles/profile-page/ProfileActions"; import ProfileAttestations from "@/components/profiles/profile-page/ProfileAttestations"; -import ProfileIssuedAttestations from "../profiles/profile-page/ProfileIssuedAttestations"; +import ProfileIssuedAttestations from "@/components/profiles/profile-page/ProfileIssuedAttestations"; +import { useGetProfiles } from "@/hooks/profiles/use-get-profiles"; +import { useMemo } from "react"; type Props = { address?: string }; export default function ProfilePage({ address }: Props) { + const profilesQuery = useGetProfiles(); + + const profile = useMemo(() => { + const list = profilesQuery.data ?? []; + const p = list.find( + (x) => x.address.toLowerCase() === (address || "").toLowerCase() + ); + return p; + }, [profilesQuery.data, address]); + return ( -
+
- +
+ +
-
+
); -} +} \ No newline at end of file diff --git a/frontend/src/components/profiles/list/ProfileCard.tsx b/frontend/src/components/profiles/list/ProfileCard.tsx index f3c590a..2a39187 100644 --- a/frontend/src/components/profiles/list/ProfileCard.tsx +++ b/frontend/src/components/profiles/list/ProfileCard.tsx @@ -19,6 +19,7 @@ interface ProfileCardProps { name?: string; description?: string; avatar?: string; + githubLogin?: string; attestationCount: number; attestations: Array<{ id: string; @@ -33,6 +34,7 @@ export function ProfileCard({ name, description, avatar, + githubLogin, attestationCount, attestations, }: ProfileCardProps) { @@ -93,6 +95,7 @@ export function ProfileCard({ address={address} name={name} description={description} + githubLogin={githubLogin} >