fix(client): 🐛 do not allow private resumes to be viewable or downloadable through the link

This commit is contained in:
Amruth Pillai
2023-07-12 15:59:22 +02:00
parent 5ef4bfcb6b
commit 1c2d796c50
121 changed files with 3193 additions and 2068 deletions

View File

@ -15,7 +15,7 @@
.controller {
@apply z-20 flex items-center justify-center shadow-lg;
@apply flex rounded-l-full rounded-r-full px-4;
@apply bg-neutral-50 dark:bg-neutral-800;
@apply bg-zinc-50 dark:bg-zinc-900;
@apply opacity-70 transition-opacity duration-200 hover:opacity-100;
> button {
@ -23,6 +23,6 @@
}
> hr {
@apply mx-3 h-5 w-0.5 bg-neutral-900/40 dark:bg-neutral-50/20;
@apply mx-3 h-5 w-0.5 bg-zinc-900/40 dark:bg-zinc-50/20;
}
}

View File

@ -12,7 +12,6 @@ import {
ZoomOut,
} from '@mui/icons-material';
import { ButtonBase, Divider, Tooltip, useMediaQuery, useTheme } from '@mui/material';
import clsx from 'clsx';
import dayjs from 'dayjs';
import get from 'lodash/get';
import { useTranslation } from 'next-i18next';
@ -26,6 +25,7 @@ import { printResumeAsPdf, PrintResumeAsPdfParams } from '@/services/printer';
import { togglePageBreakLine, togglePageOrientation, toggleSidebar } from '@/store/build/buildSlice';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import getResumeUrl from '@/utils/getResumeUrl';
import { cn } from '@/utils/styles';
import styles from './ArtboardController.module.scss';
@ -60,7 +60,7 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
const url = getResumeUrl(resume, { withHost: true });
await navigator.clipboard.writeText(url);
toast.success(t<string>('common.toast.success.resume-link-copied'));
toast.success(t('common.toast.success.resume-link-copied'));
};
const handleExportPDF = async () => {
@ -77,40 +77,40 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
return (
<div
className={clsx({
className={cn({
[styles.container]: true,
[styles.pushLeft]: left.open,
[styles.pushRight]: right.open,
})}
>
<div className={styles.controller}>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.undo')}>
<ButtonBase onClick={handleUndo} className={clsx({ 'pointer-events-none opacity-50': past.length < 2 })}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.undo')}>
<ButtonBase onClick={handleUndo} className={cn({ 'pointer-events-none opacity-50': past.length < 2 })}>
<UndoOutlined fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.redo')}>
<ButtonBase onClick={handleRedo} className={clsx({ 'pointer-events-none opacity-50': future.length === 0 })}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.redo')}>
<ButtonBase onClick={handleRedo} className={cn({ 'pointer-events-none opacity-50': future.length === 0 })}>
<RedoOutlined fontSize="medium" />
</ButtonBase>
</Tooltip>
<Divider />
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-in')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.zoom-in')}>
<ButtonBase onClick={() => zoomIn(0.25)}>
<ZoomIn fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-out')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.zoom-out')}>
<ButtonBase onClick={() => zoomOut(0.25)}>
<ZoomOut fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.center-artboard')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.center-artboard')}>
<ButtonBase onClick={() => centerView(0.95)}>
<FilterCenterFocus fontSize="medium" />
</ButtonBase>
@ -120,10 +120,10 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
{isDesktop && (
<>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-orientation')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-orientation')}>
<ButtonBase
onClick={handleTogglePageOrientation}
className={clsx({ 'pointer-events-none opacity-50': pages.length === 1 })}
className={cn({ 'pointer-events-none opacity-50': pages.length === 1 })}
>
{orientation === 'vertical' ? (
<AlignHorizontalCenter fontSize="medium" />
@ -133,13 +133,13 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-page-break-line')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-page-break-line')}>
<ButtonBase onClick={handleTogglePageBreakLine}>
<InsertPageBreak fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-sidebars')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-sidebars')}>
<ButtonBase onClick={handleToggleSidebar}>
<ViewSidebar fontSize="medium" />
</ButtonBase>
@ -149,13 +149,13 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
</>
)}
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.copy-link')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.copy-link')}>
<ButtonBase onClick={handleCopyLink}>
<Link fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.export-pdf')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.export-pdf')}>
<ButtonBase onClick={handleExportPDF} disabled={isLoading}>
<Download fontSize="medium" />
</ButtonBase>

View File

@ -1,7 +1,7 @@
.center {
@apply mx-0 flex flex-grow pt-12 lg:pt-16;
@apply transition-[margin-left,margin-right] duration-200;
@apply bg-neutral-200 dark:bg-neutral-900;
@apply bg-zinc-100 dark:bg-zinc-900;
}
.wrapper {

View File

@ -1,9 +1,9 @@
import clsx from 'clsx';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
import { useAppSelector } from '@/store/hooks';
import { cn } from '@/utils/styles';
import ArtboardController from './ArtboardController';
import styles from './Center.module.scss';
@ -19,7 +19,7 @@ const Center = () => {
if (isEmpty(resume)) return null;
return (
<div className={clsx(styles.center)}>
<div className={cn(styles.center)}>
<Header />
<TransformWrapper
@ -35,7 +35,7 @@ const Center = () => {
<>
<TransformComponent wrapperClass={styles.wrapper}>
<div
className={clsx({
className={cn({
[styles.artboard]: true,
'flex-col': orientation === 'vertical',
})}

View File

@ -1,10 +1,10 @@
.header {
@apply mx-0 flex justify-between shadow;
@apply bg-neutral-800 text-neutral-100;
@apply bg-zinc-900 text-zinc-100;
@apply transition-[margin-left,margin-right] duration-200;
button > svg {
@apply text-base text-neutral-100;
@apply text-base text-zinc-100;
}
}

View File

@ -20,7 +20,6 @@ import {
useMediaQuery,
useTheme,
} from '@mui/material';
import clsx from 'clsx';
import get from 'lodash/get';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
@ -37,6 +36,7 @@ import { setSidebarState, toggleSidebar } from '@/store/build/buildSlice';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { setModalState } from '@/store/modal/modalSlice';
import getResumeUrl from '@/utils/getResumeUrl';
import { cn } from '@/utils/styles';
import styles from './Header.module.scss';
@ -102,7 +102,7 @@ const Header = () => {
},
},
},
})
}),
);
};
@ -132,14 +132,14 @@ const Header = () => {
const url = getResumeUrl(resume, { withHost: true });
await navigator.clipboard.writeText(url);
toast.success(t<string>('common.toast.success.resume-link-copied'));
toast.success(t('common.toast.success.resume-link-copied'));
};
return (
<AppBar elevation={0} position="fixed">
<Toolbar
variant="regular"
className={clsx({
className={cn({
[styles.header]: true,
[styles.pushLeft]: left.open,
[styles.pushRight]: right.open,
@ -165,14 +165,14 @@ const Header = () => {
<ListItemIcon>
<DriveFileRenameOutline className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.rename')}</ListItemText>
<ListItemText>{t('builder.header.menu.rename')}</ListItemText>
</MenuItem>
<MenuItem onClick={handleDuplicate}>
<ListItemIcon>
<CopyAll className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.duplicate')}</ListItemText>
<ListItemText>{t('builder.header.menu.duplicate')}</ListItemText>
</MenuItem>
{resume.public ? (
@ -180,27 +180,27 @@ const Header = () => {
<ListItemIcon>
<LinkIcon className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.share-link')}</ListItemText>
<ListItemText>{t('builder.header.menu.share-link')}</ListItemText>
</MenuItem>
) : (
<Tooltip arrow placement="right" title={t<string>('builder.header.menu.tooltips.share-link')}>
<Tooltip arrow placement="right" title={t('builder.header.menu.tooltips.share-link')}>
<div>
<MenuItem>
<ListItemIcon>
<LinkIcon className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.share-link')}</ListItemText>
<ListItemText>{t('builder.header.menu.share-link')}</ListItemText>
</MenuItem>
</div>
</Tooltip>
)}
<Tooltip arrow placement="right" title={t<string>('builder.header.menu.tooltips.delete')}>
<Tooltip arrow placement="right" title={t('builder.header.menu.tooltips.delete')}>
<MenuItem onClick={handleDelete}>
<ListItemIcon>
<Delete className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.delete')}</ListItemText>
<ListItemText>{t('builder.header.menu.delete')}</ListItemText>
</MenuItem>
</Tooltip>
</Menu>

View File

@ -18,8 +18,8 @@
content: 'Page Break';
top: calc(297mm - 19px);
@apply absolute w-full border-b border-dashed border-neutral-800/75;
@apply flex items-end justify-end pr-2 pb-0.5 text-xs font-bold text-neutral-800/75;
@apply absolute w-full border-b border-dashed border-zinc-900/75;
@apply flex items-end justify-end pr-2 pb-0.5 text-xs font-bold text-zinc-900/75;
@apply print:hidden;
:global(.preview-mode) &,

View File

@ -49,9 +49,7 @@ const Page: React.FC<Props> = ({ page, showPageNumbers = false }) => {
{TemplatePage && <TemplatePage page={page} />}
</div>
{showPageNumbers && (
<h4 className={styles.pageNumber}>{`${t<string>('builder.common.glossary.page')} ${page + 1}`}</h4>
)}
{showPageNumbers && <h4 className={styles.pageNumber}>{`${t('builder.common.glossary.page')} ${page + 1}`}</h4>}
</div>
);
};

View File

@ -1,12 +1,12 @@
.container {
@apply h-screen w-[95vw] md:w-[70vw] lg:w-[50vw] xl:w-[30vw] 2xl:w-[28vw];
@apply bg-neutral-50 text-neutral-900 dark:bg-neutral-900 dark:text-neutral-50;
@apply relative flex border-r-2 border-neutral-50/10;
@apply bg-zinc-100 text-zinc-900 dark:bg-zinc-900 dark:text-zinc-100;
@apply relative flex border-r-2 border-zinc-100/10;
nav {
@apply absolute inset-y-0 left-0;
@apply w-14 py-4 md:w-16 md:px-2;
@apply bg-neutral-100 shadow dark:bg-neutral-800;
@apply bg-zinc-100 shadow dark:bg-zinc-900;
@apply flex flex-col items-center justify-between;
hr {
@ -29,7 +29,7 @@
> section {
@apply grid gap-4;
@apply pt-5 pb-7 first:pt-0;
@apply border-b border-neutral-900/10 last:border-b-0 dark:border-neutral-50/10;
@apply border-b border-zinc-900/10 last:border-b-0 dark:border-zinc-100/10;
hr {
@apply my-2;

View File

@ -8,7 +8,7 @@ import React, { ReactComponentElement, useMemo } from 'react';
import { Section as SectionRecord } from 'schema';
import { validate } from 'uuid';
import Logo from '@/components/shared/Logo';
import Icon from '@/components/shared/Icon';
import { getCustomSections, getSectionsByType, left } from '@/config/sections';
import { setSidebarState } from '@/store/build/buildSlice';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
@ -69,7 +69,7 @@ const LeftSidebar = () => {
sectionsComponents.push(
<section key={id} id={id}>
{component}
</section>
</section>,
);
if (addMore) {
@ -89,7 +89,7 @@ const LeftSidebar = () => {
elements.push(
<section key={newId} id={`section-${newId}`}>
{newComponent}
</section>
</section>,
);
}
sectionsComponents.push(...elements);
@ -112,7 +112,9 @@ const LeftSidebar = () => {
<nav className="overflow-y-auto">
<div>
<Link href="/dashboard">
<Logo size={40} />
<IconButton>
<Icon size={24} />
</IconButton>
</Link>
<Divider />
</div>
@ -123,7 +125,7 @@ const LeftSidebar = () => {
arrow
key={id}
placement="right"
title={t<string>(`builder.leftSidebar.sections.${id}.heading`, get(sections, `${id}.name`))}
title={t(`builder.leftSidebar.sections.${id}.heading`, get(sections, `${id}.name`))}
>
<IconButton onClick={() => handleClick(id)}>{icon}</IconButton>
</Tooltip>
@ -132,7 +134,7 @@ const LeftSidebar = () => {
{customSections.map(({ id }) => (
<Tooltip
key={id}
title={t<string>(`builder.leftSidebar.sections.${id}.heading`, get(sections, `${id}.name`))}
title={t(`builder.leftSidebar.sections.${id}.heading`, get(sections, `${id}.name`))}
placement="right"
arrow
>
@ -157,8 +159,8 @@ const LeftSidebar = () => {
<div className="py-6 text-right">
<Button fullWidth variant="outlined" startIcon={<Add />} onClick={handleAddSection}>
{t<string>('builder.common.actions.add', {
token: t<string>('builder.leftSidebar.sections.section.heading'),
{t('builder.common.actions.add', {
token: t('builder.leftSidebar.sections.section.heading'),
})}
</Button>
</div>

View File

@ -24,7 +24,7 @@ const Basics = () => {
return (
<>
<Heading path="sections.basics" name={t<string>('builder.leftSidebar.sections.basics.heading')} />
<Heading path="sections.basics" name={t('builder.leftSidebar.sections.basics.heading')} />
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div className="grid items-center gap-4 sm:col-span-2 sm:grid-cols-3">
@ -33,10 +33,10 @@ const Basics = () => {
</div>
<div className="grid w-full gap-2 sm:col-span-2">
<ResumeInput label={t<string>('builder.leftSidebar.sections.basics.name.label')} path="basics.name" />
<ResumeInput label={t('builder.leftSidebar.sections.basics.name.label')} path="basics.name" />
<Button variant="outlined" startIcon={<PhotoFilter />} onClick={handleClick}>
{t<string>('builder.leftSidebar.sections.basics.actions.photo-filters')}
{t('builder.leftSidebar.sections.basics.actions.photo-filters')}
</Button>
<Popover
@ -59,28 +59,24 @@ const Basics = () => {
<ResumeInput
type="date"
label={t<string>('builder.leftSidebar.sections.basics.birthdate.label')}
label={t('builder.leftSidebar.sections.basics.birthdate.label')}
path="basics.birthdate"
className="sm:col-span-2"
/>
<ResumeInput
label={t<string>('builder.common.form.email.label')}
path="basics.email"
className="sm:col-span-2"
/>
<ResumeInput label={t<string>('builder.common.form.phone.label')} path="basics.phone" />
<ResumeInput label={t<string>('builder.common.form.url.label')} path="basics.website" />
<ResumeInput label={t('builder.common.form.email.label')} path="basics.email" className="sm:col-span-2" />
<ResumeInput label={t('builder.common.form.phone.label')} path="basics.phone" />
<ResumeInput label={t('builder.common.form.url.label')} path="basics.website" />
<Divider className="sm:col-span-2" />
<ResumeInput
label={t<string>('builder.leftSidebar.sections.basics.headline.label')}
label={t('builder.leftSidebar.sections.basics.headline.label')}
path="basics.headline"
className="sm:col-span-2"
/>
<ResumeInput
type="textarea"
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
path="basics.summary"
className="sm:col-span-2"
markdownSupported

View File

@ -8,28 +8,19 @@ const Location = () => {
return (
<>
<Heading path="sections.location" name={t<string>('builder.leftSidebar.sections.location.heading')} />
<Heading path="sections.location" name={t('builder.leftSidebar.sections.location.heading')} />
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<ResumeInput
label={t<string>('builder.leftSidebar.sections.location.address.label')}
label={t('builder.leftSidebar.sections.location.address.label')}
path="basics.location.address"
className="sm:col-span-2"
/>
<ResumeInput label={t('builder.leftSidebar.sections.location.city.label')} path="basics.location.city" />
<ResumeInput label={t('builder.leftSidebar.sections.location.region.label')} path="basics.location.region" />
<ResumeInput label={t('builder.leftSidebar.sections.location.country.label')} path="basics.location.country" />
<ResumeInput
label={t<string>('builder.leftSidebar.sections.location.city.label')}
path="basics.location.city"
/>
<ResumeInput
label={t<string>('builder.leftSidebar.sections.location.region.label')}
path="basics.location.region"
/>
<ResumeInput
label={t<string>('builder.leftSidebar.sections.location.country.label')}
path="basics.location.country"
/>
<ResumeInput
label={t<string>('builder.leftSidebar.sections.location.postal-code.label')}
label={t('builder.leftSidebar.sections.location.postal-code.label')}
path="basics.location.postalCode"
/>
</div>

View File

@ -30,9 +30,9 @@ const PhotoFilters = () => {
const handleSetBorder = (value: boolean) => dispatch(setResumeState({ path: 'basics.photo.filters.border', value }));
return (
<div className="flex flex-col gap-2 p-5 dark:bg-neutral-800">
<div className="flex flex-col gap-2 p-5 dark:bg-zinc-900">
<div>
<h4 className="font-medium">{t<string>('builder.leftSidebar.sections.basics.photo-filters.size.heading')}</h4>
<h4 className="font-medium">{t('builder.leftSidebar.sections.basics.photo-filters.size.heading')}</h4>
<div className="mx-2">
<Slider
@ -54,20 +54,18 @@ const PhotoFilters = () => {
<Divider />
<div>
<h4 className="font-medium">
{t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.heading')}
</h4>
<h4 className="font-medium">{t('builder.leftSidebar.sections.basics.photo-filters.effects.heading')}</h4>
<div className="flex items-center">
<FormControlLabel
label={t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.grayscale.label')}
label={t('builder.leftSidebar.sections.basics.photo-filters.effects.grayscale.label')}
control={
<Checkbox color="secondary" checked={grayscale} onChange={(_, value) => handleSetGrayscale(value)} />
}
/>
<FormControlLabel
label={t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.border.label')}
label={t('builder.leftSidebar.sections.basics.photo-filters.effects.border.label')}
control={<Checkbox color="secondary" checked={border} onChange={(_, value) => handleSetBorder(value)} />}
/>
</div>
@ -76,7 +74,7 @@ const PhotoFilters = () => {
<Divider />
<div className="flex flex-col gap-2">
<h4 className="font-medium">{t<string>('builder.leftSidebar.sections.basics.photo-filters.shape.heading')}</h4>
<h4 className="font-medium">{t('builder.leftSidebar.sections.basics.photo-filters.shape.heading')}</h4>
<ToggleButtonGroup exclusive value={shape} onChange={(_, value) => handleChangeShape(value)}>
<ToggleButton size="small" value="square" className="w-14">

View File

@ -49,7 +49,7 @@ const PhotoUpload: React.FC = () => {
const file = event.target.files[0];
if (file.size > FILE_UPLOAD_MAX_SIZE) {
toast.error(t<string>('common.toast.error.upload-photo-size'));
toast.error(t('common.toast.error.upload-photo-size'));
return;
}
@ -67,8 +67,8 @@ const PhotoUpload: React.FC = () => {
<Tooltip
title={
isEmpty(photo.url)
? (t<string>('builder.leftSidebar.sections.basics.photo-upload.tooltip.upload') as string)
: (t<string>('builder.leftSidebar.sections.basics.photo-upload.tooltip.remove') as string)
? (t('builder.leftSidebar.sections.basics.photo-upload.tooltip.upload') as string)
: (t('builder.leftSidebar.sections.basics.photo-upload.tooltip.remove') as string)
}
>
<Avatar sx={{ width: 96, height: 96 }} src={photo.url} />

View File

@ -28,7 +28,7 @@ const Profiles = () => {
return (
<>
<Heading path="sections.profiles" name={t<string>('builder.leftSidebar.sections.profiles.heading')} />
<Heading path="sections.profiles" name={t('builder.leftSidebar.sections.profiles.heading')} />
<List
path="basics.profiles"
@ -40,8 +40,8 @@ const Profiles = () => {
<footer className="flex justify-end">
<Button variant="outlined" startIcon={<Add />} onClick={handleAdd}>
{t<string>('builder.common.actions.add', {
token: t<string>('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
{t('builder.common.actions.add', {
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
})}
</Button>
</footer>

View File

@ -98,8 +98,8 @@ const Section: React.FC<Props> = ({
<SectionSettings path={path} />
<Button variant="outlined" startIcon={<Add />} onClick={handleAdd}>
{t<string>('builder.common.actions.add', {
token: t<string>(`builder.leftSidebar.${path}.heading`, { defaultValue: heading }),
{t('builder.common.actions.add', {
token: t(`builder.leftSidebar.${path}.heading`, { defaultValue: heading }),
})}
</Button>
</footer>
@ -107,7 +107,7 @@ const Section: React.FC<Props> = ({
{addMore ? (
<div className="py-6 text-right">
<Button fullWidth variant="outlined" startIcon={<Add />} onClick={handleDuplicateSection}>
{t<string>('builder.common.actions.duplicate')}
{t('builder.common.actions.duplicate')}
</Button>
</div>
) : (

View File

@ -32,7 +32,7 @@ const SectionSettings: React.FC<Props> = ({ path }) => {
return (
<div>
<Tooltip title={t<string>('builder.common.columns.tooltip')}>
<Tooltip title={t('builder.common.columns.tooltip')}>
<ButtonBase onClick={handleClick} sx={{ padding: 1, borderRadius: 1 }} className="opacity-50 hover:opacity-75">
<ViewWeek /> <span className="ml-1.5 text-xs">{columns}</span>
</ButtonBase>
@ -47,8 +47,8 @@ const SectionSettings: React.FC<Props> = ({ path }) => {
horizontal: 'left',
}}
>
<div className="p-5 dark:bg-neutral-800">
<h4 className="mb-2 font-medium">{t<string>('builder.common.columns.heading')}</h4>
<div className="p-5 dark:bg-zinc-900">
<h4 className="mb-2 font-medium">{t('builder.common.columns.heading')}</h4>
<ToggleButtonGroup exclusive value={columns} onChange={(_, value: number) => handleSetColumns(value)}>
{[1, 2, 3, 4].map((index) => (

View File

@ -1,12 +1,12 @@
.container {
@apply h-screen w-[95vw] md:w-[70vw] lg:w-[50vw] xl:w-[30vw] 2xl:w-[28vw];
@apply bg-neutral-50 text-neutral-900 dark:bg-neutral-900 dark:text-neutral-50;
@apply relative flex border-l-2 border-neutral-50/10;
@apply bg-zinc-50 text-zinc-900 dark:bg-zinc-900 dark:text-zinc-50;
@apply relative flex border-l-2 border-zinc-50/10;
nav {
@apply absolute inset-y-0 right-0;
@apply w-12 py-4 md:w-16 md:px-2;
@apply bg-neutral-100 shadow dark:bg-neutral-800;
@apply bg-zinc-100 shadow dark:bg-zinc-900;
@apply flex flex-col items-center justify-between;
hr {
@ -29,7 +29,7 @@
> section {
@apply grid gap-4;
@apply pt-5 pb-7 first:pt-0;
@apply border-b border-neutral-900/10 last:border-b-0 dark:border-neutral-50/10;
@apply border-b border-zinc-900/10 last:border-b-0 dark:border-zinc-50/10;
hr {
@apply my-2;

View File

@ -45,7 +45,7 @@ const RightSidebar = () => {
<div className={styles.container}>
<nav className="overflow-y-auto">
<div>
<Avatar size={40} />
<Avatar size={24} />
<Divider />
</div>
@ -55,7 +55,7 @@ const RightSidebar = () => {
key={id}
arrow
placement="right"
title={t<string>(`builder.rightSidebar.sections.${id}.heading`, { defaultValue: capitalize(id) })}
title={t(`builder.rightSidebar.sections.${id}.heading`, { defaultValue: capitalize(id) })}
>
<IconButton onClick={() => handleClick(id)}>{icon}</IconButton>
</Tooltip>

View File

@ -18,7 +18,7 @@ const CustomCSS = () => {
const dispatch = useAppDispatch();
const customCSS: CustomCSSType = useAppSelector((state) =>
get(state.resume.present, 'metadata.css', {} as CustomCSSType)
get(state.resume.present, 'metadata.css', {} as CustomCSSType),
);
const handleChange = (value: string | undefined) => {
@ -27,7 +27,7 @@ const CustomCSS = () => {
return (
<>
<Heading path="metadata.css" name={t<string>('builder.rightSidebar.sections.css.heading')} isHideable />
<Heading path="metadata.css" name={t('builder.rightSidebar.sections.css.heading')} isHideable />
<Editor
height="200px"

View File

@ -20,12 +20,12 @@ const Export = () => {
const pdfListItemText = {
normal: {
primary: t<string>('builder.rightSidebar.sections.export.pdf.normal.primary'),
secondary: t<string>('builder.rightSidebar.sections.export.pdf.normal.secondary'),
primary: t('builder.rightSidebar.sections.export.pdf.normal.primary'),
secondary: t('builder.rightSidebar.sections.export.pdf.normal.secondary'),
},
loading: {
primary: t<string>('builder.rightSidebar.sections.export.pdf.loading.primary'),
secondary: t<string>('builder.rightSidebar.sections.export.pdf.loading.secondary'),
primary: t('builder.rightSidebar.sections.export.pdf.loading.primary'),
secondary: t('builder.rightSidebar.sections.export.pdf.loading.secondary'),
},
};
@ -55,7 +55,7 @@ const Export = () => {
return (
<>
<Heading path="metadata.export" name={t<string>('builder.rightSidebar.sections.export.heading')} />
<Heading path="metadata.export" name={t('builder.rightSidebar.sections.export.heading')} />
<List sx={{ padding: 0 }}>
<ListItem sx={{ padding: 0 }}>
@ -63,8 +63,8 @@ const Export = () => {
<Schema />
<ListItemText
primary={t<string>('builder.rightSidebar.sections.export.json.primary')}
secondary={t<string>('builder.rightSidebar.sections.export.json.secondary')}
primary={t('builder.rightSidebar.sections.export.json.primary')}
secondary={t('builder.rightSidebar.sections.export.json.secondary')}
/>
</ListItemButton>
</ListItem>

View File

@ -1,6 +1,6 @@
.page {
@apply relative border pl-4 pb-4 dark:border-neutral-100/10;
@apply rounded bg-neutral-100 dark:bg-neutral-800;
@apply relative border pl-4 pb-4 dark:border-zinc-100/10;
@apply rounded bg-zinc-100 dark:bg-zinc-900;
.delete {
@apply opacity-50 hover:opacity-75;
@ -28,14 +28,14 @@
.base {
@apply absolute inset-0 w-4/5;
@apply rounded bg-neutral-200 dark:bg-neutral-700;
@apply rounded bg-zinc-200 dark:bg-zinc-800;
}
}
.section {
@apply relative my-3 w-full px-4 py-2;
@apply cursor-move break-all rounded text-xs capitalize;
@apply bg-neutral-800/90 text-neutral-50 dark:bg-neutral-50/90 dark:text-neutral-800;
@apply bg-zinc-900/90 text-zinc-50 dark:bg-zinc-50/90 dark:text-zinc-900;
&.disabled {
@apply opacity-60;

View File

@ -60,9 +60,9 @@ const Layout = () => {
<>
<Heading
path="metadata.layout"
name={t<string>('builder.rightSidebar.sections.layout.heading')}
name={t('builder.rightSidebar.sections.layout.heading')}
action={
<Tooltip title={t<string>('builder.rightSidebar.sections.layout.tooltip.reset-layout')}>
<Tooltip title={t('builder.rightSidebar.sections.layout.tooltip.reset-layout')}>
<IconButton onClick={handleResetLayout}>
<Restore />
</IconButton>
@ -76,14 +76,14 @@ const Layout = () => {
<div key={pageIndex} className={styles.page}>
<div className="flex items-center justify-between pr-3">
<p className={styles.heading}>
{t<string>('builder.common.glossary.page')} {pageIndex + 1}
{t('builder.common.glossary.page')} {pageIndex + 1}
</p>
<div className={clsx(styles.delete, { hidden: pageIndex === 0 })}>
<Tooltip
title={
t<string>('builder.common.actions.delete', {
token: t<string>('builder.common.glossary.page'),
t('builder.common.actions.delete', {
token: t('builder.common.glossary.page'),
}) as string
}
>
@ -136,7 +136,7 @@ const Layout = () => {
<div className="flex items-center justify-end">
<Button variant="outlined" startIcon={<Add />} onClick={handleAddPage}>
{t<string>('builder.common.actions.add', { token: t<string>('builder.common.glossary.page') })}
{t('builder.common.actions.add', { token: t('builder.common.glossary.page') })}
</Button>
</div>
</DragDropContext>

View File

@ -3,7 +3,7 @@
.section {
@apply grid gap-2 rounded p-6;
@apply bg-neutral-100 dark:bg-neutral-800;
@apply bg-zinc-100 dark:bg-zinc-900;
h2 {
@apply inline-flex items-center gap-2 text-base font-medium;

View File

@ -12,53 +12,51 @@ const Links = () => {
return (
<>
<Heading path="metadata.links" name={t<string>('builder.rightSidebar.sections.links.heading')} />
<Heading path="metadata.links" name={t('builder.rightSidebar.sections.links.heading')} />
<div className={styles.container}>
<div className={styles.section}>
<h2>
<Savings fontSize="small" />
{t<string>('builder.rightSidebar.sections.links.donate.heading')}
{t('builder.rightSidebar.sections.links.donate.heading')}
</h2>
<p>{t<string>('builder.rightSidebar.sections.links.donate.body')}</p>
<p>{t('builder.rightSidebar.sections.links.donate.body')}</p>
<a href={DONATION_URL} target="_blank" rel="noreferrer">
<Button startIcon={<Coffee />}>{t<string>('builder.rightSidebar.sections.links.donate.button')}</Button>
<Button startIcon={<Coffee />}>{t('builder.rightSidebar.sections.links.donate.button')}</Button>
</a>
</div>
<div className={styles.section}>
<h2>
<BugReport fontSize="small" />
{t<string>('builder.rightSidebar.sections.links.bugs-features.heading')}
{t('builder.rightSidebar.sections.links.bugs-features.heading')}
</h2>
<p>{t<string>('builder.rightSidebar.sections.links.bugs-features.body')}</p>
<p>{t('builder.rightSidebar.sections.links.bugs-features.body')}</p>
<a href={GITHUB_ISSUES_URL} target="_blank" rel="noreferrer">
<Button startIcon={<GitHub />}>
{t<string>('builder.rightSidebar.sections.links.bugs-features.button')}
</Button>
<Button startIcon={<GitHub />}>{t('builder.rightSidebar.sections.links.bugs-features.button')}</Button>
</a>
</div>
<div>
<a href={GITHUB_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<Link />}>
{t<string>('builder.rightSidebar.sections.links.github')}
{t('builder.rightSidebar.sections.links.github')}
</Button>
</a>
<a href={REDDIT_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<Link />}>
{t<string>('builder.rightSidebar.sections.links.reddit')}
{t('builder.rightSidebar.sections.links.reddit')}
</Button>
</a>
<a href={DOCS_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<Link />}>
{t<string>('builder.rightSidebar.sections.links.docs')}
{t('builder.rightSidebar.sections.links.docs')}
</Button>
</a>
</div>

View File

@ -55,7 +55,7 @@ const Settings = () => {
const themeString = useMemo(() => (isDarkMode ? 'Matte Black Everything' : 'As bright as your future'), [isDarkMode]);
const { mutateAsync: loadSampleDataMutation } = useMutation<Resume, ServerError, LoadSampleDataParams>(
loadSampleData
loadSampleData,
);
const { mutateAsync: resetResumeMutation } = useMutation<Resume, ServerError, ResetResumeParams>(resetResume);
@ -96,13 +96,13 @@ const Settings = () => {
return (
<>
<Heading path="metadata.settings" name={t<string>('builder.rightSidebar.sections.settings.heading')} />
<Heading path="metadata.settings" name={t('builder.rightSidebar.sections.settings.heading')} />
<List disablePadding>
{/* Global Settings */}
<>
<ListSubheader disableSticky className="rounded">
{t<string>('builder.rightSidebar.sections.settings.global.heading')}
{t('builder.rightSidebar.sections.settings.global.heading')}
</ListSubheader>
<ListItem>
@ -110,7 +110,7 @@ const Settings = () => {
<Palette />
</ListItemIcon>
<ListItemText
primary={t<string>('builder.rightSidebar.sections.settings.global.theme.primary')}
primary={t('builder.rightSidebar.sections.settings.global.theme.primary')}
secondary={themeString}
/>
<ThemeSwitch checked={isDarkMode} onChange={(_, value: boolean) => handleSetTheme(value)} />
@ -119,8 +119,8 @@ const Settings = () => {
<ListItem className="flex-col">
<ListItemText
className="w-full"
primary={t<string>('builder.rightSidebar.sections.settings.global.date.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.global.date.secondary')}
primary={t('builder.rightSidebar.sections.settings.global.date.primary')}
secondary={t('builder.rightSidebar.sections.settings.global.date.secondary')}
/>
<Autocomplete<string, false, true, false>
disableClearable
@ -135,8 +135,8 @@ const Settings = () => {
<ListItem className="flex-col">
<ListItemText
className="w-full"
primary={t<string>('builder.rightSidebar.sections.settings.global.language.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.global.language.secondary')}
primary={t('builder.rightSidebar.sections.settings.global.language.primary')}
secondary={t('builder.rightSidebar.sections.settings.global.language.secondary')}
/>
<Autocomplete<Language, false, true, false>
disableClearable
@ -160,14 +160,14 @@ const Settings = () => {
{/* Page Settings */}
<>
<ListSubheader disableSticky className="rounded">
{t<string>('builder.rightSidebar.sections.settings.page.heading')}
{t('builder.rightSidebar.sections.settings.page.heading')}
</ListSubheader>
<ListItem className="flex-col">
<ListItemText
className="w-full"
primary={t<string>('builder.rightSidebar.sections.settings.page.format.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.page.format.secondary')}
primary={t('builder.rightSidebar.sections.settings.page.format.primary')}
secondary={t('builder.rightSidebar.sections.settings.page.format.secondary')}
/>
<Autocomplete<PageConfig['format'], false, true, false>
disableClearable
@ -182,11 +182,11 @@ const Settings = () => {
<ListItem>
<ListItemText
primary={t<string>('builder.rightSidebar.sections.settings.page.orientation.primary')}
primary={t('builder.rightSidebar.sections.settings.page.orientation.primary')}
secondary={
pages.length === 1
? t<string>('builder.rightSidebar.sections.settings.page.orientation.disabled')
: t<string>('builder.rightSidebar.sections.settings.page.orientation.secondary')
? t('builder.rightSidebar.sections.settings.page.orientation.disabled')
: t('builder.rightSidebar.sections.settings.page.orientation.secondary')
}
/>
<Switch
@ -199,8 +199,8 @@ const Settings = () => {
<ListItem>
<ListItemText
primary={t<string>('builder.rightSidebar.sections.settings.page.break-line.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.page.break-line.secondary')}
primary={t('builder.rightSidebar.sections.settings.page.break-line.primary')}
secondary={t('builder.rightSidebar.sections.settings.page.break-line.secondary')}
/>
<Switch color="secondary" checked={breakLine} onChange={() => dispatch(togglePageBreakLine())} />
</ListItem>
@ -209,7 +209,7 @@ const Settings = () => {
{/* Resume Settings */}
<>
<ListSubheader disableSticky className="rounded">
{t<string>('builder.rightSidebar.sections.settings.resume.heading')}
{t('builder.rightSidebar.sections.settings.resume.heading')}
</ListSubheader>
<ListItem disableGutters>
@ -218,8 +218,8 @@ const Settings = () => {
<Anchor />
</ListItemIcon>
<ListItemText
primary={t<string>('builder.rightSidebar.sections.settings.resume.sample.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.resume.sample.secondary')}
primary={t('builder.rightSidebar.sections.settings.resume.sample.primary')}
secondary={t('builder.rightSidebar.sections.settings.resume.sample.secondary')}
/>
</ListItemButton>
</ListItem>
@ -231,11 +231,9 @@ const Settings = () => {
</ListItemIcon>
<ListItemText
primary={
confirmReset
? 'Are you sure?'
: t<string>('builder.rightSidebar.sections.settings.resume.reset.primary')
confirmReset ? 'Are you sure?' : t('builder.rightSidebar.sections.settings.resume.reset.primary')
}
secondary={t<string>('builder.rightSidebar.sections.settings.resume.reset.secondary')}
secondary={t('builder.rightSidebar.sections.settings.resume.reset.secondary')}
/>
</ListItemButton>
</ListItem>

View File

@ -29,19 +29,19 @@ const Sharing = () => {
await navigator.clipboard.writeText(text);
toast.success(t<string>('common.toast.success.resume-link-copied'));
toast.success(t('common.toast.success.resume-link-copied'));
};
return (
<>
<Heading path="metadata.sharing" name={t<string>('builder.rightSidebar.sections.sharing.heading')} />
<Heading path="metadata.sharing" name={t('builder.rightSidebar.sections.sharing.heading')} />
<List sx={{ padding: 0 }}>
<ListItem className="flex flex-col" sx={{ padding: 0 }}>
<div className="flex w-full items-center justify-between">
<ListItemText
primary={t<string>('builder.rightSidebar.sections.sharing.visibility.title')}
secondary={t<string>('builder.rightSidebar.sections.sharing.visibility.subtitle')}
primary={t('builder.rightSidebar.sections.sharing.visibility.title')}
secondary={t('builder.rightSidebar.sections.sharing.visibility.subtitle')}
/>
<Switch color="secondary" checked={isPublic} onChange={(_, value) => handleSetVisibility(value)} />
</div>
@ -63,7 +63,7 @@ const Sharing = () => {
<div className="mt-1 flex w-full">
<FormControlLabel
label={t<string>('builder.rightSidebar.sections.sharing.short-url.label')}
label={t('builder.rightSidebar.sections.sharing.short-url.label')}
control={
<Checkbox className="mr-1" checked={showShortUrl} onChange={(_, value) => setShowShortUrl(value)} />
}

View File

@ -24,7 +24,7 @@ const Templates = () => {
return (
<>
<Heading path="metadata.templates" name={t<string>('builder.rightSidebar.sections.templates.heading')} />
<Heading path="metadata.templates" name={t('builder.rightSidebar.sections.templates.heading')} />
<div className={styles.container}>
{Object.values(templateMap).map((template) => (

View File

@ -17,7 +17,7 @@ const Theme = () => {
const dispatch = useAppDispatch();
const { background, text, primary } = useAppSelector<ThemeConfig>((state) =>
get(state.resume.present, 'metadata.theme')
get(state.resume.present, 'metadata.theme'),
);
const handleChange = (property: string, color: string) => {
@ -26,7 +26,7 @@ const Theme = () => {
return (
<>
<Heading path="metadata.theme" name={t<string>('builder.rightSidebar.sections.theme.heading')} />
<Heading path="metadata.theme" name={t('builder.rightSidebar.sections.theme.heading')} />
<div className={styles.container}>
<div className={styles.colorOptions}>
@ -36,18 +36,18 @@ const Theme = () => {
</div>
<ColorPicker
label={t<string>('builder.rightSidebar.sections.theme.form.primary.label')}
label={t('builder.rightSidebar.sections.theme.form.primary.label')}
color={primary}
className="col-span-2"
onChange={(color) => handleChange('primary', color)}
/>
<ColorPicker
label={t<string>('builder.rightSidebar.sections.theme.form.background.label')}
label={t('builder.rightSidebar.sections.theme.form.background.label')}
color={background}
onChange={(color) => handleChange('background', color)}
/>
<ColorPicker
label={t<string>('builder.rightSidebar.sections.theme.form.text.label')}
label={t('builder.rightSidebar.sections.theme.form.text.label')}
color={text}
onChange={(color) => handleChange('text', color)}
/>

View File

@ -46,7 +46,7 @@ const Widgets: React.FC<WidgetProps> = ({ label, category }) => {
setResumeState({
path: `metadata.typography.${property}.${category}`,
value: property === 'family' ? (value as Font).family : value,
})
}),
);
};
@ -64,7 +64,7 @@ const Widgets: React.FC<WidgetProps> = ({ label, category }) => {
step={1}
marks={[
{ value: 12, label: '12px' },
{ value: 24, label: t<string>('builder.rightSidebar.sections.typography.form.font-size.label') },
{ value: 24, label: t('builder.rightSidebar.sections.typography.form.font-size.label') },
{ value: 36, label: '36px' },
]}
valueLabelDisplay="auto"
@ -82,10 +82,7 @@ const Widgets: React.FC<WidgetProps> = ({ label, category }) => {
value={fonts.find((font) => font.family === family[category])}
onChange={(_, font: Font | null) => handleChange('family', font)}
renderInput={(params) => (
<TextField
{...params}
label={t<string>('builder.rightSidebar.sections.typography.form.font-family.label')}
/>
<TextField {...params} label={t('builder.rightSidebar.sections.typography.form.font-family.label')} />
)}
/>
</div>
@ -98,13 +95,10 @@ const Typography = () => {
return (
<>
<Heading path="metadata.typography" name={t<string>('builder.rightSidebar.sections.typography.heading')} />
<Heading path="metadata.typography" name={t('builder.rightSidebar.sections.typography.heading')} />
<Widgets
label={t<string>('builder.rightSidebar.sections.typography.widgets.headings.label')}
category="heading"
/>
<Widgets label={t<string>('builder.rightSidebar.sections.typography.widgets.body.label')} category="body" />
<Widgets label={t('builder.rightSidebar.sections.typography.widgets.headings.label')} category="heading" />
<Widgets label={t('builder.rightSidebar.sections.typography.widgets.body.label')} category="body" />
</>
);
};