design nosepass template, add tests, add template previews
@@ -8,6 +8,7 @@ type PictureProps = {
|
||||
|
||||
export const Picture = ({ className }: PictureProps) => {
|
||||
const picture = useArtboardStore((state) => state.resume.basics.picture);
|
||||
const fontSize = useArtboardStore((state) => state.resume.metadata.typography.font.size);
|
||||
|
||||
if (!isUrl(picture.url) || picture.effects.hidden) return null;
|
||||
|
||||
@@ -15,11 +16,17 @@ export const Picture = ({ className }: PictureProps) => {
|
||||
<img
|
||||
src={picture.url}
|
||||
alt="Profile"
|
||||
className={cn("relative z-20 object-cover", className)}
|
||||
className={cn(
|
||||
"relative z-20 object-cover",
|
||||
picture.effects.border && "border-primary",
|
||||
picture.effects.grayscale && "grayscale",
|
||||
className,
|
||||
)}
|
||||
style={{
|
||||
maxWidth: `${picture.size}px`,
|
||||
aspectRatio: `${picture.aspectRatio}`,
|
||||
borderRadius: `${picture.borderRadius}px`,
|
||||
borderWidth: `${picture.effects.border ? fontSize / 3 : 0}px`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SectionKey } from "@reactive-resume/schema";
|
||||
import { pageSizeMap, TemplateKey } from "@reactive-resume/utils";
|
||||
import { pageSizeMap, Template } from "@reactive-resume/utils";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { ReactZoomPanPinchRef, TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
|
||||
@@ -12,7 +12,7 @@ export const BuilderLayout = () => {
|
||||
const transformRef = useRef<ReactZoomPanPinchRef>(null);
|
||||
const format = useArtboardStore((state) => state.resume.metadata.page.format);
|
||||
const layout = useArtboardStore((state) => state.resume.metadata.layout);
|
||||
const template = useArtboardStore((state) => state.resume.metadata.template as TemplateKey);
|
||||
const template = useArtboardStore((state) => state.resume.metadata.template as Template);
|
||||
|
||||
const Template = useMemo(() => getTemplate(template), [template]);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SectionKey } from "@reactive-resume/schema";
|
||||
import { TemplateKey } from "@reactive-resume/utils";
|
||||
import { Template } from "@reactive-resume/utils";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { Page } from "../components/page";
|
||||
@@ -8,7 +8,7 @@ import { getTemplate } from "../templates";
|
||||
|
||||
export const PreviewLayout = () => {
|
||||
const layout = useArtboardStore((state) => state.resume.metadata.layout);
|
||||
const template = useArtboardStore((state) => state.resume.metadata.template as TemplateKey);
|
||||
const template = useArtboardStore((state) => state.resume.metadata.template as Template);
|
||||
|
||||
const Template = useMemo(() => getTemplate(template), [template]);
|
||||
|
||||
|
||||
@@ -19,5 +19,5 @@
|
||||
}
|
||||
|
||||
.wysiwyg {
|
||||
@apply prose max-w-none text-current prose-headings:mt-0 prose-headings:mb-2 prose-p:mt-0 prose-p:mb-2 prose-ul:mt-0 prose-ul:mb-2 prose-li:mt-0 prose-li:mb-2 prose-ol:mt-0 prose-ol:mb-2 prose-img:mt-0 prose-img:mb-2 prose-hr:mt-0 prose-hr:mb-2 prose-p:leading-normal;
|
||||
@apply prose max-w-none text-current prose-headings:mt-0 prose-headings:mb-2 prose-p:mt-0 prose-p:mb-2 prose-ul:mt-0 prose-ul:mb-2 prose-li:mt-0 prose-li:mb-2 prose-ol:mt-0 prose-ol:mb-2 prose-img:mt-0 prose-img:mb-2 prose-hr:mt-0 prose-hr:mb-2 prose-p:leading-normal prose-li:leading-normal;
|
||||
}
|
||||
|
||||
@@ -79,11 +79,11 @@ const Summary = () => {
|
||||
|
||||
return (
|
||||
<section id={section.id}>
|
||||
<div className="mb-2 hidden font-bold uppercase text-primary group-[.main]:block">
|
||||
<div className="mb-2 hidden font-bold text-primary group-[.main]:block">
|
||||
<h4>{section.name}</h4>
|
||||
</div>
|
||||
|
||||
<div className="mb-2 hidden items-center gap-x-2 text-center font-bold uppercase text-primary group-[.sidebar]:flex">
|
||||
<div className="mb-2 hidden items-center gap-x-2 text-center font-bold text-primary group-[.sidebar]:flex">
|
||||
<div className="h-1.5 w-1.5 rounded-full border border-primary" />
|
||||
<h4>{section.name}</h4>
|
||||
<div className="h-1.5 w-1.5 rounded-full border border-primary" />
|
||||
@@ -162,18 +162,18 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<section id={section.id} className="grid">
|
||||
<div className="mb-2 hidden font-bold uppercase text-primary group-[.main]:block">
|
||||
<div className="mb-2 hidden font-bold text-primary group-[.main]:block">
|
||||
<h4>{section.name}</h4>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto mb-2 hidden items-center gap-x-2 text-center font-bold uppercase text-primary group-[.sidebar]:flex">
|
||||
<div className="mx-auto mb-2 hidden items-center gap-x-2 text-center font-bold text-primary group-[.sidebar]:flex">
|
||||
<div className="h-1.5 w-1.5 rounded-full border border-primary" />
|
||||
<h4>{section.name}</h4>
|
||||
<div className="h-1.5 w-1.5 rounded-full border border-primary" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="grid gap-3 group-[.sidebar]:mx-auto group-[.sidebar]:text-center"
|
||||
className="grid gap-x-6 gap-y-3 group-[.sidebar]:mx-auto group-[.sidebar]:text-center"
|
||||
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
|
||||
>
|
||||
{section.items
|
||||
@@ -193,19 +193,19 @@ const Section = <T,>({
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="leading-snug">{children?.(item as T)}</div>
|
||||
<div>{children?.(item as T)}</div>
|
||||
|
||||
{summary && !isEmptyString(summary) && (
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{level && level > 0 && <Rating level={level} />}
|
||||
{level !== undefined && level > 0 && <Rating level={level} />}
|
||||
|
||||
{keywords && keywords.length > 0 && (
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
|
||||
{url && <Link url={url} />}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
|
||||
<div className="absolute left-[-4.5px] top-px hidden h-[8px] w-[8px] rounded-full bg-primary group-[.main]:block" />
|
||||
</div>
|
||||
@@ -223,7 +223,7 @@ const Profiles = () => {
|
||||
return (
|
||||
<Section<Profile> section={section}>
|
||||
{(item) => (
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{isUrl(item.url.href) ? (
|
||||
<Link
|
||||
url={item.url}
|
||||
@@ -258,7 +258,7 @@ const Experience = () => {
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -276,7 +276,7 @@ const Education = () => {
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
<div>{item.studyType}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -292,7 +292,7 @@ const Awards = () => {
|
||||
<div>
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -308,7 +308,7 @@ const Certifications = () => {
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -349,7 +349,7 @@ const Publications = () => {
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.publisher}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -366,7 +366,7 @@ const Volunteer = () => {
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -377,11 +377,11 @@ const Languages = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.languages);
|
||||
|
||||
return (
|
||||
<Section<Language> section={section} levelKey="fluencyLevel">
|
||||
<Section<Language> section={section} levelKey="level">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.fluency}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -399,7 +399,7 @@ const Projects = () => {
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.description}</div>
|
||||
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -438,7 +438,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.description}</div>
|
||||
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
<div>{item.location}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -158,7 +158,7 @@ const Section = <T,>({
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="col-span-4 grid gap-3"
|
||||
className="col-span-4 grid gap-x-6 gap-y-3"
|
||||
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
|
||||
>
|
||||
{section.items
|
||||
@@ -171,18 +171,18 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url && <Link url={url} />}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary && !isEmptyString(summary) && (
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{level && level > 0 && <Rating level={level} />}
|
||||
{level !== undefined && level > 0 && <Rating level={level} />}
|
||||
|
||||
{keywords && keywords.length > 0 && (
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -200,7 +200,7 @@ const Profiles = () => {
|
||||
return (
|
||||
<Section<Profile> section={section}>
|
||||
{(item) => (
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{isUrl(item.url.href) ? (
|
||||
<Link
|
||||
url={item.url}
|
||||
@@ -384,11 +384,11 @@ const Languages = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.languages);
|
||||
|
||||
return (
|
||||
<Section<Language> section={section} levelKey="fluencyLevel">
|
||||
<Section<Language> section={section} levelKey="level">
|
||||
{(item) => (
|
||||
<div className="space-y-0.5">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.fluency}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Experience,
|
||||
Interest,
|
||||
Language,
|
||||
Profile,
|
||||
Project,
|
||||
Publication,
|
||||
Reference,
|
||||
@@ -26,8 +27,6 @@ import { TemplateProps } from "../types/template";
|
||||
|
||||
const Header = () => {
|
||||
const basics = useArtboardStore((state) => state.resume.basics);
|
||||
const profiles = useArtboardStore((state) => state.resume.sections.profiles);
|
||||
const fontSize = useArtboardStore((state) => state.resume.metadata.typography.font.size);
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-3">
|
||||
@@ -71,31 +70,6 @@ const Header = () => {
|
||||
</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>
|
||||
</div>
|
||||
@@ -109,7 +83,7 @@ const Summary = () => {
|
||||
|
||||
return (
|
||||
<section id={section.id}>
|
||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold uppercase">{section.name}</h4>
|
||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="wysiwyg"
|
||||
@@ -184,10 +158,10 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<section id={section.id} className="grid">
|
||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold uppercase">{section.name}</h4>
|
||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="grid gap-3"
|
||||
className="grid gap-x-6 gap-y-3"
|
||||
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
|
||||
>
|
||||
{section.items
|
||||
@@ -200,18 +174,18 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url && <Link url={url} />}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary && !isEmptyString(summary) && (
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{level && level > 0 && <Rating level={level} />}
|
||||
{level !== undefined && level > 0 && <Rating level={level} />}
|
||||
|
||||
{keywords && keywords.length > 0 && (
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -267,6 +241,38 @@ const Education = () => {
|
||||
);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
@@ -381,11 +387,11 @@ const Languages = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.languages);
|
||||
|
||||
return (
|
||||
<Section<Language> section={section} levelKey="fluencyLevel">
|
||||
<Section<Language> section={section} levelKey="level">
|
||||
{(item) => (
|
||||
<div className="space-y-0.5">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.fluency}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -457,6 +463,8 @@ const Custom = ({ id }: { id: string }) => {
|
||||
|
||||
const mapSectionToComponent = (section: SectionKey) => {
|
||||
switch (section) {
|
||||
case "profiles":
|
||||
return <Profiles />;
|
||||
case "summary":
|
||||
return <Summary />;
|
||||
case "experience":
|
||||
|
||||
@@ -101,7 +101,7 @@ const Summary = () => {
|
||||
|
||||
return (
|
||||
<section id={section.id}>
|
||||
<h4 className="mb-2 text-base font-bold uppercase">{section.name}</h4>
|
||||
<h4 className="mb-2 text-base font-bold">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="wysiwyg"
|
||||
@@ -173,10 +173,10 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<section id={section.id} className="grid">
|
||||
<h4 className="mb-2 text-base font-bold uppercase">{section.name}</h4>
|
||||
<h4 className="mb-2 text-base font-bold">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="grid gap-3"
|
||||
className="grid gap-x-6 gap-y-3"
|
||||
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
|
||||
>
|
||||
{section.items
|
||||
@@ -193,21 +193,21 @@ const Section = <T,>({
|
||||
className={cn("relative space-y-2 pl-4 group-[.sidebar]:pl-0", className)}
|
||||
>
|
||||
<div className="relative -ml-4 group-[.sidebar]:ml-0">
|
||||
<div className="pl-4 leading-snug group-[.sidebar]:pl-0">
|
||||
<div className="pl-4 group-[.sidebar]:pl-0">
|
||||
{children?.(item as T)}
|
||||
{url && <Link url={url} />}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-y-0 -left-px border-l-[4px] border-primary group-[.sidebar]:hidden" />
|
||||
</div>
|
||||
|
||||
{summary && !isEmptyString(summary) && (
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{level && level > 0 && <Rating level={level} />}
|
||||
{level !== undefined && level > 0 && <Rating level={level} />}
|
||||
|
||||
{keywords && keywords.length > 0 && (
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
|
||||
@@ -227,7 +227,7 @@ const Profiles = () => {
|
||||
return (
|
||||
<Section<Profile> section={section}>
|
||||
{(item) => (
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{isUrl(item.url.href) ? (
|
||||
<Link
|
||||
url={item.url}
|
||||
@@ -411,11 +411,11 @@ const Languages = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.languages);
|
||||
|
||||
return (
|
||||
<Section<Language> section={section} levelKey="fluencyLevel">
|
||||
<Section<Language> section={section} levelKey="level">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.fluency}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { TemplateKey } from "@reactive-resume/utils";
|
||||
import { Template } from "@reactive-resume/utils";
|
||||
|
||||
import { Azurill } from "./azurill";
|
||||
import { Bronzor } from "./bronzor";
|
||||
import { Chikorita } from "./chikorita";
|
||||
import { Ditto } from "./ditto";
|
||||
import { Kakuna } from "./kakuna";
|
||||
import { Nosepass } from "./nosepass";
|
||||
import { Onyx } from "./onyx";
|
||||
import { Pikachu } from "./pikachu";
|
||||
import { Rhyhorn } from "./rhyhorn";
|
||||
|
||||
export const getTemplate = (template: TemplateKey) => {
|
||||
export const getTemplate = (template: Template) => {
|
||||
switch (template) {
|
||||
case "onyx":
|
||||
return Onyx;
|
||||
@@ -27,6 +28,8 @@ export const getTemplate = (template: TemplateKey) => {
|
||||
return Bronzor;
|
||||
case "pikachu":
|
||||
return Pikachu;
|
||||
case "nosepass":
|
||||
return Nosepass;
|
||||
default:
|
||||
return Onyx;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ const Summary = () => {
|
||||
|
||||
return (
|
||||
<section id={section.id}>
|
||||
<h4 className="mb-2 border-b border-primary text-center font-bold uppercase text-primary">
|
||||
<h4 className="mb-2 border-b border-primary text-center font-bold text-primary">
|
||||
{section.name}
|
||||
</h4>
|
||||
|
||||
@@ -179,12 +179,12 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<section id={section.id} className="grid">
|
||||
<h4 className="mb-2 border-b border-primary text-center font-bold uppercase text-primary">
|
||||
<h4 className="mb-2 border-b border-primary text-center font-bold text-primary">
|
||||
{section.name}
|
||||
</h4>
|
||||
|
||||
<div
|
||||
className="grid gap-3"
|
||||
className="grid gap-x-6 gap-y-3"
|
||||
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
|
||||
>
|
||||
{section.items
|
||||
@@ -197,19 +197,19 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div className="leading-snug">{children?.(item as T)}</div>
|
||||
<div>{children?.(item as T)}</div>
|
||||
|
||||
{summary && !isEmptyString(summary) && (
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{level && level > 0 && <Rating level={level} />}
|
||||
{level !== undefined && level > 0 && <Rating level={level} />}
|
||||
|
||||
{keywords && keywords.length > 0 && (
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
|
||||
{url && <Link url={url} />}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -228,7 +228,7 @@ const Experience = () => {
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -246,7 +246,7 @@ const Education = () => {
|
||||
<div>{item.area}</div>
|
||||
<div>{item.score}</div>
|
||||
<div>{item.studyType}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -262,7 +262,7 @@ const Awards = () => {
|
||||
<div>
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -278,7 +278,7 @@ const Certifications = () => {
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -319,7 +319,7 @@ const Publications = () => {
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.publisher}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -336,7 +336,7 @@ const Volunteer = () => {
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -347,11 +347,11 @@ const Languages = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.languages);
|
||||
|
||||
return (
|
||||
<Section<Language> section={section} levelKey="fluencyLevel">
|
||||
<Section<Language> section={section} levelKey="level">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.fluency}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
@@ -368,7 +368,7 @@ const Projects = () => {
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.description}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -407,7 +407,7 @@ const Custom = ({ id }: { id: string }) => {
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.description}</div>
|
||||
<div>{item.location}</div>
|
||||
<div className="font-bold italic">{item.date}</div>
|
||||
<div className="font-bold">{item.date}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
import {
|
||||
Award,
|
||||
Certification,
|
||||
CustomSection,
|
||||
CustomSectionGroup,
|
||||
Education,
|
||||
Experience,
|
||||
Interest,
|
||||
Language,
|
||||
Profile,
|
||||
Project,
|
||||
Publication,
|
||||
Reference,
|
||||
SectionKey,
|
||||
SectionWithItem,
|
||||
Skill,
|
||||
URL,
|
||||
Volunteer,
|
||||
} from "@reactive-resume/schema";
|
||||
import { cn, 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="grid grid-cols-4 gap-x-6">
|
||||
<div className="mt-1 space-y-2 text-right">
|
||||
<p className="font-medium text-primary">Personal Information</p>
|
||||
<Picture className="ml-auto" />
|
||||
</div>
|
||||
|
||||
<div className="col-span-3 space-y-2">
|
||||
<div>
|
||||
<div className="text-2xl font-bold">{basics.name}</div>
|
||||
<div className="text-base">{basics.headline}</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1 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} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-x-3 text-sm">
|
||||
{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 className="text-primary">{item.name}</span>
|
||||
<span>{item.value}</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} className="grid grid-cols-4 gap-x-6">
|
||||
<div className="text-right">
|
||||
<h4 className="font-medium text-primary">{section.name}</h4>
|
||||
</div>
|
||||
|
||||
<div className="col-span-3">
|
||||
<div className="relative">
|
||||
<hr className="mt-3 border-primary pb-3" />
|
||||
<div className="absolute bottom-3 right-0 h-3 w-3 bg-primary" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="wysiwyg"
|
||||
style={{ columns: section.columns }}
|
||||
dangerouslySetInnerHTML={{ __html: section.content }}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
urlKey?: keyof T;
|
||||
dateKey?: keyof T;
|
||||
levelKey?: keyof T;
|
||||
summaryKey?: keyof T;
|
||||
keywordsKey?: keyof T;
|
||||
};
|
||||
|
||||
const Section = <T,>({
|
||||
section,
|
||||
children,
|
||||
urlKey,
|
||||
dateKey,
|
||||
summaryKey,
|
||||
keywordsKey,
|
||||
}: SectionProps<T>) => {
|
||||
if (!section.visible || !section.items.length) return null;
|
||||
|
||||
return (
|
||||
<section id={section.id} className={cn("grid", dateKey !== undefined && "gap-y-4")}>
|
||||
<div className="grid grid-cols-4 gap-x-6">
|
||||
<div className="text-right">
|
||||
<h4 className="font-medium text-primary">{section.name}</h4>
|
||||
</div>
|
||||
|
||||
<div className="col-span-3">
|
||||
<div className="relative">
|
||||
<hr className="mt-3 border-primary" />
|
||||
<div className="absolute bottom-0 right-0 h-3 w-3 bg-primary" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dateKey !== undefined && (
|
||||
<div className="grid grid-cols-4 gap-x-6 gap-y-4">
|
||||
{section.items
|
||||
.filter((item) => item.visible)
|
||||
.map((item) => {
|
||||
const url = (urlKey && get(item, urlKey)) as URL | undefined;
|
||||
const date = (dateKey && get(item, dateKey, "")) as string | undefined;
|
||||
const summary = (summaryKey && get(item, summaryKey, "")) as string | undefined;
|
||||
const keywords = (keywordsKey && get(item, keywordsKey, [])) as string[] | undefined;
|
||||
|
||||
return (
|
||||
<Fragment key={item.id}>
|
||||
<div className="text-right font-medium text-primary">{date}</div>
|
||||
|
||||
<div className="col-span-3 space-y-1">
|
||||
{children?.(item as T)}
|
||||
|
||||
{url !== undefined && <Link url={url} />}
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{dateKey === undefined && (
|
||||
<div className="grid grid-cols-4 gap-x-6">
|
||||
<div
|
||||
className="col-span-3 col-start-2 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 summary = (summaryKey && get(item, summaryKey, "")) as string | undefined;
|
||||
const keywords = (keywordsKey && get(item, keywordsKey, [])) as
|
||||
| string[]
|
||||
| undefined;
|
||||
|
||||
return (
|
||||
<div key={item.id}>
|
||||
{children?.(item as T)}
|
||||
|
||||
{url !== undefined && <Link url={url} />}
|
||||
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
</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 Experience = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.experience);
|
||||
|
||||
return (
|
||||
<Section<Experience> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.company}</div>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const Education = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.education);
|
||||
|
||||
return (
|
||||
<Section<Education> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.institution}</div>
|
||||
<div>{item.area}</div>
|
||||
<div>{item.studyType}</div>
|
||||
<div>{item.score}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const Awards = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.awards);
|
||||
|
||||
return (
|
||||
<Section<Award> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.title}</div>
|
||||
<div>{item.awarder}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const Certifications = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.certifications);
|
||||
|
||||
return (
|
||||
<Section<Certification> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.issuer}</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">
|
||||
{(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" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.publisher}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const Volunteer = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.volunteer);
|
||||
|
||||
return (
|
||||
<Section<Volunteer> section={section} urlKey="url" dateKey="date" summaryKey="summary">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.organization}</div>
|
||||
<div>{item.position}</div>
|
||||
<div>{item.location}</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"
|
||||
dateKey="date"
|
||||
summaryKey="summary"
|
||||
keywordsKey="keywords"
|
||||
>
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.description}</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"
|
||||
dateKey="date"
|
||||
summaryKey="summary"
|
||||
keywordsKey="keywords"
|
||||
>
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.description}</div>
|
||||
<div>{item.location}</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 Nosepass = ({ columns, isFirstPage = false }: TemplateProps) => {
|
||||
const name = useArtboardStore((state) => state.resume.basics.name);
|
||||
|
||||
const [main, sidebar] = columns;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<img alt="Europass Logo" className="h-[48px]" src="https://i.imgur.com/eRK005p.png" />
|
||||
|
||||
<p className="font-medium text-primary">Curriculum Vitae</p>
|
||||
|
||||
<p className="font-medium text-primary">{name}</p>
|
||||
</div>
|
||||
|
||||
{isFirstPage && <Header />}
|
||||
|
||||
<div className="space-y-4">
|
||||
{main.map((section) => (
|
||||
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
|
||||
))}
|
||||
|
||||
{sidebar.map((section) => (
|
||||
<Fragment key={section}>{mapSectionToComponent(section)}</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -110,7 +110,7 @@ const Summary = () => {
|
||||
|
||||
return (
|
||||
<section id={section.id}>
|
||||
<h4 className="font-bold uppercase text-primary">{section.name}</h4>
|
||||
<h4 className="font-bold text-primary">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="wysiwyg"
|
||||
@@ -182,10 +182,10 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<section id={section.id} className="grid">
|
||||
<h4 className="font-bold uppercase text-primary">{section.name}</h4>
|
||||
<h4 className="font-bold text-primary">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="grid gap-3"
|
||||
className="grid gap-x-6 gap-y-3"
|
||||
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
|
||||
>
|
||||
{section.items
|
||||
@@ -198,18 +198,18 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url && <Link url={url} />}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary && !isEmptyString(summary) && (
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{level && level > 0 && <Rating level={level} />}
|
||||
{level !== undefined && level > 0 && <Rating level={level} />}
|
||||
|
||||
{keywords && keywords.length > 0 && (
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -379,11 +379,11 @@ const Languages = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.languages);
|
||||
|
||||
return (
|
||||
<Section<Language> section={section} levelKey="fluencyLevel">
|
||||
<Section<Language> section={section} levelKey="level">
|
||||
{(item) => (
|
||||
<div className="space-y-0.5">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.fluency}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
@@ -34,8 +34,8 @@ const Header = () => {
|
||||
className="summary group bg-primary px-6 pb-7 pt-6 text-background"
|
||||
style={{ borderRadius: `calc(${borderRadius}px - 2px)` }}
|
||||
>
|
||||
<div className="col-span-2 space-y-4">
|
||||
<div className="leading-tight">
|
||||
<div className="col-span-2 space-y-2.5">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold">{basics.name}</h2>
|
||||
<p>{basics.headline}</p>
|
||||
</div>
|
||||
@@ -102,7 +102,7 @@ const Summary = () => {
|
||||
|
||||
return (
|
||||
<section id={section.id}>
|
||||
<h4 className="mb-2 text-base font-bold uppercase">{section.name}</h4>
|
||||
<h4 className="mb-2 border-b border-primary text-base font-bold">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="wysiwyg"
|
||||
@@ -182,10 +182,10 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<section id={section.id} className="grid">
|
||||
<h4 className="mb-2 border-b border-primary text-base font-bold uppercase">{section.name}</h4>
|
||||
<h4 className="mb-2 border-b border-primary text-base font-bold">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="grid gap-3"
|
||||
className="grid gap-x-6 gap-y-3"
|
||||
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
|
||||
>
|
||||
{section.items
|
||||
@@ -198,18 +198,18 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url && <Link url={url} />}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary && !isEmptyString(summary) && (
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{level && level > 0 && <Rating level={level} />}
|
||||
{level !== undefined && level > 0 && <Rating level={level} />}
|
||||
|
||||
{keywords && keywords.length > 0 && (
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -227,7 +227,7 @@ const Profiles = () => {
|
||||
return (
|
||||
<Section<Profile> section={section}>
|
||||
{(item) => (
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{isUrl(item.url.href) ? (
|
||||
<Link
|
||||
url={item.url}
|
||||
@@ -358,7 +358,7 @@ const Interests = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.interests);
|
||||
|
||||
return (
|
||||
<Section<Interest> section={section} className="space-y-0" keywordsKey="keywords">
|
||||
<Section<Interest> section={section} className="space-y-1" keywordsKey="keywords">
|
||||
{(item) => <div className="font-bold">{item.name}</div>}
|
||||
</Section>
|
||||
);
|
||||
@@ -411,11 +411,11 @@ const Languages = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.languages);
|
||||
|
||||
return (
|
||||
<Section<Language> section={section} levelKey="fluencyLevel">
|
||||
<Section<Language> section={section} levelKey="level">
|
||||
{(item) => (
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.fluency}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
@@ -82,7 +82,7 @@ const Summary = () => {
|
||||
|
||||
return (
|
||||
<section id={section.id}>
|
||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold uppercase">{section.name}</h4>
|
||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="wysiwyg"
|
||||
@@ -154,10 +154,10 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<section id={section.id} className="grid">
|
||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold uppercase">{section.name}</h4>
|
||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
||||
|
||||
<div
|
||||
className="grid gap-3"
|
||||
className="grid gap-x-6 gap-y-3"
|
||||
style={{ gridTemplateColumns: `repeat(${section.columns}, 1fr)` }}
|
||||
>
|
||||
{section.items
|
||||
@@ -170,18 +170,18 @@ const Section = <T,>({
|
||||
|
||||
return (
|
||||
<div key={item.id} className={cn("space-y-2", className)}>
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{children?.(item as T)}
|
||||
{url && <Link url={url} />}
|
||||
{url !== undefined && <Link url={url} />}
|
||||
</div>
|
||||
|
||||
{summary && !isEmptyString(summary) && (
|
||||
{summary !== undefined && !isEmptyString(summary) && (
|
||||
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
|
||||
)}
|
||||
|
||||
{level && level > 0 && <Rating level={level} />}
|
||||
{level !== undefined && level > 0 && <Rating level={level} />}
|
||||
|
||||
{keywords && keywords.length > 0 && (
|
||||
{keywords !== undefined && keywords.length > 0 && (
|
||||
<p className="text-sm">{keywords.join(", ")}</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -199,7 +199,7 @@ const Profiles = () => {
|
||||
return (
|
||||
<Section<Profile> section={section}>
|
||||
{(item) => (
|
||||
<div className="leading-snug">
|
||||
<div>
|
||||
{isUrl(item.url.href) ? (
|
||||
<Link
|
||||
url={item.url}
|
||||
@@ -317,7 +317,7 @@ const Skills = () => {
|
||||
return (
|
||||
<Section<Skill> section={section} levelKey="level" keywordsKey="keywords">
|
||||
{(item) => (
|
||||
<div className="leading-tight">
|
||||
<div>
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
@@ -383,11 +383,11 @@ const Languages = () => {
|
||||
const section = useArtboardStore((state) => state.resume.sections.languages);
|
||||
|
||||
return (
|
||||
<Section<Language> section={section} levelKey="fluencyLevel">
|
||||
<Section<Language> section={section} levelKey="level">
|
||||
{(item) => (
|
||||
<div className="space-y-0.5">
|
||||
<div className="font-bold">{item.name}</div>
|
||||
<div>{item.fluency}</div>
|
||||
<div>{item.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 131 KiB |
@@ -0,0 +1,321 @@
|
||||
{
|
||||
"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.2,
|
||||
"borderRadius": 4,
|
||||
"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": "azurill",
|
||||
"layout": [
|
||||
[
|
||||
["summary", "experience", "education", "references"],
|
||||
[
|
||||
"profiles",
|
||||
"skills",
|
||||
"certifications",
|
||||
"projects",
|
||||
"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": "#ca8a04"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "Merriweather",
|
||||
"subset": "latin",
|
||||
"variants": ["regular"],
|
||||
"size": 13
|
||||
},
|
||||
"lineHeight": 1.75,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": true
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
{
|
||||
"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": 140,
|
||||
"aspectRatio": 1,
|
||||
"borderRadius": 4,
|
||||
"effects": {
|
||||
"hidden": true,
|
||||
"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": 2,
|
||||
"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": 2,
|
||||
"visible": true,
|
||||
"id": "languages",
|
||||
"items": [
|
||||
{
|
||||
"id": "ey0c353j3apepzqmyl8zd37c",
|
||||
"visible": true,
|
||||
"name": "English",
|
||||
"description": "Native Speaker",
|
||||
"level": 0
|
||||
},
|
||||
{
|
||||
"id": "a4dnf32wyj5phgln2rx97qk2",
|
||||
"visible": true,
|
||||
"name": "Spanish",
|
||||
"description": "Intermediate",
|
||||
"level": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"profiles": {
|
||||
"name": "Profiles",
|
||||
"columns": 3,
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "k97vdxzmssmgel79km48kxgg",
|
||||
"visible": true,
|
||||
"network": "StackOverflow",
|
||||
"username": "johndoe",
|
||||
"icon": "stackoverflow",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://stackoverflow.com/u/johndoe"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"projects": {
|
||||
"name": "Projects",
|
||||
"columns": 2,
|
||||
"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": true,
|
||||
"id": "references",
|
||||
"items": [
|
||||
{
|
||||
"id": "f2sv5z0cce6ztjl87yuk8fak",
|
||||
"visible": true,
|
||||
"name": "Available upon request",
|
||||
"description": "",
|
||||
"summary": "",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"skills": {
|
||||
"name": "Skills",
|
||||
"columns": 3,
|
||||
"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": "bronzor",
|
||||
"layout": [
|
||||
[
|
||||
["profiles", "summary", "experience", "education", "projects"],
|
||||
[
|
||||
"skills",
|
||||
"certifications",
|
||||
"languages",
|
||||
"references",
|
||||
"interests",
|
||||
"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": "#059669"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "IBM Plex Sans",
|
||||
"subset": "latin",
|
||||
"variants": ["regular", "500"],
|
||||
"size": 13
|
||||
},
|
||||
"lineHeight": 1.5,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": true
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
{
|
||||
"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": 140,
|
||||
"aspectRatio": 1,
|
||||
"borderRadius": 4,
|
||||
"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": [
|
||||
{
|
||||
"id": "ey0c353j3apepzqmyl8zd37c",
|
||||
"visible": true,
|
||||
"name": "English",
|
||||
"description": "Native Speaker",
|
||||
"level": 0
|
||||
},
|
||||
{
|
||||
"id": "a4dnf32wyj5phgln2rx97qk2",
|
||||
"visible": true,
|
||||
"name": "Spanish",
|
||||
"description": "Intermediate",
|
||||
"level": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"profiles": {
|
||||
"name": "Profiles",
|
||||
"columns": 3,
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "k97vdxzmssmgel79km48kxgg",
|
||||
"visible": true,
|
||||
"network": "StackOverflow",
|
||||
"username": "johndoe",
|
||||
"icon": "stackoverflow",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://stackoverflow.com/u/johndoe"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"projects": {
|
||||
"name": "Projects",
|
||||
"columns": 2,
|
||||
"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": true,
|
||||
"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": "chikorita",
|
||||
"layout": [
|
||||
[
|
||||
["profiles", "summary", "experience", "education", "projects"],
|
||||
[
|
||||
"skills",
|
||||
"certifications",
|
||||
"languages",
|
||||
"references",
|
||||
"interests",
|
||||
"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": "#059669"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "Merriweather",
|
||||
"subset": "latin",
|
||||
"variants": ["regular"],
|
||||
"size": 13
|
||||
},
|
||||
"lineHeight": 1.5,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": true
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
{
|
||||
"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": 140,
|
||||
"aspectRatio": 1,
|
||||
"borderRadius": 4,
|
||||
"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": [
|
||||
{
|
||||
"id": "ey0c353j3apepzqmyl8zd37c",
|
||||
"visible": true,
|
||||
"name": "English",
|
||||
"description": "Native Speaker",
|
||||
"level": 0
|
||||
},
|
||||
{
|
||||
"id": "a4dnf32wyj5phgln2rx97qk2",
|
||||
"visible": true,
|
||||
"name": "Spanish",
|
||||
"description": "Intermediate",
|
||||
"level": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "k97vdxzmssmgel79km48kxgg",
|
||||
"visible": true,
|
||||
"network": "StackOverflow",
|
||||
"username": "johndoe",
|
||||
"icon": "stackoverflow",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://stackoverflow.com/u/johndoe"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"projects": {
|
||||
"name": "Projects",
|
||||
"columns": 2,
|
||||
"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": true,
|
||||
"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": "ditto",
|
||||
"layout": [
|
||||
[
|
||||
["summary", "experience", "education", "projects"],
|
||||
[
|
||||
"profiles",
|
||||
"skills",
|
||||
"certifications",
|
||||
"languages",
|
||||
"references",
|
||||
"interests",
|
||||
"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": "Merriweather",
|
||||
"subset": "latin",
|
||||
"variants": ["regular"],
|
||||
"size": 13
|
||||
},
|
||||
"lineHeight": 1.75,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": true
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
{
|
||||
"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": 64,
|
||||
"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": 2,
|
||||
"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": "",
|
||||
"score": "",
|
||||
"date": "August 2012 to May 2016",
|
||||
"summary": "",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"experience": {
|
||||
"name": "Experience",
|
||||
"columns": 2,
|
||||
"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": 2,
|
||||
"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": true,
|
||||
"id": "references",
|
||||
"items": [
|
||||
{
|
||||
"id": "f2sv5z0cce6ztjl87yuk8fak",
|
||||
"visible": true,
|
||||
"name": "Available upon request",
|
||||
"description": "",
|
||||
"summary": "",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"skills": {
|
||||
"name": "Skills",
|
||||
"columns": 3,
|
||||
"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": "kakuna",
|
||||
"layout": [
|
||||
[
|
||||
[
|
||||
"summary",
|
||||
"experience",
|
||||
"education",
|
||||
"projects",
|
||||
"certifications",
|
||||
"skills",
|
||||
"references"
|
||||
],
|
||||
["profiles", "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": "#57534e"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "Lora",
|
||||
"subset": "latin",
|
||||
"variants": ["regular"],
|
||||
"size": 13
|
||||
},
|
||||
"lineHeight": 1.5,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": true
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
{
|
||||
"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": 90,
|
||||
"aspectRatio": 1,
|
||||
"borderRadius": 4,
|
||||
"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": "Aug 2012 - 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": "Jan 2019 - 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": "Jun 2016 - Dec 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": [
|
||||
{
|
||||
"id": "ey0c353j3apepzqmyl8zd37c",
|
||||
"visible": true,
|
||||
"name": "English",
|
||||
"description": "Native Speaker",
|
||||
"level": 0
|
||||
},
|
||||
{
|
||||
"id": "a4dnf32wyj5phgln2rx97qk2",
|
||||
"visible": true,
|
||||
"name": "Spanish",
|
||||
"description": "Intermediate",
|
||||
"level": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"profiles": {
|
||||
"name": "Profiles",
|
||||
"columns": 3,
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "k97vdxzmssmgel79km48kxgg",
|
||||
"visible": true,
|
||||
"network": "StackOverflow",
|
||||
"username": "johndoe",
|
||||
"icon": "stackoverflow",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://stackoverflow.com/u/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": true,
|
||||
"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": "nosepass",
|
||||
"layout": [
|
||||
[
|
||||
["profiles", "summary", "experience", "education"],
|
||||
["interests", "awards", "volunteer", "publications"]
|
||||
],
|
||||
[["projects", "certifications", "skills", "languages", "references"], []]
|
||||
],
|
||||
"css": {
|
||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||
"visible": false
|
||||
},
|
||||
"page": {
|
||||
"margin": 24,
|
||||
"format": "a4",
|
||||
"options": {
|
||||
"breakLine": true,
|
||||
"pageNumbers": true
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"background": "#ffffff",
|
||||
"text": "#000000",
|
||||
"primary": "#5d93bc"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "Work Sans",
|
||||
"subset": "latin",
|
||||
"variants": ["regular"],
|
||||
"size": 13.8
|
||||
},
|
||||
"lineHeight": 1.5,
|
||||
"hideIcons": true,
|
||||
"underlineLinks": true
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
{
|
||||
"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": 100,
|
||||
"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": 2,
|
||||
"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 of Science",
|
||||
"area": "Computer Science",
|
||||
"score": "Berkeley, CA",
|
||||
"date": "August 2012 to May 2016",
|
||||
"summary": "",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://www.universityofcalifornia.edu/"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"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": true,
|
||||
"id": "references",
|
||||
"items": [
|
||||
{
|
||||
"id": "f2sv5z0cce6ztjl87yuk8fak",
|
||||
"visible": true,
|
||||
"name": "Available upon request",
|
||||
"description": "",
|
||||
"summary": "",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"skills": {
|
||||
"name": "Skills",
|
||||
"columns": 3,
|
||||
"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": "onyx",
|
||||
"layout": [
|
||||
[
|
||||
[
|
||||
"summary",
|
||||
"experience",
|
||||
"education",
|
||||
"projects",
|
||||
"certifications",
|
||||
"skills",
|
||||
"references"
|
||||
],
|
||||
["profiles", "interests", "languages", "awards", "volunteer", "publications"]
|
||||
]
|
||||
],
|
||||
"css": {
|
||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||
"visible": false
|
||||
},
|
||||
"page": {
|
||||
"margin": 18,
|
||||
"format": "a4",
|
||||
"options": {
|
||||
"breakLine": true,
|
||||
"pageNumbers": true
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"background": "#ffffff",
|
||||
"text": "#000000",
|
||||
"primary": "#dc2626"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "PT Serif",
|
||||
"subset": "latin",
|
||||
"variants": ["regular"],
|
||||
"size": 13
|
||||
},
|
||||
"lineHeight": 1.5,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": false
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
{
|
||||
"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": 140,
|
||||
"aspectRatio": 1,
|
||||
"borderRadius": 4,
|
||||
"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": [
|
||||
{
|
||||
"id": "ey0c353j3apepzqmyl8zd37c",
|
||||
"visible": true,
|
||||
"name": "English",
|
||||
"description": "Native Speaker",
|
||||
"level": 0
|
||||
},
|
||||
{
|
||||
"id": "a4dnf32wyj5phgln2rx97qk2",
|
||||
"visible": true,
|
||||
"name": "Spanish",
|
||||
"description": "Intermediate",
|
||||
"level": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "k97vdxzmssmgel79km48kxgg",
|
||||
"visible": true,
|
||||
"network": "StackOverflow",
|
||||
"username": "johndoe",
|
||||
"icon": "stackoverflow",
|
||||
"url": {
|
||||
"label": "",
|
||||
"href": "https://stackoverflow.com/u/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": true,
|
||||
"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": "pikachu",
|
||||
"layout": [
|
||||
[
|
||||
["summary", "experience", "education", "projects"],
|
||||
[
|
||||
"profiles",
|
||||
"skills",
|
||||
"certifications",
|
||||
"languages",
|
||||
"references",
|
||||
"interests",
|
||||
"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": "#ca8a04"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "Titillium Web",
|
||||
"subset": "latin",
|
||||
"variants": ["regular"],
|
||||
"size": 13.8
|
||||
},
|
||||
"lineHeight": 1.5,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": false
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
{
|
||||
"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": 64,
|
||||
"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": 2,
|
||||
"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": 4,
|
||||
"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": 2,
|
||||
"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": 3,
|
||||
"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": "rhyhorn",
|
||||
"layout": [
|
||||
[
|
||||
[
|
||||
"profiles",
|
||||
"summary",
|
||||
"experience",
|
||||
"education",
|
||||
"projects",
|
||||
"certifications",
|
||||
"skills",
|
||||
"references"
|
||||
],
|
||||
["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": "#ca8a04"
|
||||
},
|
||||
"typography": {
|
||||
"font": {
|
||||
"family": "Merriweather",
|
||||
"subset": "latin",
|
||||
"variants": ["regular"],
|
||||
"size": 13
|
||||
},
|
||||
"lineHeight": 1.75,
|
||||
"hideIcons": false,
|
||||
"underlineLinks": true
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.1 MiB |
@@ -1,20 +1,21 @@
|
||||
export const colors: string[] = [
|
||||
"#78716c", // stone-500
|
||||
"#ef4444", // red-500
|
||||
"#f97316", // orange-500
|
||||
"#f59e0b", // amber-500
|
||||
"#eab308", // yellow-500
|
||||
"#84cc16", // lime-500
|
||||
"#22c55e", // green-500
|
||||
"#10b981", // emerald-500
|
||||
"#14b8a6", // teal-500
|
||||
"#06b6d4", // cyan-500
|
||||
"#0ea5e9", // sky-500
|
||||
"#3b82f6", // blue-500
|
||||
"#6366f1", // indigo-500
|
||||
"#8b5cf6", // violet-500
|
||||
"#a855f7", // purple-500
|
||||
"#d946ef", // fuchsia-500
|
||||
"#ec4899", // pink-500
|
||||
"#f43f5e", // rose-500
|
||||
"#475569", // slate-600
|
||||
"#57534e", // stone-600
|
||||
"#dc2626", // red-600
|
||||
"#ea580c", // orange-600
|
||||
"#d97706", // amber-600
|
||||
"#ca8a04", // yellow-600
|
||||
"#65a30d", // lime-600
|
||||
"#16a34a", // green-600
|
||||
"#059669", // emerald-600
|
||||
"#0d9488", // teal-600
|
||||
"#0891b2", // cyan-600
|
||||
"#0284c7", // sky-600
|
||||
"#2563eb", // blue-600
|
||||
"#4f46e5", // indigo-600
|
||||
"#7c3aed", // violet-600
|
||||
"#9333ea", // purple-600
|
||||
"#c026d3", // fuchsia-600
|
||||
"#db2777", // pink-600
|
||||
"#e11d48", // rose-600
|
||||
];
|
||||
|
||||
@@ -210,7 +210,7 @@ msgid "Backup Codes may contain only lowercase letters or numbers, and must be e
|
||||
msgstr "Backup Codes may contain only lowercase letters or numbers, and must be exactly 10 characters."
|
||||
|
||||
#: apps/client/src/pages/builder/sidebars/left/index.tsx:57
|
||||
msgctxt "The Basics section of a Resume consists of User's Picture, Full Name, Location etc."
|
||||
msgctxt "The basics section of a resume consists of User's Picture, Full Name, Location etc."
|
||||
msgid "Basics"
|
||||
msgstr "Basics"
|
||||
|
||||
@@ -247,7 +247,7 @@ msgstr "Cancel"
|
||||
msgid "Casual"
|
||||
msgstr "Casual"
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:90
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:89
|
||||
msgid "Center Artboard"
|
||||
msgstr "Center Artboard"
|
||||
|
||||
@@ -310,7 +310,7 @@ msgstr "Continue"
|
||||
msgid "Copy"
|
||||
msgstr "Copy"
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:124
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:123
|
||||
msgid "Copy Link to Resume"
|
||||
msgstr "Copy Link to Resume"
|
||||
|
||||
@@ -521,7 +521,7 @@ msgstr "Even if you're not in a position to contribute financially, you can stil
|
||||
msgid "Export"
|
||||
msgstr "Export"
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:130
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:129
|
||||
msgid "Export as PDF"
|
||||
msgstr "Export as PDF"
|
||||
|
||||
@@ -652,6 +652,8 @@ msgstr "Here, you can update your account information such as your profile pictu
|
||||
msgid "Here, you can update your profile to customize and personalize your experience."
|
||||
msgstr "Here, you can update your profile to customize and personalize your experience."
|
||||
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/languages.tsx:77
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/skills.tsx:82
|
||||
#: apps/client/src/pages/builder/sidebars/left/sections/picture/options.tsx:185
|
||||
msgid "Hidden"
|
||||
msgstr "Hidden"
|
||||
@@ -787,7 +789,7 @@ msgstr "JSON"
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/custom-section.tsx:145
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/interests.tsx:55
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/projects.tsx:122
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/skills.tsx:95
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/skills.tsx:99
|
||||
msgid "Keywords"
|
||||
msgstr "Keywords"
|
||||
|
||||
@@ -952,7 +954,7 @@ msgstr "Notes"
|
||||
msgid "One-Time Password"
|
||||
msgstr "One-Time Password"
|
||||
|
||||
#: apps/client/src/libs/axios.ts:32
|
||||
#: apps/client/src/libs/axios.ts:34
|
||||
#: apps/client/src/pages/dashboard/resumes/_dialogs/import.tsx:190
|
||||
msgid "Oops, the server returned an error."
|
||||
msgstr "Oops, the server returned an error."
|
||||
@@ -1028,7 +1030,7 @@ msgstr "Photograph by Patrick Tomasso"
|
||||
msgid "Pick any font from Google Fonts"
|
||||
msgstr "Pick any font from Google Fonts"
|
||||
|
||||
#: apps/client/src/pages/builder/sidebars/left/sections/picture/section.tsx:52
|
||||
#: apps/client/src/pages/builder/sidebars/left/sections/picture/section.tsx:69
|
||||
#: apps/client/src/pages/dashboard/settings/_sections/account.tsx:120
|
||||
msgid "Picture"
|
||||
msgstr "Picture"
|
||||
@@ -1118,7 +1120,7 @@ msgstr "Reactive Resume is a passion project of over 3 years of hard work, and w
|
||||
msgid "Reactive Resume thrives thanks to its vibrant community. This project owes its progress to numerous individuals who've dedicated their time and skills. Below, we celebrate the coders who've enhanced its features on GitHub and the linguists whose translations on Crowdin have made it accessible to a broader audience."
|
||||
msgstr "Reactive Resume thrives thanks to its vibrant community. This project owes its progress to numerous individuals who've dedicated their time and skills. Below, we celebrate the coders who've enhanced its features on GitHub and the linguists whose translations on Crowdin have made it accessible to a broader audience."
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:64
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:63
|
||||
msgid "Redo"
|
||||
msgstr "Redo"
|
||||
|
||||
@@ -1150,7 +1152,7 @@ msgstr "Reset Layout"
|
||||
msgid "Reset your password"
|
||||
msgstr "Reset your password"
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:84
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:83
|
||||
msgid "Reset Zoom"
|
||||
msgstr "Reset Zoom"
|
||||
|
||||
@@ -1444,11 +1446,11 @@ msgstr "Title"
|
||||
msgid "Title"
|
||||
msgstr "Title"
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:98
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:97
|
||||
msgid "Toggle Page Break Line"
|
||||
msgstr "Toggle Page Break Line"
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:110
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:109
|
||||
msgid "Toggle Page Numbers"
|
||||
msgstr "Toggle Page Numbers"
|
||||
|
||||
@@ -1487,7 +1489,7 @@ msgstr "Typography"
|
||||
msgid "Underline Links"
|
||||
msgstr "Underline Links"
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:58
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:57
|
||||
msgid "Undo"
|
||||
msgstr "Undo"
|
||||
|
||||
@@ -1603,7 +1605,7 @@ msgstr "What's new in the latest version"
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/custom-section.tsx:150
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/interests.tsx:60
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/projects.tsx:127
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/skills.tsx:100
|
||||
#: apps/client/src/pages/builder/sidebars/left/dialogs/skills.tsx:104
|
||||
msgid "You can add multiple keywords by separating them with a comma or pressing enter."
|
||||
msgstr "You can add multiple keywords by separating them with a comma or pressing enter."
|
||||
|
||||
@@ -1651,10 +1653,10 @@ msgstr "Your OpenAI API Key has not been set yet. Please go to your account sett
|
||||
msgid "Your password has been updated successfully."
|
||||
msgstr "Your password has been updated successfully."
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:72
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:71
|
||||
msgid "Zoom In"
|
||||
msgstr "Zoom In"
|
||||
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:78
|
||||
#: apps/client/src/pages/builder/_components/toolbar.tsx:77
|
||||
msgid "Zoom Out"
|
||||
msgstr "Zoom Out"
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
CircleNotch,
|
||||
ClockClockwise,
|
||||
CubeFocus,
|
||||
DownloadSimple,
|
||||
FilePdf,
|
||||
Hash,
|
||||
LineSegment,
|
||||
LinkSimple,
|
||||
@@ -51,7 +51,7 @@ export const BuilderToolbar = () => {
|
||||
<motion.div
|
||||
initial={{ opacity: 0.5 }}
|
||||
whileHover={{ opacity: 1 }}
|
||||
className="fixed inset-x-0 mx-auto hidden pb-4 pt-6 text-center md:block"
|
||||
className="fixed inset-x-0 bottom-0 mx-auto pb-4 pt-6 text-center md:block"
|
||||
>
|
||||
<div className="inline-flex items-center justify-center rounded-full bg-background px-4 shadow-xl">
|
||||
<Tooltip content={t`Undo`}>
|
||||
@@ -134,7 +134,7 @@ export const BuilderToolbar = () => {
|
||||
onClick={onPrint}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? <CircleNotch className="animate-spin" /> : <DownloadSimple />}
|
||||
{loading ? <CircleNotch className="animate-spin" /> : <FilePdf />}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
Input,
|
||||
Slider,
|
||||
} from "@reactive-resume/ui";
|
||||
import { getCEFRLevel } from "@reactive-resume/utils";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -44,11 +43,11 @@ export const LanguagesDialog = () => {
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="fluency"
|
||||
name="description"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t`Fluency`}</FormLabel>
|
||||
<FormLabel>{t`Description`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -58,22 +57,26 @@ export const LanguagesDialog = () => {
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="fluencyLevel"
|
||||
name="level"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="sm:col-span-2">
|
||||
<FormLabel>{t`Fluency (CEFR)`}</FormLabel>
|
||||
<FormLabel>{t`Level`}</FormLabel>
|
||||
<FormControl className="py-2">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Slider
|
||||
{...field}
|
||||
min={1}
|
||||
max={6}
|
||||
min={0}
|
||||
max={5}
|
||||
value={[field.value]}
|
||||
onValueChange={(value) => field.onChange(value[0])}
|
||||
/>
|
||||
|
||||
<span className="text-base font-bold">{getCEFRLevel(field.value)}</span>
|
||||
{field.value === 0 ? (
|
||||
<span className="text-base font-bold">{t`Hidden`}</span>
|
||||
) : (
|
||||
<span className="text-base font-bold">{field.value}</span>
|
||||
)}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
|
||||
@@ -71,14 +71,18 @@ export const SkillsDialog = () => {
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Slider
|
||||
{...field}
|
||||
min={1}
|
||||
min={0}
|
||||
max={5}
|
||||
value={[field.value]}
|
||||
orientation="horizontal"
|
||||
onValueChange={(value) => field.onChange(value[0])}
|
||||
/>
|
||||
|
||||
<span className="text-base font-bold">{field.value}</span>
|
||||
{field.value === 0 ? (
|
||||
<span className="text-base font-bold">{t`Hidden`}</span>
|
||||
) : (
|
||||
<span className="text-base font-bold">{field.value}</span>
|
||||
)}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
Volunteer,
|
||||
} from "@reactive-resume/schema";
|
||||
import { Button, ScrollArea, Separator } from "@reactive-resume/ui";
|
||||
import { getCEFRLevel } from "@reactive-resume/utils";
|
||||
import { Fragment, useRef } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
@@ -57,21 +56,21 @@ export const LeftSidebar = () => {
|
||||
name={t({
|
||||
message: "Basics",
|
||||
context:
|
||||
"The Basics section of a Resume consists of User's Picture, Full Name, Location etc.",
|
||||
"The basics section of a resume consists of User's Picture, Full Name, Location etc.",
|
||||
})}
|
||||
/>
|
||||
<SectionIcon id="summary" onClick={() => scrollIntoView("#summary")} />
|
||||
<SectionIcon id="profiles" onClick={() => scrollIntoView("#profiles")} />
|
||||
<SectionIcon id="experience" onClick={() => scrollIntoView("#experience")} />
|
||||
<SectionIcon id="education" onClick={() => scrollIntoView("#education")} />
|
||||
<SectionIcon id="skills" onClick={() => scrollIntoView("#skills")} />
|
||||
<SectionIcon id="languages" onClick={() => scrollIntoView("#languages")} />
|
||||
<SectionIcon id="awards" onClick={() => scrollIntoView("#awards")} />
|
||||
<SectionIcon id="certifications" onClick={() => scrollIntoView("#certifications")} />
|
||||
<SectionIcon id="interests" onClick={() => scrollIntoView("#interests")} />
|
||||
<SectionIcon id="languages" onClick={() => scrollIntoView("#languages")} />
|
||||
<SectionIcon id="volunteer" onClick={() => scrollIntoView("#volunteer")} />
|
||||
<SectionIcon id="projects" onClick={() => scrollIntoView("#projects")} />
|
||||
<SectionIcon id="publications" onClick={() => scrollIntoView("#publications")} />
|
||||
<SectionIcon id="skills" onClick={() => scrollIntoView("#skills")} />
|
||||
<SectionIcon id="volunteer" onClick={() => scrollIntoView("#volunteer")} />
|
||||
<SectionIcon id="references" onClick={() => scrollIntoView("#references")} />
|
||||
|
||||
<SectionIcon
|
||||
@@ -118,6 +117,21 @@ export const LeftSidebar = () => {
|
||||
description={(item) => item.area}
|
||||
/>
|
||||
<Separator />
|
||||
<SectionBase<Skill>
|
||||
id="skills"
|
||||
title={(item) => item.name}
|
||||
description={(item) => {
|
||||
if (item.description) return item.description;
|
||||
if (item.keywords.length > 0) return `${item.keywords.length} keywords`;
|
||||
}}
|
||||
/>
|
||||
<Separator />
|
||||
<SectionBase<Language>
|
||||
id="languages"
|
||||
title={(item) => item.name}
|
||||
description={(item) => item.description}
|
||||
/>
|
||||
<Separator />
|
||||
<SectionBase<Award>
|
||||
id="awards"
|
||||
title={(item) => item.title}
|
||||
@@ -138,18 +152,6 @@ export const LeftSidebar = () => {
|
||||
}}
|
||||
/>
|
||||
<Separator />
|
||||
<SectionBase<Language>
|
||||
id="languages"
|
||||
title={(item) => item.name}
|
||||
description={(item) => item.fluency || getCEFRLevel(item.fluencyLevel)}
|
||||
/>
|
||||
<Separator />
|
||||
<SectionBase<Volunteer>
|
||||
id="volunteer"
|
||||
title={(item) => item.organization}
|
||||
description={(item) => item.position}
|
||||
/>
|
||||
<Separator />
|
||||
<SectionBase<Project>
|
||||
id="projects"
|
||||
title={(item) => item.name}
|
||||
@@ -162,13 +164,10 @@ export const LeftSidebar = () => {
|
||||
description={(item) => item.publisher}
|
||||
/>
|
||||
<Separator />
|
||||
<SectionBase<Skill>
|
||||
id="skills"
|
||||
title={(item) => item.name}
|
||||
description={(item) => {
|
||||
if (item.description) return item.description;
|
||||
if (item.keywords.length > 0) return `${item.keywords.length} keywords`;
|
||||
}}
|
||||
<SectionBase<Volunteer>
|
||||
id="volunteer"
|
||||
title={(item) => item.organization}
|
||||
description={(item) => item.position}
|
||||
/>
|
||||
<Separator />
|
||||
<SectionBase<Reference>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Aperture, UploadSimple } from "@phosphor-icons/react";
|
||||
import { Aperture, Trash, UploadSimple } from "@phosphor-icons/react";
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
buttonVariants,
|
||||
Input,
|
||||
@@ -11,8 +10,8 @@ import {
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@reactive-resume/ui";
|
||||
import { cn, getInitials } from "@reactive-resume/utils";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { motion } from "framer-motion";
|
||||
import { useMemo, useRef } from "react";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -23,10 +22,9 @@ import { PictureOptions } from "./options";
|
||||
|
||||
export const PictureSection = () => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const { uploadImage, loading } = useUploadImage();
|
||||
const { uploadImage } = useUploadImage();
|
||||
|
||||
const setValue = useResumeStore((state) => state.setValue);
|
||||
const name = useResumeStore((state) => state.resume.data.basics.name);
|
||||
const picture = useResumeStore((state) => state.resume.data.basics.picture);
|
||||
|
||||
const isValidUrl = useMemo(() => z.string().url().safeParse(picture.url).success, [picture.url]);
|
||||
@@ -41,16 +39,37 @@ export const PictureSection = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const onAvatarClick = () => {
|
||||
if (isValidUrl) {
|
||||
setValue("basics.picture.url", "");
|
||||
} else {
|
||||
inputRef.current?.click();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Avatar className="h-14 w-14">
|
||||
{isValidUrl && <AvatarImage src={picture.url} />}
|
||||
<AvatarFallback className="text-lg font-bold">{getInitials(name)}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="group relative cursor-pointer" onClick={onAvatarClick}>
|
||||
<Avatar className="h-14 w-14 bg-secondary">
|
||||
<AvatarImage src={picture.url} />
|
||||
</Avatar>
|
||||
|
||||
{isValidUrl ? (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center rounded-full bg-background/30 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<Trash size={16} weight="bold" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center rounded-full bg-background/30 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<UploadSimple size={16} weight="bold" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-col gap-y-1.5">
|
||||
<Label htmlFor="basics.picture.url">{t`Picture`}</Label>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<input hidden type="file" ref={inputRef} onChange={onSelectImage} />
|
||||
|
||||
<Input
|
||||
id="basics.picture.url"
|
||||
placeholder="https://..."
|
||||
@@ -58,44 +77,23 @@ export const PictureSection = () => {
|
||||
onChange={(event) => setValue("basics.picture.url", event.target.value)}
|
||||
/>
|
||||
|
||||
<AnimatePresence>
|
||||
{/* Show options button if picture exists */}
|
||||
{isValidUrl && (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<motion.button
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className={cn(buttonVariants({ size: "icon", variant: "ghost" }))}
|
||||
>
|
||||
<Aperture />
|
||||
</motion.button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[360px]">
|
||||
<PictureOptions />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
|
||||
{/* Show upload button if picture doesn't exist, else show remove button to delete picture */}
|
||||
{!isValidUrl && (
|
||||
<>
|
||||
<input hidden type="file" ref={inputRef} onChange={onSelectImage} />
|
||||
|
||||
{isValidUrl && (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<motion.button
|
||||
disabled={loading}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
onClick={() => inputRef.current?.click()}
|
||||
className={cn(buttonVariants({ size: "icon", variant: "ghost" }))}
|
||||
>
|
||||
<UploadSimple />
|
||||
<Aperture />
|
||||
</motion.button>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[360px]">
|
||||
<PictureOptions />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Button, HoverCard, HoverCardContent, HoverCardTrigger } from "@reactive-resume/ui";
|
||||
import { AspectRatio } from "@reactive-resume/ui";
|
||||
import { cn, templatesList } from "@reactive-resume/utils";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { useResumeStore } from "@/client/stores/resume";
|
||||
|
||||
@@ -19,31 +20,28 @@ export const TemplateSection = () => {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="grid grid-cols-2 gap-4">
|
||||
{templatesList.map(({ id, name }) => (
|
||||
<HoverCard key={id} openDelay={0} closeDelay={0}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setValue("metadata.template", id)}
|
||||
className={cn(
|
||||
"flex h-12 items-center justify-center overflow-hidden rounded border text-center text-sm capitalize ring-primary transition-colors hover:bg-secondary-accent focus:outline-none focus:ring-1 disabled:opacity-100",
|
||||
id === currentTemplate && "ring-1",
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</Button>
|
||||
</HoverCardTrigger>
|
||||
<main className="grid grid-cols-2 gap-5 @lg/right:grid-cols-3 @2xl/right:grid-cols-4">
|
||||
{templatesList.map((template, index) => (
|
||||
<AspectRatio ratio={1 / 1.4142}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -50 }}
|
||||
animate={{ opacity: 1, x: 0, transition: { delay: index * 0.1 } }}
|
||||
whileTap={{ scale: 0.98, transition: { duration: 0.1 } }}
|
||||
onClick={() => setValue("metadata.template", template)}
|
||||
className={cn(
|
||||
"relative cursor-pointer rounded-sm ring-primary transition-all hover:ring-2",
|
||||
currentTemplate === template && "ring-2",
|
||||
)}
|
||||
>
|
||||
<img src={`/templates/jpg/${template}.jpg`} alt={template} className="rounded-sm" />
|
||||
|
||||
<HoverCardContent className="max-w-xs overflow-hidden border-none bg-white p-0">
|
||||
<img
|
||||
alt={name}
|
||||
loading="lazy"
|
||||
src="/templates/sample.jpg"
|
||||
className="aspect-[1/1.4142]"
|
||||
/>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
<div className="absolute inset-x-0 bottom-0 h-32 w-full bg-gradient-to-b from-transparent to-background/80">
|
||||
<p className="absolute inset-x-0 bottom-2 text-center font-bold capitalize text-primary">
|
||||
{template}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AspectRatio>
|
||||
))}
|
||||
</main>
|
||||
</section>
|
||||
|
||||
@@ -137,7 +137,7 @@ export const TypographySection = () => {
|
||||
<Slider
|
||||
min={12}
|
||||
max={18}
|
||||
step={1}
|
||||
step={0.05}
|
||||
value={[typography.font.size]}
|
||||
onValueChange={(value) => {
|
||||
setValue("metadata.typography.font.size", value[0]);
|
||||
@@ -154,7 +154,7 @@ export const TypographySection = () => {
|
||||
<Slider
|
||||
min={0}
|
||||
max={3}
|
||||
step={0.25}
|
||||
step={0.05}
|
||||
value={[typography.lineHeight]}
|
||||
onValueChange={(value) => {
|
||||
setValue("metadata.typography.lineHeight", value[0]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { Check, DownloadSimple, Warning } from "@phosphor-icons/react";
|
||||
import { Check, DownloadSimple } from "@phosphor-icons/react";
|
||||
import {
|
||||
JsonResume,
|
||||
JsonResumeParser,
|
||||
@@ -141,7 +141,6 @@ export const ImportDialog = () => {
|
||||
|
||||
toast({
|
||||
variant: "error",
|
||||
icon: <Warning size={16} weight="bold" />,
|
||||
title: t`An error occurred while validating the file.`,
|
||||
});
|
||||
}
|
||||
@@ -186,7 +185,6 @@ export const ImportDialog = () => {
|
||||
} catch (error) {
|
||||
toast({
|
||||
variant: "error",
|
||||
icon: <Warning size={16} weight="bold" />,
|
||||
title: t`Oops, the server returned an error.`,
|
||||
description: importError?.message,
|
||||
});
|
||||
|
||||
@@ -6,9 +6,9 @@ import { FAQSection } from "./sections/faq";
|
||||
import { FeaturesSection } from "./sections/features";
|
||||
import { HeroSection } from "./sections/hero";
|
||||
import { LogoCloudSection } from "./sections/logo-cloud";
|
||||
import { SampleResumesSection } from "./sections/sample-resumes";
|
||||
import { StatisticsSection } from "./sections/statistics";
|
||||
import { SupportSection } from "./sections/support";
|
||||
import { TemplatesSection } from "./sections/templates";
|
||||
import { TestimonialsSection } from "./sections/testimonials";
|
||||
|
||||
export const HomePage = () => (
|
||||
@@ -23,7 +23,7 @@ export const HomePage = () => (
|
||||
<LogoCloudSection />
|
||||
<StatisticsSection />
|
||||
<FeaturesSection />
|
||||
<SampleResumesSection />
|
||||
<TemplatesSection />
|
||||
<TestimonialsSection />
|
||||
<SupportSection />
|
||||
<FAQSection />
|
||||
|
||||
@@ -1,35 +1,22 @@
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import { t } from "@lingui/macro";
|
||||
import { templatesList } from "@reactive-resume/utils";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const resumes = [
|
||||
"/sample-resumes/ditto",
|
||||
"/sample-resumes/ditto",
|
||||
"/sample-resumes/ditto",
|
||||
"/sample-resumes/ditto",
|
||||
];
|
||||
|
||||
export const SampleResumesSection = () => (
|
||||
export const TemplatesSection = () => (
|
||||
<section id="sample-resumes" className="relative py-24 sm:py-32">
|
||||
<div className="container flex flex-col gap-12 lg:min-h-[600px] lg:flex-row lg:items-start">
|
||||
<div className="space-y-4 lg:mt-16 lg:basis-96">
|
||||
<h2 className="text-4xl font-bold">{t`Sample Resumes`}</h2>
|
||||
<h2 className="text-4xl font-bold">{t`Templates`}</h2>
|
||||
|
||||
<Trans>
|
||||
<p className="leading-relaxed">
|
||||
Have a look at some of the resume created to showcase the templates available on
|
||||
Reactive Resume.
|
||||
</p>
|
||||
|
||||
<p className="leading-relaxed">
|
||||
They could also serve as examples to help guide the creation of your next resume.
|
||||
</p>
|
||||
</Trans>
|
||||
<p className="leading-relaxed">
|
||||
{t`Explore the templates available in Reactive Resume and view the resumes crafted with them. They could also serve as examples to help guide the creation of your next resume.`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full overflow-hidden lg:absolute lg:right-0 lg:max-w-[55%]">
|
||||
<motion.div
|
||||
animate={{
|
||||
x: [0, -400],
|
||||
x: [0, templatesList.length * 200 * -1],
|
||||
transition: {
|
||||
x: {
|
||||
duration: 30,
|
||||
@@ -40,20 +27,21 @@ export const SampleResumesSection = () => (
|
||||
}}
|
||||
className="flex items-center gap-x-6"
|
||||
>
|
||||
{resumes.map((resume, index) => (
|
||||
{templatesList.map((template, index) => (
|
||||
<motion.a
|
||||
key={index}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={`${resume}.pdf`}
|
||||
href={`templates/pdf/${template}.pdf`}
|
||||
className="max-w-none flex-none"
|
||||
viewport={{ once: true }}
|
||||
initial={{ opacity: 0, x: -100 }}
|
||||
whileInView={{ opacity: 1, x: 0, transition: { delay: (index + 1) * 0.5 } }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
>
|
||||
<img
|
||||
alt={resume}
|
||||
src={`${resume}.jpg`}
|
||||
alt={template}
|
||||
loading="lazy"
|
||||
src={`/templates/jpg/${template}.jpg`}
|
||||
className=" aspect-[1/1.4142] h-[400px] rounded object-cover lg:h-[600px]"
|
||||
/>
|
||||
</motion.a>
|
||||
@@ -1,7 +1,9 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { StatisticsDto, UrlDto } from "@reactive-resume/dto";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import { RESUME_KEY } from "@/client/constants/query-keys";
|
||||
import { toast } from "@/client/hooks/use-toast";
|
||||
import { axios } from "@/client/libs/axios";
|
||||
import { queryClient } from "@/client/libs/query-client";
|
||||
|
||||
@@ -24,6 +26,15 @@ export const usePrintResume = () => {
|
||||
return { ...cache, downloads: cache.downloads + 1 } satisfies StatisticsDto;
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
const message = error?.message;
|
||||
|
||||
toast({
|
||||
variant: "error",
|
||||
title: t`Oops, the server returned an error.`,
|
||||
description: message,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return { printResume: printResumeFn, loading, error };
|
||||
|
||||
@@ -40,7 +40,7 @@ export const configSchema = z.object({
|
||||
REDIS_URL: z.string().url().startsWith("redis://").optional(),
|
||||
|
||||
// Sentry
|
||||
SENTRY_DSN: z.string().url().startsWith("https://").optional(),
|
||||
VITE_SENTRY_DSN: z.string().url().startsWith("https://").optional(),
|
||||
|
||||
// Crowdin (Optional)
|
||||
CROWDIN_PROJECT_ID: z.coerce.number().optional(),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { HealthIndicator, HealthIndicatorResult } from "@nestjs/terminus";
|
||||
import { withTimeout } from "@reactive-resume/utils";
|
||||
|
||||
import { PrinterService } from "../printer/printer.service";
|
||||
|
||||
@@ -12,8 +11,7 @@ export class BrowserHealthIndicator extends HealthIndicator {
|
||||
|
||||
async isHealthy(): Promise<HealthIndicatorResult> {
|
||||
try {
|
||||
const version = await withTimeout(this.printerService.getVersion(), 5000);
|
||||
// const version = await this.printerService.getVersion();
|
||||
const version = await this.printerService.getVersion();
|
||||
|
||||
return this.getStatus("browser", true, { version });
|
||||
} catch (error) {
|
||||
|
||||
@@ -23,7 +23,7 @@ async function bootstrap() {
|
||||
|
||||
// Sentry
|
||||
// Error Reporting and Performance Monitoring
|
||||
const sentryDsn = configService.get("SENTRY_DSN");
|
||||
const sentryDsn = configService.get("VITE_SENTRY_DSN");
|
||||
|
||||
if (sentryDsn) {
|
||||
const express = app.getHttpAdapter().getInstance();
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Injectable } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import fontkit from "@pdf-lib/fontkit";
|
||||
import { ResumeDto } from "@reactive-resume/dto";
|
||||
import { getFontUrls, withTimeout } from "@reactive-resume/utils";
|
||||
import { getFontUrls } from "@reactive-resume/utils";
|
||||
import { ErrorMessage } from "@reactive-resume/utils";
|
||||
import retry from "async-retry";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
@@ -14,8 +14,6 @@ import { Config } from "../config/schema";
|
||||
import { StorageService } from "../storage/storage.service";
|
||||
import { UtilsService } from "../utils/utils.service";
|
||||
|
||||
const PRINTER_TIMEOUT = 10000; // 10 seconds
|
||||
|
||||
@Injectable()
|
||||
export class PrinterService {
|
||||
private readonly logger = new Logger(PrinterService.name);
|
||||
@@ -55,11 +53,11 @@ export class PrinterService {
|
||||
async () => {
|
||||
const start = performance.now();
|
||||
|
||||
const url = await retry(() => withTimeout(this.generateResume(resume), PRINTER_TIMEOUT), {
|
||||
retries: 2,
|
||||
const url = await retry(() => this.generateResume(resume), {
|
||||
retries: 3,
|
||||
randomize: true,
|
||||
onRetry: (_, attempt) => {
|
||||
this.logger.debug(`Print Resume: retry attempt #${attempt}`);
|
||||
this.logger.log(`Retrying to print resume #${resume.id}, attempt #${attempt}`);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -76,117 +74,138 @@ export class PrinterService {
|
||||
async printPreview(resume: ResumeDto) {
|
||||
return this.utils.getCachedOrSet(
|
||||
`user:${resume.userId}:storage:previews:${resume.id}`,
|
||||
async () => withTimeout(this.generatePreview(resume), PRINTER_TIMEOUT),
|
||||
async () => {
|
||||
const start = performance.now();
|
||||
|
||||
const url = await retry(() => this.generatePreview(resume), {
|
||||
retries: 3,
|
||||
randomize: true,
|
||||
onRetry: (_, attempt) => {
|
||||
this.logger.log(
|
||||
`Retrying to generate preview of resume #${resume.id}, attempt #${attempt}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const duration = Number(performance.now() - start).toFixed(0);
|
||||
|
||||
this.logger.debug(`Chrome took ${duration}ms to generate preview`);
|
||||
|
||||
return url;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async generateResume(resume: ResumeDto) {
|
||||
const browser = await this.getBrowser();
|
||||
const page = await browser.newPage();
|
||||
try {
|
||||
const browser = await this.getBrowser();
|
||||
const page = await browser.newPage();
|
||||
|
||||
let url = this.utils.getUrl();
|
||||
const publicUrl = this.configService.getOrThrow<string>("PUBLIC_URL");
|
||||
const storageUrl = this.configService.getOrThrow<string>("STORAGE_URL");
|
||||
let url = this.utils.getUrl();
|
||||
const publicUrl = this.configService.getOrThrow<string>("PUBLIC_URL");
|
||||
const storageUrl = this.configService.getOrThrow<string>("STORAGE_URL");
|
||||
|
||||
if ([publicUrl, storageUrl].some((url) => url.includes("localhost"))) {
|
||||
// Switch client URL from `localhost` to `host.docker.internal` in development
|
||||
// This is required because the browser is running in a container and the client is running on the host machine.
|
||||
url = url.replace("localhost", "host.docker.internal");
|
||||
if ([publicUrl, storageUrl].some((url) => url.includes("localhost"))) {
|
||||
// Switch client URL from `localhost` to `host.docker.internal` in development
|
||||
// This is required because the browser is running in a container and the client is running on the host machine.
|
||||
url = url.replace("localhost", "host.docker.internal");
|
||||
|
||||
await page.setRequestInterception(true);
|
||||
await page.setRequestInterception(true);
|
||||
|
||||
// Intercept requests of `localhost` to `host.docker.internal` in development
|
||||
page.on("request", (request) => {
|
||||
if (request.url().startsWith(storageUrl)) {
|
||||
const modifiedUrl = request.url().replace("localhost", `host.docker.internal`);
|
||||
// Intercept requests of `localhost` to `host.docker.internal` in development
|
||||
page.on("request", (request) => {
|
||||
if (request.url().startsWith(storageUrl)) {
|
||||
const modifiedUrl = request.url().replace("localhost", `host.docker.internal`);
|
||||
|
||||
request.continue({ url: modifiedUrl });
|
||||
} else {
|
||||
request.continue();
|
||||
}
|
||||
});
|
||||
request.continue({ url: modifiedUrl });
|
||||
} else {
|
||||
request.continue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set the data of the resume to be printed in the browser's session storage
|
||||
const numPages = resume.data.metadata.layout.length;
|
||||
|
||||
await page.evaluateOnNewDocument((data) => {
|
||||
window.localStorage.setItem("resume", JSON.stringify(data));
|
||||
}, resume.data);
|
||||
|
||||
await page.goto(`${url}/artboard/preview`, { waitUntil: "networkidle0" });
|
||||
|
||||
const pagesBuffer: Buffer[] = [];
|
||||
|
||||
const processPage = async (index: number) => {
|
||||
const pageElement = await page.$(`[data-page="${index}"]`);
|
||||
const width = (await (await pageElement?.getProperty("scrollWidth"))?.jsonValue()) ?? 0;
|
||||
const height = (await (await pageElement?.getProperty("scrollHeight"))?.jsonValue()) ?? 0;
|
||||
|
||||
const tempHtml = await page.evaluate((element: HTMLDivElement) => {
|
||||
const clonedElement = element.cloneNode(true) as HTMLDivElement;
|
||||
const tempHtml = document.body.innerHTML;
|
||||
document.body.innerHTML = `${clonedElement.outerHTML}`;
|
||||
return tempHtml;
|
||||
}, pageElement);
|
||||
|
||||
pagesBuffer.push(await page.pdf({ width, height, printBackground: true }));
|
||||
|
||||
await page.evaluate((tempHtml: string) => {
|
||||
document.body.innerHTML = tempHtml;
|
||||
}, tempHtml);
|
||||
};
|
||||
|
||||
// Loop through all the pages and print them, by first displaying them, printing the PDF and then hiding them back
|
||||
for (let index = 1; index <= numPages; index++) {
|
||||
await processPage(index);
|
||||
}
|
||||
|
||||
// Using 'pdf-lib', merge all the pages from their buffers into a single PDF
|
||||
const pdf = await PDFDocument.create();
|
||||
pdf.registerFontkit(fontkit);
|
||||
|
||||
// Get information about fonts used in the resume from the metadata
|
||||
const fontData = resume.data.metadata.typography.font;
|
||||
const fontUrls = getFontUrls(fontData.family, fontData.variants);
|
||||
|
||||
// Load all the fonts from the URLs using HttpService
|
||||
const responses = await Promise.all(
|
||||
fontUrls.map((url) =>
|
||||
this.httpService.axiosRef.get(url, {
|
||||
responseType: "arraybuffer",
|
||||
}),
|
||||
),
|
||||
);
|
||||
const fontsBuffer = responses.map((response) => response.data as ArrayBuffer);
|
||||
|
||||
// Embed all the fonts in the PDF
|
||||
await Promise.all(fontsBuffer.map((buffer) => pdf.embedFont(buffer)));
|
||||
|
||||
for (let index = 0; index < pagesBuffer.length; index++) {
|
||||
const page = await PDFDocument.load(pagesBuffer[index]);
|
||||
const [copiedPage] = await pdf.copyPages(page, [0]);
|
||||
pdf.addPage(copiedPage);
|
||||
}
|
||||
|
||||
// Save the PDF to storage and return the URL to download the resume
|
||||
// Store the URL in cache for future requests, under the previously generated hash digest
|
||||
const buffer = Buffer.from(await pdf.save());
|
||||
|
||||
// This step will also save the resume URL in cache
|
||||
const resumeUrl = await this.storageService.uploadObject(
|
||||
resume.userId,
|
||||
"resumes",
|
||||
buffer,
|
||||
resume.id,
|
||||
);
|
||||
|
||||
// Close all the pages and disconnect from the browser
|
||||
await page.close();
|
||||
browser.disconnect();
|
||||
|
||||
return resumeUrl;
|
||||
} catch (error) {
|
||||
console.trace(error);
|
||||
}
|
||||
|
||||
// Set the data of the resume to be printed in the browser's session storage
|
||||
const numPages = resume.data.metadata.layout.length;
|
||||
|
||||
await page.evaluateOnNewDocument((data) => {
|
||||
window.localStorage.setItem("resume", JSON.stringify(data));
|
||||
}, resume.data);
|
||||
|
||||
await page.goto(`${url}/artboard/preview`, { waitUntil: "networkidle0" });
|
||||
|
||||
const pagesBuffer: Buffer[] = [];
|
||||
|
||||
const processPage = async (index: number) => {
|
||||
const pageElement = await page.$(`[data-page="${index}"]`);
|
||||
const width = (await (await pageElement?.getProperty("scrollWidth"))?.jsonValue()) ?? 0;
|
||||
const height = (await (await pageElement?.getProperty("scrollHeight"))?.jsonValue()) ?? 0;
|
||||
|
||||
const tempHtml = await page.evaluate((element: HTMLDivElement) => {
|
||||
const clonedElement = element.cloneNode(true) as HTMLDivElement;
|
||||
const tempHtml = document.body.innerHTML;
|
||||
document.body.innerHTML = `${clonedElement.outerHTML}`;
|
||||
return tempHtml;
|
||||
}, pageElement);
|
||||
|
||||
pagesBuffer.push(await page.pdf({ width, height, printBackground: true }));
|
||||
|
||||
await page.evaluate((tempHtml: string) => {
|
||||
document.body.innerHTML = tempHtml;
|
||||
}, tempHtml);
|
||||
};
|
||||
|
||||
// Loop through all the pages and print them, by first displaying them, printing the PDF and then hiding them back
|
||||
for (let index = 1; index <= numPages; index++) {
|
||||
await processPage(index);
|
||||
}
|
||||
|
||||
// Using 'pdf-lib', merge all the pages from their buffers into a single PDF
|
||||
const pdf = await PDFDocument.create();
|
||||
pdf.registerFontkit(fontkit);
|
||||
|
||||
// Get information about fonts used in the resume from the metadata
|
||||
const fontData = resume.data.metadata.typography.font;
|
||||
|
||||
const fontUrls = getFontUrls(fontData.family, fontData.variants);
|
||||
|
||||
// Load all the fonts from the URLs using HttpService
|
||||
const responses = await Promise.all(
|
||||
fontUrls.map((url) =>
|
||||
this.httpService.axiosRef.get(url, {
|
||||
responseType: "arraybuffer",
|
||||
}),
|
||||
),
|
||||
);
|
||||
const fontsBuffer = responses.map((response) => response.data as ArrayBuffer);
|
||||
|
||||
// Embed all the fonts in the PDF
|
||||
await Promise.all(fontsBuffer.map((buffer) => pdf.embedFont(buffer)));
|
||||
|
||||
for (let index = 0; index < pagesBuffer.length; index++) {
|
||||
const page = await PDFDocument.load(pagesBuffer[index]);
|
||||
const copiedPage = await pdf.copyPages(page, [0]);
|
||||
pdf.addPage(copiedPage[0]);
|
||||
}
|
||||
|
||||
// Save the PDF to storage and return the URL to download the resume
|
||||
// Store the URL in cache for future requests, under the previously generated hash digest
|
||||
const buffer = Buffer.from(await pdf.save());
|
||||
|
||||
// This step will also save the resume URL in cache
|
||||
const resumeUrl = await this.storageService.uploadObject(
|
||||
resume.userId,
|
||||
"resumes",
|
||||
buffer,
|
||||
resume.id,
|
||||
);
|
||||
|
||||
// Close all the pages and disconnect from the browser
|
||||
await page.close();
|
||||
browser.disconnect();
|
||||
|
||||
return resumeUrl;
|
||||
}
|
||||
|
||||
async generatePreview(resume: ResumeDto) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { CreateResumeDto, ImportResumeDto, ResumeDto, UpdateResumeDto } from "@reactive-resume/dto";
|
||||
import { defaultResumeData, ResumeData } from "@reactive-resume/schema";
|
||||
@@ -18,7 +18,6 @@ import { UtilsService } from "../utils/utils.service";
|
||||
@Injectable()
|
||||
export class ResumeService {
|
||||
private readonly redis: Redis;
|
||||
private readonly logger = new Logger(ResumeService.name);
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
|
||||