diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-01-20 19:32:36 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-01-20 19:32:36 +0100 |
| commit | 50a3df40bc8d41271c4cd8d6873a6d4e1dd87b42 (patch) | |
| tree | ee338cebabbcce5c53c417172b25367c00e19e17 /src | |
| parent | df9e8b1985a0f1c71c0657e72fad008bf437faba (diff) | |
chore: add a project summary component
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/ProjectSummary/ProjectSummary.module.scss | 73 | ||||
| -rw-r--r-- | src/components/ProjectSummary/ProjectSummary.tsx | 122 | ||||
| -rw-r--r-- | src/pages/projet/[slug].tsx | 2 | ||||
| -rw-r--r-- | src/services/repos/github.ts | 15 | ||||
| -rw-r--r-- | src/ts/types/github.ts | 5 |
5 files changed, 217 insertions, 0 deletions
diff --git a/src/components/ProjectSummary/ProjectSummary.module.scss b/src/components/ProjectSummary/ProjectSummary.module.scss new file mode 100644 index 0000000..cf1e77f --- /dev/null +++ b/src/components/ProjectSummary/ProjectSummary.module.scss @@ -0,0 +1,73 @@ +@use "@styles/abstracts/functions" as fun; + +.wrapper { + margin-bottom: var(--spacing-md); + padding: var(--spacing-sm) var(--spacing-md) var(--spacing-md); + border: fun.convert-px(1) solid var(--color-border); +} + +.cover { + height: fun.convert-px(150); + position: relative; +} + +.info { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(20ch, 1fr)); + align-items: start; + justify-content: left; + column-gap: var(--spacing-md); + margin: var(--spacing-md) 0 0; +} + +.inline-data { + display: inline-block; + margin-top: fun.convert-px(3); + + &:not(:last-of-type) { + margin-right: var(--spacing-xs); + } +} + +.techno { + padding: 0 var(--spacing-2xs); + border: fun.convert-px(1) solid var(--color-primary-darker); +} + +.repo { + display: block; + width: 3em; + height: 3em; + background: none; + box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) + var(--color-shadow), + fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-1) + var(--color-shadow), + fun.convert-px(3) fun.convert-px(4) fun.convert-px(4) fun.convert-px(-3) + var(--color-shadow), + 0 0 0 0 var(--color-shadow); + transition: all 0.3s linear 0s; + + &:hover, + &:focus { + box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) + var(--color-shadow), + fun.convert-px(1) fun.convert-px(1) fun.convert-px(2) fun.convert-px(-1) + var(--color-shadow-light), + fun.convert-px(3) fun.convert-px(3) fun.convert-px(4) fun.convert-px(-4) + var(--color-shadow-light), + fun.convert-px(6) fun.convert-px(6) fun.convert-px(10) fun.convert-px(-3) + var(--color-shadow); + transform: scale(1.15); + } + + &:focus { + outline: var(--color-primary) dashed fun.convert-px(2); + } + + &:active { + box-shadow: 0 0 0 0 var(--color-shadow); + outline: none; + transform: scale(0.9); + } +} diff --git a/src/components/ProjectSummary/ProjectSummary.tsx b/src/components/ProjectSummary/ProjectSummary.tsx new file mode 100644 index 0000000..0d00f06 --- /dev/null +++ b/src/components/ProjectSummary/ProjectSummary.tsx @@ -0,0 +1,122 @@ +import GithubIcon from '@assets/images/social-media/github.svg'; +import GitlabIcon from '@assets/images/social-media/gitlab.svg'; +import { t } from '@lingui/macro'; +import { getRepoData } from '@services/repos/github'; +import { ProjectMeta } from '@ts/types/app'; +import { RepoData } from '@ts/types/github'; +import { slugify } from '@utils/helpers/slugify'; +import Image from 'next/image'; +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import styles from './ProjectSummary.module.scss'; + +const ProjectSummary = ({ + slug, + title, + cover, + meta, +}: { + slug: string; + title: string; + cover: string; + meta: ProjectMeta; +}) => { + const { license, repos, technologies } = meta; + const [data, setData] = useState<RepoData>(); + const { locale } = useRouter(); + const githubUser = process.env.NEXT_PUBLIC_GITHUB_USER; + + useEffect(() => { + getRepoData(slug) + .then((repoData) => setData(repoData)) + .catch((e) => console.error(e)); + }, [slug]); + + const getFormattedDate = (date: string) => { + const dateOptions: Intl.DateTimeFormatOptions = { + day: 'numeric', + month: 'long', + year: 'numeric', + }; + + return new Date(date).toLocaleDateString(locale, dateOptions); + }; + + return ( + <div className={styles.wrapper}> + <div className={styles.cover}> + <Image + src={cover} + alt={t`${title} preview`} + layout="fill" + objectFit="contain" + /> + </div> + <dl className={styles.info}> + {data && ( + <div className={styles.info__item}> + <dt>{t`Created on`}</dt> + <dd>{t`${getFormattedDate(data.created_at)}`}</dd> + </div> + )} + {data && ( + <div className={styles.info__item}> + <dt>{t`Last updated on`}</dt> + <dd>{t`${getFormattedDate(data.updated_at)}`}</dd> + </div> + )} + <div className={styles.info__item}> + <dt>{t`License`}</dt> + <dd>{license}</dd> + </div> + {technologies && ( + <div className={styles.info__item}> + <dt>{t`Technologies`}</dt> + {technologies.map((techno) => ( + <dd + key={slugify(techno)} + className={`${styles.techno} ${styles['inline-data']}`} + > + {techno} + </dd> + ))} + </div> + )} + {repos && ( + <div className={styles.info__item}> + <dt>{t`Repositories`}</dt> + {repos.github && ( + <dd className={styles['inline-data']}> + <a href={repos.github} className={styles.repo}> + <GithubIcon /> + <span className="screen-reader-text">Github</span> + </a> + </dd> + )} + {repos.gitlab && ( + <dd className={styles['inline-data']}> + <a href={repos.gitlab} className={styles.repo}> + <GitlabIcon /> + <span className="screen-reader-text">Gitlab</span> + </a> + </dd> + )} + </div> + )} + {data && ( + <div> + <dt>{t`Popularity`}</dt> + <dd> + ⭐ + <a href={`https://github.com/${githubUser}/${slug}/stargazers`}> + {t`${data.stargazers_count} stars on Github`} + </a> + </dd> + </div> + )} + </dl> + </div> + ); +}; + +export default ProjectSummary; diff --git a/src/pages/projet/[slug].tsx b/src/pages/projet/[slug].tsx index 03aa6ea..ce5dd66 100644 --- a/src/pages/projet/[slug].tsx +++ b/src/pages/projet/[slug].tsx @@ -1,5 +1,6 @@ import { getLayout } from '@components/Layouts/Layout'; import PostHeader from '@components/PostHeader/PostHeader'; +import ProjectSummary from '@components/ProjectSummary/ProjectSummary'; import Sidebar from '@components/Sidebar/Sidebar'; import { ToC } from '@components/Widgets'; import { config } from '@config/website'; @@ -102,6 +103,7 @@ const Project: NextPageWithLayout<ProjectProps> = ({ <ToC /> </Sidebar> <div className={styles.body}> + <ProjectSummary slug={id} title={title} cover={cover} meta={meta} /> <ProjectContent /> </div> </article> diff --git a/src/services/repos/github.ts b/src/services/repos/github.ts new file mode 100644 index 0000000..37400ad --- /dev/null +++ b/src/services/repos/github.ts @@ -0,0 +1,15 @@ +import { RepoData } from '@ts/types/github'; + +/** + * Retrieve repository data from Github by slug. + * @param repo - The repository slug. + * @returns {Promise<RepoData>} - The repository data. + */ +export const getRepoData = async (repo: string): Promise<RepoData> => { + const user = process.env.NEXT_PUBLIC_GITHUB_USER; + const api = `https://api.github.com/repos/${user}/${repo}`; + + const response = await fetch(api); + + return response.json(); +}; diff --git a/src/ts/types/github.ts b/src/ts/types/github.ts new file mode 100644 index 0000000..923fb08 --- /dev/null +++ b/src/ts/types/github.ts @@ -0,0 +1,5 @@ +export type RepoData = { + created_at: string; + updated_at: string; + stargazers_count: number; +}; |
