Rewrite in React
This commit is contained in:
171
src/views/PortfolioView.tsx
Normal file
171
src/views/PortfolioView.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user