mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-16 09:41:31 +10:00
feat(i18n): implement localization using LinguiJS
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { HouseSimple, Lock, SidebarSimple } from "@phosphor-icons/react";
|
||||
import { useBreakpoint } from "@reactive-resume/hooks";
|
||||
import { Button, Tooltip } from "@reactive-resume/ui";
|
||||
@ -52,7 +53,7 @@ export const BuilderHeader = () => {
|
||||
<h1 className="font-medium">{title}</h1>
|
||||
|
||||
{locked && (
|
||||
<Tooltip content="This resume is locked, please unlock to make further changes.">
|
||||
<Tooltip content={t`This resume is locked, please unlock to make further changes.`}>
|
||||
<Lock size={14} className="ml-2 opacity-75" />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import {
|
||||
ArrowClockwise,
|
||||
ArrowCounterClockwise,
|
||||
@ -55,14 +56,14 @@ export const BuilderToolbar = () => {
|
||||
>
|
||||
<div className="inline-flex items-center justify-center rounded-full bg-background px-4 shadow-xl">
|
||||
{/* Undo */}
|
||||
<Tooltip content="Undo">
|
||||
<Tooltip content={t`Undo`}>
|
||||
<Button size="icon" variant="ghost" className="rounded-none" onClick={() => undo()}>
|
||||
<ArrowCounterClockwise />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
{/* Redo */}
|
||||
<Tooltip content="Redo">
|
||||
<Tooltip content={t`Redo`}>
|
||||
<Button size="icon" variant="ghost" className="rounded-none" onClick={() => redo()}>
|
||||
<ArrowClockwise />
|
||||
</Button>
|
||||
@ -70,25 +71,25 @@ export const BuilderToolbar = () => {
|
||||
|
||||
<Separator orientation="vertical" className="h-9" />
|
||||
|
||||
<Tooltip content="Zoom In">
|
||||
<Tooltip content={t`Zoom In`}>
|
||||
<Button size="icon" variant="ghost" className="rounded-none" onClick={onZoomIn}>
|
||||
<MagnifyingGlassPlus />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Zoom Out">
|
||||
<Tooltip content={t`Zoom Out`}>
|
||||
<Button size="icon" variant="ghost" className="rounded-none" onClick={onZoomOut}>
|
||||
<MagnifyingGlassMinus />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Reset Zoom">
|
||||
<Tooltip content={t`Reset Zoom`}>
|
||||
<Button size="icon" variant="ghost" className="rounded-none" onClick={onResetView}>
|
||||
<ClockClockwise />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Center Artboard">
|
||||
<Tooltip content={t`Center Artboard`}>
|
||||
<Button size="icon" variant="ghost" className="rounded-none" onClick={onCenterView}>
|
||||
<CubeFocus />
|
||||
</Button>
|
||||
@ -97,7 +98,7 @@ export const BuilderToolbar = () => {
|
||||
<Separator orientation="vertical" className="h-9" />
|
||||
|
||||
{/* Toggle Page Break Line */}
|
||||
<Tooltip content="Toggle Page Break Line">
|
||||
<Tooltip content={t`Toggle Page Break Line`}>
|
||||
<Toggle
|
||||
className="rounded-none"
|
||||
pressed={pageOptions.breakLine}
|
||||
@ -110,7 +111,7 @@ export const BuilderToolbar = () => {
|
||||
</Tooltip>
|
||||
|
||||
{/* Toggle Page Numbers */}
|
||||
<Tooltip content="Toggle Page Numbers">
|
||||
<Tooltip content={t`Toggle Page Numbers`}>
|
||||
<Toggle
|
||||
className="rounded-none"
|
||||
pressed={pageOptions.pageNumbers}
|
||||
@ -125,14 +126,14 @@ export const BuilderToolbar = () => {
|
||||
<Separator orientation="vertical" className="h-9" />
|
||||
|
||||
{/* Copy Link to Resume */}
|
||||
<Tooltip content="Copy Link to Resume">
|
||||
<Tooltip content={t`Copy Link to Resume`}>
|
||||
<Button size="icon" variant="ghost" className="rounded-none" disabled={!isPublic}>
|
||||
<LinkSimple />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
{/* Download PDF */}
|
||||
<Tooltip content="Download PDF">
|
||||
<Tooltip content={t`Download PDF`}>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { ResumeDto } from "@reactive-resume/dto";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
@ -34,7 +35,9 @@ export const BuilderPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{title} - Reactive Resume</title>
|
||||
<title>
|
||||
{title} - {t`Reactive Resume`}
|
||||
</title>
|
||||
</Helmet>
|
||||
|
||||
<iframe
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { awardSchema, defaultAward } from "@reactive-resume/schema";
|
||||
import {
|
||||
FormControl,
|
||||
@ -35,9 +36,9 @@ export const AwardsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Title</FormLabel>
|
||||
<FormLabel>{t({ message: "Title", context: "Name of the Award" })}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="3rd Runner Up" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -49,9 +50,9 @@ export const AwardsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Awarder</FormLabel>
|
||||
<FormLabel>{t`Awarder`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="TechCrunch Disrupt SF" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -63,9 +64,15 @@ export const AwardsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Date</FormLabel>
|
||||
<FormLabel>{t`Date`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Aug 2019" />
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={t({
|
||||
message: "March 2023",
|
||||
comment: "The month and year should be uniform across all languages.",
|
||||
})}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -77,9 +84,9 @@ export const AwardsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://techcrunch.com/events/disrupt-sf-2019" />
|
||||
<URLInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -91,7 +98,7 @@ export const AwardsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { certificationSchema, defaultCertification } from "@reactive-resume/schema";
|
||||
import {
|
||||
FormControl,
|
||||
@ -35,9 +36,9 @@ export const CertificationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{t({ message: "Name", context: "Name of the Certification" })}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Web Developer Bootcamp" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -49,9 +50,9 @@ export const CertificationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Issuer</FormLabel>
|
||||
<FormLabel>{t`Issuer`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Udemy" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -63,9 +64,9 @@ export const CertificationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Date</FormLabel>
|
||||
<FormLabel>{t`Date`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Aug 2019" />
|
||||
<Input {...field} placeholder={t`March 2023`} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -77,7 +78,7 @@ export const CertificationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://udemy.com/certificate/UC-..." />
|
||||
</FormControl>
|
||||
@ -91,7 +92,7 @@ export const CertificationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { X } from "@phosphor-icons/react";
|
||||
import { CustomSection, customSectionSchema, defaultCustomSection } from "@reactive-resume/schema";
|
||||
import {
|
||||
@ -49,7 +50,7 @@ export const CustomSectionDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{t`Name`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@ -63,7 +64,7 @@ export const CustomSectionDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Description</FormLabel>
|
||||
<FormLabel>{t`Description`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@ -77,7 +78,7 @@ export const CustomSectionDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Date</FormLabel>
|
||||
<FormLabel>{t`Date`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@ -91,7 +92,7 @@ export const CustomSectionDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Location</FormLabel>
|
||||
<FormLabel>{t`Location`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@ -105,7 +106,7 @@ export const CustomSectionDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} />
|
||||
</FormControl>
|
||||
@ -119,7 +120,7 @@ export const CustomSectionDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
@ -141,12 +142,12 @@ export const CustomSectionDialog = () => {
|
||||
render={({ field }) => (
|
||||
<div className="col-span-2 space-y-3">
|
||||
<FormItem>
|
||||
<FormLabel>Keywords</FormLabel>
|
||||
<FormLabel>{t`Keywords`}</FormLabel>
|
||||
<FormControl>
|
||||
<BadgeInput {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
You can add multiple keywords by separating them with a comma.
|
||||
{t`You can add multiple keywords by separating them with a comma or pressing enter.`}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { defaultEducation, educationSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
FormControl,
|
||||
@ -35,9 +36,9 @@ export const EducationDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Institution</FormLabel>
|
||||
<FormLabel>{t`Institution`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Carnegie Mellon University" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -49,9 +50,14 @@ export const EducationDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Type of Study</FormLabel>
|
||||
<FormLabel>
|
||||
{t({
|
||||
message: "Type of Study",
|
||||
comment: "For example, Bachelor's Degree or Master's Degree",
|
||||
})}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Bachelor's Degree" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -63,9 +69,14 @@ export const EducationDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Area of Study</FormLabel>
|
||||
<FormLabel>
|
||||
{t({
|
||||
message: "Area of Study",
|
||||
comment: "For example, Computer Science or Business Administration",
|
||||
})}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Computer Science" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -77,7 +88,12 @@ export const EducationDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Score</FormLabel>
|
||||
<FormLabel>
|
||||
{t({
|
||||
message: "Score",
|
||||
comment: "Score or honors for the degree, for example, CGPA or magna cum laude",
|
||||
})}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="9.2 GPA" />
|
||||
</FormControl>
|
||||
@ -91,9 +107,9 @@ export const EducationDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Date</FormLabel>
|
||||
<FormLabel>{t`Date`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Aug 2006 - Oct 2012" />
|
||||
<Input {...field} placeholder={t`March 2023 - Present`} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -105,9 +121,9 @@ export const EducationDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://www.cmu.edu/" />
|
||||
<URLInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -119,7 +135,7 @@ export const EducationDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { defaultExperience, experienceSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
FormControl,
|
||||
@ -35,9 +36,9 @@ export const ExperienceDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Company</FormLabel>
|
||||
<FormLabel>{t`Company`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Alphabet Inc." />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -49,9 +50,14 @@ export const ExperienceDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Position</FormLabel>
|
||||
<FormLabel>
|
||||
{t({
|
||||
message: "Position",
|
||||
context: "Position held at a company, for example, Software Engineer",
|
||||
})}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Chief Executive Officer" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -63,9 +69,9 @@ export const ExperienceDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Date</FormLabel>
|
||||
<FormLabel>{t`Date`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Dec 2019 - Present" />
|
||||
<Input {...field} placeholder={t`March 2023 - Present`} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -77,9 +83,9 @@ export const ExperienceDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Location</FormLabel>
|
||||
<FormLabel>{t`Location`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="New York, NY" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -91,9 +97,9 @@ export const ExperienceDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://www.abc.xyz/" />
|
||||
<URLInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -105,7 +111,7 @@ export const ExperienceDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { X } from "@phosphor-icons/react";
|
||||
import { defaultInterest, interestSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
@ -36,9 +37,9 @@ export const InterestsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{t`Name`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Video Games" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -51,12 +52,12 @@ export const InterestsDialog = () => {
|
||||
render={({ field }) => (
|
||||
<div className="col-span-2 space-y-3">
|
||||
<FormItem>
|
||||
<FormLabel>Keywords</FormLabel>
|
||||
<FormLabel>{t`Keywords`}</FormLabel>
|
||||
<FormControl>
|
||||
<BadgeInput {...field} placeholder="FIFA 23, Call of Duty, etc." />
|
||||
<BadgeInput {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
You can add multiple keywords by separating them with a comma.
|
||||
{t`You can add multiple keywords by separating them with a comma or pressing enter.`}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { defaultLanguage, languageSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
FormControl,
|
||||
@ -33,9 +34,9 @@ export const LanguagesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{t`Name`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="German" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -47,9 +48,9 @@ export const LanguagesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Fluency</FormLabel>
|
||||
<FormLabel>{t`Fluency`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Native Speaker" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -61,7 +62,7 @@ export const LanguagesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>Fluency (CEFR)</FormLabel>
|
||||
<FormLabel>{t`Fluency (CEFR)`}</FormLabel>
|
||||
<FormControl className="py-2">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Slider
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import { defaultProfile, profileSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
Avatar,
|
||||
@ -35,8 +36,9 @@ export const ProfilesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Network</FormLabel>
|
||||
<FormLabel>{t`Network`}</FormLabel>
|
||||
<FormControl>
|
||||
{/* eslint-disable-next-line lingui/no-unlocalized-strings */}
|
||||
<Input {...field} placeholder="LinkedIn" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@ -49,9 +51,9 @@ export const ProfilesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Username</FormLabel>
|
||||
<FormLabel>{t`Username`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="johndoe" />
|
||||
<Input {...field} placeholder="john.doe" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -63,7 +65,7 @@ export const ProfilesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>URL</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://linkedin.com/in/johndoe" />
|
||||
</FormControl>
|
||||
@ -77,7 +79,7 @@ export const ProfilesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel htmlFor="iconSlug">Icon</FormLabel>
|
||||
<FormLabel htmlFor="iconSlug">{t`Icon`}</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Avatar className="h-8 w-8 bg-white">
|
||||
@ -93,15 +95,17 @@ export const ProfilesDialog = () => {
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
<FormDescription className="ml-10">
|
||||
Powered by{" "}
|
||||
<a
|
||||
href="https://simpleicons.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
className="font-medium"
|
||||
>
|
||||
Simple Icons
|
||||
</a>
|
||||
<Trans>
|
||||
Powered by{" "}
|
||||
<a
|
||||
href="https://simpleicons.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
className="font-medium"
|
||||
>
|
||||
Simple Icons
|
||||
</a>
|
||||
</Trans>
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { X } from "@phosphor-icons/react";
|
||||
import { defaultProject, projectSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
@ -40,9 +41,9 @@ export const ProjectsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{t`Name`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Reactive Resume" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -54,9 +55,9 @@ export const ProjectsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Description</FormLabel>
|
||||
<FormLabel>{t`Description`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Open Source Resume Builder" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -68,9 +69,9 @@ export const ProjectsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Date</FormLabel>
|
||||
<FormLabel>{t`Date`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Sep 2018 - Present" />
|
||||
<Input {...field} placeholder={t`March 2023 - Present`} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -82,7 +83,7 @@ export const ProjectsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://rxresu.me" />
|
||||
</FormControl>
|
||||
@ -96,7 +97,7 @@ export const ProjectsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
@ -118,12 +119,12 @@ export const ProjectsDialog = () => {
|
||||
render={({ field }) => (
|
||||
<div className="col-span-2 space-y-3">
|
||||
<FormItem>
|
||||
<FormLabel>Keywords</FormLabel>
|
||||
<FormLabel>{t`Keywords`}</FormLabel>
|
||||
<FormControl>
|
||||
<BadgeInput {...field} placeholder="FIFA 23, Call of Duty, etc." />
|
||||
<BadgeInput {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
You can add multiple keywords by separating them with a comma.
|
||||
{t`You can add multiple keywords by separating them with a comma or pressing enter.`}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { defaultPublication, publicationSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
FormControl,
|
||||
@ -35,9 +36,9 @@ export const PublicationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{t`Name`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="The Great Gatsby" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -49,9 +50,9 @@ export const PublicationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Publisher</FormLabel>
|
||||
<FormLabel>{t`Publisher`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Charles Scribner's Sons" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -63,9 +64,9 @@ export const PublicationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Release Date</FormLabel>
|
||||
<FormLabel>{t`Date`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="April 10, 1925" />
|
||||
<Input {...field} placeholder={t`March 2023`} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -77,9 +78,9 @@ export const PublicationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://books.google.com/..." />
|
||||
<URLInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -91,7 +92,7 @@ export const PublicationsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { defaultReference, referenceSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
FormControl,
|
||||
@ -35,9 +36,9 @@ export const ReferencesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{t`Name`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Cosmo Kramer" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -49,9 +50,9 @@ export const ReferencesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Description</FormLabel>
|
||||
<FormLabel>{t`Description`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Neighbour" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -63,9 +64,9 @@ export const ReferencesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://linkedin.com/in/cosmo.kramer" />
|
||||
<URLInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -77,7 +78,7 @@ export const ReferencesDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { X } from "@phosphor-icons/react";
|
||||
import { defaultSkill, skillSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
@ -37,9 +38,9 @@ export const SkillsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormLabel>{t`Name`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Content Management" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -51,9 +52,9 @@ export const SkillsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Description</FormLabel>
|
||||
<FormLabel>{t`Description`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Advanced" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -65,7 +66,7 @@ export const SkillsDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>Level</FormLabel>
|
||||
<FormLabel>{t`Level`}</FormLabel>
|
||||
<FormControl className="py-2">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Slider
|
||||
@ -91,12 +92,12 @@ export const SkillsDialog = () => {
|
||||
render={({ field }) => (
|
||||
<div className="col-span-2 space-y-3">
|
||||
<FormItem>
|
||||
<FormLabel>Keywords</FormLabel>
|
||||
<FormLabel>{t`Keywords`}</FormLabel>
|
||||
<FormControl>
|
||||
<BadgeInput {...field} placeholder="WordPress, Joomla, Webflow etc." />
|
||||
<BadgeInput {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
You can add multiple keywords by separating them with a comma.
|
||||
{t`You can add multiple keywords by separating them with a comma or pressing enter.`}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { t } from "@lingui/macro";
|
||||
import { defaultVolunteer, volunteerSchema } from "@reactive-resume/schema";
|
||||
import {
|
||||
FormControl,
|
||||
@ -35,9 +36,9 @@ export const VolunteerDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Organization</FormLabel>
|
||||
<FormLabel>{t`Organization`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Amnesty International" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -49,9 +50,9 @@ export const VolunteerDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Position</FormLabel>
|
||||
<FormLabel>{t`Position`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Recruiter" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -63,9 +64,9 @@ export const VolunteerDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Date</FormLabel>
|
||||
<FormLabel>{t`Date`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Dec 2016 - Aug 2017" />
|
||||
<Input {...field} placeholder={t`March 2023 - Present`} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -77,9 +78,9 @@ export const VolunteerDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Location</FormLabel>
|
||||
<FormLabel>{t`Location`}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="New York, NY" />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -91,9 +92,9 @@ export const VolunteerDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Website</FormLabel>
|
||||
<FormLabel>{t`Website`}</FormLabel>
|
||||
<FormControl>
|
||||
<URLInput {...field} placeholder="https://www.amnesty.org/" />
|
||||
<URLInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -105,7 +106,7 @@ export const VolunteerDialog = () => {
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1 sm:col-span-2">
|
||||
<FormLabel>Summary</FormLabel>
|
||||
<FormLabel>{t`Summary`}</FormLabel>
|
||||
<FormControl>
|
||||
<RichInput
|
||||
{...field}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Plus, PlusCircle } from "@phosphor-icons/react";
|
||||
import {
|
||||
Award,
|
||||
@ -50,7 +51,15 @@ export const LeftSidebar = () => {
|
||||
</Button>
|
||||
|
||||
<div className="flex flex-col items-center justify-center gap-y-2">
|
||||
<SectionIcon id="basics" name="Basics" onClick={() => scrollIntoView("#basics")} />
|
||||
<SectionIcon
|
||||
id="basics"
|
||||
onClick={() => scrollIntoView("#basics")}
|
||||
name={t({
|
||||
message: "Basics",
|
||||
context:
|
||||
"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")} />
|
||||
@ -68,10 +77,11 @@ export const LeftSidebar = () => {
|
||||
<SectionIcon
|
||||
id="custom"
|
||||
variant="outline"
|
||||
name="Add a new section"
|
||||
name={t`Add a new section`}
|
||||
icon={<Plus size={14} />}
|
||||
onClick={() => {
|
||||
addSection();
|
||||
// eslint-disable-next-line lingui/no-unlocalized-strings
|
||||
scrollIntoView("& > section:last-of-type");
|
||||
}}
|
||||
/>
|
||||
@ -184,7 +194,7 @@ export const LeftSidebar = () => {
|
||||
|
||||
<Button size="lg" variant="outline" onClick={addSection}>
|
||||
<PlusCircle />
|
||||
<span className="ml-2">Add a new section</span>
|
||||
<span className="ml-2">{t`Add a new section`}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { basicsSchema } from "@reactive-resume/schema";
|
||||
import { Input, Label } from "@reactive-resume/ui";
|
||||
|
||||
@ -17,7 +18,7 @@ export const BasicsSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("basics")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Basics</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Basics`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -27,10 +28,9 @@ export const BasicsSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5 sm:col-span-2">
|
||||
<Label htmlFor="basics.name">Full Name</Label>
|
||||
<Label htmlFor="basics.name">{t`Full Name`}</Label>
|
||||
<Input
|
||||
id="basics.name"
|
||||
placeholder="John Doe"
|
||||
value={basics.name}
|
||||
hasError={!basicsSchema.pick({ name: true }).safeParse({ name: basics.name }).success}
|
||||
onChange={(event) => setValue("basics.name", event.target.value)}
|
||||
@ -38,17 +38,16 @@ export const BasicsSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5 sm:col-span-2">
|
||||
<Label htmlFor="basics.headline">Headline</Label>
|
||||
<Label htmlFor="basics.headline">{t`Headline`}</Label>
|
||||
<Input
|
||||
id="basics.headline"
|
||||
placeholder="Highly Creative Frontend Web Developer"
|
||||
value={basics.headline}
|
||||
onChange={(event) => setValue("basics.headline", event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="basics.email">Email Address</Label>
|
||||
<Label htmlFor="basics.email">{t`Email`}</Label>
|
||||
<Input
|
||||
id="basics.email"
|
||||
placeholder="john.doe@example.com"
|
||||
@ -61,7 +60,7 @@ export const BasicsSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="basics.url">Website</Label>
|
||||
<Label htmlFor="basics.url">{t`Website`}</Label>
|
||||
<URLInput
|
||||
id="basics.url"
|
||||
value={basics.url}
|
||||
@ -71,7 +70,7 @@ export const BasicsSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="basics.phone">Phone Number</Label>
|
||||
<Label htmlFor="basics.phone">{t`Phone`}</Label>
|
||||
<Input
|
||||
id="basics.phone"
|
||||
placeholder="+1 (123) 4567 7890"
|
||||
@ -81,10 +80,9 @@ export const BasicsSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="basics.location">Location</Label>
|
||||
<Label htmlFor="basics.location">{t`Location`}</Label>
|
||||
<Input
|
||||
id="basics.location"
|
||||
placeholder="105 Cedarhurst Ave, Cedarhurst, NY 11516"
|
||||
value={basics.location}
|
||||
onChange={(event) => setValue("basics.location", event.target.value)}
|
||||
/>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { DotsSixVertical, Plus, X } from "@phosphor-icons/react";
|
||||
import { CustomField as ICustomField } from "@reactive-resume/schema";
|
||||
@ -38,21 +39,22 @@ export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) =>
|
||||
<DotsSixVertical />
|
||||
</Button>
|
||||
|
||||
<Input
|
||||
placeholder="Icon"
|
||||
{/* <Input
|
||||
placeholder={t`Icon`}
|
||||
value={field.icon}
|
||||
className="!ml-0"
|
||||
onChange={(event) => handleChange("icon", event.target.value)}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<Input
|
||||
placeholder="Name"
|
||||
placeholder={t`Name`}
|
||||
value={field.name}
|
||||
className="!ml-0"
|
||||
onChange={(event) => handleChange("name", event.target.value)}
|
||||
/>
|
||||
|
||||
<Input
|
||||
placeholder="Value"
|
||||
placeholder={t`Value`}
|
||||
value={field.value}
|
||||
onChange={(event) => handleChange("value", event.target.value)}
|
||||
/>
|
||||
@ -126,7 +128,7 @@ export const CustomFieldsSection = ({ className }: Props) => {
|
||||
|
||||
<Button variant="link" onClick={onAddCustomField}>
|
||||
<Plus className="mr-2" />
|
||||
<span>Add a custom field</span>
|
||||
<span>{t`Add a custom field`}</span>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import {
|
||||
AspectRatio,
|
||||
Checkbox,
|
||||
@ -69,7 +70,7 @@ export const PictureOptions = () => {
|
||||
<div className="flex flex-col gap-y-5">
|
||||
<div className="grid grid-cols-3 items-center gap-x-6">
|
||||
<Label htmlFor="picture.size" className="col-span-1">
|
||||
Size (in px)
|
||||
{t`Size (in px)`}
|
||||
</Label>
|
||||
<Input
|
||||
type="number"
|
||||
@ -85,7 +86,7 @@ export const PictureOptions = () => {
|
||||
|
||||
<div className="grid grid-cols-3 items-center gap-x-6">
|
||||
<Label htmlFor="picture.aspectRatio" className="col-span-1">
|
||||
Aspect Ratio
|
||||
{t`Aspect Ratio`}
|
||||
</Label>
|
||||
<div className="col-span-2 flex items-center justify-between">
|
||||
<ToggleGroup
|
||||
@ -94,19 +95,19 @@ export const PictureOptions = () => {
|
||||
onValueChange={onAspectRatioChange}
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
<Tooltip content="Square">
|
||||
<Tooltip content={t`Square`}>
|
||||
<ToggleGroupItem value="square">
|
||||
<div className="h-3 w-3 border border-foreground" />
|
||||
</ToggleGroupItem>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Horizontal">
|
||||
<Tooltip content={t`Horizontal`}>
|
||||
<ToggleGroupItem value="horizontal">
|
||||
<div className="h-2 w-3 border border-foreground" />
|
||||
</ToggleGroupItem>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Portrait">
|
||||
<Tooltip content={t`Portrait`}>
|
||||
<ToggleGroupItem value="portrait">
|
||||
<div className="h-3 w-2 border border-foreground" />
|
||||
</ToggleGroupItem>
|
||||
@ -130,7 +131,7 @@ export const PictureOptions = () => {
|
||||
|
||||
<div className="grid grid-cols-3 items-center gap-x-6">
|
||||
<Label htmlFor="picture.borderRadius" className="col-span-1">
|
||||
Border Radius
|
||||
{t`Border Radius`}
|
||||
</Label>
|
||||
<div className="col-span-2 flex items-center justify-between">
|
||||
<ToggleGroup
|
||||
@ -139,19 +140,19 @@ export const PictureOptions = () => {
|
||||
onValueChange={onBorderRadiusChange}
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
<Tooltip content="Square">
|
||||
<Tooltip content={t`Square`}>
|
||||
<ToggleGroupItem value="square">
|
||||
<div className="h-3 w-3 border border-foreground" />
|
||||
</ToggleGroupItem>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Rounded">
|
||||
<Tooltip content={t`Rounded`}>
|
||||
<ToggleGroupItem value="rounded">
|
||||
<div className="h-3 w-3 rounded-sm border border-foreground" />
|
||||
</ToggleGroupItem>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Circle">
|
||||
<Tooltip content={t`Circle`}>
|
||||
<ToggleGroupItem value="circle">
|
||||
<div className="h-3 w-3 rounded-full border border-foreground" />
|
||||
</ToggleGroupItem>
|
||||
@ -176,7 +177,7 @@ export const PictureOptions = () => {
|
||||
<div>
|
||||
<div className="grid grid-cols-3 items-start gap-x-6">
|
||||
<div className="col-span-1">
|
||||
<Label>Effects</Label>
|
||||
<Label>{t`Effects`}</Label>
|
||||
</div>
|
||||
<div className="col-span-2 space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
@ -187,7 +188,7 @@ export const PictureOptions = () => {
|
||||
setValue("basics.picture.effects.hidden", checked);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="picture.effects.hidden">Hidden</Label>
|
||||
<Label htmlFor="picture.effects.hidden">{t`Hidden`}</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
@ -198,7 +199,7 @@ export const PictureOptions = () => {
|
||||
setValue("basics.picture.effects.border", checked);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="picture.effects.border">Border</Label>
|
||||
<Label htmlFor="picture.effects.border">{t`Border`}</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
@ -209,7 +210,7 @@ export const PictureOptions = () => {
|
||||
setValue("basics.picture.effects.grayscale", checked);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="picture.effects.grayscale">Grayscale</Label>
|
||||
<Label htmlFor="picture.effects.grayscale">{t`Grayscale`}</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Aperture, UploadSimple } from "@phosphor-icons/react";
|
||||
import {
|
||||
Avatar,
|
||||
@ -48,7 +49,7 @@ export const PictureSection = () => {
|
||||
</Avatar>
|
||||
|
||||
<div className="flex w-full flex-col gap-y-1.5">
|
||||
<Label htmlFor="basics.picture.url">Picture</Label>
|
||||
<Label htmlFor="basics.picture.url">{t`Picture`}</Label>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Input
|
||||
id="basics.picture.url"
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
sortableKeyboardCoordinates,
|
||||
verticalListSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { t } from "@lingui/macro";
|
||||
import { Plus } from "@phosphor-icons/react";
|
||||
import { SectionItem, SectionKey, SectionWithItem } from "@reactive-resume/schema";
|
||||
import { Button } from "@reactive-resume/ui";
|
||||
@ -103,7 +104,12 @@ export const SectionBase = <T extends SectionItem>({ id, title, description }: P
|
||||
className="gap-x-2 border-dashed py-6 leading-relaxed hover:bg-secondary-accent"
|
||||
>
|
||||
<Plus size={14} />
|
||||
<span className="font-medium">Add New {section.name}</span>
|
||||
<span className="font-medium">
|
||||
{t({
|
||||
message: "Add New Item",
|
||||
context: "For example, add a new work experience, or add a new profile.",
|
||||
})}
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@ -137,7 +143,12 @@ export const SectionBase = <T extends SectionItem>({ id, title, description }: P
|
||||
<footer className="flex items-center justify-end">
|
||||
<Button variant="outline" className="ml-auto gap-x-2" onClick={onCreate}>
|
||||
<Plus />
|
||||
<span>Add New {section.name}</span>
|
||||
<span>
|
||||
{t({
|
||||
message: "Add New Item",
|
||||
context: "For example, add a new work experience, or add a new profile.",
|
||||
})}
|
||||
</span>
|
||||
</Button>
|
||||
</footer>
|
||||
)}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { CopySimple, PencilSimple, Plus } from "@phosphor-icons/react";
|
||||
import { SectionItem, SectionWithItem } from "@reactive-resume/schema";
|
||||
@ -20,7 +21,7 @@ import {
|
||||
} from "@reactive-resume/ui";
|
||||
import { produce } from "immer";
|
||||
import get from "lodash.get";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
|
||||
import { DialogName, useDialog } from "@/client/stores/dialog";
|
||||
@ -40,12 +41,12 @@ export const SectionDialog = <T extends SectionItem>({
|
||||
children,
|
||||
}: Props<T>) => {
|
||||
const { isOpen, mode, close, payload } = useDialog<T>(id);
|
||||
|
||||
const setValue = useResumeStore((state) => state.setValue);
|
||||
const section = useResumeStore((state) => {
|
||||
if (!id) return null;
|
||||
return get(state.resume.data.sections, id);
|
||||
}) as SectionWithItem<T> | null;
|
||||
const name = useMemo(() => section?.name ?? "", [section?.name]);
|
||||
|
||||
const isCreate = mode === "create";
|
||||
const isUpdate = mode === "update";
|
||||
@ -111,18 +112,16 @@ export const SectionDialog = <T extends SectionItem>({
|
||||
<Form {...form}>
|
||||
<form>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you sure you want to delete this {name}?</AlertDialogTitle>
|
||||
<AlertDialogTitle>{t`Are you sure you want to delete this item?`}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action can be reverted by clicking on the undo button in the floating
|
||||
toolbar.
|
||||
{t`This action can be reverted by clicking on the undo button in the floating toolbar.`}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
|
||||
<AlertDialogCancel>{t`Cancel`}</AlertDialogCancel>
|
||||
<AlertDialogAction variant="error" onClick={form.handleSubmit(onSubmit)}>
|
||||
Delete
|
||||
{t`Delete`}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</form>
|
||||
@ -144,9 +143,9 @@ export const SectionDialog = <T extends SectionItem>({
|
||||
{isUpdate && <PencilSimple />}
|
||||
{isDuplicate && <CopySimple />}
|
||||
<h2>
|
||||
{isCreate && `Create a new ${name}`}
|
||||
{isUpdate && `Update an existing ${name}`}
|
||||
{isDuplicate && `Duplicate an existing ${name}`}
|
||||
{isCreate && t`Create a new item`}
|
||||
{isUpdate && t`Update an existing item`}
|
||||
{isDuplicate && t`Duplicate an existing item`}
|
||||
</h2>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
@ -156,9 +155,9 @@ export const SectionDialog = <T extends SectionItem>({
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="submit">
|
||||
{isCreate && "Create"}
|
||||
{isUpdate && "Save Changes"}
|
||||
{isDuplicate && "Duplicate"}
|
||||
{isCreate && t`Create`}
|
||||
{isUpdate && t`Save Changes`}
|
||||
{isDuplicate && t`Duplicate`}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { t } from "@lingui/macro";
|
||||
import { CopySimple, DotsSixVertical, PencilSimple, TrashSimple } from "@phosphor-icons/react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@ -81,19 +82,19 @@ export const SectionListItem = ({
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="center" side="left" sideOffset={-16}>
|
||||
<DropdownMenuCheckboxItem checked={visible} onCheckedChange={onToggleVisibility}>
|
||||
<span className="-ml-0.5">Visible</span>
|
||||
<span className="-ml-0.5">{t`Visible`}</span>
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuItem onClick={onUpdate}>
|
||||
<PencilSimple size={14} />
|
||||
<span className="ml-2">Edit</span>
|
||||
<span className="ml-2">{t`Edit`}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onDuplicate}>
|
||||
<CopySimple size={14} />
|
||||
<span className="ml-2">Copy</span>
|
||||
<span className="ml-2">{t`Copy`}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="text-error" onClick={onDelete}>
|
||||
<TrashSimple size={14} />
|
||||
<span className="ml-2">Remove</span>
|
||||
<span className="ml-2">{t`Remove`}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { plural, t } from "@lingui/macro";
|
||||
import {
|
||||
ArrowCounterClockwise,
|
||||
Broom,
|
||||
@ -63,7 +64,7 @@ export const SectionOptions = ({ id }: Props) => {
|
||||
<>
|
||||
<DropdownMenuItem onClick={onCreate}>
|
||||
<Plus />
|
||||
<span className="ml-2">Add a new item</span>
|
||||
<span className="ml-2">{t`Add a new item`}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
@ -73,12 +74,12 @@ export const SectionOptions = ({ id }: Props) => {
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem onClick={toggleVisibility}>
|
||||
{section.visible ? <Eye /> : <EyeSlash />}
|
||||
<span className="ml-2">{section.visible ? "Hide" : "Show"}</span>
|
||||
<span className="ml-2">{section.visible ? t`Hide` : t`Show`}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<PencilSimple />
|
||||
<span className="ml-2">Rename</span>
|
||||
<span className="ml-2">{t`Rename`}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
<div className="relative col-span-2">
|
||||
@ -103,15 +104,15 @@ export const SectionOptions = ({ id }: Props) => {
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<Columns />
|
||||
<span className="ml-2">Columns</span>
|
||||
<span className="ml-2">{t`Columns`}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuRadioGroup value={`${section.columns}`} onValueChange={onChangeColumns}>
|
||||
<DropdownMenuRadioItem value="1">1 Column</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="2">2 Columns</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="3">3 Columns</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="4">4 Columns</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="5">5 Columns</DropdownMenuRadioItem>
|
||||
{Array.from({ length: 5 }, (_, i) => i + 1).map((value) => (
|
||||
<DropdownMenuRadioItem value={`${value}`}>
|
||||
{plural(value, { one: "Column", other: "Columns" })}
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
@ -119,12 +120,12 @@ export const SectionOptions = ({ id }: Props) => {
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem disabled={!hasItems} onClick={onResetItems}>
|
||||
<Broom />
|
||||
<span className="ml-2">Reset</span>
|
||||
<span className="ml-2">{t`Reset`}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="text-error" disabled={!isCustomSection} onClick={onRemove}>
|
||||
<TrashSimple />
|
||||
<span className="ml-2">Remove</span>
|
||||
<span className="ml-2">{t`Remove`}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Tag } from "@phosphor-icons/react";
|
||||
import { URL, urlSchema } from "@reactive-resume/schema";
|
||||
import { Button, Input, Popover, PopoverContent, PopoverTrigger } from "@reactive-resume/ui";
|
||||
@ -36,14 +37,14 @@ export const URLInput = forwardRef<HTMLInputElement, Props>(
|
||||
<PopoverContent className="p-1.5">
|
||||
<Input
|
||||
value={value.label}
|
||||
placeholder="Label"
|
||||
placeholder={t`Label`}
|
||||
onChange={(event) => onChange({ ...value, label: event.target.value })}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
{hasError && <small className="opacity-75">URL must start with https://</small>}
|
||||
{hasError && <small className="opacity-75">{t`URL must start with https://`}</small>}
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { ScrollArea, Separator } from "@reactive-resume/ui";
|
||||
import { useRef } from "react";
|
||||
|
||||
@ -56,26 +57,30 @@ export const RightSidebar = () => {
|
||||
<div />
|
||||
|
||||
<div className="flex flex-col items-center justify-center gap-y-2">
|
||||
<SectionIcon id="template" name="Template" onClick={() => scrollIntoView("#template")} />
|
||||
<SectionIcon id="layout" name="Layout" onClick={() => scrollIntoView("#layout")} />
|
||||
<SectionIcon
|
||||
id="template"
|
||||
name={t`Template`}
|
||||
onClick={() => scrollIntoView("#template")}
|
||||
/>
|
||||
<SectionIcon id="layout" name={t`Layout`} onClick={() => scrollIntoView("#layout")} />
|
||||
<SectionIcon
|
||||
id="typography"
|
||||
name="Typography"
|
||||
name={t`Typography`}
|
||||
onClick={() => scrollIntoView("#typography")}
|
||||
/>
|
||||
<SectionIcon id="theme" name="Theme" onClick={() => scrollIntoView("#theme")} />
|
||||
<SectionIcon id="page" name="Page" onClick={() => scrollIntoView("#page")} />
|
||||
<SectionIcon id="sharing" name="Sharing" onClick={() => scrollIntoView("#sharing")} />
|
||||
<SectionIcon id="theme" name={t`Theme`} onClick={() => scrollIntoView("#theme")} />
|
||||
<SectionIcon id="page" name={t`Page`} onClick={() => scrollIntoView("#page")} />
|
||||
<SectionIcon id="sharing" name={t`Sharing`} onClick={() => scrollIntoView("#sharing")} />
|
||||
<SectionIcon
|
||||
id="statistics"
|
||||
name="Statistics"
|
||||
name={t`Statistics`}
|
||||
onClick={() => scrollIntoView("#statistics")}
|
||||
/>
|
||||
<SectionIcon id="export" name="Export" onClick={() => scrollIntoView("#export")} />
|
||||
<SectionIcon id="notes" name="Notes" onClick={() => scrollIntoView("#notes")} />
|
||||
<SectionIcon id="export" name={t`Export`} onClick={() => scrollIntoView("#export")} />
|
||||
<SectionIcon id="notes" name={t`Notes`} onClick={() => scrollIntoView("#notes")} />
|
||||
<SectionIcon
|
||||
id="information"
|
||||
name="Information"
|
||||
name={t`Information`}
|
||||
onClick={() => scrollIntoView("#information")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { CircleNotch, FileJs, FilePdf } from "@phosphor-icons/react";
|
||||
import { buttonVariants, Card, CardContent, CardDescription, CardTitle } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
import { useToast } from "@/client/hooks/use-toast";
|
||||
import { usePrintResume } from "@/client/services/resume/print";
|
||||
import { useResumeStore } from "@/client/stores/resume";
|
||||
|
||||
import { getSectionIcon } from "../shared/section-icon";
|
||||
|
||||
export const ExportSection = () => {
|
||||
const { toast } = useToast();
|
||||
const { printResume, loading } = usePrintResume();
|
||||
|
||||
const onJsonExport = () => {
|
||||
@ -19,11 +18,6 @@ export const ExportSection = () => {
|
||||
const resumeJSON = JSON.stringify(resume.data, null, 2);
|
||||
|
||||
saveAs(new Blob([resumeJSON], { type: "application/json" }), filename);
|
||||
|
||||
toast({
|
||||
variant: "success",
|
||||
title: "A JSON snapshot of your resume has been successfully exported.",
|
||||
});
|
||||
};
|
||||
|
||||
const onPdfExport = async () => {
|
||||
@ -43,7 +37,7 @@ export const ExportSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("export")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Export</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Export`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -57,10 +51,9 @@ export const ExportSection = () => {
|
||||
>
|
||||
<FileJs size={22} />
|
||||
<CardContent className="flex-1">
|
||||
<CardTitle className="text-sm">JSON</CardTitle>
|
||||
<CardTitle className="text-sm">{t`JSON`}</CardTitle>
|
||||
<CardDescription className="font-normal">
|
||||
Download a JSON snapshot of your resume. This file can be used to import your resume
|
||||
in the future, or can even be shared with others to collaborate.
|
||||
{t`Download a JSON snapshot of your resume. This file can be used to import your resume in the future, or can even be shared with others to collaborate.`}
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -76,10 +69,9 @@ export const ExportSection = () => {
|
||||
{loading ? <CircleNotch size={22} className="animate-spin" /> : <FilePdf size={22} />}
|
||||
|
||||
<CardContent className="flex-1">
|
||||
<CardTitle className="text-sm">PDF</CardTitle>
|
||||
<CardTitle className="text-sm">{t`PDF`}</CardTitle>
|
||||
<CardDescription className="font-normal">
|
||||
Download a PDF of your resume. This file can be used to print your resume, send it to
|
||||
recruiters, or upload on job portals.
|
||||
{t`Download a PDF of your resume. This file can be used to print your resume, send it to recruiters, or upload on job portals.`}
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import { Book, EnvelopeSimpleOpen, GithubLogo, HandHeart } from "@phosphor-icons/react";
|
||||
import {
|
||||
buttonVariants,
|
||||
@ -14,28 +15,30 @@ import { getSectionIcon } from "../shared/section-icon";
|
||||
const DonateCard = () => (
|
||||
<Card className="space-y-4 bg-info text-info-foreground">
|
||||
<CardContent className="space-y-2">
|
||||
<CardTitle>Support the app by donating what you can!</CardTitle>
|
||||
<CardTitle>{t`Support the app by donating what you can!`}</CardTitle>
|
||||
<CardDescription className="space-y-2">
|
||||
<p>
|
||||
I built Reactive Resume mostly by myself during my spare time, with a lot of help from
|
||||
other great open-source contributors.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you like the app and want to support keeping it free forever, please donate whatever
|
||||
you can afford to give.
|
||||
</p>
|
||||
<Trans>
|
||||
<p>
|
||||
I built Reactive Resume mostly by myself during my spare time, with a lot of help from
|
||||
other great open-source contributors.
|
||||
</p>
|
||||
<p>
|
||||
If you like the app and want to support keeping it free forever, please donate whatever
|
||||
you can afford to give.
|
||||
</p>
|
||||
<p>Your donations could be tax-deductible, depending on your location.</p>
|
||||
</Trans>
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<a
|
||||
href="https://opencollective.com/reactive-resume"
|
||||
className={cn(buttonVariants({ size: "sm" }))}
|
||||
href="https://github.com/sponsors/AmruthPillai"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
target="_blank"
|
||||
>
|
||||
<HandHeart size={14} weight="bold" className="mr-2" />
|
||||
<span>Donate to Reactive Resume</span>
|
||||
<span>{t`Donate to Reactive Resume`}</span>
|
||||
</a>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
@ -44,36 +47,37 @@ const DonateCard = () => (
|
||||
const IssuesCard = () => (
|
||||
<Card className="space-y-4">
|
||||
<CardContent className="space-y-2">
|
||||
<CardTitle>Found a bug, or have an idea for a new feature?</CardTitle>
|
||||
<CardTitle>{t`Found a bug, or have an idea for a new feature?`}</CardTitle>
|
||||
<CardDescription className="space-y-2">
|
||||
<p>I'm sure the app is not perfect, but I'd like for it to be.</p>
|
||||
|
||||
<p>
|
||||
If you faced any issues while creating your resume, or have an idea that would help you
|
||||
and other users in creating your resume more easily, drop an issue on the repository or
|
||||
send me an email about it.
|
||||
</p>
|
||||
<Trans>
|
||||
<p>I'm sure the app is not perfect, but I'd like for it to be.</p>
|
||||
<p>
|
||||
If you faced any issues while creating your resume, or have an idea that would help you
|
||||
and other users in creating your resume more easily, drop an issue on the repository or
|
||||
send me an email about it.
|
||||
</p>
|
||||
</Trans>
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
<CardFooter className="space-x-4">
|
||||
<a
|
||||
className={cn(buttonVariants({ size: "sm" }))}
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/issues/new/choose"
|
||||
target="_blank"
|
||||
className={cn(buttonVariants({ size: "sm" }))}
|
||||
rel="noopener noreferrer nofollow"
|
||||
target="_blank"
|
||||
>
|
||||
<GithubLogo size={14} weight="bold" className="mr-2" />
|
||||
<span>Raise an issue</span>
|
||||
<span>{t`Raise an issue`}</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
className={cn(buttonVariants({ size: "sm" }))}
|
||||
href="mailto:hello@amruthpillai.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
target="_blank"
|
||||
>
|
||||
<EnvelopeSimpleOpen size={14} weight="bold" className="mr-2" />
|
||||
<span>Send me a message</span>
|
||||
<span>{t`Send me a message`}</span>
|
||||
</a>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
@ -82,17 +86,18 @@ const IssuesCard = () => (
|
||||
const DocumentationCard = () => (
|
||||
<Card className="space-y-4">
|
||||
<CardContent className="space-y-2">
|
||||
<CardTitle>Don't know where to begin? Hit the docs!</CardTitle>
|
||||
<CardTitle>{t`Don't know where to begin? Hit the docs!`}</CardTitle>
|
||||
<CardDescription className="space-y-2">
|
||||
<p>
|
||||
The community has spent a lot of time writing the documentation for Reactive Resume, and
|
||||
I'm sure it will help you get started with the app.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There are also a lot of examples to help you get started, and features that you might not
|
||||
know about which could help you build your perfect resume.
|
||||
</p>
|
||||
<Trans>
|
||||
<p>
|
||||
The community has spent a lot of time writing the documentation for Reactive Resume, and
|
||||
I'm sure it will help you get started with the app.
|
||||
</p>
|
||||
<p>
|
||||
There are also a lot of examples to help you get started, and features that you might
|
||||
not know about which could help you build your perfect resume.
|
||||
</p>
|
||||
</Trans>
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
<CardFooter className="space-x-4">
|
||||
@ -103,7 +108,7 @@ const DocumentationCard = () => (
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
<Book size={14} weight="bold" className="mr-2" />
|
||||
<span>Documentation</span>
|
||||
<span>{t`Documentation`}</span>
|
||||
</a>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
@ -115,7 +120,7 @@ export const InformationSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("information")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Information</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Information`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
verticalListSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import { ArrowCounterClockwise, DotsSixVertical, Plus, TrashSimple } from "@phosphor-icons/react";
|
||||
import { defaultMetadata } from "@reactive-resume/schema";
|
||||
import { Button, Portal, Tooltip } from "@reactive-resume/ui";
|
||||
@ -203,10 +204,10 @@ export const LayoutSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("layout")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Layout</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Layout`}</h2>
|
||||
</div>
|
||||
|
||||
<Tooltip content="Reset Layout">
|
||||
<Tooltip content={t`Reset Layout`}>
|
||||
<Button size="icon" variant="ghost" onClick={onResetLayout}>
|
||||
<ArrowCounterClockwise />
|
||||
</Button>
|
||||
@ -232,7 +233,9 @@ export const LayoutSection = () => {
|
||||
return (
|
||||
<div key={pageIndex} className="rounded border p-3 pb-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="mb-3 text-xs font-bold">Page {pageIndex + 1}</p>
|
||||
<p className="mb-3 text-xs font-bold">
|
||||
<Trans>Page {pageIndex + 1}</Trans>
|
||||
</p>
|
||||
|
||||
{pageIndex !== 0 && (
|
||||
<Button
|
||||
@ -247,8 +250,8 @@ export const LayoutSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 items-start gap-x-4">
|
||||
<Column id={mainIndex} name="Main" items={main} />
|
||||
<Column id={sidebarIndex} name="Sidebar" items={sidebar} />
|
||||
<Column id={mainIndex} name={t`Main`} items={main} />
|
||||
<Column id={sidebarIndex} name={t`Sidebar`} items={sidebar} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -261,7 +264,7 @@ export const LayoutSection = () => {
|
||||
|
||||
<Button variant="outline" className="ml-auto" onClick={onAddPage}>
|
||||
<Plus />
|
||||
<span className="ml-2">Add New Page</span>
|
||||
<span className="ml-2">{t`Add New Page`}</span>
|
||||
</Button>
|
||||
</main>
|
||||
</section>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { RichInput } from "@reactive-resume/ui";
|
||||
|
||||
import { useResumeStore } from "@/client/stores/resume";
|
||||
@ -13,22 +14,20 @@ export const NotesSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("notes")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Notes</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Notes`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="grid gap-y-4">
|
||||
<p className="leading-relaxed">
|
||||
This section is reserved for your personal notes specific to this resume. The content here
|
||||
remains private and is not shared with anyone else.
|
||||
{t`This section is reserved for your personal notes specific to this resume. The content here remains private and is not shared with anyone else.`}
|
||||
</p>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<RichInput content={notes} onChange={(content) => setValue("metadata.notes", content)} />
|
||||
|
||||
<p className="text-xs leading-relaxed opacity-75">
|
||||
For example, information regarding which companies you sent this resume to or the links
|
||||
to the job descriptions can be noted down here.
|
||||
{t`For example, information regarding which companies you sent this resume to or the links to the job descriptions can be noted down here.`}
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import {
|
||||
Label,
|
||||
Select,
|
||||
@ -22,13 +23,13 @@ export const PageSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("page")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Page</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Page`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="grid gap-y-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label>Format</Label>
|
||||
<Label>{t`Format`}</Label>
|
||||
<Select
|
||||
value={page.format}
|
||||
onValueChange={(value) => {
|
||||
@ -36,17 +37,17 @@ export const PageSection = () => {
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Format" />
|
||||
<SelectValue placeholder={t`Format`} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="a4">A4</SelectItem>
|
||||
<SelectItem value="letter">Letter</SelectItem>
|
||||
<SelectItem value="a4">{t`A4`}</SelectItem>
|
||||
<SelectItem value="letter">{t`Letter`}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label>Margin</Label>
|
||||
<Label>{t`Margin`}</Label>
|
||||
<div className="flex items-center gap-x-4 py-1">
|
||||
<Slider
|
||||
min={0}
|
||||
@ -63,7 +64,7 @@ export const PageSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label>Options</Label>
|
||||
<Label>{t`Options`}</Label>
|
||||
|
||||
<div className="py-2">
|
||||
<div className="flex items-center gap-x-4">
|
||||
@ -74,7 +75,7 @@ export const PageSection = () => {
|
||||
setValue("metadata.page.options.breakLine", checked);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="metadata.page.options.breakLine">Show Break Line</Label>
|
||||
<Label htmlFor="metadata.page.options.breakLine">{t`Show Break Line`}</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -87,7 +88,7 @@ export const PageSection = () => {
|
||||
setValue("metadata.page.options.pageNumbers", checked);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="metadata.page.options.pageNumbers">Show Page Numbers</Label>
|
||||
<Label htmlFor="metadata.page.options.pageNumbers">{t`Show Page Numbers`}</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { CopySimple } from "@phosphor-icons/react";
|
||||
import { Button, Input, Label, Switch, Tooltip } from "@reactive-resume/ui";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
@ -25,9 +26,8 @@ export const SharingSection = () => {
|
||||
|
||||
toast({
|
||||
variant: "success",
|
||||
title: "A link has been copied to your clipboard.",
|
||||
description:
|
||||
"Anyone with this link can view and download the resume. Share it on your profile or with recruiters.",
|
||||
title: t`A link has been copied to your clipboard.`,
|
||||
description: t`Anyone with this link can view and download the resume. Share it on your profile or with recruiters.`,
|
||||
});
|
||||
};
|
||||
|
||||
@ -36,7 +36,7 @@ export const SharingSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("sharing")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Sharing</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Sharing`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -52,9 +52,9 @@ export const SharingSection = () => {
|
||||
/>
|
||||
<div>
|
||||
<Label htmlFor="visibility" className="space-y-1">
|
||||
<p>Public</p>
|
||||
<p>{t`Public`}</p>
|
||||
<p className="text-xs opacity-60">
|
||||
Anyone with the link can view and download the resume.
|
||||
{t`Anyone with the link can view and download the resume.`}
|
||||
</p>
|
||||
</Label>
|
||||
</div>
|
||||
@ -70,12 +70,12 @@ export const SharingSection = () => {
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<Label htmlFor="resume-url">URL</Label>
|
||||
<Label htmlFor="resume-url">{t`URL`}</Label>
|
||||
|
||||
<div className="flex gap-x-1.5">
|
||||
<Input id="resume-url" readOnly value={url} className="flex-1" />
|
||||
|
||||
<Tooltip content="Copy to Clipboard">
|
||||
<Tooltip content={t`Copy to Clipboard`}>
|
||||
<Button size="icon" variant="ghost" onClick={onCopy}>
|
||||
<CopySimple />
|
||||
</Button>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Info } from "@phosphor-icons/react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
@ -19,7 +20,7 @@ export const StatisticsSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("statistics")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Statistics</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Statistics`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -34,12 +35,9 @@ export const StatisticsSection = () => {
|
||||
>
|
||||
<Alert variant="info">
|
||||
<Info size={18} />
|
||||
|
||||
<AlertTitle>Statistics are available only for public resumes.</AlertTitle>
|
||||
|
||||
<AlertTitle>{t`Statistics are available only for public resumes.`}</AlertTitle>
|
||||
<AlertDescription className="text-xs leading-relaxed">
|
||||
You can track the number of views your resume has received, or how many people
|
||||
have downloaded the resume by enabling public sharing.
|
||||
{t`You can track the number of views your resume has received, or how many people have downloaded the resume by enabling public sharing.`}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</motion.div>
|
||||
@ -50,14 +48,14 @@ export const StatisticsSection = () => {
|
||||
<h3 className={cn("text-4xl font-bold blur-none transition-all", !isPublic && "blur-sm")}>
|
||||
{statistics?.views ?? 0}
|
||||
</h3>
|
||||
<p className="opacity-75">Views</p>
|
||||
<p className="opacity-75">{t`Views`}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className={cn("text-4xl font-bold blur-none transition-all", !isPublic && "blur-sm")}>
|
||||
{statistics?.downloads ?? 0}
|
||||
</h3>
|
||||
<p className="opacity-75">Downloads</p>
|
||||
<p className="opacity-75">{t`Downloads`}</p>
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Button, HoverCard, HoverCardContent, HoverCardTrigger } from "@reactive-resume/ui";
|
||||
import { cn, templatesList } from "@reactive-resume/utils";
|
||||
|
||||
@ -14,7 +15,7 @@ export const TemplateSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("template")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Template</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Template`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Input, Label, Popover, PopoverContent, PopoverTrigger } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
@ -16,7 +17,7 @@ export const ThemeSection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("theme")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Theme</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Theme`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -39,7 +40,7 @@ export const ThemeSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="theme.primary">Primary Color</Label>
|
||||
<Label htmlFor="theme.primary">{t`Primary Color`}</Label>
|
||||
<div className="relative">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
@ -69,7 +70,7 @@ export const ThemeSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="theme.primary">Background Color</Label>
|
||||
<Label htmlFor="theme.primary">{t`Background Color`}</Label>
|
||||
<div className="relative">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
@ -99,7 +100,7 @@ export const ThemeSection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="theme.primary">Text Color</Label>
|
||||
<Label htmlFor="theme.primary">{t`Text Color`}</Label>
|
||||
<div className="relative">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
/* eslint-disable lingui/no-unlocalized-strings */
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
import { Button, Combobox, ComboboxOption, Label, Slider, Switch } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { fonts } from "@reactive-resume/utils";
|
||||
@ -61,7 +64,7 @@ export const TypographySection = () => {
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("typography")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Typography</h2>
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">{t`Typography`}</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -89,11 +92,11 @@ export const TypographySection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label>Font Family</Label>
|
||||
<Label>{t`Font Family`}</Label>
|
||||
<Combobox
|
||||
options={families}
|
||||
value={typography.font.family}
|
||||
searchPlaceholder="Search for a font family"
|
||||
searchPlaceholder={t`Search for a font family`}
|
||||
onValueChange={(value) => {
|
||||
setValue("metadata.typography.font.family", value);
|
||||
setValue("metadata.typography.font.subset", "latin");
|
||||
@ -104,11 +107,11 @@ export const TypographySection = () => {
|
||||
|
||||
<div className="grid grid-cols-2 gap-x-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label>Font Subset</Label>
|
||||
<Label>{t`Font Subset`}</Label>
|
||||
<Combobox
|
||||
options={subsets}
|
||||
value={typography.font.subset}
|
||||
searchPlaceholder="Search for a font subset"
|
||||
searchPlaceholder={t`Search for a font subset`}
|
||||
onValueChange={(value) => {
|
||||
setValue("metadata.typography.font.subset", value);
|
||||
}}
|
||||
@ -116,12 +119,12 @@ export const TypographySection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label>Font Variants</Label>
|
||||
<Label>{t`Font Variants`}</Label>
|
||||
<Combobox
|
||||
multiple
|
||||
options={variants}
|
||||
value={typography.font.variants}
|
||||
searchPlaceholder="Search for a font variant"
|
||||
searchPlaceholder={t`Search for a font variant`}
|
||||
onValueChange={(value) => {
|
||||
setValue("metadata.typography.font.variants", value);
|
||||
}}
|
||||
@ -130,7 +133,7 @@ export const TypographySection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label>Font Size</Label>
|
||||
<Label>{t`Font Size`}</Label>
|
||||
<div className="flex items-center gap-x-4 py-1">
|
||||
<Slider
|
||||
min={12}
|
||||
@ -147,7 +150,7 @@ export const TypographySection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label>Line Height</Label>
|
||||
<Label>{t`Line Height`}</Label>
|
||||
<div className="flex items-center gap-x-4 py-1">
|
||||
<Slider
|
||||
min={0}
|
||||
@ -164,7 +167,7 @@ export const TypographySection = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label>Options</Label>
|
||||
<Label>{t`Options`}</Label>
|
||||
|
||||
<div className="py-2">
|
||||
<div className="flex items-center gap-x-4">
|
||||
@ -175,7 +178,7 @@ export const TypographySection = () => {
|
||||
setValue("metadata.typography.underlineLinks", checked);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="metadata.typography.underlineLinks">Underline Links</Label>
|
||||
<Label htmlFor="metadata.typography.underlineLinks">{t`Underline Links`}</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user