Skip to content

Commit 203bbc6

Browse files
Add dashboard redesign (kamranahmedse#8189)
* Improve personal dashboard design * Add projects toggle * Improve UI for AI roadmaps * Add builtin roadmaps and best practices * Collapse and expand * Move to separate files * Refactor hero items group * Collapse expand * Add expand collapse in hero title * Add collapse expand of groups * Style updates * Collapse expand * Remove global collapse expand * Update hero title * Fix spacing * Empty screen handling * Add empty message * Add profile button * Add questions listing on dashboard * Add guides and videos on dashboard * Responsiveness * Update messaging
1 parent 31a8521 commit 203bbc6

28 files changed

+1214
-915
lines changed

.astro/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"enabled": false
44
},
55
"_variables": {
6-
"lastUpdateCheck": 1738019390029
6+
"lastUpdateCheck": 1739229597159
77
}
88
}
Lines changed: 82 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
1+
import { useStore } from '@nanostores/react';
12
import { useEffect, useState } from 'react';
2-
import { httpGet } from '../../lib/http';
3+
import { cn } from '../../../editor/utils/classname';
4+
import { useParams } from '../../hooks/use-params';
35
import { useToast } from '../../hooks/use-toast';
4-
import { useStore } from '@nanostores/react';
6+
import { httpGet } from '../../lib/http';
7+
import { getUser } from '../../lib/jwt';
58
import { $teamList } from '../../stores/team';
69
import type { TeamListResponse } from '../TeamDropdown/TeamDropdown';
7-
import { DashboardTab } from './DashboardTab';
10+
import { DashboardTabButton } from './DashboardTabButton';
811
import { PersonalDashboard, type BuiltInRoadmap } from './PersonalDashboard';
912
import { TeamDashboard } from './TeamDashboard';
10-
import { getUser } from '../../lib/jwt';
11-
import { useParams } from '../../hooks/use-params';
13+
import type { QuestionGroupType } from '../../lib/question-group';
14+
import type { GuideFileType } from '../../lib/guide';
15+
import type { VideoFileType } from '../../lib/video';
1216

1317
type DashboardPageProps = {
1418
builtInRoleRoadmaps?: BuiltInRoadmap[];
1519
builtInSkillRoadmaps?: BuiltInRoadmap[];
1620
builtInBestPractices?: BuiltInRoadmap[];
1721
isTeamPage?: boolean;
22+
questionGroups?: QuestionGroupType[];
23+
guides?: GuideFileType[];
24+
videos?: VideoFileType[];
1825
};
1926

2027
export function DashboardPage(props: DashboardPageProps) {
@@ -23,6 +30,9 @@ export function DashboardPage(props: DashboardPageProps) {
2330
builtInBestPractices,
2431
builtInSkillRoadmaps,
2532
isTeamPage = false,
33+
questionGroups,
34+
guides,
35+
videos,
2636
} = props;
2737

2838
const currentUser = getUser();
@@ -66,78 +76,80 @@ export function DashboardPage(props: DashboardPageProps) {
6676
: '/images/default-avatar.png';
6777

6878
return (
69-
<div className="min-h-screen bg-gray-50 pb-20 pt-8">
70-
<div className="container">
71-
<div className="mb-6 flex flex-wrap items-center gap-1.5 sm:mb-8">
72-
<DashboardTab
73-
label="Personal"
74-
isActive={!selectedTeamId && !isTeamPage}
75-
href="/dashboard"
76-
avatar={userAvatar}
77-
/>
78-
79-
{isLoading && (
80-
<>
81-
<DashboardTabSkeleton />
82-
<DashboardTabSkeleton />
83-
</>
84-
)}
85-
86-
{!isLoading && (
87-
<>
88-
{teamList.map((team) => {
89-
const { avatar } = team;
90-
const avatarUrl = avatar
91-
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${avatar}`
92-
: '/images/default-avatar.png';
93-
return (
94-
<DashboardTab
95-
key={team._id}
96-
label={team.name}
97-
isActive={team._id === selectedTeamId}
98-
{...(team.status === 'invited'
99-
? {
100-
href: `/respond-invite?i=${team.memberId}`,
101-
}
102-
: {
103-
href: `/team?t=${team._id}`,
104-
})}
105-
avatar={avatarUrl}
106-
/>
107-
);
108-
})}
109-
<DashboardTab
110-
label="+ Create Team"
111-
isActive={false}
112-
href="/team/new"
113-
className="border border-dashed border-gray-300 bg-transparent px-3 text-[13px] text-sm text-gray-500 hover:border-gray-600 hover:text-black"
114-
/>
115-
</>
116-
)}
79+
<>
80+
<div
81+
className={cn('bg-slate-900', {
82+
'striped-loader-slate': isLoading,
83+
})}
84+
>
85+
<div className="bg-slate-800/30 py-5">
86+
<div className="container flex flex-wrap items-center gap-1.5">
87+
<DashboardTabButton
88+
label="Personal"
89+
isActive={!selectedTeamId && !isTeamPage}
90+
href="/dashboard"
91+
avatar={userAvatar}
92+
/>
93+
94+
{!isLoading && (
95+
<>
96+
{teamList.map((team) => {
97+
const { avatar } = team;
98+
const avatarUrl = avatar
99+
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${avatar}`
100+
: '/images/default-avatar.png';
101+
return (
102+
<DashboardTabButton
103+
key={team._id}
104+
label={team.name}
105+
isActive={team._id === selectedTeamId}
106+
{...(team.status === 'invited'
107+
? {
108+
href: `/respond-invite?i=${team.memberId}`,
109+
}
110+
: {
111+
href: `/team?t=${team._id}`,
112+
})}
113+
avatar={avatarUrl}
114+
/>
115+
);
116+
})}
117+
<DashboardTabButton
118+
label="+ Create Team"
119+
isActive={false}
120+
href="/team/new"
121+
className="border border-dashed border-slate-700 bg-transparent px-3 text-[13px] text-sm text-gray-500 hover:border-solid hover:border-slate-700 hover:text-gray-400"
122+
/>
123+
</>
124+
)}
125+
</div>
117126
</div>
127+
</div>
118128

129+
<div className="">
119130
{!selectedTeamId && !isTeamPage && (
120-
<PersonalDashboard
121-
builtInRoleRoadmaps={builtInRoleRoadmaps}
122-
builtInSkillRoadmaps={builtInSkillRoadmaps}
123-
builtInBestPractices={builtInBestPractices}
124-
/>
131+
<div className="bg-slate-900">
132+
<PersonalDashboard
133+
builtInRoleRoadmaps={builtInRoleRoadmaps}
134+
builtInSkillRoadmaps={builtInSkillRoadmaps}
135+
builtInBestPractices={builtInBestPractices}
136+
questionGroups={questionGroups}
137+
guides={guides}
138+
videos={videos}
139+
/>
140+
</div>
125141
)}
126142

127143
{(selectedTeamId || isTeamPage) && (
128-
<TeamDashboard
129-
builtInRoleRoadmaps={builtInRoleRoadmaps!}
130-
builtInSkillRoadmaps={builtInSkillRoadmaps!}
131-
teamId={selectedTeamId!}
132-
/>
144+
<div className="container">
145+
<TeamDashboard
146+
builtInRoleRoadmaps={builtInRoleRoadmaps!}
147+
builtInSkillRoadmaps={builtInSkillRoadmaps!}
148+
teamId={selectedTeamId!}
149+
/>
150+
</div>
133151
)}
134152
</div>
135-
</div>
136-
);
137-
}
138-
139-
function DashboardTabSkeleton() {
140-
return (
141-
<div className="h-[30px] w-[114px] animate-pulse rounded-md border bg-white"></div>
153+
</>
142154
);
143155
}

src/components/Dashboard/DashboardTab.tsx renamed to src/components/Dashboard/DashboardTabButton.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type DashboardTabProps = {
1111
icon?: ReactNode;
1212
};
1313

14-
export function DashboardTab(props: DashboardTabProps) {
14+
export function DashboardTabButton(props: DashboardTabProps) {
1515
const { isActive, onClick, label, className, href, avatar, icon } = props;
1616

1717
const Slot = href ? 'a' : 'button';
@@ -20,8 +20,10 @@ export function DashboardTab(props: DashboardTabProps) {
2020
<Slot
2121
onClick={onClick}
2222
className={cn(
23-
'flex h-[30px] shrink-0 items-center gap-1 rounded-md border bg-white p-1.5 px-2 text-sm leading-none text-gray-600',
24-
isActive ? 'border-gray-500 bg-gray-200 text-gray-900' : '',
23+
'flex h-[30px] shrink-0 items-center gap-1 rounded-md border border-slate-700 bg-slate-800 p-1.5 pl-2 pr-3 text-sm leading-none text-gray-400 transition-colors hover:bg-slate-700',
24+
isActive
25+
? 'border-slate-200 bg-slate-200 text-gray-900 hover:bg-slate-200'
26+
: '',
2527
className,
2628
)}
2729
{...(href ? { href } : {})}
@@ -30,7 +32,7 @@ export function DashboardTab(props: DashboardTabProps) {
3032
<img
3133
src={avatar}
3234
alt="avatar"
33-
className="h-4 w-4 mr-0.5 rounded-full object-cover"
35+
className="mr-0.5 h-4 w-4 rounded-full object-cover"
3436
/>
3537
)}
3638
{icon}

0 commit comments

Comments
 (0)