Files
Emil_Shanayev_Resume/components/Sections.tsx
T
EmilandClaude Sonnet 5 04c1cc5877
Deploy to GitHub Pages / build (push) Canceled after 0s
Deploy to GitHub Pages / deploy (push) Canceled after 0s
Make the resume bilingual and part of the shared site chrome
Adds a Russian version (data/resume.ru.json, default language) with an RU/EN
switch stored under the key shared with the front page and the portfolio.
The top bar becomes the site-wide one (brand, Resume, Portfolio, Gitea,
Contact, language, theme) with a second row of anchors for this page's
sections, and the fonts move to Unbounded and Onest so the three pages share
one type system. Portfolio links now point at /portfolio/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtcZtnG4GQA1d6mW5PNrS9
2026-09-20 23:48:38 +03:00

142 lines
5.7 KiB
TypeScript

import { Bi } from '@/lib/i18n'
import type { Resume } from '@/lib/types'
import styles from './sections.module.css'
// The English resume gives the structure; the Russian one is paired with it by position, and anything the
// Russian file lacks falls back to the English text.
type Pair = { en: Resume; ru: Resume }
export function Skills({ en, ru }: Readonly<Pair>) {
return (
<section className={styles.section} id="skills">
<div className={styles.shell}>
<h2 className={styles.title}><Bi ru="Чем я занимаюсь" en="What I work with" /></h2>
<div className={styles.skills}>
{en.skills.map((skill, i) => {
const r = ru.skills?.[i]
return (
<article className={styles.skill} key={skill.title}>
<h3><Bi ru={r?.title ?? skill.title} en={skill.title} /></h3>
<p><Bi ru={r?.text ?? skill.text} en={skill.text} /></p>
<ul className={styles.tags}>
{skill.stack.map((tag, t) => (
<li key={tag}><Bi ru={r?.stack?.[t] ?? tag} en={tag} /></li>
))}
</ul>
</article>
)
})}
</div>
</div>
</section>
)
}
export function Experience({ en, ru }: Readonly<Pair>) {
return (
<section className={styles.section} id="experience">
<div className={styles.shell}>
<h2 className={styles.title}><Bi ru="Опыт" en="Experience" /></h2>
<div className={styles.entries}>
{en.experience.map((entry, i) => {
const r = ru.experience?.[i]
return (
<article className={styles.entry} key={entry.title}>
<p className={styles.meta}><Bi ru={r?.meta ?? entry.meta} en={entry.meta} /></p>
<div>
<h3><Bi ru={r?.title ?? entry.title} en={entry.title} /></h3>
<ul className={styles.list}>
{entry.points.map((point, p) => (
<li key={point}><Bi ru={r?.points?.[p] ?? point} en={point} /></li>
))}
</ul>
</div>
</article>
)
})}
</div>
</div>
</section>
)
}
export function Education({ en, ru }: Readonly<Pair>) {
return (
<section className={styles.section} id="education">
<div className={styles.shell}>
<h2 className={styles.title}><Bi ru="Образование и языки" en="Education and languages" /></h2>
<div className={styles.columns}>
<div>
<h3 className={styles.label}><Bi ru="Образование" en="Education" /></h3>
<div className={styles.stack}>
{en.education.map((entry, i) => {
const r = ru.education?.[i]
return (
<div key={entry.institution}>
<p className={styles.strong}><Bi ru={r?.institution ?? entry.institution} en={entry.institution} /></p>
<p><Bi ru={r?.program ?? entry.program} en={entry.program} /></p>
<p className={styles.meta}><Bi ru={r?.period ?? entry.period} en={entry.period} /></p>
</div>
)
})}
</div>
</div>
<div>
<h3 className={styles.label}><Bi ru="Языки" en="Languages" /></h3>
<dl className={styles.languages}>
{en.languages.map((language, i) => {
const r = ru.languages?.[i]
return (
<div key={language.name}>
<dt><Bi ru={r?.name ?? language.name} en={language.name} /></dt>
<dd><Bi ru={r?.level ?? language.level} en={language.level} /></dd>
</div>
)
})}
</dl>
</div>
<div>
<h3 className={styles.label}><Bi ru="Интересы" en="Interests" /></h3>
<p>
<Bi
ru={`${(ru.interests?.length ? ru.interests : en.interests).join(', ')}.`}
en={`${en.interests.join(', ')}.`}
/>
</p>
</div>
</div>
</div>
</section>
)
}
export function Contact({ en, ru }: Readonly<Pair>) {
const [primary, ...others] = en.contacts
return (
<footer className={styles.contact} id="contact">
<div className={styles.shell}>
<h2 className={styles.contactTitle}><Bi ru="Связаться" en="Get in touch" /></h2>
<p className={styles.contactLead}><Bi ru="Почта — самый быстрый способ." en="Email is the fastest way to reach me." /></p>
<a className={styles.mail} href={primary.href}>{primary.value}</a>
<ul className={styles.contacts}>
{others.map((contact, i) => {
const label = ru.contacts?.[i + 1]?.label ?? contact.label
return (
<li key={contact.value}>
<span>{label === contact.label ? contact.label : <Bi ru={label} en={contact.label} />}</span>
<a href={contact.href} target={contact.href.startsWith('http') ? '_blank' : undefined} rel="noreferrer">{contact.value}</a>
</li>
)
})}
</ul>
<p className={styles.legal}>
<Bi
ru="© 2026 Эмиль Шанаев. Сайт работает на собственном сервере: Gitea, Caddy и Docker. Резюме и проекты подтягиваются из репозиториев."
en="© 2026 Emil Shanayev. This site runs on my own server: Gitea, Caddy and Docker. The resume and projects are read from repositories."
/>
</p>
</div>
</footer>
)
}