fix(i18n): load locales from file system, instead of http-backend
@ -1,8 +0,0 @@
|
|||||||
NEXT_PUBLIC_APP_VERSION=$npm_package_version
|
|
||||||
|
|
||||||
# App & Server URLs
|
|
||||||
NEXT_PUBLIC_APP_URL=$APP_URL
|
|
||||||
NEXT_PUBLIC_SERVER_URL=$SERVER_URL
|
|
||||||
|
|
||||||
# Google OAuth
|
|
||||||
NEXT_PUBLIC_GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID
|
|
||||||
@ -19,5 +19,8 @@
|
|||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"jest": true
|
"jest": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@next/next/no-img-element": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
ZoomIn,
|
ZoomIn,
|
||||||
ZoomOut,
|
ZoomOut,
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { ButtonBase, Divider, Tooltip } from '@mui/material';
|
import { ButtonBase, Divider, Tooltip, useMediaQuery, useTheme } from '@mui/material';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
@ -28,9 +28,11 @@ import styles from './ArtboardController.module.scss';
|
|||||||
const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, centerView }) => {
|
const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, centerView }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const resume = useAppSelector((state) => state.resume);
|
||||||
|
const isDesktop = useMediaQuery(theme.breakpoints.up('sm'));
|
||||||
const { left, right } = useAppSelector((state) => state.build.sidebar);
|
const { left, right } = useAppSelector((state) => state.build.sidebar);
|
||||||
const orientation = useAppSelector((state) => state.build.page.orientation);
|
const orientation = useAppSelector((state) => state.build.page.orientation);
|
||||||
|
|
||||||
@ -92,6 +94,8 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
{isDesktop && (
|
||||||
|
<>
|
||||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-orientation')}>
|
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-orientation')}>
|
||||||
<ButtonBase onClick={handleTogglePageOrientation}>
|
<ButtonBase onClick={handleTogglePageOrientation}>
|
||||||
{orientation === 'vertical' ? (
|
{orientation === 'vertical' ? (
|
||||||
@ -115,6 +119,8 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.copy-link')}>
|
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.copy-link')}>
|
||||||
<ButtonBase onClick={handleCopyLink}>
|
<ButtonBase onClick={handleCopyLink}>
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const RightSidebar = () => {
|
|||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
||||||
<div>v{process.env.NEXT_PUBLIC_APP_VERSION}</div>
|
<div>v{process.env.appVersion}</div>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,18 +1,15 @@
|
|||||||
import HttpBackend from 'i18next-http-backend';
|
import { UserConfig } from 'next-i18next';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
const i18nConfig = {
|
const i18nConfig: UserConfig = {
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en'],
|
locales: ['en'],
|
||||||
},
|
},
|
||||||
debug: false,
|
debug: false,
|
||||||
nsSeparator: '.',
|
nsSeparator: '.',
|
||||||
|
localePath: join(__dirname, '../../../public/locales'),
|
||||||
ns: ['common', 'modals', 'landing', 'dashboard', 'builder'],
|
ns: ['common', 'modals', 'landing', 'dashboard', 'builder'],
|
||||||
serializeConfig: false,
|
|
||||||
use: [HttpBackend],
|
|
||||||
backend: {
|
|
||||||
loadPath: `${process.env.NEXT_PUBLIC_APP_URL}/locales/{{lng}}/{{ns}}.json`,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default i18nConfig;
|
export default i18nConfig;
|
||||||
|
|||||||
@ -54,7 +54,7 @@ const LoginModal: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { signIn } = useGoogleLogin({
|
const { signIn } = useGoogleLogin({
|
||||||
clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
|
clientId: process.env.googleClientId,
|
||||||
onSuccess: async (response: GoogleLoginResponse) => {
|
onSuccess: async (response: GoogleLoginResponse) => {
|
||||||
await loginWithGoogleMutation({ accessToken: response.accessToken });
|
await loginWithGoogleMutation({ accessToken: response.accessToken });
|
||||||
|
|
||||||
|
|||||||
@ -9,12 +9,19 @@ const nextConfig = {
|
|||||||
locales: ['en'],
|
locales: ['en'],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
env: {
|
||||||
|
appUrl: process.env.APP_URL,
|
||||||
|
serverUrl: process.env.SERVER_URL,
|
||||||
|
appVersion: process.env.npm_package_version,
|
||||||
|
googleClientId: process.env.GOOGLE_CLIENT_ID,
|
||||||
|
},
|
||||||
|
|
||||||
nx: {
|
nx: {
|
||||||
svgr: false,
|
svgr: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
images: {
|
images: {
|
||||||
domains: ['localhost', 'www.gravatar.com'],
|
domains: ['www.gravatar.com'],
|
||||||
},
|
},
|
||||||
|
|
||||||
// Hack to make Tailwind darkMode 'class' strategy with CSS Modules
|
// Hack to make Tailwind darkMode 'class' strategy with CSS Modules
|
||||||
|
|||||||
@ -20,8 +20,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"buildTarget": "client:build",
|
"buildTarget": "client:build",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"port": 3000,
|
"port": 3000
|
||||||
"proxyConfig": "apps/client/proxy.conf.json"
|
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 265 KiB After Width: | Height: | Size: 265 KiB |
|
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 274 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 224 KiB After Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 314 KiB After Width: | Height: | Size: 314 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 319 KiB After Width: | Height: | Size: 319 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 329 KiB After Width: | Height: | Size: 329 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 217 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 323 KiB After Width: | Height: | Size: 323 KiB |
|
Before Width: | Height: | Size: 267 KiB After Width: | Height: | Size: 267 KiB |
|
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 255 KiB |
|
Before Width: | Height: | Size: 272 KiB After Width: | Height: | Size: 272 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
@ -13,11 +13,11 @@ export type ServerError = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const axios = _axios.create({
|
const axios = _axios.create({
|
||||||
baseURL: `${process.env.NEXT_PUBLIC_SERVER_URL}/api`,
|
baseURL: `${process.env.serverUrl}/api`,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const uninterceptedAxios = _axios.create({
|
export const uninterceptedAxios = _axios.create({
|
||||||
baseURL: `${process.env.NEXT_PUBLIC_SERVER_URL}/api`,
|
baseURL: `${process.env.serverUrl}/api`,
|
||||||
});
|
});
|
||||||
|
|
||||||
axios.interceptors.request.use((config) => {
|
axios.interceptors.request.use((config) => {
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { Theme } from '@reactive-resume/schema';
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import Image from 'next/image';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import Markdown from '@/components/shared/Markdown';
|
import Markdown from '@/components/shared/Markdown';
|
||||||
@ -25,10 +24,9 @@ export const MastheadSidebar: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="col-span-2 grid justify-items-start gap-3 px-4 pt-4">
|
<div className="col-span-2 grid justify-items-start gap-3 px-4 pt-4">
|
||||||
{photo.visible && !isEmpty(photo.url) && (
|
{photo.visible && !isEmpty(photo.url) && (
|
||||||
<Image
|
<img
|
||||||
alt={name}
|
alt={name}
|
||||||
src={photo.url}
|
src={photo.url}
|
||||||
objectFit="cover"
|
|
||||||
width={photo.filters.size}
|
width={photo.filters.size}
|
||||||
height={photo.filters.size}
|
height={photo.filters.size}
|
||||||
className={getPhotoClassNames(photo.filters)}
|
className={getPhotoClassNames(photo.filters)}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { Theme } from '@reactive-resume/schema';
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import Image from 'next/image';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import Markdown from '@/components/shared/Markdown';
|
import Markdown from '@/components/shared/Markdown';
|
||||||
@ -26,10 +25,9 @@ export const MastheadSidebar: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="col-span-2 grid justify-items-start gap-3 p-4">
|
<div className="col-span-2 grid justify-items-start gap-3 p-4">
|
||||||
{photo.visible && !isEmpty(photo.url) && (
|
{photo.visible && !isEmpty(photo.url) && (
|
||||||
<Image
|
<img
|
||||||
alt={name}
|
alt={name}
|
||||||
src={photo.url}
|
src={photo.url}
|
||||||
objectFit="cover"
|
|
||||||
width={photo.filters.size}
|
width={photo.filters.size}
|
||||||
height={photo.filters.size}
|
height={photo.filters.size}
|
||||||
className={getPhotoClassNames(photo.filters)}
|
className={getPhotoClassNames(photo.filters)}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { Email, Phone, Public, Room } from '@mui/icons-material';
|
import { Email, Phone, Public, Room } from '@mui/icons-material';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import Image from 'next/image';
|
|
||||||
|
|
||||||
import Markdown from '@/components/shared/Markdown';
|
import Markdown from '@/components/shared/Markdown';
|
||||||
import { useAppSelector } from '@/store/hooks';
|
import { useAppSelector } from '@/store/hooks';
|
||||||
@ -18,19 +17,18 @@ export const MastheadSidebar: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="col-span-2 grid justify-items-center gap-4">
|
<div className="col-span-2 grid justify-items-center gap-4">
|
||||||
{photo.visible && !isEmpty(photo.url) && (
|
{photo.visible && !isEmpty(photo.url) && (
|
||||||
<Image
|
<img
|
||||||
alt={name}
|
alt={name}
|
||||||
src={photo.url}
|
src={photo.url}
|
||||||
objectFit="cover"
|
|
||||||
width={photo.filters.size}
|
width={photo.filters.size}
|
||||||
height={photo.filters.size}
|
height={photo.filters.size}
|
||||||
className={getPhotoClassNames(photo.filters)}
|
className={getPhotoClassNames(photo.filters)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div className="text-center">
|
||||||
<h1>{name}</h1>
|
<h1>{name}</h1>
|
||||||
<p className="opacity-75">{headline}</p>
|
<p className="mt-1 opacity-75">{headline}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2 rounded border-2 p-4" style={{ borderColor: primaryColor }}>
|
<div className="flex flex-col gap-2 rounded border-2 p-4" style={{ borderColor: primaryColor }}>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { Email, Phone, Public, Room } from '@mui/icons-material';
|
import { Email, Phone, Public, Room } from '@mui/icons-material';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import Image from 'next/image';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useAppSelector } from '@/store/hooks';
|
import { useAppSelector } from '@/store/hooks';
|
||||||
@ -14,22 +13,25 @@ const Masthead = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-4 border-b pb-4 text-center">
|
<div className="grid gap-3 justify-center mb-4 border-b pb-4 text-center">
|
||||||
|
<div className="mx-auto">
|
||||||
{photo.visible && !isEmpty(photo.url) && (
|
{photo.visible && !isEmpty(photo.url) && (
|
||||||
<Image
|
<img
|
||||||
alt={name}
|
alt={name}
|
||||||
src={photo.url}
|
src={photo.url}
|
||||||
width={photo.filters.size}
|
width={photo.filters.size}
|
||||||
height={photo.filters.size}
|
height={photo.filters.size}
|
||||||
objectFit="cover"
|
|
||||||
className={getPhotoClassNames(photo.filters)}
|
className={getPhotoClassNames(photo.filters)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<h1 className="mt-2 mb-1">{name}</h1>
|
<div>
|
||||||
|
<h1 className="mb-1">{name}</h1>
|
||||||
<p className="opacity-75">{headline}</p>
|
<p className="opacity-75">{headline}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 flex flex-wrap justify-center gap-3">
|
<div className="flex flex-wrap justify-center gap-3">
|
||||||
<DataDisplay icon={<Email />} link={`mailto:${email}`}>
|
<DataDisplay icon={<Email />} link={`mailto:${email}`}>
|
||||||
{email}
|
{email}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { Email, Phone, Public, Room } from '@mui/icons-material';
|
import { Email, Phone, Public, Room } from '@mui/icons-material';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import Image from 'next/image';
|
|
||||||
|
|
||||||
import { useAppSelector } from '@/store/hooks';
|
import { useAppSelector } from '@/store/hooks';
|
||||||
import DataDisplay from '@/templates/shared/DataDisplay';
|
import DataDisplay from '@/templates/shared/DataDisplay';
|
||||||
@ -15,12 +14,11 @@ const Masthead: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{photo.visible && !isEmpty(photo.url) && (
|
{photo.visible && !isEmpty(photo.url) && (
|
||||||
<Image
|
<img
|
||||||
alt={name}
|
alt={name}
|
||||||
src={photo.url}
|
src={photo.url}
|
||||||
width={photo.filters.size}
|
width={photo.filters.size}
|
||||||
height={photo.filters.size}
|
height={photo.filters.size}
|
||||||
objectFit="cover"
|
|
||||||
className={getPhotoClassNames(photo.filters)}
|
className={getPhotoClassNames(photo.filters)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { Email, Phone, Public, Room } from '@mui/icons-material';
|
|||||||
import { Theme } from '@reactive-resume/schema';
|
import { Theme } from '@reactive-resume/schema';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import Image from 'next/image';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import Markdown from '@/components/shared/Markdown';
|
import Markdown from '@/components/shared/Markdown';
|
||||||
@ -19,13 +18,7 @@ export const MastheadSidebar: React.FC = () => {
|
|||||||
<div className="col-span-2 grid justify-items-center gap-4">
|
<div className="col-span-2 grid justify-items-center gap-4">
|
||||||
{photo.visible && !isEmpty(photo.url) && (
|
{photo.visible && !isEmpty(photo.url) && (
|
||||||
<div className="relative aspect-square h-full w-full">
|
<div className="relative aspect-square h-full w-full">
|
||||||
<Image
|
<img alt={name} src={photo.url} className={getPhotoClassNames(photo.filters)} />
|
||||||
alt={name}
|
|
||||||
layout="fill"
|
|
||||||
src={photo.url}
|
|
||||||
objectFit="cover"
|
|
||||||
className={getPhotoClassNames(photo.filters)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -18,37 +18,37 @@ const templateMap: Record<string, TemplateMeta> = {
|
|||||||
kakuna: {
|
kakuna: {
|
||||||
id: 'kakuna',
|
id: 'kakuna',
|
||||||
name: 'Kakuna',
|
name: 'Kakuna',
|
||||||
preview: require('./Kakuna/preview.jpg'),
|
preview: '/images/templates/kakuna.jpg',
|
||||||
component: Kakuna,
|
component: Kakuna,
|
||||||
},
|
},
|
||||||
onyx: {
|
onyx: {
|
||||||
id: 'onyx',
|
id: 'onyx',
|
||||||
name: 'Onyx',
|
name: 'Onyx',
|
||||||
preview: require('./Onyx/preview.jpg'),
|
preview: '/images/templates/onyx.jpg',
|
||||||
component: Onyx,
|
component: Onyx,
|
||||||
},
|
},
|
||||||
pikachu: {
|
pikachu: {
|
||||||
id: 'pikachu',
|
id: 'pikachu',
|
||||||
name: 'Pikachu',
|
name: 'Pikachu',
|
||||||
preview: require('./Pikachu/preview.jpg'),
|
preview: '/images/templates/pikachu.jpg',
|
||||||
component: Pikachu,
|
component: Pikachu,
|
||||||
},
|
},
|
||||||
gengar: {
|
gengar: {
|
||||||
id: 'gengar',
|
id: 'gengar',
|
||||||
name: 'Gengar',
|
name: 'Gengar',
|
||||||
preview: require('./Gengar/preview.jpg'),
|
preview: '/images/templates/gengar.jpg',
|
||||||
component: Gengar,
|
component: Gengar,
|
||||||
},
|
},
|
||||||
castform: {
|
castform: {
|
||||||
id: 'castform',
|
id: 'castform',
|
||||||
name: 'Castform',
|
name: 'Castform',
|
||||||
preview: require('./Castform/preview.jpg'),
|
preview: '/images/templates/castform.jpg',
|
||||||
component: Castform,
|
component: Castform,
|
||||||
},
|
},
|
||||||
glalie: {
|
glalie: {
|
||||||
id: 'glalie',
|
id: 'glalie',
|
||||||
name: 'Glalie',
|
name: 'Glalie',
|
||||||
preview: require('./Glalie/preview.jpg'),
|
preview: '/images/templates/glalie.jpg',
|
||||||
component: Glalie,
|
component: Glalie,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
5
apps/client/types/environment.d.ts
vendored
@ -2,12 +2,7 @@ declare global {
|
|||||||
namespace NodeJS {
|
namespace NodeJS {
|
||||||
interface ProcessEnv {
|
interface ProcessEnv {
|
||||||
TZ: string;
|
TZ: string;
|
||||||
ANALYZE?: boolean;
|
|
||||||
NODE_ENV: 'development' | 'production';
|
NODE_ENV: 'development' | 'production';
|
||||||
|
|
||||||
// Public Environment Variables
|
|
||||||
NEXT_PUBLIC_APP_VERSION?: string;
|
|
||||||
NEXT_PUBLIC_GOOGLE_CLIENT_ID?: string;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,7 @@ export const parseListItemPath = (item: ListItem, path: string | string[], separ
|
|||||||
|
|
||||||
export const getPhotoClassNames = (filters: PhotoFilters) =>
|
export const getPhotoClassNames = (filters: PhotoFilters) =>
|
||||||
clsx({
|
clsx({
|
||||||
|
'object-cover': true,
|
||||||
grayscale: filters.grayscale,
|
grayscale: filters.grayscale,
|
||||||
'!border-[4px] !border-solid': filters.border,
|
'!border-[4px] !border-solid': filters.border,
|
||||||
'rounded-lg': filters.shape === 'rounded-square',
|
'rounded-lg': filters.shape === 'rounded-square',
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
SERVER_PORT=3100
|
|
||||||
0
apps/server/src/assets/resumes/.gitkeep
Normal file
0
apps/server/src/assets/templates/.gitkeep
Normal file
0
apps/server/src/assets/uploads/.gitkeep
Normal file
@ -19,9 +19,6 @@ import { User } from '@/users/entities/user.entity';
|
|||||||
database: configService.get<string>('postgres.database'),
|
database: configService.get<string>('postgres.database'),
|
||||||
synchronize: true,
|
synchronize: true,
|
||||||
entities: [User, Resume],
|
entities: [User, Resume],
|
||||||
ssl: {
|
|
||||||
ca: configService.get<string>('postgres.certificate'),
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -27,10 +27,12 @@ const bootstrap = async () => {
|
|||||||
app.setViewEngine('hbs');
|
app.setViewEngine('hbs');
|
||||||
|
|
||||||
const configService = app.get(ConfigService);
|
const configService = app.get(ConfigService);
|
||||||
|
const serverUrl = configService.get<number>('app.serverUrl');
|
||||||
const port = configService.get<number>('app.port');
|
const port = configService.get<number>('app.port');
|
||||||
|
|
||||||
await app.listen(port);
|
await app.listen(port);
|
||||||
Logger.log(`🚀 Server is running on: http://localhost:${port}/${globalPrefix}`);
|
|
||||||
|
Logger.log(`🚀 Server is running on: ${serverUrl}/${globalPrefix}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const sampleData: Partial<Resume> = {
|
|||||||
email: 'alexis.jones@gmail.com',
|
email: 'alexis.jones@gmail.com',
|
||||||
phone: '+1 800 1200 3820',
|
phone: '+1 800 1200 3820',
|
||||||
photo: {
|
photo: {
|
||||||
url: `${process.env.APP_URL}/images/sample-photo.jpg`,
|
url: `/images/sample-photo.jpg`,
|
||||||
filters: {
|
filters: {
|
||||||
size: 128,
|
size: 128,
|
||||||
shape: 'rounded-square',
|
shape: 'rounded-square',
|
||||||
|
|||||||
@ -31,10 +31,9 @@ export class ResumeService {
|
|||||||
async create(createResumeDto: CreateResumeDto, userId: number) {
|
async create(createResumeDto: CreateResumeDto, userId: number) {
|
||||||
try {
|
try {
|
||||||
const user = await this.usersService.findById(userId);
|
const user = await this.usersService.findById(userId);
|
||||||
const serverUrl = this.configService.get<string>('app.serverUrl');
|
|
||||||
|
|
||||||
const shortId = nanoid(SHORT_ID_LENGTH);
|
const shortId = nanoid(SHORT_ID_LENGTH);
|
||||||
const image = `${serverUrl}/covers/${sample(covers)}`;
|
const image = `/images/covers/${sample(covers)}`;
|
||||||
|
|
||||||
const resume = this.resumeRepository.create({
|
const resume = this.resumeRepository.create({
|
||||||
...defaultState,
|
...defaultState,
|
||||||
@ -67,10 +66,9 @@ export class ResumeService {
|
|||||||
async import(importResumeDto: Partial<ResumeSchema>, userId: number) {
|
async import(importResumeDto: Partial<ResumeSchema>, userId: number) {
|
||||||
try {
|
try {
|
||||||
const user = await this.usersService.findById(userId);
|
const user = await this.usersService.findById(userId);
|
||||||
const serverUrl = this.configService.get<string>('app.serverUrl');
|
|
||||||
|
|
||||||
const shortId = nanoid(SHORT_ID_LENGTH);
|
const shortId = nanoid(SHORT_ID_LENGTH);
|
||||||
const image = `${serverUrl}/covers/${sample(covers)}`;
|
const image = `/images/covers/${sample(covers)}`;
|
||||||
|
|
||||||
const resume = this.resumeRepository.create({
|
const resume = this.resumeRepository.create({
|
||||||
...defaultState,
|
...defaultState,
|
||||||
@ -168,10 +166,9 @@ export class ResumeService {
|
|||||||
async duplicate(id: number, userId: number) {
|
async duplicate(id: number, userId: number) {
|
||||||
try {
|
try {
|
||||||
const originalResume = await this.findOne(id, userId);
|
const originalResume = await this.findOne(id, userId);
|
||||||
const serverUrl = this.configService.get<string>('app.serverUrl');
|
|
||||||
|
|
||||||
const shortId = nanoid(SHORT_ID_LENGTH);
|
const shortId = nanoid(SHORT_ID_LENGTH);
|
||||||
const image = `${serverUrl}/covers/${sample(covers)}`;
|
const image = `/images/covers/${sample(covers)}`;
|
||||||
|
|
||||||
const duplicatedResume: Partial<Resume> = {
|
const duplicatedResume: Partial<Resume> = {
|
||||||
...pick(originalResume, ['name', 'slug', 'basics', 'metadata', 'sections', 'public']),
|
...pick(originalResume, ['name', 'slug', 'basics', 'metadata', 'sections', 'public']),
|
||||||
|
|||||||
@ -26,18 +26,18 @@ services:
|
|||||||
# POSTGRES_HOST: postgres
|
# POSTGRES_HOST: postgres
|
||||||
|
|
||||||
# Production
|
# Production
|
||||||
app:
|
# app:
|
||||||
image: amruthpillai/reactive-resume
|
# image: amruthpillai/reactive-resume
|
||||||
container_name: app
|
# container_name: app
|
||||||
depends_on:
|
# depends_on:
|
||||||
- postgres
|
# - postgres
|
||||||
ports:
|
# ports:
|
||||||
- '3000:3000'
|
# - '3000:3000'
|
||||||
- '3100:3100'
|
# - '3100:3100'
|
||||||
env_file: .env
|
# env_file: .env
|
||||||
environment:
|
# environment:
|
||||||
NODE_ENV: production
|
# NODE_ENV: production
|
||||||
POSTGRES_HOST: postgres
|
# POSTGRES_HOST: postgres
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
|
|||||||
@ -63,7 +63,6 @@
|
|||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"googleapis": "^95.0.0",
|
"googleapis": "^95.0.0",
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"i18next-http-backend": "^1.3.2",
|
|
||||||
"joi": "^17.6.0",
|
"joi": "^17.6.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"md5-hex": "^4.0.0",
|
"md5-hex": "^4.0.0",
|
||||||
@ -103,7 +102,7 @@
|
|||||||
"sharp": "^0.30.2",
|
"sharp": "^0.30.2",
|
||||||
"tailwindcss": "^3.0.23",
|
"tailwindcss": "^3.0.23",
|
||||||
"tslib": "^2.3.1",
|
"tslib": "^2.3.1",
|
||||||
"typeorm": "^0.2.44",
|
"typeorm": "^0.2.45",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"webfontloader": "^1.6.28"
|
"webfontloader": "^1.6.28"
|
||||||
},
|
},
|
||||||
|
|||||||
32
pnpm-lock.yaml
generated
@ -90,7 +90,6 @@ specifiers:
|
|||||||
googleapis: ^95.0.0
|
googleapis: ^95.0.0
|
||||||
handlebars: ^4.7.7
|
handlebars: ^4.7.7
|
||||||
husky: ^7.0.4
|
husky: ^7.0.4
|
||||||
i18next-http-backend: ^1.3.2
|
|
||||||
jest: 27.5.1
|
jest: 27.5.1
|
||||||
joi: ^17.6.0
|
joi: ^17.6.0
|
||||||
lodash: ^4.17.21
|
lodash: ^4.17.21
|
||||||
@ -140,7 +139,7 @@ specifiers:
|
|||||||
ts-jest: ^27.1.3
|
ts-jest: ^27.1.3
|
||||||
ts-node: ^10.6.0
|
ts-node: ^10.6.0
|
||||||
tslib: ^2.3.1
|
tslib: ^2.3.1
|
||||||
typeorm: ^0.2.44
|
typeorm: ^0.2.45
|
||||||
typescript: <4.6.0
|
typescript: <4.6.0
|
||||||
uuid: ^8.3.2
|
uuid: ^8.3.2
|
||||||
webfontloader: ^1.6.28
|
webfontloader: ^1.6.28
|
||||||
@ -167,7 +166,7 @@ dependencies:
|
|||||||
'@nestjs/schedule': 1.0.2_1ce925e2290a1cea9e3700e8a60baeb5
|
'@nestjs/schedule': 1.0.2_1ce925e2290a1cea9e3700e8a60baeb5
|
||||||
'@nestjs/schematics': 8.0.7_typescript@4.5.5
|
'@nestjs/schematics': 8.0.7_typescript@4.5.5
|
||||||
'@nestjs/serve-static': 2.2.2_31e7036b193d6d3c9cadab18cbb4af84
|
'@nestjs/serve-static': 2.2.2_31e7036b193d6d3c9cadab18cbb4af84
|
||||||
'@nestjs/typeorm': 8.0.3_3917dd1d6231e97e428aeb9078dfff90
|
'@nestjs/typeorm': 8.0.3_a8e966c473b8cac7d0f44522ae3cdd56
|
||||||
'@nrwl/next': 13.8.4_bb72e592f08d4a41355e886e2062b95a
|
'@nrwl/next': 13.8.4_bb72e592f08d4a41355e886e2062b95a
|
||||||
'@nrwl/tao': 13.8.4
|
'@nrwl/tao': 13.8.4
|
||||||
'@nrwl/workspace': 13.8.4_b055d0f33702adf7c462f4bf04e60212
|
'@nrwl/workspace': 13.8.4_b055d0f33702adf7c462f4bf04e60212
|
||||||
@ -185,7 +184,6 @@ dependencies:
|
|||||||
downloadjs: 1.4.7
|
downloadjs: 1.4.7
|
||||||
googleapis: 95.0.0
|
googleapis: 95.0.0
|
||||||
handlebars: 4.7.7
|
handlebars: 4.7.7
|
||||||
i18next-http-backend: 1.3.2
|
|
||||||
joi: 17.6.0
|
joi: 17.6.0
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
md5-hex: 4.0.0
|
md5-hex: 4.0.0
|
||||||
@ -225,7 +223,7 @@ dependencies:
|
|||||||
sharp: 0.30.2
|
sharp: 0.30.2
|
||||||
tailwindcss: 3.0.23_4b9e11f8e85900587b5e2272c5d4c20c
|
tailwindcss: 3.0.23_4b9e11f8e85900587b5e2272c5d4c20c
|
||||||
tslib: 2.3.1
|
tslib: 2.3.1
|
||||||
typeorm: 0.2.44_pg@8.7.3
|
typeorm: 0.2.45_pg@8.7.3
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
webfontloader: 1.6.28
|
webfontloader: 1.6.28
|
||||||
|
|
||||||
@ -2765,7 +2763,7 @@ packages:
|
|||||||
tslib: 2.3.1
|
tslib: 2.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@nestjs/typeorm/8.0.3_3917dd1d6231e97e428aeb9078dfff90:
|
/@nestjs/typeorm/8.0.3_a8e966c473b8cac7d0f44522ae3cdd56:
|
||||||
resolution: {integrity: sha512-tf9rTXP6LeFInkwd+tktQhtLRsKp4RRYImprqT8gcHcJDx+xMP1IygnXELOKwF5vo2/mnhrGtBlRQ/iiS6170g==}
|
resolution: {integrity: sha512-tf9rTXP6LeFInkwd+tktQhtLRsKp4RRYImprqT8gcHcJDx+xMP1IygnXELOKwF5vo2/mnhrGtBlRQ/iiS6170g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@nestjs/common': ^8.0.0
|
'@nestjs/common': ^8.0.0
|
||||||
@ -2778,7 +2776,7 @@ packages:
|
|||||||
'@nestjs/core': 8.4.0_2e10c1a16efb48b2968dca34c1adcfc4
|
'@nestjs/core': 8.4.0_2e10c1a16efb48b2968dca34c1adcfc4
|
||||||
reflect-metadata: 0.1.13
|
reflect-metadata: 0.1.13
|
||||||
rxjs: 7.5.4
|
rxjs: 7.5.4
|
||||||
typeorm: 0.2.44_pg@8.7.3
|
typeorm: 0.2.45_pg@8.7.3
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -6362,14 +6360,6 @@ packages:
|
|||||||
moment-timezone: 0.5.34
|
moment-timezone: 0.5.34
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/cross-fetch/3.1.5:
|
|
||||||
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
|
|
||||||
dependencies:
|
|
||||||
node-fetch: 2.6.7
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cross-spawn/5.1.0:
|
/cross-spawn/5.1.0:
|
||||||
resolution: {integrity: sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=}
|
resolution: {integrity: sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -8351,14 +8341,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-/MfAGMP0jHonV966uFf9PkWWuDjPYLIcsipnSO3NxpNtAgRUKLTwvm85fEmsF6hGeu0zbZiCQ3W74jwO6K9uXA==}
|
resolution: {integrity: sha512-/MfAGMP0jHonV966uFf9PkWWuDjPYLIcsipnSO3NxpNtAgRUKLTwvm85fEmsF6hGeu0zbZiCQ3W74jwO6K9uXA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/i18next-http-backend/1.3.2:
|
|
||||||
resolution: {integrity: sha512-SfcoUmsSWnc2LYsDsCq5TCg18cxJXvXymX9N37V+qqMKQY8Gf0rWkjOnRd20sMK633Dq4NF9tvqPbOiFJ49Kbw==}
|
|
||||||
dependencies:
|
|
||||||
cross-fetch: 3.1.5
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/i18next/21.6.13:
|
/i18next/21.6.13:
|
||||||
resolution: {integrity: sha512-MVjNttw+5mIuu2/fwTpSU0EeI7iU/6pnDvGQboCzkILiv0/gD+FLZaF7qSHmUHO4ZkE6xJQ9SlBgGvMHxhC82Q==}
|
resolution: {integrity: sha512-MVjNttw+5mIuu2/fwTpSU0EeI7iU/6pnDvGQboCzkILiv0/gD+FLZaF7qSHmUHO4ZkE6xJQ9SlBgGvMHxhC82Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -13752,8 +13734,8 @@ packages:
|
|||||||
resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=}
|
resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/typeorm/0.2.44_pg@8.7.3:
|
/typeorm/0.2.45_pg@8.7.3:
|
||||||
resolution: {integrity: sha512-yFyb9Ts73vGaS/O06TvLpzvT5U/ngO31GeciNc0eoH7P1QcG8kVZdOy9FHJqkTeDmIljMRgWjbYUoMw53ZY7Xw==}
|
resolution: {integrity: sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@sap/hana-client': ^2.11.14
|
'@sap/hana-client': ^2.11.14
|
||||||
|
|||||||