feat(templates): redesign and add back in the original templates from v3

This commit is contained in:
Amruth Pillai
2023-11-26 14:33:40 +01:00
parent 765bf71220
commit 446c5db68f
30 changed files with 2817 additions and 415 deletions

View File

@ -23,7 +23,6 @@ export const Page = ({ mode = "preview", pageNumber, children }: Props) => {
className={cn("relative bg-white", mode === "builder" && "shadow-2xl")} className={cn("relative bg-white", mode === "builder" && "shadow-2xl")}
style={{ style={{
fontFamily, fontFamily,
padding: page.margin,
width: `${pageSizeMap[page.format].width * MM_TO_PX}px`, width: `${pageSizeMap[page.format].width * MM_TO_PX}px`,
minHeight: `${pageSizeMap[page.format].height * MM_TO_PX}px`, minHeight: `${pageSizeMap[page.format].height * MM_TO_PX}px`,
}} }}

View File

@ -32,6 +32,7 @@ export const ArtboardPage = () => {
document.documentElement.style.setProperty("font-size", `${metadata.typography.font.size}px`); document.documentElement.style.setProperty("font-size", `${metadata.typography.font.size}px`);
document.documentElement.style.setProperty("line-height", `${metadata.typography.lineHeight}`); document.documentElement.style.setProperty("line-height", `${metadata.typography.lineHeight}`);
document.documentElement.style.setProperty("--margin", `${metadata.page.margin}px`);
document.documentElement.style.setProperty("--font-size", `${metadata.typography.font.size}px`); document.documentElement.style.setProperty("--font-size", `${metadata.typography.font.size}px`);
document.documentElement.style.setProperty( document.documentElement.style.setProperty(
"--line-height", "--line-height",

View File

@ -486,7 +486,7 @@ export const Azurill = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<div className="space-y-3"> <div className="p-custom space-y-3">
{isFirstPage && <Header />} {isFirstPage && <Header />}
<div className="grid grid-cols-3 gap-x-4"> <div className="grid grid-cols-3 gap-x-4">

View File

@ -497,7 +497,7 @@ export const Bronzor = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<div className="space-y-4"> <div className="p-custom space-y-4">
{isFirstPage && <Header />} {isFirstPage && <Header />}
<div className="space-y-4"> <div className="space-y-4">

View File

@ -29,47 +29,45 @@ const Header = () => {
const basics = useArtboardStore((state) => state.resume.basics); const basics = useArtboardStore((state) => state.resume.basics);
return ( return (
<div className="grid grid-cols-3"> <div className="flex items-center space-x-4">
<div className="col-span-2 flex items-center gap-x-4"> <Picture />
<Picture />
<div className="space-y-2"> <div className="space-y-2">
<div> <div>
<div className="text-2xl font-bold">{basics.name}</div> <div className="text-2xl font-bold">{basics.name}</div>
<div className="text-base">{basics.headline}</div> <div className="text-base">{basics.headline}</div>
</div> </div>
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm"> <div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm">
{basics.location && ( {basics.location && (
<div className="flex items-center gap-x-1.5"> <div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-map-pin text-primary" /> <i className="ph ph-bold ph-map-pin text-primary" />
<div>{basics.location}</div> <div>{basics.location}</div>
</div> </div>
)} )}
{basics.phone && ( {basics.phone && (
<div className="flex items-center gap-x-1.5"> <div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-phone text-primary" /> <i className="ph ph-bold ph-phone text-primary" />
<a href={`tel:${basics.phone}`} target="_blank" rel="noreferrer"> <a href={`tel:${basics.phone}`} target="_blank" rel="noreferrer">
{basics.phone} {basics.phone}
</a> </a>
</div> </div>
)} )}
{basics.email && ( {basics.email && (
<div className="flex items-center gap-x-1.5"> <div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-at text-primary" /> <i className="ph ph-bold ph-at text-primary" />
<a href={`mailto:${basics.email}`} target="_blank" rel="noreferrer"> <a href={`mailto:${basics.email}`} target="_blank" rel="noreferrer">
{basics.email} {basics.email}
</a> </a>
</div> </div>
)} )}
<Link url={basics.url} /> <Link url={basics.url} />
{basics.customFields.map((item) => ( {basics.customFields.map((item) => (
<div key={item.id} className="flex items-center gap-x-1.5"> <div key={item.id} className="flex items-center gap-x-1.5">
<i className={cn(`ph ph-bold ph-${item.icon}`, "text-primary")} /> <i className={cn(`ph ph-bold ph-${item.icon}`, "text-primary")} />
<span>{[item.name, item.value].filter(Boolean).join(": ")}</span> <span>{[item.name, item.value].filter(Boolean).join(": ")}</span>
</div> </div>
))} ))}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -497,35 +495,23 @@ const mapSectionToComponent = (section: SectionKey) => {
}; };
export const Chikorita = ({ columns, isFirstPage = false }: TemplateProps) => { export const Chikorita = ({ columns, isFirstPage = false }: TemplateProps) => {
const margin = useArtboardStore((state) => state.resume.metadata.page.margin);
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<> <div className="grid h-full grid-cols-3">
<div className="space-y-4"> <div className="main p-custom group col-span-2 space-y-4">
{isFirstPage && <Header />} {isFirstPage && <Header />}
<div className="relative z-10 grid grid-cols-3 space-x-8"> {main.map((section) => (
<div className="main group col-span-2 space-y-4"> <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
{main.map((section) => ( ))}
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))}
</div>
<div className="sidebar group space-y-4 text-background">
{sidebar.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))}
</div>
</div>
</div> </div>
<div <div className="sidebar p-custom group h-full space-y-4 bg-primary text-background">
style={{ columnGap: margin }} {sidebar.map((section) => (
className="pointer-events-none absolute inset-0 grid grid-cols-3" <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
> ))}
<div className="col-start-3 bg-primary" />
</div> </div>
</> </div>
); );
}; };

View File

@ -29,16 +29,16 @@ const Header = () => {
const basics = useArtboardStore((state) => state.resume.basics); const basics = useArtboardStore((state) => state.resume.basics);
return ( return (
<div className="relative z-20 grid grid-cols-3 space-x-4"> <div className="p-custom relative grid grid-cols-3 space-x-4 pb-0">
<div className="mx-auto"> <Picture className="mx-auto" />
<Picture />
</div>
<div className="col-span-2 space-y-0.5 text-background"> <div className="relative z-10 col-span-2 text-background">
<h2 className="min-h-[30px] text-4xl font-bold">{basics.name}</h2> <div className="space-y-0.5">
<p className="min-h-[24px]">{basics.headline}</p> <h2 className="text-3xl font-bold">{basics.name}</h2>
<p>{basics.headline}</p>
</div>
<div className="text-text !mt-10"> <div className="text-text col-span-2 col-start-2 mt-10">
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm"> <div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm">
{basics.location && ( {basics.location && (
<> <>
@ -524,19 +524,22 @@ export const Ditto = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<div className="space-y-4"> <div>
{isFirstPage && <div className="absolute inset-x-0 top-0 z-10 h-32 bg-primary" />} {isFirstPage && (
<div className="relative">
<Header />
<div className="absolute inset-x-0 top-0 h-[85px] w-full bg-primary" />
</div>
)}
{isFirstPage && <Header />} <div className="grid grid-cols-3">
<div className="sidebar p-custom group space-y-4">
<div className="grid grid-cols-3 space-x-4">
<div className="sidebar group space-y-4">
{sidebar.map((section) => ( {sidebar.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment> <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))} ))}
</div> </div>
<div className="main group col-span-2 space-y-4"> <div className="main p-custom group col-span-2 space-y-4">
{main.map((section) => ( {main.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment> <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))} ))}

View File

@ -0,0 +1,533 @@
import {
Award,
Certification,
CustomSection,
CustomSectionGroup,
Education,
Experience,
Interest,
Language,
Profile,
Project,
Publication,
Reference,
SectionKey,
SectionWithItem,
Skill,
URL,
Volunteer,
} from "@reactive-resume/schema";
import { cn, hexToRgb, isEmptyString, isUrl } from "@reactive-resume/utils";
import get from "lodash.get";
import { Fragment } from "react";
import { Picture } from "../components/picture";
import { useArtboardStore } from "../store/artboard";
import { TemplateProps } from "../types/template";
const Header = () => {
const basics = useArtboardStore((state) => state.resume.basics);
return (
<div className="p-custom space-y-4 bg-primary text-background">
<Picture className="border-background" />
<div>
<h2 className="text-2xl font-bold">{basics.name}</h2>
<p>{basics.headline}</p>
</div>
<div className="flex flex-col items-start gap-y-2 text-sm">
{basics.location && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-map-pin" />
<div>{basics.location}</div>
</div>
)}
{basics.phone && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-phone" />
<a href={`tel:${basics.phone}`} target="_blank" rel="noreferrer">
{basics.phone}
</a>
</div>
)}
{basics.email && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-at" />
<a href={`mailto:${basics.email}`} target="_blank" rel="noreferrer">
{basics.email}
</a>
</div>
)}
{isUrl(basics.url.href) && <Link url={basics.url} />}
{basics.customFields.map((item) => (
<Fragment key={item.id}>
<div className="flex items-center gap-x-1.5">
<i className={cn(`ph ph-bold ph-${item.icon}`)} />
<span>{[item.name, item.value].filter(Boolean).join(": ")}</span>
</div>
</Fragment>
))}
</div>
</div>
);
};
const Summary = () => {
const section = useArtboardStore((state) => state.resume.sections.summary);
if (!section.visible || isEmptyString(section.content)) return null;
return (
<section id={section.id}>
<div
className="wysiwyg"
style={{ columns: section.columns }}
dangerouslySetInnerHTML={{ __html: section.content }}
/>
</section>
);
};
type RatingProps = { level: number };
const Rating = ({ level }: RatingProps) => (
<div className="flex items-center gap-x-1">
{Array.from({ length: 5 }).map((_, index) => (
<div
key={index}
className={cn("h-2.5 w-5 border border-primary", level > index && "bg-primary")}
/>
))}
</div>
);
type LinkProps = {
url: URL;
icon?: React.ReactNode;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-background" />}
<a
href={url.href}
target="_blank"
rel="noreferrer noopener nofollow"
className={cn("inline-block", className)}
>
{label || url.label || url.href}
</a>
</div>
);
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
className?: string;
urlKey?: keyof T;
levelKey?: keyof T;
summaryKey?: keyof T;
keywordsKey?: keyof T;
};
const Section = <T,>({
section,
children,
className,
urlKey,
levelKey,
summaryKey,
keywordsKey,
}: SectionProps<T>) => {
if (!section.visible || !section.items.length) return null;
return (
<section id={section.id} className="grid">
<h4 className="mb-2 border-b border-primary text-base font-bold">{section.name}</h4>
<div
className="grid gap-x-6 gap-y-3"
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
>
{section.items
.filter((item) => item.visible)
.map((item) => {
const url = (urlKey && get(item, urlKey)) as URL | undefined;
const level = (levelKey && get(item, levelKey, 0)) as number | undefined;
const summary = (summaryKey && get(item, summaryKey, "")) as string | undefined;
const keywords = (keywordsKey && get(item, keywordsKey, [])) as string[] | undefined;
return (
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
)}
{level !== undefined && level > 0 && <Rating level={level} />}
{keywords !== undefined && keywords.length > 0 && (
<p className="text-sm">{keywords.join(", ")}</p>
)}
</div>
);
})}
</div>
</section>
);
};
const Profiles = () => {
const section = useArtboardStore((state) => state.resume.sections.profiles);
const fontSize = useArtboardStore((state) => state.resume.metadata.typography.font.size);
return (
<Section<Profile> section={section}>
{(item) => (
<div>
{isUrl(item.url.href) ? (
<Link
url={item.url}
label={item.username}
icon={
<img
className="ph"
width={fontSize}
height={fontSize}
alt={item.network}
src={`https://cdn.simpleicons.org/${item.icon}`}
/>
}
/>
) : (
<p>{item.username}</p>
)}
<p className="text-sm">{item.network}</p>
</div>
)}
</Section>
);
};
const Experience = () => {
const section = useArtboardStore((state) => state.resume.sections.experience);
return (
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<div>{item.position}</div>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
<div className="font-bold">{item.date}</div>
<div>{item.location}</div>
</div>
</div>
)}
</Section>
);
};
const Education = () => {
const section = useArtboardStore((state) => state.resume.sections.education);
return (
<Section<Education> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
<div className="font-bold">{item.date}</div>
<div>{item.studyType}</div>
</div>
</div>
)}
</Section>
);
};
const Awards = () => {
const section = useArtboardStore((state) => state.resume.sections.awards);
return (
<Section<Award> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const Certifications = () => {
const section = useArtboardStore((state) => state.resume.sections.certifications);
return (
<Section<Certification> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const Skills = () => {
const section = useArtboardStore((state) => state.resume.sections.skills);
return (
<Section<Skill> section={section} levelKey="level" keywordsKey="keywords">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Interests = () => {
const section = useArtboardStore((state) => state.resume.sections.interests);
return (
<Section<Interest> section={section} className="space-y-1" keywordsKey="keywords">
{(item) => <div className="font-bold">{item.name}</div>}
</Section>
);
};
const Publications = () => {
const section = useArtboardStore((state) => state.resume.sections.publications);
return (
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.publisher}</div>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const Volunteer = () => {
const section = useArtboardStore((state) => state.resume.sections.volunteer);
return (
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<div>{item.position}</div>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
<div className="font-bold">{item.date}</div>
<div>{item.location}</div>
</div>
</div>
)}
</Section>
);
};
const Languages = () => {
const section = useArtboardStore((state) => state.resume.sections.languages);
return (
<Section<Language> section={section} levelKey="level">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Projects = () => {
const section = useArtboardStore((state) => state.resume.sections.projects);
return (
<Section<Project> section={section} urlKey="url" summaryKey="summary" keywordsKey="keywords">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const References = () => {
const section = useArtboardStore((state) => state.resume.sections.references);
return (
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Custom = ({ id }: { id: string }) => {
const section = useArtboardStore((state) => state.resume.sections.custom[id]);
return (
<Section<CustomSection>
section={section}
urlKey="url"
summaryKey="summary"
keywordsKey="keywords"
>
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
<div className="font-bold">{item.date}</div>
<div>{item.location}</div>
</div>
</div>
)}
</Section>
);
};
const mapSectionToComponent = (section: SectionKey) => {
switch (section) {
case "profiles":
return <Profiles />;
case "experience":
return <Experience />;
case "education":
return <Education />;
case "awards":
return <Awards />;
case "certifications":
return <Certifications />;
case "skills":
return <Skills />;
case "interests":
return <Interests />;
case "publications":
return <Publications />;
case "volunteer":
return <Volunteer />;
case "languages":
return <Languages />;
case "projects":
return <Projects />;
case "references":
return <References />;
default:
if (section.startsWith("custom.")) return <Custom id={section.split(".")[1]} />;
return null;
}
};
export const Gengar = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns;
const primaryColor = useArtboardStore((state) => state.resume.metadata.theme.primary);
return (
<div className="grid min-h-[inherit] grid-cols-3">
<div
className={cn(
"sidebar group flex flex-col",
!(isFirstPage || sidebar.length > 0) && "hidden",
)}
>
{isFirstPage && <Header />}
<div
className="p-custom flex-1 space-y-4"
style={{ backgroundColor: hexToRgb(primaryColor, 0.2) }}
>
{sidebar.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))}
</div>
</div>
<div className="main group col-span-2">
{isFirstPage && (
<div
className="p-custom space-y-4"
style={{ backgroundColor: hexToRgb(primaryColor, 0.2) }}
>
<Summary />
</div>
)}
<div className="p-custom space-y-4">
{main.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))}
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,527 @@
import {
Award,
Certification,
CustomSection,
CustomSectionGroup,
Education,
Experience,
Interest,
Language,
Profile,
Project,
Publication,
Reference,
SectionKey,
SectionWithItem,
Skill,
URL,
Volunteer,
} from "@reactive-resume/schema";
import { cn, hexToRgb, isEmptyString, isUrl, linearTransform } from "@reactive-resume/utils";
import get from "lodash.get";
import { Fragment } from "react";
import { Picture } from "../components/picture";
import { useArtboardStore } from "../store/artboard";
import { TemplateProps } from "../types/template";
const Header = () => {
const basics = useArtboardStore((state) => state.resume.basics);
return (
<div className="flex flex-col items-center space-y-4 text-center">
<Picture />
<div className="space-y-4">
<div>
<div className="text-2xl font-bold">{basics.name}</div>
<div className="text-base">{basics.headline}</div>
</div>
<div className="flex flex-col items-start gap-y-1.5 rounded border border-primary px-3 py-4 text-left text-sm">
{basics.location && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-map-pin text-primary" />
<div>{basics.location}</div>
</div>
)}
{basics.phone && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-phone text-primary" />
<a href={`tel:${basics.phone}`} target="_blank" rel="noreferrer">
{basics.phone}
</a>
</div>
)}
{basics.email && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-at text-primary" />
<a href={`mailto:${basics.email}`} target="_blank" rel="noreferrer">
{basics.email}
</a>
</div>
)}
<Link url={basics.url} />
{basics.customFields.map((item) => (
<div key={item.id} className="flex items-center gap-x-1.5">
<i className={cn(`ph ph-bold ph-${item.icon} text-primary`)} />
<span>{[item.name, item.value].filter(Boolean).join(": ")}</span>
</div>
))}
</div>
</div>
</div>
);
};
const Summary = () => {
const section = useArtboardStore((state) => state.resume.sections.summary);
if (!section.visible || isEmptyString(section.content)) return null;
return (
<section id={section.id}>
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
<div
className="wysiwyg"
style={{ columns: section.columns }}
dangerouslySetInnerHTML={{ __html: section.content }}
/>
</section>
);
};
type RatingProps = { level: number };
const Rating = ({ level }: RatingProps) => {
const primaryColor = useArtboardStore((state) => state.resume.metadata.theme.primary);
return (
<div className="relative">
<div
className="h-2.5 w-full rounded-sm"
style={{ backgroundColor: hexToRgb(primaryColor, 0.4) }}
/>
<div
className="absolute inset-y-0 left-0 h-2.5 w-full rounded-sm bg-primary"
style={{ width: `${linearTransform(level, 0, 5, 0, 100)}%` }}
/>
</div>
);
};
type LinkProps = {
url: URL;
icon?: React.ReactNode;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-primary" />}
<a
href={url.href}
target="_blank"
rel="noreferrer noopener nofollow"
className={cn("inline-block", className)}
>
{label || url.label || url.href}
</a>
</div>
);
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
className?: string;
urlKey?: keyof T;
levelKey?: keyof T;
summaryKey?: keyof T;
keywordsKey?: keyof T;
};
const Section = <T,>({
section,
children,
className,
urlKey,
levelKey,
summaryKey,
keywordsKey,
}: SectionProps<T>) => {
if (!section.visible || !section.items.length) return null;
return (
<section id={section.id} className="grid">
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold group-[.sidebar]:text-primary">
{section.name}
</h4>
<div
className="grid gap-x-6 gap-y-3"
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
>
{section.items
.filter((item) => item.visible)
.map((item) => {
const url = (urlKey && get(item, urlKey)) as URL | undefined;
const level = (levelKey && get(item, levelKey, 0)) as number | undefined;
const summary = (summaryKey && get(item, summaryKey, "")) as string | undefined;
const keywords = (keywordsKey && get(item, keywordsKey, [])) as string[] | undefined;
return (
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
)}
{level !== undefined && level > 0 && <Rating level={level} />}
{keywords !== undefined && keywords.length > 0 && (
<p className="text-sm">{keywords.join(", ")}</p>
)}
</div>
);
})}
</div>
</section>
);
};
const Experience = () => {
const section = useArtboardStore((state) => state.resume.sections.experience);
return (
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<div>{item.position}</div>
</div>
<div className="shrink-0 text-right">
<div className="font-bold">{item.date}</div>
<div>{item.location}</div>
</div>
</div>
)}
</Section>
);
};
const Education = () => {
const section = useArtboardStore((state) => state.resume.sections.education);
return (
<Section<Education> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
<div className="shrink-0 text-right">
<div className="font-bold">{item.date}</div>
<div>{item.studyType}</div>
</div>
</div>
)}
</Section>
);
};
const Profiles = () => {
const section = useArtboardStore((state) => state.resume.sections.profiles);
const fontSize = useArtboardStore((state) => state.resume.metadata.typography.font.size);
return (
<Section<Profile> section={section}>
{(item) => (
<div>
{isUrl(item.url.href) ? (
<Link
url={item.url}
label={item.username}
icon={
<img
className="ph"
width={fontSize}
height={fontSize}
alt={item.network}
src={`https://cdn.simpleicons.org/${item.icon}`}
/>
}
/>
) : (
<p>{item.username}</p>
)}
<p className="text-sm">{item.network}</p>
</div>
)}
</Section>
);
};
const Awards = () => {
const section = useArtboardStore((state) => state.resume.sections.awards);
return (
<Section<Award> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
</div>
<div className="shrink-0 text-right">
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const Certifications = () => {
const section = useArtboardStore((state) => state.resume.sections.certifications);
return (
<Section<Certification> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
</div>
<div className="shrink-0 text-right">
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const Skills = () => {
const section = useArtboardStore((state) => state.resume.sections.skills);
return (
<Section<Skill> section={section} levelKey="level" keywordsKey="keywords">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Interests = () => {
const section = useArtboardStore((state) => state.resume.sections.interests);
return (
<Section<Interest> section={section} keywordsKey="keywords" className="space-y-0.5">
{(item) => <div className="font-bold">{item.name}</div>}
</Section>
);
};
const Publications = () => {
const section = useArtboardStore((state) => state.resume.sections.publications);
return (
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.publisher}</div>
</div>
<div className="shrink-0 text-right">
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const Volunteer = () => {
const section = useArtboardStore((state) => state.resume.sections.volunteer);
return (
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<div>{item.position}</div>
</div>
<div className="shrink-0 text-right">
<div className="font-bold">{item.date}</div>
<div>{item.location}</div>
</div>
</div>
)}
</Section>
);
};
const Languages = () => {
const section = useArtboardStore((state) => state.resume.sections.languages);
return (
<Section<Language> section={section} levelKey="level">
{(item) => (
<div className="space-y-0.5">
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Projects = () => {
const section = useArtboardStore((state) => state.resume.sections.projects);
return (
<Section<Project> section={section} urlKey="url" summaryKey="summary" keywordsKey="keywords">
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
<div className="shrink-0 text-right">
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const References = () => {
const section = useArtboardStore((state) => state.resume.sections.references);
return (
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Custom = ({ id }: { id: string }) => {
const section = useArtboardStore((state) => state.resume.sections.custom[id]);
return (
<Section<CustomSection>
section={section}
urlKey="url"
summaryKey="summary"
keywordsKey="keywords"
>
{(item) => (
<div className="flex items-center justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
<div className="shrink-0 text-right">
<div className="font-bold">{item.date}</div>
<div>{item.location}</div>
</div>
</div>
)}
</Section>
);
};
const mapSectionToComponent = (section: SectionKey) => {
switch (section) {
case "profiles":
return <Profiles />;
case "summary":
return <Summary />;
case "experience":
return <Experience />;
case "education":
return <Education />;
case "awards":
return <Awards />;
case "certifications":
return <Certifications />;
case "skills":
return <Skills />;
case "interests":
return <Interests />;
case "publications":
return <Publications />;
case "volunteer":
return <Volunteer />;
case "languages":
return <Languages />;
case "projects":
return <Projects />;
case "references":
return <References />;
default:
if (section.startsWith("custom.")) return <Custom id={section.split(".")[1]} />;
return null;
}
};
export const Glalie = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns;
const primaryColor = useArtboardStore((state) => state.resume.metadata.theme.primary);
return (
<div className="grid min-h-[inherit] grid-cols-3">
<div
className="sidebar p-custom group space-y-4"
style={{ backgroundColor: hexToRgb(primaryColor, 0.2) }}
>
{isFirstPage && <Header />}
{sidebar.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))}
</div>
<div className="main p-custom group col-span-2 space-y-4">
{main.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))}
</div>
</div>
);
};

View File

@ -4,7 +4,10 @@ import { Azurill } from "./azurill";
import { Bronzor } from "./bronzor"; import { Bronzor } from "./bronzor";
import { Chikorita } from "./chikorita"; import { Chikorita } from "./chikorita";
import { Ditto } from "./ditto"; import { Ditto } from "./ditto";
import { Gengar } from "./gengar";
import { Glalie } from "./glalie";
import { Kakuna } from "./kakuna"; import { Kakuna } from "./kakuna";
import { Leafish } from "./leafish";
import { Nosepass } from "./nosepass"; import { Nosepass } from "./nosepass";
import { Onyx } from "./onyx"; import { Onyx } from "./onyx";
import { Pikachu } from "./pikachu"; import { Pikachu } from "./pikachu";
@ -12,24 +15,30 @@ import { Rhyhorn } from "./rhyhorn";
export const getTemplate = (template: Template) => { export const getTemplate = (template: Template) => {
switch (template) { switch (template) {
case "onyx":
return Onyx;
case "kakuna":
return Kakuna;
case "rhyhorn":
return Rhyhorn;
case "azurill": case "azurill":
return Azurill; return Azurill;
case "ditto":
return Ditto;
case "chikorita":
return Chikorita;
case "bronzor": case "bronzor":
return Bronzor; return Bronzor;
case "pikachu": case "chikorita":
return Pikachu; return Chikorita;
case "ditto":
return Ditto;
case "gengar":
return Gengar;
case "glalie":
return Glalie;
case "kakuna":
return Kakuna;
case "leafish":
return Leafish;
case "nosepass": case "nosepass":
return Nosepass; return Nosepass;
case "onyx":
return Onyx;
case "pikachu":
return Pikachu;
case "rhyhorn":
return Rhyhorn;
default: default:
return Onyx; return Onyx;
} }

View File

@ -452,7 +452,7 @@ export const Kakuna = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<div className="space-y-4"> <div className="p-custom space-y-4">
{isFirstPage && <Header />} {isFirstPage && <Header />}
<div className="space-y-4"> <div className="space-y-4">

View File

@ -0,0 +1,468 @@
import {
Award,
Certification,
CustomSection,
CustomSectionGroup,
Education,
Experience,
Interest,
Language,
Project,
Publication,
Reference,
SectionKey,
SectionWithItem,
Skill,
URL,
Volunteer,
} from "@reactive-resume/schema";
import { cn, hexToRgb, isEmptyString, isUrl } from "@reactive-resume/utils";
import get from "lodash.get";
import React, { Fragment } from "react";
import { Picture } from "../components/picture";
import { useArtboardStore } from "../store/artboard";
import { TemplateProps } from "../types/template";
const Header = () => {
const basics = useArtboardStore((state) => state.resume.basics);
const section = useArtboardStore((state) => state.resume.sections.summary);
const profiles = useArtboardStore((state) => state.resume.sections.profiles);
const primaryColor = useArtboardStore((state) => state.resume.metadata.theme.primary);
const fontSize = useArtboardStore((state) => state.resume.metadata.typography.font.size);
return (
<div>
<div
className="p-custom flex items-center space-x-8"
style={{ backgroundColor: hexToRgb(primaryColor, 0.2) }}
>
<div className="space-y-3">
<div>
<div className="text-3xl font-bold">{basics.name}</div>
<div className="text-base font-medium text-primary">{basics.headline}</div>
</div>
<div
className="wysiwyg"
style={{ columns: section.columns }}
dangerouslySetInnerHTML={{ __html: section.content }}
/>
</div>
<Picture />
</div>
<div className="p-custom space-y-3" style={{ backgroundColor: hexToRgb(primaryColor, 0.4) }}>
<div className="flex flex-wrap items-center gap-x-3 gap-y-0.5 text-sm">
{basics.location && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-map-pin text-primary" />
<div>{basics.location}</div>
</div>
)}
{basics.phone && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-phone text-primary" />
<a href={`tel:${basics.phone}`} target="_blank" rel="noreferrer">
{basics.phone}
</a>
</div>
)}
{basics.email && (
<div className="flex items-center gap-x-1.5">
<i className="ph ph-bold ph-at text-primary" />
<a href={`mailto:${basics.email}`} target="_blank" rel="noreferrer">
{basics.email}
</a>
</div>
)}
<Link url={basics.url} />
{basics.customFields.map((item) => (
<div key={item.id} className="flex items-center gap-x-1.5">
<i className={cn(`ph ph-bold ph-${item.icon}`, "text-primary")} />
<span>{[item.name, item.value].filter(Boolean).join(": ")}</span>
</div>
))}
</div>
{profiles.visible && profiles.items.length > 0 && (
<div className="flex items-center gap-x-3 gap-y-0.5">
{profiles.items
.filter((item) => item.visible)
.map((item) => (
<div key={item.id} className="flex items-center gap-x-2">
<Link
url={item.url}
label={item.username}
className="text-sm"
icon={
<img
className="ph"
width={fontSize}
height={fontSize}
alt={item.network}
src={`https://cdn.simpleicons.org/${item.icon}`}
/>
}
/>
</div>
))}
</div>
)}
</div>
</div>
);
};
type RatingProps = { level: number };
const Rating = ({ level }: RatingProps) => (
<div className="flex items-center gap-x-1.5">
{Array.from({ length: 5 }).map((_, index) => (
<div
key={index}
className={cn("h-3 w-6 border-2 border-primary", level > index && "bg-primary")}
/>
))}
</div>
);
type LinkProps = {
url: URL;
icon?: React.ReactNode;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
<a
href={url.href}
target="_blank"
rel="noreferrer noopener nofollow"
className={cn("inline-block", className)}
>
{label || url.label || url.href}
</a>
</div>
);
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
className?: string;
urlKey?: keyof T;
levelKey?: keyof T;
summaryKey?: keyof T;
keywordsKey?: keyof T;
};
const Section = <T,>({
section,
children,
className,
urlKey,
levelKey,
summaryKey,
keywordsKey,
}: SectionProps<T>) => {
if (!section.visible || !section.items.length) return null;
return (
<section id={section.id} className="grid">
<h4 className="mb-2 border-b border-primary text-left font-bold text-primary">
{section.name}
</h4>
<div
className="grid gap-x-6 gap-y-3"
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
>
{section.items
.filter((item) => item.visible)
.map((item) => {
const url = (urlKey && get(item, urlKey)) as URL | undefined;
const level = (levelKey && get(item, levelKey, 0)) as number | undefined;
const summary = (summaryKey && get(item, summaryKey, "")) as string | undefined;
const keywords = (keywordsKey && get(item, keywordsKey, [])) as string[] | undefined;
return (
<div key={item.id} className={cn("space-y-2", className)}>
<div>{children?.(item as T)}</div>
{summary !== undefined && !isEmptyString(summary) && (
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
)}
{level !== undefined && level > 0 && <Rating level={level} />}
{keywords !== undefined && keywords.length > 0 && (
<p className="text-sm">{keywords.join(", ")}</p>
)}
{url !== undefined && <Link url={url} />}
</div>
);
})}
</div>
</section>
);
};
const Experience = () => {
const section = useArtboardStore((state) => state.resume.sections.experience);
return (
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.company}</div>
<div>{item.position}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
</div>
)}
</Section>
);
};
const Education = () => {
const section = useArtboardStore((state) => state.resume.sections.education);
return (
<Section<Education> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.institution}</div>
<div>{item.area}</div>
<div>{item.score}</div>
<div>{item.studyType}</div>
<div className="font-bold">{item.date}</div>
</div>
)}
</Section>
);
};
const Awards = () => {
const section = useArtboardStore((state) => state.resume.sections.awards);
return (
<Section<Award> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<div className="font-bold">{item.date}</div>
</div>
)}
</Section>
);
};
const Certifications = () => {
const section = useArtboardStore((state) => state.resume.sections.certifications);
return (
<Section<Certification> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<div className="font-bold">{item.date}</div>
</div>
)}
</Section>
);
};
const Skills = () => {
const section = useArtboardStore((state) => state.resume.sections.skills);
return (
<Section<Skill> section={section} levelKey="level" keywordsKey="keywords">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Interests = () => {
const section = useArtboardStore((state) => state.resume.sections.interests);
return (
<Section<Interest> section={section} keywordsKey="keywords" className="space-y-0.5">
{(item) => <div className="font-bold">{item.name}</div>}
</Section>
);
};
const Publications = () => {
const section = useArtboardStore((state) => state.resume.sections.publications);
return (
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.publisher}</div>
<div className="font-bold">{item.date}</div>
</div>
)}
</Section>
);
};
const Volunteer = () => {
const section = useArtboardStore((state) => state.resume.sections.volunteer);
return (
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.organization}</div>
<div>{item.position}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
</div>
)}
</Section>
);
};
const Languages = () => {
const section = useArtboardStore((state) => state.resume.sections.languages);
return (
<Section<Language> section={section} levelKey="level">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Projects = () => {
const section = useArtboardStore((state) => state.resume.sections.projects);
return (
<Section<Project> section={section} urlKey="url" summaryKey="summary" keywordsKey="keywords">
{(item) => (
<div>
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const References = () => {
const section = useArtboardStore((state) => state.resume.sections.references);
return (
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
</div>
)}
</Section>
);
};
const Custom = ({ id }: { id: string }) => {
const section = useArtboardStore((state) => state.resume.sections.custom[id]);
return (
<Section<CustomSection>
section={section}
urlKey="url"
summaryKey="summary"
keywordsKey="keywords"
>
{(item) => (
<div>
<div>
<div className="font-bold">{item.name}</div>
<div>{item.description}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
</div>
</div>
)}
</Section>
);
};
const mapSectionToComponent = (section: SectionKey) => {
switch (section) {
case "experience":
return <Experience />;
case "education":
return <Education />;
case "awards":
return <Awards />;
case "certifications":
return <Certifications />;
case "skills":
return <Skills />;
case "interests":
return <Interests />;
case "publications":
return <Publications />;
case "volunteer":
return <Volunteer />;
case "languages":
return <Languages />;
case "projects":
return <Projects />;
case "references":
return <References />;
default:
if (section.startsWith("custom.")) return <Custom id={section.split(".")[1]} />;
return null;
}
};
export const Leafish = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns;
return (
<div>
{isFirstPage && <Header />}
<div className="p-custom grid grid-cols-2 items-start space-x-6">
<div className="grid gap-y-4">
{main.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))}
</div>
<div className="grid gap-y-4">
{sidebar.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))}
</div>
</div>
</div>
);
};

View File

@ -31,7 +31,6 @@ const Header = () => {
return ( return (
<div className="grid grid-cols-4 gap-x-6"> <div className="grid grid-cols-4 gap-x-6">
<div className="mt-1 space-y-2 text-right"> <div className="mt-1 space-y-2 text-right">
<p className="font-medium text-primary">Personal Information</p>
<Picture className="ml-auto" /> <Picture className="ml-auto" />
</div> </div>
@ -504,9 +503,9 @@ export const Nosepass = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<div className="space-y-4"> <div className="p-custom space-y-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<img alt="Europass Logo" className="h-[48px]" src="https://i.imgur.com/eRK005p.png" /> <img alt="Europass Logo" className="h-[42px]" src="https://i.imgur.com/eRK005p.png" />
<p className="font-medium text-primary">Curriculum Vitae</p> <p className="font-medium text-primary">Curriculum Vitae</p>

View File

@ -490,18 +490,16 @@ export const Onyx = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<div className="space-y-4"> <div className="p-custom space-y-4">
{isFirstPage && <Header />} {isFirstPage && <Header />}
<div className="space-y-4"> {main.map((section) => (
{main.map((section) => ( <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment> ))}
))}
{sidebar.map((section) => ( {sidebar.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment> <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))} ))}
</div>
</div> </div>
); );
}; };

View File

@ -524,23 +524,21 @@ export const Pikachu = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<div className="space-y-4"> <div className="p-custom grid grid-cols-3 space-x-6">
<div className="grid grid-cols-3 space-x-6"> <div className="sidebar group space-y-4">
<div className="sidebar group space-y-4"> {isFirstPage && <Picture className="w-full !max-w-none" />}
{isFirstPage && <Picture className="w-full !max-w-none" />}
{sidebar.map((section) => ( {sidebar.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment> <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))} ))}
</div> </div>
<div className="main group col-span-2 space-y-4"> <div className="main group col-span-2 space-y-4">
{isFirstPage && <Header />} {isFirstPage && <Header />}
{main.map((section) => ( {main.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment> <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))} ))}
</div>
</div> </div>
</div> </div>
); );

View File

@ -496,18 +496,16 @@ export const Rhyhorn = ({ columns, isFirstPage = false }: TemplateProps) => {
const [main, sidebar] = columns; const [main, sidebar] = columns;
return ( return (
<div className="space-y-4"> <div className="p-custom space-y-4">
{isFirstPage && <Header />} {isFirstPage && <Header />}
<div className="space-y-4"> {main.map((section) => (
{main.map((section) => ( <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment> ))}
))}
{sidebar.map((section) => ( {sidebar.map((section) => (
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment> <Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
))} ))}
</div>
</div> </div>
); );
}; };

View File

@ -22,6 +22,7 @@ module.exports = {
relaxed: "calc(var(--line-height) + 0.3)", relaxed: "calc(var(--line-height) + 0.3)",
loose: "calc(var(--line-height) + 0.5)", loose: "calc(var(--line-height) + 0.5)",
}, },
spacing: { custom: "var(--margin)" },
}, },
}, },
plugins: [require("@tailwindcss/typography")], plugins: [require("@tailwindcss/typography")],

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 KiB

View File

@ -0,0 +1,349 @@
{
"basics": {
"name": "John Doe",
"headline": "Creative and Innovative Web Developer",
"email": "john.doe@gmail.com",
"phone": "(555) 123-4567",
"location": "Pleasantville, CA 94588",
"url": {
"label": "",
"href": "https://johndoe.me/"
},
"customFields": [],
"picture": {
"url": "https://i.imgur.com/HgwyOuJ.jpg",
"size": 120,
"aspectRatio": 1,
"borderRadius": 0,
"effects": {
"hidden": false,
"border": false,
"grayscale": false
}
}
},
"sections": {
"summary": {
"name": "Summary",
"columns": 1,
"visible": true,
"id": "summary",
"content": "<p>Innovative Web Developer with 5 years of experience in building impactful and user-friendly websites and applications. Specializes in <strong>front-end technologies</strong> and passionate about modern web standards and cutting-edge development techniques. Proven track record of leading successful projects from concept to deployment.</p>"
},
"awards": {
"name": "Awards",
"columns": 1,
"visible": true,
"id": "awards",
"items": []
},
"certifications": {
"name": "Certifications",
"columns": 1,
"visible": true,
"id": "certifications",
"items": [
{
"id": "spdhh9rrqi1gvj0yqnbqunlo",
"visible": true,
"name": "Full-Stack Web Development",
"issuer": "CodeAcademy",
"date": "2020",
"summary": "",
"url": {
"label": "",
"href": ""
}
},
{
"id": "n838rddyqv47zexn6cxauwqp",
"visible": true,
"name": "AWS Certified Developer",
"issuer": "Amazon Web Services",
"date": "2019",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"education": {
"name": "Education",
"columns": 1,
"visible": true,
"id": "education",
"items": [
{
"id": "yo3p200zo45c6cdqc6a2vtt3",
"visible": true,
"institution": "University of California",
"studyType": "Bachelor's in Computer Science",
"area": "Berkeley, CA",
"score": "",
"date": "August 2012 to May 2016",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"experience": {
"name": "Experience",
"columns": 1,
"visible": true,
"id": "experience",
"items": [
{
"id": "lhw25d7gf32wgdfpsktf6e0x",
"visible": true,
"company": "Creative Solutions Inc.",
"position": "Senior Web Developer",
"location": "San Francisco, CA",
"date": "January 2019 to Present",
"summary": "<ul><li><p>Spearheaded the redesign of the main product website, resulting in a 40% increase in user engagement.</p></li><li><p>Developed and implemented a new responsive framework, improving cross-device compatibility.</p></li><li><p>Mentored a team of four junior developers, fostering a culture of technical excellence.</p></li></ul>",
"url": {
"label": "",
"href": "https://creativesolutions.inc/"
}
},
{
"id": "r6543lil53ntrxmvel53gbtm",
"visible": true,
"company": "TechAdvancers",
"position": "Web Developer",
"location": "San Jose, CA",
"date": "June 2016 to December 2018",
"summary": "<ul><li><p>Collaborated in a team of 10 to develop high-quality web applications using React.js and Node.js.</p></li><li><p>Managed the integration of third-party services such as Stripe for payments and Twilio for SMS services.</p></li><li><p>Optimized application performance, achieving a 30% reduction in load times.</p></li></ul>",
"url": {
"label": "",
"href": "https://techadvancers.com/"
}
}
]
},
"volunteer": {
"name": "Volunteering",
"columns": 1,
"visible": true,
"id": "volunteer",
"items": []
},
"interests": {
"name": "Interests",
"columns": 1,
"visible": true,
"id": "interests",
"items": []
},
"languages": {
"name": "Languages",
"columns": 1,
"visible": true,
"id": "languages",
"items": []
},
"profiles": {
"name": "Profiles",
"columns": 1,
"visible": true,
"id": "profiles",
"items": [
{
"id": "cnbk5f0aeqvhx69ebk7hktwd",
"visible": true,
"network": "LinkedIn",
"username": "johndoe",
"icon": "linkedin",
"url": {
"label": "",
"href": "https://linkedin.com/in/johndoe"
}
},
{
"id": "ukl0uecvzkgm27mlye0wazlb",
"visible": true,
"network": "GitHub",
"username": "johndoe",
"icon": "github",
"url": {
"label": "",
"href": "https://github.com/johndoe"
}
}
]
},
"projects": {
"name": "Projects",
"columns": 1,
"visible": true,
"id": "projects",
"items": [
{
"id": "yw843emozcth8s1ubi1ubvlf",
"visible": true,
"name": "E-Commerce Platform",
"description": "Project Lead",
"date": "",
"summary": "<p>Led the development of a full-stack e-commerce platform, improving sales conversion by 25%.</p>",
"keywords": [],
"url": {
"label": "",
"href": ""
}
},
{
"id": "ncxgdjjky54gh59iz2t1xi1v",
"visible": true,
"name": "Interactive Dashboard",
"description": "Frontend Developer",
"date": "",
"summary": "<p>Created an interactive analytics dashboard for a SaaS application, enhancing data visualization for clients.</p>",
"keywords": [],
"url": {
"label": "",
"href": ""
}
}
]
},
"publications": {
"name": "Publications",
"columns": 1,
"visible": true,
"id": "publications",
"items": []
},
"references": {
"name": "References",
"columns": 1,
"visible": false,
"id": "references",
"items": [
{
"id": "f2sv5z0cce6ztjl87yuk8fak",
"visible": true,
"name": "Available upon request",
"description": "",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"skills": {
"name": "Skills",
"columns": 1,
"visible": true,
"id": "skills",
"items": [
{
"id": "hn0keriukh6c0ojktl9gsgjm",
"visible": true,
"name": "Web Technologies",
"description": "Advanced",
"level": 0,
"keywords": [
"HTML5",
"JavaScript",
"PHP",
"Python"
]
},
{
"id": "r8c3y47vykausqrgmzwg5pur",
"visible": true,
"name": "Web Frameworks",
"description": "Intermediate",
"level": 0,
"keywords": [
"React.js",
"Angular",
"Vue.js",
"Laravel",
"Django"
]
},
{
"id": "b5l75aseexqv17quvqgh73fe",
"visible": true,
"name": "Tools",
"description": "Intermediate",
"level": 0,
"keywords": [
"Webpack",
"Git",
"Jenkins",
"Docker",
"JIRA"
]
}
]
},
"custom": {}
},
"metadata": {
"template": "gengar",
"layout": [
[
[
"summary",
"experience",
"education",
"projects",
"references"
],
[
"profiles",
"skills",
"certifications",
"interests",
"languages",
"awards",
"volunteer",
"publications"
]
],
[
[],
[]
]
],
"css": {
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
"visible": false
},
"page": {
"margin": 14,
"format": "a4",
"options": {
"breakLine": true,
"pageNumbers": true
}
},
"theme": {
"background": "#ffffff",
"text": "#000000",
"primary": "#0891b2"
},
"typography": {
"font": {
"family": "IBM Plex Serif",
"subset": "latin",
"variants": [
"regular"
],
"size": 13
},
"lineHeight": 1.6,
"hideIcons": false,
"underlineLinks": true
},
"notes": ""
}
}

View File

@ -0,0 +1,345 @@
{
"basics": {
"name": "John Doe",
"headline": "Creative and Innovative Web Developer",
"email": "john.doe@gmail.com",
"phone": "(555) 123-4567",
"location": "Pleasantville, CA 94588",
"url": {
"label": "",
"href": "https://johndoe.me/"
},
"customFields": [],
"picture": {
"url": "https://i.imgur.com/HgwyOuJ.jpg",
"size": 120,
"aspectRatio": 1,
"borderRadius": 0,
"effects": {
"hidden": false,
"border": false,
"grayscale": false
}
}
},
"sections": {
"summary": {
"name": "Summary",
"columns": 1,
"visible": true,
"id": "summary",
"content": "<p>Innovative Web Developer with 5 years of experience in building impactful and user-friendly websites and applications. Specializes in <strong>front-end technologies</strong> and passionate about modern web standards and cutting-edge development techniques. Proven track record of leading successful projects from concept to deployment.</p>"
},
"awards": {
"name": "Awards",
"columns": 1,
"visible": true,
"id": "awards",
"items": []
},
"certifications": {
"name": "Certifications",
"columns": 1,
"visible": true,
"id": "certifications",
"items": [
{
"id": "spdhh9rrqi1gvj0yqnbqunlo",
"visible": true,
"name": "Full-Stack Web Development",
"issuer": "CodeAcademy",
"date": "2020",
"summary": "",
"url": {
"label": "",
"href": ""
}
},
{
"id": "n838rddyqv47zexn6cxauwqp",
"visible": true,
"name": "AWS Certified Developer",
"issuer": "Amazon Web Services",
"date": "2019",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"education": {
"name": "Education",
"columns": 1,
"visible": true,
"id": "education",
"items": [
{
"id": "yo3p200zo45c6cdqc6a2vtt3",
"visible": true,
"institution": "University of California",
"studyType": "Bachelor's in Computer Science",
"area": "Berkeley, CA",
"score": "",
"date": "August 2012 to May 2016",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"experience": {
"name": "Experience",
"columns": 1,
"visible": true,
"id": "experience",
"items": [
{
"id": "lhw25d7gf32wgdfpsktf6e0x",
"visible": true,
"company": "Creative Solutions Inc.",
"position": "Senior Web Developer",
"location": "San Francisco, CA",
"date": "January 2019 to Present",
"summary": "<ul><li><p>Spearheaded the redesign of the main product website, resulting in a 40% increase in user engagement.</p></li><li><p>Developed and implemented a new responsive framework, improving cross-device compatibility.</p></li><li><p>Mentored a team of four junior developers, fostering a culture of technical excellence.</p></li></ul>",
"url": {
"label": "",
"href": "https://creativesolutions.inc/"
}
},
{
"id": "r6543lil53ntrxmvel53gbtm",
"visible": true,
"company": "TechAdvancers",
"position": "Web Developer",
"location": "San Jose, CA",
"date": "June 2016 to December 2018",
"summary": "<ul><li><p>Collaborated in a team of 10 to develop high-quality web applications using React.js and Node.js.</p></li><li><p>Managed the integration of third-party services such as Stripe for payments and Twilio for SMS services.</p></li><li><p>Optimized application performance, achieving a 30% reduction in load times.</p></li></ul>",
"url": {
"label": "",
"href": "https://techadvancers.com/"
}
}
]
},
"volunteer": {
"name": "Volunteering",
"columns": 1,
"visible": true,
"id": "volunteer",
"items": []
},
"interests": {
"name": "Interests",
"columns": 1,
"visible": true,
"id": "interests",
"items": []
},
"languages": {
"name": "Languages",
"columns": 1,
"visible": true,
"id": "languages",
"items": []
},
"profiles": {
"name": "Profiles",
"columns": 1,
"visible": true,
"id": "profiles",
"items": [
{
"id": "cnbk5f0aeqvhx69ebk7hktwd",
"visible": true,
"network": "LinkedIn",
"username": "johndoe",
"icon": "linkedin",
"url": {
"label": "",
"href": "https://linkedin.com/in/johndoe"
}
},
{
"id": "ukl0uecvzkgm27mlye0wazlb",
"visible": true,
"network": "GitHub",
"username": "johndoe",
"icon": "github",
"url": {
"label": "",
"href": "https://github.com/johndoe"
}
}
]
},
"projects": {
"name": "Projects",
"columns": 1,
"visible": true,
"id": "projects",
"items": [
{
"id": "yw843emozcth8s1ubi1ubvlf",
"visible": true,
"name": "E-Commerce Platform",
"description": "Project Lead",
"date": "",
"summary": "<p>Led the development of a full-stack e-commerce platform, improving sales conversion by 25%.</p>",
"keywords": [],
"url": {
"label": "",
"href": ""
}
},
{
"id": "ncxgdjjky54gh59iz2t1xi1v",
"visible": true,
"name": "Interactive Dashboard",
"description": "Frontend Developer",
"date": "",
"summary": "<p>Created an interactive analytics dashboard for a SaaS application, enhancing data visualization for clients.</p>",
"keywords": [],
"url": {
"label": "",
"href": ""
}
}
]
},
"publications": {
"name": "Publications",
"columns": 1,
"visible": true,
"id": "publications",
"items": []
},
"references": {
"name": "References",
"columns": 1,
"visible": false,
"id": "references",
"items": [
{
"id": "f2sv5z0cce6ztjl87yuk8fak",
"visible": true,
"name": "Available upon request",
"description": "",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"skills": {
"name": "Skills",
"columns": 1,
"visible": true,
"id": "skills",
"items": [
{
"id": "hn0keriukh6c0ojktl9gsgjm",
"visible": true,
"name": "Web Technologies",
"description": "Advanced",
"level": 0,
"keywords": [
"HTML5",
"JavaScript",
"PHP",
"Python"
]
},
{
"id": "r8c3y47vykausqrgmzwg5pur",
"visible": true,
"name": "Web Frameworks",
"description": "Intermediate",
"level": 0,
"keywords": [
"React.js",
"Angular",
"Vue.js",
"Laravel",
"Django"
]
},
{
"id": "b5l75aseexqv17quvqgh73fe",
"visible": true,
"name": "Tools",
"description": "Intermediate",
"level": 0,
"keywords": [
"Webpack",
"Git",
"Jenkins",
"Docker",
"JIRA"
]
}
]
},
"custom": {}
},
"metadata": {
"template": "glalie",
"layout": [
[
[
"summary",
"experience",
"education",
"projects",
"references"
],
[
"profiles",
"skills",
"certifications",
"interests",
"languages",
"awards",
"volunteer",
"publications"
]
]
],
"css": {
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
"visible": false
},
"page": {
"margin": 14,
"format": "a4",
"options": {
"breakLine": true,
"pageNumbers": true
}
},
"theme": {
"background": "#ffffff",
"text": "#000000",
"primary": "#0d9488"
},
"typography": {
"font": {
"family": "IBM Plex Serif",
"subset": "latin",
"variants": [
"regular"
],
"size": 13
},
"lineHeight": 1.6,
"hideIcons": false,
"underlineLinks": true
},
"notes": ""
}
}

View File

@ -0,0 +1,345 @@
{
"basics": {
"name": "John Doe",
"headline": "Creative and Innovative Web Developer",
"email": "john.doe@gmail.com",
"phone": "(555) 123-4567",
"location": "Pleasantville, CA 94588",
"url": {
"label": "",
"href": "https://johndoe.me/"
},
"customFields": [],
"picture": {
"url": "https://i.imgur.com/HgwyOuJ.jpg",
"size": 120,
"aspectRatio": 1,
"borderRadius": 0,
"effects": {
"hidden": false,
"border": false,
"grayscale": false
}
}
},
"sections": {
"summary": {
"name": "Summary",
"columns": 1,
"visible": true,
"id": "summary",
"content": "<p>Innovative Web Developer with 5 years of experience in building impactful and user-friendly websites and applications. Specializes in <strong>front-end technologies</strong> and passionate about modern web standards and cutting-edge development techniques. Proven track record of leading successful projects from concept to deployment.</p>"
},
"awards": {
"name": "Awards",
"columns": 1,
"visible": true,
"id": "awards",
"items": []
},
"certifications": {
"name": "Certifications",
"columns": 1,
"visible": true,
"id": "certifications",
"items": [
{
"id": "spdhh9rrqi1gvj0yqnbqunlo",
"visible": true,
"name": "Full-Stack Web Development",
"issuer": "CodeAcademy",
"date": "2020",
"summary": "",
"url": {
"label": "",
"href": ""
}
},
{
"id": "n838rddyqv47zexn6cxauwqp",
"visible": true,
"name": "AWS Certified Developer",
"issuer": "Amazon Web Services",
"date": "2019",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"education": {
"name": "Education",
"columns": 1,
"visible": true,
"id": "education",
"items": [
{
"id": "yo3p200zo45c6cdqc6a2vtt3",
"visible": true,
"institution": "University of California",
"studyType": "Bachelor's in Computer Science",
"area": "Berkeley, CA",
"score": "",
"date": "August 2012 to May 2016",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"experience": {
"name": "Experience",
"columns": 1,
"visible": true,
"id": "experience",
"items": [
{
"id": "lhw25d7gf32wgdfpsktf6e0x",
"visible": true,
"company": "Creative Solutions Inc.",
"position": "Senior Web Developer",
"location": "San Francisco, CA",
"date": "January 2019 to Present",
"summary": "<ul><li><p>Spearheaded the redesign of the main product website, resulting in a 40% increase in user engagement.</p></li><li><p>Developed and implemented a new responsive framework, improving cross-device compatibility.</p></li><li><p>Mentored a team of four junior developers, fostering a culture of technical excellence.</p></li></ul>",
"url": {
"label": "",
"href": "https://creativesolutions.inc/"
}
},
{
"id": "r6543lil53ntrxmvel53gbtm",
"visible": true,
"company": "TechAdvancers",
"position": "Web Developer",
"location": "San Jose, CA",
"date": "June 2016 to December 2018",
"summary": "<ul><li><p>Collaborated in a team of 10 to develop high-quality web applications using React.js and Node.js.</p></li><li><p>Managed the integration of third-party services such as Stripe for payments and Twilio for SMS services.</p></li><li><p>Optimized application performance, achieving a 30% reduction in load times.</p></li></ul>",
"url": {
"label": "",
"href": "https://techadvancers.com/"
}
}
]
},
"volunteer": {
"name": "Volunteering",
"columns": 1,
"visible": true,
"id": "volunteer",
"items": []
},
"interests": {
"name": "Interests",
"columns": 1,
"visible": true,
"id": "interests",
"items": []
},
"languages": {
"name": "Languages",
"columns": 1,
"visible": true,
"id": "languages",
"items": []
},
"profiles": {
"name": "Profiles",
"columns": 1,
"visible": true,
"id": "profiles",
"items": [
{
"id": "cnbk5f0aeqvhx69ebk7hktwd",
"visible": true,
"network": "LinkedIn",
"username": "johndoe",
"icon": "linkedin",
"url": {
"label": "",
"href": "https://linkedin.com/in/johndoe"
}
},
{
"id": "ukl0uecvzkgm27mlye0wazlb",
"visible": true,
"network": "GitHub",
"username": "johndoe",
"icon": "github",
"url": {
"label": "",
"href": "https://github.com/johndoe"
}
}
]
},
"projects": {
"name": "Projects",
"columns": 1,
"visible": true,
"id": "projects",
"items": [
{
"id": "yw843emozcth8s1ubi1ubvlf",
"visible": true,
"name": "E-Commerce Platform",
"description": "Project Lead",
"date": "",
"summary": "<p>Led the development of a full-stack e-commerce platform, improving sales conversion by 25%.</p>",
"keywords": [],
"url": {
"label": "",
"href": ""
}
},
{
"id": "ncxgdjjky54gh59iz2t1xi1v",
"visible": true,
"name": "Interactive Dashboard",
"description": "Frontend Developer",
"date": "",
"summary": "<p>Created an interactive analytics dashboard for a SaaS application, enhancing data visualization for clients.</p>",
"keywords": [],
"url": {
"label": "",
"href": ""
}
}
]
},
"publications": {
"name": "Publications",
"columns": 1,
"visible": true,
"id": "publications",
"items": []
},
"references": {
"name": "References",
"columns": 1,
"visible": false,
"id": "references",
"items": [
{
"id": "f2sv5z0cce6ztjl87yuk8fak",
"visible": true,
"name": "Available upon request",
"description": "",
"summary": "",
"url": {
"label": "",
"href": ""
}
}
]
},
"skills": {
"name": "Skills",
"columns": 1,
"visible": true,
"id": "skills",
"items": [
{
"id": "hn0keriukh6c0ojktl9gsgjm",
"visible": true,
"name": "Web Technologies",
"description": "Advanced",
"level": 0,
"keywords": [
"HTML5",
"JavaScript",
"PHP",
"Python"
]
},
{
"id": "r8c3y47vykausqrgmzwg5pur",
"visible": true,
"name": "Web Frameworks",
"description": "Intermediate",
"level": 0,
"keywords": [
"React.js",
"Angular",
"Vue.js",
"Laravel",
"Django"
]
},
{
"id": "b5l75aseexqv17quvqgh73fe",
"visible": true,
"name": "Tools",
"description": "Intermediate",
"level": 0,
"keywords": [
"Webpack",
"Git",
"Jenkins",
"Docker",
"JIRA"
]
}
]
},
"custom": {}
},
"metadata": {
"template": "leafish",
"layout": [
[
[
"summary",
"experience",
"education",
"references"
],
[
"profiles",
"skills",
"certifications",
"interests",
"languages",
"projects",
"awards",
"volunteer",
"publications"
]
]
],
"css": {
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
"visible": false
},
"page": {
"margin": 14,
"format": "a4",
"options": {
"breakLine": true,
"pageNumbers": true
}
},
"theme": {
"background": "#ffffff",
"text": "#000000",
"primary": "#7b4f1a"
},
"typography": {
"font": {
"family": "IBM Plex Serif",
"subset": "latin",
"variants": [
"regular"
],
"size": 13
},
"lineHeight": 1.6,
"hideIcons": false,
"underlineLinks": true
},
"notes": ""
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,20 +3,20 @@ import { ResumeData } from ".";
export const sampleResume: ResumeData = { export const sampleResume: ResumeData = {
basics: { basics: {
name: "John Doe", name: "John Doe",
headline: "Fintech Digital Assets Manager & Technology Innovator", headline: "Creative and Innovative Web Developer",
email: "hello@johndoe.com", email: "john.doe@gmail.com",
phone: "555-555-5555", phone: "(555) 123-4567",
location: "Sarasota, FL", location: "Pleasantville, CA 94588",
url: { url: {
label: "", label: "",
href: "https://johndoe.com", href: "https://johndoe.me/",
}, },
customFields: [], customFields: [],
picture: { picture: {
url: "https://i.imgur.com/HgwyOuJ.jpg", url: "https://i.imgur.com/HgwyOuJ.jpg",
size: 70, size: 120,
aspectRatio: 1, aspectRatio: 1,
borderRadius: 6, borderRadius: 0,
effects: { effects: {
hidden: false, hidden: false,
border: false, border: false,
@ -31,39 +31,14 @@ export const sampleResume: ResumeData = {
visible: true, visible: true,
id: "summary", id: "summary",
content: content:
"<p>With over <strong><em>two decades of experience</em></strong> at the forefront of digital asset financial technology (FinTech) integration across the entire spectrum of financial service company operations, I bring a strategic and analytical approach, underpinned by a deep understanding of cryptocurrency markets, digital asset financial management, compliance, and regulatory frameworks.</p><h4>Career Highlights</h4><ul><li><p>I have a proven track record in the <strong>design and deployment of bespoke trading platforms</strong> tailored to the unique needs of clients and independent financial entities, exemplified by driving a remarkable <strong>1,200% increase in Assets Under Management (AUM)</strong> for Mesirow Financial Holdings, Inc.</p></li><li><p>Recognized for exceptional leadership abilities, I have consistently <strong>cultivated high-performance trading and software engineering teams</strong>, achieving retention rates that surpass industry standards.</p></li><li><p>Instrumental in <strong>advancing Telcoins strategic legislative initiatives</strong>, my direct involvement has been pivotal in the enactment of supportive financial regulations.</p></li></ul>", "<p>Innovative Web Developer with 5 years of experience in building impactful and user-friendly websites and applications. Specializes in <strong>front-end technologies</strong> and passionate about modern web standards and cutting-edge development techniques. Proven track record of leading successful projects from concept to deployment.</p>",
}, },
awards: { awards: {
name: "Awards", name: "Awards",
columns: 1, columns: 1,
visible: true, visible: true,
id: "awards", id: "awards",
items: [ items: [],
{
id: "xk8q4bsp3vdlixehddifqsej",
visible: true,
title: "Current Landscape of Digital Assets",
awarder: "Midwest International Trade Association",
date: "Jul 2022",
summary: "",
url: {
label: "",
href: "",
},
},
{
id: "ky6dcdrlo6bgmagidab7hze8",
visible: true,
title: 'Presented "State of Telcoin"',
awarder: "Nebraska Bankers Association",
date: "Jul 2021",
summary: "",
url: {
label: "",
href: "",
},
},
],
}, },
certifications: { certifications: {
name: "Certifications", name: "Certifications",
@ -72,11 +47,11 @@ export const sampleResume: ResumeData = {
id: "certifications", id: "certifications",
items: [ items: [
{ {
id: "cx7xjbbp8q4n6uywmbjmnk06", id: "spdhh9rrqi1gvj0yqnbqunlo",
visible: true, visible: true,
name: "Series 7, 66 and 3 Licenses", name: "Full-Stack Web Development",
issuer: "Financial Industry Regulatory Authority (FINRA)", issuer: "CodeAcademy",
date: "Sep 2020", date: "2020",
summary: "", summary: "",
url: { url: {
label: "", label: "",
@ -84,11 +59,11 @@ export const sampleResume: ResumeData = {
}, },
}, },
{ {
id: "c60noj3hfmpui6qmisyicpza", id: "n838rddyqv47zexn6cxauwqp",
visible: true, visible: true,
name: "Microsoft Technical Associate (MTA)", name: "AWS Certified Developer",
issuer: "Microsoft Database Fundamentals", issuer: "Amazon Web Services",
date: "Aug 2019", date: "2019",
summary: "", summary: "",
url: { url: {
label: "", label: "",
@ -104,31 +79,17 @@ export const sampleResume: ResumeData = {
id: "education", id: "education",
items: [ items: [
{ {
id: "rxzt0h3k6hm2osgt99luj6ga", id: "yo3p200zo45c6cdqc6a2vtt3",
visible: true, visible: true,
institution: "Harvard Business School", institution: "University of California",
studyType: "Masters in Business Administration", studyType: "Bachelor's in Computer Science",
area: "Economics", area: "Berkeley, CA",
score: "Cambridge, MA", score: "",
date: "Aug 2008 - Jul 2012", date: "August 2012 to May 2016",
summary: "", summary: "",
url: { url: {
label: "", label: "",
href: "https://www.harvard.edu/", href: "",
},
},
{
id: "sw5c5016z5oth2nqk2wpiqrk",
visible: true,
institution: "Western Illinois University",
studyType: "Bachelor of Business Administration",
area: "Economics",
score: "Macomb, IL",
date: "Dec 2004 - Feb 2008",
summary: "",
url: {
label: "",
href: "https://www.wiu.edu/",
}, },
}, },
], ],
@ -140,17 +101,31 @@ export const sampleResume: ResumeData = {
id: "experience", id: "experience",
items: [ items: [
{ {
id: "w36cxnlowbgb4r7ruusjexb6", id: "lhw25d7gf32wgdfpsktf6e0x",
visible: true, visible: true,
company: "Telcoin", company: "Creative Solutions Inc.",
position: "Vice President, Banking Operations", position: "Senior Web Developer",
location: "Los Angeles, CA", location: "San Francisco, CA",
date: "Aug 2020 - Present", date: "January 2019 to Present",
summary: summary:
"<p>Key visionary in charting the company's DeFi strategy and direction, partnering across the executive team. Identify and seize opportunities to position Telcoin as an industry leader, grow business, extend the digital asset platform reach, and influence favorable legislation. Lead banking operations, including a cross-functional team and partnerships with mobile money platforms.</p><ul><li><p><strong>Paved the way for platform expansion by evaluating various vendors as on-ramps for the Telcoin application.</strong> Assessed companies with filters, such as customer cost, availability by country, licenses, banking connection, and delivery time. Presented to team members for follow-up with introductory calls.</p></li><li><p><strong>Played a key role in gaining authorization of digital asset depository entities in Nebraska</strong> through legislative and regulatory advocacy, leading to passage of 2021 Nebraska Financial Innovation Act (NFIA). Promoted, educated, and lobbied for bill, meeting with Nebraska politicians and other agencies in Washington, DC and with banker's associations and civic leaders. Reviewed the WY Special Purpose Depository Act of 2020.</p></li><li><p><strong>Partnered with CEO to pioneer a stablecoin to coincide with passage of NFIA. </strong>Researched fiat-backed and algorithmic stablecoins, attestation processes, national and international legislation, and market trends. Developed and presented stablecoin at Nebraska Bankers Association.</p></li><li><p><strong>Integrated ACH collection operations with core bank.</strong> Manage ACH processing operations and collections from the app and monitor for potential fraud.</p></li><li><p><strong>Prevented $1M+ in losses due to fraud. </strong>Identified and alerted team to attempt to deceive the KYC onboarding process.</p></li></ul>", "<ul><li><p>Spearheaded the redesign of the main product website, resulting in a 40% increase in user engagement.</p></li><li><p>Developed and implemented a new responsive framework, improving cross-device compatibility.</p></li><li><p>Mentored a team of four junior developers, fostering a culture of technical excellence.</p></li></ul>",
url: { url: {
label: "", label: "",
href: "https://telcoin.xyz/", href: "https://creativesolutions.inc/",
},
},
{
id: "r6543lil53ntrxmvel53gbtm",
visible: true,
company: "TechAdvancers",
position: "Web Developer",
location: "San Jose, CA",
date: "June 2016 to December 2018",
summary:
"<ul><li><p>Collaborated in a team of 10 to develop high-quality web applications using React.js and Node.js.</p></li><li><p>Managed the integration of third-party services such as Stripe for payments and Twilio for SMS services.</p></li><li><p>Optimized application performance, achieving a 30% reduction in load times.</p></li></ul>",
url: {
label: "",
href: "https://techadvancers.com/",
}, },
}, },
], ],
@ -160,83 +135,21 @@ export const sampleResume: ResumeData = {
columns: 1, columns: 1,
visible: true, visible: true,
id: "volunteer", id: "volunteer",
items: [ items: [],
{
id: "z9uwndyvz4vxndywohmsv5q2",
visible: true,
organization: "Rotary Club of Lakewood Ranch",
position: "Member",
location: "New York, NY",
date: "Dec 2018 - Present",
summary: "",
url: {
label: "",
href: "",
},
},
],
}, },
interests: { interests: {
name: "Interests", name: "Interests",
columns: 1, columns: 1,
visible: true, visible: true,
id: "interests", id: "interests",
items: [ items: [],
{
id: "yxhban7wuust7an86bwhoy68",
visible: true,
name: "Photography",
keywords: ["Polaroid", "Adobe Lightroom", "Canon DSLR"],
},
{
id: "vdirkh4frapk72mpfoxkddju",
visible: true,
name: "Video Games",
keywords: ["FIFA 23", "Call of Duty", "Slay the Spire"],
},
{
id: "ww6gvkhb6lacyi7q0ju13oez",
visible: true,
name: "Product Development",
keywords: ["HTML", "CSS", "JavaScript", "React"],
},
],
}, },
languages: { languages: {
name: "Languages", name: "Languages",
columns: 1, columns: 1,
visible: true, visible: true,
id: "languages", id: "languages",
items: [ items: [],
{
id: "je6s7k590yp9wtv65bbcyiua",
visible: true,
name: "English",
description: "Native Speaker",
level: 5,
},
{
id: "qdltq93lqi5g290t70brrpit",
visible: true,
name: "German",
description: "Advanced",
level: 5,
},
{
id: "txxfl568h5yg5xojwj5sk3ms",
visible: true,
name: "French",
description: "Advanced",
level: 5,
},
{
id: "zla2ezdkhemy68kg8bj86jk0",
visible: true,
name: "Spanish",
description: "Learning",
level: 2,
},
],
}, },
profiles: { profiles: {
name: "Profiles", name: "Profiles",
@ -245,7 +158,7 @@ export const sampleResume: ResumeData = {
id: "profiles", id: "profiles",
items: [ items: [
{ {
id: "hey1likga9nqv5x548aogybk", id: "cnbk5f0aeqvhx69ebk7hktwd",
visible: true, visible: true,
network: "LinkedIn", network: "LinkedIn",
username: "johndoe", username: "johndoe",
@ -256,18 +169,7 @@ export const sampleResume: ResumeData = {
}, },
}, },
{ {
id: "tkofnqpwaf56tjqtxq7x97z8", id: "ukl0uecvzkgm27mlye0wazlb",
visible: true,
network: "Twitter",
username: "johndoe",
icon: "x",
url: {
label: "",
href: "https://x.com/johndoe",
},
},
{
id: "xg5nljag80domeki26dblx1k",
visible: true, visible: true,
network: "GitHub", network: "GitHub",
username: "johndoe", username: "johndoe",
@ -277,17 +179,6 @@ export const sampleResume: ResumeData = {
href: "https://github.com/johndoe", href: "https://github.com/johndoe",
}, },
}, },
{
id: "u4y7yfqi607r6un05y3leap5",
visible: true,
network: "StackOverflow",
username: "johndoe",
icon: "stackoverflow",
url: {
label: "",
href: "https://stackoverflow.com/users/1248133",
},
},
], ],
}, },
projects: { projects: {
@ -295,33 +186,30 @@ export const sampleResume: ResumeData = {
columns: 1, columns: 1,
visible: true, visible: true,
id: "projects", id: "projects",
items: [],
},
publications: {
name: "Publications",
columns: 1,
visible: true,
id: "publications",
items: [ items: [
{ {
id: "nx1qpj7u5duywvcror662woy", id: "yw843emozcth8s1ubi1ubvlf",
visible: true, visible: true,
name: "Current Landscape of Digital Assets and Cryptocurrency", name: "E-Commerce Platform",
publisher: "Midwest International Trade Association", description: "Project Lead",
date: "Jul 2022", date: "",
summary: "", summary:
"<p>Led the development of a full-stack e-commerce platform, improving sales conversion by 25%.</p>",
keywords: [],
url: { url: {
label: "", label: "",
href: "", href: "",
}, },
}, },
{ {
id: "u385pdal3exspenxswj02gd2", id: "ncxgdjjky54gh59iz2t1xi1v",
visible: true, visible: true,
name: "State of Telcoin", name: "Interactive Dashboard",
publisher: "Nebraska Bankers Association", description: "Frontend Developer",
date: "Apr 2019", date: "",
summary: "", summary:
"<p>Created an interactive analytics dashboard for a SaaS application, enhancing data visualization for clients.</p>",
keywords: [],
url: { url: {
label: "", label: "",
href: "", href: "",
@ -329,32 +217,28 @@ export const sampleResume: ResumeData = {
}, },
], ],
}, },
publications: {
name: "Publications",
columns: 1,
visible: true,
id: "publications",
items: [],
},
references: { references: {
name: "References", name: "References",
columns: 1, columns: 1,
visible: true, visible: false,
id: "references", id: "references",
items: [ items: [
{ {
id: "znn813gd72aptpgz8dc2g0g8", id: "f2sv5z0cce6ztjl87yuk8fak",
visible: true, visible: true,
name: "Cosmo Kramer", name: "Available upon request",
description: "Manager at EY", description: "",
summary: "<p><em>(credentials available on request)</em></p>", summary: "",
url: { url: {
label: "", label: "",
href: "https://linkedin.com/in/cosmo.kramer", href: "",
},
},
{
id: "erqekvctbkc4dxvqxm6mfvgj",
visible: true,
name: "Elangovan Musk",
description: "CEO at Tulsa",
summary: "<p><em>(credentials available on request)</em></p>",
url: {
label: "",
href: "https://linkedin.com/in/elan.musk",
}, },
}, },
], ],
@ -366,101 +250,48 @@ export const sampleResume: ResumeData = {
id: "skills", id: "skills",
items: [ items: [
{ {
id: "lo7bvn3dv7s13effzyy4ov2l", id: "hn0keriukh6c0ojktl9gsgjm",
visible: true, visible: true,
name: "Financial Markets", name: "Web Technologies",
description: "Expert",
level: 5,
keywords: [
"Foreign Exchange (FX) Markets",
"Futures",
"Options",
"Stablecoins",
"Decentralized Finance (DeFi)",
"Crypto Exchanges",
"NFTs",
],
},
{
id: "hr8ncy5ewyq2wug238b1licq",
visible: true,
name: "Digital Asset Management",
description: "Expert",
level: 5,
keywords: [
"Digital Asset Strategy",
"Digital Asset Technology",
"Team Leadership",
"Technical Platform Design",
],
},
{
id: "s8x0oe9bfslhb4cp3wucpfus",
visible: true,
name: "Technical Expertise",
description: "Expert",
level: 5,
keywords: [
"Architecture and Development",
"Continuous Product Development",
"Quantitative Models",
"Data Standardization",
],
},
{
id: "du8xdxue8hg3sfw13fixt1if",
visible: true,
name: "Legal & Compliance",
description: "Advanced", description: "Advanced",
level: 4, level: 0,
keywords: [ keywords: ["HTML5", "JavaScript", "PHP", "Python"],
"KYC", },
"Problem Solving Communication", {
"Presentations", id: "r8c3y47vykausqrgmzwg5pur",
"Training", visible: true,
"Market Data", name: "Web Frameworks",
"Bloomberg", description: "Intermediate",
"Reuters", level: 0,
], keywords: ["React.js", "Angular", "Vue.js", "Laravel", "Django"],
},
{
id: "b5l75aseexqv17quvqgh73fe",
visible: true,
name: "Tools",
description: "Intermediate",
level: 0,
keywords: ["Webpack", "Git", "Jenkins", "Docker", "JIRA"],
}, },
], ],
}, },
custom: { custom: {},
juryi0w9w9jabsgorks0bixq: {
name: "Experience",
columns: 1,
visible: true,
id: "juryi0w9w9jabsgorks0bixq",
items: [
{
id: "j69rrkkdf94jqqfe5v726d1n",
visible: true,
name: "QuantRes",
description: "Execution Team Manager",
date: "Jan 2018 - Jul 2020",
location: "Nassau, Bahamas",
summary:
"<p>Proprietary quantitative trading firm and privately-held quantitative futures fund with $1B in AUM and a focus on futures and OTC FX opportunities. 25+ year track record of trading global derivatives markets.</p><h4>Execution Team Manager</h4><p>Led international OTC FX and futures trading desk, software development, compliance, and market data for fund averaging $5B in monthly FX transactions and generating 500K+ futures contracts per month.</p><p>Steered a 12-member team of 4 traders and 8 software engineers. Oversaw co-located trade execution and managed direct market access in 4 data centers globally for exchanges not co-located. Built partnerships with trading technology providers. Point of contact with banks, brokerages, FXSpotStream, and 9 other liquidity providers. Led market data feed agreements with exchanges. Maintained CME and Eurex exchange memberships. Headed fulfillment of regulatory compliance and due diligence requests.</p><ul><li><p><strong>Decreased HFT FX trading costs</strong> by integrating streaming of 1 million NDF prices into co-located servers, coordinating with engineers of the FX pricing engine.</p></li></ul><ul><li><p><strong>Streamlined market data contracts and reduced costs by improving data quality and availability.</strong> Consolidated technology providers (Bloomberg and Reuters) and enhanced data collection for time periods needed, data types, and breadth of data.</p></li></ul><ul><li><p><strong>Scaled capabilities by recruiting to enlarge the team</strong>, hiring 3 traders, 2 software engineers, and a consultant with 85% retention for 2+ years. Sourced top talent by posting jobs, leveraging network of contacts, and outsourcing with recruiters.</p></li></ul><ul><li><p><strong>Reduced bank, vendor, and futures broker fees, expanded access to services</strong>, and maintained open lines of communication by strengthening existing relationships. Contacted sales representatives to facilitate introductions.</p></li></ul>",
keywords: [],
url: {
label: "",
href: "https://quantres.xyz/",
},
},
],
},
},
}, },
metadata: { metadata: {
template: "pikachu", template: "glalie",
layout: [ layout: [
[ [
["summary", "experience"], ["summary", "experience", "education", "projects", "references"],
["profiles", "skills", "awards", "certifications"], [
], "profiles",
[ "skills",
["custom.juryi0w9w9jabsgorks0bixq", "education", "projects", "references"], "certifications",
["interests", "publications", "volunteer", "languages"], "interests",
"languages",
"awards",
"volunteer",
"publications",
],
], ],
], ],
css: { css: {
@ -468,7 +299,7 @@ export const sampleResume: ResumeData = {
visible: false, visible: false,
}, },
page: { page: {
margin: 16, margin: 14,
format: "a4", format: "a4",
options: { options: {
breakLine: true, breakLine: true,
@ -478,20 +309,19 @@ export const sampleResume: ResumeData = {
theme: { theme: {
background: "#ffffff", background: "#ffffff",
text: "#000000", text: "#000000",
primary: "#398dc9", primary: "#ca8a04",
}, },
typography: { typography: {
font: { font: {
family: "IBM Plex Sans Condensed", family: "Merriweather",
subset: "latin", subset: "latin",
variants: ["regular", "500"], variants: ["regular"],
size: 13, size: 13,
}, },
lineHeight: 1.5, lineHeight: 1.75,
hideIcons: false, hideIcons: false,
underlineLinks: true, underlineLinks: true,
}, },
notes: notes: "",
"<p>I sent this resume to Deloitte back in July 2022. I am yet to hear back from them.</p>",
}, },
}; };

View File

@ -1,4 +1,5 @@
export * from "./namespaces/array"; export * from "./namespaces/array";
export * from "./namespaces/color";
export * from "./namespaces/csv"; export * from "./namespaces/csv";
export * from "./namespaces/date"; export * from "./namespaces/date";
export * from "./namespaces/error"; export * from "./namespaces/error";

View File

@ -0,0 +1,11 @@
export const hexToRgb = (hex: string, alpha: number = 0) => {
const r = parseInt(hex.slice(1, 3), 16),
g = parseInt(hex.slice(3, 5), 16),
b = parseInt(hex.slice(5, 7), 16);
if (alpha) {
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
} else {
return `rgb(${r}, ${g}, ${b})`;
}
};

View File

@ -8,5 +8,3 @@ export const linearTransform = (
if (inMax === inMin) return value === inMax ? outMin : NaN; if (inMax === inMin) return value === inMax ? outMin : NaN;
return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin; return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
}; };
// Handle this case: returns output minimum if input maximum equals input minimum

View File

@ -3,7 +3,10 @@ export const templatesList = [
"bronzor", "bronzor",
"chikorita", "chikorita",
"ditto", "ditto",
"gengar",
"glalie",
"kakuna", "kakuna",
"leafish",
"nosepass", "nosepass",
"onyx", "onyx",
"pikachu", "pikachu",