mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-01 06:50:26 +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 (
|
||||
<div className="min-h-screen bg-gray-50 pb-20 pt-8">
|
||||
<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
|
||||
label="Personal"
|
||||
isActive={!selectedTeamId}
|
||||
onClick={() => setSelectedTeamId(undefined)}
|
||||
avatar={userAvatar}
|
||||
/>
|
||||
|
||||
{isLoading && (
|
||||
<>
|
||||
<DashboardTabSkeleton />
|
||||
|
@@ -14,6 +14,9 @@ import { CheckEmoji } from '../ReactIcons/CheckEmoji.tsx';
|
||||
import { ConstructionEmoji } from '../ReactIcons/ConstructionEmoji.tsx';
|
||||
import { BookEmoji } from '../ReactIcons/BookEmoji.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 = {
|
||||
name: string;
|
||||
@@ -21,6 +24,7 @@ type UserDashboardResponse = {
|
||||
avatar: string;
|
||||
headline: string;
|
||||
username: string;
|
||||
profileVisibility: AllowedProfileVisibility;
|
||||
progresses: UserProgress[];
|
||||
projects: ProjectStatusDocument[];
|
||||
aiRoadmaps: {
|
||||
@@ -222,18 +226,20 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
|
||||
return 0;
|
||||
});
|
||||
|
||||
const { username } = personalDashboardDetails || {};
|
||||
|
||||
return (
|
||||
<section>
|
||||
{isLoading ? (
|
||||
<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">
|
||||
Hi {name}, good {getCurrentPeriod()}!
|
||||
</h2>
|
||||
<a
|
||||
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
|
||||
</a>
|
||||
@@ -253,8 +259,20 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
|
||||
<DashboardCard
|
||||
imgUrl={avatarLink}
|
||||
title={name!}
|
||||
description="Setup your profile"
|
||||
href="/account/update-profile"
|
||||
description={
|
||||
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
|
||||
@@ -312,33 +330,61 @@ type DashboardCardProps = {
|
||||
title: string;
|
||||
description: string;
|
||||
href: string;
|
||||
externalLinkIcon?: LucideIcon;
|
||||
externalLinkText?: string;
|
||||
externalLinkHref?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
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 (
|
||||
<a
|
||||
href={href}
|
||||
className="flex flex-col overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50"
|
||||
<div
|
||||
className={cn(
|
||||
'relative overflow-hidden',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{Icon && (
|
||||
<div className="px-4 pb-3 pt-4">
|
||||
<Icon className="size-6" />
|
||||
</div>
|
||||
)}
|
||||
<a href={href} className="flex flex-col rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50">
|
||||
{Icon && (
|
||||
<div className="px-4 pb-3 pt-4">
|
||||
<Icon className="size-6" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{imgUrl && (
|
||||
<div className="px-4 pb-1.5 pt-3.5">
|
||||
<img src={imgUrl} alt={title} className="size-8 rounded-full" />
|
||||
</div>
|
||||
)}
|
||||
{imgUrl && (
|
||||
<div className="px-4 pb-1.5 pt-3.5">
|
||||
<img src={imgUrl} alt={title} className="size-8 rounded-full" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex grow flex-col justify-center gap-0.5 p-4">
|
||||
<h3 className="truncate font-medium text-black">{title}</h3>
|
||||
<p className="text-xs text-black">{description}</p>
|
||||
</div>
|
||||
</a>
|
||||
<div className="flex grow flex-col justify-center gap-0.5 p-4">
|
||||
<h3 className="truncate font-medium text-black">{title}</h3>
|
||||
<p className="text-xs text-black">{description}</p>
|
||||
</div>
|
||||
</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 && (
|
||||
<span className="text-xs text-green-600">
|
||||
URL after update{' '}
|
||||
<a
|
||||
href={`${import.meta.env.DEV ? 'http://localhost:3000' : 'https://roadmap.sh'}/u/${username}`}
|
||||
target="_blank"
|
||||
<span
|
||||
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}
|
||||
</a>
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
|
@@ -71,6 +71,7 @@ export function UpdatePublicProfileForm() {
|
||||
const [profileRoadmaps, setProfileRoadmaps] = useState<RoadmapType[]>([]);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isProfileUpdated, setIsProfileUpdated] = useState(false);
|
||||
|
||||
const { isCopied, copyText } = useCopyText();
|
||||
|
||||
@@ -109,6 +110,7 @@ export function UpdatePublicProfileForm() {
|
||||
|
||||
await loadProfileSettings();
|
||||
toast.success('Profile updated successfully');
|
||||
setIsProfileUpdated(true);
|
||||
};
|
||||
|
||||
const loadProfileSettings = async () => {
|
||||
@@ -593,6 +595,42 @@ export function UpdatePublicProfileForm() {
|
||||
>
|
||||
{isLoading ? 'Please wait..' : 'Save Profile'}
|
||||
</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>
|
||||
</div>
|
||||
);
|
||||
|
@@ -3,6 +3,7 @@ import {
|
||||
Globe,
|
||||
LinkedinIcon,
|
||||
Mail,
|
||||
Pencil,
|
||||
Twitter,
|
||||
} from 'lucide-react';
|
||||
import type { GetPublicProfileResponse } from '../../api/user';
|
||||
@@ -15,11 +16,12 @@ type UserPublicProfileHeaderProps = {
|
||||
export function UserPublicProfileHeader(props: UserPublicProfileHeaderProps) {
|
||||
const { userDetails } = props;
|
||||
|
||||
const { name, links, publicConfig, avatar, email } = userDetails;
|
||||
const { name, links, publicConfig, avatar, email, isOwnProfile } =
|
||||
userDetails;
|
||||
const { headline, isAvailableForHire, isEmailVisible } = publicConfig!;
|
||||
|
||||
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
|
||||
src={
|
||||
avatar
|
||||
@@ -27,7 +29,7 @@ export function UserPublicProfileHeader(props: UserPublicProfileHeaderProps) {
|
||||
: '/images/default-avatar.png'
|
||||
}
|
||||
alt={name}
|
||||
className="h-32 w-32 object-cover rounded-full"
|
||||
className="h-32 w-32 rounded-full object-cover"
|
||||
/>
|
||||
|
||||
<div>
|
||||
@@ -51,6 +53,16 @@ export function UserPublicProfileHeader(props: UserPublicProfileHeaderProps) {
|
||||
{isEmailVisible && <UserLink href={`mailto:${email}`} icon={Mail} />}
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
@@ -38,6 +38,10 @@ export function UserPublicProjects(props: UserPublicProjectsProps) {
|
||||
return 0;
|
||||
}) || [];
|
||||
|
||||
if (!enrichedProjects.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-5">
|
||||
<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