mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-14 23:07:01 +10:00
lot of bugfixes, better migration script
This commit is contained in:
@@ -15,12 +15,7 @@ export function GithubStarsButton() {
|
||||
|
||||
return (
|
||||
<Button asChild variant="outline">
|
||||
<a
|
||||
href="https://github.com/amruthpillai/reactive-resume"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<a target="_blank" href="https://github.com/amruthpillai/reactive-resume" aria-label={ariaLabel} rel="noopener">
|
||||
<GithubLogoIcon aria-hidden="true" />
|
||||
{starCount != null ? (
|
||||
<CountUp to={starCount} duration={0.5} separator="," className="font-bold" aria-hidden="true" />
|
||||
|
||||
@@ -269,11 +269,7 @@ function EditorToolbar({ editor }: { editor: Editor }) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.editor
|
||||
.chain()
|
||||
.focus()
|
||||
.setLink({ href: url, target: "_blank", rel: "noopener noreferrer nofollow" })
|
||||
.run();
|
||||
ctx.editor.chain().focus().setLink({ href: url, target: "_blank", rel: "noopener nofollow" }).run();
|
||||
},
|
||||
unsetLink: () => ctx.editor.chain().focus().unsetLink().run(),
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ export function getSectionComponent(
|
||||
style={{ gridTemplateColumns: `repeat(${customSection.columns}, 1fr)` }}
|
||||
>
|
||||
{visibleItems.map((item) => (
|
||||
<div key={item.id} className={cn(`section-item section-item-${customSection.type} *:space-y-1`)}>
|
||||
<div key={item.id} className={cn(`section-item section-item-${customSection.type}`)}>
|
||||
{renderItemByType(customSection.type, item, itemClassName)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { TiptapContent } from "@/components/input/rich-input";
|
||||
import type { SectionItem } from "@/schema/resume/data";
|
||||
import { cn } from "@/utils/style";
|
||||
import { PageLink } from "../page-link";
|
||||
|
||||
type ReferencesItemProps = SectionItem<"references"> & {
|
||||
className?: string;
|
||||
@@ -9,12 +10,21 @@ type ReferencesItemProps = SectionItem<"references"> & {
|
||||
export function ReferencesItem({ className, ...item }: ReferencesItemProps) {
|
||||
return (
|
||||
<div className={cn("references-item", className)}>
|
||||
<p className="section-item-name references-item-name">
|
||||
<strong>{item.name}</strong>
|
||||
</p>
|
||||
<div className="section-item-header references-item-header">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="section-item-name references-item-name">
|
||||
<strong>{item.name}</strong>
|
||||
</p>
|
||||
</div>
|
||||
<p className="section-item-subtitle references-item-position">{item.position}</p>
|
||||
</div>
|
||||
<div className="section-item-description references-item-description">
|
||||
<TiptapContent content={item.description} />
|
||||
</div>
|
||||
<div className="section-item-footer references-item-footer flex flex-col">
|
||||
<div className="section-item-metadata references-item-phone">{item.phone}</div>
|
||||
<PageLink {...item.website} label={item.website.label} className="section-item-link references-item-link" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { LevelDisplay } from "@/components/level/display";
|
||||
import { cn } from "@/utils/style";
|
||||
import { useResumeStore } from "../store/resume";
|
||||
|
||||
type Props = React.ComponentProps<"div"> & {
|
||||
@@ -8,5 +9,5 @@ type Props = React.ComponentProps<"div"> & {
|
||||
export function PageLevel({ level, className, ...props }: Props) {
|
||||
const { icon, type } = useResumeStore((state) => state.resume.data.metadata.design.level);
|
||||
|
||||
return <LevelDisplay icon={icon} type={type} level={level} className={className} {...props} />;
|
||||
return <LevelDisplay icon={icon} type={type} level={level} className={cn("h-4", className)} {...props} />;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export function PageLink({ url, label, className }: Props) {
|
||||
if (!url) return null;
|
||||
|
||||
return (
|
||||
<a href={url} target="_blank" rel="noopener noreferrer" className={cn("inline-block text-wrap", className)}>
|
||||
<a href={url} target="_blank" rel="noopener" className={cn("inline-block text-wrap", className)}>
|
||||
{label || url}
|
||||
</a>
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ export function LaprasTemplate({ pageIndex, pageLayout }: TemplateProps) {
|
||||
"--heading-negative-margin": `${headingNegativeMargin}pt`,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className="template-lapras page-content space-y-7 px-(--page-margin-x) py-(--page-margin-y)"
|
||||
className="template-lapras page-content space-y-7 px-(--page-margin-x) py-(--page-margin-y) print:p-0"
|
||||
>
|
||||
{isFirstPage && <Header />}
|
||||
|
||||
|
||||
@@ -30,7 +30,11 @@ export function PikachuTemplate({ pageIndex, pageLayout }: TemplateProps) {
|
||||
data-layout="sidebar"
|
||||
className="group page-sidebar flex w-(--page-sidebar-width) shrink-0 flex-col space-y-3"
|
||||
>
|
||||
{isFirstPage && <PagePicture className="max-h-full! max-w-full!" />}
|
||||
{isFirstPage && (
|
||||
<div className="flex items-center justify-center">
|
||||
<PagePicture />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!fullWidth && (
|
||||
<div className="shrink-0 space-y-4 overflow-x-hidden">
|
||||
|
||||
@@ -22,6 +22,7 @@ const localFontList = [
|
||||
{ type: "local", category: "serif", family: "Palatino", weights: ["400", "600", "700"] },
|
||||
{ type: "local", category: "serif", family: "Times New Roman", weights: ["400", "600", "700"] },
|
||||
] as LocalFont[];
|
||||
|
||||
const webFontList = webFontListJSON as WebFont[];
|
||||
|
||||
function buildWebFontMap() {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -9,7 +9,7 @@ export function Copyright({ className, ...props }: Props) {
|
||||
<p>
|
||||
<Trans>
|
||||
Licensed under{" "}
|
||||
<a href="#" target="_blank" rel="noopener noreferrer" className="font-medium underline underline-offset-2">
|
||||
<a href="#" target="_blank" rel="noopener" className="font-medium underline underline-offset-2">
|
||||
MIT
|
||||
</a>
|
||||
.
|
||||
@@ -25,7 +25,7 @@ export function Copyright({ className, ...props }: Props) {
|
||||
A passion project by{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="noopener"
|
||||
href="https://amruthpillai.com"
|
||||
className="font-medium underline underline-offset-2"
|
||||
>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { PencilSimpleLineIcon, PlusIcon } from "@phosphor-icons/react";
|
||||
import { useForm, useFormContext } from "react-hook-form";
|
||||
import type z from "zod";
|
||||
import { RichInput } from "@/components/input/rich-input";
|
||||
import { URLInput } from "@/components/input/url-input";
|
||||
import { useResumeStore } from "@/components/resume/store/resume";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
@@ -28,6 +29,9 @@ export function CreateReferenceDialog({ data }: DialogProps<"resume.sections.ref
|
||||
id: generateId(),
|
||||
hidden: data?.item?.hidden ?? false,
|
||||
name: data?.item?.name ?? "",
|
||||
position: data?.item?.position ?? "",
|
||||
website: data?.item?.website ?? { url: "", label: "" },
|
||||
phone: data?.item?.phone ?? "",
|
||||
description: data?.item?.description ?? "",
|
||||
},
|
||||
});
|
||||
@@ -83,6 +87,9 @@ export function UpdateReferenceDialog({ data }: DialogProps<"resume.sections.ref
|
||||
id: data.item.id,
|
||||
hidden: data.item.hidden,
|
||||
name: data.item.name,
|
||||
position: data.item.position,
|
||||
website: data.item.website,
|
||||
phone: data.item.phone,
|
||||
description: data.item.description,
|
||||
},
|
||||
});
|
||||
@@ -140,7 +147,7 @@ function ReferenceForm() {
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem className="sm:col-span-full">
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Name</Trans>
|
||||
</FormLabel>
|
||||
@@ -152,6 +159,54 @@ function ReferenceForm() {
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="position"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Position</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="phone"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Phone</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="website"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Website</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} value={field.value} onChange={field.onChange} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="description"
|
||||
|
||||
@@ -584,6 +584,9 @@ export class JSONResumeImporter {
|
||||
id: generateId(),
|
||||
hidden: false,
|
||||
name: ref.name || "",
|
||||
position: "",
|
||||
website: { url: "", label: "" },
|
||||
phone: "",
|
||||
description: ref.reference ? `<p>${ref.reference}</p>` : "",
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/** biome-ignore-all lint/style/noNonNullAssertion: only used in places where we know the value is not null */
|
||||
|
||||
import z, { flattenError, ZodError } from "zod";
|
||||
import { type ResumeData, resumeDataSchema } from "@/schema/resume/data";
|
||||
import { type Template, templateSchema } from "@/schema/templates";
|
||||
@@ -122,7 +124,6 @@ type V4ResumeData = {
|
||||
items: Array<{
|
||||
id: string;
|
||||
visible: boolean;
|
||||
name?: string;
|
||||
title?: string;
|
||||
awarder?: string;
|
||||
date?: string;
|
||||
@@ -433,16 +434,16 @@ export class ReactiveResumeV4JSONImporter {
|
||||
},
|
||||
sections: {
|
||||
profiles: {
|
||||
title: (v4Data.sections.profiles?.name ?? "").trim() || "Profiles",
|
||||
title: v4Data.sections.profiles?.name ?? "",
|
||||
columns: v4Data.sections.profiles?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.profiles?.visible ?? true),
|
||||
items: (v4Data.sections.profiles?.items ?? [])
|
||||
.filter((item) => item.network && item.network.trim().length > 0)
|
||||
.filter((item) => item.network && item.network.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
icon: item.icon ?? "",
|
||||
network: item.network?.trim() || "Other",
|
||||
network: item.network!,
|
||||
username: item.username ?? "",
|
||||
website: {
|
||||
url: item.url?.href ?? "",
|
||||
@@ -451,15 +452,15 @@ export class ReactiveResumeV4JSONImporter {
|
||||
})),
|
||||
},
|
||||
experience: {
|
||||
title: (v4Data.sections.experience?.name ?? "").trim() || "Experience",
|
||||
title: v4Data.sections.experience?.name ?? "",
|
||||
columns: v4Data.sections.experience?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.experience?.visible ?? true),
|
||||
items: (v4Data.sections.experience?.items ?? [])
|
||||
.filter((item) => item.company && item.company.trim().length > 0)
|
||||
.filter((item) => item.company && item.company.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
company: item.company?.trim() || "Company",
|
||||
company: item.company!,
|
||||
position: item.position ?? "",
|
||||
location: item.location ?? "",
|
||||
period: item.date ?? "",
|
||||
@@ -471,15 +472,15 @@ export class ReactiveResumeV4JSONImporter {
|
||||
})),
|
||||
},
|
||||
education: {
|
||||
title: (v4Data.sections.education?.name ?? "").trim() || "Education",
|
||||
title: v4Data.sections.education?.name ?? "",
|
||||
columns: v4Data.sections.education?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.education?.visible ?? true),
|
||||
items: (v4Data.sections.education?.items ?? [])
|
||||
.filter((item) => item.institution && item.institution.trim().length > 0)
|
||||
.filter((item) => item.institution && item.institution.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
school: item.institution?.trim() || "School",
|
||||
school: item.institution!,
|
||||
degree: item.studyType ?? "",
|
||||
area: item.area ?? "",
|
||||
grade: item.score ?? "",
|
||||
@@ -493,15 +494,15 @@ export class ReactiveResumeV4JSONImporter {
|
||||
})),
|
||||
},
|
||||
projects: {
|
||||
title: (v4Data.sections.projects?.name ?? "").trim() || "Projects",
|
||||
title: v4Data.sections.projects?.name ?? "",
|
||||
columns: v4Data.sections.projects?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.projects?.visible ?? true),
|
||||
items: (v4Data.sections.projects?.items ?? [])
|
||||
.filter((item) => item.name && item.name.trim().length > 0)
|
||||
.filter((item) => item.name && item.name.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
name: item.name?.trim() || "Project",
|
||||
name: item.name!,
|
||||
period: item.date ?? "",
|
||||
website: {
|
||||
url: item.url?.href ?? "",
|
||||
@@ -511,59 +512,59 @@ export class ReactiveResumeV4JSONImporter {
|
||||
})),
|
||||
},
|
||||
skills: {
|
||||
title: (v4Data.sections.skills?.name ?? "").trim() || "Skills",
|
||||
title: v4Data.sections.skills?.name ?? "",
|
||||
columns: v4Data.sections.skills?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.skills?.visible ?? true),
|
||||
items: (v4Data.sections.skills?.items ?? [])
|
||||
.filter((item) => item.name && item.name.trim().length > 0)
|
||||
.filter((item) => item.name && item.name.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
icon: "",
|
||||
name: item.name?.trim() || "Skill",
|
||||
name: item.name!,
|
||||
proficiency: item.description ?? "",
|
||||
level: clampLevel(item.level ?? 0),
|
||||
keywords: item.keywords ?? [],
|
||||
})),
|
||||
},
|
||||
languages: {
|
||||
title: (v4Data.sections.languages?.name ?? "").trim() || "Languages",
|
||||
title: v4Data.sections.languages?.name ?? "",
|
||||
columns: v4Data.sections.languages?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.languages?.visible ?? true),
|
||||
items: (v4Data.sections.languages?.items ?? [])
|
||||
.filter((item) => item.language && item.language.trim().length > 0)
|
||||
.filter((item) => item.language && item.language.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
language: item.language?.trim() || "Language",
|
||||
language: item.language!,
|
||||
fluency: item.fluency ?? "",
|
||||
level: clampLevel(item.level ?? 0),
|
||||
})),
|
||||
},
|
||||
interests: {
|
||||
title: (v4Data.sections.interests?.name ?? "").trim() || "Interests",
|
||||
title: v4Data.sections.interests?.name ?? "",
|
||||
columns: v4Data.sections.interests?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.interests?.visible ?? true),
|
||||
items: (v4Data.sections.interests?.items ?? [])
|
||||
.filter((item) => item.name && item.name.trim().length > 0)
|
||||
.filter((item) => item.name && item.name.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
icon: "",
|
||||
name: item.name?.trim() || "Interest",
|
||||
name: item.name!,
|
||||
keywords: item.keywords ?? [],
|
||||
})),
|
||||
},
|
||||
awards: {
|
||||
title: (v4Data.sections.awards?.name ?? "").trim() || "Awards",
|
||||
title: v4Data.sections.awards?.name ?? "",
|
||||
columns: v4Data.sections.awards?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.awards?.visible ?? true),
|
||||
items: (v4Data.sections.awards?.items ?? [])
|
||||
.filter((item) => (item.title || item.name) && (item.title?.trim() || item.name?.trim() || "").length > 0)
|
||||
.filter((item) => item.title && item.title.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
title: (item.title || item.name)?.trim() || "Award",
|
||||
title: item.title!,
|
||||
awarder: item.awarder ?? "",
|
||||
date: item.date ?? "",
|
||||
website: {
|
||||
@@ -574,15 +575,15 @@ export class ReactiveResumeV4JSONImporter {
|
||||
})),
|
||||
},
|
||||
certifications: {
|
||||
title: (v4Data.sections.certifications?.name ?? "").trim() || "Certifications",
|
||||
title: v4Data.sections.certifications?.name ?? "",
|
||||
columns: v4Data.sections.certifications?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.certifications?.visible ?? true),
|
||||
items: (v4Data.sections.certifications?.items ?? [])
|
||||
.filter((item) => item.name && item.name.trim().length > 0)
|
||||
.filter((item) => item.name && item.name.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
title: item.name?.trim() || "Certification",
|
||||
title: item.name!,
|
||||
issuer: item.issuer ?? "",
|
||||
date: item.date ?? "",
|
||||
website: {
|
||||
@@ -593,15 +594,15 @@ export class ReactiveResumeV4JSONImporter {
|
||||
})),
|
||||
},
|
||||
publications: {
|
||||
title: (v4Data.sections.publications?.name ?? "").trim() || "Publications",
|
||||
title: v4Data.sections.publications?.name ?? "",
|
||||
columns: v4Data.sections.publications?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.publications?.visible ?? true),
|
||||
items: (v4Data.sections.publications?.items ?? [])
|
||||
.filter((item) => item.name && item.name.trim().length > 0)
|
||||
.filter((item) => item.name && item.name.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
title: item.name?.trim() || "Publication",
|
||||
title: item.name!,
|
||||
publisher: item.publisher ?? "",
|
||||
date: item.date ?? "",
|
||||
website: {
|
||||
@@ -612,15 +613,15 @@ export class ReactiveResumeV4JSONImporter {
|
||||
})),
|
||||
},
|
||||
volunteer: {
|
||||
title: (v4Data.sections.volunteer?.name ?? "").trim() || "Volunteering",
|
||||
title: v4Data.sections.volunteer?.name ?? "",
|
||||
columns: v4Data.sections.volunteer?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.volunteer?.visible ?? true),
|
||||
items: (v4Data.sections.volunteer?.items ?? [])
|
||||
.filter((item) => item.organization && item.organization.trim().length > 0)
|
||||
.filter((item) => item.organization && item.organization.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
organization: item.organization?.trim() || "Organization",
|
||||
organization: item.organization!,
|
||||
location: item.location ?? "",
|
||||
period: item.date ?? "",
|
||||
website: {
|
||||
@@ -631,16 +632,22 @@ export class ReactiveResumeV4JSONImporter {
|
||||
})),
|
||||
},
|
||||
references: {
|
||||
title: (v4Data.sections.references?.name ?? "").trim() || "References",
|
||||
title: v4Data.sections.references?.name ?? "",
|
||||
columns: v4Data.sections.references?.columns ?? 1,
|
||||
hidden: !(v4Data.sections.references?.visible ?? true),
|
||||
items: (v4Data.sections.references?.items ?? [])
|
||||
.filter((item) => item.name && item.name.trim().length > 0)
|
||||
.filter((item) => item.name && item.name.length > 0)
|
||||
.map((item) => ({
|
||||
id: item.id ?? generateId(),
|
||||
hidden: !(item.visible ?? true),
|
||||
name: item.name?.trim() || "Reference",
|
||||
description: item.summary ?? item.description ?? "",
|
||||
name: item.name!,
|
||||
position: item.description ?? "",
|
||||
phone: "",
|
||||
website: {
|
||||
url: item.url?.href ?? "",
|
||||
label: item.url?.label ?? "",
|
||||
},
|
||||
description: item.summary ?? "",
|
||||
})),
|
||||
},
|
||||
},
|
||||
@@ -652,10 +659,10 @@ export class ReactiveResumeV4JSONImporter {
|
||||
hidden: !(section.visible ?? true),
|
||||
items: section.items
|
||||
.filter((item) => item.visible !== false)
|
||||
.map((item) => ({
|
||||
.map((item, index) => ({
|
||||
id: item.id || generateId(),
|
||||
hidden: !item.visible,
|
||||
company: item.name?.trim() || "",
|
||||
company: item.name?.trim() || `#${index + 1}`,
|
||||
position: item.description ?? "",
|
||||
location: item.location ?? "",
|
||||
period: item.date ?? "",
|
||||
|
||||
@@ -205,7 +205,7 @@ export const DonationBanner = () => (
|
||||
transition={{ duration: 0.6, delay: 0.6 }}
|
||||
>
|
||||
<Button asChild size="lg" className="h-11 gap-2 px-6">
|
||||
<a href="https://opencollective.com/reactive-resume" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://opencollective.com/reactive-resume" target="_blank" rel="noopener">
|
||||
<HeartIcon aria-hidden="true" weight="fill" className="text-rose-400 dark:text-rose-600" />
|
||||
Open Collective
|
||||
<span className="sr-only"> ({t`opens in new tab`})</span>
|
||||
@@ -213,7 +213,7 @@ export const DonationBanner = () => (
|
||||
</Button>
|
||||
|
||||
<Button asChild size="lg" className="h-11 gap-2 px-6">
|
||||
<a href="https://github.com/sponsors/AmruthPillai" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://github.com/sponsors/AmruthPillai" target="_blank" rel="noopener">
|
||||
<GithubLogoIcon aria-hidden="true" weight="fill" className="text-zinc-400 dark:text-zinc-600" />
|
||||
GitHub Sponsors
|
||||
<span className="sr-only"> ({t`opens in new tab`})</span>
|
||||
|
||||
@@ -36,7 +36,7 @@ const getFaqItems = (): FAQItemData[] => [
|
||||
<a
|
||||
href={crowdinUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="noopener"
|
||||
className={buttonVariants({ variant: "link", className: "h-auto px-0!" })}
|
||||
>
|
||||
contribute to the translations on Crowdin
|
||||
|
||||
@@ -76,7 +76,7 @@ export function Footer() {
|
||||
<a
|
||||
href={social.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="noopener"
|
||||
aria-label={`${social.label} (${t`opens in new tab`})`}
|
||||
>
|
||||
<social.icon aria-hidden="true" size={18} />
|
||||
@@ -123,7 +123,7 @@ function FooterLink({ url, label }: FooterLinkItem) {
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="noopener"
|
||||
className="relative inline-block text-sm transition-colors hover:text-foreground"
|
||||
>
|
||||
{label}
|
||||
|
||||
@@ -48,7 +48,7 @@ export function Hero() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.8 }}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="noopener"
|
||||
href="https://docs.rxresu.me/getting-started"
|
||||
>
|
||||
<Badge variant="secondary" className="h-auto gap-1.5 px-3 py-0.5">
|
||||
@@ -104,7 +104,7 @@ export function Hero() {
|
||||
</Button>
|
||||
|
||||
<Button asChild size="lg" variant="ghost" className="gap-2 px-4">
|
||||
<a href="https://docs.rxresu.me" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://docs.rxresu.me" target="_blank" rel="noopener">
|
||||
<BookIcon aria-hidden="true" className="size-4" />
|
||||
<Trans>Learn More</Trans>
|
||||
<span className="sr-only">
|
||||
|
||||
@@ -28,7 +28,7 @@ export function ProductHuntBanner() {
|
||||
return (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="noopener"
|
||||
className="flex h-8 items-center justify-center bg-secondary text-center font-medium text-[0.85rem] text-secondary-foreground tracking-tight underline-offset-2 hover:underline"
|
||||
href="https://www.producthunt.com/products/reactive-resume/launches/reactive-resume-v5-2?embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-reactive-resume-v5-2"
|
||||
>
|
||||
|
||||
@@ -103,7 +103,7 @@ export function Testimonials() {
|
||||
<a
|
||||
href={`mailto:${email}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
rel="noopener"
|
||||
className="font-medium text-foreground underline underline-offset-2 transition-colors hover:text-primary"
|
||||
>
|
||||
{email}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
import {
|
||||
ArrowsClockwiseIcon,
|
||||
CircleNotchIcon,
|
||||
CubeFocusIcon,
|
||||
FileJsIcon,
|
||||
@@ -28,7 +27,7 @@ export function BuilderDock() {
|
||||
const [_, copyToClipboard] = useCopyToClipboard();
|
||||
const { data: session } = authClient.useSession();
|
||||
const params = useParams({ from: "/builder/$resumeId" });
|
||||
const { zoomIn, zoomOut, resetTransform, centerView } = useControls();
|
||||
const { zoomIn, zoomOut, centerView } = useControls();
|
||||
|
||||
const { data: resume } = useQuery(orpc.resume.getById.queryOptions({ input: { id: params.resumeId } }));
|
||||
const { mutateAsync: printResumeAsPDF, isPending: isPrinting } = useMutation(
|
||||
@@ -40,11 +39,6 @@ export function BuilderDock() {
|
||||
return `${window.location.origin}/${session.user.username}/${resume.slug}`;
|
||||
}, [session, resume]);
|
||||
|
||||
const onReset = useCallback(() => {
|
||||
resetTransform();
|
||||
centerView();
|
||||
}, [resetTransform, centerView]);
|
||||
|
||||
const onCopyUrl = useCallback(async () => {
|
||||
await copyToClipboard(publicUrl);
|
||||
toast.success(t`A link to your resume has been copied to clipboard.`);
|
||||
@@ -79,7 +73,6 @@ export function BuilderDock() {
|
||||
>
|
||||
<DockIcon icon={MagnifyingGlassPlusIcon} title={t`Zoom in`} onClick={() => zoomIn(0.1)} />
|
||||
<DockIcon icon={MagnifyingGlassMinusIcon} title={t`Zoom out`} onClick={() => zoomOut(0.1)} />
|
||||
<DockIcon icon={ArrowsClockwiseIcon} title={t`Reset zoom`} onClick={() => onReset()} />
|
||||
<DockIcon icon={CubeFocusIcon} title={t`Center view`} onClick={() => centerView()} />
|
||||
<div className="mx-1 h-8 w-px bg-border" />
|
||||
<DockIcon icon={LinkSimpleIcon} title={t`Copy URL`} onClick={() => onCopyUrl()} />
|
||||
|
||||
@@ -25,7 +25,7 @@ export function InformationSectionBuilder() {
|
||||
</div>
|
||||
|
||||
<Button asChild size="sm" variant="default" className="mt-2 whitespace-normal px-4! text-xs">
|
||||
<a href="http://opencollective.com/reactive-resume" target="_blank" rel="noopener noreferrer">
|
||||
<a href="http://opencollective.com/reactive-resume" target="_blank" rel="noopener">
|
||||
<HandHeartIcon />
|
||||
<span className="truncate">
|
||||
<Trans>Donate to Reactive Resume</Trans>
|
||||
@@ -36,31 +36,31 @@ export function InformationSectionBuilder() {
|
||||
|
||||
<div className="flex flex-wrap gap-0.5">
|
||||
<Button asChild size="sm" variant="link" className="text-xs">
|
||||
<a href="https://docs.rxresu.me" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://docs.rxresu.me" target="_blank" rel="noopener">
|
||||
<Trans>Documentation</Trans>
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
<Button asChild size="sm" variant="link" className="text-xs">
|
||||
<a href="https://github.com/amruthpillai/reactive-resume" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://github.com/amruthpillai/reactive-resume" target="_blank" rel="noopener">
|
||||
<Trans>Source Code</Trans>
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
<Button asChild size="sm" variant="link" className="text-xs">
|
||||
<a href="https://github.com/amruthpillai/reactive-resume/issues" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://github.com/amruthpillai/reactive-resume/issues" target="_blank" rel="noopener">
|
||||
<Trans>Report a Bug</Trans>
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
<Button asChild size="sm" variant="link" className="text-xs">
|
||||
<a href="https://crowdin.com/project/reactive-resume" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://crowdin.com/project/reactive-resume" target="_blank" rel="noopener">
|
||||
<Trans>Translations</Trans>
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
<Button asChild size="sm" variant="link" className="text-xs">
|
||||
<a href="https://opencollective.com/reactive-resume" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://opencollective.com/reactive-resume" target="_blank" rel="noopener">
|
||||
<Trans>Sponsors</Trans>
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
@@ -30,8 +30,8 @@ function TemplateSectionForm() {
|
||||
<div className="flex @md:flex-row flex-col items-stretch gap-x-4 gap-y-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="group/preview relative h-auto w-40 shrink-0 cursor-pointer p-0"
|
||||
onClick={onOpenTemplateGallery}
|
||||
className="group/preview relative h-auto w-40 shrink-0 cursor-pointer p-0"
|
||||
>
|
||||
<div className="relative z-10 aspect-page size-full overflow-hidden rounded-md opacity-100 transition-opacity group-hover/preview:opacity-50">
|
||||
<img src={metadata.imageUrl} alt={metadata.name} className="size-full object-cover" />
|
||||
@@ -48,9 +48,9 @@ function TemplateSectionForm() {
|
||||
<p className="text-muted-foreground text-sm">{i18n.t(metadata.description)}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<div className="flex flex-wrap gap-2.5">
|
||||
{metadata.tags.map((tag) => (
|
||||
<Badge key={tag} variant="outline">
|
||||
<Badge key={tag} variant="secondary">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
|
||||
@@ -85,7 +85,7 @@ function RouteComponent() {
|
||||
</p>
|
||||
|
||||
<Button asChild variant="link">
|
||||
<a href="https://docs.rxresu.me/api-reference" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://docs.rxresu.me/api-reference" target="_blank" rel="noopener">
|
||||
<LinkSimpleIcon />
|
||||
<Trans>API Reference</Trans>
|
||||
</a>
|
||||
|
||||
@@ -45,7 +45,7 @@ function RouteComponent() {
|
||||
variant="link"
|
||||
className="h-5 justify-start text-muted-foreground text-xs active:scale-100"
|
||||
>
|
||||
<a href="https://crowdin.com/project/reactive-resume" target="_blank" rel="noopener noreferrer">
|
||||
<a href="https://crowdin.com/project/reactive-resume" target="_blank" rel="noopener">
|
||||
<Trans>Help translate the app to your language</Trans>
|
||||
<ArrowRightIcon className="size-3" />
|
||||
</a>
|
||||
|
||||
+22
-76
@@ -8,11 +8,7 @@ export const iconSchema = z
|
||||
);
|
||||
|
||||
export const urlSchema = z.object({
|
||||
url: z
|
||||
.string()
|
||||
.describe(
|
||||
"The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide.",
|
||||
),
|
||||
url: z.string().describe("The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)."),
|
||||
label: z.string().describe("The label to display for the URL. Leave blank to display the URL as-is."),
|
||||
});
|
||||
|
||||
@@ -21,7 +17,7 @@ export const pictureSchema = z.object({
|
||||
url: z
|
||||
.string()
|
||||
.describe(
|
||||
"The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide.",
|
||||
"The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://).",
|
||||
),
|
||||
size: z
|
||||
.number()
|
||||
@@ -65,18 +61,14 @@ export const customFieldSchema = z.object({
|
||||
id: z.string().describe("The unique identifier for the custom field. Usually generated as a UUID."),
|
||||
icon: iconSchema,
|
||||
text: z.string().describe("The text to display for the custom field."),
|
||||
link: z
|
||||
.url()
|
||||
.or(z.literal(""))
|
||||
.describe("If the custom field should be a link, the URL to link to. Leave blank to hide.")
|
||||
.catch(""),
|
||||
link: z.url().or(z.literal("")).describe("If the custom field should be a link, the URL to link to.").catch(""),
|
||||
});
|
||||
|
||||
export const basicsSchema = z.object({
|
||||
name: z.string().describe("The full name of the author of the resume."),
|
||||
headline: z.string().describe("The headline of the author of the resume."),
|
||||
email: z.email().or(z.literal("")).describe("The email address of the author of the resume. Leave blank to hide."),
|
||||
phone: z.string().describe("The phone number of the author of the resume. Leave blank to hide."),
|
||||
email: z.email().or(z.literal("")).describe("The email address of the author of the resume."),
|
||||
phone: z.string().describe("The phone number of the author of the resume."),
|
||||
location: z.string().describe("The location of the author of the resume."),
|
||||
website: urlSchema.describe("The website of the author of the resume."),
|
||||
customFields: z.array(customFieldSchema).describe("The custom fields to display on the resume."),
|
||||
@@ -86,9 +78,7 @@ export const summarySchema = z.object({
|
||||
title: z.string().describe("The title of the summary of the resume."),
|
||||
columns: z.number().describe("The number of columns the summary should span across."),
|
||||
hidden: z.boolean().describe("Whether to hide the summary from the resume."),
|
||||
content: z
|
||||
.string()
|
||||
.describe("The content of the summary of the resume. This should be a HTML-formatted string. Leave blank to hide."),
|
||||
content: z.string().describe("The content of the summary of the resume. This should be a HTML-formatted string."),
|
||||
});
|
||||
|
||||
export const baseItemSchema = z.object({
|
||||
@@ -101,9 +91,7 @@ export const awardItemSchema = baseItemSchema.extend({
|
||||
awarder: z.string().describe("The awarder of the award."),
|
||||
date: z.string().describe("The date when the award was received."),
|
||||
website: urlSchema.describe("The website of the award, if any."),
|
||||
description: z
|
||||
.string()
|
||||
.describe("The description of the award. This should be a HTML-formatted string. Leave blank to hide."),
|
||||
description: z.string().describe("The description of the award. This should be a HTML-formatted string."),
|
||||
});
|
||||
|
||||
export const certificationItemSchema = baseItemSchema.extend({
|
||||
@@ -111,9 +99,7 @@ export const certificationItemSchema = baseItemSchema.extend({
|
||||
issuer: z.string().describe("The issuer of the certification."),
|
||||
date: z.string().describe("The date when the certification was received."),
|
||||
website: urlSchema.describe("The website of the certification, if any."),
|
||||
description: z
|
||||
.string()
|
||||
.describe("The description of the certification. This should be a HTML-formatted string. Leave blank to hide."),
|
||||
description: z.string().describe("The description of the certification. This should be a HTML-formatted string."),
|
||||
});
|
||||
|
||||
export const educationItemSchema = baseItemSchema.extend({
|
||||
@@ -124,9 +110,7 @@ export const educationItemSchema = baseItemSchema.extend({
|
||||
location: z.string().describe("The location of the school or institution."),
|
||||
period: z.string().describe("The period of time the education was obtained over."),
|
||||
website: urlSchema.describe("The website of the school or institution, if any."),
|
||||
description: z
|
||||
.string()
|
||||
.describe("The description of the education. This should be a HTML-formatted string. Leave blank to hide."),
|
||||
description: z.string().describe("The description of the education. This should be a HTML-formatted string."),
|
||||
});
|
||||
|
||||
export const experienceItemSchema = baseItemSchema.extend({
|
||||
@@ -135,9 +119,7 @@ export const experienceItemSchema = baseItemSchema.extend({
|
||||
location: z.string().describe("The location of the company or organization."),
|
||||
period: z.string().describe("The period of time the author was employed at the company or organization."),
|
||||
website: urlSchema.describe("The website of the company or organization, if any."),
|
||||
description: z
|
||||
.string()
|
||||
.describe("The description of the experience. This should be a HTML-formatted string. Leave blank to hide."),
|
||||
description: z.string().describe("The description of the experience. This should be a HTML-formatted string."),
|
||||
});
|
||||
|
||||
export const interestItemSchema = baseItemSchema.extend({
|
||||
@@ -177,9 +159,7 @@ export const projectItemSchema = baseItemSchema.extend({
|
||||
name: z.string().min(1).describe("The name of the project."),
|
||||
period: z.string().describe("The period of time the project was worked on."),
|
||||
website: urlSchema.describe("The link to the project, if any."),
|
||||
description: z
|
||||
.string()
|
||||
.describe("The description of the project. This should be a HTML-formatted string. Leave blank to hide."),
|
||||
description: z.string().describe("The description of the project. This should be a HTML-formatted string."),
|
||||
});
|
||||
|
||||
export const publicationItemSchema = baseItemSchema.extend({
|
||||
@@ -187,17 +167,18 @@ export const publicationItemSchema = baseItemSchema.extend({
|
||||
publisher: z.string().describe("The publisher of the publication."),
|
||||
date: z.string().describe("The date when the publication was published."),
|
||||
website: urlSchema.describe("The link to the publication, if any."),
|
||||
description: z
|
||||
.string()
|
||||
.describe("The description of the publication. This should be a HTML-formatted string. Leave blank to hide."),
|
||||
description: z.string().describe("The description of the publication. This should be a HTML-formatted string."),
|
||||
});
|
||||
|
||||
export const referenceItemSchema = baseItemSchema.extend({
|
||||
name: z.string().min(1).describe("The name of the reference, or a note such as 'Available upon request'."),
|
||||
position: z.string().describe("The position or job title of the reference."),
|
||||
website: urlSchema.describe("The website or LinkedIn profile of the reference, if any."),
|
||||
phone: z.string().describe("The phone number of the reference."),
|
||||
description: z
|
||||
.string()
|
||||
.describe(
|
||||
"The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string. Leave blank to hide.",
|
||||
"The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string.",
|
||||
),
|
||||
});
|
||||
|
||||
@@ -230,9 +211,7 @@ export const volunteerItemSchema = baseItemSchema.extend({
|
||||
website: urlSchema.describe("The link to the organization or company, if any."),
|
||||
description: z
|
||||
.string()
|
||||
.describe(
|
||||
"The description of the volunteer experience. This should be a HTML-formatted string. Leave blank to hide.",
|
||||
),
|
||||
.describe("The description of the volunteer experience. This should be a HTML-formatted string."),
|
||||
});
|
||||
|
||||
export const baseSectionSchema = z.object({
|
||||
@@ -842,7 +821,7 @@ export const sampleResumeData: ResumeData = {
|
||||
},
|
||||
skills: {
|
||||
title: "Technical Skills",
|
||||
columns: 2,
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
items: [
|
||||
{
|
||||
@@ -899,42 +878,6 @@ export const sampleResumeData: ResumeData = {
|
||||
level: 4,
|
||||
keywords: ["Profiling", "Memory Management", "Frame Rate"],
|
||||
},
|
||||
{
|
||||
id: "skill7",
|
||||
hidden: false,
|
||||
icon: "git-branch",
|
||||
name: "Version Control",
|
||||
proficiency: "Advanced",
|
||||
level: 5,
|
||||
keywords: ["Git", "Perforce", "Plastic SCM"],
|
||||
},
|
||||
{
|
||||
id: "skill8",
|
||||
hidden: false,
|
||||
icon: "devices",
|
||||
name: "Platform Development",
|
||||
proficiency: "Advanced",
|
||||
level: 4,
|
||||
keywords: ["PC", "Console (PS5/Xbox)", "Mobile", "Switch"],
|
||||
},
|
||||
{
|
||||
id: "skill9",
|
||||
hidden: false,
|
||||
icon: "network",
|
||||
name: "Multiplayer/Networking",
|
||||
proficiency: "Intermediate",
|
||||
level: 3,
|
||||
keywords: ["Photon", "Mirror", "Client-Server", "P2P"],
|
||||
},
|
||||
{
|
||||
id: "skill10",
|
||||
hidden: false,
|
||||
icon: "palette",
|
||||
name: "Shaders & VFX",
|
||||
proficiency: "Intermediate",
|
||||
level: 3,
|
||||
keywords: ["Shader Graph", "HLSL", "Particle Systems"],
|
||||
},
|
||||
],
|
||||
},
|
||||
languages: {
|
||||
@@ -960,7 +903,7 @@ export const sampleResumeData: ResumeData = {
|
||||
},
|
||||
interests: {
|
||||
title: "Interests",
|
||||
columns: 2,
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
items: [
|
||||
{
|
||||
@@ -1132,6 +1075,9 @@ export const sampleResumeData: ResumeData = {
|
||||
id: "ref1",
|
||||
hidden: false,
|
||||
name: "Available upon request",
|
||||
position: "",
|
||||
website: { url: "", label: "" },
|
||||
phone: "",
|
||||
description: "",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
{
|
||||
"$schema": "https://rxresu.me/schema.json",
|
||||
"version": "5.0.0",
|
||||
"picture": {
|
||||
"hidden": true,
|
||||
"url": "",
|
||||
"size": 100,
|
||||
"rotation": 0,
|
||||
"aspectRatio": 1,
|
||||
"borderRadius": 0,
|
||||
"borderColor": "rgba(0, 0, 0, 0.5)",
|
||||
"borderWidth": 0,
|
||||
"shadowColor": "rgba(0, 0, 0, 0.5)",
|
||||
"shadowWidth": 0
|
||||
},
|
||||
"basics": {
|
||||
"name": "Erica Engineer, E.I.T.",
|
||||
"headline": "Environmental Engineering",
|
||||
"email": "EricaE@uofga.edu",
|
||||
"phone": "555-555-5555",
|
||||
"location": "",
|
||||
"website": {
|
||||
"url": "https://issuu.com/eengineersample",
|
||||
"label": "issuu.com/eengineersample"
|
||||
},
|
||||
"customFields": []
|
||||
},
|
||||
"summary": {
|
||||
"title": "Summary of Qualifications",
|
||||
"columns": 1,
|
||||
"hidden": false,
|
||||
"content": "<ul><li>Experienced in designing solutions that require integration of environmental and human health constraints compatible with economic growth and sustainable development</li><li>Skilled in a variety of modeling and drafting software, including AutoCAD and ALGOR</li><li>Efficient at utilizing project management and teamwork skills to promote overarching goals</li></ul>"
|
||||
},
|
||||
"sections": {
|
||||
"profiles": {
|
||||
"title": "Profiles",
|
||||
"columns": 3,
|
||||
"hidden": true,
|
||||
"items": []
|
||||
},
|
||||
"experience": {
|
||||
"title": "Relevant Work Experience",
|
||||
"columns": 1,
|
||||
"hidden": false,
|
||||
"items": [
|
||||
{
|
||||
"id": "a0eae67b-7e5a-4ebf-bbef-fd48ee27ffd1",
|
||||
"hidden": false,
|
||||
"company": "ABC Pollution Control",
|
||||
"position": "Environmental Engineering Intern",
|
||||
"location": "Miami, FL",
|
||||
"period": "June 2022 – August 2022",
|
||||
"website": {
|
||||
"url": "",
|
||||
"label": ""
|
||||
},
|
||||
"description": "<ul><li>Develop 3-5 bullet points using the following formula: 1 bullet point = task + skill + quantity + result/purpose</li><li>Make sure that your most recent experience is at the top of each section, working backwards from there</li></ul>"
|
||||
},
|
||||
{
|
||||
"id": "4114031b-ebd2-4d6f-8e47-5b1675f59d10",
|
||||
"hidden": false,
|
||||
"company": "Golob & Legion Engineers",
|
||||
"position": "Intern",
|
||||
"location": "Athens, GA",
|
||||
"period": "May 2021 – August 2021",
|
||||
"website": {
|
||||
"url": "",
|
||||
"label": ""
|
||||
},
|
||||
"description": "<ul><li>Start each bullet point with an action verb, and make sure they are present tense for present experiences, and past tense for past experiences</li></ul>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"education": {
|
||||
"title": "Education",
|
||||
"columns": 1,
|
||||
"hidden": false,
|
||||
"items": [
|
||||
{
|
||||
"id": "5cdf4747-beea-40c6-be86-bf3bba92f024",
|
||||
"hidden": false,
|
||||
"school": "University of Georgia",
|
||||
"degree": "Bachelor of Science",
|
||||
"area": "Environmental Engineering",
|
||||
"grade": "3.75/4.00",
|
||||
"location": "Athens, GA",
|
||||
"period": "May 2024",
|
||||
"website": {
|
||||
"url": "",
|
||||
"label": ""
|
||||
},
|
||||
"description": "<p>Engineer in Training (E.I.T.), State of Georgia, 2022<br/>Passed Fundamentals of Engineering Exam, 2022</p>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"projects": {
|
||||
"title": "Relevant Project Experience",
|
||||
"columns": 1,
|
||||
"hidden": false,
|
||||
"items": [
|
||||
{
|
||||
"id": "6c49e60d-b0b3-4f9f-aba5-065b09371b55",
|
||||
"hidden": false,
|
||||
"name": "Group Project, Environmental Engineering Design Methodology",
|
||||
"period": "January 2023 – March 2023",
|
||||
"website": {
|
||||
"url": "",
|
||||
"label": ""
|
||||
},
|
||||
"description": "<ul><li>Develop 3-5 bullet points using the following formula: 1 bullet point = task + skill + quantity + result/purpose</li><li>Ex. Joined a 5-person team to analyze existing ecosystems and conduct environmental tests using ArcGIS for presentation to organizational leadership to inform future building plans</li></ul>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"skills": {
|
||||
"title": "Skills",
|
||||
"columns": 2,
|
||||
"hidden": false,
|
||||
"items": [
|
||||
{
|
||||
"id": "0997cf16-43d1-449e-9f96-8a5484a46131",
|
||||
"hidden": false,
|
||||
"icon": "star",
|
||||
"name": "Technical",
|
||||
"proficiency": "",
|
||||
"level": 0,
|
||||
"keywords": ["AutoCAD", "AutoCAD LT", "Inventor", "SAP2000", "ArcGIS", "Solidworks"]
|
||||
},
|
||||
{
|
||||
"id": "8341a8fc-59b6-480d-9796-d7254e3920b9",
|
||||
"hidden": false,
|
||||
"icon": "star",
|
||||
"name": "Professional",
|
||||
"proficiency": "",
|
||||
"level": 0,
|
||||
"keywords": [
|
||||
"Project management methodologies",
|
||||
"Technical writing",
|
||||
"Oral/Visual Presentation",
|
||||
"Laboratory Procedures"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"languages": {
|
||||
"title": "Languages",
|
||||
"columns": 2,
|
||||
"hidden": true,
|
||||
"items": []
|
||||
},
|
||||
"interests": {
|
||||
"title": "Interests",
|
||||
"columns": 2,
|
||||
"hidden": true,
|
||||
"items": []
|
||||
},
|
||||
"awards": {
|
||||
"title": "Awards",
|
||||
"columns": 1,
|
||||
"hidden": true,
|
||||
"items": []
|
||||
},
|
||||
"certifications": {
|
||||
"title": "Certifications",
|
||||
"columns": 1,
|
||||
"hidden": true,
|
||||
"items": []
|
||||
},
|
||||
"publications": {
|
||||
"title": "Publications",
|
||||
"columns": 1,
|
||||
"hidden": true,
|
||||
"items": []
|
||||
},
|
||||
"volunteer": {
|
||||
"title": "Involvement Experience",
|
||||
"columns": 1,
|
||||
"hidden": false,
|
||||
"items": [
|
||||
{
|
||||
"id": "ce2871f0-5350-4545-bf30-e2b6f9d0f1a7",
|
||||
"hidden": false,
|
||||
"organization": "The National Association of Environmental Professionals",
|
||||
"location": "Athens, GA",
|
||||
"period": "January 2022 – Present",
|
||||
"website": {
|
||||
"url": "",
|
||||
"label": ""
|
||||
},
|
||||
"description": "<ul><li>Try to focus on how each involvement experience will make you a better engineer, and talk about what you did, not what the organization is about</li></ul>"
|
||||
},
|
||||
{
|
||||
"id": "5b631b63-e6d1-4484-b6c4-d59ebae013fd",
|
||||
"hidden": false,
|
||||
"organization": "The Society of Environmental Engineers, UGA",
|
||||
"location": "Athens, GA",
|
||||
"period": "August 2021 – Present",
|
||||
"website": {
|
||||
"url": "",
|
||||
"label": ""
|
||||
},
|
||||
"description": "<ul><li>Attend weekly meetings to focus on strengthening skill sets and developing as a professional</li></ul>"
|
||||
},
|
||||
{
|
||||
"id": "3fbd7e53-6519-437a-8ae6-53de24b34a58",
|
||||
"hidden": false,
|
||||
"organization": "UGA Tennis Team, Co-Captain",
|
||||
"location": "Athens, GA",
|
||||
"period": "August 2020 – May 2023",
|
||||
"website": {
|
||||
"url": "",
|
||||
"label": ""
|
||||
},
|
||||
"description": "<ul><li>Provided leadership for 8 younger athletes related to team motivation, encouragement, and success</li><li>Maintain 10 hours of training per week in addition to full course load and travel for weekend events</li></ul>"
|
||||
},
|
||||
{
|
||||
"id": "94adc4b4-2539-415c-a271-2571031f5650",
|
||||
"hidden": false,
|
||||
"organization": "Sigma Pi Kappa, UGA",
|
||||
"location": "Athens, GA",
|
||||
"period": "December 2021 – December 2022",
|
||||
"website": {
|
||||
"url": "",
|
||||
"label": ""
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"references": {
|
||||
"title": "References",
|
||||
"columns": 1,
|
||||
"hidden": true,
|
||||
"items": []
|
||||
}
|
||||
},
|
||||
"customSections": [
|
||||
{
|
||||
"id": "fdf72428-49d2-4869-ad46-efa5cf1e2be9",
|
||||
"title": "Relevant Coursework",
|
||||
"columns": 1,
|
||||
"hidden": false,
|
||||
"content": "<p>Engineering Hydrology and Hydraulics | Sustainable Building Design | Energy and Environmental Policy Analysis | GIS for Urban Engineering, Planning, and Development | Environmental Justice: Evidence and Impact</p>"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"template": "onyx",
|
||||
"layout": {
|
||||
"sidebarWidth": 35,
|
||||
"pages": [
|
||||
{
|
||||
"fullWidth": false,
|
||||
"main": [
|
||||
"summary",
|
||||
"education",
|
||||
"fdf72428-49d2-4869-ad46-efa5cf1e2be9",
|
||||
"projects",
|
||||
"experience",
|
||||
"volunteer"
|
||||
],
|
||||
"sidebar": ["skills"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"enabled": false,
|
||||
"value": ""
|
||||
},
|
||||
"page": {
|
||||
"gapX": 4,
|
||||
"gapY": 4,
|
||||
"marginX": 18,
|
||||
"marginY": 18,
|
||||
"format": "a4",
|
||||
"locale": "en-US"
|
||||
},
|
||||
"design": {
|
||||
"level": {
|
||||
"icon": "star",
|
||||
"type": "circle"
|
||||
},
|
||||
"colors": {
|
||||
"primary": "rgba(59, 130, 246, 1)",
|
||||
"text": "rgba(0, 0, 0, 1)",
|
||||
"background": "rgba(255, 255, 255, 1)"
|
||||
}
|
||||
},
|
||||
"typography": {
|
||||
"body": {
|
||||
"fontFamily": "IBM Plex Serif",
|
||||
"fontWeights": ["400", "600"],
|
||||
"fontSize": 11,
|
||||
"lineHeight": 1.5
|
||||
},
|
||||
"heading": {
|
||||
"fontFamily": "Oswald",
|
||||
"fontWeights": ["600"],
|
||||
"fontSize": 15,
|
||||
"lineHeight": 1.5
|
||||
}
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user