diff --git a/frontend/src/app/(app)/(root)/my-space/[bucket]/page.tsx b/frontend/src/app/(app)/(root)/my-space/[bucket]/page.tsx index f6df43b2..ef243273 100644 --- a/frontend/src/app/(app)/(root)/my-space/[bucket]/page.tsx +++ b/frontend/src/app/(app)/(root)/my-space/[bucket]/page.tsx @@ -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 ( -
- -
- ); + return ; } diff --git a/frontend/src/app/(app)/(root)/my-space/soft-skills/page.tsx b/frontend/src/app/(app)/(root)/my-space/soft-skills/page.tsx new file mode 100644 index 00000000..0fc0d499 --- /dev/null +++ b/frontend/src/app/(app)/(root)/my-space/soft-skills/page.tsx @@ -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 ; +} diff --git a/frontend/src/components/common/AccordionList/AccordionList.interface.ts b/frontend/src/components/common/AccordionList/AccordionList.interface.ts index 288c7e5f..604cfbb6 100644 --- a/frontend/src/components/common/AccordionList/AccordionList.interface.ts +++ b/frontend/src/components/common/AccordionList/AccordionList.interface.ts @@ -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; } diff --git a/frontend/src/components/common/AccordionList/AccordionList.tsx b/frontend/src/components/common/AccordionList/AccordionList.tsx index 7fd692ae..9789fd15 100644 --- a/frontend/src/components/common/AccordionList/AccordionList.tsx +++ b/frontend/src/components/common/AccordionList/AccordionList.tsx @@ -5,8 +5,13 @@ import { AccordionListItem } from './AccordionListItem'; export const AccordionList = ({ items }: PropsWithChildren) => { return (
- {items.map(({ title, children, key }) => ( - + {items.map(({ title, children, key, icon }) => ( + {children} ))} diff --git a/frontend/src/components/common/AccordionList/AccordionListItem.tsx b/frontend/src/components/common/AccordionList/AccordionListItem.tsx index c564ac54..ae4ed77b 100644 --- a/frontend/src/components/common/AccordionList/AccordionListItem.tsx +++ b/frontend/src/components/common/AccordionList/AccordionListItem.tsx @@ -9,6 +9,7 @@ export const AccordionListItem = ({ title, noContentTooltipText, children, + icon, }: PropsWithChildren) => { const [isOpen, setOpen] = useState(false); const disableExpand = !children; @@ -26,9 +27,12 @@ export const AccordionListItem = ({ onClick={handleClick} disabled={disableExpand} > - - {title} - +
+ {icon} + + {title} + +
void; +} diff --git a/frontend/src/components/common/ExpandableSection/ExpandableSection.tsx b/frontend/src/components/common/ExpandableSection/ExpandableSection.tsx new file mode 100644 index 00000000..4866e7f2 --- /dev/null +++ b/frontend/src/components/common/ExpandableSection/ExpandableSection.tsx @@ -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> = ({ + onClick, + open, + title, + description, + verticalLine, + children, +}) => { + return ( +
+
+ + {verticalLine &&
} +
+
+ + {open && children} +
+
+ ); +}; diff --git a/frontend/src/components/common/ExpandableSection/index.ts b/frontend/src/components/common/ExpandableSection/index.ts new file mode 100644 index 00000000..3bef8c21 --- /dev/null +++ b/frontend/src/components/common/ExpandableSection/index.ts @@ -0,0 +1 @@ +export { ExpandableSection } from './ExpandableSection'; diff --git a/frontend/src/components/common/StatusChip/StatusChip.interface.ts b/frontend/src/components/common/StatusChip/StatusChip.interface.ts new file mode 100644 index 00000000..0d9c243f --- /dev/null +++ b/frontend/src/components/common/StatusChip/StatusChip.interface.ts @@ -0,0 +1,5 @@ +export interface StatusChipProps { + variant?: Variants; +} + +export type Variants = 'green' | 'red' | 'yellow' | 'grey'; diff --git a/frontend/src/components/common/StatusChip/StatusChip.tsx b/frontend/src/components/common/StatusChip/StatusChip.tsx new file mode 100644 index 00000000..11b2aef3 --- /dev/null +++ b/frontend/src/components/common/StatusChip/StatusChip.tsx @@ -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> = ({ variant = 'grey', children }) => { + return ( +
+ + {children} + +
+ ); +}; diff --git a/frontend/src/components/common/StatusChip/index.ts b/frontend/src/components/common/StatusChip/index.ts new file mode 100644 index 00000000..466a85ed --- /dev/null +++ b/frontend/src/components/common/StatusChip/index.ts @@ -0,0 +1 @@ +export { StatusChip } from './StatusChip'; diff --git a/frontend/src/components/modules/AdvancementLevel/AdvancementLevel.interface.ts b/frontend/src/components/modules/AdvancementLevel/AdvancementLevel.interface.ts deleted file mode 100644 index 2d580c66..00000000 --- a/frontend/src/components/modules/AdvancementLevel/AdvancementLevel.interface.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface AdvancementLevelProps { - level?: number; -} diff --git a/frontend/src/components/modules/BucketCard/BucketCard.interface.ts b/frontend/src/components/modules/BucketCard/BucketCard.interface.ts index e6168f7e..50e3b840 100644 --- a/frontend/src/components/modules/BucketCard/BucketCard.interface.ts +++ b/frontend/src/components/modules/BucketCard/BucketCard.interface.ts @@ -3,5 +3,6 @@ import { LadderBandBucket } from '@app/types/library'; export interface BucketCardProps { bucket: LadderBandBucket; withLevel?: boolean; + withStatus?: boolean; href: string; } diff --git a/frontend/src/components/modules/BucketCard/BucketCard.tsx b/frontend/src/components/modules/BucketCard/BucketCard.tsx index 86788486..7719e624 100644 --- a/frontend/src/components/modules/BucketCard/BucketCard.tsx +++ b/frontend/src/components/modules/BucketCard/BucketCard.tsx @@ -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 ( { {bucketName} -
+
{withLevel && (
- Level # + Level {level ?? '#'} - +
)} + {withStatus && {status}}
- + {description} diff --git a/frontend/src/components/modules/ExampleWayToPassLevelModal/ExampleWayToPassLevelModal.interface.ts b/frontend/src/components/modules/ExampleWayToPassLevelModal/ExampleWayToPassLevelModal.interface.ts new file mode 100644 index 00000000..7d4fe7bd --- /dev/null +++ b/frontend/src/components/modules/ExampleWayToPassLevelModal/ExampleWayToPassLevelModal.interface.ts @@ -0,0 +1,7 @@ +import { ExampleProject } from '@app/types/library'; + +export interface ExampleWayToPassLevelModalProps { + open: boolean; + onClose: () => void; + projects: ExampleProject[]; +} diff --git a/frontend/src/components/modules/ExampleWayToPassLevelModal/ExampleWayToPassLevelModal.tsx b/frontend/src/components/modules/ExampleWayToPassLevelModal/ExampleWayToPassLevelModal.tsx new file mode 100644 index 00000000..907ad158 --- /dev/null +++ b/frontend/src/components/modules/ExampleWayToPassLevelModal/ExampleWayToPassLevelModal.tsx @@ -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 = ({ open, onClose, projects }) => { + return ( + + {projects.map(({ title, overview }) => ( +
+

{title}

+
+ +
+
+ ))} +
+ ); +}; diff --git a/frontend/src/components/modules/ExampleWayToPassLevelModal/index.ts b/frontend/src/components/modules/ExampleWayToPassLevelModal/index.ts new file mode 100644 index 00000000..d2aff6f3 --- /dev/null +++ b/frontend/src/components/modules/ExampleWayToPassLevelModal/index.ts @@ -0,0 +1 @@ +export { ExampleWayToPassLevelModal } from './ExampleWayToPassLevelModal'; diff --git a/frontend/src/components/modules/LevelDots/LevelDots.interface.ts b/frontend/src/components/modules/LevelDots/LevelDots.interface.ts new file mode 100644 index 00000000..e9a1feee --- /dev/null +++ b/frontend/src/components/modules/LevelDots/LevelDots.interface.ts @@ -0,0 +1,3 @@ +export interface LevelDotsProps { + level?: number; +} diff --git a/frontend/src/components/modules/AdvancementLevel/AdvancementLevel.tsx b/frontend/src/components/modules/LevelDots/LevelDots.tsx similarity index 78% rename from frontend/src/components/modules/AdvancementLevel/AdvancementLevel.tsx rename to frontend/src/components/modules/LevelDots/LevelDots.tsx index 3196400c..0725e2fd 100644 --- a/frontend/src/components/modules/AdvancementLevel/AdvancementLevel.tsx +++ b/frontend/src/components/modules/LevelDots/LevelDots.tsx @@ -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 = ({ level }) => { const currentLevel = Math.min(Math.max(level ?? MIN_LEVEL, MIN_LEVEL), MAX_LEVEL); return ( diff --git a/frontend/src/components/modules/AdvancementLevel/constants.ts b/frontend/src/components/modules/LevelDots/constants.ts similarity index 100% rename from frontend/src/components/modules/AdvancementLevel/constants.ts rename to frontend/src/components/modules/LevelDots/constants.ts diff --git a/frontend/src/components/modules/LevelDots/index.ts b/frontend/src/components/modules/LevelDots/index.ts new file mode 100644 index 00000000..1fbec75f --- /dev/null +++ b/frontend/src/components/modules/LevelDots/index.ts @@ -0,0 +1 @@ +export { LevelDots } from './LevelDots'; diff --git a/frontend/src/components/modules/SkillStatusIcon/SkillStatusIcon.interface.tsx b/frontend/src/components/modules/SkillStatusIcon/SkillStatusIcon.interface.tsx new file mode 100644 index 00000000..3d90be9f --- /dev/null +++ b/frontend/src/components/modules/SkillStatusIcon/SkillStatusIcon.interface.tsx @@ -0,0 +1,3 @@ +export interface SkillStatusIconProps { + status?: string; +} diff --git a/frontend/src/components/modules/SkillStatusIcon/SkillStatusIcon.tsx b/frontend/src/components/modules/SkillStatusIcon/SkillStatusIcon.tsx new file mode 100644 index 00000000..48f2efa1 --- /dev/null +++ b/frontend/src/components/modules/SkillStatusIcon/SkillStatusIcon.tsx @@ -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 = ({ status }) => { + switch (status) { + case ProofStatus.approved: + return ( +
+ +
+ ); + case ProofStatus.pending: + return ( +
+ +
+ ); + case ProofStatus.rejected: + return ( +
+ +
+ ); + default: + return ; + } +}; diff --git a/frontend/src/components/modules/SkillStatusIcon/index.ts b/frontend/src/components/modules/SkillStatusIcon/index.ts new file mode 100644 index 00000000..4dd8ad80 --- /dev/null +++ b/frontend/src/components/modules/SkillStatusIcon/index.ts @@ -0,0 +1 @@ +export { SkillStatusIcon } from './SkillStatusIcon'; diff --git a/frontend/src/components/modules/Topbar/Topbar.tsx b/frontend/src/components/modules/Topbar/Topbar.tsx index 2987ae75..7655ea97 100644 --- a/frontend/src/components/modules/Topbar/Topbar.tsx +++ b/frontend/src/components/modules/Topbar/Topbar.tsx @@ -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 @@ -16,7 +17,7 @@ export const Topbar = () => { const menuItems = [ { - href: '/people/my-profile', + href: routes.people.myProfile, label: 'Profile settings', icon: , }, diff --git a/frontend/src/components/pages/BucketDetails/BucketDetails.tsx b/frontend/src/components/pages/BucketDetails/BucketDetails.tsx index bbc46230..0993e82f 100644 --- a/frontend/src/components/pages/BucketDetails/BucketDetails.tsx +++ b/frontend/src/components/pages/BucketDetails/BucketDetails.tsx @@ -36,7 +36,7 @@ export const BucketDetails: React.FC = ({ data, ladderName, handleOpen(level.advancementLevel)} /> diff --git a/frontend/src/components/pages/BucketDetails/modules/AdvancementLevel/AdvancementLevel.tsx b/frontend/src/components/pages/BucketDetails/modules/AdvancementLevel/AdvancementLevel.tsx index 98beb23c..ae4fe313 100644 --- a/frontend/src/components/pages/BucketDetails/modules/AdvancementLevel/AdvancementLevel.tsx +++ b/frontend/src/components/pages/BucketDetails/modules/AdvancementLevel/AdvancementLevel.tsx @@ -1,80 +1,54 @@ 'use client'; -import { ChevronRightIcon } from '@app/static/icons/ChevronRightIcon'; import { AccordionCard } from '@app/components/common/AccordionCard'; import { AccordionList } from '@app/components/common/AccordionList'; -import { Modal } from '@app/components/common/Modal'; import { AdvancementLevelProps } from './AdvancemetLevel.interface'; import { useAdvancementLevel } from './AdvancementLevel.hooks'; -import { Markdown } from '@app/components/common/Markdown'; -import { Typography } from '@app/components/common/Typography'; import { Button } from '@app/components/common/Button'; +import { ExpandableSection } from '@app/components/common/ExpandableSection'; +import { ExampleWayToPassLevelModal } from '@app/components/modules/ExampleWayToPassLevelModal'; +import { FC } from 'react'; -export const AdvancementLevel: React.FC = ({ showVerticalLine, data, open, onClick }) => { +export const AdvancementLevel: FC = ({ verticalLine, data, open, onClick }) => { const { hideModal, openModal, modalOpen } = useAdvancementLevel(); const { advancementLevel, description, projects, categories } = data; const shouldBeExpandedByDefault = Object.keys(data.categories).length === 1; return ( -
-
- - {showVerticalLine &&
} -
-
- - {open && ( + + <> + {projects.length > 0 && ( <> - {projects.length > 0 && ( - - )} - {Object.entries(categories).map(([category, skills]) => ( - - ({ - key: name, - title: name, - children: description ?

{description}

: undefined, - }))} - /> -
- ))} + + )} -
- - {projects.map(({ title, overview }) => ( -
-

{title}

-
- -
-
+ {Object.entries(categories).map(([category, skills]) => ( + + ({ + key: name, + title: name, + children: description ?

{description}

: undefined, + }))} + /> +
))} -
-
+ + ); }; diff --git a/frontend/src/components/pages/BucketDetails/modules/AdvancementLevel/AdvancemetLevel.interface.ts b/frontend/src/components/pages/BucketDetails/modules/AdvancementLevel/AdvancemetLevel.interface.ts index 5c633165..55e4b68d 100644 --- a/frontend/src/components/pages/BucketDetails/modules/AdvancementLevel/AdvancemetLevel.interface.ts +++ b/frontend/src/components/pages/BucketDetails/modules/AdvancementLevel/AdvancemetLevel.interface.ts @@ -2,7 +2,7 @@ import { AdvancementLevel } from '@app/types/library'; export interface AdvancementLevelProps { data: AdvancementLevel; - showVerticalLine: boolean; + verticalLine: boolean; open: boolean; onClick: () => void; } diff --git a/frontend/src/components/pages/mySpace/MySpace/modules/LadderTab/modules/LadderDetails/LadderDetails.tsx b/frontend/src/components/pages/mySpace/MySpace/modules/LadderTab/modules/LadderDetails/LadderDetails.tsx index 7353a759..d9098a6e 100644 --- a/frontend/src/components/pages/mySpace/MySpace/modules/LadderTab/modules/LadderDetails/LadderDetails.tsx +++ b/frontend/src/components/pages/mySpace/MySpace/modules/LadderTab/modules/LadderDetails/LadderDetails.tsx @@ -1,11 +1,22 @@ import { LadderDetailsProps } from './LadderDetails.interface'; -import { LadderBandBucket } from '@app/types/library'; +import { BucketType, LadderBandBucket } from '@app/types/library'; import { Typography } from '@app/components/common/Typography'; import { ThresholdCard } from '@app/components/modules/ThresholdCard'; import { BucketCard } from '@app/components/modules/BucketCard'; import { routes } from '@app/constants'; export const LadderDetails = ({ ladder, ladderName, band }: LadderDetailsProps) => { + const softSkills: LadderBandBucket[] = [ + { + bucketName: 'Soft skills', + bucketSlug: '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.', + bucketType: BucketType.soft, + status: 'Completed', + }, + ]; // TODO: remove when data are fetched from api + return (
@@ -38,18 +49,21 @@ export const LadderDetails = ({ ladder, ladderName, band }: LadderDetailsProps) ))}
- {ladder.softSkillBuckets.length > 0 && ( + {/* TODO: replace with: + ladder.softSkillBuckets.length > 0 + */} + {softSkills.length > 0 && (
Soft skills
- {ladder.softSkillBuckets.map((bucket: LadderBandBucket) => ( + {softSkills.map((bucket: LadderBandBucket) => ( ))}
diff --git a/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.hooks.ts b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.hooks.ts new file mode 100644 index 00000000..1ecdaceb --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.hooks.ts @@ -0,0 +1,14 @@ +import { useState } from 'react'; + +export const useMySpaceBucketDetails = () => { + const [levelOpen, setLevelOpen] = useState(null); + + const handleOpen = (level: null | number) => { + setLevelOpen((prev) => (prev === level ? null : level)); + }; + + return { + levelOpen, + handleOpen, + }; +}; diff --git a/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.interface.ts b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.interface.ts new file mode 100644 index 00000000..333e90b0 --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.interface.ts @@ -0,0 +1,5 @@ +import { Bucket } from '@app/types/library'; + +export interface MySpaceBucketDetailsProps { + data: Bucket; +} diff --git a/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.tsx b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.tsx new file mode 100644 index 00000000..c860086b --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/MySpaceBucketDetails.tsx @@ -0,0 +1,57 @@ +'use client'; + +import { MySpaceBucketDetailsProps } from './MySpaceBucketDetails.interface'; +import { routes } from '@app/constants'; +import { Breadcrumbs } from '@app/components/modules/Breadcrumbs'; +import { Typography } from '@app/components/common/Typography'; +import { AdvancementLevel } from './modules/AdvancementLevel'; +import { useMySpaceBucketDetails } from './MySpaceBucketDetails.hooks'; +import { LevelDots } from '@app/components/modules/LevelDots'; + +export const MySpaceBucketDetails: React.FC = ({ data }) => { + const { bucketSlug, bucketName, description, advancementLevels } = data; + const { levelOpen, handleOpen } = useMySpaceBucketDetails(); + const level = 2; + + return ( +
+ +
+
+
+
+ + {bucketName} + +
+ + Level {level} + + +
+
+ + {description} + +
+
+
+ {advancementLevels.map((level, index) => ( + handleOpen(level.advancementLevel)} + /> + ))} +
+
+
+ ); +}; diff --git a/frontend/src/components/pages/mySpace/MySpaceBucketDetails/index.ts b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/index.ts new file mode 100644 index 00000000..b1dcd60b --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/index.ts @@ -0,0 +1 @@ +export { MySpaceBucketDetails } from './MySpaceBucketDetails'; diff --git a/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.hooks.ts b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.hooks.ts new file mode 100644 index 00000000..e2a24bcc --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.hooks.ts @@ -0,0 +1,14 @@ +import { useState } from 'react'; + +export const useAdvancementLevel = () => { + const [modalOpen, setModalOpen] = useState(false); + + const openModal = () => setModalOpen(true); + const hideModal = () => setModalOpen(false); + + return { + hideModal, + openModal, + modalOpen, + }; +}; diff --git a/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.interface.ts b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.interface.ts new file mode 100644 index 00000000..55e4b68d --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.interface.ts @@ -0,0 +1,8 @@ +import { AdvancementLevel } from '@app/types/library'; + +export interface AdvancementLevelProps { + data: AdvancementLevel; + verticalLine: boolean; + open: boolean; + onClick: () => void; +} diff --git a/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.tsx b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.tsx new file mode 100644 index 00000000..e3551621 --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/AdvancementLevel.tsx @@ -0,0 +1,54 @@ +'use client'; +import { AccordionCard } from '@app/components/common/AccordionCard'; +import { AccordionList } from '@app/components/common/AccordionList'; +import { AdvancementLevelProps } from './AdvancementLevel.interface'; +import { Button } from '@app/components/common/Button'; +import { ExpandableSection } from '@app/components/common/ExpandableSection'; +import { ExampleWayToPassLevelModal } from '@app/components/modules/ExampleWayToPassLevelModal'; +import { useAdvancementLevel } from './AdvancementLevel.hooks'; +import { SkillStatusIcon } from '@app/components/modules/SkillStatusIcon'; + +export const AdvancementLevel: React.FC = ({ verticalLine, data, open, onClick }) => { + const { hideModal, openModal, modalOpen } = useAdvancementLevel(); + const { advancementLevel, description, projects, categories } = data; + const shouldBeExpandedByDefault = Object.keys(data.categories).length === 1; + + return ( + + <> + {projects.length > 0 && ( + <> + + + + )} + {Object.entries(categories).map(([category, skills]) => ( + + ({ + key: name, + title: name, + children: description ?

{description}

: undefined, + icon: , + }))} + /> +
+ ))} + +
+ ); +}; diff --git a/frontend/src/components/modules/AdvancementLevel/index.ts b/frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/index.ts similarity index 100% rename from frontend/src/components/modules/AdvancementLevel/index.ts rename to frontend/src/components/pages/mySpace/MySpaceBucketDetails/modules/AdvancementLevel/index.ts diff --git a/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/MySpaceSoftSkillBucketDetails.interface.ts b/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/MySpaceSoftSkillBucketDetails.interface.ts new file mode 100644 index 00000000..e0a37ef5 --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/MySpaceSoftSkillBucketDetails.interface.ts @@ -0,0 +1,5 @@ +import { SoftSkillBucket } from '@app/types/library'; + +export interface MySpaceSoftSkillBucketDetailsProps { + data: SoftSkillBucket; +} diff --git a/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/MySpaceSoftSkillBucketDetails.tsx b/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/MySpaceSoftSkillBucketDetails.tsx new file mode 100644 index 00000000..18a0eb6b --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/MySpaceSoftSkillBucketDetails.tsx @@ -0,0 +1,52 @@ +'use client'; + +import { routes } from '@app/constants'; +import { Breadcrumbs } from '@app/components/modules/Breadcrumbs'; +import { Typography } from '@app/components/common/Typography'; +import { MySpaceSoftSkillBucketDetailsProps } from './MySpaceSoftSkillBucketDetails.interface'; +import { StatusChip } from '@app/components/common/StatusChip'; +import { AccordionCard } from '@app/components/common/AccordionCard'; +import { AccordionList } from '@app/components/common/AccordionList'; +import { SkillStatusIcon } from '@app/components/modules/SkillStatusIcon'; + +export const MySpaceSoftSkillBucketDetails: React.FC = ({ data }) => { + const { description, status, categories } = data; + + return ( +
+ +
+
+
+
+ + Soft skills + + {status} +
+ + {description} + + {Object.entries(categories).map(([category, skills]) => ( + + ({ + key: name, + title: name, + children: description ?

{description}

: undefined, + icon: , + }))} + /> +
+ ))} +
+
+
+
+ ); +}; diff --git a/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/index.ts b/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/index.ts new file mode 100644 index 00000000..b503e4c0 --- /dev/null +++ b/frontend/src/components/pages/mySpace/MySpaceSoftSkillBucketDetails/index.ts @@ -0,0 +1 @@ +export { MySpaceSoftSkillBucketDetails } from './MySpaceSoftSkillBucketDetails'; diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts index b8802b33..ba887844 100644 --- a/frontend/src/constants/routes.ts +++ b/frontend/src/constants/routes.ts @@ -7,6 +7,7 @@ export const routes = { }, mySpace: { index: '/my-space', + softSkills: '/my-space/soft-skills', }, people: { index: '/people?tab=active&page=1', diff --git a/frontend/src/static/icons/ArrowRight.tsx b/frontend/src/static/icons/ArrowRight.tsx new file mode 100644 index 00000000..f72cbe65 --- /dev/null +++ b/frontend/src/static/icons/ArrowRight.tsx @@ -0,0 +1,11 @@ +export const ArrowRight: React.FC> = (props) => ( + + + +); diff --git a/frontend/src/static/icons/CrossIcon.tsx b/frontend/src/static/icons/CrossIcon.tsx new file mode 100644 index 00000000..d18cacb5 --- /dev/null +++ b/frontend/src/static/icons/CrossIcon.tsx @@ -0,0 +1,10 @@ +export const CrossIcon: React.FC> = (props) => ( + + + +); diff --git a/frontend/src/static/icons/DashedCircle.tsx b/frontend/src/static/icons/DashedCircle.tsx new file mode 100644 index 00000000..b83734a5 --- /dev/null +++ b/frontend/src/static/icons/DashedCircle.tsx @@ -0,0 +1,15 @@ +export const DashedCircle: React.FC> = (props) => ( + + + +); diff --git a/frontend/src/types/library.ts b/frontend/src/types/library.ts index fb94376d..9a54a714 100644 --- a/frontend/src/types/library.ts +++ b/frontend/src/types/library.ts @@ -8,22 +8,33 @@ export interface LadderBand { export interface LadderBandBucket { bucketName: string; bucketSlug: string; - bucketType: typeof BucketType; + bucketType: keyof typeof BucketType; description: string; level?: number; + status?: string; } export interface Bucket { bucketName: string; bucketSlug: string; - bucketType: typeof BucketType; + bucketType: keyof typeof BucketType; description: string; advancementLevels: AdvancementLevel[]; } +export interface SoftSkillBucket { + bucketName: string; + bucketSlug: string; + description: string; + status?: string; + categories: { + [categoryGroup: string]: AtomicSkill[]; + }; +} + export const BucketType = { - HARD: 'hard', - SOFT: 'soft', + hard: 'hard', + soft: 'soft', } as const; export interface AdvancementLevel { @@ -43,4 +54,16 @@ export interface ExampleProject { export interface AtomicSkill { name: string; description?: string; + proofStatus?: keyof typeof ProofStatus; } + +export const ProofStatus = { + approved: 'approved', + pending: 'pending', + rejected: 'rejected', +} as const; + +export const SoftSkillStatus = { + completed: 'completed', + pending: 'pending', +} as const;