feat/roadcards
Kamran Ahmed 1 year ago
parent f90862f783
commit 7a901543b7
  1. 7
      package.json
  2. 2020
      pnpm-lock.yaml
  3. 57
      src/components/RoadCard/RoadCardPage.tsx
  4. 133
      src/components/RoadCard/RoadmapSelect.tsx
  5. 2
      src/components/RoadCard/TallBadge.tsx

@ -34,7 +34,6 @@
"node-html-parser": "^6.1.5",
"npm-check-updates": "^16.10.12",
"preact": "^10.15.1",
"react-select": "^5.7.3",
"rehype-external-links": "^2.1.0",
"roadmap-renderer": "^1.0.6",
"tailwindcss": "^3.3.2"
@ -51,11 +50,5 @@
"prettier": "^2.8.8",
"prettier-plugin-astro": "^0.10.0",
"prettier-plugin-tailwindcss": "^0.3.0"
},
"pnpm": {
"overrides": {
"react": "npm:@preact/compat@latest",
"react-dom": "npm:@preact/compat@latest"
}
}
}

File diff suppressed because it is too large Load Diff

@ -8,7 +8,7 @@ import CopyIcon from '../../icons/copy.svg';
import { RoadmapSelect, RoadmapSelectProps } from './RoadmapSelect';
import { httpGet } from '../../lib/http';
import { pageProgressMessage } from '../../stores/page';
import type { UserProgressResponse } from '../../lib/home-progress';
import type { UserProgressResponse } from '../HeroSection/FavoriteRoadmaps';
type EditorProps = {
title: string;
@ -62,35 +62,6 @@ export function RoadCardPage() {
return null;
}
const fetchProgress = async () => {
const { response: progressList, error } =
await httpGet<UserProgressResponse>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-all-progress`
);
if (error || !progressList) {
return;
}
const enrichedRoadmaps = [];
for (const progress of progressList) {
if (progress.resourceType !== 'roadmap') return;
enrichedRoadmaps.push({
label: progress.resourceTitle,
value: progress.resourceId,
});
}
setProgress(enrichedRoadmaps);
};
useEffect(() => {
fetchProgress().finally(() => {
pageProgressMessage.set('');
});
}, []);
return (
<>
<div className="mb-8 hidden md:block">
@ -104,10 +75,11 @@ export function RoadCardPage() {
<div className="mb-6 flex items-center border-b">
<div className="flex items-center">
<button
className={`relative top-px flex items-center justify-center px-3 pb-3 leading-none shadow-gray-600 ${selectedBadge === 'tall'
? 'shadow-[inset_0_-1px_0_var(--tw-shadow-color)]'
: 'text-gray-600'
}`}
className={`relative top-px flex items-center justify-center px-3 pb-3 leading-none shadow-gray-600 ${
selectedBadge === 'tall'
? 'shadow-[inset_0_-1px_0_var(--tw-shadow-color)]'
: 'text-gray-600'
}`}
onClick={() => {
setSelectedBadge('tall');
}}
@ -116,10 +88,11 @@ export function RoadCardPage() {
</button>
<button
className={`relative top-px flex items-center justify-center px-3 pb-3 leading-none shadow-gray-600 ${selectedBadge === 'wide'
? 'shadow-[inset_0_-1px_0_var(--tw-shadow-color)]'
: 'text-gray-600'
}`}
className={`relative top-px flex items-center justify-center px-3 pb-3 leading-none shadow-gray-600 ${
selectedBadge === 'wide'
? 'shadow-[inset_0_-1px_0_var(--tw-shadow-color)]'
: 'text-gray-600'
}`}
onClick={() => {
setSelectedBadge('wide');
}}
@ -137,8 +110,12 @@ export function RoadCardPage() {
/>
<div className="mt-6">
{selectedBadge === 'tall' && <TallBadgeTab selectedRoadmaps={selectedRoadmaps} />}
{selectedBadge === 'wide' && <WideBadgeTab selectedRoadmaps={selectedRoadmaps} />}
{selectedBadge === 'tall' && (
<TallBadgeTab selectedRoadmaps={selectedRoadmaps} />
)}
{selectedBadge === 'wide' && (
<WideBadgeTab selectedRoadmaps={selectedRoadmaps} />
)}
</div>
</>
);

@ -1,123 +1,46 @@
import Select, { ActionMeta, OnChangeValue } from 'react-select';
import { httpGet } from '../../lib/http';
import { useEffect, useState } from 'preact/hooks';
import { pageProgressMessage } from '../../stores/page';
import type { UserProgressResponse } from '../HeroSection/FavoriteRoadmaps';
export type RoadmapOptionProps = {
value: string;
label: string;
};
export interface RoadmapSelectProps {
options: RoadmapOptionProps[];
selectedRoadmaps: RoadmapOptionProps[];
setSelectedRoadmap: (roadmap: RoadmapOptionProps[]) => void;
}
export function RoadmapSelect() {
const [progressList, setProgressList] = useState<UserProgressResponse>();
const controlStyles = {
base: 'border rounded-lg bg-white hover:cursor-pointer',
focus: 'border-primary-600 ring-1 ring-primary-500',
nonFocus: 'border-gray-300 hover:border-gray-400',
};
const placeholderStyles = 'text-gray-500 pl-1 py-0.5';
const selectInputStyles = 'pl-1 py-0.5';
const valueContainerStyles = 'p-1 gap-1';
const singleValueStyles = 'text-sm ml-1';
const multiValueStyles =
'bg-gray-100 rounded items-center py-0.5 pl-2 pr-1 gap-1.5';
const multiValueLabelStyles = 'leading-none py-1 text-sm';
const multiValueRemoveStyles =
'border border-gray-200 bg-white hover:bg-red-50 hover:text-red-800 text-gray-500 hover:border-red-300 rounded-md p-0.5';
const indicatorsContainerStyles = 'py-0 px-1 gap-1';
const clearIndicatorStyles =
'text-gray-500 p-1 rounded-md hover:bg-red-50 hover:text-red-800';
const indicatorSeparatorStyles = 'bg-gray-300';
const dropdownIndicatorStyles =
'p-1 hover:bg-gray-100 text-gray-500 rounded-md hover:text-black';
const menuStyles =
'p-1 mt-2 border border-gray-200 bg-white rounded-lg shadow-lg';
const groupHeadingStyles = 'ml-3 mt-2 mb-1 text-gray-500 text-sm';
const optionStyles = {
base: 'hover:cursor-pointer px-3 text-sm py-2 rounded',
focus: 'bg-gray-100 active:bg-gray-200',
selected: "after:content-['✔'] after:ml-2 after:text-green-500 text-gray-500",
};
const noOptionsMessageStyles =
'text-gray-500 p-2 bg-gray-50 border border-dashed border-gray-200 rounded-sm';
const fetchProgress = async () => {
const { response, error } = await httpGet<UserProgressResponse>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-user-all-progress`
);
export function RoadmapSelect({
options,
selectedRoadmaps,
setSelectedRoadmap,
}: RoadmapSelectProps) {
const onChange = (
newValue: OnChangeValue<RoadmapOptionProps, true>,
actionMeta: ActionMeta<RoadmapOptionProps>
) => {
if (actionMeta.action === 'clear') {
setSelectedRoadmap([]);
if (error || !response) {
return;
}
// Only allow selecting up to 4 roadmaps.
if (newValue.length > 4) {
return;
}
setSelectedRoadmap(Array.isArray(newValue) ? newValue : [newValue]);
setProgressList(response);
};
useEffect(() => {
fetchProgress().finally(() => {
pageProgressMessage.set('');
});
}, []);
return (
<div>
{/* @ts-ignore */}
<Select
isMulti
options={options}
onChange={onChange}
value={selectedRoadmaps}
placeholder="Select roadmaps"
unstyled
styles={{
input: (base) => ({
...base,
'input:focus': {
boxShadow: 'none',
},
}),
// On mobile, the label will truncate automatically, so we want to
// override that behaviour.
multiValueLabel: (base) => ({
...base,
// whiteSpace: 'normal',
// overflow: 'visible',
}),
control: (base) => ({
...base,
transition: 'none',
}),
}}
classNames={{
control: ({ isFocused }) =>
`${isFocused ? controlStyles.focus : controlStyles.nonFocus} ${
controlStyles.base
}`,
placeholder: () => placeholderStyles,
input: () => selectInputStyles,
valueContainer: () => valueContainerStyles,
singleValue: () => singleValueStyles,
multiValue: () => multiValueStyles,
multiValueLabel: () => multiValueLabelStyles,
multiValueRemove: () => multiValueRemoveStyles,
indicatorsContainer: () => indicatorsContainerStyles,
clearIndicator: () => clearIndicatorStyles,
indicatorSeparator: () => indicatorSeparatorStyles,
dropdownIndicator: () => dropdownIndicatorStyles,
menu: () => menuStyles,
groupHeading: () => groupHeadingStyles,
option: ({ isFocused, isSelected }) =>
`${isFocused && optionStyles.focus} ${
isSelected && optionStyles.selected
} ${optionStyles.base}`,
noOptionsMessage: () => noOptionsMessageStyles,
}}
/>
<div className="relative">
<input
type="text"
className="mb-0.5 w-full rounded-md border p-2 text-sm focus:outline-0 focus:ring-0"
placeholder="Pick roadmaps"
/>
<div className="top-full absolute rounded-md"></div>
</div>
<div className="mt-1 text-sm text-gray-500">Select up to 4 roadmaps</div>
<div className="mt-1 text-xs text-gray-500">Select up to 4 roadmaps</div>
</div>
);
}

@ -11,7 +11,7 @@ export function LongBadge({ badgeUrl }: BadgeProps) {
href={badgeUrl}
target="_blank"
rel="noopener noreferrer"
className="relative block aspect-[300/392] w-full hover:cursor-pointer"
className="relative block aspect-[422/551] w-full hover:cursor-pointer"
>
<img
src={badgeUrl}

Loading…
Cancel
Save