feat. added dates & roles to work experience

This commit is contained in:
2026-05-17 22:51:20 +02:00
parent 605c1a41b5
commit 42e605014b
3 changed files with 76 additions and 3 deletions

View File

@@ -160,7 +160,7 @@ export default function AdminPage() {
"projects.json": { name: "", description: "", link: "", open_source: false },
"mini_projects.json": { title: "", description: "", why: "" },
"experience.json": { name: "", type: "experience", description: "" },
"real_work.json": { company: "", summary: "" }
"real_work.json": { company: "", role: "", from: "", until: "", summary: "" }
};
setData([templates[selectedFile], ...data]);
};
@@ -333,6 +333,18 @@ export default function AdminPage() {
<label className="text-xs font-bold text-gray-500 uppercase">Company</label>
<input type="text" value={item.company || ""} onChange={(e) => updateEntry(idx, "company", e.target.value)} className="w-full bg-black/40 border border-white/10 rounded-lg p-2 text-sm" />
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-gray-500 uppercase">Role</label>
<input type="text" value={item.role || ""} onChange={(e) => updateEntry(idx, "role", e.target.value)} className="w-full bg-black/40 border border-white/10 rounded-lg p-2 text-sm" />
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-gray-500 uppercase">From</label>
<input type="text" value={item.from || ""} onChange={(e) => updateEntry(idx, "from", e.target.value)} className="w-full bg-black/40 border border-white/10 rounded-lg p-2 text-sm" placeholder="YYYY-MM" />
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-gray-500 uppercase">Until</label>
<input type="text" value={item.until || ""} onChange={(e) => updateEntry(idx, "until", e.target.value)} className="w-full bg-black/40 border border-white/10 rounded-lg p-2 text-sm" placeholder="YYYY-MM or Present" />
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-gray-500 uppercase">URL</label>
<input type="text" value={item.url || ""} onChange={(e) => updateEntry(idx, "url", e.target.value)} className="w-full bg-black/40 border border-white/10 rounded-lg p-2 text-sm" />

View File

@@ -6,7 +6,50 @@ interface WorkExperienceProps {
realWork: RealWork[];
}
function getWorkDateTimestamp(dateValue?: string) {
if (!dateValue) return Number.NEGATIVE_INFINITY;
const timestamp = Date.parse(dateValue);
return Number.isNaN(timestamp) ? Number.NEGATIVE_INFINITY : timestamp;
}
function getSortTimestamp(entry: RealWork) {
if (!entry.until || entry.until.trim().toLowerCase() === "present") {
return Number.POSITIVE_INFINITY;
}
const untilTimestamp = getWorkDateTimestamp(entry.until);
if (untilTimestamp !== Number.NEGATIVE_INFINITY) return untilTimestamp;
return getWorkDateTimestamp(entry.from);
}
function formatWorkDate(dateValue: string) {
const yearMonthMatch = /^(\d{4})-(\d{2})$/.exec(dateValue.trim());
if (yearMonthMatch) {
const year = Number.parseInt(yearMonthMatch[1], 10);
const monthIndex = Number.parseInt(yearMonthMatch[2], 10) - 1;
if (monthIndex >= 0 && monthIndex <= 11) {
return new Date(Date.UTC(year, monthIndex, 1)).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
timeZone: "UTC",
});
}
}
const timestamp = Date.parse(dateValue);
if (Number.isNaN(timestamp)) return dateValue;
return new Date(timestamp).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
});
}
export function WorkExperience({ realWork }: WorkExperienceProps) {
const sortedRealWork = [...realWork].sort(
(a, b) =>
getSortTimestamp(b) - getSortTimestamp(a) ||
getWorkDateTimestamp(b.from) - getWorkDateTimestamp(a.from),
);
return (
<section className="w-full max-w-4xl mx-auto px-4 space-y-8">
<div className="text-center space-y-2">
@@ -24,13 +67,28 @@ export function WorkExperience({ realWork }: WorkExperienceProps) {
</div>
) : (
<div className="space-y-4">
{realWork.map((entry, index) => (
{sortedRealWork.map((entry, index) => (
<div
key={`${entry.company}-${index}`}
className="p-6 md:p-8 rounded-2xl bg-gradient-to-br from-cyan-500/10 to-blue-500/5 backdrop-blur-sm border border-cyan-500/20 space-y-5"
>
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
<h3 className="text-2xl font-semibold text-white">{entry.company}</h3>
<div>
<h3 className="text-2xl font-semibold text-white">{entry.company}</h3>
{entry.role ? (
<p className="text-sm font-medium text-cyan-200/90 mt-1">{entry.role}</p>
) : null}
{entry.from || entry.until ? (
<p className="text-xs font-medium uppercase tracking-wide text-cyan-300/80 mt-1">
{entry.from ? formatWorkDate(entry.from) : "Unknown"} -{" "}
{entry.until
? entry.until.trim().toLowerCase() === "present"
? "Present"
: formatWorkDate(entry.until)
: "Present"}
</p>
) : null}
</div>
{entry.url ? (
<a
href={entry.url}

View File

@@ -34,6 +34,9 @@ export interface Project {
export interface RealWork {
company: string;
role?: string;
from?: string;
until?: string;
url?: string;
summary: string;
tags?: string[];