UI changes for dashboard

feat/dashboard
Kamran Ahmed 1 month ago
parent cf743ee618
commit 10e493de99
  1. 8
      src/components/Dashboard/DashboardPage.tsx
  2. 2
      src/components/Dashboard/DashboardTab.tsx
  3. 33
      src/components/Dashboard/PersonalDashboard.tsx
  4. 39
      src/components/ReactIcons/BookEmoji.tsx
  5. 36
      src/components/ReactIcons/BuildEmoji.tsx
  6. 37
      src/components/ReactIcons/BulbEmoji.tsx
  7. 6
      src/components/ReactIcons/CheckEmoji.tsx
  8. 24
      src/components/ReactIcons/ConstructionEmoji.tsx

@ -54,7 +54,7 @@ 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-8 flex flex-wrap items-center gap-1"> <div className="mb-8 flex flex-wrap items-center gap-1.5">
<DashboardTab <DashboardTab
label="Personal" label="Personal"
isActive={!selectedTeamId} isActive={!selectedTeamId}
@ -64,8 +64,6 @@ export function DashboardPage(props: DashboardPageProps) {
{isLoading && ( {isLoading && (
<> <>
<DashboardTabSkeleton /> <DashboardTabSkeleton />
<DashboardTabSkeleton />
<DashboardTabSkeleton />
</> </>
)} )}
@ -98,7 +96,7 @@ export function DashboardPage(props: DashboardPageProps) {
label="+ Create Team" label="+ Create Team"
isActive={false} isActive={false}
href="/team/new" href="/team/new"
className="border border-dashed text-gray-600 hover:border-gray-600 hover:text-black" className="border bg-transparent border-dashed text-gray-500 border-gray-300 text-sm px-3 hover:border-gray-600 hover:text-black"
/> />
</> </>
)} )}
@ -119,6 +117,6 @@ export function DashboardPage(props: DashboardPageProps) {
function DashboardTabSkeleton() { function DashboardTabSkeleton() {
return ( return (
<div className="h-[30px] w-20 animate-pulse rounded-md border bg-white"></div> <div className="h-[30px] w-[120px] animate-pulse rounded-md border bg-white"></div>
); );
} }

@ -30,7 +30,7 @@ export function DashboardTab(props: DashboardTabProps) {
<img <img
src={avatar} src={avatar}
alt="avatar" alt="avatar"
className="h-4 w-4 rounded-full object-cover" className="h-4 w-4 mr-0.5 rounded-full object-cover"
/> />
)} )}
{icon} {icon}

@ -1,4 +1,4 @@
import { useEffect, useState, type ReactNode } from 'react'; import { type JSXElementConstructor, useEffect, useState } from 'react';
import { httpGet } from '../../lib/http'; import { httpGet } from '../../lib/http';
import type { UserProgress } from '../TeamProgress/TeamProgressPage'; import type { UserProgress } from '../TeamProgress/TeamProgressPage';
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions'; import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
@ -10,6 +10,9 @@ import { RecommendedRoadmaps } from './RecommendedRoadmaps';
import { ProgressStack } from './ProgressStack'; import { ProgressStack } from './ProgressStack';
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { $accountStreak, type StreakResponse } from '../../stores/streak'; import { $accountStreak, type StreakResponse } from '../../stores/streak';
import { CheckEmoji } from '../ReactIcons/CheckEmoji.tsx';
import { ConstructionEmoji } from '../ReactIcons/ConstructionEmoji.tsx';
import { BookEmoji } from '../ReactIcons/BookEmoji.tsx';
type UserDashboardResponse = { type UserDashboardResponse = {
name: string; name: string;
@ -222,7 +225,7 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
</h2> </h2>
)} )}
<div className="mt-8 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-4"> <div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-4">
{isLoading ? ( {isLoading ? (
<> <>
<DashboardCardSkeleton /> <DashboardCardSkeleton />
@ -233,10 +236,10 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
) : ( ) : (
<> <>
<a <a
className="overflow-hidden rounded-lg border border-gray-300 bg-white" className="overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50"
href="/account/update-profile" href="/account/update-profile"
> >
<div className="px-4 py-2.5"> <div className="px-4 pb-1.5 pt-3.5">
<img <img
src={avatarLink} src={avatarLink}
alt={name} alt={name}
@ -251,19 +254,19 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
</a> </a>
<DashboardCard <DashboardCard
icon={'💡'} icon={BookEmoji}
title="Learn a new Skill" title="Learn a new Skill"
description="Visit our Roadmaps" description="Visit our Roadmaps"
href="/roadmaps" href="/roadmaps"
/> />
<DashboardCard <DashboardCard
icon={'🏗'} icon={ConstructionEmoji}
title="Practice your skills" title="Practice your skills"
description="Visit Projects" description="Visit Projects"
href="/backend/projects" href="/backend/projects"
/> />
<DashboardCard <DashboardCard
icon={'📚'} icon={CheckEmoji}
title="Do things right way" title="Do things right way"
description="Visit Best Practices" description="Visit Best Practices"
href="/best-practices" href="/best-practices"
@ -299,28 +302,26 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
} }
type DashboardCardProps = { type DashboardCardProps = {
icon: string | ReactNode; icon: JSXElementConstructor<any>;
title: string; title: string;
description: string; description: string;
href: string; href: string;
}; };
function DashboardCard(props: DashboardCardProps) { function DashboardCard(props: DashboardCardProps) {
const { icon, title, description, href } = props; const { icon: Icon, title, description, href } = props;
return ( return (
<a <a
href={href} href={href}
target="_blank" target="_blank"
className="flex flex-col overflow-hidden rounded-lg border border-gray-300" className="flex flex-col overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50"
> >
<div className="border-b border-gray-300 bg-gray-100 px-4 py-2.5"> <div className="px-4 pb-3 pt-4">
<span className="flex size-8 items-center justify-center text-xl"> <Icon className="size-6" />
{icon}
</span>
</div> </div>
<div className="flex grow flex-col justify-center gap-0.5 bg-white 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>
@ -330,6 +331,6 @@ function DashboardCard(props: DashboardCardProps) {
function DashboardCardSkeleton() { function DashboardCardSkeleton() {
return ( return (
<div className="h-[129px] animate-pulse rounded-lg border border-gray-300 bg-white"></div> <div className="h-[128px] animate-pulse rounded-lg border border-gray-300 bg-white"></div>
); );
} }

@ -0,0 +1,39 @@
import type { SVGProps } from 'react';
import React from 'react';
export function BookEmoji(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 36 36"
{...props}
>
<path
fill="#3e721d"
d="M35 26a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V6.313C1 4.104 6.791 0 9 0h20.625C32.719 0 35 2.312 35 5.375z"
></path>
<path
fill="#ccd6dd"
d="M33 30a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V6c0-4.119-.021-4 5-4h21a4 4 0 0 1 4 4z"
></path>
<path
fill="#e1e8ed"
d="M31 31a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h24a3 3 0 0 1 3 3z"
></path>
<path
fill="#5c913b"
d="M31 32a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V10a4 4 0 0 1 4-4h21a4 4 0 0 1 4 4z"
></path>
<path
fill="#77b255"
d="M29 32a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V12a4 4 0 0 1 4-4h19.335C27.544 8 29 9.456 29 11.665z"
></path>
<path
fill="#3e721d"
d="M6 6C4.312 6 4.269 4.078 5 3.25C5.832 2.309 7.125 2 9.438 2H11V0H8.281C4.312 0 1 2.5 1 5.375V32a4 4 0 0 0 4 4h2V6z"
></path>
</svg>
);
}

@ -0,0 +1,36 @@
import React from 'react';
import type { SVGProps } from 'react';
export function BuildEmoji(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 36 36"
{...props}
>
<path
fill="#66757f"
d="M28.25 8.513a.263.263 0 0 0-.263-.263h-.475a.263.263 0 0 0-.263.263v11.475c0 .145.117.263.263.263h.475a.263.263 0 0 0 .263-.263z"
></path>
<g fill="#f19020">
<circle cx={27.75} cy={19.75} r={1.5}></circle>
<circle cx={27.75} cy={22.25} r={1}></circle>
</g>
<path
fill="#bd2032"
d="M33.25 8.25h-4.129L9.946.29L9.944.289h-.001c-.016-.007-.032-.005-.047-.01C9.849.265 9.802.25 9.75.25h-.002a.5.5 0 0 0-.19.038a.5.5 0 0 0-.122.082c-.012.009-.026.014-.037.025a.5.5 0 0 0-.11.164V.56c-.004.009-.003.02-.006.029l-5.541 7.81l-.006.014a.99.99 0 0 0-.486.837v2a1 1 0 0 0 1 1h1.495L2.031 34H.25v2h18.958v-2h-1.74l-3.713-21.75H33.25a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1m-21.769 4L9.75 13.639L8.02 12.25zM9.75 21.3l3.667 2.404l-3.667 2l-3.667-2zm-3.639.71l.474-2.784l1.866 1.223zm4.938-1.561l1.87-1.225l.477 2.789zm-1.299-.866l-2.828-1.885l2.828-2.322l2.828 2.322zm-2.563-3.887l.362-2.127l1.131.928zm3.633-1.198l1.132-.929l.364 2.13zM5.073 8.25L9.25 2.362V6.25h-2a1 1 0 0 0-1 1v1zm.53 16.738l2.73 1.489l-3.29 1.794zM15.443 34H4.067l.686-4.024L9.75 27.25l5.006 2.731zm-1.54-9.015l.562 3.291l-3.298-1.799zM13.25 8.25v-1a1 1 0 0 0-1-1h-2V1.499L26.513 8.25zm2 3h-1.16v-2h1.16zm3 0h-2v-2h2zm3 0h-2v-2h2zm3 0h-2v-2h2zm3 0h-2v-2h2zm3 0h-2v-2h2zm3-.5a.5.5 0 0 1-.5.5h-1.5v-2h1.5a.5.5 0 0 1 .5.5z"
></path>
<path
fill="#4b545d"
d="M12.25 7.25h-2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h3v-4z"
></path>
<path fill="#cdd7df" d="M11.25 7.25h2v4h-2z"></path>
<path
fill="#66757f"
d="M34.844 24v-1H20.656v1h.844v2.469h-.844v1h14.188v-1H34V24z"
></path>
</svg>
);
}

@ -0,0 +1,37 @@
// twitter bulb emoji
import type { SVGProps } from 'react';
type BulbEmojiProps = SVGProps<SVGSVGElement>;
export function BulbEmoji(props: BulbEmojiProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 36 36"
{...props}
>
<path
fill="#FFD983"
d="M29 11.06c0 6.439-5 7.439-5 13.44c0 3.098-3.123 3.359-5.5 3.359c-2.053 0-6.586-.779-6.586-3.361C11.914 18.5 7 17.5 7 11.06C7 5.029 12.285.14 18.083.14C23.883.14 29 5.029 29 11.06"
></path>
<path
fill="#CCD6DD"
d="M22.167 32.5c0 .828-2.234 2.5-4.167 2.5s-4.167-1.672-4.167-2.5S16.066 32 18 32s4.167-.328 4.167.5"
></path>
<path
fill="#FFCC4D"
d="M22.707 10.293a1 1 0 0 0-1.414 0L18 13.586l-3.293-3.293a.999.999 0 1 0-1.414 1.414L17 15.414V26a1 1 0 1 0 2 0V15.414l3.707-3.707a1 1 0 0 0 0-1.414"
></path>
<path
fill="#99AAB5"
d="M24 31a2 2 0 0 1-2 2h-8a2 2 0 0 1-2-2v-6h12z"
></path>
<path
fill="#CCD6DD"
d="M11.999 32a1 1 0 0 1-.163-1.986l12-2a.994.994 0 0 1 1.15.822a1 1 0 0 1-.822 1.15l-12 2a1 1 0 0 1-.165.014m0-4a1 1 0 0 1-.163-1.986l12-2a.995.995 0 0 1 1.15.822a1 1 0 0 1-.822 1.15l-12 2a1 1 0 0 1-.165.014"
></path>
</svg>
);
}

@ -0,0 +1,6 @@
import React from 'react';
import type { SVGProps } from 'react';
export function CheckEmoji(props: SVGProps<SVGSVGElement>) {
return (<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 36 36" {...props}><path fill="#77b255" d="M36 32a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4h28a4 4 0 0 1 4 4z"></path><path fill="#fff" d="M29.28 6.362a2.5 2.5 0 0 0-3.458.736L14.936 23.877l-5.029-4.65a2.5 2.5 0 1 0-3.394 3.671l7.209 6.666c.48.445 1.09.665 1.696.665c.673 0 1.534-.282 2.099-1.139c.332-.506 12.5-19.27 12.5-19.27a2.5 2.5 0 0 0-.737-3.458"></path></svg>);
}

@ -0,0 +1,24 @@
import type { SVGProps } from 'react';
import React from 'react';
export function ConstructionEmoji(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 36 36"
{...props}
>
<path
fill="#ffcc4d"
d="M36 15a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V7a4 4 0 0 1 4-4h28a4 4 0 0 1 4 4z"
></path>
<path
fill="#292f33"
d="M6 3H4a4 4 0 0 0-4 4v2zm6 0L0 15c0 1.36.682 2.558 1.72 3.28L17 3zM7 19h5L28 3h-5zm16 0L35.892 6.108A4 4 0 0 0 33.64 3.36L18 19zm13-4v-3l-7 7h3a4 4 0 0 0 4-4"
></path>
<path fill="#99aab5" d="M4 19h5v14H4zm23 0h5v14h-5z"></path>
</svg>
);
}
Loading…
Cancel
Save