@@ -424,7 +424,7 @@ export default function CvBuilder() {
{collapsedSections.languages && (
-
+
{cvData.languages.map((lang: any, index: number) => (
@@ -464,31 +464,41 @@ export default function CvBuilder() {
{collapsedSections.hobbies && (
-
+
{cvData.hobbies.map((hobby: any, index: number) => (
-
-
-
addListItem('hobbies', { language: '', level: '' })} />
- removeListItem('hobbies', index)} />
+
+
+
addListItem('hobbies', { name: '' })}
+ />
+ removeListItem('hobbies', index)}
+ />
updateListItem('hobbies', index, 'name', value)}
+ onChange={value =>
+ updateListItem('hobbies', index, 'name', value)
+ }
/>
))}
)}
+
@@ -519,6 +529,10 @@ export default function CvBuilder() {
)}
+
+
+
+
);
}
diff --git a/src/app/faq/page.tsx b/src/app/faq/page.tsx
index 8bdc052..4bfaada 100644
--- a/src/app/faq/page.tsx
+++ b/src/app/faq/page.tsx
@@ -1,6 +1,7 @@
import getAndParseFaqs from "@/utils/getAndParseFaqs";
import type { FaqType } from "@/types/faq";
import FAQClient from "@/components/core/faqClient";
+import BottomAd from "@/components/ads/bottom";
interface FAQPageProps {
searchParams?: { setAmount?: string };
@@ -18,6 +19,10 @@ export default async function FAQPage({ searchParams }: FAQPageProps) {
return (
);
}
diff --git a/src/app/over-ons/page.tsx b/src/app/over-ons/page.tsx
index 7ff72bd..5086575 100644
--- a/src/app/over-ons/page.tsx
+++ b/src/app/over-ons/page.tsx
@@ -10,7 +10,9 @@ export default async function AboutPage() {
>
)
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 1b03415..fa269f6 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,7 +1,15 @@
import Link from "next/link";
import MakeCvButton from "@/components/buttons/makeCvButton";
+import FAQClient from "@/components/core/faqClient";
+import type {FaqType} from "@/types/faq";
+import getAndParseFaqs from "@/utils/getAndParseFaqs";
+import Image from "next/image";
+import BottomAd from "@/components/ads/bottom";
+
+export default async function Home() {
+ const allFaqs: FaqType[] = await getAndParseFaqs();
+ const faqs: FaqType[] = allFaqs.slice(0, 4);
-export default function Home() {
return (
<>
@@ -24,9 +32,11 @@ export default function Home() {
-
@@ -35,9 +45,11 @@ export default function Home() {
-
@@ -46,9 +58,11 @@ export default function Home() {
-
@@ -94,13 +108,18 @@ export default function Home() {
Veelgestelde vragen
+
+
Voor alle vragen{" "}
klik hier
+
+
+
>
);
diff --git a/src/app/privacy/page.tsx b/src/app/privacy/page.tsx
index eda1300..ac5d60b 100644
--- a/src/app/privacy/page.tsx
+++ b/src/app/privacy/page.tsx
@@ -10,7 +10,9 @@ export default async function PrivacyPage() {
>
)
diff --git a/src/components/buttons/button.tsx b/src/components/buttons/button.tsx
index 0c636c8..89106da 100644
--- a/src/components/buttons/button.tsx
+++ b/src/components/buttons/button.tsx
@@ -1,17 +1,43 @@
-import type { ButtonType } from '@/types/button';
+"use client";
+
+import type { ButtonType } from "@/types/button";
+import { useState } from "react";
+
+export default function Button({ id, label, type, onClickAction }: ButtonType) {
+ const [loading, setLoading] = useState(false);
-export default function Button({ id, label, type, onClick }: ButtonType) {
- // @ts-ignore
return (
-
-
- )
+
+
+ );
}
diff --git a/src/components/core/Spinner.tsx b/src/components/core/Spinner.tsx
new file mode 100644
index 0000000..a30cf47
--- /dev/null
+++ b/src/components/core/Spinner.tsx
@@ -0,0 +1,16 @@
+export default function Spinner() {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/core/navbar.tsx b/src/components/core/navbar.tsx
index e9bea83..6c7f861 100644
--- a/src/components/core/navbar.tsx
+++ b/src/components/core/navbar.tsx
@@ -40,7 +40,7 @@ export default function Navbar() {
if (!currentPath) return "text-gray-700 dark:text-white hover:text-orange-600";
return currentPath === path
- ? "text-orange-600 font-semibold"
+ ? "text-orange-600"
: "text-gray-700 hover:text-orange-600 dark:text-white";
};
@@ -67,9 +67,6 @@ export default function Navbar() {
Over ons
-
- Privacy
-
diff --git a/src/components/core/showParsed.tsx b/src/components/core/showParsed.tsx
index 705bec9..5980687 100644
--- a/src/components/core/showParsed.tsx
+++ b/src/components/core/showParsed.tsx
@@ -14,6 +14,7 @@ export default function ShowParsed(html: { html: string ; }) {
prose-h1:text-orange-500 prose-h2:text-orange-500
prose-h3:text-orange-500 prose-strong:dark:text-white
dark:text-white
+ prose-a:text-white
"
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(html.html || ""),
diff --git a/src/components/selects/templateSelect.tsx b/src/components/selects/templateSelect.tsx
index e8eb676..21f031a 100644
--- a/src/components/selects/templateSelect.tsx
+++ b/src/components/selects/templateSelect.tsx
@@ -13,7 +13,7 @@ export default function TemplateSelect({ value, onChange }: InputType) {
name="template"
value={value}
onChange={(e) => onChange((e.currentTarget as unknown as HTMLInputElement).value)}
- className="bg-white border border-solid border-orange-500 text-gray-700 dark:text-white dark:bg-gray-950 text-sm rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-orange-500 block w-full p-2.5"
+ className="bg-white border border-solid border-orange-500 text-gray-700 dark:text-white dark:bg-gray-950 text-sm rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-orange-500 p-2.5"
>
{Object.keys(templateNames).map((key: string) => (
- {phone || '06-12345678'}
+ {phone || "06-12345678"}
@@ -67,7 +67,7 @@ export default function Orion({
E-mailadres
- {email || 'john.doe@example.com'}
+ {email || "john.doe@example.com"}
@@ -75,7 +75,7 @@ export default function Orion({
Woonplaats
- {city || 'Amsterdam'}
+ {city || "Amsterdam"}
@@ -83,13 +83,13 @@ export default function Orion({
Geboortedatum
- {formatDate(birthdate) || '01-01-2000'}
+ {formatDate(birthdate) || "01-01-2000"}
- {skills.length > 0 && skills[0].skill !== '' && (
+ {skills.length > 0 && skills[0].skill !== "" && (
Skills
@@ -104,7 +104,7 @@ export default function Orion({
)}
- {languages.length > 0 && languages[0].language !== '' && (
+ {languages.length > 0 && languages[0].language !== "" && (
Talen
@@ -117,10 +117,10 @@ export default function Orion({
)}
- {hobbies.length > 0 && hobbies[0].name !== '' && (
+ {hobbies.length > 0 && hobbies[0].name !== "" && (
- Hobby's
+ Hobby"s
{hobbies.map((hobby, i) => (
@@ -149,7 +149,7 @@ export default function Orion({
Curriculum Vitae
- {name || 'John Doe'}
+ {name || "John Doe"}
- {edu.name || 'Bachelor of Computer Science'}
+ {edu.name || "Bachelor of Computer Science"}
- ({`${edu.startYear}-${edu.current ? 'Huidig' : edu.endYear}`})
+ ({`${edu.startYear}-${edu.current ? "Huidig" : edu.endYear}`})
- {edu.institution || 'Uni Amsterdam'}
+ {edu.institution || "Uni Amsterdam"}
@@ -195,14 +195,14 @@ export default function Orion({
- {exp.jobTitle || 'Developer'}
+ {exp.jobTitle || "Developer"}
- ({`${exp.startYear}-${exp.current ? 'Huidig' : exp.endYear}`})
+ ({`${exp.startYear}-${exp.current ? "Huidig" : exp.endYear}`})
- {exp.employer || 'Example Tech'}
+ {exp.employer || "Example Tech"}
- {certifications.length > 0 && certifications[0].name !== '' && (
+ {certifications.length > 0 && certifications[0].name !== "" && (
Certificaten
@@ -226,7 +226,7 @@ export default function Orion({
{cert.name}
- ({cert.current ? 'Huidig' : `${cert.month?.slice(0, 3)}-${cert.year}`})
+ ({cert.current ? "Huidig" : `${cert.month?.slice(0, 3)}-${cert.year}`})
Betrokken bij de jeugdtraining, administratieve bijhoudingen en in
diff --git a/src/templates/Saturnus.tsx b/src/templates/Saturnus.tsx
new file mode 100644
index 0000000..53e4d8d
--- /dev/null
+++ b/src/templates/Saturnus.tsx
@@ -0,0 +1,252 @@
+import type { CvBuilderType } from "@/types/templates";
+import { fontMap} from "@/config/fonts";
+import { formatDate } from "@/utils/formatDate";
+
+export const settings = {
+ padding: "0" as const,
+};
+
+export default function Saturnus({
+ name,
+ email,
+ phone,
+ city,
+ birthdate,
+ preferredFunction,
+ aboutMeDescription,
+ profilePicture,
+ primaryColor,
+ secondaryColor,
+ fontFamily,
+ skills,
+ languages,
+ workExperiences,
+ educations,
+ certifications,
+ hobbies
+}: CvBuilderType) {
+ return (
+
+
+
+
+
+
+
+
+
Cv
+
+ Curriculum Vitae
+
+
+ {name || "John Doe"}
+
+
+
+
+
+
+
+
+
+ Opleidingen
+
+
+ {educations.map((edu, index) => (
+
+
+
+ {edu.name || "Bachelor of Computer Science"}
+
+
+ ({`${edu.startYear}-${edu.current ? "Huidig" : edu.endYear}`})
+
+
+
+ {edu.institution || "Uni Amsterdam"}
+
+
+
+ ))}
+
+
+
+
+
+ Werkervaring
+
+
+ {workExperiences.map((exp, index) => (
+
+
+
+ {exp.jobTitle || "Developer"}
+
+
+ ({`${exp.startYear}-${exp.current ? "Huidig" : exp.endYear}`})
+
+
+
+ {exp.employer || "Example Tech"}
+
+
+
+
+ ))}
+
+
+
+ {certifications.length > 0 && certifications[0].name !== "" && (
+
+
+ Certificaten
+
+
+ {certifications.map((cert: any, index: any) => (
+
+
+
+ {cert.name}
+
+
+ ({cert.current ? "Huidig" : `${cert.month?.slice(0, 3)}-${cert.year}`})
+
+
+ Betrokken bij de jeugdtraining, administratieve bijhoudingen en in
+ bijzonder bestuursondersteuning.
+
+ ))}
+
+
+ )}
+
+
+
+ {name || "John Doe"} © {new Date().getFullYear()}
+
+
+
+
+
+ );
+}
diff --git a/src/types/button.ts b/src/types/button.ts
index dca661b..cf0218b 100644
--- a/src/types/button.ts
+++ b/src/types/button.ts
@@ -2,5 +2,5 @@ export type ButtonType = {
id?: string;
label: string;
type: 'button' | 'submit' | 'reset';
- onClick: () => void;
+ onClickAction?: () => void;
}
diff --git a/src/utils/exporttoPdf.ts b/src/utils/exporttoPdf.ts
index b6643db..faf4bff 100644
--- a/src/utils/exporttoPdf.ts
+++ b/src/utils/exporttoPdf.ts
@@ -1,32 +1,37 @@
-import html2canvas from 'html2canvas-pro';
-import jsPDF from 'jspdf';
-
-export const exportToPdf = async (elementId: string) => {
- const input = document.getElementById(elementId);
- if (!input) return console.error('Input element not found.');
-
- const canvas = await html2canvas(input, { scale: 2, useCORS: true });
- const imgData: string = canvas.toDataURL('image/jpeg', 1.0);
- const pdf = new jsPDF('p', 'mm', 'a4');
-
- const a4Width: number = pdf.internal.pageSize.getWidth();
- const a4Height: number = pdf.internal.pageSize.getHeight();
-
- const pxPerMm: number = canvas.width / a4Width;
- const imgWidth: number = a4Width;
- const imgHeight: number = canvas.height / pxPerMm;
-
- let totalPages: number = Math.ceil(imgHeight / a4Height);
-
- // Hacky workaround
- if (totalPages > 2) totalPages -= 1;
-
- for (let i: number = 0; i < totalPages; i++) {
- if (i > 0) pdf.addPage();
-
- const position: number = -i * a4Height;
- pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
- }
-
- pdf.save('cv.pdf');
-};
+export async function exportToPdf() {
+ console.log("Generating PDF...");
+
+ const element = document.getElementById("pdf");
+
+ if (!element) return;
+
+ const content = element.outerHTML;
+
+ const html = `
+
+
+
+
+
+
+ ${content}
+
+
+ `;
+
+ const res = await fetch("/api/generate-pdf", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ html }),
+ });
+
+ const blob = await res.blob();
+ const url = window.URL.createObjectURL(blob);
+
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = "download.pdf";
+ link.click();
+
+ window.URL.revokeObjectURL(url);
+}
diff --git a/src/utils/getAndParseFaqs.ts b/src/utils/getAndParseFaqs.ts
index 44bd818..c7bc8a7 100644
--- a/src/utils/getAndParseFaqs.ts
+++ b/src/utils/getAndParseFaqs.ts
@@ -6,7 +6,7 @@ import frontMatter from "front-matter";
import type { FaqType } from "@/types/faq";
export default async function getAndParseFaqs(): Promise
{
- const faqDir = path.join(process.cwd(), "src", "app", "content", "faq");
+ const faqDir = path.join(process.cwd(), "src", "content", "faq");
const filenames = fs.readdirSync(faqDir);
const files: FaqType[] = [];
diff --git a/src/utils/getTemplates.ts b/src/utils/getTemplates.ts
index c8f2351..5b1f215 100644
--- a/src/utils/getTemplates.ts
+++ b/src/utils/getTemplates.ts
@@ -1,12 +1,14 @@
-import Luna, { settings as lunaSettings } from '@/templates/Luna';
-import Nova, { settings as novaSettings } from '@/templates/Nova';
-import Orion, { settings as orionSettings } from '@/templates/Orion';
+import Luna, { settings as lunaSettings } from "@/templates/Luna";
+import Nova, { settings as novaSettings } from "@/templates/Nova";
+import Orion, { settings as orionSettings } from "@/templates/Orion";
+import Saturnus, { settings as saturnusSettings } from "@/templates/Saturnus";
export function getTemplates(onlyNames = false) {
const templates: Record = {
- Luna: onlyNames ? 'Luna' : { component: Luna, settings: lunaSettings },
- Nova: onlyNames ? 'Nova' : { component: Nova, settings: novaSettings },
- Orion: onlyNames ? 'Orion' : { component: Orion, settings: orionSettings },
+ Luna: onlyNames ? "Luna" : { component: Luna, settings: lunaSettings },
+ Nova: onlyNames ? "Nova" : { component: Nova, settings: novaSettings },
+ Orion: onlyNames ? "Orion" : { component: Orion, settings: orionSettings },
+ Saturnus: onlyNames ? "Saturnus" : { component: Saturnus, settings: saturnusSettings },
};
return templates;
diff --git a/src/utils/getandParseMarkup.ts b/src/utils/getandParseMarkup.ts
index d9ae5bb..2a50687 100644
--- a/src/utils/getandParseMarkup.ts
+++ b/src/utils/getandParseMarkup.ts
@@ -6,13 +6,13 @@ import frontMatter from "front-matter";
import type { BlogType } from "@/types/blog";
export async function getAndParse(filePath: string): Promise {
- const absPath = path.join(process.cwd(), "src", "app", "content", filePath);
+ const absPath = path.join(process.cwd(), "src", "content", filePath);
const markdown = fs.readFileSync(absPath, "utf-8");
return DOMPurify.sanitize(await marked.parse(markdown));
}
export async function getAllBlogMarkdownFiles(): Promise {
- const blogDir = path.join(process.cwd(), "src", "app", "content", "blog");
+ const blogDir = path.join(process.cwd(), "src", "content", "blog");
const filenames = fs.readdirSync(blogDir);
const files: BlogType[] = [];