Files
tietokonepaja.fi/src/views/PortfolioView.tsx
2026-05-19 21:25:23 +03:00

172 lines
4.8 KiB
TypeScript

import { useState } from 'react'
import { useLanguage, type Translations } from '../contexts/LanguageContext'
import styles from './PortfolioView.module.css'
interface Tag {
label: string
color: string
}
interface Project {
nameKey: keyof Translations
descKey: keyof Translations
url: string
sourceUrl?: string
tags: Tag[]
}
const customerProjects: Project[] = [
{
nameKey: 'portfolioProject1Name',
descKey: 'portfolioProject1Desc',
url: 'https://prosinervo.com/',
tags: [
{ label: 'WordPress', color: '#21759b' },
{ label: 'PHP', color: '#7a86b8' },
{ label: 'MySQL', color: '#4479a1' },
],
},
{
nameKey: 'portfolioProject2Name',
descKey: 'portfolioProject2Desc',
url: 'https://www.runosaari.fi/',
sourceUrl: 'https://gitea.tietokonepaja.fi/tietokonepaja/runosaari',
tags: [
{ label: 'Next.js', color: '#3d3d3d' },
{ label: 'React', color: '#087ea4' },
{ label: 'TypeScript', color: '#3178c6' },
{ label: 'SCSS', color: '#c6538c' },
],
},
{
nameKey: 'portfolioProject3Name',
descKey: 'portfolioProject3Desc',
url: 'https://www.livonsaarenosuuskauppa.fi/',
sourceUrl: 'https://gitea.tietokonepaja.fi/tietokonepaja/osuuskauppa',
tags: [
{ label: 'Next.js', color: '#3d3d3d' },
{ label: 'TypeScript', color: '#3178c6' },
{ label: 'SCSS', color: '#c6538c' },
{ label: 'Klapi API', color: '#2e7d32' },
],
},
]
const maintenanceProjects: Project[] = [
{
nameKey: 'portfolioMaint1Name',
descKey: 'portfolioMaint1Desc',
url: 'https://klapi.tietokonepaja.fi/',
sourceUrl: 'https://gitea.tietokonepaja.fi/tietokonepaja/klapi',
tags: [
{ label: 'ASP.NET Core', color: '#512bd4' },
{ label: 'C#', color: '#7b4fa6' },
{ label: 'TypeScript', color: '#3178c6' },
{ label: 'React', color: '#087ea4' },
{ label: 'SQLite', color: '#0f7b6c' },
],
},
{
nameKey: 'portfolioMaint2Name',
descKey: 'portfolioMaint2Desc',
url: 'https://mail.tietokonepaja.fi/',
tags: [
{ label: 'Mailcow', color: '#e65100' },
{ label: 'Docker', color: '#2496ed' },
{ label: 'Postfix', color: '#b71c1c' },
{ label: 'Dovecot', color: '#1565c0' },
{ label: 'Nginx', color: '#009900' },
],
},
{
nameKey: 'portfolioMaint3Name',
descKey: 'portfolioMaint3Desc',
url: 'https://gitea.tietokonepaja.fi/',
tags: [
{ label: 'Gitea', color: '#609926' },
{ label: 'Go', color: '#00add8' },
{ label: 'Docker', color: '#2496ed' },
],
},
{
nameKey: 'portfolioMaint4Name',
descKey: 'portfolioMaint4Desc',
url: 'https://hattara.tietokonepaja.fi/',
tags: [
{ label: 'Nextcloud', color: '#0082c9' },
{ label: 'PHP', color: '#7a86b8' },
{ label: 'Docker', color: '#2496ed' },
],
},
]
function ProjectCard({ project }: { project: Project }) {
const { t } = useLanguage()
const [open, setOpen] = useState(false)
return (
<div className={styles.projectCard}>
<button
className={styles.projectHeader}
onClick={() => setOpen((prev) => !prev)}
aria-expanded={open}
>
<h4>{t[project.nameKey]}</h4>
<div className={styles.tagList}>
{project.tags.map((tag) => (
<span
key={tag.label}
className={styles.tag}
style={{ backgroundColor: tag.color }}
>
{tag.label}
</span>
))}
</div>
<span className={`${styles.chevron}${open ? ` ${styles.open}` : ''}`}></span>
</button>
<div className={`${styles.expandWrapper}${open ? ` ${styles.expanded}` : ''}`}>
<div className={styles.expandContent}>
<p>{t[project.descKey]}</p>
<div className={styles.projectLinks}>
<a href={project.url} target="_blank" rel="noopener noreferrer">
{t.portfolioVisitSite}
</a>
{project.sourceUrl && (
<a href={project.sourceUrl} target="_blank" rel="noopener noreferrer">
{t.portfolioSourceCode}
</a>
)}
</div>
</div>
</div>
</div>
)
}
export default function PortfolioView() {
const { t } = useLanguage()
return (
<div className={styles.portfolio}>
<section>
<h3>{t.portfolioCustomerHeading}</h3>
<div className={styles.projectList}>
{customerProjects.map((project) => (
<ProjectCard key={project.url} project={project} />
))}
</div>
</section>
<section>
<h3>{t.portfolioMaintenanceHeading}</h3>
<div className={styles.projectList}>
{maintenanceProjects.map((project) => (
<ProjectCard key={project.url} project={project} />
))}
</div>
</section>
</div>
)
}