-
-
Notifications
You must be signed in to change notification settings - Fork 232
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement profile settings feature (#808)
* feat: add profile update api function * refactor: minor updates * feat: add settings na d profile route * feat: add toast support and validation * chore: minor api call fixes * refactor: remove unwanted code * refactor: update default values * refactor: add timezone array to select options * refactor: add cursor indication for disabled input * feat: create useAuthSession hook for user data * chore: guard settings page * refactor: update input placeholders * fix: build and language chart issues * refactor: cleanups and finishing touches * chore: remove unused code * refactor: update user profile link icon * Update components/organisms/UserSettingsPage/user-settings-page.tsx Co-authored-by: Brian Douglas <[email protected]> * Update components/organisms/UserSettingsPage/user-settings-page.tsx Co-authored-by: Brian Douglas <[email protected]> * Update components/organisms/UserSettingsPage/user-settings-page.tsx Co-authored-by: Brian Douglas <[email protected]> * chore: update Db user type * feat: implement update user email preference * refactor: cleanup anf fininshing touches * chore: general refactor and cleanup * chore: update api call response * fix: build error --------- Co-authored-by: Brian Douglas <[email protected]>
- Loading branch information
Showing
19 changed files
with
1,554 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
import React, { useState } from "react"; | ||
import React, { useState, useEffect } from "react"; | ||
|
||
import { User } from "@supabase/supabase-js"; | ||
|
||
import Button from "components/atoms/Button/button"; | ||
import Checkbox from "components/atoms/Checkbox/checkbox"; | ||
|
@@ -8,10 +10,56 @@ import Select from "components/atoms/Select/select"; | |
import SelectOption from "components/atoms/Select/select-option"; | ||
import LanguagePill from "components/atoms/LanguagePill/LanguagePill"; | ||
|
||
const UserSettingsPage = () => { | ||
import { updateUser } from "lib/hooks/update-user"; | ||
import { ToastTrigger } from "lib/utils/toast-trigger"; | ||
import { authSession } from "lib/hooks/authSession"; | ||
import { validateEmail } from "lib/utils/validate-email"; | ||
import { timezones } from "lib/utils/timezones"; | ||
import { updateEmailPreferences } from "lib/hooks/updateEmailPreference"; | ||
import { useFetchUser } from "lib/hooks/useFetchUser"; | ||
|
||
interface userSettingsPageProps { | ||
user: User | null; | ||
} | ||
|
||
type EmailPreferenceType = { | ||
display_email?: boolean; | ||
receive_collaboration?: boolean; | ||
}; | ||
const UserSettingsPage = ({ user }: userSettingsPageProps) => { | ||
const { data: insightsUser } = useFetchUser(user?.user_metadata.user_name); | ||
|
||
const [isValidEmail, setIsValidEmail] = useState<boolean>(true); | ||
const [userInfo, setUserInfo] = useState<DbUser>(); | ||
const [email, setEmail] = useState<string | undefined>(userInfo?.email || user?.email); | ||
const [emailPreference, setEmailPreference] = useState<EmailPreferenceType>({ | ||
// eslint-disable-next-line camelcase | ||
display_email: false, | ||
// eslint-disable-next-line camelcase | ||
receive_collaboration: false | ||
}); | ||
const [selectedInterest, setSelectedInterest] = useState<string[]>([]); | ||
const interestArray = ["javascript", "python", "rust", "ML", "AI", "react"]; | ||
|
||
useEffect(() => { | ||
async function fetchAuthSession() { | ||
const response = await authSession(); | ||
if (response !== false) setUserInfo(response); | ||
} | ||
|
||
if (user) setEmail(user.email); | ||
if (insightsUser) { | ||
setEmailPreference({ | ||
// eslint-disable-next-line camelcase | ||
display_email: insightsUser?.display_email, | ||
// eslint-disable-next-line camelcase | ||
receive_collaboration: insightsUser?.receive_collaboration | ||
}); | ||
setSelectedInterest(insightsUser?.interests.split(",")); | ||
} | ||
fetchAuthSession(); | ||
}, [user, insightsUser]); | ||
|
||
const handleSelectInterest = (interest: string) => { | ||
if (selectedInterest.length > 0 && selectedInterest.includes(interest)) { | ||
setSelectedInterest((prev) => prev.filter((item) => item !== interest)); | ||
|
@@ -20,23 +68,65 @@ const UserSettingsPage = () => { | |
} | ||
}; | ||
|
||
const handleUpdateEmailPreference = async () => { | ||
const data = await updateEmailPreferences({ ...emailPreference }); | ||
if (data) { | ||
ToastTrigger({ message: "Updated successfully", type: "success" }); | ||
} else { | ||
ToastTrigger({ message: "An error occured!!!", type: "error" }); | ||
} | ||
}; | ||
|
||
const handleUpdateInterest = async () => { | ||
const data = await updateUser({ | ||
data: { interests: selectedInterest }, | ||
params: "interests" | ||
}); | ||
if (data) { | ||
ToastTrigger({ message: "Updated successfully", type: "success" }); | ||
} else { | ||
ToastTrigger({ message: "An error occured!!!", type: "error" }); | ||
} | ||
}; | ||
const handleUpdateProfile = async () => { | ||
const data = await updateUser({ | ||
data: { email } | ||
}); | ||
if (data) { | ||
ToastTrigger({ message: "Updated successfully", type: "success" }); | ||
} else { | ||
ToastTrigger({ message: "An error occured!!!", type: "error" }); | ||
} | ||
}; | ||
|
||
return ( | ||
<div> | ||
<div className="flex flex-col md:flex-row md:justify-between gap-4 text-sm text-light-slate-11"> | ||
<div className="flex flex-col md:flex-row md:gap-48 gap-4 text-sm text-light-slate-11"> | ||
<div> | ||
<Title className="!text-2xl !text-light-slate-11" level={2}> | ||
Public profile | ||
</Title> | ||
<form className="flex flex-col gap-6 mt-6"> | ||
<form onSubmit={(e) => e.preventDefault()} className="flex flex-col gap-6 mt-6"> | ||
<TextInput | ||
classNames="bg-light-slate-4 text-light-slate-11 font-medium" | ||
label="Name*" | ||
placeholder="April O'Neil" | ||
value={user?.user_metadata.full_name} | ||
disabled | ||
/> | ||
<TextInput | ||
classNames="bg-light-slate-4 text-light-slate-11 font-medium" | ||
placeholder="[email protected]" | ||
onChange={(e) => { | ||
setEmail(e.target.value); | ||
if (validateEmail(e.target.value)) { | ||
setIsValidEmail(true); | ||
} else { | ||
setIsValidEmail(false); | ||
} | ||
}} | ||
label="Email*" | ||
value={email} | ||
/> | ||
|
||
{/* Bio section */} | ||
|
@@ -45,42 +135,61 @@ const UserSettingsPage = () => { | |
<textarea | ||
rows={4} | ||
placeholder="Tell us about yourself." | ||
className="bg-light-slate-4 rounded-lg px-3 py-2 " | ||
className="bg-light-slate-4 rounded-lg px-3 py-2 disabled:cursor-not-allowed " | ||
readOnly | ||
value={ | ||
userInfo?.bio || | ||
"I am an open source developer with a passion for music and video games. I strive to improve the open source community and am always looking for new ways to contribute." | ||
} | ||
></textarea> | ||
</div> | ||
<TextInput | ||
classNames="bg-light-slate-4 text-light-slate-11 font-medium" | ||
placeholder="https://turtlepower.pizza" | ||
label="URL" | ||
disabled | ||
/> | ||
<TextInput | ||
classNames="bg-light-slate-4 text-light-slate-11" | ||
placeholder="@aprilcodes" | ||
label="Twitter Username" | ||
disabled | ||
value={`@${(userInfo && userInfo.twitter_username) || "saucedopen"}`} | ||
/> | ||
<TextInput | ||
classNames="bg-light-slate-4 text-light-slate-11 font-medium" | ||
placeholder="StockGen" | ||
label="Company" | ||
disabled | ||
value={userInfo?.company || "OpenSauced"} | ||
/> | ||
<TextInput | ||
classNames="bg-light-slate-4 text-light-slate-11 font-medium" | ||
placeholder="USA" | ||
label="Location" | ||
disabled | ||
value={userInfo?.location || "Canada"} | ||
/> | ||
<div> | ||
<Checkbox value={"true"} title="profile email" label="Display current local time" /> | ||
<Checkbox checked={false} title="profile email" label="Display current local time on profile" /> | ||
<span className="ml-7 text-light-slate-9 text-sm font-normal"> | ||
Other users will see the time difference from their local time. | ||
</span> | ||
</div> | ||
<div className="flex flex-col gap-2"> | ||
<label>Time zone*</label> | ||
<Select> | ||
<SelectOption value="Wat+1">Select time zone</SelectOption> | ||
<SelectOption value="select timezone">Select time zone</SelectOption> | ||
{timezones.map((timezone, index) => ( | ||
<SelectOption key={index} value={timezone.value}> | ||
{timezone.text} | ||
</SelectOption> | ||
))} | ||
</Select> | ||
</div> | ||
<Button type="primary">Update profile</Button> | ||
<Button disabled={!isValidEmail} onClick={handleUpdateProfile} type="primary"> | ||
Update profile | ||
</Button> | ||
</form> | ||
</div> | ||
<div className="flex flex-col-reverse md:flex-col gap-6"> | ||
|
@@ -98,19 +207,42 @@ const UserSettingsPage = () => { | |
/> | ||
))} | ||
</div> | ||
<button className="px-4 w-max py-2 rounded-lg bg-light-slate-4 border border-light-slate-8"> | ||
<Button | ||
type="default" | ||
disabled={selectedInterest.length === 0} | ||
onClick={handleUpdateInterest} | ||
className="!px-4 !text-light-slate-11 !py-2 !bg-light-slate-4" | ||
> | ||
Update Interests | ||
</button> | ||
</Button> | ||
</div> | ||
<div className="flex flex-col gap-6"> | ||
<div className="flex flex-col gap-3 "> | ||
<label className="text-light-slate-11 text-2xl font-normal">Email Preferences</label> | ||
<Checkbox value={"true"} title="profile email" label="Display Email On Profile" /> | ||
<Checkbox value={"true"} title="collaboration requests" label="Receive collaboration requests" /> | ||
<Checkbox | ||
// eslint-disable-next-line camelcase | ||
onChange={() => setEmailPreference((prev) => ({ ...prev, display_email: !prev.display_email }))} | ||
checked={emailPreference.display_email} | ||
title="profile email" | ||
label="Display email on profile" | ||
/> | ||
<Checkbox | ||
onChange={() => | ||
// eslint-disable-next-line camelcase | ||
setEmailPreference((prev) => ({ ...prev, receive_collaboration: !prev.receive_collaboration })) | ||
} | ||
checked={emailPreference.receive_collaboration} | ||
title="collaboration requests" | ||
label="Receive collaboration requests" | ||
/> | ||
</div> | ||
<button className="px-4 w-max py-2 rounded-lg bg-light-slate-4 border border-light-slate-8"> | ||
<Button | ||
onClick={handleUpdateEmailPreference} | ||
type="default" | ||
className="!px-4 w-max !py-2 !bg-light-slate-4 " | ||
> | ||
Update Preferences | ||
</button> | ||
</Button> | ||
</div> | ||
</div> | ||
</div> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { supabase } from "lib/utils/supabase"; | ||
|
||
const baseUrl = process.env.NEXT_PUBLIC_API_URL; | ||
export interface UserResponse extends DbUser {} | ||
const authSession = async () => { | ||
const sessionResponse = await supabase.auth.getSession(); | ||
const sessionToken = sessionResponse?.data.session?.access_token; | ||
// const { data, error } = useSWR<UserResponse, Error>("auth/session", publicApiFetcher as Fetcher<UserResponse, Error>); | ||
const response = await fetch(`${baseUrl}/auth/session`, { | ||
headers: { | ||
Accept: "application/json", | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${sessionToken}` | ||
} | ||
}); | ||
|
||
if (response.status === 200) { | ||
return response.json(); | ||
} else { | ||
return false; | ||
} | ||
}; | ||
|
||
export { authSession }; |
Oops, something went wrong.