Compare commits

...

53 Commits

Author SHA1 Message Date
d342c0a9af fix axios type issue 2023-01-03 17:17:35 +01:00
63084eebb4 feat(dependencies): ⬆️ update dependencies, fix date display issue, add more profile icons 2023-01-03 17:06:30 +01:00
3b4ea00db8 Merge pull request #1162 from AmruthPillai/dependabot/gradle/app/org.jetbrains.kotlin.android-1.8.0
Bump org.jetbrains.kotlin.android from 1.7.22 to 1.8.0 in /app
2023-01-03 16:51:17 +01:00
c8f7bffe7e Merge pull request #1160 from coolswood/main
fix: Gengar two cols bug
2023-01-03 16:51:09 +01:00
3ff56f89d9 Merge pull request #1158 from AmruthPillai/i18n_main
New Crowdin updates
2023-01-03 16:50:48 +01:00
7fb9f27837 Bump org.jetbrains.kotlin.android from 1.7.22 to 1.8.0 in /app
Bumps [org.jetbrains.kotlin.android](https://github.com/JetBrains/kotlin) from 1.7.22 to 1.8.0.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.22...v1.8.0)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin.android
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 02:04:34 +00:00
c9685d4ce7 fix: Gengar two cols bug 2022-12-31 10:01:54 +02:00
4dc987e27d New translations landing.json (Persian) 2022-12-30 19:09:41 +01:00
f7af06ae9a New translations builder.json (Persian) 2022-12-30 18:12:15 +01:00
a5c337faa3 Merge pull request #1151 from AmruthPillai/dependabot/github_actions/digitalocean/action-doctl-2.3.0
Bump digitalocean/action-doctl from 2.2.0 to 2.3.0
2022-12-30 11:48:34 +01:00
fc4704f0a6 Merge pull request #1150 from AmruthPillai/dependabot/docker/server/playwright-v1.29.1-focal
Bump playwright from v1.29.0-focal to v1.29.1-focal in /server
2022-12-30 11:48:25 +01:00
d968334ada Merge pull request #1155 from coolswood/main
fix: overflow-y-auto
2022-12-29 21:37:29 +01:00
fea6d23178 fix: overflow-y-auto 2022-12-29 19:37:47 +02:00
3fefc95572 Bump digitalocean/action-doctl from 2.2.0 to 2.3.0
Bumps [digitalocean/action-doctl](https://github.com/digitalocean/action-doctl) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/digitalocean/action-doctl/releases)
- [Commits](https://github.com/digitalocean/action-doctl/compare/v2.2.0...v2.3.0)

---
updated-dependencies:
- dependency-name: digitalocean/action-doctl
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-26 02:04:49 +00:00
b07e7d1213 Bump playwright from v1.29.0-focal to v1.29.1-focal in /server
Bumps playwright from v1.29.0-focal to v1.29.1-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-26 02:04:31 +00:00
d47b8bfb03 Merge pull request #1139 from AmruthPillai/dependabot/github_actions/actions/checkout-3.2.0
Bump actions/checkout from 3.1.0 to 3.2.0
2022-12-19 10:22:44 +01:00
5bf7fbdae1 Merge pull request #1140 from AmruthPillai/dependabot/docker/server/playwright-v1.29.0-focal
Bump playwright from v1.28.1-focal to v1.29.0-focal in /server
2022-12-19 10:22:34 +01:00
fca766b382 Bump playwright from v1.28.1-focal to v1.29.0-focal in /server
Bumps playwright from v1.28.1-focal to v1.29.0-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 02:06:27 +00:00
feadfb1b67 Bump actions/checkout from 3.1.0 to 3.2.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.1.0...v3.2.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 02:06:19 +00:00
e69000f221 Merge pull request #1137 from AmruthPillai/feat/add-remark-gfm-math-plugins
feat(client):  add github flavored syntax and math equations to markdown support
2022-12-17 10:27:19 +01:00
6b4a54465a feat(client): add github flavored syntax and math equations to markdown support 2022-12-17 10:19:25 +01:00
878659999f Merge pull request #1135 from AmruthPillai/i18n_main
New Crowdin updates
2022-12-17 09:53:13 +01:00
1868c47e30 New translations common.json (Dutch) 2022-12-17 09:48:12 +01:00
51442efc23 New translations common.json (German) 2022-12-17 09:48:07 +01:00
556e962ec5 refactor(client): 📝 add link to subreddit 2022-12-16 22:56:23 +01:00
b5ce67f863 Update README.md 2022-12-16 18:06:46 +01:00
c3ce89dc3a Update README.md 2022-12-16 18:04:22 +01:00
e87930c758 bump version to 3.6.16 2022-12-16 17:59:58 +01:00
815a693e58 Merge pull request #1134 from AmruthPillai/feature/configure-gitpod
Configure Gitpod for easier development and self-hosting
2022-12-16 17:58:50 +01:00
8287fcae96 add gitpod to README.md 2022-12-16 17:58:38 +01:00
cd7fe6c404 Remove .env.gitpod 2022-12-16 15:58:22 +00:00
d47d5dd819 build and run project finally, in gitpod 2022-12-16 16:52:37 +01:00
1919d79e43 fix url host parsing 2022-12-16 15:50:43 +00:00
ab08cd9e34 add sync await/done 2022-12-16 16:46:26 +01:00
2522bdd0a2 add onOpen: ignore to postgres port 2022-12-16 16:42:02 +01:00
f9b6aefffe create .env generation script 2022-12-16 16:39:29 +01:00
2ba6658a0b add CORS to server, input appUrl 2022-12-16 15:04:08 +00:00
dbc46f27a3 add postgres port 2022-12-16 14:12:43 +00:00
f21e1caed1 trying this now 2022-12-16 15:09:43 +01:00
4ffe2a6330 add 2022-12-16 15:03:22 +01:00
1bc0438872 remove 2022-12-16 15:03:06 +01:00
57fb9fdaea add dockerfile support to gitpod.yml 2022-12-16 14:01:17 +00:00
58ce641f18 add postgres to gitpod 2022-12-16 13:56:08 +00:00
5f4e7802e4 Merge branch 'main' into feature/configure-gitpod 2022-12-16 13:44:46 +00:00
42d3109ae1 Merge pull request #1128 from AmruthPillai/dependabot/gradle/app/org.jetbrains.kotlin.android-1.7.22
Bump org.jetbrains.kotlin.android from 1.7.21 to 1.7.22 in /app
2022-12-16 14:43:57 +01:00
f7ca7b97fa Merge pull request #1127 from AmruthPillai/dependabot/docker/server/playwright-v1.28.1-focal
Bump playwright from v1.28.0-focal to v1.28.1-focal in /server
2022-12-16 14:43:51 +01:00
f5d8a54134 Merge pull request #1130 from Jack-Kingdom/main
Fix: wait for networkidle event to ensure image load
2022-12-16 14:43:43 +01:00
eaec14dc62 initial gitpod configuration 2022-12-16 13:42:03 +00:00
c93b3264cd Bump playwright from v1.28.0-focal to v1.28.1-focal in /server
Bumps playwright from v1.28.0-focal to v1.28.1-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-16 13:34:56 +00:00
bf41aa9c6c feat(sentry): remove sentry integration 2022-12-16 14:34:15 +01:00
8af6bfd5ae Fix: wait for networkidle event to ensure image load 2022-12-10 02:55:38 +08:00
ab08c10874 Bump org.jetbrains.kotlin.android from 1.7.21 to 1.7.22 in /app
Bumps [org.jetbrains.kotlin.android](https://github.com/JetBrains/kotlin) from 1.7.21 to 1.7.22.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.21...v1.7.22)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin.android
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 02:08:23 +00:00
9af9a0284e set sentry user ID, if available and applicable 2022-12-03 00:15:08 +01:00
60 changed files with 2960 additions and 1892 deletions

View File

@ -1,12 +1,3 @@
# Turbo Cache (Optional)
TURBO_TEAM=
TURBO_TOKEN=
# Sentry Error Logging (Optional)
SENTRY_AUTH_TOKEN=
SERVER_SENTRY_DSN=
PUBLIC_CLIENT_SENTRY_DSN=
# Server + Client
TZ=UTC
PUBLIC_URL=http://localhost:3000
@ -20,7 +11,7 @@ POSTGRES_PASSWORD=postgres
# Server
SECRET_KEY=
POSTGRES_HOST=postgres
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_SSL_CERT=
JWT_SECRET=
@ -41,5 +32,5 @@ STORAGE_ACCESS_KEY=
STORAGE_SECRET_KEY=
PDF_DELETION_TIME=345600000
# Flags (Client)
# Client
PUBLIC_FLAG_DISABLE_SIGNUPS=false

View File

@ -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"

View File

@ -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 }}

View File

@ -10,18 +10,13 @@ jobs:
name: Build and Push Docker Image
runs-on: ubuntu-latest
env:
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_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
@ -56,22 +51,8 @@ jobs:
push: true
platforms: linux/amd64
file: ${{ matrix.image }}/Dockerfile
build-args: |
TURBO_TEAM=${{ secrets.TURBO_TEAM }}
TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
tags: |
amruthpillai/reactive-resume:${{ matrix.image }}-latest
amruthpillai/reactive-resume:${{ matrix.image }}-${{ steps.version.outputs.current-version }}
ghcr.io/amruthpillai/reactive-resume:${{ matrix.image }}-latest
ghcr.io/amruthpillai/reactive-resume:${{ matrix.image }}-${{ steps.version.outputs.current-version }}
- name: Create Sentry Release
uses: getsentry/action-release@v1.2.1
env:
SENTRY_ORG: reactive-resume
SENTRY_PROJECT: ${{ matrix.image }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
with:
environment: production
version: ${{ steps.version.outputs.current-version }}

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Environment Variables
.env
.env.*
!.env.gitpod
!.env.example
# Project Dependencies

41
.gitpod.yml Normal file
View 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

View File

@ -6,7 +6,8 @@
[![Project License](https://img.shields.io/github/license/AmruthPillai/Reactive-Resume?style=flat-square)](https://github.com/AmruthPillai/Reactive-Resume/blob/main/LICENSE)
[![Crowdin](https://badges.crowdin.net/reactive-resume/localized.svg)](https://translate.rxresu.me)
[![Docker Pulls](https://img.shields.io/docker/pulls/amruthpillai/reactive-resume?style=flat-square)](https://hub.docker.com/r/amruthpillai/reactive-resume)
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/AmruthPillai/Reactive-Resume/Build%20and%20Push%20Docker%20Image?label=docker%20build&style=flat-square)
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/AmruthPillai/Reactive-Resume/docker-build-push.yml?branch=main&label=docker%20build&style=flat-square)
[![Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/AmruthPillai/Reactive-Resume)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FAmruthPillai%2FReactive-Resume.svg?type=shield)](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
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](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

View File

@ -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) {

3
client/.gitignore vendored
View File

@ -40,6 +40,3 @@ __ENV.js
# next-sitemap
sitemap*.xml
# sentry
.sentryclirc

View File

@ -21,14 +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
ARG SENTRY_AUTH_TOKEN
ENV TURBO_TEAM $TURBO_TEAM
ENV TURBO_TOKEN $TURBO_TOKEN
ENV SENTRY_AUTH_TOKEN $SENTRY_AUTH_TOKEN
RUN pnpm run build --filter client
FROM base as production

View File

@ -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} />

View File

@ -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 />

View File

@ -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')}

View File

@ -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>(

View File

@ -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>
);

View File

@ -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';

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,6 +1,5 @@
const { version } = require('../package.json');
const { i18n } = require('./next-i18next.config');
const { withSentryConfig } = require('@sentry/nextjs');
/** @type {import('next').NextConfig} */
const nextConfig = {
@ -16,11 +15,6 @@ const nextConfig = {
domains: ['cdn.rxresu.me', 'www.gravatar.com'],
},
sentry: {
hideSourceMaps: true,
widenClientFileUpload: true,
},
// Hack to make Tailwind darkMode 'class' strategy with CSS Modules
// Ref: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomment-968368156
webpack: (config) => {
@ -53,11 +47,4 @@ const nextConfig = {
},
};
/** @type {import('@sentry/nextjs').SentryWebpackPluginOptions} */
const sentryConfig = {
silent: true,
project: 'client',
dryRun: process.env.NODE_ENV !== 'production',
};
module.exports = withSentryConfig(nextConfig, sentryConfig);
module.exports = nextConfig;

View File

@ -13,72 +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",
"@sentry/nextjs": "^7.21.1",
"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"
}
}

View File

@ -1,14 +0,0 @@
import * as Sentry from '@sentry/nextjs';
import type { NextPage } from 'next';
import type { ErrorProps } from 'next/error';
import NextErrorComponent from 'next/error';
const CustomErrorComponent: NextPage<ErrorProps> = (props) => <NextErrorComponent statusCode={props.statusCode} />;
CustomErrorComponent.getInitialProps = async (contextData) => {
await Sentry.captureUnderscoreErrorException(contextData);
return NextErrorComponent.getInitialProps(contextData);
};
export default CustomErrorComponent;

View File

@ -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')}

View File

@ -16,7 +16,7 @@
"present": "Heute"
},
"subtitle": "Ein kostenloser Open Source Lebenslauf-Baukasten.",
"title": "Reactive Resume",
"title": "Reaktives Lebenslauf",
"toast": {
"error": {
"upload-file-size": "Bitte laden Sie nur Dateien unter 2 Megabytes hoch.",

View File

@ -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",

View File

@ -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"
}
}

View File

@ -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": "زمانی که تنها یک صفحه وجود دارد، تاثیری ندارد",

View File

@ -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": "نظرات کاربران"
}
}

View File

@ -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"

View File

@ -1,15 +0,0 @@
import env from '@beam-australia/react-env';
import * as Sentry from '@sentry/nextjs';
import packageJSON from '../package.json';
const SENTRY_DSN = env('CLIENT_SENTRY_DSN');
if (SENTRY_DSN) {
Sentry.init({
dsn: SENTRY_DSN,
tracesSampleRate: 1.0,
release: packageJSON.version,
enabled: process.env.NODE_ENV === 'production',
});
}

View File

@ -1,3 +0,0 @@
defaults.url=https://sentry.io/
defaults.org=reactive-resume
defaults.project=client

View File

@ -1,15 +0,0 @@
import env from '@beam-australia/react-env';
import * as Sentry from '@sentry/nextjs';
import packageJSON from '../package.json';
const SENTRY_DSN = env('CLIENT_SENTRY_DSN');
if (SENTRY_DSN) {
Sentry.init({
dsn: SENTRY_DSN,
tracesSampleRate: 1.0,
release: packageJSON.version,
enabled: process.env.NODE_ENV === 'production',
});
}

View File

@ -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}`,
};

View File

@ -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;
}
}
}

View File

@ -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 : '',

View File

@ -10,7 +10,7 @@ export const dateFormatOptions: string[] = [
'DD.MM.YYYY',
'DD/MM/YYYY',
'MM.DD.YYYY',
'M.D.YYYY',
'M.D.YYYY',
'MM/DD/YYYY',
'YYYY.MM.DD',
'YYYY/MM/DD',
@ -20,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') ?? '';
@ -31,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
@ -40,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}`;
};

View File

@ -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 />,

View File

@ -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');

View File

@ -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;

View File

@ -1,13 +1,15 @@
{
"name": "reactive-resume",
"version": "3.6.15",
"version": "3.6.17",
"private": true,
"scripts": {
"dev": "env-cmd --silent cross-var cross-env VERSION=$npm_package_version turbo run dev",
"lint": "turbo run lint",
"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 ."
"update-deps": "ncu -x nanoid,class-validator --deep -u && pnpm install",
"generate-env": "ts-node ./scripts/generate-env.ts",
"format": "prettier --write .",
"lint": "turbo run lint"
},
"workspaces": [
"schema",
@ -18,16 +20,20 @@
"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",
"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",

4153
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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();

View File

@ -21,17 +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
ARG SENTRY_AUTH_TOKEN
ENV TURBO_TEAM $TURBO_TEAM
ENV TURBO_TOKEN $TURBO_TOKEN
ENV SENTRY_AUTH_TOKEN $SENTRY_AUTH_TOKEN
RUN pnpm run build --filter server
FROM mcr.microsoft.com/playwright:v1.28.0-focal as production
FROM mcr.microsoft.com/playwright:v1.29.1-focal as production
WORKDIR /app

View File

@ -8,67 +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",
"@sentry/node": "^7.21.1",
"@sentry/tracing": "^7.21.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"
}
}

View File

@ -1,3 +0,0 @@
defaults.url=https://sentry.io/
defaults.org=reactive-resume
defaults.project=server

View File

@ -11,7 +11,6 @@ import { HttpExceptionFilter } from './filters/http-exception.filter';
import { FontsModule } from './fonts/fonts.module';
import { HealthModule } from './health/health.module';
import { IntegrationsModule } from './integrations/integrations.module';
import { SentryInterceptor } from './interceptors/sentry.interceptor';
import { MailModule } from './mail/mail.module';
import { PrinterModule } from './printer/printer.module';
import { ResumeModule } from './resume/resume.module';
@ -41,10 +40,6 @@ import { UsersModule } from './users/users.module';
provide: APP_INTERCEPTOR,
useClass: ClassSerializerInterceptor,
},
{
provide: APP_INTERCEPTOR,
useClass: SentryInterceptor,
},
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,

View File

@ -7,7 +7,6 @@ import authConfig from './auth.config';
import cacheConfig from './cache.config';
import databaseConfig from './database.config';
import googleConfig from './google.config';
import loggingConfig from './logging.config';
import mailConfig from './mail.config';
import storageConfig from './storage.config';
@ -60,24 +59,12 @@ const validationSchema = Joi.object({
PDF_DELETION_TIME: Joi.number()
.default(4 * 24 * 60 * 60 * 1000) // 4 days
.allow(''),
// Logging
SERVER_SENTRY_DSN: Joi.string().allow(''),
});
@Module({
imports: [
NestConfigModule.forRoot({
load: [
appConfig,
authConfig,
cacheConfig,
databaseConfig,
googleConfig,
loggingConfig,
mailConfig,
storageConfig,
],
load: [appConfig, authConfig, cacheConfig, databaseConfig, googleConfig, mailConfig, storageConfig],
validationSchema: validationSchema,
}),
],

View File

@ -1,5 +0,0 @@
import { registerAs } from '@nestjs/config';
export default registerAs('logging', () => ({
sentryDSN: process.env.SERVER_SENTRY_DSN,
}));

View File

@ -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();
};
}

View File

@ -1,15 +0,0 @@
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import * as Sentry from '@sentry/node';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class SentryInterceptor implements NestInterceptor {
intercept(_context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
tap(null, (exception) => {
Sentry.captureException(exception);
})
);
}
}

View File

@ -1,10 +1,7 @@
import '@sentry/tracing';
import { Logger, ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import * as Sentry from '@sentry/node';
import cookieParser from 'cookie-parser';
import { AppModule } from './app.module';
@ -13,27 +10,16 @@ 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());
// Pipes
app.useGlobalPipes(new ValidationPipe({ transform: true }));
// Sentry Error Logging
const sentryDSN = configService.get<string>('logging.sentryDSN');
const version = configService.get<string>('app.version');
if (sentryDSN) {
Sentry.init({
dsn: sentryDSN,
release: version,
tracesSampleRate: 1.0,
enabled: process.env.NODE_ENV === 'production',
});
}
// Server Port
const port = configService.get<number>('app.port');
await app.listen(port);

View File

@ -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(

View File

@ -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',

View File

@ -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"]
}
}