Redesign the roadcard page

feat/roadcards
Kamran Ahmed 1 year ago
parent f6e88fcc7a
commit 17b4b05368
  1. 134
      src/components/RoadCard/RoadCardPage.tsx
  2. 24
      src/components/RoadCard/RoadmapSelect.tsx
  3. 10
      src/helper/get-badge-link.ts

@ -1,14 +1,29 @@
import { useEffect, useState } from 'preact/hooks'; import { useState } from 'preact/hooks';
import { useCopyText } from '../../hooks/use-copy-text'; import { useCopyText } from '../../hooks/use-copy-text';
import { useAuth } from '../../hooks/use-auth'; import { useAuth } from '../../hooks/use-auth';
import { TallBadgeTab } from './TallBadgeTab'; import { TallBadgeTab } from './TallBadgeTab';
import { WideBadgeTab } from './WideBadgeTab'; import { WideBadgeTab } from './WideBadgeTab';
import CopyIcon from '../../icons/copy.svg'; import CopyIcon from '../../icons/copy.svg';
import { RoadmapSelect, RoadmapSelectProps } from './RoadmapSelect'; import { RoadmapSelect } from './RoadmapSelect';
import { httpGet } from '../../lib/http';
import { pageProgressMessage } from '../../stores/page'; type StepCounterProps = {
import type { UserProgressResponse } from '../HeroSection/FavoriteRoadmaps'; step: number;
};
function StepCounter(props: StepCounterProps) {
const { step } = props;
return (
<span
className={
'flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-300 text-white'
}
>
{step}
</span>
);
}
type EditorProps = { type EditorProps = {
title: string; title: string;
@ -51,71 +66,80 @@ export type BadgeProps = {
}; };
export function RoadCardPage() { export function RoadCardPage() {
const [selectedBadge, setSelectedBadge] = useState<'tall' | 'wide'>('tall'); const [version, setVersion] = useState<'tall' | 'wide'>('tall');
const [progress, setProgress] = useState<RoadmapSelectProps['options']>([]); const [variant, setVariant] = useState<'dark' | 'light'>('dark');
const [selectedRoadmaps, setSelectedRoadmap] = useState<
RoadmapSelectProps['options']
>([]);
const user = useAuth(); const user = useAuth();
if (!user) { if (!user) {
return null; return null;
} }
const badgeUrl = new URL(
`${import.meta.env.PUBLIC_API_URL}/v1-badge/${version}/${user?.id}`
);
badgeUrl.searchParams.set('variant', variant);
return ( return (
<> <>
<div className="mb-8 hidden md:block"> <div className="mb-4 flex items-start gap-4">
<h2 className="text-3xl font-bold sm:text-4xl">Road Card</h2> <StepCounter step={1} />
<p className="mt-2 text-gray-400"> <div>
Grab your #RoadCard and share your progress with others. <span className="mb-3 flex items-center gap-2 text-sm leading-none text-gray-400">
</p> Select progress to show (maximum 4 items)
</span>
<div className="flex min-h-[30px] flex-wrap">
<RoadmapSelect />
</div>
</div>
</div> </div>
<div> <div className="mb-4 flex items-start gap-4">
<div className="mb-6 flex items-center border-b"> <StepCounter step={2} />
<div className="flex items-center"> <div>
<button <span className="mb-3 flex items-center gap-2 text-sm leading-none text-gray-400">
className={`relative top-px flex items-center justify-center px-3 pb-3 leading-none shadow-gray-600 ${ Select Mode (Dark vs Light)
selectedBadge === 'tall' </span>
? 'shadow-[inset_0_-1px_0_var(--tw-shadow-color)]'
: 'text-gray-600'
}`}
onClick={() => {
setSelectedBadge('tall');
}}
>
Tall
</button>
<button <div className="flex gap-2">
className={`relative top-px flex items-center justify-center px-3 pb-3 leading-none shadow-gray-600 ${ <button className="rounded-md border p-1 px-2 text-sm">Dark</button>
selectedBadge === 'wide' <button className="rounded-md border p-1 px-2 text-sm">
? 'shadow-[inset_0_-1px_0_var(--tw-shadow-color)]' Light
: 'text-gray-600'
}`}
onClick={() => {
setSelectedBadge('wide');
}}
>
Wide
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<RoadmapSelect <div className="mb-4 flex items-start gap-4">
options={progress} <StepCounter step={3} />
selectedRoadmaps={selectedRoadmaps} <div>
setSelectedRoadmap={setSelectedRoadmap} <span className="mb-3 flex items-center gap-2 text-sm leading-none text-gray-400">
/> Select Variant
</span>
<div className="mt-6">
{selectedBadge === 'tall' && ( <div className="flex gap-2">
<TallBadgeTab selectedRoadmaps={selectedRoadmaps} /> <button className="rounded-md border p-1 px-2 text-sm">Tall</button>
)} <button className="rounded-md border p-1 px-2 text-sm">Wide</button>
{selectedBadge === 'wide' && ( </div>
<WideBadgeTab selectedRoadmaps={selectedRoadmaps} /> </div>
)} </div>
<div className="mb-4 flex items-start gap-4">
<StepCounter step={4} />
<div>
<span className="mb-3 flex items-center gap-2 text-sm leading-none text-gray-400">
Share your #RoadCard with others
</span>
<a
href={badgeUrl.toString()}
target="_blank"
rel="noopener noreferrer"
className="relative block w-[270px] hover:cursor-pointer"
>
<img src={badgeUrl.toString()} alt="RoadCard" />
</a>
</div>
</div> </div>
</> </>
); );

@ -3,11 +3,6 @@ import { useEffect, useState } from 'preact/hooks';
import { pageProgressMessage } from '../../stores/page'; import { pageProgressMessage } from '../../stores/page';
import type { UserProgressResponse } from '../HeroSection/FavoriteRoadmaps'; import type { UserProgressResponse } from '../HeroSection/FavoriteRoadmaps';
export type RoadmapOptionProps = {
value: string;
label: string;
};
export function RoadmapSelect() { export function RoadmapSelect() {
const [progressList, setProgressList] = useState<UserProgressResponse>(); const [progressList, setProgressList] = useState<UserProgressResponse>();
@ -30,17 +25,14 @@ export function RoadmapSelect() {
}, []); }, []);
return ( return (
<div> <div className="flex flex-wrap gap-1">
<div className="relative"> {progressList
<input ?.filter((progress) => progress.resourceType === 'roadmap')
type="text" .map((progress) => (
className="mb-0.5 w-full rounded-md border p-2 text-sm focus:outline-0 focus:ring-0" <button className="rounded-md border p-1 px-2 text-sm">
placeholder="Pick roadmaps" {progress.resourceTitle}
/> </button>
<div className="top-full absolute rounded-md"></div> ))}
</div>
<div className="mt-1 text-xs text-gray-500">Select up to 4 roadmaps</div>
</div> </div>
); );
} }

@ -1,11 +1,10 @@
import type { RoadmapOptionProps } from '../components/RoadCard/RoadmapSelect';
import type { useAuth } from '../hooks/use-auth'; import type { useAuth } from '../hooks/use-auth';
export type GetBadgeLinkProps = { export type GetBadgeLinkProps = {
user: ReturnType<typeof useAuth>; user: ReturnType<typeof useAuth>;
variant: 'dark' | 'light'; variant: 'dark' | 'light';
badge: 'tall' | 'wide'; badge: 'tall' | 'wide';
roadmaps?: RoadmapOptionProps[]; roadmaps?: string[];
}; };
export function getBadgeLink({ export function getBadgeLink({
@ -20,12 +19,11 @@ export function getBadgeLink({
if (variant) { if (variant) {
badgeUrl.searchParams.set('variant', variant); badgeUrl.searchParams.set('variant', variant);
} }
if (roadmaps?.length) { if (roadmaps?.length) {
badgeUrl.searchParams.set( badgeUrl.searchParams.set('roadmaps', roadmaps.join(','));
'roadmaps',
roadmaps.map(({ value }) => value).join(',')
);
} }
const textareaContent = ` const textareaContent = `
<a href="${badgeUrl}"> <a href="${badgeUrl}">
<img src="${badgeUrl}" alt="${user?.name}${user?.name && "'s"} Road Card"/> <img src="${badgeUrl}" alt="${user?.name}${user?.name && "'s"} Road Card"/>

Loading…
Cancel
Save