mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-13 16:22:59 +10:00
perf: 🎨 remove fragment imports, optimize templates
This commit is contained in:
BIN
apps/client/public/templates/rhyhorn.jpg
Normal file
BIN
apps/client/public/templates/rhyhorn.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 344 KiB |
@ -436,9 +436,9 @@ export const sampleResume: ResumeData = {
|
||||
id: "j69rrkkdf94jqqfe5v726d1n",
|
||||
visible: true,
|
||||
name: "QuantRes",
|
||||
description: "Nassau, Bahamas",
|
||||
description: "Execution Team Manager",
|
||||
date: "Jan 2018 - Jul 2020",
|
||||
level: 0,
|
||||
location: "Nassau, Bahamas",
|
||||
summary:
|
||||
"<p>Proprietary quantitative trading firm and privately-held quantitative futures fund with $1B in AUM and a focus on futures and OTC FX opportunities. 25+ year track record of trading global derivatives markets.</p><h3>Execution Team Manager</h3><p>Led international OTC FX and futures trading desk, software development, compliance, and market data for fund averaging $5B in monthly FX transactions and generating 500K+ futures contracts per month.</p><p>Steered a 12-member team of 4 traders and 8 software engineers. Oversaw co-located trade execution and managed direct market access in 4 data centers globally for exchanges not co-located. Built partnerships with trading technology providers. Point of contact with banks, brokerages, FXSpotStream, and 9 other liquidity providers. Led market data feed agreements with exchanges. Maintained CME and Eurex exchange memberships. Headed fulfillment of regulatory compliance and due diligence requests.</p><ul><li><p><strong>Decreased HFT FX trading costs</strong> by integrating streaming of 1 million NDF prices into co-located servers, coordinating with engineers of the FX pricing engine.</p></li></ul><ul><li><p><strong>Streamlined market data contracts and reduced costs by improving data quality and availability.</strong> Consolidated technology providers (Bloomberg and Reuters) and enhanced data collection for time periods needed, data types, and breadth of data.</p></li></ul><ul><li><p><strong>Scaled capabilities by recruiting to enlarge the team</strong>, hiring 3 traders, 2 software engineers, and a consultant with 85% retention for 2+ years. Sourced top talent by posting jobs, leveraging network of contacts, and outsourcing with recruiters.</p></li></ul><ul><li><p><strong>Reduced bank, vendor, and futures broker fees, expanded access to services</strong>, and maintained open lines of communication by strengthening existing relationships. Contacted sales representatives to facilitate introductions.</p></li></ul>",
|
||||
keywords: [],
|
||||
@ -477,7 +477,7 @@ export const sampleResume: ResumeData = {
|
||||
theme: {
|
||||
background: "#ffffff",
|
||||
text: "#000000",
|
||||
primary: "#000000",
|
||||
primary: "#222222",
|
||||
},
|
||||
typography: {
|
||||
font: {
|
||||
|
||||
@ -33,7 +33,7 @@ export const BuilderLayout = () => {
|
||||
if (isDesktop) {
|
||||
return (
|
||||
<div className="relative h-full w-full overflow-hidden">
|
||||
<PanelGroup direction="horizontal">
|
||||
<PanelGroup direction="horizontal" autoSaveId="builder-layout">
|
||||
<Panel
|
||||
collapsible
|
||||
minSize={20}
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
PageGrid,
|
||||
PageNumber,
|
||||
PageWrapper,
|
||||
Rhyhorn,
|
||||
templatesList,
|
||||
} from "@reactive-resume/templates";
|
||||
import { pageSizeMap } from "@reactive-resume/utils";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
@ -35,6 +35,15 @@ export const BuilderPage = () => {
|
||||
};
|
||||
}, [resume.metadata.page]);
|
||||
|
||||
const Template = useMemo(() => {
|
||||
const Component = templatesList.find((template) => template.id === resume.metadata.template)
|
||||
?.Component;
|
||||
|
||||
if (!Component) return null;
|
||||
|
||||
return Component;
|
||||
}, [resume.metadata.template]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
@ -64,7 +73,12 @@ export const BuilderPage = () => {
|
||||
<PageWrapper>
|
||||
{showPageNumbers && <PageNumber>Page {pageIndex + 1}</PageNumber>}
|
||||
|
||||
<Rhyhorn isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
|
||||
{Template !== null && (
|
||||
<Template
|
||||
isFirstPage={pageIndex === 0}
|
||||
columns={columns as SectionKey[][]}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showBreakLine && <PageBreakLine $pageHeight={pageHeight} />}
|
||||
</PageWrapper>
|
||||
|
||||
@ -12,7 +12,6 @@ import {
|
||||
FormMessage,
|
||||
Input,
|
||||
RichInput,
|
||||
Slider,
|
||||
} from "@reactive-resume/ui";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useForm } from "react-hook-form";
|
||||
@ -88,13 +87,13 @@ export const CustomSectionDialog = () => {
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="url"
|
||||
name="location"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>Location</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -102,24 +101,13 @@ export const CustomSectionDialog = () => {
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="level"
|
||||
name="url"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>Level</FormLabel>
|
||||
<FormControl className="py-2">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Slider
|
||||
{...field}
|
||||
min={0}
|
||||
max={5}
|
||||
value={[field.value]}
|
||||
orientation="horizontal"
|
||||
onValueChange={(value) => field.onChange(value[0])}
|
||||
/>
|
||||
|
||||
<span className="text-base font-bold">{field.value}</span>
|
||||
</div>
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@ -16,7 +16,7 @@ type CustomFieldProps = {
|
||||
export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) => {
|
||||
const controls = useDragControls();
|
||||
|
||||
const handleChange = (key: "name" | "value", value: string) =>
|
||||
const handleChange = (key: "icon" | "name" | "value", value: string) =>
|
||||
onChange({ ...field, [key]: value });
|
||||
|
||||
return (
|
||||
@ -28,7 +28,7 @@ export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) =>
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -50 }}
|
||||
>
|
||||
<div className="flex items-end justify-between space-x-4">
|
||||
<div className="flex items-end justify-between space-x-2">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
@ -38,9 +38,15 @@ export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) =>
|
||||
<DotsSixVertical />
|
||||
</Button>
|
||||
|
||||
<Input
|
||||
placeholder="Icon"
|
||||
value={field.icon}
|
||||
className="!ml-0"
|
||||
onChange={(event) => handleChange("icon", event.target.value)}
|
||||
/>
|
||||
|
||||
<Input
|
||||
placeholder="Name"
|
||||
className="!ml-2"
|
||||
value={field.name}
|
||||
onChange={(event) => handleChange("name", event.target.value)}
|
||||
/>
|
||||
@ -54,7 +60,7 @@ export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) =>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="!ml-2 shrink-0"
|
||||
className="!ml-0 shrink-0"
|
||||
onClick={() => onRemove(field.id)}
|
||||
>
|
||||
<X />
|
||||
@ -73,7 +79,10 @@ export const CustomFieldsSection = ({ className }: Props) => {
|
||||
const customFields = useResumeStore((state) => state.resume.data.basics.customFields);
|
||||
|
||||
const onAddCustomField = () => {
|
||||
setValue("basics.customFields", [...customFields, { id: createId(), name: "", value: "" }]);
|
||||
setValue("basics.customFields", [
|
||||
...customFields,
|
||||
{ id: createId(), icon: "", name: "", value: "" },
|
||||
]);
|
||||
};
|
||||
|
||||
const onChangeCustomField = (field: ICustomField) => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Button } from "@reactive-resume/ui";
|
||||
import { templatesList } from "@reactive-resume/templates";
|
||||
import { Button, HoverCard, HoverCardContent, HoverCardTrigger } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
|
||||
import { useResumeStore } from "@/client/stores/resume";
|
||||
@ -6,9 +7,6 @@ import { useResumeStore } from "@/client/stores/resume";
|
||||
import { getSectionIcon } from "../shared/section-icon";
|
||||
|
||||
export const TemplateSection = () => {
|
||||
// TODO: Import templates from @reactive-resume/templates
|
||||
const templateList = ["rhyhorn"];
|
||||
|
||||
const setValue = useResumeStore((state) => state.setValue);
|
||||
const currentTemplate = useResumeStore((state) => state.resume.data.metadata.template);
|
||||
|
||||
@ -21,20 +19,31 @@ export const TemplateSection = () => {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="grid grid-cols-2 gap-y-4">
|
||||
{templateList.map((template) => (
|
||||
<Button
|
||||
key={template}
|
||||
variant="outline"
|
||||
disabled={template === currentTemplate}
|
||||
onClick={() => setValue("metadata.template", template)}
|
||||
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",
|
||||
template === currentTemplate && "ring-1",
|
||||
)}
|
||||
>
|
||||
{template}
|
||||
</Button>
|
||||
<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>
|
||||
|
||||
<HoverCardContent className="max-w-xs overflow-hidden border-none bg-white p-0">
|
||||
<img
|
||||
alt={name}
|
||||
loading="lazy"
|
||||
src={`/templates/${id}.jpg`}
|
||||
className="aspect-[1/1.4142]"
|
||||
/>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
))}
|
||||
</main>
|
||||
</section>
|
||||
|
||||
@ -48,7 +48,7 @@ export const ThemeSection = () => {
|
||||
style={{ backgroundColor: theme.primary }}
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent asChild className="rounded-lg border-none bg-transparent p-0">
|
||||
<PopoverContent className="rounded-lg border-none bg-transparent p-0">
|
||||
<HexColorPicker
|
||||
color={theme.primary}
|
||||
onChange={(color) => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { CaretDown, MagicWand, Plus, TestTube } from "@phosphor-icons/react";
|
||||
import { CaretDown, Flask, MagicWand, Plus } from "@phosphor-icons/react";
|
||||
import { createResumeSchema, ResumeDto } from "@reactive-resume/dto";
|
||||
import { idSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
@ -265,7 +265,7 @@ export const ResumeDialog = () => {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="right" align="center">
|
||||
<DropdownMenuItem onClick={onCreateSample}>
|
||||
<TestTube className="mr-2" />
|
||||
<Flask className="mr-2" />
|
||||
Create Sample Resume
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { useTemplate } from "@reactive-resume/hooks";
|
||||
import { ResumeData, SectionKey } from "@reactive-resume/schema";
|
||||
import { Artboard, PageWrapper, Rhyhorn } from "@reactive-resume/templates";
|
||||
import { Artboard, PageWrapper } from "@reactive-resume/templates";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { useSessionStorage } from "usehooks-ts";
|
||||
|
||||
export const PrinterPage = () => {
|
||||
const [resume] = useSessionStorage<ResumeData | null>("resume", null);
|
||||
const template = useTemplate(resume?.metadata.template);
|
||||
|
||||
if (!resume) return <Navigate to="/" replace />;
|
||||
|
||||
@ -12,7 +14,9 @@ export const PrinterPage = () => {
|
||||
<Artboard resume={resume} style={{ pointerEvents: "auto" }}>
|
||||
{resume.metadata.layout.map((columns, pageIndex) => (
|
||||
<PageWrapper key={pageIndex} data-page={pageIndex + 1}>
|
||||
<Rhyhorn isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
|
||||
{template !== null && (
|
||||
<template.Component isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
|
||||
)}
|
||||
</PageWrapper>
|
||||
))}
|
||||
</Artboard>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ResumeDto } from "@reactive-resume/dto";
|
||||
import { useTemplate } from "@reactive-resume/hooks";
|
||||
import { SectionKey } from "@reactive-resume/schema";
|
||||
import { Artboard, PageWrapper, Rhyhorn } from "@reactive-resume/templates";
|
||||
import { Artboard, PageWrapper } from "@reactive-resume/templates";
|
||||
import { Button } from "@reactive-resume/ui";
|
||||
import { pageSizeMap } from "@reactive-resume/utils";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
@ -15,6 +16,7 @@ import { findResumeByUsernameSlug } from "@/client/services/resume";
|
||||
export const PublicResumePage = () => {
|
||||
const { title, data: resume } = useLoaderData() as ResumeDto;
|
||||
const format = resume.metadata.page.format;
|
||||
const template = useTemplate(resume.metadata.template);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -29,7 +31,12 @@ export const PublicResumePage = () => {
|
||||
<Artboard resume={resume} style={{ pointerEvents: "auto" }}>
|
||||
{resume.metadata.layout.map((columns, pageIndex) => (
|
||||
<PageWrapper key={pageIndex} data-page={pageIndex + 1}>
|
||||
<Rhyhorn isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
|
||||
{template !== null && (
|
||||
<template.Component
|
||||
isFirstPage={pageIndex === 0}
|
||||
columns={columns as SectionKey[][]}
|
||||
/>
|
||||
)}
|
||||
</PageWrapper>
|
||||
))}
|
||||
</Artboard>
|
||||
|
||||
Reference in New Issue
Block a user