mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-06 17:26:29 +02:00
feat: update public profile (#7170)
* feat: update public profile * Update arp@M52V7hmG4ORf4TIVw3W3J.md (#7171) * Update arp@M52V7hmG4ORf4TIVw3W3J.md A little changes made to the Topic * Update src/data/roadmaps/cyber-security/content/arp@M52V7hmG4ORf4TIVw3W3J.md * Update src/data/roadmaps/cyber-security/content/arp@M52V7hmG4ORf4TIVw3W3J.md --------- Co-authored-by: Arik Chakma <arikchangma@gmail.com> * chore: update roadmap content json (#7164) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * Add redis roadmap * Add redis roadmap * Add redis roadmap * Improved Docker Roadmap. 🌨️ (#7029) * Introduction. * Namespaces. * Installation & Setup * Data Persistence. * Databases. * Building Container Images. * Container Registries. * Running Containers. * Container Security * Docker CLI. (Goated) * Developer Experience. * Deploying Containers + Extras. * Few Refractors. * Trim Content As Requested. * Undo / Remove Refractors. * Update 100-dockerhub.md * Update 101-dockerhub-alt.md * Update index.md * Apply Requested Changes. * Update what-is-hosting@aqMaEY8gkKMikiqleV5EP.md (#7174) Add new article for 'Web Hosting', it has clearly explained all the details for the beginners. And it will be more reasonable to have not only videos but also article * replaced broken link (#7176) * 7165 roadmap title typo (#7177) * corrected the node title * corrected file name * Fix SEO title * chore: update roadmap content json (#7173) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * Complete spell-check for "Prespective Analytics" (#7179) Corrected "Prespective Analytics" into _Prescriptive Analytics_ References: Issue #7165 PR #7177 * Fix typo in article title (#7180) * Update 101-memory-management.md (#7181) * Update 101-memory-management.md * Update src/data/roadmaps/java/content/101-java-advanced-topics/101-memory-management.md --------- Co-authored-by: dsh <daniel.s.holdsworth@gmail.com> * Add cybersecurity content (#7136) * add 80+ topics * 7 topics * 19 topics * complete cyber roadmap * expanded internal links into full urls * Update DevOps roadmap resources (#7081) * 6 topics * 6 topics * 37 topics * 25 topics * 53 topics * finalised the updated content * Apply suggestions from code review Co-authored-by: Arik Chakma <arikchangma@gmail.com> Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> * reverted the removal of go link --------- Co-authored-by: Arik Chakma <arikchangma@gmail.com> Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> * chore: update roadmap content json (#7188) Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> * Update Rendering link to the new React docs (#7205) * Fix typo (#7202) * Update technical-documentation@X0xUzEP0S6SyspvqyoDDk.md (#7198) * Update technical-documentation@X0xUzEP0S6SyspvqyoDDk.md * Update src/data/roadmaps/devrel/content/technical-documentation@X0xUzEP0S6SyspvqyoDDk.md --------- Co-authored-by: dsh <daniel.s.holdsworth@gmail.com> * Update vs-code@j5nNSYI8s-cH8EA6G1EWY.md (#7195) * Update vs-code@j5nNSYI8s-cH8EA6G1EWY.md * Update src/data/roadmaps/devrel/content/vs-code@j5nNSYI8s-cH8EA6G1EWY.md --------- Co-authored-by: dsh <daniel.s.holdsworth@gmail.com> * Update basic-programming-skills@aSYXa25_0O2qQl1O-N3xl.md (#7193) * Update basic-programming-skills@aSYXa25_0O2qQl1O-N3xl.md * Update src/data/roadmaps/devrel/content/basic-programming-skills@aSYXa25_0O2qQl1O-N3xl.md --------- Co-authored-by: dsh <daniel.s.holdsworth@gmail.com> * [Update] Software architect: ITIL (#7191) * [Update] Software architect: ITIL * [Update] Android roadmap: Firebase docs (#7190) * [Update] Android roadmap: Firebase docs * [Update] Android roadmap: Firebase docs * Chibuike 19/add redis contents (#7186) * added content to 10 redis topics --------- Co-authored-by: dsh <daniel.s.holdsworth@gmail.com> Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> * Update links type to official (#7209) * add link for an article about rendering ,by kentcdodds.com (#7208) * add link for an article about rendering ,by kentcdodds.com * Update src/data/roadmaps/react/content/rendering@0uiGsC5SWavNdlFqizkKe.md --------- Co-authored-by: dsh <daniel.s.holdsworth@gmail.com> * Fix: Change "virutalenv" to "virtualenv" (#7184) * Update 102-control-flow.md (#7182) * feat: update dashboard layout (#7155) * Update button design for cards * Default visiblity to all * Fix qa roadmap issue and public projects * Update button design for profile --------- Co-authored-by: Vipul Patil <70363133+1VIP1786@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: kamranahmedse <4921183+kamranahmedse@users.noreply.github.com> Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com> Co-authored-by: Vedansh <superuser.ntsystems@outlook.com> Co-authored-by: Jiayou Zhu <43867657+ZlatanCN@users.noreply.github.com> Co-authored-by: dsh <daniel.s.holdsworth@gmail.com> Co-authored-by: Sarkis Kovlekjian <83559262+kenshanta@users.noreply.github.com> Co-authored-by: Mark <mac21macky@gmail.com> Co-authored-by: Rahul <rahulrp3031@gmail.com> Co-authored-by: tal bendet <68239430+t-bendet@users.noreply.github.com> Co-authored-by: Philip B. Krogh <71797726+phibkro@users.noreply.github.com> Co-authored-by: Brian Rodriguez <rzknairb@gmail.com> Co-authored-by: Obiechina Emmanuel <94564639+chibuike-19@users.noreply.github.com> Co-authored-by: Inkyung Huh <inkyung.huh@metric-studio.com> Co-authored-by: Ahmad Asaad <ahmadasaadh@gmail.com>
This commit is contained in:
@@ -54,13 +54,14 @@ export function DashboardPage(props: DashboardPageProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 pb-20 pt-8">
|
<div className="min-h-screen bg-gray-50 pb-20 pt-8">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="mb-6 sm:mb-8 flex flex-wrap items-center gap-1.5">
|
<div className="mb-6 flex flex-wrap items-center gap-1.5 sm:mb-8">
|
||||||
<DashboardTab
|
<DashboardTab
|
||||||
label="Personal"
|
label="Personal"
|
||||||
isActive={!selectedTeamId}
|
isActive={!selectedTeamId}
|
||||||
onClick={() => setSelectedTeamId(undefined)}
|
onClick={() => setSelectedTeamId(undefined)}
|
||||||
avatar={userAvatar}
|
avatar={userAvatar}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<>
|
<>
|
||||||
<DashboardTabSkeleton />
|
<DashboardTabSkeleton />
|
||||||
|
@@ -14,6 +14,9 @@ import { CheckEmoji } from '../ReactIcons/CheckEmoji.tsx';
|
|||||||
import { ConstructionEmoji } from '../ReactIcons/ConstructionEmoji.tsx';
|
import { ConstructionEmoji } from '../ReactIcons/ConstructionEmoji.tsx';
|
||||||
import { BookEmoji } from '../ReactIcons/BookEmoji.tsx';
|
import { BookEmoji } from '../ReactIcons/BookEmoji.tsx';
|
||||||
import { DashboardAiRoadmaps } from './DashboardAiRoadmaps.tsx';
|
import { DashboardAiRoadmaps } from './DashboardAiRoadmaps.tsx';
|
||||||
|
import type { AllowedProfileVisibility } from '../../api/user.ts';
|
||||||
|
import { PencilIcon, type LucideIcon } from 'lucide-react';
|
||||||
|
import { cn } from '../../lib/classname.ts';
|
||||||
|
|
||||||
type UserDashboardResponse = {
|
type UserDashboardResponse = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -21,6 +24,7 @@ type UserDashboardResponse = {
|
|||||||
avatar: string;
|
avatar: string;
|
||||||
headline: string;
|
headline: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
profileVisibility: AllowedProfileVisibility;
|
||||||
progresses: UserProgress[];
|
progresses: UserProgress[];
|
||||||
projects: ProjectStatusDocument[];
|
projects: ProjectStatusDocument[];
|
||||||
aiRoadmaps: {
|
aiRoadmaps: {
|
||||||
@@ -222,18 +226,20 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { username } = personalDashboardDetails || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="h-7 w-1/4 animate-pulse rounded-lg bg-gray-200"></div>
|
<div className="h-7 w-1/4 animate-pulse rounded-lg bg-gray-200"></div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-start sm:items-center justify-between flex-col sm:flex-row gap-1">
|
<div className="flex flex-col items-start justify-between gap-1 sm:flex-row sm:items-center">
|
||||||
<h2 className="text-lg font-medium">
|
<h2 className="text-lg font-medium">
|
||||||
Hi {name}, good {getCurrentPeriod()}!
|
Hi {name}, good {getCurrentPeriod()}!
|
||||||
</h2>
|
</h2>
|
||||||
<a
|
<a
|
||||||
href="/home"
|
href="/home"
|
||||||
className="text-xs font-medium bg-gray-200 hover:bg-gray-300 px-2.5 py-1 rounded-full text-gray-700 hover:text-black"
|
className="rounded-full bg-gray-200 px-2.5 py-1 text-xs font-medium text-gray-700 hover:bg-gray-300 hover:text-black"
|
||||||
>
|
>
|
||||||
Visit Homepage
|
Visit Homepage
|
||||||
</a>
|
</a>
|
||||||
@@ -253,8 +259,20 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
|
|||||||
<DashboardCard
|
<DashboardCard
|
||||||
imgUrl={avatarLink}
|
imgUrl={avatarLink}
|
||||||
title={name!}
|
title={name!}
|
||||||
description="Setup your profile"
|
description={
|
||||||
href="/account/update-profile"
|
username ? 'View your profile' : 'Setup your profile'
|
||||||
|
}
|
||||||
|
href={username ? `/u/${username}` : '/account/update-profile'}
|
||||||
|
{...(username && {
|
||||||
|
externalLinkIcon: PencilIcon,
|
||||||
|
externalLinkHref: '/account/update-profile',
|
||||||
|
externalLinkText: 'Edit',
|
||||||
|
})}
|
||||||
|
className={
|
||||||
|
!username
|
||||||
|
? 'border-dashed border-gray-500 bg-gray-100 hover:border-gray-500 hover:bg-gray-200'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DashboardCard
|
<DashboardCard
|
||||||
@@ -312,33 +330,61 @@ type DashboardCardProps = {
|
|||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
href: string;
|
href: string;
|
||||||
|
externalLinkIcon?: LucideIcon;
|
||||||
|
externalLinkText?: string;
|
||||||
|
externalLinkHref?: string;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function DashboardCard(props: DashboardCardProps) {
|
function DashboardCard(props: DashboardCardProps) {
|
||||||
const { icon: Icon, imgUrl, title, description, href } = props;
|
const {
|
||||||
|
icon: Icon,
|
||||||
|
imgUrl,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
href,
|
||||||
|
externalLinkHref,
|
||||||
|
externalLinkIcon: ExternalLinkIcon,
|
||||||
|
externalLinkText,
|
||||||
|
className,
|
||||||
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<div
|
||||||
href={href}
|
className={cn(
|
||||||
className="flex flex-col overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50"
|
'relative overflow-hidden',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{Icon && (
|
<a href={href} className="flex flex-col rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50">
|
||||||
<div className="px-4 pb-3 pt-4">
|
{Icon && (
|
||||||
<Icon className="size-6" />
|
<div className="px-4 pb-3 pt-4">
|
||||||
</div>
|
<Icon className="size-6" />
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{imgUrl && (
|
{imgUrl && (
|
||||||
<div className="px-4 pb-1.5 pt-3.5">
|
<div className="px-4 pb-1.5 pt-3.5">
|
||||||
<img src={imgUrl} alt={title} className="size-8 rounded-full" />
|
<img src={imgUrl} alt={title} className="size-8 rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex grow flex-col justify-center gap-0.5 p-4">
|
<div className="flex grow flex-col justify-center gap-0.5 p-4">
|
||||||
<h3 className="truncate font-medium text-black">{title}</h3>
|
<h3 className="truncate font-medium text-black">{title}</h3>
|
||||||
<p className="text-xs text-black">{description}</p>
|
<p className="text-xs text-black">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
{externalLinkHref && (
|
||||||
|
<a
|
||||||
|
href={externalLinkHref}
|
||||||
|
className="absolute right-1 top-1 flex items-center gap-1.5 rounded-md bg-gray-200 p-1 px-2 text-xs text-gray-600 hover:bg-gray-300 hover:text-black"
|
||||||
|
>
|
||||||
|
{ExternalLinkIcon && <ExternalLinkIcon className="size-3" />}
|
||||||
|
{externalLinkText}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -87,15 +87,13 @@ export function ProfileUsername(props: ProfileUsernameProps) {
|
|||||||
{currentUsername !== username && username && isUnique && (
|
{currentUsername !== username && username && isUnique && (
|
||||||
<span className="text-xs text-green-600">
|
<span className="text-xs text-green-600">
|
||||||
URL after update{' '}
|
URL after update{' '}
|
||||||
<a
|
<span
|
||||||
href={`${import.meta.env.DEV ? 'http://localhost:3000' : 'https://roadmap.sh'}/u/${username}`}
|
|
||||||
target="_blank"
|
|
||||||
className={
|
className={
|
||||||
'ml-0.5 rounded-md border border-purple-500 px-1.5 py-0.5 text-xs font-medium text-purple-700 transition-colors hover:bg-purple-500 hover:text-white'
|
'ml-0.5 rounded-md border border-purple-500 px-1.5 py-0.5 text-xs font-medium text-purple-700 transition-colors'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
roadmap.sh/u/{username}
|
roadmap.sh/u/{username}
|
||||||
</a>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
@@ -71,6 +71,7 @@ export function UpdatePublicProfileForm() {
|
|||||||
const [profileRoadmaps, setProfileRoadmaps] = useState<RoadmapType[]>([]);
|
const [profileRoadmaps, setProfileRoadmaps] = useState<RoadmapType[]>([]);
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isProfileUpdated, setIsProfileUpdated] = useState(false);
|
||||||
|
|
||||||
const { isCopied, copyText } = useCopyText();
|
const { isCopied, copyText } = useCopyText();
|
||||||
|
|
||||||
@@ -109,6 +110,7 @@ export function UpdatePublicProfileForm() {
|
|||||||
|
|
||||||
await loadProfileSettings();
|
await loadProfileSettings();
|
||||||
toast.success('Profile updated successfully');
|
toast.success('Profile updated successfully');
|
||||||
|
setIsProfileUpdated(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadProfileSettings = async () => {
|
const loadProfileSettings = async () => {
|
||||||
@@ -593,6 +595,42 @@ export function UpdatePublicProfileForm() {
|
|||||||
>
|
>
|
||||||
{isLoading ? 'Please wait..' : 'Save Profile'}
|
{isLoading ? 'Please wait..' : 'Save Profile'}
|
||||||
</button>
|
</button>
|
||||||
|
{isProfileUpdated && publicProfileUrl && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
'flex shrink-0 flex-row items-center gap-1 rounded-lg border border-black py-1.5 pl-2.5 pr-3.5 text-xs uppercase text-black transition-colors hover:bg-black hover:text-white',
|
||||||
|
isCopied
|
||||||
|
? 'border-green-600 bg-green-600 text-white hover:bg-green-600 hover:text-white'
|
||||||
|
: '',
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
copyText(`${window.location.origin}${publicProfileUrl}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isCopied ? (
|
||||||
|
<>
|
||||||
|
<CheckCircle className="size-4" />
|
||||||
|
Copied Profile URL
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Copy className="size-4" />
|
||||||
|
Copy Profile URL
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
className='flex shrink-0 flex-row items-center gap-1 rounded-lg border border-black py-1.5 pl-2.5 pr-3.5 text-xs uppercase text-black transition-colors hover:bg-black hover:text-white'
|
||||||
|
href={publicProfileUrl}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<ArrowUpRight className="size-4" />
|
||||||
|
View Profile
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -3,6 +3,7 @@ import {
|
|||||||
Globe,
|
Globe,
|
||||||
LinkedinIcon,
|
LinkedinIcon,
|
||||||
Mail,
|
Mail,
|
||||||
|
Pencil,
|
||||||
Twitter,
|
Twitter,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import type { GetPublicProfileResponse } from '../../api/user';
|
import type { GetPublicProfileResponse } from '../../api/user';
|
||||||
@@ -15,11 +16,12 @@ type UserPublicProfileHeaderProps = {
|
|||||||
export function UserPublicProfileHeader(props: UserPublicProfileHeaderProps) {
|
export function UserPublicProfileHeader(props: UserPublicProfileHeaderProps) {
|
||||||
const { userDetails } = props;
|
const { userDetails } = props;
|
||||||
|
|
||||||
const { name, links, publicConfig, avatar, email } = userDetails;
|
const { name, links, publicConfig, avatar, email, isOwnProfile } =
|
||||||
|
userDetails;
|
||||||
const { headline, isAvailableForHire, isEmailVisible } = publicConfig!;
|
const { headline, isAvailableForHire, isEmailVisible } = publicConfig!;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container flex items-center gap-6 rounded-xl border bg-white p-8">
|
<div className="container relative flex items-center gap-6 rounded-xl border bg-white p-8">
|
||||||
<img
|
<img
|
||||||
src={
|
src={
|
||||||
avatar
|
avatar
|
||||||
@@ -27,7 +29,7 @@ export function UserPublicProfileHeader(props: UserPublicProfileHeaderProps) {
|
|||||||
: '/images/default-avatar.png'
|
: '/images/default-avatar.png'
|
||||||
}
|
}
|
||||||
alt={name}
|
alt={name}
|
||||||
className="h-32 w-32 object-cover rounded-full"
|
className="h-32 w-32 rounded-full object-cover"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -51,6 +53,16 @@ export function UserPublicProfileHeader(props: UserPublicProfileHeaderProps) {
|
|||||||
{isEmailVisible && <UserLink href={`mailto:${email}`} icon={Mail} />}
|
{isEmailVisible && <UserLink href={`mailto:${email}`} icon={Mail} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isOwnProfile && (
|
||||||
|
<a
|
||||||
|
href="/account/update-profile"
|
||||||
|
className="absolute right-4 top-4 flex items-center gap-1.5 text-sm text-gray-500 hover:text-black"
|
||||||
|
>
|
||||||
|
<Pencil className="h-3 w-3 stroke-2" />
|
||||||
|
Edit Profile
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -38,6 +38,10 @@ export function UserPublicProjects(props: UserPublicProjectsProps) {
|
|||||||
return 0;
|
return 0;
|
||||||
}) || [];
|
}) || [];
|
||||||
|
|
||||||
|
if (!enrichedProjects.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<h2 className="mb-2 text-xs uppercase tracking-wide text-gray-400">
|
<h2 className="mb-2 text-xs uppercase tracking-wide text-gray-400">
|
||||||
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user