diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7490745a..6070e40e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -ARG VARIANT="16-bullseye" +ARG VARIANT="lts-bullseye" FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT} diff --git a/CHANGELOG.md b/CHANGELOG.md index 17510fa9..ed4c87a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ 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.4.0](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.3.3...v3.4.0) (2022-04-30) + ### [3.3.4](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.3.3...v3.3.4) (2022-04-09) ### [3.3.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.3.2...v3.3.3) (2022-04-09) diff --git a/README.md b/README.md index 99cd3274..1d1552a5 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,20 @@ You have complete control over what goes into your resume, how it looks, what co ## Table of Contents -- [Features](#features) -- [Languages](#languages) -- [Tutorial](#tutorial) -- [Build from Source](#build-from-source) -- [Contributing](#contributing) -- [Report Bugs and Feature Requests](#report-bugs-and-feature-requests) -- [Donations](#donations) -- [Infrastructure](#infrastructure) -- [Contributors Wall](#contributors-wall) -- [License](#license) +- [Reactive Resume](#reactive-resume) + - [Go to App | [Docs](https://docs.rxresu.me)](#go-to-app--docs) + - [Table of Contents](#table-of-contents) + - [Features](#features) + - [Languages](#languages) + - [Tutorial](#tutorial) + - [Build from Source](#build-from-source) + - [Contributing](#contributing) + - [Report Bugs and Feature Requests](#report-bugs-and-feature-requests) + - [Donations](#donations) + - [💸 PayPal](#-paypal) + - [Infrastructure](#infrastructure) + - [Contributors Wall](#contributors-wall) + - [License](#license) ## Features @@ -53,19 +57,23 @@ You have complete control over what goes into your resume, how it looks, what co - Arabic (اَلْعَرَبِيَّةُ) - Bengali (বাংলা) - Chinese (中文) +- Czech (čeština) - Danish (Dansk) - Dutch (Nederlands) - English - French (Français) - German (Deutsch) +- Greek (Ελληνικά) - Hindi (हिन्दी) - Italian (Italiano) - Kannada (ಕನ್ನಡ) - Malayalam (മലയാളം) +- Odia (ଓଡ଼ିଆ) - Polish (Polski) - Portuguese (Português) - Russian (русский) - Spanish (Español) +- Swedish (Svenska) - Tamil (தமிழ்) - Turkish (Türkçe) - Vietnamese (Tiếng Việt) diff --git a/client/Dockerfile b/client/Dockerfile index b4f89bc1..64be8a0f 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,4 +1,4 @@ -FROM node:17-alpine as dependencies +FROM node:lts-alpine as dependencies RUN apk add --no-cache curl g++ make python3 \ && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm @@ -11,7 +11,7 @@ COPY ./client/package.json ./client/package.json RUN pnpm install --frozen-lockfile -FROM node:17-alpine as builder +FROM node:lts-alpine as builder RUN apk add --no-cache curl g++ make python3 \ && curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm @@ -27,7 +27,7 @@ COPY --from=dependencies /app/client/node_modules ./client/node_modules RUN pnpm run build:schema RUN pnpm run build:client -FROM node:17-alpine as production +FROM node:lts-alpine as production WORKDIR /app diff --git a/client/components/build/LeftSidebar/LeftSidebar.tsx b/client/components/build/LeftSidebar/LeftSidebar.tsx index 17101122..d0cbae66 100644 --- a/client/components/build/LeftSidebar/LeftSidebar.tsx +++ b/client/components/build/LeftSidebar/LeftSidebar.tsx @@ -81,14 +81,14 @@ const LeftSidebar = () => { arrow key={id} placement="right" - title={get(sections, `${id}.name`, t(`builder.leftSidebar.sections.${id}.heading`))} + title={get(sections, `${id}.name`, t(`builder.leftSidebar.sections.${id}.heading`)) as string} > handleClick(id)}>{icon} ))} {customSections.map(({ id }) => ( - + handleClick(id)}> diff --git a/client/components/build/LeftSidebar/sections/Basics.tsx b/client/components/build/LeftSidebar/sections/Basics.tsx index 845d9358..23c3be14 100644 --- a/client/components/build/LeftSidebar/sections/Basics.tsx +++ b/client/components/build/LeftSidebar/sections/Basics.tsx @@ -57,6 +57,12 @@ const Basics = () => { + ('builder.leftSidebar.sections.basics.birthdate.label')} + path="basics.birthdate" + className="sm:col-span-2" + /> ('builder.common.form.email.label')} path="basics.email" diff --git a/client/components/build/RightSidebar/sections/Layout.tsx b/client/components/build/RightSidebar/sections/Layout.tsx index 3d69a6d0..ed5d225c 100644 --- a/client/components/build/RightSidebar/sections/Layout.tsx +++ b/client/components/build/RightSidebar/sections/Layout.tsx @@ -117,7 +117,7 @@ const Layout = () => { [styles.disabled]: !get(resumeSections, `${sectionId}.visible`, true), })} > - {get(resumeSections, `${sectionId}.name`, '')} + {get(resumeSections, `${sectionId}.name`, '') as string} )} diff --git a/client/components/shared/BaseModal.tsx b/client/components/shared/BaseModal.tsx index fc0be666..c93c0dd6 100644 --- a/client/components/shared/BaseModal.tsx +++ b/client/components/shared/BaseModal.tsx @@ -5,11 +5,12 @@ import { useRouter } from 'next/router'; import styles from './BaseModal.module.scss'; type Props = { - icon?: React.ReactNode; isOpen: boolean; heading: string; - handleClose: () => void; + icon?: React.ReactNode; + children?: React.ReactNode; footerChildren?: React.ReactNode; + handleClose: () => void; }; const BaseModal: React.FC = ({ icon, isOpen, heading, children, handleClose, footerChildren }) => { diff --git a/client/components/shared/List.tsx b/client/components/shared/List.tsx index 19b2cc5f..14b174ee 100644 --- a/client/components/shared/List.tsx +++ b/client/components/shared/List.tsx @@ -76,6 +76,7 @@ const List: React.FC = ({ return ( void; }; -const ListItem: React.FC = ({ item, index, title, subtitle, onMove, onEdit, onDelete, onDuplicate }) => { +const ListItem: React.FC = ({ item, path, index, title, subtitle, onMove, onEdit, onDelete, onDuplicate }) => { const { t } = useTranslation(); const ref = useRef(null); const [anchorEl, setAnchorEl] = useState(null); const [{ handlerId }, drop] = useDrop({ - accept: 'ListItem', + accept: path, collect(monitor) { return { handlerId: monitor.getHandlerId() }; }, @@ -68,7 +69,7 @@ const ListItem: React.FC = ({ item, index, title, subtitle, onMove, onEdi }); const [{ isDragging }, drag] = useDrag({ - type: 'ListItem', + type: path, item: () => { return { id: item.id, index }; }, diff --git a/client/components/shared/NoSSR.tsx b/client/components/shared/NoSSR.tsx deleted file mode 100644 index 2b7f3534..00000000 --- a/client/components/shared/NoSSR.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import dynamic from 'next/dynamic'; - -const NoSSR: React.FC = ({ children }) => <>{children}; - -export default dynamic(() => Promise.resolve(NoSSR), { ssr: false }); diff --git a/client/components/shared/ResumeInput.tsx b/client/components/shared/ResumeInput.tsx index 270a582a..29af054f 100644 --- a/client/components/shared/ResumeInput.tsx +++ b/client/components/shared/ResumeInput.tsx @@ -1,4 +1,7 @@ +import { DatePicker } from '@mui/lab'; import { TextField } from '@mui/material'; +import dayjs from 'dayjs'; +import { isEmpty } from 'lodash'; import get from 'lodash/get'; import { useEffect, useState } from 'react'; @@ -8,7 +11,7 @@ import { setResumeState } from '@/store/resume/resumeSlice'; import MarkdownSupported from './MarkdownSupported'; interface Props { - type?: 'text' | 'textarea'; + type?: 'text' | 'textarea' | 'date'; label: string; path: string; className?: string; @@ -31,6 +34,11 @@ const ResumeInput: React.FC = ({ type = 'text', label, path, className, m dispatch(setResumeState({ path, value: event.target.value })); }; + const onChangeValue = (value: string) => { + setValue(value); + dispatch(setResumeState({ path, value })); + }; + if (type === 'textarea') { return ( = ({ type = 'text', label, path, className, m ); } + if (type === 'date') { + return ( + } + onChange={(date: Date | null, keyboardInputValue: string | undefined) => { + isEmpty(keyboardInputValue) && onChangeValue(''); + date && dayjs(date).isValid() && onChangeValue(date.toISOString()); + }} + /> + ); + } + return ; }; diff --git a/client/config/languages.ts b/client/config/languages.ts index be8781b9..a7a72bd0 100644 --- a/client/config/languages.ts +++ b/client/config/languages.ts @@ -7,8 +7,10 @@ export type Language = { export const languages: Language[] = [ { code: 'ar', name: 'Arabic', localName: 'اَلْعَرَبِيَّةُ' }, { code: 'bn', name: 'Bengali', localName: 'বাংলা' }, + { code: 'cs', name: 'Czech', localName: 'čeština' }, { code: 'da', name: 'Danish', localName: 'Dansk' }, { code: 'de', name: 'German', localName: 'Deutsch' }, + { code: 'el', name: 'Greek', localName: 'Ελληνικά' }, { code: 'en', name: 'English' }, { code: 'es', name: 'Spanish', localName: 'Español' }, { code: 'fr', name: 'French', localName: 'Français' }, @@ -17,9 +19,11 @@ export const languages: Language[] = [ { code: 'kn', name: 'Kannada', localName: 'ಕನ್ನಡ' }, { code: 'ml', name: 'Malayalam', localName: 'മലയാളം' }, { code: 'nl', name: 'Dutch', localName: 'Nederlands' }, + { code: 'or', name: 'Odia', localName: 'ଓଡ଼ିଆ' }, { code: 'pl', name: 'Polish', localName: 'Polski' }, { code: 'pt', name: 'Portuguese', localName: 'Português' }, { code: 'ru', name: 'Russian', localName: 'русский' }, + { code: 'sv', name: 'Swedish', localName: 'Svenska' }, { code: 'ta', name: 'Tamil', localName: 'தமிழ்' }, { code: 'tr', name: 'Turkish', localName: 'Türkçe' }, { code: 'vi', name: 'Vietnamese', localName: 'Tiếng Việt' }, diff --git a/client/modals/auth/LoginModal.tsx b/client/modals/auth/LoginModal.tsx index c3a21fe9..8bd70a5d 100644 --- a/client/modals/auth/LoginModal.tsx +++ b/client/modals/auth/LoginModal.tsx @@ -6,7 +6,6 @@ import Joi from 'joi'; import { isEmpty } from 'lodash'; import { Trans, useTranslation } from 'next-i18next'; import { useMemo, useState } from 'react'; -import { GoogleLoginResponse, GoogleLoginResponseOffline, useGoogleLogin } from 'react-google-login'; import { Controller, useForm } from 'react-hook-form'; import toast from 'react-hot-toast'; import { useIsMutating, useMutation } from 'react-query'; @@ -18,6 +17,8 @@ import { ServerError } from '@/services/axios'; import { useAppDispatch, useAppSelector } from '@/store/hooks'; import { setModalState } from '@/store/modal/modalSlice'; +declare const google: any; + type FormData = { identifier: string; password: string; @@ -56,15 +57,6 @@ const LoginModal: React.FC = () => { loginWithGoogle ); - const { signIn } = useGoogleLogin({ - clientId: env('GOOGLE_CLIENT_ID'), - onSuccess: async (response: GoogleLoginResponse | GoogleLoginResponseOffline) => { - await loginWithGoogleMutation({ accessToken: (response as GoogleLoginResponse).accessToken }); - - handleClose(); - }, - }); - const handleClose = () => { dispatch(setModalState({ modal: 'auth.login', state: { open: false } })); reset(); @@ -93,8 +85,18 @@ const LoginModal: React.FC = () => { dispatch(setModalState({ modal: 'auth.forgot', state: { open: true } })); }; - const handleLoginWithGoogle = () => { - signIn(); + const handleLoginWithGoogle = async () => { + google.accounts.id.initialize({ + client_id: env('GOOGLE_CLIENT_ID'), + callback: async (response: any) => { + await loginWithGoogleMutation({ credential: response.credential }); + + handleClose(); + }, + auto_select: false, + }); + + google.accounts.id.prompt(); }; const PasswordVisibility = (): React.ReactElement => { diff --git a/client/modals/auth/RegisterModal.tsx b/client/modals/auth/RegisterModal.tsx index f16da881..d65236f4 100644 --- a/client/modals/auth/RegisterModal.tsx +++ b/client/modals/auth/RegisterModal.tsx @@ -5,7 +5,6 @@ import { Button, TextField } from '@mui/material'; import Joi from 'joi'; import { isEmpty } from 'lodash'; import { Trans, useTranslation } from 'next-i18next'; -import { GoogleLoginResponse, GoogleLoginResponseOffline, useGoogleLogin } from 'react-google-login'; import { Controller, useForm } from 'react-hook-form'; import { useMutation } from 'react-query'; @@ -15,6 +14,8 @@ import { ServerError } from '@/services/axios'; import { useAppDispatch, useAppSelector } from '@/store/hooks'; import { setModalState } from '@/store/modal/modalSlice'; +declare const google: any; + type FormData = { name: string; username: string; @@ -63,15 +64,6 @@ const RegisterModal: React.FC = () => { loginWithGoogle ); - const { signIn } = useGoogleLogin({ - clientId: env('GOOGLE_CLIENT_ID'), - onSuccess: async (response: GoogleLoginResponse | GoogleLoginResponseOffline) => { - await loginWithGoogleMutation({ accessToken: (response as GoogleLoginResponse).accessToken }); - - handleClose(); - }, - }); - const handleClose = () => { dispatch(setModalState({ modal: 'auth.register', state: { open: false } })); reset(); @@ -87,8 +79,18 @@ const RegisterModal: React.FC = () => { dispatch(setModalState({ modal: 'auth.login', state: { open: true } })); }; - const handleLoginWithGoogle = () => { - signIn(); + const handleLoginWithGoogle = async () => { + google.accounts.id.initialize({ + client_id: env('GOOGLE_CLIENT_ID'), + callback: async (response: any) => { + await loginWithGoogleMutation({ credential: response.credential }); + + handleClose(); + }, + auto_select: false, + }); + + google.accounts.id.prompt(); }; return ( diff --git a/client/package.json b/client/package.json index c0d0a3d3..d847a775 100644 --- a/client/package.json +++ b/client/package.json @@ -13,67 +13,66 @@ "@emotion/react": "^11.9.0", "@emotion/styled": "^11.8.1", "@hookform/resolvers": "2.8.8", - "@monaco-editor/react": "^4.4.1", - "@mui/icons-material": "^5.6.0", - "@mui/lab": "^5.0.0-alpha.76", - "@mui/material": "^5.6.0", + "@monaco-editor/react": "^4.4.4", + "@mui/icons-material": "^5.6.2", + "@mui/lab": "^5.0.0-alpha.79", + "@mui/material": "^5.6.3", "@reduxjs/toolkit": "^1.8.1", - "axios": "^0.26.1", + "axios": "^0.27.2", "clsx": "^1.1.1", - "dayjs": "^1.11.0", + "dayjs": "^1.11.1", "downloadjs": "^1.4.7", "joi": "^17.6.0", "lodash": "^4.17.21", "md5-hex": "^4.0.0", "monaco-editor": "^0.33.0", - "nanoid": "^3.3.2", - "next": "12.1.4", + "nanoid": "^3.3.3", + "next": "12.1.5", "next-i18next": "^11.0.0", - "react": "<18", + "react": "^18", "react-beautiful-dnd": "^13.1.0", "react-colorful": "^5.5.1", - "react-dnd": "^15.1.2", - "react-dnd-html5-backend": "^15.1.2", - "react-dom": "<18", - "react-google-login": "^5.2.2", - "react-hook-form": "^7.29.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^18", + "react-hook-form": "^7.30.0", "react-hot-toast": "2.2.0", "react-hotkeys-hook": "^3.4.4", "react-icons": "^4.3.1", - "react-markdown": "^8.0.2", - "react-query": "^3.34.19", - "react-redux": "^7.2.8", + "react-markdown": "^8.0.3", + "react-query": "^3.38.0", + "react-redux": "^8.0.1", "react-zoom-pan-pinch": "^2.1.3", - "redux": "^4.1.2", + "redux": "^4.2.0", "redux-persist": "^6.0.0", "redux-saga": "^1.1.3", "remark-gfm": "^3.0.1", - "sharp": "^0.30.3", + "sharp": "^0.30.4", "uuid": "^8.3.2", "webfontloader": "^1.6.28" }, "devDependencies": { - "@babel/core": "^7.17.9", + "@babel/core": "^7.17.10", "@reactive-resume/schema": "workspace:*", "@tailwindcss/typography": "^0.5.2", "@types/downloadjs": "^1.4.3", - "@types/lodash": "^4.14.181", - "@types/node": "17.0.23", - "@types/react": "<18", + "@types/lodash": "^4.14.182", + "@types/node": "17.0.30", + "@types/react": "^18", "@types/react-beautiful-dnd": "^13.1.2", - "@types/react-redux": "^7.1.23", + "@types/react-redux": "^7.1.24", "@types/tailwindcss": "^3.0.10", "@types/uuid": "^8.3.4", "@types/webfontloader": "^1.6.34", - "autoprefixer": "^10.4.4", + "autoprefixer": "^10.4.5", "csstype": "^3.0.11", - "eslint": "^8.12.0", - "eslint-config-next": "12.1.4", - "next-sitemap": "^2.5.19", - "postcss": "^8.4.12", + "eslint": "^8.14.0", + "eslint-config-next": "12.1.5", + "next-sitemap": "^2.5.20", + "postcss": "^8.4.13", "prettier": "^2.6.2", - "sass": "^1.50.0", - "tailwindcss": "^3.0.23", - "typescript": "<4.6.0" + "sass": "^1.51.0", + "tailwindcss": "^3.0.24", + "typescript": "^4.6.4" } } diff --git a/client/pages/_app.tsx b/client/pages/_app.tsx index bcf4777a..ff29cc8e 100644 --- a/client/pages/_app.tsx +++ b/client/pages/_app.tsx @@ -4,6 +4,7 @@ import DateAdapter from '@mui/lab/AdapterDayjs'; import LocalizationProvider from '@mui/lab/LocalizationProvider'; import type { AppProps } from 'next/app'; import Head from 'next/head'; +import Script from 'next/script'; import { appWithTranslation } from 'next-i18next'; import { Toaster } from 'react-hot-toast'; import { QueryClientProvider } from 'react-query'; @@ -52,6 +53,8 @@ const App: React.FC = ({ Component, pageProps }) => { + +