Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE][CPF-75] My space ladder bucket #128

Merged
merged 11 commits into from
Aug 1, 2024
15 changes: 3 additions & 12 deletions frontend/src/app/(app)/(root)/my-space/[bucket]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
import { getBucketDetails } from '@app/api/bucket';
import { routes } from '@app/constants';
import { Breadcrumbs } from '@app/components/modules/Breadcrumbs';
import { MySpaceBucketDetails } from '@app/components/pages/mySpace/MySpaceBucketDetails';

export default async function MySpaceBucketDetailed({ params }: { params: { bucket: string } }) {
const { bucket } = params;
// TODO: get proper data from api
const data = await getBucketDetails(bucket);

return (
<div>
<Breadcrumbs
breadcrumbs={[
{ label: 'My space', href: routes.mySpace.index, current: false },
{ label: data.bucketName, href: `${routes.library.index}/${bucket}`, current: true },
]}
/>
</div>
);
return <MySpaceBucketDetails data={data} />;
}
39 changes: 39 additions & 0 deletions frontend/src/app/(app)/(root)/my-space/soft-skills/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { MySpaceSoftSkillBucketDetails } from '@app/components/pages/mySpace/MySpaceSoftSkillBucketDetails';
import { ProofStatus, SoftSkillBucket } from '@app/types/library';

// TODO: get data from api
const loremIpsum =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, felis et tincidunt tempor, justo orci cursus ipsum, nec efficitur neque felis sit amet orci.';

const data: SoftSkillBucket = {
bucketSlug: 'soft-skills',
bucketName: 'Soft skills',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam aliquet, felis et tincidunt tempor, justo orci cursus ipsum, nec efficitur neque felis sit amet orci. Vivamus tempus, ex et ultrices rutrum, libero mi molestie mi, non tempus ex metus sed augue. Morbi euismod, nulla nec tempus consequat, quam mi pellentesque elit, non sagittis est nisl sed arcu.',
status: 'completed',
categories: {
Responsibility: [
{
name: 'Fulfills undertaken obligations regarding tasks',
description: loremIpsum,
proofStatus: ProofStatus.approved,
},
{
name: 'He is responsive and communicates responsibly - without unnecessary delay.',
description: loremIpsum,
proofStatus: ProofStatus.approved,
},
],
Quality: [
{
name: 'Fulfills undertaken obligations regarding tasks',
description: loremIpsum,
proofStatus: ProofStatus.approved,
},
],
},
};

export default async function MySpaceBucketDetailed() {
return <MySpaceSoftSkillBucketDetails data={data} />;
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { ReactNode } from 'react';

export interface AccordionListProps {
items: {
key: string;
title: string;
children?: JSX.Element;
children?: ReactNode;
icon?: ReactNode;
}[];
}

export interface AccordionListItemProps {
title: string;
noContentTooltipText: string;
icon?: ReactNode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import { AccordionListItem } from './AccordionListItem';
export const AccordionList = ({ items }: PropsWithChildren<AccordionListProps>) => {
return (
<div className="flex flex-col">
{items.map(({ title, children, key }) => (
<AccordionListItem key={key} title={title} noContentTooltipText="There's no description for this skill.">
{items.map(({ title, children, key, icon }) => (
<AccordionListItem
key={key}
title={title}
noContentTooltipText="There's no description for this skill."
icon={icon}
>
{children}
</AccordionListItem>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const AccordionListItem = ({
title,
noContentTooltipText,
children,
icon,
}: PropsWithChildren<AccordionListItemProps>) => {
const [isOpen, setOpen] = useState(false);
const disableExpand = !children;
Expand All @@ -26,9 +27,12 @@ export const AccordionListItem = ({
onClick={handleClick}
disabled={disableExpand}
>
<Typography variant="body-m/medium" className="text-left text-navy-600">
{title}
</Typography>
<div className="flex items-center gap-4">
{icon}
<Typography variant="body-m/medium" className="text-left text-navy-600">
{title}
</Typography>
</div>
<Tooltip tooltipText={noContentTooltipText}>
<div
className={`flex min-h-10 min-w-10 items-center justify-center ${disableExpand ? 'text-navy-300' : 'text-navy-500'}`}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface ExpandableSectionProps {
title: string;
description?: string;
verticalLine?: boolean;
open: boolean;
onClick: () => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ChevronRightIcon } from '@app/static/icons/ChevronRightIcon';
import { Typography } from '@app/components/common/Typography';
import { ExpandableSectionProps } from '@app/components/common/ExpandableSection/ExpandableSection.interface';
import { FC, PropsWithChildren } from 'react';

export const ExpandableSection: FC<PropsWithChildren<ExpandableSectionProps>> = ({
onClick,
open,
title,
description,
verticalLine,
children,
}) => {
return (
<div className="flex flex-row gap-2">
<div className="relative flex flex-col items-center">
<button
className="mb-2 mt-4 flex h-6 w-6 items-center justify-center rounded-full bg-blue-800 hover:opacity-50"
onClick={onClick}
>
<ChevronRightIcon className={`h-3.5 w-3.5 text-white ${!open ? 'rotate-90' : '-rotate-90'}`} />
</button>
{verticalLine && <div className="absolute left-3 top-12 h-[calc(100%-40px)] w-[1.5px] bg-blue-800" />}
</div>
<div className="mb-4 ml-4 flex w-full flex-col gap-4">
<button
className={`flex w-full cursor-pointer flex-col gap-4 rounded-lg ${!open && 'hover:bg-navy-50'}`}
onClick={onClick}
>
<Typography as="h3" variant="body-l/semibold">
{title}
</Typography>
{description && (
<Typography variant="body-m/regular" className="text-navy-600">
{description}
</Typography>
)}
</button>
{open && children}
</div>
</div>
);
};
1 change: 1 addition & 0 deletions frontend/src/components/common/ExpandableSection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ExpandableSection } from './ExpandableSection';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface StatusChipProps {
variant?: Variants;
}

export type Variants = 'green' | 'red' | 'yellow' | 'grey';
38 changes: 38 additions & 0 deletions frontend/src/components/common/StatusChip/StatusChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { FC, PropsWithChildren } from 'react';
import { StatusChipProps, Variants } from '@app/components/common/StatusChip/StatusChip.interface';
import { Typography } from '@app/components/common/Typography';
import { generateClassNames } from '@app/utils';

const styles: {
[key in Variants]: {
text: string;
wrapper: string;
};
} = {
grey: {
text: 'text-navy-600',
wrapper: 'bg-navy-100',
},
yellow: {
text: 'text-yellow-600',
wrapper: 'bg-yellow-200',
},
red: {
text: 'text-red-700',
wrapper: 'bg-red-100',
},
green: {
text: 'text-green-600',
wrapper: 'bg-green-200',
},
};

export const StatusChip: FC<PropsWithChildren<StatusChipProps>> = ({ variant = 'grey', children }) => {
return (
<div className={generateClassNames('flex h-6 items-center rounded-md px-2 py-1', styles[variant].wrapper)}>
<Typography variant="hint/caps-medium" className={styles[variant].text}>
{children}
</Typography>
</div>
);
};
1 change: 1 addition & 0 deletions frontend/src/components/common/StatusChip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { StatusChip } from './StatusChip';

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import { LadderBandBucket } from '@app/types/library';
export interface BucketCardProps {
bucket: LadderBandBucket;
withLevel?: boolean;
withStatus?: boolean;
href: string;
}
17 changes: 10 additions & 7 deletions frontend/src/components/modules/BucketCard/BucketCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import Link from 'next/link';
import { BucketCardProps } from './BucketCard.interface';
import { ChevronRightIcon } from '@app/static/icons/ChevronRightIcon';
import { Typography } from '@app/components/common/Typography';
import { AdvancementLevel } from '@app/components/modules/AdvancementLevel';
import { LevelDots } from '@app/components/modules/LevelDots';
import { StatusChip } from '@app/components/common/StatusChip';

export const BucketCard = ({ bucket, withLevel, href }: BucketCardProps) => {
const { bucketName, description } = bucket;
export const BucketCard = ({ bucket, withLevel, withStatus, href }: BucketCardProps) => {
const { bucketName, description, status } = bucket;
const level = 1; //TODO: replace with real value from api

return (
<Link
Expand All @@ -16,21 +18,22 @@ export const BucketCard = ({ bucket, withLevel, href }: BucketCardProps) => {
<Typography variant="head-s/semibold" as="h2">
{bucketName}
</Typography>
<div className="flex flex-row gap-6">
<div className="flex flex-row items-center gap-6">
{withLevel && (
<div className="flex flex-row items-center gap-4">
<Typography variant="body-m/medium" className="text-navy-600">
Level #
Level {level ?? '#'}
</Typography>
<AdvancementLevel level={1} />
<LevelDots level={level} />
</div>
)}
{withStatus && <StatusChip variant="green">{status}</StatusChip>}
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-navy-50">
<ChevronRightIcon className="text-navy-500" />
</div>
</div>
</div>
<Typography variant="body-m/regular" className="text-navy-600">
<Typography variant="body-m/regular" className="truncate text-navy-600">
{description}
</Typography>
</Link>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ExampleProject } from '@app/types/library';

export interface ExampleWayToPassLevelModalProps {
open: boolean;
onClose: () => void;
projects: ExampleProject[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Modal } from '@app/components/common/Modal';
import { Markdown } from '@app/components/common/Markdown';
import { ExampleWayToPassLevelModalProps } from './ExampleWayToPassLevelModal.interface';

export const ExampleWayToPassLevelModal: React.FC<ExampleWayToPassLevelModalProps> = ({ open, onClose, projects }) => {
return (
<Modal open={open} onClose={onClose} title="An example way to pass level">
{projects.map(({ title, overview }) => (
<div key={title} className="overflow-hidden text-base text-navy-600">
<p>{title}</p>
<article className="prose mt-5">
<Markdown text={overview} />
</article>
</div>
))}
</Modal>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ExampleWayToPassLevelModal } from './ExampleWayToPassLevelModal';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface LevelDotsProps {
level?: number;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { AdvancementLevelProps } from './AdvancementLevel.interface';
import { LevelDotsProps } from './LevelDots.interface';
import { MAX_LEVEL, MIN_LEVEL } from './constants';
import { FC } from 'react';

export const AdvancementLevel = ({ level }: AdvancementLevelProps) => {
export const LevelDots: FC<LevelDotsProps> = ({ level }) => {
const currentLevel = Math.min(Math.max(level ?? MIN_LEVEL, MIN_LEVEL), MAX_LEVEL);

return (
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/modules/LevelDots/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { LevelDots } from './LevelDots';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface SkillStatusIconProps {
status?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { SkillStatusIconProps } from '@app/components/modules/SkillStatusIcon/SkillStatusIcon.interface';
import { FC } from 'react';
import { DashedCircle } from '@app/static/icons/DashedCircle';
import { ProofStatus } from '@app/types/library';
import { CheckMarkIcon } from '@app/static/icons/CheckMarkIcon';
import { ArrowRight } from '@app/static/icons/ArrowRight';
import { CrossIcon } from '@app/static/icons/CrossIcon';

export const SkillStatusIcon: FC<SkillStatusIconProps> = ({ status }) => {
switch (status) {
case ProofStatus.approved:
return (
<div className="flex h-5 w-5 items-center justify-center rounded-full bg-green-600 text-white">
<CheckMarkIcon />
</div>
);
case ProofStatus.pending:
return (
<div className="flex h-5 w-5 items-center justify-center rounded-full bg-yellow-500 text-white">
<ArrowRight />
</div>
);
case ProofStatus.rejected:
return (
<div className="flex h-5 w-5 items-center justify-center rounded-full bg-red-600 text-white">
<CrossIcon />
</div>
);
default:
return <DashedCircle className="text-navy-300" />;
}
};
1 change: 1 addition & 0 deletions frontend/src/components/modules/SkillStatusIcon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SkillStatusIcon } from './SkillStatusIcon';
3 changes: 2 additions & 1 deletion frontend/src/components/modules/Topbar/Topbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Avatar } from '@app/components/common/Avatar';
import { UserIcon } from '@app/static/icons/UserIcon';
import { LogoutIcon } from '@app/static/icons/LogoutIcon';
import Link from 'next/link';
import { routes } from '@app/constants';

export const Topbar = () => {
// TODO: get user from some context
Expand All @@ -16,7 +17,7 @@ export const Topbar = () => {

const menuItems = [
{
href: '/people/my-profile',
href: routes.people.myProfile,
label: 'Profile settings',
icon: <UserIcon />,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const BucketDetails: React.FC<BucketDetailsProps> = ({ data, ladderName,
<AdvancementLevel
key={level.advancementLevel}
data={level}
showVerticalLine={index < advancementLevels.length - 1}
verticalLine={index < advancementLevels.length - 1}
open={levelOpen === level.advancementLevel}
onClick={() => handleOpen(level.advancementLevel)}
/>
Expand Down
Loading