Refactor public profile form

pull/5494/head
Kamran Ahmed 8 months ago
parent 3a18207b32
commit 384d73363d
  1. 7
      src/components/UpdateProfile/UpdateProfileForm.tsx
  2. 77
      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 { pageProgressMessage } from '../../stores/page';
import UploadProfilePicture from './UploadProfilePicture';
import {ArrowDown, ChevronDown} from "lucide-react";
export function UpdateProfileForm() {
const [name, setName] = useState('');
const [avatar, setAvatar] = useState('');
const [email, setEmail] = useState('');
const [username, setUsername] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
@ -50,10 +52,11 @@ export function UpdateProfileForm() {
return;
}
const { name, email, avatar } = response;
const { name, email, avatar, username } = response;
setName(name);
setEmail(email);
setUsername(username);
setAvatar(avatar || '');
setIsLoading(false);
@ -69,7 +72,7 @@ export function UpdateProfileForm() {
return (
<div>
<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">
Update and set up your public profile below.
</p>

@ -8,16 +8,10 @@ import type {
UserDocument,
} from '../../api/user';
import { SelectionButton } from '../RoadCard/SelectionButton';
import {
ArrowUpRight,
Check,
Eye,
EyeOff,
Globe,
LockIcon,
} from 'lucide-react';
import { ArrowUpRight, Eye, EyeOff } from 'lucide-react';
import { useToast } from '../../hooks/use-toast';
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx';
import { VisibilityDropdown } from './VisibilityDropdown.tsx';
type RoadmapType = {
id: string;
@ -187,52 +181,35 @@ export function UpdatePublicProfileForm() {
);
const publicRoadmaps = profileRoadmaps.filter((r) => !r.isCustomResource);
const isAllCustomRoadmapsSelected =
customRoadmaps.length === publicCustomRoadmaps.length ||
customRoadmapVisibility === 'all';
const isAllRoadmapsSelected =
roadmaps.length === publicRoadmaps.length || roadmapVisibility === 'all';
return (
<>
<div className="-mx-10 mt-10 border-t px-10 pt-10">
{isCreatingRoadmap && (
<CreateRoadmapModal onClose={() => setIsCreatingRoadmap(false)} />
)}
<div className="mt-10 flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<h3 className="text-2xl font-bold">Public Profile</h3>
{publicProfileUrl && (
<a
href={publicProfileUrl}
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"
>
<ArrowUpRight className="h-3 w-3 stroke-[3]" />
Visit Profile
</a>
)}
</div>
<div className="flex items-center gap-2">
<SelectionButton
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 className="flex gap-2 flex-col sm:flex-row justify-between mb-1">
<div className="flex gap-2 flex-grow flex-col sm:flex-row items-start">
<h3 className="mr-1 text-xl sm:text-3xl font-bold">Personal Profile</h3>
{publicProfileUrl && (
<a
href={publicProfileUrl}
target="_blank"
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]" />
Visit
</a>
)}
</div>
<VisibilityDropdown
visibility={profileVisibility}
setVisibility={setProfileVisibility}
/>
</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}>
<div className="flex w-full flex-col">
<label
@ -285,7 +262,7 @@ export function UpdatePublicProfileForm() {
<h3 className="text-sm font-medium">
Which roadmap progresses do you want to show on your profile?
</h3>
<div className="mt-3 flex items-center gap-2">
<div className="mt-3 flex items-center flex-wrap gap-2">
<SelectionButton
type="button"
text="All Progress"
@ -355,7 +332,7 @@ export function UpdatePublicProfileForm() {
<h3 className="text-sm font-medium">
Pick your custom roadmaps to show on your profile
</h3>
<div className="mt-3 flex items-center gap-2">
<div className="mt-3 flex items-center gap-2 flex-wrap">
<SelectionButton
type="button"
text="All Roadmaps"
@ -538,6 +515,6 @@ export function UpdatePublicProfileForm() {
{isLoading ? 'Please wait...' : 'Update Public Profile'}
</button>
</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