mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-11 04:52:36 +10:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d67272cf9e | |||
| f937a88b9d | |||
| 7465a7ec78 | |||
| b53d8854df | |||
| bffa0be909 | |||
| 06fee1696e | |||
| f9a11092a6 | |||
| 9c76999945 | |||
| d23d1a615e | |||
| a5701a37a6 | |||
| a739b25f42 | |||
| 145aa14ba0 | |||
| 94358bf61c | |||
| ce50df61a5 | |||
| f18da54dfa | |||
| 14c5e36fae | |||
| 1483f9b4f2 | |||
| f7d8e4ebb4 | |||
| 7c42d6e607 | |||
| 08dea8ad8b | |||
| 950d7ea4e7 | |||
| ebc12042a9 | |||
| d8168d2a9d | |||
| 7cfda3c83d | |||
| 8fcfbdd69d | |||
| 1eb52261f2 | |||
| 88400b769d | |||
| b6831fc532 | |||
| b231b60b5a | |||
| 2679c9ebc2 | |||
| 278253b809 | |||
| 8a933de0bd | |||
| 704cba06f4 | |||
| b946098bd0 | |||
| f7b95f7679 | |||
| e38967874e | |||
| 8368c4e183 | |||
| 951f14ef69 | |||
| 4a75be95ef | |||
| 1125557fbc | |||
| db63138307 | |||
| a52feac93b | |||
| ad7b6ad2c6 | |||
| 33e3850bb7 | |||
| c29605dbd0 | |||
| 14b2ba4f73 | |||
| 8868684125 | |||
| c1ceb0cd50 | |||
| 1105f672a5 | |||
| 67cc49c068 | |||
| 505406508b | |||
| bfd37951df | |||
| 339cae05f1 | |||
| 48069c10a4 | |||
| 51317b2901 | |||
| e5ce53b2aa | |||
| 2bc7c93174 | |||
| 1d97f01942 | |||
| 5ad517f1d3 | |||
| 8088c70038 | |||
| e36df82ba9 | |||
| de513a12da | |||
| 06f1a813ce | |||
| 1de9195f20 | |||
| eaa21ead3e | |||
| 3ea4a9b000 | |||
| f0484c1c28 | |||
| 7ebda09a5f | |||
| 161ca0ee28 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -1,2 +1,2 @@
|
||||
github: AmruthPillai
|
||||
custom: https://paypal.me/RajaRajanA
|
||||
custom: https://paypal.me/amruthde
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
4
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
@ -1,9 +1,9 @@
|
||||
name: 🐞 Bug Report
|
||||
description: Create a bug report to help improve Reactive Resume.
|
||||
|
||||
title: "[Bug] <title>"
|
||||
title: '[Bug] <title>'
|
||||
labels: [Bug, Needs Triage]
|
||||
assignees: "AmruthPillai"
|
||||
assignees: 'AmruthPillai'
|
||||
|
||||
body:
|
||||
- type: checkboxes
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
4
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
@ -1,9 +1,9 @@
|
||||
name: ✨ Feature Request
|
||||
description: Suggest an feature or idea that you would like to see in Reactive Resume.
|
||||
|
||||
title: "[Feature] <title>"
|
||||
title: '[Feature] <title>'
|
||||
labels: [Feature, Needs Triage]
|
||||
assignees: "AmruthPillai"
|
||||
assignees: 'AmruthPillai'
|
||||
|
||||
body:
|
||||
- type: checkboxes
|
||||
|
||||
8
.github/workflows/build-deploy.yml
vendored
8
.github/workflows/build-deploy.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v2.2.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2.9.0
|
||||
uses: docker/setup-buildx-action@v2.9.1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
@ -91,14 +91,14 @@ jobs:
|
||||
with:
|
||||
images: amruthpillai/reactive-resume
|
||||
tags: |
|
||||
type=raw,value=${{ matrix.image }}-latest
|
||||
type=raw,value=${{ matrix.image }}-${{ steps.version.outputs.current-version }}
|
||||
type=raw,value=${{ matrix.image }}-arm64-latest
|
||||
type=raw,value=${{ matrix.image }}-arm64-${{ steps.version.outputs.current-version }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.2.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2.9.0
|
||||
uses: docker/setup-buildx-action@v2.9.1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
|
||||
@ -30,7 +30,7 @@ ports:
|
||||
onOpen: ignore
|
||||
visibility: private
|
||||
|
||||
# Client
|
||||
# Server
|
||||
- port: 3100
|
||||
onOpen: ignore
|
||||
visibility: public
|
||||
|
||||
@ -30,7 +30,7 @@ const Export = () => {
|
||||
};
|
||||
|
||||
const handleExportJSON = async () => {
|
||||
const { nanoid } = await import('nanoid');
|
||||
const nanoid = (await import('nanoid')).nanoid;
|
||||
const download = (await import('downloadjs')).default;
|
||||
|
||||
const redactedResume = pick(resume, ['basics', 'sections', 'metadata', 'public']);
|
||||
|
||||
50
client/components/home/sections/Hero.tsx
Normal file
50
client/components/home/sections/Hero.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import Tilt from 'react-parallax-tilt';
|
||||
|
||||
import { defaultTiltProps } from '@/constants/tilt';
|
||||
|
||||
import HomeActions from '../Actions';
|
||||
import HeroBackground from '../Background';
|
||||
import HeroPattern from '../Pattern';
|
||||
|
||||
const HeroSection = () => (
|
||||
<section className="relative">
|
||||
<HeroPattern />
|
||||
<HeroBackground />
|
||||
|
||||
<div className="mx-auto max-w-7xl px-6 pb-24 pt-10 sm:pb-32 lg:flex lg:px-8 lg:py-52">
|
||||
<div className="mx-auto max-w-2xl shrink-0 lg:mx-0 lg:max-w-xl lg:pt-12">
|
||||
<div className="mt-10 space-y-2">
|
||||
<h6 className="text-base font-bold tracking-wide">Finally,</h6>
|
||||
<h1 className="text-4xl font-bold !leading-[1.15] tracking-tight sm:text-6xl">
|
||||
A free and open-source resume builder
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p className="prose prose-base prose-zinc mt-6 text-lg leading-8 dark:prose-invert">
|
||||
Reactive Resume is a free and open-source resume builder that simplifies the tasks of creating, updating, and
|
||||
sharing your resume.
|
||||
</p>
|
||||
|
||||
<div className="mt-12 space-x-4">
|
||||
<HomeActions />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto mt-16 flex max-w-2xl sm:mt-24 lg:ml-10 lg:mr-0 lg:mt-0 lg:max-w-none lg:flex-none xl:ml-32">
|
||||
<div className="max-w-3xl flex-none sm:max-w-5xl lg:max-w-none">
|
||||
<Tilt {...defaultTiltProps}>
|
||||
<img
|
||||
width={2432}
|
||||
height={1442}
|
||||
src="/images/screenshots/builder.png"
|
||||
alt="Reactive Resume Screenshot - Builder Screen"
|
||||
className="w-[76rem] rounded-lg bg-zinc-50/5 shadow-2xl ring-1 ring-zinc-950/10 dark:bg-zinc-950/5 dark:ring-zinc-50/10"
|
||||
/>
|
||||
</Tilt>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
export default HeroSection;
|
||||
@ -1,5 +1,5 @@
|
||||
.content {
|
||||
@apply rounded px-6 text-sm shadow lg:w-1/2 xl:w-2/5;
|
||||
@apply rounded px-6 text-sm shadow lg:w-2/3 xl:w-1/2;
|
||||
@apply absolute inset-4 sm:inset-x-4 sm:inset-y-auto lg:inset-auto;
|
||||
@apply overflow-scroll bg-zinc-100 dark:bg-zinc-900 lg:overflow-auto;
|
||||
@apply max-h-[90vh] min-h-fit;
|
||||
|
||||
@ -8,7 +8,7 @@ export const Copyright = ({ className }: Props) => (
|
||||
<div
|
||||
className={clsx('prose prose-sm prose-zinc flex flex-col gap-y-1 text-xs opacity-40 dark:prose-invert', className)}
|
||||
>
|
||||
<span className="font-medium">v4.0.0</span>
|
||||
<span className="font-medium">v{process.env.appVersion}</span>
|
||||
<span>
|
||||
Licensed under <a href="https://github.com/AmruthPillai/Reactive-Resume/blob/main/LICENSE">MIT</a>
|
||||
</span>
|
||||
|
||||
@ -15,9 +15,9 @@ const Markdown: React.FC<Props> = ({ className, children }) => {
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
className={clsx('markdown', className)}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
>
|
||||
{children}
|
||||
</ReactMarkdown>
|
||||
|
||||
@ -2,6 +2,7 @@ import { TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||||
import dayjs from 'dayjs';
|
||||
import get from 'lodash/get';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
@ -57,8 +58,9 @@ const ResumeInput: React.FC<Props> = ({ type = 'text', label, path, className, m
|
||||
<DatePicker
|
||||
openTo="year"
|
||||
label={label}
|
||||
value={dayjs(value)}
|
||||
slots={{ textField: (params) => <TextField {...params} error={false} className={className} /> }}
|
||||
className={className}
|
||||
views={['year', 'month', 'day']}
|
||||
value={isEmpty(value) ? null : dayjs(value)}
|
||||
onChange={(date: dayjs.Dayjs | null) => {
|
||||
if (!date) return onChangeValue('');
|
||||
if (dayjs(date).isValid()) return onChangeValue(dayjs(date).format('YYYY-MM-DD'));
|
||||
|
||||
@ -13,7 +13,7 @@ const ThemeSwitch = styled(Switch)(({ theme }) => ({
|
||||
transform: 'translateX(22px)',
|
||||
'& .MuiSwitch-thumb:before': {
|
||||
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
|
||||
'#fff'
|
||||
'#fff',
|
||||
)}" d="M4.2 2.5l-.7 1.8-1.8.7 1.8.7.7 1.8.6-1.8L6.7 5l-1.9-.7-.6-1.8zm15 8.3a6.7 6.7 0 11-6.6-6.6 5.8 5.8 0 006.6 6.6z"/></svg>')`,
|
||||
},
|
||||
'& + .MuiSwitch-track': {
|
||||
@ -36,7 +36,7 @@ const ThemeSwitch = styled(Switch)(({ theme }) => ({
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
|
||||
'#fff'
|
||||
'#fff',
|
||||
)}" d="M9.305 1.667V3.75h1.389V1.667h-1.39zm-4.707 1.95l-.982.982L5.09 6.072l.982-.982-1.473-1.473zm10.802 0L13.927 5.09l.982.982 1.473-1.473-.982-.982zM10 5.139a4.872 4.872 0 00-4.862 4.86A4.872 4.872 0 0010 14.862 4.872 4.872 0 0014.86 10 4.872 4.872 0 0010 5.139zm0 1.389A3.462 3.462 0 0113.471 10a3.462 3.462 0 01-3.473 3.472A3.462 3.462 0 016.527 10 3.462 3.462 0 0110 6.528zM1.665 9.305v1.39h2.083v-1.39H1.666zm14.583 0v1.39h2.084v-1.39h-2.084zM5.09 13.928L3.616 15.4l.982.982 1.473-1.473-.982-.982zm9.82 0l-.982.982 1.473 1.473.982-.982-1.473-1.473zM9.305 16.25v2.083h1.389V16.25h-1.39z"/></svg>')`,
|
||||
},
|
||||
},
|
||||
|
||||
@ -54,5 +54,5 @@ export const languageMap: Record<string, Language> = languages.reduce(
|
||||
...acc,
|
||||
[lang.code]: lang,
|
||||
}),
|
||||
{}
|
||||
{},
|
||||
);
|
||||
|
||||
@ -24,6 +24,13 @@ const theme: ThemeOptions = {
|
||||
variant: 'outlined',
|
||||
},
|
||||
},
|
||||
MuiInputBase: {
|
||||
styleOverrides: {
|
||||
input: {
|
||||
boxShadow: 'none !important',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAppBar: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
|
||||
@ -10,7 +10,7 @@ export const FILENAME_TIMESTAMP = 'DDMMYYYYHHmmss';
|
||||
|
||||
// Links
|
||||
export const DOCS_URL = 'https://docs.rxresu.me';
|
||||
export const DONATION_URL = 'https://paypal.me/RajaRajanA';
|
||||
export const DONATION_URL = 'https://paypal.me/amruthde';
|
||||
export const TRANSLATE_URL = 'https://translate.rxresu.me/';
|
||||
export const DIGITALOCEAN_URL = 'https://pillai.xyz/digitalocean';
|
||||
export const REDDIT_URL = 'https://www.reddit.com/r/reactiveresume/';
|
||||
|
||||
@ -121,7 +121,7 @@ const LoginModal: React.FC = () => {
|
||||
>
|
||||
<p>{t('modals.auth.login.body')}</p>
|
||||
|
||||
<form className="grid gap-4 xl:w-2/3">
|
||||
<form className="grid gap-4">
|
||||
<Controller
|
||||
name="identifier"
|
||||
control={control}
|
||||
|
||||
@ -172,14 +172,11 @@ const EducationModal: React.FC = () => {
|
||||
label={t('builder.common.form.start-date.label')}
|
||||
value={dayjs(field.value)}
|
||||
views={['year', 'month', 'day']}
|
||||
slots={{
|
||||
textField: (params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || params.inputProps?.placeholder}
|
||||
/>
|
||||
),
|
||||
slotProps={{
|
||||
textField: {
|
||||
error: !!fieldState.error,
|
||||
helperText: fieldState.error?.message || t('builder.common.form.start-date.help-text')
|
||||
},
|
||||
}}
|
||||
onChange={(date: dayjs.Dayjs | null) => {
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
@ -198,14 +195,11 @@ const EducationModal: React.FC = () => {
|
||||
label={t('builder.common.form.end-date.label')}
|
||||
value={dayjs(field.value)}
|
||||
views={['year', 'month', 'day']}
|
||||
slots={{
|
||||
textField: (params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || t('builder.common.form.end-date.help-text')}
|
||||
/>
|
||||
),
|
||||
slotProps={{
|
||||
textField: {
|
||||
error: !!fieldState.error,
|
||||
helperText: fieldState.error?.message || t('builder.common.form.end-date.help-text')
|
||||
},
|
||||
}}
|
||||
onChange={(date: dayjs.Dayjs | null) => {
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
|
||||
@ -16,16 +16,16 @@
|
||||
"@hello-pangea/dnd": "^16.3.0",
|
||||
"@hookform/resolvers": "3.1.1",
|
||||
"@monaco-editor/react": "^4.5.1",
|
||||
"@mui/icons-material": "^5.14.0",
|
||||
"@mui/lab": "^5.0.0-alpha.136",
|
||||
"@mui/material": "^5.14.0",
|
||||
"@mui/system": "^5.14.0",
|
||||
"@mui/x-date-pickers": "6.9.2",
|
||||
"@mui/icons-material": "^5.14.1",
|
||||
"@mui/lab": "^5.0.0-alpha.137",
|
||||
"@mui/material": "^5.14.2",
|
||||
"@mui/system": "^5.14.1",
|
||||
"@mui/x-date-pickers": "6.10.2",
|
||||
"@radix-ui/react-separator": "^1.0.3",
|
||||
"@react-oauth/google": "^0.11.0",
|
||||
"@react-oauth/google": "^0.11.1",
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"axios": "^1.4.0",
|
||||
"clsx": "^1.2.1",
|
||||
"clsx": "^2.0.0",
|
||||
"dayjs": "^1.11.9",
|
||||
"downloadjs": "^1.4.7",
|
||||
"joi": "^17.9.2",
|
||||
@ -33,7 +33,7 @@
|
||||
"md5-hex": "^4.0.0",
|
||||
"monaco-editor": "^0.40.0",
|
||||
"nanoid": "^3.3.6",
|
||||
"next": "13.4.9",
|
||||
"next": "13.4.12",
|
||||
"next-i18next": "^14.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
@ -41,49 +41,49 @@
|
||||
"react-dnd-html5-backend": "16.0.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-github-btn": "^1.4.0",
|
||||
"react-hook-form": "^7.45.1",
|
||||
"react-hook-form": "^7.45.2",
|
||||
"react-hot-toast": "2.4.1",
|
||||
"react-icons": "^4.10.1",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-parallax-tilt": "^1.7.152",
|
||||
"react-parallax-tilt": "^1.7.154",
|
||||
"react-query": "^3.39.3",
|
||||
"react-redux": "^8.1.1",
|
||||
"react-zoom-pan-pinch": "^3.1.0",
|
||||
"redux": "^4.2.1",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-saga": "^1.2.3",
|
||||
"redux-undo": "^1.0.1",
|
||||
"redux-undo": "^1.1.0",
|
||||
"rehype-katex": "^6.0.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"sharp": "^0.32.2",
|
||||
"tailwind-merge": "^1.13.2",
|
||||
"sharp": "^0.32.4",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"uuid": "^9.0.0",
|
||||
"webfontloader": "^1.6.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.8",
|
||||
"@babel/core": "^7.22.9",
|
||||
"@fontsource/ibm-plex-sans": "^5.0.5",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/forms": "^0.5.4",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/downloadjs": "^1.4.3",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/node": "^20.4.1",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@types/lodash": "^4.14.196",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.17",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/react-redux": "^7.1.25",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"@types/webfontloader": "^1.6.35",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"csstype": "^3.1.2",
|
||||
"eslint-config-next": "^13.4.9",
|
||||
"eslint-config-next": "^13.4.12",
|
||||
"eslint-plugin-tailwindcss": "^3.13.0",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"next-sitemap": "^4.1.8",
|
||||
"postcss": "^8.4.25",
|
||||
"sass": "^1.63.6",
|
||||
"postcss": "^8.4.27",
|
||||
"sass": "^1.64.1",
|
||||
"schema": "workspace:*",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,11 @@
|
||||
import type { GetStaticProps, NextPage } from 'next';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import Tilt from 'react-parallax-tilt';
|
||||
|
||||
import HeroBackground from '@/components/home/Background';
|
||||
import Footer from '@/components/home/Footer';
|
||||
import Header from '@/components/home/Header';
|
||||
import HeroPattern from '@/components/home/Pattern';
|
||||
import HeroSection from '@/components/home/sections/Hero';
|
||||
import LogoSection from '@/components/home/sections/Logo';
|
||||
import StatsSection from '@/components/home/sections/Stats';
|
||||
import { defaultTiltProps } from '@/constants/tilt';
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => ({
|
||||
props: {
|
||||
@ -21,43 +18,13 @@ const Home: NextPage = () => (
|
||||
<Header />
|
||||
|
||||
<main className="relative isolate mb-[450px] overflow-hidden bg-zinc-50 dark:bg-zinc-950">
|
||||
<section className="relative">
|
||||
<HeroPattern />
|
||||
<HeroBackground />
|
||||
|
||||
<div className="mx-auto max-w-7xl px-6 pb-24 pt-10 sm:pb-32 lg:flex lg:px-8 lg:py-52">
|
||||
<div className="mx-auto max-w-2xl shrink-0 lg:mx-0 lg:max-w-xl lg:pt-12">
|
||||
<div className="mt-10 space-y-2">
|
||||
<h6 className="text-base font-bold tracking-wide">Finally,</h6>
|
||||
<h1 className="text-4xl font-bold !leading-[1.15] tracking-tight sm:text-6xl">
|
||||
A free and open-source resume builder
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p className="prose prose-base prose-zinc mt-6 text-lg leading-8 dark:prose-invert">
|
||||
Reactive Resume is a free and open-source resume builder that simplifies the tasks of creating, updating,
|
||||
and sharing your resume.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto mt-16 flex max-w-2xl sm:mt-24 lg:ml-10 lg:mr-0 lg:mt-0 lg:max-w-none lg:flex-none xl:ml-32">
|
||||
<div className="max-w-3xl flex-none sm:max-w-5xl lg:max-w-none">
|
||||
<Tilt {...defaultTiltProps}>
|
||||
<img
|
||||
width={2432}
|
||||
height={1442}
|
||||
src="/images/screenshots/builder.png"
|
||||
alt="Reactive Resume Screenshot - Builder Screen"
|
||||
className="w-[76rem] rounded-lg bg-zinc-50/5 shadow-2xl ring-1 ring-zinc-950/10 dark:bg-zinc-950/5 dark:ring-zinc-50/10"
|
||||
/>
|
||||
</Tilt>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* Hero */}
|
||||
<HeroSection />
|
||||
|
||||
{/* Logo Cloud */}
|
||||
<LogoSection />
|
||||
|
||||
{/* Statistics */}
|
||||
<StatsSection />
|
||||
</main>
|
||||
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "የድርጅት ስም"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "اسم الشركة"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Име на фирмата"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Nom de la companyia"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Jméno společnosti"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "firmanavn"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Firmenname"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Όνομα εταιρείας"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "nombre de empresa"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "نام شرکت"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Yrityksen nimi"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Nom de l'entreprise"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "שם החברה"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "कंपनी का नाम"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Cégnév"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Nama perusahaan"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Nome della ditta"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "会社名"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "ឈ្មោះក្រុមហ៊ុន"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "ಸಂಸ್ಥೆಯ ಹೆಸರು"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "회사 이름"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "കമ്പനി പേര്"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "कंपनीचे नाव"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "कम्पनीको नाम"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "କମ୍ପାନି ନାଁ"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Nazwa firmy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
"actions": {
|
||||
"add": "Adicionar {{token}}",
|
||||
"delete": "Remover {{token}}",
|
||||
"edit": "Editar {{token}}",
|
||||
"duplicate": "Duplicar sessão"
|
||||
"duplicate": "Duplicar sessão",
|
||||
"edit": "Editar {{token}}"
|
||||
},
|
||||
"columns": {
|
||||
"heading": "Colunas",
|
||||
"tooltip": "Alterar o número de colunas"
|
||||
"tooltip": "Alterar número de colunas"
|
||||
},
|
||||
"form": {
|
||||
"date": {
|
||||
@ -21,7 +21,7 @@
|
||||
"label": "Endereço de e-mail"
|
||||
},
|
||||
"end-date": {
|
||||
"help-text": "Deixe este campo em branco se for até o presente",
|
||||
"help-text": "Deixe este campo em branco se for emprego atual",
|
||||
"label": "Data de Término"
|
||||
},
|
||||
"keywords": {
|
||||
@ -49,7 +49,7 @@
|
||||
"label": "Subtítulo"
|
||||
},
|
||||
"summary": {
|
||||
"label": "Resumo"
|
||||
"label": "Descritivo"
|
||||
},
|
||||
"title": {
|
||||
"label": "Título"
|
||||
@ -67,7 +67,7 @@
|
||||
"duplicate": "Duplicar",
|
||||
"edit": "Editar"
|
||||
},
|
||||
"empty-text": "Essa lista está vazia."
|
||||
"empty-text": "Lista está vazia."
|
||||
},
|
||||
"tooltip": {
|
||||
"delete-item": "Tem certeza de que deseja excluir este item? Esta ação é irreversível.",
|
||||
@ -81,13 +81,13 @@
|
||||
"center-artboard": "Prancheta central",
|
||||
"copy-link": "Copiar link do currículo",
|
||||
"export-pdf": "Exportar PDF",
|
||||
"redo": "Refazer",
|
||||
"toggle-orientation": "Alternar orientação da página",
|
||||
"toggle-page-break-line": "Alternar linha de quebra de página",
|
||||
"toggle-sidebars": "Alternar barra lateral",
|
||||
"zoom-in": "Mais Zoom",
|
||||
"zoom-out": "Menos Zoom",
|
||||
"undo": "Desfazer",
|
||||
"redo": "Redo"
|
||||
"zoom-in": "Mais Zoom",
|
||||
"zoom-out": "Menos Zoom"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
@ -109,18 +109,15 @@
|
||||
"awarder": {
|
||||
"label": "Concedente"
|
||||
}
|
||||
},
|
||||
"heading": "Títulos",
|
||||
"heading_one": "Título"
|
||||
},
|
||||
"work": {
|
||||
"heading": "Experiências de Trabalho",
|
||||
"heading_one": "Experiência de Trabalho"
|
||||
}
|
||||
},
|
||||
"basics": {
|
||||
"actions": {
|
||||
"photo-filters": "Filtros da foto"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Data de nascimento"
|
||||
},
|
||||
"heading": "Informações básicas",
|
||||
"headline": {
|
||||
"label": "Título"
|
||||
@ -128,9 +125,6 @@
|
||||
"name": {
|
||||
"label": "Nome Completo"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Data de nascimento"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
@ -160,9 +154,7 @@
|
||||
"issuer": {
|
||||
"label": "Emissor"
|
||||
}
|
||||
},
|
||||
"heading": "Certificações",
|
||||
"heading_one": "Certificação"
|
||||
}
|
||||
},
|
||||
"education": {
|
||||
"form": {
|
||||
@ -182,13 +174,13 @@
|
||||
"label": "Instituição"
|
||||
}
|
||||
},
|
||||
"heading": "Formação Acadêmica",
|
||||
"heading_one": "Formação Acadêmica"
|
||||
"heading": "Escolaridade",
|
||||
"heading_one": "Escolaridade"
|
||||
},
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Nome da Empresa"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -204,10 +196,10 @@
|
||||
},
|
||||
"heading": "Localização",
|
||||
"postal-code": {
|
||||
"label": "Código Postal"
|
||||
"label": "CEP"
|
||||
},
|
||||
"region": {
|
||||
"label": "Região"
|
||||
"label": "Estado"
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
@ -227,34 +219,14 @@
|
||||
"publisher": {
|
||||
"label": "Editor"
|
||||
}
|
||||
},
|
||||
"heading": "Publicações",
|
||||
"heading_one": "Publicação"
|
||||
}
|
||||
},
|
||||
"references": {
|
||||
"form": {
|
||||
"relationship": {
|
||||
"label": "Relação"
|
||||
}
|
||||
},
|
||||
"heading": "Referências",
|
||||
"heading_one": "Referência"
|
||||
},
|
||||
"skills": {
|
||||
"heading": "Habilidades",
|
||||
"heading_one": "Habilidade"
|
||||
},
|
||||
"languages": {
|
||||
"heading": "Idiomas",
|
||||
"heading_one": "Idioma"
|
||||
},
|
||||
"interests": {
|
||||
"heading": "Interesses",
|
||||
"heading_one": "Interesse"
|
||||
},
|
||||
"projects": {
|
||||
"heading": "Projetos",
|
||||
"heading_one": "Projeto"
|
||||
}
|
||||
},
|
||||
"section": {
|
||||
"heading": "Seção"
|
||||
@ -264,9 +236,7 @@
|
||||
"organization": {
|
||||
"label": "Organização"
|
||||
}
|
||||
},
|
||||
"heading": "Trabalhos Voluntários",
|
||||
"heading_one": "Trabalho Voluntário"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -293,7 +263,7 @@
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"heading": "Layout",
|
||||
"heading": "Disposição",
|
||||
"tooltip": {
|
||||
"reset-layout": "Redefinir Layout"
|
||||
}
|
||||
@ -301,17 +271,18 @@
|
||||
"links": {
|
||||
"bugs-features": {
|
||||
"body": "Alguma coisa te impede de fazer um currículo? Ou você tem uma ideia incrível para adicionar? Crie uma issue no GitHub para começar.",
|
||||
"button": "GitHub Issues",
|
||||
"button": "Problemas do GitHub",
|
||||
"heading": "Bugs? Sugestões de recursos?"
|
||||
},
|
||||
"docs": "Documentação",
|
||||
"donate": {
|
||||
"body": "Se você gostou de usar o Reactive Resume, considere doar o máximo possível para manter o aplicativo em funcionamento, sem anúncios e gratuito para sempre.",
|
||||
"button": "Pague-me um café",
|
||||
"heading": "Faça uma doação ao Reactive Resume"
|
||||
},
|
||||
"github": "Código Fonte",
|
||||
"docs": "Documentação",
|
||||
"heading": "Links"
|
||||
"heading": "links",
|
||||
"reddit": "Reddit"
|
||||
},
|
||||
"settings": {
|
||||
"global": {
|
||||
@ -330,14 +301,14 @@
|
||||
},
|
||||
"heading": "Configurações",
|
||||
"page": {
|
||||
"break-line": {
|
||||
"primary": "Quebra de Linha",
|
||||
"secondary": "Mostrar uma linha em todas as páginas para marcar a altura de uma página A4"
|
||||
},
|
||||
"format": {
|
||||
"primary": "Tamanho do papel",
|
||||
"secondary": "Determina as dimensões das suas páginas de currículo"
|
||||
},
|
||||
"break-line": {
|
||||
"primary": "Linha de quebra",
|
||||
"secondary": "Mostrar uma linha em todas as páginas para marcar a altura de uma página A4"
|
||||
},
|
||||
"heading": "Página",
|
||||
"orientation": {
|
||||
"disabled": "Não tem efeito quando há somente uma página",
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
"present": "Presente"
|
||||
},
|
||||
"subtitle": "Gerador de currículos gratuito e de código aberto.",
|
||||
"title": "Reactive Resume",
|
||||
"title": "Currículo reativo",
|
||||
"toast": {
|
||||
"error": {
|
||||
"upload-file-size": "Carregue apenas arquivos com menos de 2 megabytes.",
|
||||
|
||||
@ -18,25 +18,26 @@
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"heading": "Links",
|
||||
"heading": "links",
|
||||
"links": {
|
||||
"docs": "Documentação",
|
||||
"donate": "Fazer doação",
|
||||
"github": "Código Fonte",
|
||||
"docs": "Documentação",
|
||||
"privacy": "Política de privacidade",
|
||||
"reddit": "Reddit",
|
||||
"service": "Termos de serviço"
|
||||
}
|
||||
},
|
||||
"screenshots": {
|
||||
"heading": "Imagens"
|
||||
},
|
||||
"testimonials": {
|
||||
"heading": "Depoimentos",
|
||||
"body": "Considero importante a sua opinião, positiva ou negativa, a respeito do Reactive Resume, bem como a sua experiência ao usá-lo.<br/>Confira algumas das mensagens enviadas por pessoas de todo o mundo.",
|
||||
"contact": "Envie a sua mensagem por <1>e-mail</1> ou pelo formulário disponível <3>aqui</3>."
|
||||
},
|
||||
"summary": {
|
||||
"body": "O Reactive Resume é um gerador de currículos, gratuito e de código aberto, desenvolvido para facilitar as tarefas tediosas de criação, atualização e divulgação de seu currículo. Este aplicativo possibilita a criação de múltiplos currículos, que podem ser compartilhados com recrutadores ou amigos com um link exclusivo ou impressos como PDF. Tudo isso de graça, sem anúncios, sem rastreamento, mantendo a integridade e privacidade dos seus dados.",
|
||||
"heading": "Resumo"
|
||||
},
|
||||
"testimonials": {
|
||||
"body": "Considero importante a sua opinião, positiva ou negativa, a respeito do Reactive Resume, bem como a sua experiência ao usá-lo.<br/>Confira algumas das mensagens enviadas por pessoas de todo o mundo.",
|
||||
"contact": "Envie a sua mensagem por <1>e-mail</1> ou pelo formulário disponível <3>aqui</3>.",
|
||||
"heading": "Depoimentos"
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +71,31 @@
|
||||
}
|
||||
},
|
||||
"heading": "Redefinir sua senha"
|
||||
},
|
||||
"profile": {
|
||||
"heading": "Sua conta",
|
||||
"form": {
|
||||
"avatar": {
|
||||
"help-text": "Você pode atualizar sua foto de perfil no <1>Gravatar</1>"
|
||||
},
|
||||
"name": {
|
||||
"label": "Nome completo"
|
||||
},
|
||||
"email": {
|
||||
"label": "Endereço de email",
|
||||
"help-text": "Não é possível atualizar seu endereço de e-mail no momento, crie uma nova conta."
|
||||
}
|
||||
},
|
||||
"delete-account": {
|
||||
"heading": "Excluir conta e dados",
|
||||
"body": "Para excluir sua conta, seus dados e todos os seus currículos, digite \"{{keyword}}\" na caixa de texto e clique no botão. Observe que esta é uma ação irreversível e seus dados não podem ser recuperados novamente.",
|
||||
"actions": {
|
||||
"delete": "Deletar conta"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"save": "Salvar alterações"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "nome da empresa"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Numele companiei"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Название компании"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Име компаније"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "நிறுவனத்தின் பெயர்"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Firma Adı"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Назва компанії"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "Tên công ty"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
"experience": {
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Company Name"
|
||||
"label": "公司名称"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -83,7 +83,7 @@ export const resetPassword = async (resetPasswordParams: ResetPasswordParams) =>
|
||||
export const updateProfile = async (updateProfileParams: UpdateProfileParams) => {
|
||||
const { data: user } = await axios.patch<User, AxiosResponse<User>, UpdateProfileParams>(
|
||||
'/auth/update-profile',
|
||||
updateProfileParams
|
||||
updateProfileParams,
|
||||
);
|
||||
|
||||
store.dispatch(setUser(user));
|
||||
|
||||
@ -46,7 +46,7 @@ axios.interceptors.response.use(
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export default axios;
|
||||
|
||||
@ -9,6 +9,6 @@ export type PrintResumeAsPdfParams = {
|
||||
export const printResumeAsPdf = (printResumeAsPdfParams: PrintResumeAsPdfParams): Promise<string> =>
|
||||
axios
|
||||
.get(
|
||||
`/printer/${printResumeAsPdfParams.username}/${printResumeAsPdfParams.slug}?lastUpdated=${printResumeAsPdfParams.lastUpdated}`
|
||||
`/printer/${printResumeAsPdfParams.username}/${printResumeAsPdfParams.slug}?lastUpdated=${printResumeAsPdfParams.lastUpdated}`,
|
||||
)
|
||||
.then((res) => res.data);
|
||||
|
||||
@ -20,7 +20,7 @@ const DEBOUNCE_WAIT = 1000;
|
||||
|
||||
const debouncedSync = debounce(
|
||||
(resume: Resume, dispatch: AppDispatch) => updateResume(resume).then((resume) => dispatch(setResume(resume))),
|
||||
DEBOUNCE_WAIT
|
||||
DEBOUNCE_WAIT,
|
||||
);
|
||||
|
||||
function* handleSync(dispatch: AppDispatch) {
|
||||
@ -31,7 +31,7 @@ function* handleSync(dispatch: AppDispatch) {
|
||||
|
||||
function* syncSaga(dispatch: AppDispatch) {
|
||||
yield takeLatest([setResumeState, addItem, editItem, duplicateItem, deleteItem, addSection, deleteSection], () =>
|
||||
handleSync(dispatch)
|
||||
handleSync(dispatch),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ import { addHttp, formatLocation, getPhotoClassNames } from '@/utils/template';
|
||||
export const MastheadSidebar: React.FC = () => {
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
||||
const { name, headline, photo, email, phone, birthdate, website, location, profiles } = useAppSelector(
|
||||
(state) => state.resume.present.basics
|
||||
(state) => state.resume.present.basics,
|
||||
);
|
||||
const theme: ThemeConfig = useAppSelector((state) => get(state.resume.present, 'metadata.theme', {} as ThemeConfig));
|
||||
const contrast = useMemo(() => getContrastColor(theme.primary), [theme.primary]);
|
||||
|
||||
@ -18,7 +18,7 @@ import { addHttp, formatLocation, getPhotoClassNames } from '@/utils/template';
|
||||
export const MastheadSidebar: React.FC = () => {
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
||||
const { name, headline, photo, email, phone, birthdate, website, location, profiles } = useAppSelector(
|
||||
(state) => state.resume.present.basics
|
||||
(state) => state.resume.present.basics,
|
||||
);
|
||||
const theme: ThemeConfig = useAppSelector((state) => get(state.resume.present, 'metadata.theme', {} as ThemeConfig));
|
||||
const contrast = useMemo(() => getContrastColor(theme.primary), [theme.primary]);
|
||||
|
||||
@ -13,7 +13,7 @@ export const MastheadSidebar: React.FC = () => {
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
||||
const primaryColor: string = useAppSelector((state) => get(state.resume.present, 'metadata.theme.primary'));
|
||||
const { name, headline, photo, email, phone, birthdate, website, location, profiles } = useAppSelector(
|
||||
(state) => state.resume.present.basics
|
||||
(state) => state.resume.present.basics,
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -12,7 +12,7 @@ import { addHttp, formatLocation, getPhotoClassNames } from '@/utils/template';
|
||||
const Masthead = () => {
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
||||
const { name, photo, email, phone, website, birthdate, headline, location, profiles } = useAppSelector(
|
||||
(state) => state.resume.present.basics
|
||||
(state) => state.resume.present.basics,
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
.container {
|
||||
@apply grid grid-cols-2 gap-4 px-6 py-4 items-start;
|
||||
@apply grid grid-cols-2 gap-4 mx-6 my-4 items-start;
|
||||
|
||||
.main {
|
||||
@apply grid gap-4;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ThemeConfig } from 'schema';
|
||||
import get from 'lodash/get';
|
||||
import { ThemeConfig } from 'schema';
|
||||
|
||||
import { useAppSelector } from '@/store/hooks';
|
||||
|
||||
@ -7,12 +7,12 @@ const Heading: React.FC<React.PropsWithChildren<unknown>> = ({ children }) => {
|
||||
const theme: ThemeConfig = useAppSelector((state) => get(state.resume.present, 'metadata.theme', {} as ThemeConfig));
|
||||
|
||||
return (
|
||||
<h2
|
||||
className="mb-2 pb-1 font-bold uppercase opacity-75"
|
||||
style={{ borderBottomWidth: '3px', borderColor: theme.primary, color: theme.primary, display: 'inline-block' }}
|
||||
<h3
|
||||
className="mb-2 inline-block border-b-2 pb-1 font-bold uppercase opacity-75"
|
||||
style={{ borderColor: theme.primary, color: theme.primary, display: 'inline-block' }}
|
||||
>
|
||||
{children}
|
||||
</h2>
|
||||
</h3>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Cake, Email, Phone, Public, Room } from '@mui/icons-material';
|
||||
import { alpha } from '@mui/material';
|
||||
import { ThemeConfig } from 'schema';
|
||||
import get from 'lodash/get';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { ThemeConfig } from 'schema';
|
||||
|
||||
import Markdown from '@/components/shared/Markdown';
|
||||
import { useAppSelector } from '@/store/hooks';
|
||||
@ -14,7 +14,7 @@ import { addHttp, formatLocation, getPhotoClassNames } from '@/utils/template';
|
||||
const Masthead: React.FC = () => {
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
||||
const { name, photo, headline, summary, email, phone, birthdate, website, location, profiles } = useAppSelector(
|
||||
(state) => state.resume.present.basics
|
||||
(state) => state.resume.present.basics,
|
||||
);
|
||||
const theme: ThemeConfig = useAppSelector((state) => get(state.resume.present, 'metadata.theme', {} as ThemeConfig));
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Email, Link, Phone } from '@mui/icons-material';
|
||||
import { ListItem, Section as SectionType } from 'schema';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { useMemo } from 'react';
|
||||
import { ListItem, Section as SectionType } from 'schema';
|
||||
|
||||
import Markdown from '@/components/shared/Markdown';
|
||||
import { useAppSelector } from '@/store/hooks';
|
||||
|
||||
@ -11,7 +11,7 @@ import { addHttp, formatLocation, getPhotoClassNames } from '@/utils/template';
|
||||
const Masthead: React.FC = () => {
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
||||
const { name, photo, email, phone, website, birthdate, headline, location, profiles } = useAppSelector(
|
||||
(state) => state.resume.present.basics
|
||||
(state) => state.resume.present.basics,
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ThemeConfig } from 'schema';
|
||||
import get from 'lodash/get';
|
||||
import { ThemeConfig } from 'schema';
|
||||
|
||||
import { useAppSelector } from '@/store/hooks';
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Cake, Email, Phone, Public, Room } from '@mui/icons-material';
|
||||
import { ThemeConfig } from 'schema';
|
||||
import clsx from 'clsx';
|
||||
import get from 'lodash/get';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { useMemo } from 'react';
|
||||
import { ThemeConfig } from 'schema';
|
||||
|
||||
import Markdown from '@/components/shared/Markdown';
|
||||
import { useAppSelector } from '@/store/hooks';
|
||||
@ -16,7 +16,7 @@ import { addHttp, formatLocation, getPhotoClassNames } from '@/utils/template';
|
||||
export const MastheadSidebar: React.FC = () => {
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
||||
const { name, photo, email, phone, birthdate, website, location, profiles } = useAppSelector(
|
||||
(state) => state.resume.present.basics
|
||||
(state) => state.resume.present.basics,
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -44,11 +44,11 @@ const sectionMap = (Section: React.FC<SectionProps>): Record<string, JSX.Element
|
||||
references: <Section key="references" path="sections.references" titlePath="name" subtitlePath="relationship" />,
|
||||
});
|
||||
|
||||
export const getSectionById = (id: string, Section: React.FC<SectionProps>): JSX.Element => {
|
||||
// Check if section id is a custom section (an uuid)
|
||||
if (validate(id)) {
|
||||
return <Section key={id} path={`sections.${id}`} />;
|
||||
}
|
||||
export const getSectionById = (id: string, Section: React.FC<SectionProps>): JSX.Element | null => {
|
||||
if (!id) return null;
|
||||
|
||||
// Check if section id is a custom section (is a valid uuid)
|
||||
if (validate(id)) return <Section key={id} path={`sections.${id}`} />;
|
||||
|
||||
// Check if section id is a predefined seciton in config
|
||||
const predefinedSection = get(sectionMap(Section), id);
|
||||
@ -57,9 +57,11 @@ export const getSectionById = (id: string, Section: React.FC<SectionProps>): JSX
|
||||
return predefinedSection;
|
||||
}
|
||||
|
||||
// Other ways section should be a cloned section
|
||||
const section = find(sectionMap(Section), (element, key) => id.includes(key));
|
||||
return React.cloneElement(section!, { path: `sections.${id}` });
|
||||
// Otherwise, section must be a cloned section
|
||||
const section = find(sectionMap(Section), (_element, key) => id.includes(key));
|
||||
if (section) return React.cloneElement(section, { path: `sections.${id}` });
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default sectionMap;
|
||||
|
||||
@ -41,12 +41,12 @@ export const formatDateString = (date: string | DateRange, formatStr: string): s
|
||||
if (!dayjs(date.start).isValid()) return null;
|
||||
|
||||
if (dayjs(date.start).isSame(date.end)) {
|
||||
return dayjs.utc(date.start).local().format(formatStr);
|
||||
return dayjs.utc(date.start).format(formatStr);
|
||||
}
|
||||
|
||||
if (!isEmpty(date.end) && dayjs(date.end).isValid()) {
|
||||
return `${dayjs.utc(date.start).local().format(formatStr)} - ${dayjs.utc(date.end).local().format(formatStr)}`;
|
||||
return `${dayjs.utc(date.start).format(formatStr)} - ${dayjs.utc(date.end).format(formatStr)}`;
|
||||
}
|
||||
|
||||
return `${dayjs.utc(date.start).local().format(formatStr)} - ${presentString}`;
|
||||
return `${dayjs.utc(date.start).format(formatStr)} - ${presentString}`;
|
||||
};
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { ListItem, Location, PhotoFilters } from 'schema';
|
||||
import clsx from 'clsx';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { ListItem, Location, PhotoFilters } from 'schema';
|
||||
|
||||
export type Required<T, K extends keyof T> = T & { [P in K]-?: T[P] };
|
||||
|
||||
@ -55,7 +55,7 @@ export const parseListItemPath = (item: ListItem, path: string | string[], separ
|
||||
|
||||
export const getPhotoClassNames = (filters: PhotoFilters) =>
|
||||
clsx({
|
||||
'object-cover': true,
|
||||
'object-cover aspect-square': true,
|
||||
grayscale: filters.grayscale,
|
||||
'!border-[4px] !border-solid': filters.border,
|
||||
'rounded-lg': filters.shape === 'rounded-square',
|
||||
|
||||
@ -11,7 +11,7 @@ const FontWrapper: React.FC<React.PropsWithChildren<unknown>> = ({ children }) =
|
||||
const WebFont = (await import('webfontloader')).default;
|
||||
const families = Object.values(typography.family).reduce(
|
||||
(acc, family) => [...acc, `${family}:400,600,700`],
|
||||
[] as string[]
|
||||
[] as string[],
|
||||
);
|
||||
|
||||
WebFont.load({ google: { families } });
|
||||
|
||||
27
package.json
27
package.json
@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "reactive-resume",
|
||||
"version": "3.8.1",
|
||||
"version": "3.8.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "env-cmd --silent cross-var cross-env VERSION=$npm_package_version turbo run dev",
|
||||
"build": "env-cmd --silent cross-var cross-env VERSION=$npm_package_version turbo run build",
|
||||
"start": "env-cmd --silent cross-var cross-env VERSION=$npm_package_version turbo run start",
|
||||
"format": "prettier --write --loglevel silent --cache .",
|
||||
"dev": "dotenv -- turbo run dev",
|
||||
"build": "dotenv -- turbo run build",
|
||||
"start": "dotenv -- turbo run start",
|
||||
"lint": "turbo run lint",
|
||||
"format": "prettier --write --loglevel silent --cache .",
|
||||
"update-dependencies": "ncu -x nanoid --deep -u && pnpm install",
|
||||
"generate-env": "ts-node ./scripts/generate-env.ts"
|
||||
},
|
||||
@ -17,23 +17,20 @@
|
||||
"server"
|
||||
],
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"cross-var": "^1.1.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"uuid": "^9.0.0"
|
||||
"dotenv-cli": "^7.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"eslint": "^8.44.0",
|
||||
"@types/node": "^20.4.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.0",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"npm-check-updates": "^16.10.15",
|
||||
"npm-check-updates": "^16.10.17",
|
||||
"prettier": "^3.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"turbo": "^1.10.7",
|
||||
"turbo": "^1.10.12",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
3283
pnpm-lock.yaml
generated
3283
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@
|
||||
"build": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.44.0",
|
||||
"eslint": "^8.45.0",
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ ENV TURBO_TOKEN=$TURBO_TOKEN
|
||||
|
||||
RUN pnpm exec turbo --filter server build
|
||||
|
||||
FROM mcr.microsoft.com/playwright:v1.35.1-focal as production
|
||||
FROM mcr.microsoft.com/playwright:v1.36.1-focal as production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@ -7,16 +7,16 @@
|
||||
"start": "node dist/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.369.0",
|
||||
"@aws-sdk/client-s3": "^3.378.0",
|
||||
"@nestjs/axios": "^3.0.0",
|
||||
"@nestjs/cache-manager": "^2.0.1",
|
||||
"@nestjs/common": "^10.0.5",
|
||||
"@nestjs/cache-manager": "^2.1.0",
|
||||
"@nestjs/common": "^10.1.2",
|
||||
"@nestjs/config": "^3.0.0",
|
||||
"@nestjs/core": "^10.0.5",
|
||||
"@nestjs/core": "^10.1.2",
|
||||
"@nestjs/jwt": "^10.1.0",
|
||||
"@nestjs/mapped-types": "^2.0.2",
|
||||
"@nestjs/passport": "^10.0.0",
|
||||
"@nestjs/platform-express": "^10.0.5",
|
||||
"@nestjs/platform-express": "^10.1.2",
|
||||
"@nestjs/schedule": "^3.0.1",
|
||||
"@nestjs/serve-static": "^4.0.0",
|
||||
"@nestjs/terminus": "^10.0.1",
|
||||
@ -30,34 +30,34 @@
|
||||
"cookie-parser": "^1.4.6",
|
||||
"csvtojson": "^2.0.10",
|
||||
"dayjs": "^1.11.9",
|
||||
"google-auth-library": "^8.9.0",
|
||||
"google-auth-library": "^9.0.0",
|
||||
"joi": "^17.9.2",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"nanoid": "^3.3.6",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"nodemailer": "^6.9.3",
|
||||
"nodemailer": "^6.9.4",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pg": "^8.11.1",
|
||||
"playwright-chromium": "^1.36.0",
|
||||
"playwright-chromium": "^1.36.2",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "0.3.17",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.1.8",
|
||||
"@nestjs/cli": "^10.1.10",
|
||||
"@nestjs/schematics": "^10.0.1",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/lodash": "^4.14.196",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^20.4.1",
|
||||
"@types/nodemailer": "^6.4.8",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/nodemailer": "^6.4.9",
|
||||
"@types/passport-jwt": "^3.0.9",
|
||||
"@types/passport-local": "^1.0.35",
|
||||
"@types/uuid": "^9.0.2",
|
||||
@ -70,6 +70,6 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.1.6",
|
||||
"webpack": "^5.88.1"
|
||||
"webpack": "^5.88.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
@ -20,7 +20,7 @@ export class AuthService {
|
||||
private schedulerRegistry: SchedulerRegistry,
|
||||
private configService: ConfigService,
|
||||
private usersService: UsersService,
|
||||
private jwtService: JwtService
|
||||
private jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
async register(registerDto: RegisterDto) {
|
||||
@ -49,7 +49,7 @@ export class AuthService {
|
||||
if (!isPasswordMatching) {
|
||||
throw new HttpException(
|
||||
'The username/email and password combination provided was incorrect.',
|
||||
HttpStatus.UNAUTHORIZED
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -64,7 +64,7 @@ export class AuthService {
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
'The username/email and password combination provided was incorrect.',
|
||||
HttpStatus.UNAUTHORIZED
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,10 @@ import { UsersService } from '@/users/users.service';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(configService: ConfigService, private readonly usersService: UsersService) {
|
||||
constructor(
|
||||
configService: ConfigService,
|
||||
private readonly usersService: UsersService,
|
||||
) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
secretOrKey: configService.get('auth.jwtSecret'),
|
||||
|
||||
@ -9,7 +9,10 @@ import cachedResponse from './assets/cachedResponse.json';
|
||||
|
||||
@Injectable()
|
||||
export class FontsService {
|
||||
constructor(private configService: ConfigService, private httpService: HttpService) {}
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private httpService: HttpService,
|
||||
) {}
|
||||
|
||||
async getAll(): Promise<Font[]> {
|
||||
const apiKey = this.configService.get('google.apiKey');
|
||||
|
||||
@ -6,7 +6,7 @@ export class HealthController {
|
||||
constructor(
|
||||
private health: HealthCheckService,
|
||||
private http: HttpHealthIndicator,
|
||||
private db: TypeOrmHealthIndicator
|
||||
private db: TypeOrmHealthIndicator,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
|
||||
@ -10,7 +10,7 @@ export class PrinterController {
|
||||
async printAsPdf(
|
||||
@Param('username') username: string,
|
||||
@Param('slug') slug: string,
|
||||
@Query('lastUpdated') lastUpdated: string
|
||||
@Query('lastUpdated') lastUpdated: string,
|
||||
): Promise<string> {
|
||||
try {
|
||||
return await this.printerService.printAsPdf(username, slug, lastUpdated);
|
||||
|
||||
@ -49,7 +49,10 @@ const minimal_chromium_args = [
|
||||
export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
private browser: BrowserContext;
|
||||
|
||||
constructor(private readonly schedulerRegistry: SchedulerRegistry, private readonly configService: ConfigService) {}
|
||||
constructor(
|
||||
private readonly schedulerRegistry: SchedulerRegistry,
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async onModuleInit() {
|
||||
this.browser = await chromium.launchPersistentContext('.playwright', {
|
||||
@ -84,7 +87,7 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
this.schedulerRegistry.deleteTimeout(`delete-${file}`);
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@ -99,7 +102,7 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
|
||||
const pageFormat: PageConfig['format'] = await page.$$eval(
|
||||
'[data-page]',
|
||||
(pages) => pages[0].getAttribute('data-format') as PageConfig['format']
|
||||
(pages) => pages[0].getAttribute('data-format') as PageConfig['format'],
|
||||
);
|
||||
|
||||
const resumePages = await page.$$eval('[data-page]', (pages) =>
|
||||
@ -107,7 +110,7 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
pageNumber: index + 1,
|
||||
innerHTML: page.innerHTML,
|
||||
height: page.clientHeight,
|
||||
}))
|
||||
})),
|
||||
);
|
||||
|
||||
const pdf = await PDFDocument.create();
|
||||
|
||||
@ -43,7 +43,7 @@ export class ResumeController {
|
||||
findOneByShortId(
|
||||
@Param('shortId') shortId: string,
|
||||
@User('id') userId?: number,
|
||||
@Query('secretKey') secretKey?: string
|
||||
@Query('secretKey') secretKey?: string,
|
||||
) {
|
||||
return this.resumeService.findOneByShortId(shortId, userId, secretKey);
|
||||
}
|
||||
@ -54,7 +54,7 @@ export class ResumeController {
|
||||
@Param('username') username: string,
|
||||
@Param('slug') slug: string,
|
||||
@User('id') userId?: number,
|
||||
@Query('secretKey') secretKey?: string
|
||||
@Query('secretKey') secretKey?: string,
|
||||
) {
|
||||
return this.resumeService.findOneByIdentifier(username, slug, userId, secretKey);
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ export class UsersService {
|
||||
@InjectRepository(User) private userRepository: Repository<User>,
|
||||
private schedulerRegistry: SchedulerRegistry,
|
||||
private mailService: MailService,
|
||||
private dataSource: DataSource
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
async findById(id: number): Promise<User> {
|
||||
@ -115,7 +115,7 @@ export class UsersService {
|
||||
|
||||
throw new HttpException(
|
||||
'Please wait at least 30 minutes before resetting your password again.',
|
||||
HttpStatus.TOO_MANY_REQUESTS
|
||||
HttpStatus.TOO_MANY_REQUESTS,
|
||||
);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
|
||||
@ -5,6 +5,10 @@
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
|
||||
},
|
||||
"start": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"lint": {},
|
||||
"format": {
|
||||
"dependsOn": ["^lint"]
|
||||
|
||||
Reference in New Issue
Block a user