Refactor public profile form

pull/5494/head
Kamran Ahmed 8 months ago
parent 3a18207b32
commit 384d73363d
  1. 7
      src/components/UpdateProfile/UpdateProfileForm.tsx
  2. 59
      src/components/UpdateProfile/UpdatePublicProfileForm.tsx
  3. 99
      src/components/UpdateProfile/VisibilityDropdown.tsx

@ -2,11 +2,13 @@ import { type FormEvent, useEffect, useState } from 'react';
import { httpGet, httpPost } from '../../lib/http'; import { httpGet, httpPost } from '../../lib/http';
import { pageProgressMessage } from '../../stores/page'; import { pageProgressMessage } from '../../stores/page';
import UploadProfilePicture from './UploadProfilePicture'; import UploadProfilePicture from './UploadProfilePicture';
import {ArrowDown, ChevronDown} from "lucide-react";
export function UpdateProfileForm() { export function UpdateProfileForm() {
const [name, setName] = useState(''); const [name, setName] = useState('');
const [avatar, setAvatar] = useState(''); const [avatar, setAvatar] = useState('');
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [username, setUsername] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(''); const [error, setError] = useState('');
@ -50,10 +52,11 @@ export function UpdateProfileForm() {
return; return;
} }
const { name, email, avatar } = response; const { name, email, avatar, username } = response;
setName(name); setName(name);
setEmail(email); setEmail(email);
setUsername(username);
setAvatar(avatar || ''); setAvatar(avatar || '');
setIsLoading(false); setIsLoading(false);
@ -69,7 +72,7 @@ export function UpdateProfileForm() {
return ( return (
<div> <div>
<div className="mb-8 hidden md:block"> <div className="mb-8 hidden md:block">
<h2 className="text-2xl font-bold sm:text-3xl">Profile</h2> <h2 className="text-2xl font-bold sm:text-3xl">Basic Information</h2>
<p className="mt-0.5 text-gray-400"> <p className="mt-0.5 text-gray-400">
Update and set up your public profile below. Update and set up your public profile below.
</p> </p>

@ -8,16 +8,10 @@ import type {
UserDocument, UserDocument,
} from '../../api/user'; } from '../../api/user';
import { SelectionButton } from '../RoadCard/SelectionButton'; import { SelectionButton } from '../RoadCard/SelectionButton';
import { import { ArrowUpRight, Eye, EyeOff } from 'lucide-react';
ArrowUpRight,
Check,
Eye,
EyeOff,
Globe,
LockIcon,
} from 'lucide-react';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx'; import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx';
import { VisibilityDropdown } from './VisibilityDropdown.tsx';
type RoadmapType = { type RoadmapType = {
id: string; id: string;
@ -187,52 +181,35 @@ export function UpdatePublicProfileForm() {
); );
const publicRoadmaps = profileRoadmaps.filter((r) => !r.isCustomResource); const publicRoadmaps = profileRoadmaps.filter((r) => !r.isCustomResource);
const isAllCustomRoadmapsSelected =
customRoadmaps.length === publicCustomRoadmaps.length ||
customRoadmapVisibility === 'all';
const isAllRoadmapsSelected =
roadmaps.length === publicRoadmaps.length || roadmapVisibility === 'all';
return ( return (
<> <div className="-mx-10 mt-10 border-t px-10 pt-10">
{isCreatingRoadmap && ( {isCreatingRoadmap && (
<CreateRoadmapModal onClose={() => setIsCreatingRoadmap(false)} /> <CreateRoadmapModal onClose={() => setIsCreatingRoadmap(false)} />
)} )}
<div className="mt-10 flex items-center justify-between gap-2"> <div className="flex gap-2 flex-col sm:flex-row justify-between mb-1">
<div className="flex items-center gap-2"> <div className="flex gap-2 flex-grow flex-col sm:flex-row items-start">
<h3 className="text-2xl font-bold">Public Profile</h3> <h3 className="mr-1 text-xl sm:text-3xl font-bold">Personal Profile</h3>
{publicProfileUrl && ( {publicProfileUrl && (
<a <a
href={publicProfileUrl} href={publicProfileUrl}
target="_blank" target="_blank"
className="flex shrink-0 flex-row items-center gap-1 rounded-md border border-black py-0.5 pl-1 pr-1.5 text-xs transition-colors hover:bg-black hover:text-white" className="flex h-[30px] shrink-0 flex-row items-center gap-1 rounded-lg border border-black pl-1.5 pr-2.5 text-sm transition-colors hover:bg-black hover:text-white"
> >
<ArrowUpRight className="h-3 w-3 stroke-[3]" /> <ArrowUpRight className="h-3 w-3 stroke-[3]" />
Visit Profile Visit
</a> </a>
)} )}
</div> </div>
<VisibilityDropdown
<div className="flex items-center gap-2"> visibility={profileVisibility}
<SelectionButton setVisibility={setProfileVisibility}
type="button"
text="Public"
icon={Globe}
isDisabled={profileVisibility === 'public'}
isSelected={profileVisibility === 'public'}
onClick={() => updateProfileVisibility('public')}
/> />
<SelectionButton
type="button"
text="Private"
icon={LockIcon}
isDisabled={profileVisibility === 'private'}
isSelected={profileVisibility === 'private'}
onClick={() => updateProfileVisibility('private')}
/>
</div>
</div> </div>
<p className="text-gray-400 text-sm sm:text-base mt-2 sm:mt-0 hidden sm:block">
Set up your public profile to showcase your learning progress.
</p>
<form className="mt-6 space-y-4 pb-10" onSubmit={handleSubmit}> <form className="mt-6 space-y-4 pb-10" onSubmit={handleSubmit}>
<div className="flex w-full flex-col"> <div className="flex w-full flex-col">
<label <label
@ -285,7 +262,7 @@ export function UpdatePublicProfileForm() {
<h3 className="text-sm font-medium"> <h3 className="text-sm font-medium">
Which roadmap progresses do you want to show on your profile? Which roadmap progresses do you want to show on your profile?
</h3> </h3>
<div className="mt-3 flex items-center gap-2"> <div className="mt-3 flex items-center flex-wrap gap-2">
<SelectionButton <SelectionButton
type="button" type="button"
text="All Progress" text="All Progress"
@ -355,7 +332,7 @@ export function UpdatePublicProfileForm() {
<h3 className="text-sm font-medium"> <h3 className="text-sm font-medium">
Pick your custom roadmaps to show on your profile Pick your custom roadmaps to show on your profile
</h3> </h3>
<div className="mt-3 flex items-center gap-2"> <div className="mt-3 flex items-center gap-2 flex-wrap">
<SelectionButton <SelectionButton
type="button" type="button"
text="All Roadmaps" text="All Roadmaps"
@ -538,6 +515,6 @@ export function UpdatePublicProfileForm() {
{isLoading ? 'Please wait...' : 'Update Public Profile'} {isLoading ? 'Please wait...' : 'Update Public Profile'}
</button> </button>
</form> </form>
</> </div>
); );
} }

@ -0,0 +1,99 @@
import { ChevronDown, Globe, LockIcon } from 'lucide-react';
import { type AllowedProfileVisibility } from '../../api/user.ts';
import { pageProgressMessage } from '../../stores/page.ts';
import { httpPatch } from '../../lib/http.ts';
import { useToast } from '../../hooks/use-toast.ts';
import { useRef, useState } from 'react';
import { useOutsideClick } from '../../hooks/use-outside-click.ts';
import { cn } from '../../lib/classname.ts';
type VisibilityDropdownProps = {
visibility: AllowedProfileVisibility;
setVisibility: (visibility: AllowedProfileVisibility) => void;
};
export function VisibilityDropdown(props: VisibilityDropdownProps) {
const { visibility, setVisibility } = props;
const toast = useToast();
const dropdownRef = useRef<HTMLDivElement>(null);
useOutsideClick(dropdownRef, () => {
setIsVisibilityDropdownOpen(false);
});
const [isVisibilityDropdownOpen, setIsVisibilityDropdownOpen] =
useState(false);
async function updateProfileVisibility(visibility: AllowedProfileVisibility) {
pageProgressMessage.set('Updating profile visibility');
setIsVisibilityDropdownOpen(false);
const { error } = await httpPatch(
`${import.meta.env.PUBLIC_API_URL}/v1-update-public-profile-visibility`,
{
profileVisibility: visibility,
},
);
if (error) {
toast.error(error.message || 'Something went wrong');
return;
}
pageProgressMessage.set('');
setVisibility(visibility);
}
return (
<div className="relative">
<button
onClick={() => {
setIsVisibilityDropdownOpen(true);
}}
className={cn(
'flex items-center gap-1 rounded-lg border border-black py-1 pl-1.5 pr-2 text-sm capitalize text-black',
{
invisible: isVisibilityDropdownOpen,
},
)}
>
{visibility === 'public' && <Globe className='mr-1' size={13} />}
{visibility === 'private' && <LockIcon className='mr-1' size={13} />}
{visibility}
<ChevronDown size={13} className="ml-1" />
</button>
{isVisibilityDropdownOpen && (
<div
className="absolute right-0 top-0 overflow-hidden rounded-lg border border-gray-200 bg-white shadow-lg"
ref={dropdownRef}
>
<button
className={cn(
'flex w-full items-center gap-2 py-2.5 pl-3 pr-3.5 text-left text-sm hover:bg-gray-100',
{
'bg-gray-200': visibility === 'public',
},
)}
onClick={() => updateProfileVisibility('public')}
>
<Globe size={13} />
Public
</button>
<button
className={cn(
'flex w-full items-center gap-2 py-2.5 pl-3 pr-3.5 text-left text-sm hover:bg-gray-100',
{
'bg-gray-200': visibility === 'private',
},
)}
onClick={() => updateProfileVisibility('private')}
>
<LockIcon size={13} />
Private
</button>
</div>
)}
</div>
);
}
Loading…
Cancel
Save