1
0
mirror of https://github.com/XFox111/my-website.git synced 2026-04-22 07:28:01 +03:00

init: First version

This commit is contained in:
2024-08-19 23:08:50 +00:00
commit 3ec7d9a722
134 changed files with 17088 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
import resumeList, { Resume } from "@/_data/resumeList";
import { NextRequest } from "next/server";
import { PDFDocument, PDFPage } from "pdf-lib";
export async function GET(req: NextRequest): Promise<Response>
{
const type: string | null = req.nextUrl.searchParams.get("type");
const resume: Resume | undefined = findResume(type);
if (!resume)
return error(400, "'type' parameter is invalid");
if (!process.env.RESUME_URL)
return error(500, "Cannot find file location.");
try
{
// Fetch the PDF file from the remote URL using the fetch API
const response: Response = await fetch(process.env.RESUME_URL as string);
if (!response.ok)
return error(500, "Failed to fetch PDF file");
// Load the PDF document
const pdfData: ArrayBuffer = await response.arrayBuffer();
const srcDoc: PDFDocument = await PDFDocument.load(pdfData);
// Create a new PDF document with the specified page
const newDoc: PDFDocument = await PDFDocument.create();
const [page]: PDFPage[] = await newDoc.copyPages(srcDoc, [resume.pageIndex]);
newDoc.addPage(page);
// Serialize the new PDF document
const pdfBytes: Uint8Array = await newDoc.save();
// Send the PDF page as a response
return new Response(
Buffer.from(pdfBytes),
{
// Set response headers for PDF file
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": `inline; filename="${resume.fileName.replaceAll("\"", "'")}.pdf"`
}
}
);
}
catch (ex)
{
console.error("Error processing PDF:", ex);
return error(500, "Failed to process PDF file");
}
}
const findResume = (type: string | null): Resume | undefined =>
resumeList.find(i => i.key === type) ?? resumeList.find(i => i.default);
const error = (status: number, message: string): Response =>
new Response(message, { status });
+89
View File
@@ -0,0 +1,89 @@
@import "../theme.scss";
.page
{
@include flex(column);
@include align(center, center);
@include subtitle1($fontFamilyBaseAlt);
text-align: center;
gap: $spacingXXXL;
h1
{
@include title1($fontFamilyBaseAlt);
}
.resumeButtons
{
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
gap: $spacingXL;
.button
{
flex-flow: column;
text-align: center;
.image
{
height: 128px;
width: auto;
}
}
@media screen and (max-width: 860px)
{
grid-auto-flow: row;
.button
{
flex-flow: row;
justify-content: flex-start;
text-align: left;
.image
{
width: 48px;
height: 48px;
}
}
}
}
.linkedin
{
position: relative;
background-image: none;
overflow: clip;
> i
{
height: 32px !important;
width: 32px !important;
}
.circle
{
position: absolute;
height: 32px;
width: 32px;
border-radius: $borderRadiusCircular;
left: 16px;
z-index: -1;
background-color: var(--network-color);
transition: transform $durationFast $curveEasyEaseMax;
}
&:not(:disabled):hover
{
color: $colorNeutralForegroundStaticInverted;
.circle
{
transform: scale(1100%);
}
}
}
}
+54
View File
@@ -0,0 +1,54 @@
import Button from "@/_components/Button";
import links from "@/_data/links";
import { getTitle } from "@/_data/metadata";
import resumeList from "@/_data/resumeList";
import { Metadata } from "next";
import Image from "next/image";
import React from "react";
import { SocialIcon, social_icons } from "react-social-icons";
import cls from "./page.module.scss";
export const metadata: Metadata =
{
title: getTitle("Resume")
};
// [SPECIAL]
const ResumePage: React.FC = () => (
<main className={ cls.page }>
<h1>Who are you looking for?</h1>
<div className={ cls.resumeButtons }>
{ resumeList.map(i =>
<Button key={ i.key } className={ cls.button }
href={ `/resume/download?type=${i.key}` } download
icon={
<Image className={ cls.image } src={ i.image.src } priority draggable={ false }
aria-hidden alt={ i.image.alt } />
}>
{ i.label }
</Button>
) }
</div>
{ links.linkedin &&
<>
<p>You can also check out my</p>
<Button href={ links.linkedin } target="_blank"
className={ cls.linkedin }
// @ts-expect-error Inline variables are not supported by TS definition, although they work as intended.
style={ { "--network-color": social_icons.get("linkedin")!.color } }
icon={ <SocialIcon network="linkedin" as="i" aria-hidden /> }>
LinkedIn profile
<span className={ cls.circle } />
</Button>
</>
}
</main>
);
export default ResumePage;