mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-10 12:32:28 +10:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ebd9253038 | |||
| cf670af403 | |||
| dfccb3130f | |||
| ef06240935 | |||
| d61905db10 | |||
| 6d55f917ea | |||
| c8c5916d02 | |||
| 3ca27f2326 |
20
CHANGELOG.md
20
CHANGELOG.md
@ -2,8 +2,28 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [3.1.4](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.3...v3.1.4) (2022-03-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **client:** exported pdf did not contain "Present" keyword with translations ([cf670af](https://github.com/AmruthPillai/Reactive-Resume/commit/cf670af4035dc9b462cf5b1aad06ca089cf1d40c))
|
||||
* **client:** fix issues raised through lgtm alerts ([dfccb31](https://github.com/AmruthPillai/Reactive-Resume/commit/dfccb3130f889934d31196226be3d33e772f323b))
|
||||
|
||||
### [3.1.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.2...v3.1.3) (2022-03-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **server:** reform url for pdf generation and download ([6d55f91](https://github.com/AmruthPillai/Reactive-Resume/commit/6d55f917eab3cb2f5f3a90c5a18f03b625d60021)), closes [#661](https://github.com/AmruthPillai/Reactive-Resume/issues/661)
|
||||
|
||||
### [3.1.2](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.1...v3.1.2) (2022-03-12)
|
||||
|
||||
|
||||
### CI
|
||||
|
||||
* **docker:**: include traefik routing and proxy to ensure server connections pass in local ([11cb066](https://github.com/AmruthPillai/Reactive-Resume/commit/11cb066573c6917857b79c028b97fcda1acaf90a))
|
||||
|
||||
### [3.1.1](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.0...v3.1.1) (2022-03-12)
|
||||
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||

|
||||

|
||||
[](https://translate.rxresu.me)
|
||||
[](https://hub.docker.com/r/amruthpillai/reactive-resume)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FAmruthPillai%2FReactive-Resume?ref=badge_shield)
|
||||
|
||||
|
||||
@ -66,6 +66,7 @@ const Settings = () => {
|
||||
const code = value?.code || 'en';
|
||||
|
||||
document.cookie = `NEXT_LOCALE=${code}; path=/; expires=2147483647`;
|
||||
dispatch(setResumeState({ path: 'metadata.locale', value: code }));
|
||||
|
||||
push({ pathname, query }, asPath, { locale: code });
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Theme } from '@reactive-resume/schema';
|
||||
import { Theme as ThemeType } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
@ -16,7 +16,7 @@ const Theme = () => {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { background, text, primary } = useAppSelector<Theme>((state) => get(state.resume, 'metadata.theme'));
|
||||
const { background, text, primary } = useAppSelector<ThemeType>((state) => get(state.resume, 'metadata.theme'));
|
||||
|
||||
const handleChange = (property: string, color: string) => {
|
||||
dispatch(setResumeState({ path: `metadata.theme.${property}`, value: color }));
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Testimony } from '@/data/testimonials';
|
||||
import { Testimony as TestimonyType } from '@/data/testimonials';
|
||||
|
||||
import styles from './Testimony.module.scss';
|
||||
|
||||
type Props = Testimony;
|
||||
type Props = TestimonyType;
|
||||
|
||||
const Testimony: React.FC<Props> = ({ name, message }) => {
|
||||
return (
|
||||
|
||||
@ -5,6 +5,8 @@ import { useTranslation } from 'next-i18next';
|
||||
import { MouseEvent, useState } from 'react';
|
||||
|
||||
import { languages } from '@/config/languages';
|
||||
import { useAppDispatch } from '@/store/hooks';
|
||||
import { setResumeState } from '@/store/resume/resumeSlice';
|
||||
|
||||
import styles from './LanguageSwitcher.module.scss';
|
||||
|
||||
@ -13,6 +15,8 @@ const LanguageSwitcher = () => {
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const handleClick = (event: MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget);
|
||||
@ -23,6 +27,8 @@ const LanguageSwitcher = () => {
|
||||
const { pathname, asPath, query } = router;
|
||||
|
||||
document.cookie = `NEXT_LOCALE=${locale}; path=/; expires=2147483647`;
|
||||
dispatch(setResumeState({ path: 'metadata.locale', value: locale }));
|
||||
|
||||
router.push({ pathname, query }, asPath, { locale });
|
||||
};
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ export const FONTS_QUERY = 'fonts';
|
||||
export const RESUMES_QUERY = 'resumes';
|
||||
|
||||
// Regular Expressions
|
||||
export const VALID_URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
||||
export const VALID_URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)/;
|
||||
|
||||
// Date Formats
|
||||
export const FILENAME_TIMESTAMP = 'DDMMYYYYHHmmss';
|
||||
|
||||
@ -3,6 +3,7 @@ import clsx from 'clsx';
|
||||
import get from 'lodash/get';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { GetServerSideProps, NextPage } from 'next';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import Page from '@/components/build/Center/Page';
|
||||
@ -22,15 +23,19 @@ type Props = {
|
||||
redirect?: any;
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props | Promise<Props>, QueryParams> = async ({ query }) => {
|
||||
export const getServerSideProps: GetServerSideProps<Props | Promise<Props>, QueryParams> = async ({
|
||||
query,
|
||||
locale,
|
||||
}) => {
|
||||
const { username, slug, secretKey } = query as QueryParams;
|
||||
|
||||
try {
|
||||
if (isEmpty(secretKey)) throw new Error('There is no secret key!');
|
||||
|
||||
const resume = await fetchResumeByIdentifier({ username, slug, options: { secretKey } });
|
||||
const displayLocale = resume.metadata.locale || locale || 'en';
|
||||
|
||||
return { props: { resume } };
|
||||
return { props: { resume, ...(await serverSideTranslations(displayLocale, ['common'])) } };
|
||||
} catch (error) {
|
||||
return {
|
||||
redirect: {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Email, Link, Phone } from '@mui/icons-material';
|
||||
import { ListItem, Section } from '@reactive-resume/schema';
|
||||
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
headlinePath = 'headline',
|
||||
keywordsPath = 'keywords',
|
||||
}) => {
|
||||
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
|
||||
|
||||
if (!section.visible) return null;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Email, Link, Phone } from '@mui/icons-material';
|
||||
import { ListItem, Section } from '@reactive-resume/schema';
|
||||
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
headlinePath = 'headline',
|
||||
keywordsPath = 'keywords',
|
||||
}) => {
|
||||
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
|
||||
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Email, Link, Phone } from '@mui/icons-material';
|
||||
import { ListItem, Section } from '@reactive-resume/schema';
|
||||
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
@ -21,7 +21,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
headlinePath = 'headline',
|
||||
keywordsPath = 'keywords',
|
||||
}) => {
|
||||
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
|
||||
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Email, Phone } from '@mui/icons-material';
|
||||
import { ListItem, Section } from '@reactive-resume/schema';
|
||||
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
headlinePath = 'headline',
|
||||
keywordsPath = 'keywords',
|
||||
}) => {
|
||||
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
|
||||
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Email, Link, Phone } from '@mui/icons-material';
|
||||
import { ListItem, Section } from '@reactive-resume/schema';
|
||||
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
headlinePath = 'headline',
|
||||
keywordsPath = 'keywords',
|
||||
}) => {
|
||||
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
|
||||
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Email, Link, Phone } from '@mui/icons-material';
|
||||
import { ListItem, Section } from '@reactive-resume/schema';
|
||||
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
|
||||
headlinePath = 'headline',
|
||||
keywordsPath = 'keywords',
|
||||
}) => {
|
||||
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
|
||||
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
|
||||
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import TOCInline from '@theme/TOCInline';
|
||||
|
||||

|
||||

|
||||
[](https://translate.rxresu.me)
|
||||
[](https://hub.docker.com/r/amruthpillai/reactive-resume)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FAmruthPillai%2FReactive-Resume?ref=badge_shield)
|
||||
|
||||
|
||||
@ -33,13 +33,25 @@ pnpm dev
|
||||
|
||||
Now, your **frontend** client should be running on [`http://localhost:3000`](http://localhost:3000), your **backend** server on [`http://localhost:3100`](http://localhost:3100) and this **documentation** on [`http://localhost:3200`](http://localhost:3200).
|
||||
|
||||
5. Build the project before deploying by running the command:
|
||||
5. To ensure that the app works currently, a proxy layer has to be made between the client and server. For this, I made use of a Chrome extension called [**Rabbit URL Rewriter**](https://chrome.google.com/webstore/detail/rabbit-url-rewriter/kcbmcmeblpkcndhfhkclggekfblookii?hl=en) to forward my requests made to `localhost:3000/api` to `localhost:3100`. The configuration should look something like this:
|
||||
|
||||
```
|
||||
Website URL: http://localhost:3000
|
||||
From URL: http://localhost:3000/api/(.*)
|
||||
To URL: http://localhost:3100/$1
|
||||
```
|
||||
|
||||

|
||||
|
||||
Now, you should be able to create accounts, login etc.
|
||||
|
||||
6. Build the project before deploying by running the command:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
6. Finally, start the production servers for all three workspaces by running:
|
||||
7. Finally, start the production servers for all three workspaces by running:
|
||||
|
||||
```bash
|
||||
pnpm start
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "reactive-resume",
|
||||
"version": "3.1.2",
|
||||
"version": "3.1.4",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"schema",
|
||||
|
||||
@ -23,6 +23,7 @@ export type DateConfig = {
|
||||
|
||||
export type Metadata = {
|
||||
css: CustomCSS;
|
||||
locale: string;
|
||||
date: DateConfig;
|
||||
layout: string[][][]; // page.column.section
|
||||
template: string;
|
||||
|
||||
@ -45,7 +45,7 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
||||
const pdf = await PDFDocument.create();
|
||||
const directory = join(__dirname, '..', 'assets/exports');
|
||||
const filename = `RxResume_PDFExport_${nanoid()}.pdf`;
|
||||
const publicUrl = `/api/exports/${filename}`;
|
||||
const publicUrl = `/assets/exports/${filename}`;
|
||||
|
||||
for (let index = 0; index < resumePages.length; index++) {
|
||||
await page.evaluate((page) => (document.body.innerHTML = page.innerHTML), resumePages[index]);
|
||||
|
||||
@ -133,6 +133,7 @@ const defaultState: Partial<Resume> = {
|
||||
background: '#ffffff',
|
||||
primary: '#f44336',
|
||||
},
|
||||
locale: 'en',
|
||||
date: {
|
||||
format: 'MMMM DD, YYYY',
|
||||
},
|
||||
|
||||
@ -421,6 +421,7 @@ const sampleData: Partial<Resume> = {
|
||||
value: '/* Enter custom CSS here */\n\n* {\n outline: 1px solid #000;\n}',
|
||||
visible: false,
|
||||
},
|
||||
locale: 'en',
|
||||
date: {
|
||||
format: 'MMMM DD, YYYY',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user