mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-10 04:22:27 +10:00
Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d342c0a9af | |||
| 63084eebb4 | |||
| 3b4ea00db8 | |||
| c8f7bffe7e | |||
| 3ff56f89d9 | |||
| 7fb9f27837 | |||
| c9685d4ce7 | |||
| 4dc987e27d | |||
| f7af06ae9a | |||
| a5c337faa3 | |||
| fc4704f0a6 | |||
| d968334ada | |||
| fea6d23178 | |||
| 3fefc95572 | |||
| b07e7d1213 | |||
| d47b8bfb03 | |||
| 5bf7fbdae1 | |||
| fca766b382 | |||
| feadfb1b67 | |||
| e69000f221 | |||
| 6b4a54465a | |||
| 878659999f | |||
| 1868c47e30 | |||
| 51442efc23 | |||
| 556e962ec5 | |||
| b5ce67f863 | |||
| c3ce89dc3a | |||
| e87930c758 | |||
| 815a693e58 | |||
| 8287fcae96 | |||
| cd7fe6c404 | |||
| d47d5dd819 | |||
| 1919d79e43 | |||
| ab08cd9e34 | |||
| 2522bdd0a2 | |||
| f9b6aefffe | |||
| 2ba6658a0b | |||
| dbc46f27a3 | |||
| f21e1caed1 | |||
| 4ffe2a6330 | |||
| 1bc0438872 | |||
| 57fb9fdaea | |||
| 58ce641f18 | |||
| 5f4e7802e4 | |||
| 42d3109ae1 | |||
| f7ca7b97fa | |||
| f5d8a54134 | |||
| eaec14dc62 | |||
| c93b3264cd | |||
| bf41aa9c6c | |||
| 8af6bfd5ae | |||
| ab08c10874 | |||
| 9af9a0284e | |||
| 716a05032d | |||
| 43e43e7d76 | |||
| c91af3668d | |||
| 52f41f0b3b | |||
| 3b709d606b | |||
| 2e5fafac62 | |||
| ea2aee2d25 | |||
| e36fbb5f64 | |||
| 5221ef707b | |||
| f0df806f01 | |||
| 9d01d6a833 | |||
| 1914ebb9ae | |||
| 686dba90c9 | |||
| 95dc3bf571 | |||
| 1c8fdbf848 | |||
| d8357c9959 | |||
| 90e994377b | |||
| 82c6ee6d5d | |||
| 7b615e73c3 | |||
| 268e4a87fe | |||
| 73f8eb84c9 | |||
| a31ef89996 | |||
| d6bca7ebab | |||
| e0a42fd928 | |||
| deb4e0a0de | |||
| a687062866 | |||
| 700439c8a8 | |||
| fb09283e53 | |||
| 88ac365e03 | |||
| aec78cf875 | |||
| 77c587681b | |||
| 7ac8b906d9 | |||
| e9a5f86a6a | |||
| 7238a3b50e | |||
| ebe13fa82e | |||
| 6ee290a625 |
10
.env.example
10
.env.example
@ -1,7 +1,3 @@
|
||||
# Turbo Cache (Optional)
|
||||
TURBO_TEAM=
|
||||
TURBO_TOKEN=
|
||||
|
||||
# Server + Client
|
||||
TZ=UTC
|
||||
PUBLIC_URL=http://localhost:3000
|
||||
@ -15,7 +11,7 @@ POSTGRES_PASSWORD=postgres
|
||||
|
||||
# Server
|
||||
SECRET_KEY=
|
||||
POSTGRES_HOST=postgres
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_SSL_CERT=
|
||||
JWT_SECRET=
|
||||
@ -36,5 +32,5 @@ STORAGE_ACCESS_KEY=
|
||||
STORAGE_SECRET_KEY=
|
||||
PDF_DELETION_TIME=345600000
|
||||
|
||||
# Flags (Client)
|
||||
PUBLIC_FLAG_DISABLE_SIGNUPS=false
|
||||
# Client
|
||||
PUBLIC_FLAG_DISABLE_SIGNUPS=false
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
// TypeScript ESLint
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
|
||||
2
.github/workflows/digitalocean-deploy.yml
vendored
2
.github/workflows/digitalocean-deploy.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: Install DigitalOcean CLI
|
||||
uses: digitalocean/action-doctl@v2.2.0
|
||||
uses: digitalocean/action-doctl@v2.3.0
|
||||
with:
|
||||
token: ${{ secrets.DIGITALOCEAN_TOKEN }}
|
||||
|
||||
|
||||
6
.github/workflows/docker-build-push.yml
vendored
6
.github/workflows/docker-build-push.yml
vendored
@ -10,17 +10,13 @@ jobs:
|
||||
name: Build and Push Docker Image
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
image: [client, server]
|
||||
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3.1.0
|
||||
uses: actions/checkout@v3.2.0
|
||||
|
||||
- id: version
|
||||
name: App Version
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
# Environment Variables
|
||||
.env
|
||||
.env.*
|
||||
!.env.gitpod
|
||||
!.env.example
|
||||
|
||||
# Project Dependencies
|
||||
|
||||
41
.gitpod.yml
Normal file
41
.gitpod.yml
Normal file
@ -0,0 +1,41 @@
|
||||
tasks:
|
||||
- name: Run PostgreSQL Database
|
||||
command: docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres
|
||||
|
||||
- name: Install Project Dependencies
|
||||
command: |
|
||||
pnpm install
|
||||
pnpm dlx playwright install --with-deps chromium
|
||||
gp sync-done deps
|
||||
|
||||
- name: Generate Environment Variables
|
||||
init: gp sync-await deps
|
||||
command: |
|
||||
if [ -f .env ]; then
|
||||
echo "Found .env in workspace, skipping generation"
|
||||
else
|
||||
pnpm generate-env
|
||||
fi
|
||||
gp sync-done env
|
||||
|
||||
- name: Build and Run Project
|
||||
init: gp sync-await env
|
||||
command: |
|
||||
pnpm build
|
||||
pnpm start
|
||||
|
||||
ports:
|
||||
# PostgreSQL
|
||||
- port: 5432
|
||||
onOpen: ignore
|
||||
visibility: private
|
||||
|
||||
# Client
|
||||
- port: 3100
|
||||
onOpen: ignore
|
||||
visibility: public
|
||||
|
||||
# Client
|
||||
- port: 3000
|
||||
onOpen: open-browser
|
||||
visibility: public
|
||||
@ -6,7 +6,8 @@
|
||||
[](https://github.com/AmruthPillai/Reactive-Resume/blob/main/LICENSE)
|
||||
[](https://translate.rxresu.me)
|
||||
[](https://hub.docker.com/r/amruthpillai/reactive-resume)
|
||||

|
||||

|
||||
[](https://gitpod.io/#https://github.com/AmruthPillai/Reactive-Resume)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FAmruthPillai%2FReactive-Resume?ref=badge_shield)
|
||||
|
||||
## [Go to App](https://rxresu.me) | [Docs](https://docs.rxresu.me)
|
||||
@ -18,7 +19,7 @@ You have complete control over what goes into your resume, how it looks, what co
|
||||
## Table of Contents
|
||||
|
||||
- [Reactive Resume](#reactive-resume)
|
||||
- [Go to App Docs](https://docs.rxresu.me)
|
||||
- [Go to App | Docs](#go-to-app--docs)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Features](#features)
|
||||
- [Languages](#languages)
|
||||
@ -104,6 +105,10 @@ The docs include an extensive [Tutorial](https://docs.rxresu.me/tutorial) sectio
|
||||
|
||||
## Build from Source
|
||||
|
||||
[](https://gitpod.io/#https://github.com/AmruthPillai/Reactive-Resume)
|
||||
|
||||
Initially building the image and project on Gitpod will take at least ~10 minutes, so please be patient on first launch.
|
||||
|
||||
For extensive information on how to build the app on your local machine, head over to the docs [Source Code](https://docs.rxresu.me/source-code) section.
|
||||
|
||||
## Contributing
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application' version '7.1.2' apply false
|
||||
id 'com.android.library' version '7.1.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.7.21' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
|
||||
@ -21,12 +21,6 @@ COPY --from=dependencies /app/node_modules ./node_modules
|
||||
COPY --from=dependencies /app/schema/node_modules ./schema/node_modules
|
||||
COPY --from=dependencies /app/client/node_modules ./client/node_modules
|
||||
|
||||
ARG TURBO_TEAM
|
||||
ARG TURBO_TOKEN
|
||||
|
||||
ENV TURBO_TEAM $TURBO_TEAM
|
||||
ENV TURBO_TOKEN $TURBO_TOKEN
|
||||
|
||||
RUN pnpm run build --filter client
|
||||
|
||||
FROM base as production
|
||||
|
||||
@ -108,7 +108,7 @@ const LeftSidebar = () => {
|
||||
variant={isDesktop ? 'persistent' : 'temporary'}
|
||||
>
|
||||
<div className={styles.container}>
|
||||
<nav className="overflow-y-scroll">
|
||||
<nav className="overflow-y-auto">
|
||||
<div>
|
||||
<Link href="/dashboard">
|
||||
<Logo size={40} />
|
||||
|
||||
@ -43,7 +43,7 @@ const RightSidebar = () => {
|
||||
variant={isDesktop ? 'persistent' : 'temporary'}
|
||||
>
|
||||
<div className={styles.container}>
|
||||
<nav className="overflow-y-scroll">
|
||||
<nav className="overflow-y-auto">
|
||||
<div>
|
||||
<Avatar size={40} />
|
||||
<Divider />
|
||||
|
||||
@ -3,7 +3,7 @@ import { Button } from '@mui/material';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import Heading from '@/components/shared/Heading';
|
||||
import { DOCS_URL, DONATION_URL, GITHUB_ISSUES_URL, GITHUB_URL } from '@/constants/index';
|
||||
import { DOCS_URL, DONATION_URL, GITHUB_ISSUES_URL, GITHUB_URL, REDDIT_URL } from '@/constants/index';
|
||||
|
||||
import styles from './Links.module.scss';
|
||||
|
||||
@ -50,6 +50,12 @@ const Links = () => {
|
||||
</Button>
|
||||
</a>
|
||||
|
||||
<a href={REDDIT_URL} target="_blank" rel="noreferrer">
|
||||
<Button variant="text" startIcon={<Link />}>
|
||||
{t<string>('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')}
|
||||
|
||||
@ -51,7 +51,7 @@ const Settings = () => {
|
||||
const pageConfig: PageConfig | undefined = useMemo(() => get(resume, 'metadata.page'), [resume]);
|
||||
|
||||
const isDarkMode = useMemo(() => theme === 'dark', [theme]);
|
||||
const exampleDateString = useMemo(() => `Eg. ${dayjs().utc().format(dateConfig.format)}`, [dateConfig.format]);
|
||||
const exampleDateString = useMemo(() => `Eg. ${dayjs().format(dateConfig.format)}`, [dateConfig.format]);
|
||||
const themeString = useMemo(() => (isDarkMode ? 'Matte Black Everything' : 'As bright as your future'), [isDarkMode]);
|
||||
|
||||
const { mutateAsync: loadSampleDataMutation } = useMutation<Resume, ServerError, LoadSampleDataParams>(
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import clsx from 'clsx';
|
||||
import { isEmpty } from 'lodash';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
|
||||
type Props = {
|
||||
children?: string;
|
||||
@ -11,7 +14,11 @@ const Markdown: React.FC<Props> = ({ className, children }) => {
|
||||
if (!children || isEmpty(children)) return null;
|
||||
|
||||
return (
|
||||
<ReactMarkdown remarkPlugins={[]} className={clsx('markdown', className)}>
|
||||
<ReactMarkdown
|
||||
className={clsx('markdown', className)}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
>
|
||||
{children}
|
||||
</ReactMarkdown>
|
||||
);
|
||||
|
||||
@ -13,7 +13,7 @@ export const languages: Language[] = [
|
||||
{ code: 'ca', name: 'Catalan', localName: 'Valencian' },
|
||||
{ code: 'cs', name: 'Czech', localName: 'čeština' },
|
||||
{ code: 'da', name: 'Danish', localName: 'Dansk' },
|
||||
{ code: 'de', name: 'German', localName: 'Deutsch' },
|
||||
{ code: 'de', name: 'German', localName: 'Deutsch Formell / Sie' },
|
||||
{ code: 'el', name: 'Greek', localName: 'Ελληνικά' },
|
||||
{ code: 'en', name: 'English' },
|
||||
{ code: 'es', name: 'Spanish', localName: 'Español' },
|
||||
|
||||
@ -13,6 +13,7 @@ export const DOCS_URL = 'https://docs.rxresu.me';
|
||||
export const DONATION_URL = 'https://paypal.me/RajaRajanA';
|
||||
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/';
|
||||
export const GITHUB_URL = 'https://github.com/AmruthPillai/Reactive-Resume';
|
||||
export const PRODUCT_HUNT_URL = 'https://www.producthunt.com/posts/reactive-resume-v3';
|
||||
export const GITHUB_ISSUES_URL = 'https://github.com/AmruthPillai/Reactive-Resume/issues/new/choose';
|
||||
|
||||
@ -134,7 +134,7 @@ const AwardModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@ -134,7 +134,7 @@ const CertificateModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@ -151,7 +151,7 @@ const CustomModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
@ -175,7 +175,7 @@ const CustomModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@ -173,7 +173,7 @@ const EducationModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
@ -197,7 +197,7 @@ const EducationModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@ -143,7 +143,7 @@ const ProjectModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
@ -167,7 +167,7 @@ const ProjectModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@ -134,7 +134,7 @@ const PublicationModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@ -140,7 +140,7 @@ const VolunteerModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
@ -164,7 +164,7 @@ const VolunteerModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@ -140,7 +140,7 @@ const WorkModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
@ -164,7 +164,7 @@ const WorkModal: React.FC = () => {
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && field.onChange('');
|
||||
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
|
||||
@ -13,71 +13,73 @@
|
||||
"@emotion/css": "^11.10.5",
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@hello-pangea/dnd": "^16.0.1",
|
||||
"@hello-pangea/dnd": "^16.2.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@mui/icons-material": "^5.10.15",
|
||||
"@mui/lab": "^5.0.0-alpha.109",
|
||||
"@mui/material": "^5.10.15",
|
||||
"@mui/system": "^5.10.15",
|
||||
"@mui/x-date-pickers": "5.0.8",
|
||||
"@next/env": "^13.0.5",
|
||||
"@react-oauth/google": "^0.5.0",
|
||||
"@reduxjs/toolkit": "^1.9.0",
|
||||
"axios": "^1.2.0",
|
||||
"@mui/icons-material": "^5.11.0",
|
||||
"@mui/lab": "^5.0.0-alpha.114",
|
||||
"@mui/material": "^5.11.3",
|
||||
"@mui/system": "^5.11.2",
|
||||
"@mui/x-date-pickers": "5.0.12",
|
||||
"@next/env": "^13.1.1",
|
||||
"@react-oauth/google": "^0.5.1",
|
||||
"@reduxjs/toolkit": "^1.9.1",
|
||||
"axios": "^1.2.2",
|
||||
"clsx": "^1.2.1",
|
||||
"dayjs": "^1.11.6",
|
||||
"dayjs": "^1.11.7",
|
||||
"downloadjs": "^1.4.7",
|
||||
"joi": "^17.7.0",
|
||||
"lodash": "^4.17.21",
|
||||
"md5-hex": "^4.0.0",
|
||||
"monaco-editor": "^0.34.1",
|
||||
"nanoid": "^3.3.4",
|
||||
"next": "13.0.5",
|
||||
"next-i18next": "^13.0.0",
|
||||
"nanoid": "3.3.4",
|
||||
"next": "13.1.1",
|
||||
"next-i18next": "^13.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dnd": "16.0.1",
|
||||
"react-dnd-html5-backend": "16.0.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.39.5",
|
||||
"react-hook-form": "^7.41.3",
|
||||
"react-hot-toast": "2.4.0",
|
||||
"react-icons": "^4.6.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-markdown": "^8.0.4",
|
||||
"react-query": "^3.39.2",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-zoom-pan-pinch": "^2.1.3",
|
||||
"redux": "^4.2.0",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-saga": "^1.2.1",
|
||||
"redux-saga": "^1.2.2",
|
||||
"redux-undo": "^1.0.1",
|
||||
"rehype-katex": "^6.0.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "^0.31.2",
|
||||
"remark-math": "^5.1.1",
|
||||
"sharp": "^0.31.3",
|
||||
"uuid": "^9.0.0",
|
||||
"webfontloader": "^1.6.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.2",
|
||||
"@babel/core": "^7.20.7",
|
||||
"@reactive-resume/schema": "workspace:*",
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@types/downloadjs": "^1.4.3",
|
||||
"@types/lodash": "^4.14.190",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/react-redux": "^7.1.25",
|
||||
"@types/tailwindcss": "^3.0.11",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@types/webfontloader": "^1.6.35",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"csstype": "^3.1.1",
|
||||
"eslint-config-next": "^13.0.5",
|
||||
"eslint-plugin-tailwindcss": "^3.7.0",
|
||||
"eslint-config-next": "^13.1.1",
|
||||
"eslint-plugin-tailwindcss": "^3.8.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"next-sitemap": "^3.1.32",
|
||||
"postcss": "^8.4.19",
|
||||
"sass": "^1.56.1",
|
||||
"next-sitemap": "^3.1.44",
|
||||
"postcss": "^8.4.20",
|
||||
"sass": "^1.57.1",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"typescript": "^4.9.3"
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import styles from '@/styles/pages/Home.module.scss';
|
||||
|
||||
import { DIGITALOCEAN_URL, DOCS_URL, DONATION_URL, GITHUB_URL } from '../constants';
|
||||
import { DIGITALOCEAN_URL, DOCS_URL, DONATION_URL, GITHUB_URL, REDDIT_URL } from '../constants';
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => ({
|
||||
props: {
|
||||
@ -177,6 +177,12 @@ const Home: NextPage = () => {
|
||||
</Button>
|
||||
</a>
|
||||
|
||||
<a href={REDDIT_URL} target="_blank" rel="noreferrer">
|
||||
<Button variant="text" startIcon={<LinkIcon />}>
|
||||
{t<string>('landing.links.links.reddit')}
|
||||
</Button>
|
||||
</a>
|
||||
|
||||
<a href={DONATION_URL} target="_blank" rel="noreferrer">
|
||||
<Button variant="text" startIcon={<LinkIcon />}>
|
||||
{t<string>('landing.links.links.donate')}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
{
|
||||
"common": {
|
||||
"actions": {
|
||||
"add": "Neue {{token}} hinzufügen",
|
||||
"delete": "Löschen {{token}}",
|
||||
"edit": "Bearbeiten {{token}}"
|
||||
"add": "{{token}} hinzufügen",
|
||||
"delete": "{{token}} löschen",
|
||||
"edit": "{{token}} bearbeiten",
|
||||
"duplicate": "Abschnitt duplizieren"
|
||||
},
|
||||
"columns": {
|
||||
"heading": "Spalten",
|
||||
@ -17,10 +18,10 @@
|
||||
"label": "Beschreibung"
|
||||
},
|
||||
"email": {
|
||||
"label": "E-Mail Adresse"
|
||||
"label": "E-Mail-Adresse"
|
||||
},
|
||||
"end-date": {
|
||||
"help-text": "Dieses Feld leer lassen, wenn noch vorhanden",
|
||||
"help-text": "Dieses Feld leer lassen, wenn dieser Eintrag noch kein Enddatum hat.",
|
||||
"label": "Enddatum"
|
||||
},
|
||||
"keywords": {
|
||||
@ -69,7 +70,7 @@
|
||||
"empty-text": "Diese Liste ist leer."
|
||||
},
|
||||
"tooltip": {
|
||||
"delete-item": "Sind Sie sicher, dass Sie dieses Element löschen möchten? Dies ist eine unumkehrbare Aktion.",
|
||||
"delete-item": "Sind Sie sicher, dass Sie dieses Element löschen möchten? Dies lässt sich nicht rückgängig machen.",
|
||||
"delete-section": "Abschnitt löschen",
|
||||
"rename-section": "Abschnitt umbenennen",
|
||||
"toggle-visibility": "Sichtbarkeit umschalten"
|
||||
@ -86,7 +87,7 @@
|
||||
"zoom-in": "Vergrößern",
|
||||
"zoom-out": "Verkleinern",
|
||||
"undo": "Rückgängig machen",
|
||||
"redo": "Redo"
|
||||
"redo": "Wiederholen"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
@ -96,8 +97,8 @@
|
||||
"rename": "Umbenennen",
|
||||
"share-link": "Link teilen",
|
||||
"tooltips": {
|
||||
"delete": "Sind Sie sicher, dass Sie diesen Lebenslauf löschen möchten? Dies ist eine unumkehrbare Aktion.",
|
||||
"share-link": "Du musst die Sichtbarkeit deines Lebenslaufs in die Öffentlichkeit ändern, um ihn für andere sichtbar zu machen."
|
||||
"delete": "Sind Sie sicher, dass Sie diesen Lebenslauf löschen möchten? Dies lässt sich nicht rückgängig machen.",
|
||||
"share-link": "Sie müssen die Sichtbarkeit Ihres Lebenslaufs in Öffentlich ändern, um ihn für andere sichtbar zu machen."
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -106,7 +107,7 @@
|
||||
"awards": {
|
||||
"form": {
|
||||
"awarder": {
|
||||
"label": "Auszeichnung"
|
||||
"label": "Auszeichner"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -119,7 +120,7 @@
|
||||
"label": "Überschrift"
|
||||
},
|
||||
"name": {
|
||||
"label": "Voller Name"
|
||||
"label": "Vollständiger Name"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Geburtsdatum"
|
||||
@ -127,7 +128,7 @@
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
"label": "Grenze"
|
||||
"label": "Rahmen"
|
||||
},
|
||||
"grayscale": {
|
||||
"label": "Graustufen"
|
||||
@ -158,13 +159,13 @@
|
||||
"education": {
|
||||
"form": {
|
||||
"area-study": {
|
||||
"label": "Studienbereich"
|
||||
"label": "Studienfach"
|
||||
},
|
||||
"courses": {
|
||||
"label": "Kurse"
|
||||
},
|
||||
"degree": {
|
||||
"label": "Grad"
|
||||
"label": "Abschluss"
|
||||
},
|
||||
"grade": {
|
||||
"label": "Note"
|
||||
@ -176,7 +177,7 @@
|
||||
},
|
||||
"location": {
|
||||
"address": {
|
||||
"label": "Adresse"
|
||||
"label": "Straße"
|
||||
},
|
||||
"city": {
|
||||
"label": "Stadt"
|
||||
@ -184,12 +185,12 @@
|
||||
"country": {
|
||||
"label": "Land"
|
||||
},
|
||||
"heading": "Standort",
|
||||
"heading": "Anschrift",
|
||||
"postal-code": {
|
||||
"label": "Postleitzahl"
|
||||
},
|
||||
"region": {
|
||||
"label": "Region"
|
||||
"label": "Bundesland"
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
@ -201,7 +202,7 @@
|
||||
"label": "Benutzername"
|
||||
}
|
||||
},
|
||||
"heading": "Profiles",
|
||||
"heading": "Soziale Netzwerke",
|
||||
"heading_one": "Profil"
|
||||
},
|
||||
"publications": {
|
||||
@ -239,16 +240,16 @@
|
||||
"heading": "Exportieren",
|
||||
"json": {
|
||||
"primary": "JSON",
|
||||
"secondary": "Laden Sie eine JSON-Version Ihres Lebenslaufs herunter, die Sie wieder in Reaktives Lebenslauf importieren können."
|
||||
"secondary": "Laden Sie eine JSON-Version Ihres Lebenslaufs herunter, die Sie wieder in Reactive Resume importieren können."
|
||||
},
|
||||
"pdf": {
|
||||
"loading": {
|
||||
"primary": "PDF wird erstellt",
|
||||
"secondary": "Bitte warten Sie, wenn Ihr PDF generiert wird, dies kann bis zu 15 Sekunden dauern."
|
||||
"secondary": "Bitte warten Sie, während Ihr PDF generiert wird. Dies kann bis zu 15 Sekunden dauern."
|
||||
},
|
||||
"normal": {
|
||||
"primary": "PDF",
|
||||
"secondary": "Laden Sie sich ein PDF Ihres Lebenslaufs herunter, das Sie ausdrucken und an Ihren Traumjob senden können. Diese Datei kann nicht zur weiteren Bearbeitung importiert werden."
|
||||
"secondary": "Laden Sie sich ein PDF Ihres Lebenslaufs herunter, dass Sie ausdrucken oder an Ihren Traumarbeitgeber senden können. Diese Datei kann nicht zur weiteren Bearbeitung importiert werden."
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -256,18 +257,20 @@
|
||||
"heading": "Layout",
|
||||
"tooltip": {
|
||||
"reset-layout": "Layout zurücksetzen"
|
||||
}
|
||||
},
|
||||
"main": "Hauptteil",
|
||||
"sidebar": "Seitenleiste"
|
||||
},
|
||||
"links": {
|
||||
"bugs-features": {
|
||||
"body": "Hält Sie etwas davon ab, einen Lebenslauf zu erstellen? Oder haben Sie eine tolle Idee, die Sie hinzufügen möchten? Erhöhen Sie einen Eintrag auf GitHub, um loszulegen.",
|
||||
"button": "GitHub Themen",
|
||||
"heading": "Fehler? Feature-Anfragen?"
|
||||
"body": "Sind Sie bei der Erstellung Ihres Lebenslaufs auf ein Problem gestoßen? Oder haben Sie eine tolle Idee, die Sie hinzufügen möchten? Erstellen Sie ein Ticket auf GitHub.",
|
||||
"button": "GitHub Issues",
|
||||
"heading": "Fehler? Verbesserungsvorschläge?"
|
||||
},
|
||||
"donate": {
|
||||
"body": "Wenn Ihnen Reactive Resume gefallen hat, denken Sie bitte darüber nach, so viel wie möglich zu spenden, damit die App für immer kostenlos und werbefrei bleibt.",
|
||||
"body": "Sollte Ihnen Reactive Resume gefallen, möchte ich Sie bitten, etwas zu spenden, damit die App für immer kostenlos und werbefrei bleibt.",
|
||||
"button": "Kaufe mir einen Kaffee",
|
||||
"heading": "Spenden an Reaktives Lebenslauf"
|
||||
"heading": "Spenden Sie an Reactive Resume."
|
||||
},
|
||||
"github": "Quellcode",
|
||||
"docs": "Dokumentation",
|
||||
@ -277,43 +280,44 @@
|
||||
"global": {
|
||||
"date": {
|
||||
"primary": "Datum",
|
||||
"secondary": "Datumsformat für die gesamte App"
|
||||
"secondary": "Datumsformat für die gesamte App.",
|
||||
"prefix": "Z.B."
|
||||
},
|
||||
"heading": "Globale",
|
||||
"heading": "Global",
|
||||
"language": {
|
||||
"primary": "Sprache",
|
||||
"secondary": "Sprache anzeigen, die in der gesamten App verwendet wird"
|
||||
"secondary": "Anzeigesprache, die in der gesamten App verwendet wird."
|
||||
},
|
||||
"theme": {
|
||||
"primary": "Thema"
|
||||
"primary": "App Design"
|
||||
}
|
||||
},
|
||||
"heading": "Einstellungen",
|
||||
"page": {
|
||||
"format": {
|
||||
"primary": "Papier größe",
|
||||
"secondary": "Legt die Abmessungen Ihrer Lebenslaufseiten fest"
|
||||
"primary": "Papiergröße",
|
||||
"secondary": "Legt die Seitenabmessungen Ihres Lebenslaufs fest."
|
||||
},
|
||||
"break-line": {
|
||||
"primary": "Linie anhalten",
|
||||
"secondary": "Zeile auf allen Seiten anzeigen, um die Höhe einer A4-Seite zu markieren"
|
||||
"primary": "Seitenumbruch anzeigen",
|
||||
"secondary": "Zeigt den Seitenumbruch als Linie auf allen Seiten an."
|
||||
},
|
||||
"heading": "Seite",
|
||||
"orientation": {
|
||||
"disabled": "Hat keine Auswirkung, wenn nur eine Seite vorhanden ist",
|
||||
"disabled": "Hat keine Auswirkung, wenn nur eine Seite vorhanden ist.",
|
||||
"primary": "Ausrichtung",
|
||||
"secondary": "Ob Seiten horizontal oder vertikal angezeigt werden sollen"
|
||||
"secondary": "Legt fest, ob Seiten horizontal oder vertikal angezeigt werden sollen."
|
||||
}
|
||||
},
|
||||
"resume": {
|
||||
"heading": "Lebenslauf",
|
||||
"reset": {
|
||||
"primary": "Alles zurücksetzen",
|
||||
"secondary": "Zu viele Fehler gemacht? Klicken Sie hier, um alle Änderungen zurückzusetzen und bei Null zu beginnen. Sei vorsichtig, diese Aktion kann nicht rückgängig gemacht werden."
|
||||
"secondary": "Zu viele Fehler gemacht? Klicken Sie hier, um alle Änderungen zurückzusetzen und von vorne zu beginnen. Vorsicht! Diese Aktion kann nicht rückgängig gemacht werden."
|
||||
},
|
||||
"sample": {
|
||||
"primary": "Beispieldaten laden",
|
||||
"secondary": "Nicht sicher, wo man anfangen soll? Klicken Sie hier, um ein paar Beispieldaten zu laden, um zu sehen, wie ein vollständiger Lebenslauf aussieht."
|
||||
"secondary": "Sie sind nicht sicher, wo Sie anfangen sollen? Klicken Sie hier, um Beispieldaten zu laden. So können Sie sich ansehen, wie ein vollständiger Lebenslauf aussieht."
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -323,8 +327,8 @@
|
||||
"label": "Kurze URL bevorzugen"
|
||||
},
|
||||
"visibility": {
|
||||
"subtitle": "Erlaube jemandem mit einem Link deinen Lebenslauf anzusehen",
|
||||
"title": "Öffentlich"
|
||||
"subtitle": "Erlaubt jedem, dem Sie diesen Link schicken, Ihren Lebenslauf anzusehen.",
|
||||
"title": "Öffentlich zugänglich"
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
@ -333,16 +337,16 @@
|
||||
"theme": {
|
||||
"form": {
|
||||
"background": {
|
||||
"label": "Hintergrund"
|
||||
"label": "Hintergrundfarbe"
|
||||
},
|
||||
"primary": {
|
||||
"label": "Primär"
|
||||
"label": "Primärfarbe"
|
||||
},
|
||||
"text": {
|
||||
"label": "Text"
|
||||
"label": "Textfarbe"
|
||||
}
|
||||
},
|
||||
"heading": "Thema"
|
||||
"heading": "Lebenslauf Design"
|
||||
},
|
||||
"typography": {
|
||||
"form": {
|
||||
|
||||
@ -13,14 +13,14 @@
|
||||
"help-text": "Dieser Abschnitt unterstützt <1>Markdown</1> Formatierung."
|
||||
},
|
||||
"date": {
|
||||
"present": "gegenwärtig"
|
||||
"present": "Heute"
|
||||
},
|
||||
"subtitle": "Ein freier und Open-Source-Lebenslauf-Builder.",
|
||||
"subtitle": "Ein kostenloser Open Source Lebenslauf-Baukasten.",
|
||||
"title": "Reaktives Lebenslauf",
|
||||
"toast": {
|
||||
"error": {
|
||||
"upload-file-size": "Bitte laden Sie nur Dateien unter 2 Megabytes hoch.",
|
||||
"upload-photo-size": "Bitte laden Sie nur Fotos unter 2 Megabytes hoch, vorzugsweise quadratisch."
|
||||
"upload-photo-size": "Bitte laden Sie nur Fotos unter 2 Megabytes hoch, am besten in einem quadratischen Format."
|
||||
},
|
||||
"success": {
|
||||
"resume-link-copied": "Ein Link zu deinem Lebenslauf wurde in deine Zwischenablage kopiert."
|
||||
|
||||
@ -1,25 +1,25 @@
|
||||
{
|
||||
"create-resume": {
|
||||
"subtitle": "Bei Null anfangen",
|
||||
"subtitle": "Mit einem leeren Lebenslauf starten",
|
||||
"title": "Neuen Lebenslauf erstellen"
|
||||
},
|
||||
"import-external": {
|
||||
"subtitle": "LinkedIn, JSON Resume, Reaktives Lebenslauf",
|
||||
"title": "Aus externen Quellen importieren"
|
||||
"subtitle": "LinkedIn, JSON Lebenslauf, Reactive Resume",
|
||||
"title": "Aus externer Quelle importieren"
|
||||
},
|
||||
"resume": {
|
||||
"menu": {
|
||||
"delete": "Löschen",
|
||||
"duplicate": "Duplikat",
|
||||
"duplicate": "Duplizieren",
|
||||
"open": "Öffnen",
|
||||
"rename": "Umbenennen",
|
||||
"share-link": "Einen Link teilen",
|
||||
"share-link": "Link teilen",
|
||||
"tooltips": {
|
||||
"delete": "Möchten Sie diesen Lebenslauf wirklich löschen? Dies ist eine irreversible Aktion.",
|
||||
"delete": "Möchten Sie diesen Lebenslauf wirklich löschen? Dies lässt sich nicht rückgängig machen.",
|
||||
"share-link": "Sie müssen die Sichtbarkeit Ihres Lebenslaufs auf öffentlich ändern, um ihn für andere sichtbar zu machen."
|
||||
}
|
||||
},
|
||||
"timestamp": "Zuletzt vor {{timestamp}} aktualisiert"
|
||||
"timestamp": "Zuletzt vor {{timestamp}} geändert."
|
||||
},
|
||||
"title": "Dashboard"
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"actions": {
|
||||
"app": "Gehe zu App",
|
||||
"app": "Gehe zur App",
|
||||
"login": "Anmelden",
|
||||
"logout": "Ausloggen",
|
||||
"register": "Registrieren"
|
||||
@ -9,16 +9,16 @@
|
||||
"heading": "Eigenschaften",
|
||||
"list": {
|
||||
"ads": "Keine Werbung",
|
||||
"export": "Exportieren Sie Ihren Lebenslauf in JSON oder PDF Format",
|
||||
"free": "Frei, für immer",
|
||||
"import": "Importiere Daten von LinkedIn, JSON Lebenslauf",
|
||||
"languages": "In mehreren Sprachen zugänglich",
|
||||
"more": "Und viel mehr aufregende Features, <1>lesen Sie alles hier</1>",
|
||||
"tracking": "Keine Benutzerverfolgung"
|
||||
"export": "Exportieren Sie Ihren Lebenslauf als JSON oder PDF Format",
|
||||
"free": "Kostenlos, für immer",
|
||||
"import": "Importieren Sie Ihre Daten von LinkedIn oder als JSON Lebenslauf",
|
||||
"languages": "In mehreren Sprachen verfügbar",
|
||||
"more": "Und viele weitere aufregende Features. <1>Hier gibt es mehr informationen (in englischer Sprache)</1>",
|
||||
"tracking": "Kein Benutzertracking"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"heading": "Verknüpfungen",
|
||||
"heading": "Links",
|
||||
"links": {
|
||||
"donate": "Spenden",
|
||||
"github": "Quellcode",
|
||||
@ -32,11 +32,11 @@
|
||||
},
|
||||
"testimonials": {
|
||||
"heading": "Referenzen",
|
||||
"body": "Gut oder schlecht, ich würde gerne Ihre Meinung über Reactive Resume und wie die Erfahrung war für Sie.<br/>Hier sind einige der Nachrichten, die von Benutzern auf der ganzen Welt gesendet werden.",
|
||||
"contact": "Du kannst mich über <1>meine E-Mail</1> oder über das Kontaktformular auf <3>meiner Website</3>erreichen."
|
||||
"body": "Egal ob gut oder schlecht - ich würde gerne Ihre Meinung über Reactive Resume hören und welche Erfahrungen Sie gemacht haben.<br/>Hier sind einige der Nachrichten, die mir von Benutzern auf der ganzen Welt zugesandt wurden.",
|
||||
"contact": "Sie können mich über <1>meine E-Mail</1> oder über das Kontaktformular auf <3>meiner Website</3> erreichen."
|
||||
},
|
||||
"summary": {
|
||||
"body": "Reaktives Lebenslauf ist ein freier und Open-Source-Lebenslauf-Builder, der gebaut wurde, um die weltlichen Aufgaben zu machen, zu erstellen, Aktualisieren und teilen Sie Ihren Lebenslauf so einfach wie 1, 2, 3. Mit dieser App kannst du mehrere Bewerbungen erstellen, sie mit Recruitern oder Freunden über einen einzigartigen Link teilen und sie als PDF ausdrucken. alle kostenlos, keine Werbung, keine Verfolgung, ohne die Integrität und Privatsphäre Ihrer Daten zu verlieren.",
|
||||
"body": "Reactive Resume ist ein kostenloser Open Source Lebenslauf-Builder, der gebaut wurde, um die langweilige Aufgabe einen Lebenslauf zu erstellen, aktuell zu halten und zu teilen so einfach wie möglich zu machen. Mit dieser App können Sie mehrere Lebensläufe erstellen, sie mit Recruitern oder Freunden über einen einzigartigen Link teilen und als PDF exportieren. Kostenlos, ohne Werbung, kein Tracking, ohne die Integrität und Privatsphäre Ihrer Daten zu verlieren.",
|
||||
"heading": "Zusammenfassung"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
"auth": {
|
||||
"forgot-password": {
|
||||
"actions": {
|
||||
"send-email": "Passwort zurücksetzen E-Mail senden"
|
||||
"send-email": "Passwort zurücksetzen E-Mail senden."
|
||||
},
|
||||
"body": "Geben Sie einfach die E-Mail-Adresse ein, die mit dem Konto verknüpft ist, das Sie wiederherstellen möchten.",
|
||||
"body": "Geben Sie die E-Mail-Adresse des Benutzerkontos ein, dass Sie wiederherstellen möchten.",
|
||||
"form": {
|
||||
"email": {
|
||||
"label": "E-Mail-Addresse"
|
||||
}
|
||||
},
|
||||
"heading": "Passwort vergessen?",
|
||||
"help-text": "Wenn das Konto existiert, erhalten Sie eine E-Mail mit einem Link zum Zurücksetzen Ihres Passworts."
|
||||
"help-text": "Sollte das Konto existieren, erhalten Sie eine E-Mail mit einem Link zum Zurücksetzen des Passworts."
|
||||
},
|
||||
"login": {
|
||||
"actions": {
|
||||
@ -24,12 +24,12 @@
|
||||
},
|
||||
"username": {
|
||||
"help-text": "Sie können auch Ihre E-Mail-Adresse eingeben",
|
||||
"label": "Nutzername"
|
||||
"label": "Benutzername"
|
||||
}
|
||||
},
|
||||
"heading": "Bei Ihrem Konto anmelden",
|
||||
"recover-text": "Falls Sie Ihr Passwort vergessen haben, können Sie <1>Ihr Konto wiederherstellen</1> hier einrichten.",
|
||||
"register-text": "Wenn Sie keinen haben, können Sie hier <1>ein Konto erstellen</1> anlegen."
|
||||
"recover-text": "Falls Sie Ihr Passwort vergessen haben, können Sie es <1>hier zurücksetzen</1>.",
|
||||
"register-text": "Sollten Sie kein Benutzerkonto haben, können Sie <1>hier ein Konto anlegen</1>."
|
||||
},
|
||||
"register": {
|
||||
"actions": {
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
"actions": {
|
||||
"add": "Add New {{token}}",
|
||||
"delete": "Delete {{token}}",
|
||||
"edit": "Edit {{token}}",
|
||||
"duplicate": "Duplicate Section"
|
||||
"duplicate": "Duplicate Section",
|
||||
"edit": "Edit {{token}}"
|
||||
},
|
||||
"columns": {
|
||||
"heading": "Columns",
|
||||
@ -81,13 +81,13 @@
|
||||
"center-artboard": "Center Artboard",
|
||||
"copy-link": "Copy Link to Resume",
|
||||
"export-pdf": "Export PDF",
|
||||
"redo": "Redo",
|
||||
"toggle-orientation": "Toggle Page Orientation",
|
||||
"toggle-page-break-line": "Toggle Page Break Line",
|
||||
"toggle-sidebars": "Toggle Sidebars",
|
||||
"zoom-in": "Zoom In",
|
||||
"zoom-out": "Zoom Out",
|
||||
"undo": "Undo",
|
||||
"redo": "Redo"
|
||||
"zoom-in": "Zoom In",
|
||||
"zoom-out": "Zoom Out"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
@ -115,6 +115,9 @@
|
||||
"actions": {
|
||||
"photo-filters": "Photo Filters"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Date of Birth"
|
||||
},
|
||||
"heading": "Basics",
|
||||
"headline": {
|
||||
"label": "Headline"
|
||||
@ -122,9 +125,6 @@
|
||||
"name": {
|
||||
"label": "Full Name"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Date of Birth"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
@ -265,14 +265,15 @@
|
||||
"button": "GitHub Issues",
|
||||
"heading": "Bugs? Feature Requests?"
|
||||
},
|
||||
"docs": "Documentation",
|
||||
"donate": {
|
||||
"body": "If you liked using Reactive Resume, please consider donating as much as you can to the cause of keeping the app up and running, without ads and free forever.",
|
||||
"button": "Buy me a coffee",
|
||||
"heading": "Donate to Reactive Resume"
|
||||
},
|
||||
"github": "Source Code",
|
||||
"docs": "Documentation",
|
||||
"heading": "Links"
|
||||
"heading": "Links",
|
||||
"reddit": "Reddit"
|
||||
},
|
||||
"settings": {
|
||||
"global": {
|
||||
@ -291,14 +292,14 @@
|
||||
},
|
||||
"heading": "Settings",
|
||||
"page": {
|
||||
"format": {
|
||||
"primary": "Paper Size",
|
||||
"secondary": "Determines the dimensions of your resume pages"
|
||||
},
|
||||
"break-line": {
|
||||
"primary": "Break Line",
|
||||
"secondary": "Show a line on all pages to mark the height of an A4 page"
|
||||
},
|
||||
"format": {
|
||||
"primary": "Paper Size",
|
||||
"secondary": "Determines the dimensions of your resume pages"
|
||||
},
|
||||
"heading": "Page",
|
||||
"orientation": {
|
||||
"disabled": "Has no effect when there is only one page",
|
||||
|
||||
@ -20,23 +20,24 @@
|
||||
"links": {
|
||||
"heading": "Links",
|
||||
"links": {
|
||||
"docs": "Documentation",
|
||||
"donate": "Donate",
|
||||
"github": "Source Code",
|
||||
"docs": "Documentation",
|
||||
"privacy": "Privacy Policy",
|
||||
"reddit": "Reddit",
|
||||
"service": "Terms of Service"
|
||||
}
|
||||
},
|
||||
"screenshots": {
|
||||
"heading": "Screenshots"
|
||||
},
|
||||
"testimonials": {
|
||||
"heading": "Testimonials",
|
||||
"body": "Good or bad, I would love to hear your opinion on Reactive Resume and how the experience has been for you.<br/>Here are some of the messages sent in by users across the world.",
|
||||
"contact": "You can reach out to me through <1>my email</1> or through the contact form on <3>my website</3>."
|
||||
},
|
||||
"summary": {
|
||||
"body": "Reactive Resume is a free and open source resume builder that's built to make the mundane tasks of creating, updating and sharing your resume as easy as 1, 2, 3. With this app, you can create multiple resumes, share them with recruiters or friends through a unique link and print it as a PDF, all for free, no ads, no tracking, without losing the integrity and privacy of your data.",
|
||||
"heading": "Summary"
|
||||
},
|
||||
"testimonials": {
|
||||
"body": "Good or bad, I would love to hear your opinion on Reactive Resume and how the experience has been for you.<br/>Here are some of the messages sent in by users across the world.",
|
||||
"contact": "You can reach out to me through <1>my email</1> or through the contact form on <3>my website</3>.",
|
||||
"heading": "Testimonials"
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"actions": {
|
||||
"add": "{{token}} جدید اضافه کنید",
|
||||
"delete": "حذف {{token}}",
|
||||
"duplicate": "بخش تکراری",
|
||||
"edit": "ویرایش {{token}}"
|
||||
},
|
||||
"columns": {
|
||||
@ -80,13 +81,13 @@
|
||||
"center-artboard": "قرار دادن صفحه در مرکز",
|
||||
"copy-link": "کپی کردن لینک رزومه",
|
||||
"export-pdf": "خروجی PDF",
|
||||
"redo": "دوباره انجام دهید",
|
||||
"toggle-orientation": "تغییر وضعیت جهتگیری صفحه",
|
||||
"toggle-page-break-line": "تغییر وضعیت خط شکست صفحه",
|
||||
"toggle-sidebars": "باز/بسته کردن نوار کنار صفحه",
|
||||
"zoom-in": "بزرگنمایی",
|
||||
"zoom-out": "کوچکنمایی",
|
||||
"undo": "واگرد",
|
||||
"redo": "دوباره انجام دهید"
|
||||
"zoom-in": "بزرگنمایی",
|
||||
"zoom-out": "کوچکنمایی"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
@ -114,6 +115,9 @@
|
||||
"actions": {
|
||||
"photo-filters": "فیلترهای تصویر"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "تاریخ تولد"
|
||||
},
|
||||
"heading": "موارد پایه",
|
||||
"headline": {
|
||||
"label": "سرصفحه"
|
||||
@ -121,9 +125,6 @@
|
||||
"name": {
|
||||
"label": "نام کامل"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "تاریخ تولد"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
@ -264,14 +265,15 @@
|
||||
"button": "GitHub Issues",
|
||||
"heading": "باگها؟ درخواست ویژگی جدید؟"
|
||||
},
|
||||
"docs": "مستندات",
|
||||
"donate": {
|
||||
"body": "اگر استفاده از Reactive Resume را دوست داشتید، لطفاً تا جایی که می توانید کمک مالی کنید تا برنامه را بدون تبلیغات و برای همیشه رایگان نگه دارید.",
|
||||
"button": "برای من یک قهوه بخر",
|
||||
"heading": "کمک مالی به Reactive Resume"
|
||||
},
|
||||
"github": "کد منبع",
|
||||
"docs": "مستندات",
|
||||
"heading": "لینکها"
|
||||
"heading": "لینکها",
|
||||
"reddit": "ردیت"
|
||||
},
|
||||
"settings": {
|
||||
"global": {
|
||||
@ -290,14 +292,14 @@
|
||||
},
|
||||
"heading": "تنظیمات",
|
||||
"page": {
|
||||
"format": {
|
||||
"primary": "اندازه کاغذ",
|
||||
"secondary": "ابعاد صفحات رزومه شما را تعیین می کند"
|
||||
},
|
||||
"break-line": {
|
||||
"primary": "خط شکست",
|
||||
"secondary": "برای مشخص کردن ارتفاع صفحه A4 یک خط در همه صفحات نشان داده شود"
|
||||
},
|
||||
"format": {
|
||||
"primary": "اندازه کاغذ",
|
||||
"secondary": "ابعاد صفحات رزومه شما را تعیین می کند"
|
||||
},
|
||||
"heading": "صفحه",
|
||||
"orientation": {
|
||||
"disabled": "زمانی که تنها یک صفحه وجود دارد، تاثیری ندارد",
|
||||
|
||||
@ -20,23 +20,24 @@
|
||||
"links": {
|
||||
"heading": "لینکها",
|
||||
"links": {
|
||||
"docs": "مستندات",
|
||||
"donate": "حمایت مالی",
|
||||
"github": "کد منبع",
|
||||
"docs": "مستندات",
|
||||
"privacy": "حریم خصوصی",
|
||||
"reddit": "ردیت",
|
||||
"service": "شرایط سرویسدهی"
|
||||
}
|
||||
},
|
||||
"screenshots": {
|
||||
"heading": "اسکرینشاتها"
|
||||
},
|
||||
"testimonials": {
|
||||
"heading": "نظرات کاربران",
|
||||
"body": "خوب یا بد، من دوست دارم نظر شما را در مورد Reactive Resume و اینکه تجربه کار با آن برای شما چگونه بوده است را بدانم.<br/>تعدادی از پیام های ارسال شده توسط کاربران در سراسر جهان را اینجا میبینید.",
|
||||
"contact": "میتوانید از طریق <1>ایمیل من</1> یا فرم تماس در <3>وبسایت من</3> با من در ارتباط باشید."
|
||||
},
|
||||
"summary": {
|
||||
"body": "Reactive Resume یک رزومه ساز رایگان و متنباز است که برای ایجاد، به روز رسانی و به اشتراک گذاری رزومه شما به آسانی شمردن ۱، ۲، ۳ ساخته شده است. با این برنامه، می توانید چندین رزومه ایجاد کنید و آنها را با کارفرماها یا دوستان از طریق یک لینک منحصر به فرد و چاپ آن به صورت PDF، همه به صورت رایگان، بدون تبلیغات، بدون ردیابی، بدون از دست دادن امنیت و حریم خصوصی داده های شما، به اشتراک بگذارید.",
|
||||
"heading": "درباره من"
|
||||
},
|
||||
"testimonials": {
|
||||
"body": "خوب یا بد، من دوست دارم نظر شما را در مورد Reactive Resume و اینکه تجربه کار با آن برای شما چگونه بوده است را بدانم.<br/>تعدادی از پیام های ارسال شده توسط کاربران در سراسر جهان را اینجا میبینید.",
|
||||
"contact": "میتوانید از طریق <1>ایمیل من</1> یا فرم تماس در <3>وبسایت من</3> با من در ارتباط باشید.",
|
||||
"heading": "نظرات کاربران"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"avatar": {
|
||||
"menu": {
|
||||
"greeting": "Hallo",
|
||||
"logout": "Afmelden"
|
||||
"logout": "Uitloggen"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
@ -10,7 +10,7 @@
|
||||
"license": "Door de gemeenschap, voor de gemeenschap."
|
||||
},
|
||||
"markdown": {
|
||||
"help-text": "Deze sectie ondersteunt <1>markdown</1> opmaak."
|
||||
"help-text": "Deze sectie ondersteunt <1>html</1> opmaak."
|
||||
},
|
||||
"date": {
|
||||
"present": "Heden"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import env from '@beam-australia/react-env';
|
||||
import _axios from 'axios';
|
||||
import _axios, { RawAxiosRequestHeaders } from 'axios';
|
||||
import Router from 'next/router';
|
||||
|
||||
import { logout } from '@/store/auth/authSlice';
|
||||
@ -20,7 +20,7 @@ axios.interceptors.request.use((config) => {
|
||||
const { accessToken } = store.getState().auth;
|
||||
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
...(config.headers as RawAxiosRequestHeaders),
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
};
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import env from '@beam-australia/react-env';
|
||||
import { Resume } from '@reactive-resume/schema';
|
||||
import { AxiosResponse } from 'axios';
|
||||
|
||||
@ -62,9 +63,10 @@ export const fetchResumeByIdentifier = async ({
|
||||
options = { secretKey: '' },
|
||||
}: FetchResumeByIdentifierParams) => {
|
||||
if (!isBrowser) {
|
||||
const serverUrl = env('SERVER_URL');
|
||||
const secretKey = options.secretKey;
|
||||
|
||||
return axios.get<Resume>(`/resume/${username}/${slug}`, { params: { secretKey } }).then((res) => res.data);
|
||||
return fetch(`${serverUrl}/resume/${username}/${slug}?secretKey=${secretKey}`).then((response) => response.json());
|
||||
}
|
||||
|
||||
return axios.get<Resume>(`/resume/${username}/${slug}`).then((res) => res.data);
|
||||
|
||||
@ -2,6 +2,9 @@
|
||||
@import url('https://fonts.googleapis.com/icon?family=Material+Icons');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||
|
||||
// KaTeX (for remark-math)
|
||||
@import url('https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css');
|
||||
|
||||
// Tailwind CSS
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@ -35,6 +38,10 @@
|
||||
ul li {
|
||||
@apply ml-4 list-outside;
|
||||
}
|
||||
|
||||
.footnotes p {
|
||||
@apply inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -83,7 +83,7 @@ export const MastheadSidebar: React.FC = () => {
|
||||
icon={getProfileIcon(network)}
|
||||
link={url && addHttp(url)}
|
||||
className="!gap-2 text-xs"
|
||||
textClassName="invert"
|
||||
textClassName={clsx({ invert: contrast === 'light' })}
|
||||
>
|
||||
{username}
|
||||
</DataDisplay>
|
||||
|
||||
@ -10,7 +10,7 @@ import { useAppSelector } from '@/store/hooks';
|
||||
import { SectionProps } from '@/templates/sectionMap';
|
||||
import DataDisplay from '@/templates/shared/DataDisplay';
|
||||
import { formatDateString } from '@/utils/date';
|
||||
import { addHttp, parseListItemPath } from '@/utils/template';
|
||||
import { parseListItemPath } from '@/utils/template';
|
||||
|
||||
import Heading from './Heading';
|
||||
|
||||
@ -75,7 +75,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
{Array.from(Array(8).keys()).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="mr-1 h-2 w-4 rounded-sm border"
|
||||
className="mr-1 h-2 w-full max-w-[1rem] rounded-sm border"
|
||||
style={{
|
||||
borderColor: primaryColor,
|
||||
backgroundColor: levelNum / (10 / 8) > index ? primaryColor : '',
|
||||
@ -90,7 +90,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
{summary && <Markdown>{summary}</Markdown>}
|
||||
|
||||
{url && (
|
||||
<DataDisplay icon={<Link />} link={addHttp(url)}>
|
||||
<DataDisplay icon={<Link />} link={url}>
|
||||
{url}
|
||||
</DataDisplay>
|
||||
)}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Email, Phone } from '@mui/icons-material';
|
||||
import { Email, Link, Phone } from '@mui/icons-material';
|
||||
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
@ -8,8 +8,9 @@ import { useMemo } from 'react';
|
||||
import Markdown from '@/components/shared/Markdown';
|
||||
import { useAppSelector } from '@/store/hooks';
|
||||
import { SectionProps } from '@/templates/sectionMap';
|
||||
import DataDisplay from '@/templates/shared/DataDisplay';
|
||||
import { formatDateString } from '@/utils/date';
|
||||
import { addHttp, parseListItemPath } from '@/utils/template';
|
||||
import { parseListItemPath } from '@/utils/template';
|
||||
|
||||
import BadgeDisplay from './BadgeDisplay';
|
||||
import Heading from './Heading';
|
||||
@ -87,9 +88,9 @@ const Section: React.FC<SectionProps> = ({
|
||||
|
||||
{url && (
|
||||
<div className="inline-flex justify-center">
|
||||
<a href={addHttp(url)} target="_blank" rel="noreferrer">
|
||||
<DataDisplay link={url} icon={<Link />}>
|
||||
{url}
|
||||
</a>
|
||||
</DataDisplay>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import { useAppSelector } from '@/store/hooks';
|
||||
import { SectionProps } from '@/templates/sectionMap';
|
||||
import DataDisplay from '@/templates/shared/DataDisplay';
|
||||
import { formatDateString } from '@/utils/date';
|
||||
import { addHttp, parseListItemPath } from '@/utils/template';
|
||||
import { parseListItemPath } from '@/utils/template';
|
||||
|
||||
import Heading from './Heading';
|
||||
|
||||
@ -90,7 +90,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
{summary && <Markdown>{summary}</Markdown>}
|
||||
|
||||
{url && (
|
||||
<DataDisplay icon={<Link />} link={addHttp(url)} className="text-xs">
|
||||
<DataDisplay icon={<Link />} link={url} className="text-xs">
|
||||
{url}
|
||||
</DataDisplay>
|
||||
)}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import clsx from 'clsx';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
import { addHttp } from '@/utils/template';
|
||||
|
||||
type Props = {
|
||||
icon?: JSX.Element;
|
||||
link?: string;
|
||||
@ -11,11 +13,11 @@ type Props = {
|
||||
const DataDisplay: React.FC<React.PropsWithChildren<Props>> = ({ icon, link, className, textClassName, children }) => {
|
||||
if (isEmpty(children)) return null;
|
||||
|
||||
if (!isEmpty(link)) {
|
||||
if (link && !isEmpty(link)) {
|
||||
return (
|
||||
<div className={clsx('inline-flex items-center gap-1', className)}>
|
||||
{icon}
|
||||
<a href={link} target="_blank" rel="noreferrer" className={textClassName}>
|
||||
<a target="_blank" rel="noreferrer" href={addHttp(link)} className={textClassName}>
|
||||
{children}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -10,6 +10,7 @@ export const dateFormatOptions: string[] = [
|
||||
'DD.MM.YYYY',
|
||||
'DD/MM/YYYY',
|
||||
'MM.DD.YYYY',
|
||||
'M.D.YYYY',
|
||||
'MM/DD/YYYY',
|
||||
'YYYY.MM.DD',
|
||||
'YYYY/MM/DD',
|
||||
@ -19,7 +20,7 @@ export const dateFormatOptions: string[] = [
|
||||
'YYYY',
|
||||
];
|
||||
|
||||
export const getRelativeTime = (timestamp: dayjs.ConfigType): string => dayjs(timestamp).utc().toNow(true);
|
||||
export const getRelativeTime = (timestamp: dayjs.ConfigType): string => dayjs(timestamp).toNow(true);
|
||||
|
||||
export const formatDateString = (date: string | DateRange, formatStr: string): string | null => {
|
||||
const presentString = i18n?.t<string>('common.date.present') ?? '';
|
||||
@ -30,7 +31,7 @@ export const formatDateString = (date: string | DateRange, formatStr: string): s
|
||||
if (isString(date)) {
|
||||
if (!dayjs(date).isValid()) return null;
|
||||
|
||||
return dayjs(date).format(formatStr);
|
||||
return dayjs.utc(date).local().format(formatStr);
|
||||
}
|
||||
|
||||
// If `date` is a DateRange
|
||||
@ -39,12 +40,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(date.start).format(formatStr);
|
||||
return dayjs.utc(date.start).local().format(formatStr);
|
||||
}
|
||||
|
||||
if (!isEmpty(date.end) && dayjs(date.end).isValid()) {
|
||||
return `${dayjs(date.start).format(formatStr)} - ${dayjs(date.end).format(formatStr)}`;
|
||||
return `${dayjs.utc(date.start).local().format(formatStr)} - ${dayjs.utc(date.end).local().format(formatStr)}`;
|
||||
}
|
||||
|
||||
return `${dayjs(date.start).format(formatStr)} - ${presentString}`;
|
||||
return `${dayjs.utc(date.start).local().format(formatStr)} - ${presentString}`;
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
FaHackerrank,
|
||||
FaInstagram,
|
||||
FaLinkedinIn,
|
||||
FaMastodon,
|
||||
FaMedium,
|
||||
FaSkype,
|
||||
FaSoundcloud,
|
||||
@ -18,10 +19,11 @@ import {
|
||||
FaXing,
|
||||
FaYoutube,
|
||||
} from 'react-icons/fa';
|
||||
import { SiCodechef, SiCodeforces } from 'react-icons/si';
|
||||
import { SiCodeberg, SiCodechef, SiCodeforces } from 'react-icons/si';
|
||||
|
||||
const profileIconMap: Record<string, JSX.Element> = {
|
||||
behance: <FaBehance />,
|
||||
codeberg: <SiCodeberg />,
|
||||
codechef: <SiCodechef />,
|
||||
codeforces: <SiCodeforces />,
|
||||
dribbble: <FaDribbble />,
|
||||
@ -31,6 +33,7 @@ const profileIconMap: Record<string, JSX.Element> = {
|
||||
hackerrank: <FaHackerrank />,
|
||||
instagram: <FaInstagram />,
|
||||
linkedin: <FaLinkedinIn />,
|
||||
mastodon: <FaMastodon />,
|
||||
medium: <FaMedium />,
|
||||
skype: <FaSkype />,
|
||||
soundcloud: <FaSoundcloud />,
|
||||
|
||||
@ -13,9 +13,6 @@ const DateWrapper: React.FC<React.PropsWithChildren<unknown>> = ({ children }) =
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
// Set Default Timezone to UTC
|
||||
dayjs.tz.setDefault('UTC');
|
||||
|
||||
// Locales
|
||||
require('dayjs/locale/am');
|
||||
require('dayjs/locale/ar');
|
||||
|
||||
@ -2,16 +2,14 @@ import DateWrapper from './DateWrapper';
|
||||
import FontWrapper from './FontWrapper';
|
||||
import ThemeWrapper from './ThemeWrapper';
|
||||
|
||||
const WrapperRegistry: React.FC<React.PropsWithChildren<unknown>> = ({ children }) => {
|
||||
return (
|
||||
<ThemeWrapper>
|
||||
<FontWrapper>
|
||||
<DateWrapper>
|
||||
<>{children}</>
|
||||
</DateWrapper>
|
||||
</FontWrapper>
|
||||
</ThemeWrapper>
|
||||
);
|
||||
};
|
||||
const WrapperRegistry: React.FC<React.PropsWithChildren<unknown>> = ({ children }) => (
|
||||
<ThemeWrapper>
|
||||
<FontWrapper>
|
||||
<DateWrapper>
|
||||
<>{children}</>
|
||||
</DateWrapper>
|
||||
</FontWrapper>
|
||||
</ThemeWrapper>
|
||||
);
|
||||
|
||||
export default WrapperRegistry;
|
||||
|
||||
32
package.json
32
package.json
@ -1,14 +1,15 @@
|
||||
{
|
||||
"name": "reactive-resume",
|
||||
"version": "3.6.13",
|
||||
"version": "3.6.17",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "env-cmd --silent turbo run dev",
|
||||
"lint": "turbo run lint",
|
||||
"build": "env-cmd --silent turbo run build",
|
||||
"start": "env-cmd --silent turbo run start",
|
||||
"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",
|
||||
"update-deps": "ncu -x nanoid,class-validator --deep -u && pnpm install",
|
||||
"generate-env": "ts-node ./scripts/generate-env.ts",
|
||||
"format": "prettier --write .",
|
||||
"release": "standard-version --release-as patch"
|
||||
"lint": "turbo run lint"
|
||||
},
|
||||
"workspaces": [
|
||||
"schema",
|
||||
@ -16,18 +17,23 @@
|
||||
"server"
|
||||
],
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"cross-var": "^1.1.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"turbo": "^1.6.3"
|
||||
"turbo": "^1.6.3",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.44.0",
|
||||
"@typescript-eslint/parser": "^5.44.0",
|
||||
"eslint": "^8.28.0",
|
||||
"@types/node": "^18.11.18",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-simple-import-sort": "^8.0.0",
|
||||
"prettier": "^2.8.0",
|
||||
"standard-version": "^9.5.0",
|
||||
"typescript": "^4.9.3"
|
||||
"npm-check-updates": "^16.6.2",
|
||||
"prettier": "^2.8.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.2",
|
||||
|
||||
5339
pnpm-lock.yaml
generated
5339
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@
|
||||
"build": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.28.0",
|
||||
"typescript": "^4.9.3"
|
||||
"eslint": "^8.31.0",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
||||
|
||||
66
scripts/generate-env.ts
Normal file
66
scripts/generate-env.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { randomBytes } from 'crypto';
|
||||
import fs from 'fs/promises';
|
||||
|
||||
const envMap = {
|
||||
TZ: 'UTC',
|
||||
PUBLIC_URL: '',
|
||||
PUBLIC_SERVER_URL: '',
|
||||
PUBLIC_GOOGLE_CLIENT_ID: '',
|
||||
POSTGRES_DB: 'postgres',
|
||||
POSTGRES_USER: 'postgres',
|
||||
POSTGRES_PASSWORD: 'postgres',
|
||||
SECRET_KEY: '',
|
||||
POSTGRES_HOST: 'localhost',
|
||||
POSTGRES_PORT: '5432',
|
||||
POSTGRES_SSL_CERT: '',
|
||||
JWT_SECRET: '',
|
||||
JWT_EXPIRY_TIME: '604800',
|
||||
GOOGLE_CLIENT_SECRET: '',
|
||||
GOOGLE_API_KEY: '',
|
||||
MAIL_FROM_NAME: '',
|
||||
MAIL_FROM_EMAIL: '',
|
||||
MAIL_HOST: '',
|
||||
MAIL_PORT: '',
|
||||
MAIL_USERNAME: '',
|
||||
MAIL_PASSWORD: '',
|
||||
STORAGE_BUCKET: '',
|
||||
STORAGE_REGION: '',
|
||||
STORAGE_ENDPOINT: '',
|
||||
STORAGE_URL_PREFIX: '',
|
||||
STORAGE_ACCESS_KEY: '',
|
||||
STORAGE_SECRET_KEY: '',
|
||||
PDF_DELETION_TIME: '345600000',
|
||||
PUBLIC_FLAG_DISABLE_SIGNUPS: 'false',
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
// URLs
|
||||
// If running in a Gitpod environment, auto generated the URLs
|
||||
if (process.env.GITPOD_WORKSPACE_URL) {
|
||||
const baseUrl = new URL(process.env.GITPOD_WORKSPACE_URL!).host;
|
||||
|
||||
envMap['PUBLIC_SERVER_URL'] = `https://3100-${baseUrl}`;
|
||||
envMap['PUBLIC_URL'] = `https://3000-${baseUrl}`;
|
||||
}
|
||||
// Otherwise, fallback to localhost
|
||||
else {
|
||||
envMap['PUBLIC_SERVER_URL'] = 'https://localhost:3100';
|
||||
envMap['PUBLIC_URL'] = 'https://localhost:3000';
|
||||
}
|
||||
|
||||
// Secret Key
|
||||
envMap['SECRET_KEY'] = randomBytes(20).toString('hex');
|
||||
envMap['JWT_SECRET'] = randomBytes(40).toString('hex');
|
||||
|
||||
const envFile = Object.entries(envMap)
|
||||
.reduce((acc, [key, value]) => {
|
||||
acc.push(`${key}=${value}`);
|
||||
|
||||
return acc;
|
||||
}, [] as string[])
|
||||
.join('\n');
|
||||
|
||||
await fs.writeFile('.env', envFile);
|
||||
};
|
||||
|
||||
main();
|
||||
@ -21,15 +21,9 @@ COPY --from=dependencies /app/node_modules ./node_modules
|
||||
COPY --from=dependencies /app/schema/node_modules ./schema/node_modules
|
||||
COPY --from=dependencies /app/server/node_modules ./server/node_modules
|
||||
|
||||
ARG TURBO_TEAM
|
||||
ARG TURBO_TOKEN
|
||||
|
||||
ENV TURBO_TEAM $TURBO_TEAM
|
||||
ENV TURBO_TOKEN $TURBO_TOKEN
|
||||
|
||||
RUN pnpm run build --filter server
|
||||
|
||||
FROM mcr.microsoft.com/playwright:next-jammy as production
|
||||
FROM mcr.microsoft.com/playwright:v1.29.1-focal as production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@ -8,65 +8,66 @@
|
||||
"start": "node dist/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.216.0",
|
||||
"@nestjs/axios": "^1.0.0",
|
||||
"@nestjs/common": "^9.2.0",
|
||||
"@aws-sdk/client-s3": "^3.241.0",
|
||||
"@nestjs/axios": "^1.0.1",
|
||||
"@nestjs/common": "^9.2.1",
|
||||
"@nestjs/config": "^2.2.0",
|
||||
"@nestjs/core": "^9.2.0",
|
||||
"@nestjs/jwt": "^9.0.0",
|
||||
"@nestjs/core": "^9.2.1",
|
||||
"@nestjs/jwt": "^10.0.1",
|
||||
"@nestjs/mapped-types": "^1.2.0",
|
||||
"@nestjs/passport": "^9.0.0",
|
||||
"@nestjs/platform-express": "^9.2.0",
|
||||
"@nestjs/platform-express": "^9.2.1",
|
||||
"@nestjs/schedule": "^2.1.0",
|
||||
"@nestjs/serve-static": "^3.0.0",
|
||||
"@nestjs/terminus": "^9.1.3",
|
||||
"@nestjs/terminus": "^9.1.4",
|
||||
"@nestjs/typeorm": "^9.0.1",
|
||||
"@types/passport": "^1.0.11",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cache-manager": "^5.1.3",
|
||||
"cache-manager": "^5.1.4",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.13.2",
|
||||
"class-validator": "^0.13.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"csvtojson": "^2.0.10",
|
||||
"dayjs": "^1.11.6",
|
||||
"dayjs": "^1.11.7",
|
||||
"google-auth-library": "^8.7.0",
|
||||
"joi": "^17.7.0",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "^1.4.4",
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "3.3.4",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"nodemailer": "^6.8.0",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pg": "^8.8.0",
|
||||
"playwright-chromium": "^1.28.1",
|
||||
"playwright-chromium": "^1.29.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.5.7",
|
||||
"typeorm": "0.3.10",
|
||||
"rxjs": "^7.8.0",
|
||||
"typeorm": "0.3.11",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.1.5",
|
||||
"@nestjs/schematics": "^9.0.3",
|
||||
"@nestjs/cli": "^9.1.8",
|
||||
"@nestjs/schematics": "^9.0.4",
|
||||
"@reactive-resume/schema": "workspace:*",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/lodash": "^4.14.190",
|
||||
"@types/express": "^4.17.15",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/nodemailer": "^6.4.6",
|
||||
"@types/passport-jwt": "^3.0.7",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
"@types/passport-jwt": "^3.0.8",
|
||||
"@types/passport-local": "^1.0.34",
|
||||
"prettier": "^2.8.0",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"prettier": "^2.8.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-loader": "^9.4.1",
|
||||
"ts-loader": "^9.4.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.1.0",
|
||||
"typescript": "^4.9.3",
|
||||
"tsconfig-paths": "^4.1.2",
|
||||
"typescript": "^4.9.4",
|
||||
"webpack": "^5.75.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('app', () => ({
|
||||
timezone: process.env.TZ,
|
||||
version: process.env.VERSION,
|
||||
environment: process.env.NODE_ENV,
|
||||
secretKey: process.env.SECRET_KEY,
|
||||
port: parseInt(process.env.PORT, 10) || 3100,
|
||||
|
||||
@ -14,6 +14,7 @@ const validationSchema = Joi.object({
|
||||
// App
|
||||
TZ: Joi.string().default('UTC'),
|
||||
PORT: Joi.number().default(3100),
|
||||
VERSION: Joi.string().required(),
|
||||
SECRET_KEY: Joi.string().required(),
|
||||
NODE_ENV: Joi.string().valid('development', 'production').default('development'),
|
||||
|
||||
|
||||
@ -15,7 +15,6 @@ import {
|
||||
} from '@reactive-resume/schema';
|
||||
import csv from 'csvtojson';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { readFile, unlink } from 'fs/promises';
|
||||
import { cloneDeep, get, isEmpty, merge } from 'lodash';
|
||||
import StreamZip from 'node-stream-zip';
|
||||
@ -29,9 +28,7 @@ import { ResumeService } from '@/resume/resume.service';
|
||||
|
||||
@Injectable()
|
||||
export class IntegrationsService {
|
||||
constructor(private resumeService: ResumeService) {
|
||||
dayjs.extend(utc);
|
||||
}
|
||||
constructor(private resumeService: ResumeService) {}
|
||||
|
||||
async linkedIn(userId: number, path: string): Promise<ResumeEntity> {
|
||||
let archive: StreamZip.StreamZipAsync;
|
||||
@ -43,7 +40,7 @@ export class IntegrationsService {
|
||||
const resume: Partial<Resume> = cloneDeep(defaultState);
|
||||
|
||||
// Basics
|
||||
const timestamp = dayjs().utc().format(FILENAME_TIMESTAMP);
|
||||
const timestamp = dayjs().format(FILENAME_TIMESTAMP);
|
||||
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
|
||||
name: `Imported from LinkedIn (${timestamp})`,
|
||||
slug: `imported-from-linkedin-${timestamp}`,
|
||||
@ -276,7 +273,7 @@ export class IntegrationsService {
|
||||
const resume: Partial<Resume> = cloneDeep(defaultState);
|
||||
|
||||
// Metadata
|
||||
const timestamp = dayjs().utc().format(FILENAME_TIMESTAMP);
|
||||
const timestamp = dayjs().format(FILENAME_TIMESTAMP);
|
||||
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
|
||||
name: `Imported from JSON Resume (${timestamp})`,
|
||||
slug: `imported-from-json-resume-${timestamp}`,
|
||||
@ -611,7 +608,7 @@ export class IntegrationsService {
|
||||
const resume: Partial<Resume> = cloneDeep(jsonResume);
|
||||
|
||||
// Metadata
|
||||
const timestamp = dayjs().utc().format(FILENAME_TIMESTAMP);
|
||||
const timestamp = dayjs().format(FILENAME_TIMESTAMP);
|
||||
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
|
||||
name: `Imported from Reactive Resume (${timestamp})`,
|
||||
slug: `imported-from-reactive-resume-${timestamp}`,
|
||||
@ -632,7 +629,7 @@ export class IntegrationsService {
|
||||
const resume: Partial<Resume> = cloneDeep(defaultState);
|
||||
|
||||
// Metadata
|
||||
const timestamp = dayjs().utc().format(FILENAME_TIMESTAMP);
|
||||
const timestamp = dayjs().format(FILENAME_TIMESTAMP);
|
||||
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
|
||||
name: `Imported from Reactive Resume V2 (${timestamp})`,
|
||||
slug: `imported-from-reactive-resume-v2-${timestamp}`,
|
||||
@ -958,6 +955,6 @@ export class IntegrationsService {
|
||||
}
|
||||
|
||||
private parseDate = (date: string): string => {
|
||||
return isEmpty(date) ? '' : dayjs(date).utc().toISOString();
|
||||
return isEmpty(date) ? '' : dayjs(date).toISOString();
|
||||
};
|
||||
}
|
||||
|
||||
@ -10,8 +10,10 @@ const bootstrap = async () => {
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
||||
const configService = app.get(ConfigService);
|
||||
|
||||
const appUrl = configService.get<string>('app.url');
|
||||
|
||||
// Middleware
|
||||
app.enableCors({ credentials: true });
|
||||
app.enableCors({ origin: [appUrl], credentials: true });
|
||||
app.enableShutdownHooks();
|
||||
app.use(cookieParser());
|
||||
|
||||
|
||||
@ -31,10 +31,8 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
const publicUrl = `${serverUrl}/assets/exports/${filename}`;
|
||||
|
||||
try {
|
||||
// check if file already exists
|
||||
await access(join(directory, filename));
|
||||
} catch {
|
||||
// delete old files and scheduler jobs
|
||||
const activeSchedulerTimeouts = this.schedulerRegistry.getTimeouts();
|
||||
await readdir(directory).then(async (files) => {
|
||||
await Promise.all(
|
||||
@ -49,7 +47,6 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
);
|
||||
});
|
||||
|
||||
// create file as it doesn't exist
|
||||
const url = this.configService.get<string>('app.url');
|
||||
const secretKey = this.configService.get<string>('app.secretKey');
|
||||
const pdfDeletionTime = this.configService.get<number>('cache.pdfDeletionTime');
|
||||
@ -57,6 +54,7 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
const page = await this.browser.newPage();
|
||||
|
||||
await page.goto(`${url}/${username}/${slug}/printer?secretKey=${secretKey}`);
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForSelector('html.wf-active');
|
||||
|
||||
const pageFormat: PageConfig['format'] = await page.$$eval(
|
||||
|
||||
@ -5,9 +5,9 @@ const sampleData: Partial<Resume> = {
|
||||
name: 'Alexis Jones',
|
||||
email: 'alexis.jones@gmail.com',
|
||||
phone: '+1 800 1200 3820',
|
||||
birthdate: '1995-08-06T00:00:00.000Z',
|
||||
birthdate: '1995-08-06',
|
||||
photo: {
|
||||
url: `/images/sample-photo.jpg`,
|
||||
url: `https://i.imgur.com/40gTnCx.jpg`,
|
||||
filters: {
|
||||
size: 128,
|
||||
shape: 'rounded-square',
|
||||
@ -53,7 +53,7 @@ const sampleData: Partial<Resume> = {
|
||||
url: 'https://www.espritcam.com',
|
||||
date: {
|
||||
end: '',
|
||||
start: '2015-09-01T16:34:27.000Z',
|
||||
start: '2015-09-01',
|
||||
},
|
||||
name: 'DP Technology Corp.',
|
||||
summary:
|
||||
@ -64,8 +64,8 @@ const sampleData: Partial<Resume> = {
|
||||
id: '285d78f8-df56-4569-ba6b-cff5ebe5381e',
|
||||
url: 'https://www.vokophone.com',
|
||||
date: {
|
||||
end: '2015-07-31T22:00:00.000Z',
|
||||
start: '2011-05-31T22:00:00.000Z',
|
||||
end: '2015-07-31',
|
||||
start: '2011-05-31',
|
||||
},
|
||||
name: 'Voko Communications',
|
||||
summary:
|
||||
@ -84,7 +84,7 @@ const sampleData: Partial<Resume> = {
|
||||
{
|
||||
title: 'Blitz Hackathon',
|
||||
awarder: '2nd Place',
|
||||
date: '2018-03-31T22:00:00.000Z',
|
||||
date: '2018-03-31',
|
||||
url: '',
|
||||
summary: '',
|
||||
id: '657cadb0-c07d-4a35-8351-9079598c7ac0',
|
||||
@ -92,7 +92,7 @@ const sampleData: Partial<Resume> = {
|
||||
{
|
||||
title: 'Carl-Zeiss Hackathon',
|
||||
awarder: '2nd Place',
|
||||
date: '2017-05-09T22:00:00.000Z',
|
||||
date: '2017-05-09',
|
||||
url: '',
|
||||
summary: '',
|
||||
id: 'db3bc5cb-483e-4221-9867-9c28ee5f2051',
|
||||
|
||||
16
turbo.json
16
turbo.json
@ -1,19 +1,13 @@
|
||||
{
|
||||
"$schema": "https://turborepo.org/schema.json",
|
||||
"globalDependencies": [".env"],
|
||||
"pipeline": {
|
||||
"dev": {
|
||||
"cache": false
|
||||
},
|
||||
"lint": {
|
||||
"cache": false
|
||||
},
|
||||
"start": {
|
||||
"cache": false
|
||||
},
|
||||
"dev": {},
|
||||
"lint": {},
|
||||
"start": {},
|
||||
"build": {
|
||||
"outputs": ["dist/**", ".next/**"],
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
},
|
||||
"globalDependencies": [".env"]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user