diff --git a/components/auth/footer.tsx b/components/auth/footer.tsx index febe80c..d4fee6b 100644 --- a/components/auth/footer.tsx +++ b/components/auth/footer.tsx @@ -1,8 +1,26 @@ import {Button} from 'antd' import Link from 'next/link' +import {useRouter} from 'next/router' import React from 'react' +import {useTranslation} from 'react-i18next' +import {clearAuth, withAuth} from '../with.auth' + +interface Props { + me?: { + id: string + username: string + } +} + +const AuthFooterInner: React.FC = props => { + const { t } = useTranslation() + const router = useRouter() + + const logout = () => { + clearAuth() + router.reload() + } -export const AuthFooter: React.FC = () => { return (
{ type={'link'} ghost > - Admin - - - - - - - + {props.me ? ( + [ + + Hi, {props.me.username} + , + + ] + ): ( + [ + + + , + + + + ] + )}
) } + +export const AuthFooter = withAuth(AuthFooterInner) diff --git a/components/with.auth.tsx b/components/with.auth.tsx index 8025b14..f77b5d8 100644 --- a/components/with.auth.tsx +++ b/components/with.auth.tsx @@ -43,6 +43,10 @@ export const withAuth = (Component, roles: string[] = []): React.FC => { const {loading, data, error} = useQuery(ME_QUERY) useEffect(() => { + if (roles.length === 0) { + setAccess(true) + return + } if (!error) { return } @@ -56,6 +60,7 @@ export const withAuth = (Component, roles: string[] = []): React.FC => { useEffect(() => { if (!data || roles.length === 0) { + setAccess(true) return } @@ -79,6 +84,6 @@ export const withAuth = (Component, roles: string[] = []): React.FC => { return } - return + return }; } diff --git a/graphql/query/me.query.ts b/graphql/query/me.query.ts index cecc368..0ce7daa 100644 --- a/graphql/query/me.query.ts +++ b/graphql/query/me.query.ts @@ -3,7 +3,7 @@ import {gql} from 'apollo-boost' export interface MeQueryData { me: { id: string - + username: string roles: string[] } } @@ -13,6 +13,7 @@ export const ME_QUERY = gql` me { id roles + username } } ` diff --git a/i18n.ts b/i18n.ts index addd5bc..de7ecf1 100644 --- a/i18n.ts +++ b/i18n.ts @@ -1,5 +1,21 @@ +import i18n from "i18next" +import detector from "i18next-browser-languagedetector" +import {initReactI18next} from "react-i18next" +import {resources} from './locales' export const languages = [ 'de', 'en', ] + +i18n + .use(detector) + .use(initReactI18next) + .init({ + fallbackLng: 'en', + resources, + defaultNS: 'common', + react: { + useSuspense: process.browser, + } + }) diff --git a/locales/en/admin.ts b/locales/en/admin.ts new file mode 100644 index 0000000..e58a03c --- /dev/null +++ b/locales/en/admin.ts @@ -0,0 +1,6 @@ +export const admin = { + home: 'Home', + users: 'Users', + forms: 'Forms', + submissions: 'Submissions', +} diff --git a/locales/en/common.ts b/locales/en/common.ts new file mode 100644 index 0000000..d6b935d --- /dev/null +++ b/locales/en/common.ts @@ -0,0 +1,8 @@ +export const common = { + logout: 'Logout', + login: "Login", + register: "Create Account", + recover: 'Lost Password', + admin: "Admin", + 'mandatory-fields-missing': 'Mandatory fields missing', +} diff --git a/locales/en/form.ts b/locales/en/form.ts new file mode 100644 index 0000000..c18a092 --- /dev/null +++ b/locales/en/form.ts @@ -0,0 +1,5 @@ +export const form = { + building: 'Building Form', + submitted: 'Thank you for your submission!', + restart: 'Restart Form', +} diff --git a/locales/en/index.ts b/locales/en/index.ts new file mode 100644 index 0000000..1ac9bfe --- /dev/null +++ b/locales/en/index.ts @@ -0,0 +1,17 @@ +import {admin} from './admin' +import {common} from './common' +import {form} from './form' +import {login} from './login' +import {register} from './register' +import {statistic} from './statistic' +import {validation} from './validation' + +export const en = { + admin, + common, + form, + login, + register, + statistic, + validation, +} diff --git a/locales/en/login.ts b/locales/en/login.ts new file mode 100644 index 0000000..c0e98e2 --- /dev/null +++ b/locales/en/login.ts @@ -0,0 +1,9 @@ +export const login = { + 'welcome-back': 'Welcome back!', + 'invalid-login-credentials': 'username / password are invalid', + 'username-required': 'Please input your username!', + 'username-placeholder': 'Username', + 'password-required': 'Please input your password!', + 'password-placeholder': 'Password', + 'login-now': 'Login Now', +} diff --git a/locales/en/register.ts b/locales/en/register.ts new file mode 100644 index 0000000..ef929f8 --- /dev/null +++ b/locales/en/register.ts @@ -0,0 +1,8 @@ +export const register = { + welcome: 'Welcome, please also confirm your email', + 'credentials-already-in-use': 'Some data already in use!', + 'password-min-length': 'Must be longer than or equal to 5 characters!', + 'email-required': 'Please input your email!', + 'register-now': 'Register Now', + 'goto-login': 'Have an account? Go to login', +} diff --git a/locales/en/statistic.ts b/locales/en/statistic.ts new file mode 100644 index 0000000..2d16eac --- /dev/null +++ b/locales/en/statistic.ts @@ -0,0 +1,5 @@ +export const statistic = { + 'total-forms': 'Total Forms', + 'total-users': 'Total Users', + 'total-submissions': 'Total Submissions', +} diff --git a/locales/en/validation.ts b/locales/en/validation.ts new file mode 100644 index 0000000..847d272 --- /dev/null +++ b/locales/en/validation.ts @@ -0,0 +1,3 @@ +export const validation = { + 'invalid-email': 'Must be a valid email!', +} diff --git a/locales/index.ts b/locales/index.ts new file mode 100644 index 0000000..ee817ab --- /dev/null +++ b/locales/index.ts @@ -0,0 +1,5 @@ +import {en} from './en' + +export const resources = { + en, +} diff --git a/package.json b/package.json index d4628e0..62df387 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,15 @@ "axios": "^0.19.2", "dayjs": "^1.8.27", "graphql": "^15.0.0", + "i18next": "^19.4.5", + "i18next-browser-languagedetector": "^4.2.0", "next": "9.4.0", "next-images": "^1.4.0", "next-redux-wrapper": "^6.0.0", "react": "16.13.1", "react-color": "^2.18.1", "react-dom": "16.13.1", + "react-i18next": "^11.5.0", "react-icons": "^3.10.0", "react-id-swiper": "^3.0.0", "react-redux": "^7.2.0", diff --git a/pages/admin/forms/[id]/index.tsx b/pages/admin/forms/[id]/index.tsx index 53adf9b..335d0d2 100644 --- a/pages/admin/forms/[id]/index.tsx +++ b/pages/admin/forms/[id]/index.tsx @@ -22,8 +22,10 @@ import {NextPage} from 'next' import Link from 'next/link' import {useRouter} from 'next/router' import React, {useState} from 'react' +import {useTranslation} from 'react-i18next' const Index: NextPage = () => { + const { t } = useTranslation() const router = useRouter() const [form] = useForm() const [saving, setSaving] = useState(false) @@ -70,8 +72,8 @@ const Index: NextPage = () => { title={loading ? 'Loading Form' : `Edit Form "${data.form.title}"`} selected={'forms'} breadcrumbs={[ - { href: '/admin', name: 'Home' }, - { href: '/admin/forms', name: 'Form' }, + { href: '/admin', name: t('admin:home') }, + { href: '/admin/forms', name: t('admin:forms') }, ]} extra={[ diff --git a/pages/admin/forms/[id]/submissions.tsx b/pages/admin/forms/[id]/submissions.tsx index ceb4c3d..ec93384 100644 --- a/pages/admin/forms/[id]/submissions.tsx +++ b/pages/admin/forms/[id]/submissions.tsx @@ -11,6 +11,7 @@ import {NextPage} from 'next' import Link from 'next/link' import {useRouter} from 'next/router' import React, {useState} from 'react' +import {useTranslation} from 'react-i18next' import {SubmissionValues} from '../../../../components/form/admin/submission.values' import { ADMIN_PAGER_SUBMISSION_QUERY, @@ -21,6 +22,7 @@ import { } from '../../../../graphql/query/admin.pager.submission.query' const Submissions: NextPage = () => { + const { t } = useTranslation() const router = useRouter() const [pagination, setPagination] = useState({ pageSize: 25, @@ -74,12 +76,12 @@ const Submissions: NextPage = () => { return ( { + const { t } = useTranslation() const router = useRouter() const [form] = useForm() const [saving, setSaving] = useState(false) @@ -47,8 +49,8 @@ const Create: NextPage = () => { title={'Create New Form'} selected={'forms'} breadcrumbs={[ - { href: '/admin', name: 'Home' }, - { href: '/admin/forms', name: 'Form' }, + { href: '/admin', name: t('admin:home') }, + { href: '/admin/forms', name: t('admin:forms') }, ]} extra={[ @@ -110,7 +112,7 @@ const Index: NextPage = () => { type={'link'} ghost > - Create Account + {t('register')} @@ -118,7 +120,7 @@ const Index: NextPage = () => { type={'link'} ghost > - Lost password + {t('recover')} diff --git a/pages/register.tsx b/pages/register.tsx index ef43291..2b8b6b4 100644 --- a/pages/register.tsx +++ b/pages/register.tsx @@ -9,8 +9,10 @@ import {NextPage} from 'next' import Link from 'next/link' import {useRouter} from 'next/router' import React, {useState} from 'react' +import {useTranslation} from 'react-i18next' const Register: NextPage = () => { + const { t } = useTranslation() const [form] = useForm() const router = useRouter() const [loading, setLoading] = useState(false) @@ -32,17 +34,17 @@ const Register: NextPage = () => { result.data.tokens.refresh ) - message.success('Welcome, please also confirm your email') + message.success(t('register:welcome')) router.push('/') } catch (e) { - message.error('Some data already in use!') + message.error(t('register:credentials-already-in-use')) setLoading(false) } } const failed = () => { - message.error('mandatory fields missing') + message.error(t('mandatory-fields-missing')) } return ( @@ -72,19 +74,19 @@ const Register: NextPage = () => { { @@ -113,7 +115,7 @@ const Register: NextPage = () => { htmlType="submit" block > - Register Now + {t('register:register-now')} @@ -129,7 +131,7 @@ const Register: NextPage = () => { type={'link'} ghost > - Have an account? Go to login + {t('register:goto-login')} diff --git a/yarn.lock b/yarn.lock index 51fdb80..289379e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1021,6 +1021,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.3.1": + version "7.10.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839" + integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" @@ -3368,6 +3375,13 @@ html-comment-regex@^1.1.0: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== +html-parse-stringify2@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a" + integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= + dependencies: + void-elements "^2.0.1" + htmlparser2@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" @@ -3383,6 +3397,20 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +i18next-browser-languagedetector@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-4.2.0.tgz#82e35d31f88a1d7c2b6d5913bf8c8481cd40aafb" + integrity sha512-qRSCBWgDUSqVQb3sTxkDC+ImYLhF+wB387Y1RpOcJvyex+V3abi+W83n4Awy+dx719AOBbKTy97FjrUGrAhbyw== + dependencies: + "@babel/runtime" "^7.5.5" + +i18next@^19.4.5: + version "19.4.5" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.4.5.tgz#f9ea8bbb48d1ec66bc3436f0bb74a16b11821e11" + integrity sha512-aLvSsURoupi3x9IndmV6+m3IGhzLzhYv7Gw+//K3ovdliyGcFRV0I1MuddI0Bk/zR7BG1U+kJOjeHFUcUIdEgg== + dependencies: + "@babel/runtime" "^7.3.1" + icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" @@ -5434,6 +5462,14 @@ react-dom@16.13.1: prop-types "^15.6.2" scheduler "^0.19.1" +react-i18next@^11.5.0: + version "11.5.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.5.0.tgz#84a9bb535d44c0c1b336b94de164515c2cc2a714" + integrity sha512-V6rUT7MzYBdFCgUrhfr78FHRfnY3CFoR75ET9EP5Py5UPHKyaGiK1MvPx03TesLwsmIaVHlRFU/WLzqCedXevA== + dependencies: + "@babel/runtime" "^7.3.1" + html-parse-stringify2 "2.0.1" + react-icons@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-3.10.0.tgz#6c217a2dde2e8fa8d293210023914b123f317297" @@ -6548,6 +6584,11 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + warning@^4.0.1, warning@^4.0.3, warning@~4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"