Skip to content

Commit 12807f4

Browse files
committed
Add bilingual about page data
1 parent 831c78d commit 12807f4

20 files changed

Lines changed: 1463 additions & 782 deletions

File tree

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
---
2+
import BackLink from "@/components/BackLink/BackLink.astro";
3+
import Profile from "@/components/Profile/Profile.astro";
4+
import TimelineEntry from "@/components/AboutPage/TimelineEntry.astro";
5+
import WorksGrid from "@/components/WorksGrid/WorksGrid.astro";
6+
import { type PersonalData } from "@/data/personal";
7+
import {
8+
formatHistoryDate,
9+
sortByDateDesc,
10+
sortHistory,
11+
type AboutCopy,
12+
} from "@/libraries/about";
13+
import styles from "@/styles/about.module.scss";
14+
15+
interface Props {
16+
copy: AboutCopy;
17+
personalData: PersonalData;
18+
supplementalLink?: {
19+
href: string;
20+
label: string;
21+
ariaLabel?: string;
22+
};
23+
}
24+
25+
const { copy, personalData, supplementalLink } = Astro.props as Props;
26+
27+
const sortedHistory = sortHistory(personalData.history);
28+
const educationHistory = sortedHistory.filter(
29+
(item) => item.type === "education"
30+
);
31+
const careerHistory = sortedHistory.filter((item) => item.type !== "education");
32+
const sortedCerts = sortByDateDesc(personalData.certs);
33+
const sortedCtfs = sortByDateDesc(personalData.ctfs);
34+
---
35+
36+
<div class={styles.container}>
37+
<BackLink title={copy.backLinkTitle} supplementalLink={supplementalLink} />
38+
<Profile mode="row" hideAbout class={styles.header} priority data={personalData} />
39+
<div class={styles.content}>
40+
<section class={styles.interests}>
41+
<div class={styles.sectionHeader}>
42+
<h2 class={styles.sectionTitle}>{copy.sections.interests}</h2>
43+
</div>
44+
<div class={styles.sectionContent}>
45+
<ul>
46+
{personalData.interests.map((interest) => <li>{interest}</li>)}
47+
</ul>
48+
</div>
49+
</section>
50+
<section class={styles.likes}>
51+
<div class={styles.sectionHeader}>
52+
<h2 class={styles.sectionTitle}>{copy.sections.likes}</h2>
53+
</div>
54+
<div class={styles.sectionContent}>
55+
<ul>
56+
{personalData.likes.map((like) => <li>{like}</li>)}
57+
</ul>
58+
<div class={styles.spotifyEmbed}>
59+
<iframe
60+
title="Top Tracks"
61+
src={`https://stats-embeds.kq5.jp/embed/top?user=${personalData.statsfmUsername}`}
62+
width="100%"
63+
height="160"
64+
loading="lazy"></iframe>
65+
</div>
66+
<p>
67+
{copy.music.prefix}
68+
<a href="/music/" class={styles.itemLink}>{copy.music.label}</a>
69+
{copy.music.suffix}
70+
</p>
71+
</div>
72+
</section>
73+
<section class={styles.history}>
74+
<div class={styles.sectionHeader}>
75+
<h2 class={styles.sectionTitle}>{copy.sections.education}</h2>
76+
</div>
77+
<div class:list={[styles.sectionContent, styles.timeline]}>
78+
{
79+
educationHistory.map((item) => (
80+
<TimelineEntry
81+
date={formatHistoryDate(item)}
82+
title={item.title}
83+
links={item.links}
84+
layout="term"
85+
>
86+
{item.description && <p>{item.description}</p>}
87+
</TimelineEntry>
88+
))
89+
}
90+
</div>
91+
</section>
92+
<section class={styles.history}>
93+
<div class={styles.sectionHeader}>
94+
<h2 class={styles.sectionTitle}>{copy.sections.career}</h2>
95+
</div>
96+
<div class:list={[styles.sectionContent, styles.timeline]}>
97+
{
98+
careerHistory.map((item) => (
99+
<TimelineEntry
100+
date={formatHistoryDate(item)}
101+
title={item.title}
102+
links={item.links}
103+
layout="term"
104+
>
105+
{item.description && <p>{item.description}</p>}
106+
</TimelineEntry>
107+
))
108+
}
109+
</div>
110+
</section>
111+
<section class={styles.certs}>
112+
<div class={styles.sectionHeader}>
113+
<h2 class={styles.sectionTitle}>{copy.sections.certs}</h2>
114+
</div>
115+
<div class:list={[styles.sectionContent, styles.timeline]}>
116+
{
117+
sortedCerts.map((cert) => (
118+
<TimelineEntry
119+
date={cert.date}
120+
title={cert.name}
121+
links={cert.links}
122+
layout="date"
123+
>
124+
{cert.description && <p>{cert.description}</p>}
125+
</TimelineEntry>
126+
))
127+
}
128+
</div>
129+
</section>
130+
<section class={styles.ctfs}>
131+
<div class={styles.sectionHeader}>
132+
<h2 class={styles.sectionTitle}>{copy.sections.ctfs}</h2>
133+
</div>
134+
<div class:list={[styles.sectionContent, styles.timeline]}>
135+
{
136+
sortedCtfs.map((ctf) => (
137+
<TimelineEntry
138+
date={ctf.date}
139+
title={ctf.name}
140+
links={ctf.links}
141+
layout="date"
142+
>
143+
<p>
144+
{ctf.team} / {ctf.rank}
145+
</p>
146+
</TimelineEntry>
147+
))
148+
}
149+
</div>
150+
</section>
151+
<section class={styles.works}>
152+
<div class={styles.sectionHeader}>
153+
<h2 class={styles.sectionTitle}>{copy.sections.works}</h2>
154+
<a
155+
href="/works/"
156+
class={styles.sectionHeaderLink}
157+
aria-label={copy.worksLink.ariaLabel}
158+
>
159+
{copy.worksLink.label}
160+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
161+
</a>
162+
</div>
163+
<WorksGrid onlyPinned={true} works={personalData.works} />
164+
</section>
165+
<section class={styles.extraLinks}>
166+
<div class={styles.sectionHeader}>
167+
<h2 class={styles.sectionTitle}>{copy.sections.extraLinks}</h2>
168+
</div>
169+
<div class={styles.sectionContent}>
170+
<ul>
171+
{
172+
personalData.extraLinks.map((link) => (
173+
<li>
174+
<a href={link.url} target="_blank" rel="noopener noreferrer">
175+
{link.name}
176+
</a>
177+
</li>
178+
))
179+
}
180+
</ul>
181+
</div>
182+
</section>
183+
</div>
184+
</div>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
import styles from "@/styles/about.module.scss";
3+
4+
interface LinkItem {
5+
name: string;
6+
url: string;
7+
}
8+
9+
interface Props {
10+
date: string;
11+
title: string;
12+
links?: LinkItem[];
13+
layout?: "term" | "date";
14+
}
15+
16+
const { date, title, links, layout = "term" } = Astro.props as Props;
17+
---
18+
19+
<div
20+
class={layout === "term" ? styles.termTimelineItem : styles.dateTimelineItem}
21+
>
22+
<div class={styles.timelineDate}>{date}</div>
23+
<div class={styles.timelineContent}>
24+
<h3>{title}</h3>
25+
<div class={styles.contentMeta}>
26+
{
27+
links && (
28+
<div class={styles.itemLinks}>
29+
{links.map((link) => (
30+
<a
31+
href={link.url}
32+
class={styles.itemLink}
33+
target={link.url.startsWith("http") ? "_blank" : undefined}
34+
rel={
35+
link.url.startsWith("http") ? "noopener noreferrer" : undefined
36+
}
37+
>
38+
{link.name}
39+
</a>
40+
))}
41+
</div>
42+
)
43+
}
44+
<slot />
45+
</div>
46+
</div>
47+
</div>

src/components/BackLink/BackLink.astro

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,30 @@ import styles from "./BackLink.module.scss";
44
interface Props {
55
to?: string;
66
title?: string;
7+
supplementalLink?: {
8+
href: string;
9+
label: string;
10+
ariaLabel?: string;
11+
};
712
}
813
9-
const { to = "/", title = "Home" }: Props = Astro.props;
14+
const { to = "/", title = "Home", supplementalLink }: Props = Astro.props;
1015
---
1116

12-
<a href={to} class={styles.backLink}>
13-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>
14-
<span>{title}</span>
15-
</a>
17+
<div class={styles.backLinkRow}>
18+
<a href={to} class={styles.backLink}>
19+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>
20+
<span>{title}</span>
21+
</a>
22+
{
23+
supplementalLink && (
24+
<a
25+
href={supplementalLink.href}
26+
class={styles.supplementalLink}
27+
aria-label={supplementalLink.ariaLabel ?? supplementalLink.label}
28+
>
29+
{supplementalLink.label}
30+
</a>
31+
)
32+
}
33+
</div>

src/components/BackLink/BackLink.module.scss

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
@use "../../styles/global.scss" as *;
22

3+
.backLinkRow {
4+
display: flex;
5+
align-items: center;
6+
justify-content: space-between;
7+
gap: 1rem;
8+
margin-bottom: 1rem;
9+
}
10+
311
.backLink {
412
display: inline-flex;
513
align-items: center;
614
gap: 0.5rem;
715
color: $text-secondary;
816
font-size: 0.875rem;
9-
margin-bottom: 1rem;
1017
transition: color 0.2s;
1118

1219
&:hover {
@@ -23,3 +30,13 @@
2330
transition: transform 0.2s;
2431
}
2532
}
33+
34+
.supplementalLink {
35+
color: $text-secondary;
36+
font-size: 0.875rem;
37+
transition: color 0.2s;
38+
39+
&:hover {
40+
color: $text-primary;
41+
}
42+
}

src/components/Footer/Footer.astro

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
2-
import { METADATA, PERSONAL_DATA } from "@/const";
2+
import { METADATA } from "@/const";
3+
import { PERSONAL_DATA } from "@/data/personal";
34
45
import styles from "./Footer.module.scss";
56

src/components/Profile/Profile.astro

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
import { Image } from "astro:assets";
33
4-
import { PERSONAL_DATA } from "@/const";
4+
import { PERSONAL_DATA, type PersonalData } from "@/data/personal";
55
import iconImage from "@/images/icon.png";
66
77
import styles from "./Profile.module.scss";
@@ -11,13 +11,15 @@ interface Props {
1111
class?: string;
1212
hideAbout?: boolean;
1313
priority?: boolean;
14+
data?: PersonalData;
1415
}
1516
1617
const {
1718
class: className,
1819
mode = "column",
1920
hideAbout = false,
2021
priority = false,
22+
data = PERSONAL_DATA,
2123
} = Astro.props as Props;
2224
---
2325

@@ -32,13 +34,13 @@ const {
3234
class={styles.icon}
3335
format="avif"
3436
/>
35-
</div>
37+
</div>
3638
<div class={styles.info}>
3739
<h1 class={styles.name}>
38-
{PERSONAL_DATA.name}
39-
<span class={styles.pronunciation}>{PERSONAL_DATA.pronunciation}</span>
40+
{data.name}
41+
<span class={styles.pronunciation}>{data.pronunciation}</span>
4042
</h1>
41-
{mode === "column" && <p class={styles.bio}>{PERSONAL_DATA.bio}</p>}
43+
{mode === "column" && <p class={styles.bio}>{data.bio}</p>}
4244
<div class={styles.links}>
4345
{
4446
!hideAbout && (
@@ -52,18 +54,18 @@ const {
5254
</a>
5355
)
5456
}
55-
<a
56-
href={PERSONAL_DATA.links.twitter}
57-
target="_blank"
58-
rel="noopener noreferrer"
59-
class={styles.link}
57+
<a
58+
href={data.links.twitter}
59+
target="_blank"
60+
rel="noopener noreferrer"
61+
class={styles.link}
6062
aria-label="Twitter"
6163
title="Twitter"
6264
>
6365
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"/></svg>
6466
</a>
6567
<a
66-
href={PERSONAL_DATA.links.github}
68+
href={data.links.github}
6769
target="_blank"
6870
rel="noopener noreferrer"
6971
class={styles.link}
@@ -73,7 +75,7 @@ const {
7375
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>
7476
</a>
7577
<a
76-
href={`mailto:${PERSONAL_DATA.links.mail}`}
78+
href={`mailto:${data.links.mail}`}
7779
class={styles.link}
7880
aria-label="Mail"
7981
title="Mail"

0 commit comments

Comments
 (0)