mirror of
https://github.com/XFox111/my-website.git
synced 2026-04-22 07:28:01 +03:00
feat: ATS-compliant resume download links
This commit is contained in:
+2
-1
@@ -11,7 +11,8 @@ SMTP_TO_EMAIL=email # Email to which emails will be sent
|
|||||||
|
|
||||||
DOMAIN_NAME=example.com # Your domain name
|
DOMAIN_NAME=example.com # Your domain name
|
||||||
RESUME_URL=URL # Location of the resume PDF
|
RESUME_URL=URL # Location of the resume PDF
|
||||||
RESUME_HAS_REFS=false # Appends last page of the resume to a result PDF file
|
ATS_RESUME_URL=URL # Location of the ATS-compatible resume PDF (optional, remove to disable)
|
||||||
|
RESUME_HAS_REFS=false # Appends last page of the resume to a result PDF file (only appies to non-ATS version)
|
||||||
ALERT_TEXT_URL=URL # URL of a txt file with urgent message to be displayed (see app/_components/AlertMessage.tsx)
|
ALERT_TEXT_URL=URL # URL of a txt file with urgent message to be displayed (see app/_components/AlertMessage.tsx)
|
||||||
CLARITY_ID=string # Clarity Analytics ID (optional, remove to disable)
|
CLARITY_ID=string # Clarity Analytics ID (optional, remove to disable)
|
||||||
CLARITY_CONSENT=1 # 1 if you need to request explicit consent from user, 0 if not (requires CLARITY_ID)
|
CLARITY_CONSENT=1 # 1 if you need to request explicit consent from user, 0 if not (requires CLARITY_ID)
|
||||||
|
|||||||
@@ -5,18 +5,22 @@ import { PDFDocument, PDFPage } from "pdf-lib";
|
|||||||
export async function GET(req: NextRequest): Promise<Response>
|
export async function GET(req: NextRequest): Promise<Response>
|
||||||
{
|
{
|
||||||
const type: string | null = req.nextUrl.searchParams.get("type");
|
const type: string | null = req.nextUrl.searchParams.get("type");
|
||||||
|
const isAts: boolean = req.nextUrl.searchParams.get("ats") === "true";
|
||||||
const resume: Resume | undefined = findResume(type);
|
const resume: Resume | undefined = findResume(type);
|
||||||
|
const url: string | undefined = isAts ? process.env.ATS_RESUME_URL : process.env.RESUME_URL;
|
||||||
|
|
||||||
if (!resume)
|
if (!resume)
|
||||||
return error(400, "'type' parameter is invalid");
|
return error(400, "'type' parameter is invalid");
|
||||||
|
|
||||||
if (!process.env.RESUME_URL)
|
const fileName: string = (isAts ? "(ATS) " + resume.fileName : resume.fileName).replaceAll("\"", "'");
|
||||||
|
|
||||||
|
if (!url)
|
||||||
return error(500, "Cannot find file location.");
|
return error(500, "Cannot find file location.");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Fetch the PDF file from the remote URL using the fetch API
|
// Fetch the PDF file from the remote URL using the fetch API
|
||||||
const response: Response = await fetch(process.env.RESUME_URL as string);
|
const response: Response = await fetch(url);
|
||||||
|
|
||||||
if (!response.ok)
|
if (!response.ok)
|
||||||
return error(500, "Failed to fetch PDF file");
|
return error(500, "Failed to fetch PDF file");
|
||||||
@@ -30,7 +34,7 @@ export async function GET(req: NextRequest): Promise<Response>
|
|||||||
const [page, refs]: PDFPage[] = await newDoc.copyPages(srcDoc, [resume.pageIndex, srcDoc.getPageCount() - 1]);
|
const [page, refs]: PDFPage[] = await newDoc.copyPages(srcDoc, [resume.pageIndex, srcDoc.getPageCount() - 1]);
|
||||||
newDoc.addPage(page);
|
newDoc.addPage(page);
|
||||||
|
|
||||||
if (process.env.RESUME_HAS_REFS === "true")
|
if (process.env.RESUME_HAS_REFS === "true" && isAts)
|
||||||
newDoc.addPage(refs);
|
newDoc.addPage(refs);
|
||||||
|
|
||||||
// Serialize the new PDF document
|
// Serialize the new PDF document
|
||||||
@@ -43,7 +47,7 @@ export async function GET(req: NextRequest): Promise<Response>
|
|||||||
// Set response headers for PDF file
|
// Set response headers for PDF file
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/pdf",
|
"Content-Type": "application/pdf",
|
||||||
"Content-Disposition": `inline; filename="${resume.fileName.replaceAll("\"", "'")}.pdf"`
|
"Content-Disposition": `inline; filename="${fileName}.pdf"`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,6 +20,16 @@
|
|||||||
grid-auto-columns: 1fr;
|
grid-auto-columns: 1fr;
|
||||||
gap: $spacingXL;
|
gap: $spacingXL;
|
||||||
|
|
||||||
|
.buttonContainer
|
||||||
|
{
|
||||||
|
@include flex(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
.atsLink
|
||||||
|
{
|
||||||
|
@include body1();
|
||||||
|
}
|
||||||
|
|
||||||
.button
|
.button
|
||||||
{
|
{
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
|
|||||||
+8
-1
@@ -21,7 +21,8 @@ const ResumePage: React.FC = () => (
|
|||||||
<h1>Who are you looking for?</h1>
|
<h1>Who are you looking for?</h1>
|
||||||
<div className={ cls.resumeButtons }>
|
<div className={ cls.resumeButtons }>
|
||||||
{ resumeList.map(i =>
|
{ resumeList.map(i =>
|
||||||
<Button key={ i.key } className={ cls.button }
|
<div key={ i.key } className={ cls.buttonContainer }>
|
||||||
|
<Button className={ cls.button }
|
||||||
href={ `/resume/download?type=${i.key}` } download
|
href={ `/resume/download?type=${i.key}` } download
|
||||||
icon={
|
icon={
|
||||||
<Image className={ cls.image } src={ i.image.src } priority draggable={ false }
|
<Image className={ cls.image } src={ i.image.src } priority draggable={ false }
|
||||||
@@ -30,6 +31,12 @@ const ResumePage: React.FC = () => (
|
|||||||
|
|
||||||
{ i.label }
|
{ i.label }
|
||||||
</Button>
|
</Button>
|
||||||
|
{ process.env.ATS_RESUME_URL &&
|
||||||
|
<a className={ cls.atsLink } href={ `/resume/download?type=${i.key}&ats=true` } download>
|
||||||
|
ATS-compatible version
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
) }
|
) }
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user