Some checks failed
Docker Deploy / build-and-push (push) Has been cancelled
339 lines
14 KiB
Plaintext
339 lines
14 KiB
Plaintext
---
|
|
import { Icon } from "astro-icon/components";
|
|
import Layout from "../layouts/Layout.astro";
|
|
import ResumeSkills from "../components/ResumeSkills.vue";
|
|
import ResumeDownloadButton from "../components/ResumeDownloadButton.vue";
|
|
import ResumeSettingsModal from "../components/ResumeSettingsModal.vue";
|
|
import { config } from "../config";
|
|
import "../styles/global.css";
|
|
import * as TOML from "@iarna/toml";
|
|
|
|
interface ResumeData {
|
|
basics: {
|
|
name: string;
|
|
email: string;
|
|
website?: string;
|
|
profiles?: {
|
|
network: string;
|
|
username: string;
|
|
url: string;
|
|
}[];
|
|
};
|
|
summary?: {
|
|
content: string;
|
|
};
|
|
experience?: {
|
|
company: string;
|
|
position: string;
|
|
location: string;
|
|
date: string;
|
|
description: string[];
|
|
url?: string;
|
|
}[];
|
|
education?: {
|
|
institution: string;
|
|
degree: string;
|
|
field: string;
|
|
date: string;
|
|
details?: string[];
|
|
}[];
|
|
skills?: {
|
|
name: string;
|
|
level: number;
|
|
}[];
|
|
volunteer?: {
|
|
organization: string;
|
|
position: string;
|
|
date: string;
|
|
}[];
|
|
awards?: {
|
|
title: string;
|
|
organization: string;
|
|
date: string;
|
|
description?: string;
|
|
}[];
|
|
}
|
|
|
|
let resumeData: ResumeData | undefined = undefined;
|
|
let fetchError: string | null = null;
|
|
|
|
if (!config.resumeConfig.tomlFile || !config.resumeConfig.tomlFile.trim()) {
|
|
return Astro.redirect("/");
|
|
}
|
|
|
|
try {
|
|
let tomlContent: string;
|
|
|
|
if (config.resumeConfig.tomlFile.startsWith("/")) {
|
|
const baseUrl = Astro.url.origin;
|
|
const response = await fetch(
|
|
`${baseUrl}${config.resumeConfig.tomlFile}`,
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(
|
|
`Failed to fetch resume: ${response.status} ${response.statusText}`,
|
|
);
|
|
}
|
|
|
|
tomlContent = await response.text();
|
|
} else {
|
|
tomlContent = config.resumeConfig.tomlFile;
|
|
}
|
|
|
|
resumeData = TOML.parse(tomlContent) as unknown as ResumeData;
|
|
} catch (error) {
|
|
console.error("Error loading resume data:", error);
|
|
return Astro.redirect("/");
|
|
}
|
|
|
|
const data = resumeData;
|
|
const resumeConfig = config.resumeConfig;
|
|
|
|
if (!data) {
|
|
return Astro.redirect("/");
|
|
}
|
|
---
|
|
|
|
<Layout title="Resume">
|
|
<ResumeSettingsModal client:load />
|
|
<div class="container mx-auto p-4 sm:p-6 lg:p-8 max-w-4xl w-full">
|
|
<h1
|
|
class="text-3xl sm:text-4xl font-bold text-primary mb-4 sm:mb-6 text-center"
|
|
>
|
|
{data.basics.name}
|
|
</h1>
|
|
|
|
<div
|
|
class="flex justify-center items-center flex-wrap gap-x-3 sm:gap-x-4 gap-y-2 mb-4 sm:mb-6"
|
|
>
|
|
{
|
|
data.basics.email && (
|
|
<a
|
|
href={`mailto:${data.basics.email}`}
|
|
class="link link-hover inline-flex items-center gap-1 text-sm sm:text-base"
|
|
>
|
|
<Icon name="mdi:email" /> {data.basics.email}
|
|
</a>
|
|
)
|
|
}
|
|
{
|
|
data.basics.profiles?.map((profile) => {
|
|
const iconName = `simple-icons:${profile.network.toLowerCase()}`;
|
|
return (
|
|
<a
|
|
href={profile.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="link link-hover inline-flex items-center gap-1 text-sm sm:text-base"
|
|
>
|
|
<Icon name={iconName} />
|
|
{profile.network}
|
|
</a>
|
|
);
|
|
})
|
|
}
|
|
</div>
|
|
|
|
<ResumeDownloadButton client:load />
|
|
|
|
{
|
|
data.summary &&
|
|
resumeConfig.sections.enabled.includes("summary") && (
|
|
<div class="card bg-base-300 shadow-xl mb-4 sm:mb-6">
|
|
<div class="card-body p-4 sm:p-6 wrap-break-word">
|
|
<h2 class="card-title text-xl sm:text-2xl">
|
|
{resumeConfig.sections.summary?.title ||
|
|
"Summary"}
|
|
</h2>
|
|
<div>{data.summary.content}</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
{
|
|
data.skills &&
|
|
data.skills.length > 0 &&
|
|
resumeConfig.sections.enabled.includes("skills") && (
|
|
<div class="card bg-base-300 shadow-xl mb-4 sm:mb-6">
|
|
<div class="card-body p-4 sm:p-6 wrap-break-word">
|
|
<h2 class="card-title text-xl sm:text-2xl">
|
|
{resumeConfig.sections.skills?.title ||
|
|
"Skills"}
|
|
</h2>
|
|
<ResumeSkills
|
|
skills={data.skills.map((skill, index) => ({
|
|
id: `skill-${index}`,
|
|
name: skill.name,
|
|
level: skill.level,
|
|
}))}
|
|
client:load
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
{
|
|
data.experience &&
|
|
data.experience.length > 0 &&
|
|
resumeConfig.sections.enabled.includes("experience") && (
|
|
<div class="card bg-base-300 shadow-xl mb-4 sm:mb-6">
|
|
<div class="card-body p-4 sm:p-6 wrap-break-word">
|
|
<h2 class="card-title text-xl sm:text-2xl">
|
|
{resumeConfig.sections.experience?.title ||
|
|
"Experience"}
|
|
</h2>
|
|
<div class="space-y-4 sm:space-y-6">
|
|
{data.experience.map((experience) => (
|
|
<div class="border-l-2 border-primary pl-4 sm:pl-6">
|
|
<h3 class="text-lg sm:text-xl font-semibold">
|
|
{experience.position}
|
|
</h3>
|
|
<div class="flex flex-col sm:flex-row sm:items-center sm:gap-4 text-sm sm:text-base text-base-content/70 mb-2">
|
|
<span class="font-medium">
|
|
{experience.company}
|
|
</span>
|
|
<span>{experience.date}</span>
|
|
<span>{experience.location}</span>
|
|
</div>
|
|
<ul class="list-disc list-inside space-y-1 text-sm sm:text-base">
|
|
{experience.description.map(
|
|
(item) => (
|
|
<li>{item}</li>
|
|
),
|
|
)}
|
|
</ul>
|
|
{experience.url && (
|
|
<a
|
|
href={experience.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="inline-flex items-center gap-1 text-primary hover:text-primary-focus text-sm mt-2"
|
|
>
|
|
<Icon name="mdi:link" />
|
|
Website
|
|
</a>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
{
|
|
data.education &&
|
|
data.education.length > 0 &&
|
|
resumeConfig.sections.enabled.includes("education") && (
|
|
<div class="card bg-base-300 shadow-xl mb-4 sm:mb-6">
|
|
<div class="card-body p-4 sm:p-6 wrap-break-word">
|
|
<h2 class="card-title text-xl sm:text-2xl">
|
|
{resumeConfig.sections.education?.title ||
|
|
"Education"}
|
|
</h2>
|
|
<div class="space-y-4">
|
|
{data.education.map((education) => (
|
|
<div class="border-l-2 border-secondary pl-4 sm:pl-6">
|
|
<h3 class="text-lg sm:text-xl font-semibold">
|
|
{education.institution}
|
|
</h3>
|
|
<div class="text-sm sm:text-base text-base-content/70 mb-2">
|
|
<span class="font-medium">
|
|
{education.degree} in{" "}
|
|
{education.field}
|
|
</span>
|
|
<span class="block sm:inline sm:ml-4">
|
|
{education.date}
|
|
</span>
|
|
</div>
|
|
{education.details && (
|
|
<ul class="list-disc list-inside space-y-1 text-sm sm:text-base">
|
|
{education.details.map(
|
|
(detail) => (
|
|
<li>{detail}</li>
|
|
),
|
|
)}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
{
|
|
data.volunteer &&
|
|
data.volunteer.length > 0 &&
|
|
resumeConfig.sections.enabled.includes("volunteer") && (
|
|
<div class="card bg-base-300 shadow-xl mb-4 sm:mb-6">
|
|
<div class="card-body p-4 sm:p-6 wrap-break-word">
|
|
<h2 class="card-title text-xl sm:text-2xl">
|
|
{resumeConfig.sections.volunteer?.title ||
|
|
"Volunteer Work"}
|
|
</h2>
|
|
<div class="space-y-4">
|
|
{data.volunteer.map((volunteer) => (
|
|
<div class="border-l-2 border-accent pl-4 sm:pl-6">
|
|
<h3 class="text-lg sm:text-xl font-semibold">
|
|
{volunteer.organization}
|
|
</h3>
|
|
<div class="text-sm sm:text-base text-base-content/70 mb-2">
|
|
<span class="font-medium">
|
|
{volunteer.position}
|
|
</span>
|
|
<span class="block sm:inline sm:ml-4">
|
|
{volunteer.date}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
{
|
|
data.awards &&
|
|
data.awards.length > 0 &&
|
|
resumeConfig.sections.enabled.includes("awards") && (
|
|
<div class="card bg-base-300 shadow-xl mb-4 sm:mb-6">
|
|
<div class="card-body p-4 sm:p-6 wrap-break-word">
|
|
<h2 class="card-title text-xl sm:text-2xl">
|
|
{resumeConfig.sections.awards?.title ||
|
|
"Awards & Recognition"}
|
|
</h2>
|
|
<div class="space-y-4">
|
|
{data.awards.map((award) => (
|
|
<div class="border-l-2 border-warning pl-4 sm:pl-6">
|
|
<h3 class="text-lg sm:text-xl font-semibold">
|
|
{award.title}
|
|
</h3>
|
|
<div class="text-sm sm:text-base text-base-content/70 mb-2">
|
|
<span class="font-medium">
|
|
{award.organization}
|
|
</span>
|
|
<span class="block sm:inline sm:ml-4">
|
|
{award.date}
|
|
</span>
|
|
</div>
|
|
{award.description && (
|
|
<div class="text-sm sm:text-base text-base-content/80 mt-2">
|
|
{award.description}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
</div>
|
|
</Layout>
|