- implement i18n
- translation dynamic for sections - added articles for SEO
This commit is contained in:
parent
b7c565de79
commit
a7657b4a5c
|
@ -4,5 +4,9 @@
|
|||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true
|
||||
}
|
||||
},
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/i18n/locales"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested"
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@ import { DatabaseProvider } from './src/contexts/DatabaseContext';
|
|||
import { ModalProvider } from './src/contexts/ModalContext';
|
||||
import { ResumeProvider } from './src/contexts/ResumeContext';
|
||||
import { StorageProvider } from './src/contexts/StorageContext';
|
||||
import { ThemeProvider } from './src/contexts/ThemeContext';
|
||||
import { SettingsProvider } from './src/contexts/SettingsContext';
|
||||
import { UserProvider } from './src/contexts/UserContext';
|
||||
import './src/styles/global.css';
|
||||
import './src/i18n';
|
||||
import './src/styles/forms.css';
|
||||
import './src/styles/global.css';
|
||||
import './src/styles/shadows.css';
|
||||
import './src/styles/tailwind.css';
|
||||
import './src/styles/toastify.css';
|
||||
|
@ -27,7 +28,7 @@ const theme = createMuiTheme({
|
|||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const wrapRootElement = ({ element }) => (
|
||||
<ThemeProvider>
|
||||
<SettingsProvider>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<ModalProvider>
|
||||
<UserProvider>
|
||||
|
@ -39,5 +40,5 @@ export const wrapRootElement = ({ element }) => (
|
|||
</UserProvider>
|
||||
</ModalProvider>
|
||||
</MuiThemeProvider>
|
||||
</ThemeProvider>
|
||||
</SettingsProvider>
|
||||
);
|
||||
|
|
|
@ -82,7 +82,7 @@ module.exports = {
|
|||
{
|
||||
resolve: 'gatsby-plugin-offline',
|
||||
options: {
|
||||
precachePages: ['', '/app/*'],
|
||||
precachePages: ['', '/app/*', '/blog/*'],
|
||||
},
|
||||
},
|
||||
'gatsby-plugin-lodash',
|
||||
|
@ -95,6 +95,14 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
'gatsby-plugin-postcss',
|
||||
{
|
||||
resolve: `gatsby-source-filesystem`,
|
||||
options: {
|
||||
path: `${__dirname}/src/articles`,
|
||||
name: `articles`,
|
||||
},
|
||||
},
|
||||
`gatsby-transformer-remark`,
|
||||
{
|
||||
resolve: 'gatsby-source-filesystem',
|
||||
options: {
|
||||
|
|
|
@ -25,3 +25,41 @@ exports.onCreatePage = async ({ page, actions }) => {
|
|||
createPage(page);
|
||||
}
|
||||
};
|
||||
|
||||
exports.createPages = async ({ actions, graphql, reporter }) => {
|
||||
const { createPage } = actions;
|
||||
|
||||
const blogTemplate = require.resolve(`./src/components/Blog.js`);
|
||||
|
||||
const result = await graphql(`
|
||||
{
|
||||
allMarkdownRemark(
|
||||
sort: { order: DESC, fields: [frontmatter___date] }
|
||||
limit: 1000
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
frontmatter {
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
if (result.errors) {
|
||||
reporter.panicOnBuild(`Error while running GraphQL query.`);
|
||||
return;
|
||||
}
|
||||
|
||||
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
|
||||
createPage({
|
||||
path: node.frontmatter.slug,
|
||||
component: blogTemplate,
|
||||
context: {
|
||||
slug: node.frontmatter.slug,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -2374,6 +2374,24 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz",
|
||||
"integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ=="
|
||||
},
|
||||
"@types/vfile": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/vfile/-/vfile-3.0.2.tgz",
|
||||
"integrity": "sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/unist": "*",
|
||||
"@types/vfile-message": "*"
|
||||
}
|
||||
},
|
||||
"@types/vfile-message": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vfile-message/-/vfile-message-2.0.0.tgz",
|
||||
"integrity": "sha512-GpTIuDpb9u4zIO165fUy9+fXcULdD8HFRNli04GehoMVbeNq7D6OBnqSmg3lxZnC+UvgUhEWKxdKiwYUkGltIw==",
|
||||
"requires": {
|
||||
"vfile-message": "*"
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "15.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
|
||||
|
@ -2906,6 +2924,11 @@
|
|||
"is-string": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"array-iterate": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-1.1.4.tgz",
|
||||
"integrity": "sha512-sNRaPGh9nnmdC8Zf+pT3UqP8rnWj5Hf9wiFGsX3wUQ2yVSIhO2ShFwCoceIPpB41QF6i2OEmrHmCo36xronCVA=="
|
||||
},
|
||||
"array-map": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
|
||||
|
@ -5433,6 +5456,11 @@
|
|||
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
|
||||
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
|
||||
},
|
||||
"css-selector-parser": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz",
|
||||
"integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g=="
|
||||
},
|
||||
"css-selector-tokenizer": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz",
|
||||
|
@ -9285,6 +9313,285 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"gatsby-transformer-remark": {
|
||||
"version": "2.8.25",
|
||||
"resolved": "https://registry.npmjs.org/gatsby-transformer-remark/-/gatsby-transformer-remark-2.8.25.tgz",
|
||||
"integrity": "sha512-RTO741t6eG4lw0WWmZRjN55SLBx225fJr+t+QkqUNTbBdTxRuUfHyAK6gRzVDtIC9Jtr1fQZxK0Hx2M6nj72RQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.3",
|
||||
"bluebird": "^3.7.2",
|
||||
"gatsby-core-utils": "^1.3.12",
|
||||
"gray-matter": "^4.0.2",
|
||||
"hast-util-raw": "^4.0.0",
|
||||
"hast-util-to-html": "^4.0.1",
|
||||
"lodash": "^4.17.15",
|
||||
"mdast-util-to-hast": "^3.0.4",
|
||||
"mdast-util-to-string": "^1.1.0",
|
||||
"mdast-util-toc": "^5.0",
|
||||
"remark": "^10.0.1",
|
||||
"remark-parse": "^6.0.3",
|
||||
"remark-retext": "^3.1.3",
|
||||
"remark-stringify": "6.0.4",
|
||||
"retext-english": "^3.0.4",
|
||||
"sanitize-html": "^1.27.0",
|
||||
"underscore.string": "^3.3.5",
|
||||
"unified": "^6.2.0",
|
||||
"unist-util-remove-position": "^1.1.4",
|
||||
"unist-util-select": "^1.5.0",
|
||||
"unist-util-visit": "^1.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"hast-to-hyperscript": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-5.0.0.tgz",
|
||||
"integrity": "sha512-DLl3eYTz8uwwzEubDUdCChsR5t5b2ne+yvHrA2h58Suq/JnN3+Gsb9Tc4iZoCCsykmFUc6UUpwxTmQXs0akSeg==",
|
||||
"requires": {
|
||||
"comma-separated-tokens": "^1.0.0",
|
||||
"property-information": "^4.0.0",
|
||||
"space-separated-tokens": "^1.0.0",
|
||||
"style-to-object": "^0.2.1",
|
||||
"unist-util-is": "^2.0.0",
|
||||
"web-namespaces": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"hast-util-from-parse5": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-4.0.2.tgz",
|
||||
"integrity": "sha512-I6dtjsGtDqz4fmGSiFClFyiXdKhj5bPceS6intta7k/VDuiKz9P61C6hO6WMiNNmEm1b/EtBH8f+juvz4o0uwQ==",
|
||||
"requires": {
|
||||
"ccount": "^1.0.3",
|
||||
"hastscript": "^4.0.0",
|
||||
"property-information": "^4.0.0",
|
||||
"web-namespaces": "^1.1.2",
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"hast-util-raw": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-4.0.0.tgz",
|
||||
"integrity": "sha512-5xYHyEJMCf8lX/NT4iA5z6N43yoFsrJqXJ5GWwAbLn815URbIz+UNNFEgid33F9paZuDlqVKvB+K3Aqu5+DdSw==",
|
||||
"requires": {
|
||||
"hast-util-from-parse5": "^4.0.2",
|
||||
"hast-util-to-parse5": "^4.0.1",
|
||||
"html-void-elements": "^1.0.1",
|
||||
"parse5": "^5.0.0",
|
||||
"unist-util-position": "^3.0.0",
|
||||
"web-namespaces": "^1.0.0",
|
||||
"xtend": "^4.0.1",
|
||||
"zwitch": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hast-util-to-parse5": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-4.0.1.tgz",
|
||||
"integrity": "sha512-U/61W+fsNfBpCyJBB5Pt3l5ypIfgXqEyW9pyrtxF7XrqDJHzcFrYpnC94d0JDYjvobLpYCzcU9srhMRPEO1YXw==",
|
||||
"requires": {
|
||||
"hast-to-hyperscript": "^5.0.0",
|
||||
"property-information": "^4.0.0",
|
||||
"web-namespaces": "^1.0.0",
|
||||
"xtend": "^4.0.1",
|
||||
"zwitch": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hastscript": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-4.1.0.tgz",
|
||||
"integrity": "sha512-bOTn9hEfzewvHyXdbYGKqOr/LOz+2zYhKbC17U2YAjd16mnjqB1BQ0nooM/RdMy/htVyli0NAznXiBtwDi1cmQ==",
|
||||
"requires": {
|
||||
"comma-separated-tokens": "^1.0.0",
|
||||
"hast-util-parse-selector": "^2.2.0",
|
||||
"property-information": "^4.0.0",
|
||||
"space-separated-tokens": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-plain-obj": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
||||
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4="
|
||||
},
|
||||
"markdown-table": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz",
|
||||
"integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q=="
|
||||
},
|
||||
"mdast-util-compact": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz",
|
||||
"integrity": "sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg==",
|
||||
"requires": {
|
||||
"unist-util-visit": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"mdast-util-definitions": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.5.tgz",
|
||||
"integrity": "sha512-CJXEdoLfiISCDc2JB6QLb79pYfI6+GcIH+W2ox9nMc7od0Pz+bovcHsiq29xAQY6ayqe/9CsK2VzkSJdg1pFYA==",
|
||||
"requires": {
|
||||
"unist-util-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"mdast-util-to-hast": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-3.0.4.tgz",
|
||||
"integrity": "sha512-/eIbly2YmyVgpJNo+bFLLMCI1XgolO/Ffowhf+pHDq3X4/V6FntC9sGQCDLM147eTS+uSXv5dRzJyFn+o0tazA==",
|
||||
"requires": {
|
||||
"collapse-white-space": "^1.0.0",
|
||||
"detab": "^2.0.0",
|
||||
"mdast-util-definitions": "^1.2.0",
|
||||
"mdurl": "^1.0.1",
|
||||
"trim": "0.0.1",
|
||||
"trim-lines": "^1.0.0",
|
||||
"unist-builder": "^1.0.1",
|
||||
"unist-util-generated": "^1.1.0",
|
||||
"unist-util-position": "^3.0.0",
|
||||
"unist-util-visit": "^1.1.0",
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"parse-entities": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz",
|
||||
"integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==",
|
||||
"requires": {
|
||||
"character-entities": "^1.0.0",
|
||||
"character-entities-legacy": "^1.0.0",
|
||||
"character-reference-invalid": "^1.0.0",
|
||||
"is-alphanumerical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0",
|
||||
"is-hexadecimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"property-information": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-4.2.0.tgz",
|
||||
"integrity": "sha512-TlgDPagHh+eBKOnH2VYvk8qbwsCG/TAJdmTL7f1PROUcSO8qt/KSmShEQ/OKvock8X9tFjtqjCScyOkkkvIKVQ==",
|
||||
"requires": {
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"remark-stringify": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-6.0.4.tgz",
|
||||
"integrity": "sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg==",
|
||||
"requires": {
|
||||
"ccount": "^1.0.0",
|
||||
"is-alphanumeric": "^1.0.0",
|
||||
"is-decimal": "^1.0.0",
|
||||
"is-whitespace-character": "^1.0.0",
|
||||
"longest-streak": "^2.0.1",
|
||||
"markdown-escapes": "^1.0.0",
|
||||
"markdown-table": "^1.1.0",
|
||||
"mdast-util-compact": "^1.0.0",
|
||||
"parse-entities": "^1.0.2",
|
||||
"repeat-string": "^1.5.4",
|
||||
"state-toggle": "^1.0.0",
|
||||
"stringify-entities": "^1.0.1",
|
||||
"unherit": "^1.0.4",
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"stringify-entities": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz",
|
||||
"integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==",
|
||||
"requires": {
|
||||
"character-entities-html4": "^1.0.0",
|
||||
"character-entities-legacy": "^1.0.0",
|
||||
"is-alphanumerical": "^1.0.0",
|
||||
"is-hexadecimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"style-to-object": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.2.3.tgz",
|
||||
"integrity": "sha512-1d/k4EY2N7jVLOqf2j04dTc37TPOv/hHxZmvpg8Pdh8UYydxeu/C1W1U4vD8alzf5V2Gt7rLsmkr4dxAlDm9ng==",
|
||||
"requires": {
|
||||
"inline-style-parser": "0.1.1"
|
||||
}
|
||||
},
|
||||
"unified": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz",
|
||||
"integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==",
|
||||
"requires": {
|
||||
"bail": "^1.0.0",
|
||||
"extend": "^3.0.0",
|
||||
"is-plain-obj": "^1.1.0",
|
||||
"trough": "^1.0.0",
|
||||
"vfile": "^2.0.0",
|
||||
"x-is-string": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"unist-builder": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-1.0.4.tgz",
|
||||
"integrity": "sha512-v6xbUPP7ILrT15fHGrNyHc1Xda8H3xVhP7/HAIotHOhVPjH5dCXA097C3Rry1Q2O+HbOLCao4hfPB+EYEjHgVg==",
|
||||
"requires": {
|
||||
"object-assign": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"unist-util-is": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.3.tgz",
|
||||
"integrity": "sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA=="
|
||||
},
|
||||
"unist-util-remove-position": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz",
|
||||
"integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==",
|
||||
"requires": {
|
||||
"unist-util-visit": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"unist-util-stringify-position": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz",
|
||||
"integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ=="
|
||||
},
|
||||
"unist-util-visit": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz",
|
||||
"integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==",
|
||||
"requires": {
|
||||
"unist-util-visit-parents": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-visit-parents": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz",
|
||||
"integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==",
|
||||
"requires": {
|
||||
"unist-util-is": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"unist-util-is": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz",
|
||||
"integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"vfile": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz",
|
||||
"integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==",
|
||||
"requires": {
|
||||
"is-buffer": "^1.1.4",
|
||||
"replace-ext": "1.0.0",
|
||||
"unist-util-stringify-position": "^1.0.0",
|
||||
"vfile-message": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"vfile-message": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz",
|
||||
"integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==",
|
||||
"requires": {
|
||||
"unist-util-stringify-position": "^1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gatsby-transformer-sharp": {
|
||||
"version": "2.5.11",
|
||||
"resolved": "https://registry.npmjs.org/gatsby-transformer-sharp/-/gatsby-transformer-sharp-2.5.11.tgz",
|
||||
|
@ -9418,6 +9725,21 @@
|
|||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
|
||||
},
|
||||
"github-slugger": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz",
|
||||
"integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==",
|
||||
"requires": {
|
||||
"emoji-regex": ">=6.0.0 <=6.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"emoji-regex": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz",
|
||||
"integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
|
@ -9734,6 +10056,17 @@
|
|||
"resolved": "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz",
|
||||
"integrity": "sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg=="
|
||||
},
|
||||
"gray-matter": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.2.tgz",
|
||||
"integrity": "sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw==",
|
||||
"requires": {
|
||||
"js-yaml": "^3.11.0",
|
||||
"kind-of": "^6.0.2",
|
||||
"section-matter": "^1.0.0",
|
||||
"strip-bom-string": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"gud": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||
|
@ -9953,6 +10286,11 @@
|
|||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"hast-util-is-element": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.4.tgz",
|
||||
"integrity": "sha512-NFR6ljJRvDcyPP5SbV7MyPBgF47X3BsskLnmw1U34yL+X6YC0MoBx9EyMg8Jtx4FzGH95jw8+c1VPLHaRA0wDQ=="
|
||||
},
|
||||
"hast-util-parse-selector": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz",
|
||||
|
@ -9973,6 +10311,49 @@
|
|||
"zwitch": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hast-util-to-html": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-4.0.1.tgz",
|
||||
"integrity": "sha512-2emzwyf0xEsc4TBIPmDJmBttIw8R4SXAJiJZoiRR/s47ODYWgOqNoDbf2SJAbMbfNdFWMiCSOrI3OVnX6Qq2Mg==",
|
||||
"requires": {
|
||||
"ccount": "^1.0.0",
|
||||
"comma-separated-tokens": "^1.0.1",
|
||||
"hast-util-is-element": "^1.0.0",
|
||||
"hast-util-whitespace": "^1.0.0",
|
||||
"html-void-elements": "^1.0.0",
|
||||
"property-information": "^4.0.0",
|
||||
"space-separated-tokens": "^1.0.0",
|
||||
"stringify-entities": "^1.0.1",
|
||||
"unist-util-is": "^2.0.0",
|
||||
"xtend": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"property-information": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-4.2.0.tgz",
|
||||
"integrity": "sha512-TlgDPagHh+eBKOnH2VYvk8qbwsCG/TAJdmTL7f1PROUcSO8qt/KSmShEQ/OKvock8X9tFjtqjCScyOkkkvIKVQ==",
|
||||
"requires": {
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"stringify-entities": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz",
|
||||
"integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==",
|
||||
"requires": {
|
||||
"character-entities-html4": "^1.0.0",
|
||||
"character-entities-legacy": "^1.0.0",
|
||||
"is-alphanumerical": "^1.0.0",
|
||||
"is-hexadecimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-is": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.3.tgz",
|
||||
"integrity": "sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"hast-util-to-parse5": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-5.1.2.tgz",
|
||||
|
@ -9985,6 +10366,11 @@
|
|||
"zwitch": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hast-util-whitespace": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz",
|
||||
"integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A=="
|
||||
},
|
||||
"hastscript": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz",
|
||||
|
@ -10084,6 +10470,14 @@
|
|||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
|
||||
"integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA=="
|
||||
},
|
||||
"html-parse-stringify2": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz",
|
||||
"integrity": "sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=",
|
||||
"requires": {
|
||||
"void-elements": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"html-tag-names": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/html-tag-names/-/html-tag-names-1.1.5.tgz",
|
||||
|
@ -10258,6 +10652,14 @@
|
|||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
|
||||
"integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
|
||||
},
|
||||
"i18next": {
|
||||
"version": "19.6.0",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-19.6.0.tgz",
|
||||
"integrity": "sha512-t+pA7iN2WtwS1UQc4PFKHDIO4HYZIl2Wo8UC8gqt70Q1qY50FflAF5vV4IbQEqy4DuK3I9wv3BL1PMvkk238WA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
||||
|
@ -12480,6 +12882,50 @@
|
|||
"unist-util-visit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"mdast-util-to-nlcst": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-nlcst/-/mdast-util-to-nlcst-3.2.3.tgz",
|
||||
"integrity": "sha512-hPIsgEg7zCvdU6/qvjcR6lCmJeRuIEpZGY5xBV+pqzuMOvQajyyF8b6f24f8k3Rw8u40GwkI3aAxUXr3bB2xag==",
|
||||
"requires": {
|
||||
"nlcst-to-string": "^2.0.0",
|
||||
"repeat-string": "^1.5.2",
|
||||
"unist-util-position": "^3.0.0",
|
||||
"vfile-location": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"vfile-location": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz",
|
||||
"integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"mdast-util-to-string": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz",
|
||||
"integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A=="
|
||||
},
|
||||
"mdast-util-toc": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-5.0.3.tgz",
|
||||
"integrity": "sha512-A3xzcgC1XFHK0+abFmbINOxjwo7Bi0Nsfp3yTgTy5JHo2q2V6YZ5BVJreDWoK3szcLlSMvHqe8WPbjY50wAkow==",
|
||||
"requires": {
|
||||
"@types/mdast": "^3.0.3",
|
||||
"@types/unist": "^2.0.3",
|
||||
"extend": "^3.0.2",
|
||||
"github-slugger": "^1.2.1",
|
||||
"mdast-util-to-string": "^1.0.5",
|
||||
"unist-util-is": "^4.0.0",
|
||||
"unist-util-visit": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"unist-util-is": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz",
|
||||
"integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"mdn-data": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
|
||||
|
@ -13026,6 +13472,11 @@
|
|||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
|
||||
},
|
||||
"nlcst-to-string": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-2.0.4.tgz",
|
||||
"integrity": "sha512-3x3jwTd6UPG7vi5k4GEzvxJ5rDA7hVUIRNHPblKuMVP9Z3xmlsd9cgLcpAMkc5uPOBna82EeshROFhsPkbnTZg=="
|
||||
},
|
||||
"no-case": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz",
|
||||
|
@ -13736,6 +14187,17 @@
|
|||
"xml2js": "^0.4.5"
|
||||
}
|
||||
},
|
||||
"parse-english": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/parse-english/-/parse-english-4.1.3.tgz",
|
||||
"integrity": "sha512-IQl1v/ik9gw437T8083coohMihae0rozpc7JYC/9h6hi9xKBSxFwh5HWRpzVC2ZhEs2nUlze2aAktpNBJXdJKA==",
|
||||
"requires": {
|
||||
"nlcst-to-string": "^2.0.0",
|
||||
"parse-latin": "^4.0.0",
|
||||
"unist-util-modify-children": "^1.0.0",
|
||||
"unist-util-visit-children": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"parse-entities": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
|
||||
|
@ -13765,6 +14227,16 @@
|
|||
"lines-and-columns": "^1.1.6"
|
||||
}
|
||||
},
|
||||
"parse-latin": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-4.2.1.tgz",
|
||||
"integrity": "sha512-7T9g6mIsFFpLlo0Zzb2jLWdCt+H9Qtf/hRmMYFi/Mq6Ovi+YKo+AyDFX3OhFfu0vXX5Nid9FKJGKSSzNcTkWiA==",
|
||||
"requires": {
|
||||
"nlcst-to-string": "^2.0.0",
|
||||
"unist-util-modify-children": "^1.0.0",
|
||||
"unist-util-visit-children": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"parse-passwd": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
|
||||
|
@ -15557,6 +16029,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"react-i18next": {
|
||||
"version": "11.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.7.0.tgz",
|
||||
"integrity": "sha512-8tvVkpuxQlubcszZON+jmoCgiA9gCZ74OAYli9KChPhETtq8pJsANBTe9KRLRLmX3ubumgvidURWr0VvKz1tww==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"html-parse-stringify2": "2.0.1"
|
||||
}
|
||||
},
|
||||
"react-icons": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.10.0.tgz",
|
||||
|
@ -16061,6 +16542,141 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"remark": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/remark/-/remark-10.0.1.tgz",
|
||||
"integrity": "sha512-E6lMuoLIy2TyiokHprMjcWNJ5UxfGQjaMSMhV+f4idM625UjjK4j798+gPs5mfjzDE6vL0oFKVeZM6gZVSVrzQ==",
|
||||
"requires": {
|
||||
"remark-parse": "^6.0.0",
|
||||
"remark-stringify": "^6.0.0",
|
||||
"unified": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-buffer": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
|
||||
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
|
||||
},
|
||||
"is-plain-obj": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
||||
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4="
|
||||
},
|
||||
"markdown-table": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz",
|
||||
"integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q=="
|
||||
},
|
||||
"mdast-util-compact": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz",
|
||||
"integrity": "sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg==",
|
||||
"requires": {
|
||||
"unist-util-visit": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"parse-entities": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz",
|
||||
"integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==",
|
||||
"requires": {
|
||||
"character-entities": "^1.0.0",
|
||||
"character-entities-legacy": "^1.0.0",
|
||||
"character-reference-invalid": "^1.0.0",
|
||||
"is-alphanumerical": "^1.0.0",
|
||||
"is-decimal": "^1.0.0",
|
||||
"is-hexadecimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"remark-stringify": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-6.0.4.tgz",
|
||||
"integrity": "sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg==",
|
||||
"requires": {
|
||||
"ccount": "^1.0.0",
|
||||
"is-alphanumeric": "^1.0.0",
|
||||
"is-decimal": "^1.0.0",
|
||||
"is-whitespace-character": "^1.0.0",
|
||||
"longest-streak": "^2.0.1",
|
||||
"markdown-escapes": "^1.0.0",
|
||||
"markdown-table": "^1.1.0",
|
||||
"mdast-util-compact": "^1.0.0",
|
||||
"parse-entities": "^1.0.2",
|
||||
"repeat-string": "^1.5.4",
|
||||
"state-toggle": "^1.0.0",
|
||||
"stringify-entities": "^1.0.1",
|
||||
"unherit": "^1.0.4",
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"stringify-entities": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz",
|
||||
"integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==",
|
||||
"requires": {
|
||||
"character-entities-html4": "^1.0.0",
|
||||
"character-entities-legacy": "^1.0.0",
|
||||
"is-alphanumerical": "^1.0.0",
|
||||
"is-hexadecimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"unified": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-7.1.0.tgz",
|
||||
"integrity": "sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"@types/vfile": "^3.0.0",
|
||||
"bail": "^1.0.0",
|
||||
"extend": "^3.0.0",
|
||||
"is-plain-obj": "^1.1.0",
|
||||
"trough": "^1.0.0",
|
||||
"vfile": "^3.0.0",
|
||||
"x-is-string": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"unist-util-stringify-position": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz",
|
||||
"integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ=="
|
||||
},
|
||||
"unist-util-visit": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz",
|
||||
"integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==",
|
||||
"requires": {
|
||||
"unist-util-visit-parents": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-visit-parents": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz",
|
||||
"integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==",
|
||||
"requires": {
|
||||
"unist-util-is": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"vfile": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-3.0.1.tgz",
|
||||
"integrity": "sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ==",
|
||||
"requires": {
|
||||
"is-buffer": "^2.0.0",
|
||||
"replace-ext": "1.0.0",
|
||||
"unist-util-stringify-position": "^1.0.0",
|
||||
"vfile-message": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"vfile-message": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz",
|
||||
"integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==",
|
||||
"requires": {
|
||||
"unist-util-stringify-position": "^1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"remark-footnotes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-1.0.0.tgz",
|
||||
|
@ -16249,6 +16865,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"remark-retext": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/remark-retext/-/remark-retext-3.1.3.tgz",
|
||||
"integrity": "sha512-UujXAm28u4lnUvtOZQFYfRIhxX+auKI9PuA2QpQVTT7gYk1OgX6o0OUrSo1KOa6GNrFX+OODOtS5PWIHPxM7qw==",
|
||||
"requires": {
|
||||
"mdast-util-to-nlcst": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"remark-squeeze-paragraphs": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz",
|
||||
|
@ -16441,6 +17065,15 @@
|
|||
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
|
||||
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
|
||||
},
|
||||
"retext-english": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/retext-english/-/retext-english-3.0.4.tgz",
|
||||
"integrity": "sha512-yr1PgaBDde+25aJXrnt3p1jvT8FVLVat2Bx8XeAWX13KXo8OT+3nWGU3HWxM4YFJvmfqvJYJZG2d7xxaO774gw==",
|
||||
"requires": {
|
||||
"parse-english": "^4.0.0",
|
||||
"unherit": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"retry": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||
|
@ -16535,6 +17168,55 @@
|
|||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sanitize-html": {
|
||||
"version": "1.27.0",
|
||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.0.tgz",
|
||||
"integrity": "sha512-U1btucGeYVpg0GoK43jPpe/bDCV4cBOGuxzv5NBd0bOjyZdMKY0n98S/vNlO1wVwre0VCj8H3hbzE7gD2+RjKA==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.1",
|
||||
"htmlparser2": "^4.1.0",
|
||||
"lodash": "^4.17.15",
|
||||
"postcss": "^7.0.27",
|
||||
"srcset": "^2.0.1",
|
||||
"xtend": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"domelementtype": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
|
||||
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
|
||||
},
|
||||
"domhandler": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz",
|
||||
"integrity": "sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"domutils": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.1.0.tgz",
|
||||
"integrity": "sha512-CD9M0Dm1iaHfQ1R/TI+z3/JWp/pgub0j4jIQKH89ARR4ATAV2nbaOQS5XxU9maJP5jHaPdDDQSEHuE2UmpUTKg==",
|
||||
"requires": {
|
||||
"dom-serializer": "^0.2.1",
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz",
|
||||
"integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==",
|
||||
"requires": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^3.0.0",
|
||||
"domutils": "^2.0.0",
|
||||
"entities": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
|
@ -16559,6 +17241,25 @@
|
|||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"section-matter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
||||
"integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"kind-of": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"requires": {
|
||||
"is-extendable": "^0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"seek-bzip": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz",
|
||||
|
@ -17466,6 +18167,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"srcset": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/srcset/-/srcset-2.0.1.tgz",
|
||||
"integrity": "sha512-00kZI87TdRKwt+P8jj8UZxbfp7mK2ufxcIMWvhAOZNJTRROimpHeruWrGvCZneiuVDLqdyHefVp748ECTnyUBQ=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||
|
@ -17760,6 +18466,11 @@
|
|||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
|
||||
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
|
||||
},
|
||||
"strip-bom-string": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
|
||||
"integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI="
|
||||
},
|
||||
"strip-comments": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz",
|
||||
|
@ -18595,6 +19306,15 @@
|
|||
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
||||
"integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
|
||||
},
|
||||
"underscore.string": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
|
||||
"integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
|
||||
"requires": {
|
||||
"sprintf-js": "^1.0.3",
|
||||
"util-deprecate": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"unherit": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz",
|
||||
|
@ -18700,6 +19420,14 @@
|
|||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz",
|
||||
"integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A=="
|
||||
},
|
||||
"unist-util-modify-children": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.6.tgz",
|
||||
"integrity": "sha512-TOA6W9QLil+BrHqIZNR4o6IA5QwGOveMbnQxnWYq+7EFORx9vz/CHrtzF36zWrW61E2UKw7sM1KPtIgeceVwXw==",
|
||||
"requires": {
|
||||
"array-iterate": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-position": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz",
|
||||
|
@ -18728,6 +19456,31 @@
|
|||
"unist-util-visit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-select": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-select/-/unist-util-select-1.5.0.tgz",
|
||||
"integrity": "sha1-qTwr6MD2U4J4A7gTMa3sKqJM2TM=",
|
||||
"requires": {
|
||||
"css-selector-parser": "^1.1.0",
|
||||
"debug": "^2.2.0",
|
||||
"nth-check": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
},
|
||||
"unist-util-stringify-position": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
|
||||
|
@ -18753,6 +19506,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"unist-util-visit-children": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-1.1.4.tgz",
|
||||
"integrity": "sha512-sA/nXwYRCQVRwZU2/tQWUqJ9JSFM1X3x7JIOsIgSzrFHcfVt6NkzDtKzyxg2cZWkCwGF9CO8x4QNZRJRMK8FeQ=="
|
||||
},
|
||||
"unist-util-visit-parents": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz",
|
||||
|
@ -19278,6 +20036,11 @@
|
|||
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
|
||||
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
|
||||
},
|
||||
"void-elements": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
|
||||
"integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
|
||||
},
|
||||
"warning": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||
|
|
|
@ -39,7 +39,9 @@
|
|||
"gatsby-plugin-sharp": "^2.6.19",
|
||||
"gatsby-source-filesystem": "^2.3.19",
|
||||
"gatsby-source-gravatar": "^1.0.0",
|
||||
"gatsby-transformer-remark": "^2.8.25",
|
||||
"gatsby-transformer-sharp": "^2.5.11",
|
||||
"i18next": "^19.6.0",
|
||||
"lodash": "^4.17.19",
|
||||
"moment": "^2.27.0",
|
||||
"nanoevents": "^5.1.8",
|
||||
|
@ -47,6 +49,7 @@
|
|||
"react-beautiful-dnd": "^13.0.0",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^11.7.0",
|
||||
"react-icons": "^3.10.0",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-scroll": "^1.8.0",
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
slug: "/blog/acing-video-interviews"
|
||||
date: "2020-07-15"
|
||||
title: "Acing Video Interviews"
|
||||
---
|
||||
|
||||
Today, as in-person interviewing has had to cease or slow due to restrictions during the Covid-19 pandemic, thousands of professionals are now needing to learn how to effectively interview in a new way using online platforms, such as Zoom, GoToMeeting and others.
|
||||
Here are 10 helpful tips for making the best impression you can in your video interview, and demonstrating that you’re a great fit for the role.
|
||||
|
||||
#### Be conscious of what’s in the view
|
||||
|
||||
While so many of us are now working remotely and using Zoom or other platforms for our meetings, we’ve grown more accustomed to seeing people in their home settings, and noticing their home décor, pets, family members, and other aspects of their personal life in the background.
|
||||
|
||||
For an interview, it’s fine to be in your home or living room but try to present whatever people see as neutral and professional as possible. You want to let yourself and your words, conversation and experience speak most powerfully about your qualifications and suitability for the job. And you want to avoid the chance that your interviewer will be distracted by what’s behind you, or perhaps have a negative reaction to any personal items (such as a plate of food behind you or a messy room) in your home.
|
||||
|
||||
#### Select professional attire
|
||||
|
||||
Even though you’re conducting the interview from your home, remember you are being judged and assessed for your fit for the role, so dress professionally, just as you would if you were meeting in person.
|
||||
|
||||
#### Ready your sound and video equipment
|
||||
|
||||
Make sure that you have working Wi-Fi, a strong connection, and a quality headset or microphone so there are no tech issues during your call. Invest in quality equipment for audio and video work.
|
||||
|
||||
#### Demonstrate positive body language and behavior
|
||||
|
||||
Just as in an in-person interview, you want to demonstrate through your voice and body language that you’re interested, engaged, and professional in demeanor and language. Make sure you are not distracted (with your pet, or by loud sounds or interruptions in your home, etc.). If you know there will be significant interruptions or distractions during the scheduled time for the interview, see if you can change it to a time when those interruptions are at minimum.
|
||||
|
||||
#### Engage the interviewer with eye contact and connection
|
||||
|
||||
Make sure you smile, come across as engaging and interested, and make strong eye contact. Try not to look away during your interview or look down at your notes too frequently. Your eye contact reveals a good deal about how you’re feeling and thinking about what the interviewer is sharing with you.
|
||||
|
||||
#### As with every interview, prepare, prepare, prepare
|
||||
|
||||
Be fully prepared for your interview. Do your research in advance, understand clearly from what the hiring manager has shared in advance what they’re looking for in the role and be ready to talk about why you’re potentially very well suited to it. Have in front of you some written sound bites and bullet points that speak to how you can leverage your great talents and abilities and hit the ground running successfully in this job.
|
||||
|
||||
#### Finally, remember that you’re talented, experienced and have so much value to offer and that the interview is a two-way street
|
||||
|
||||
Don’t lose sight of the fact that you have a great deal to offer and so much experience and talent to leverage to be of service in important ways. Make sure too that you understand this is a two-way street and you are interviewing the hiring manager about the role and the organization just as much as they are interviewing you. Have your list of questions that you want to make sure you cover so that you will get a strong sense of this role, the work, and if you would truly be a fit, both emotionally and functionally.
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
slug: "/blog/ats-friendly-resumes"
|
||||
date: "2020-07-14"
|
||||
title: "ATS-Friendly Resumes"
|
||||
---
|
||||
|
||||
An ATS (Applicant Tracking System) is software used by companies to help them quickly evaluate potential candidates for any given job opening.
|
||||
|
||||
ATS software automatically scans and processes each job application a company receives, and ranks them according to their relevant qualifications. It then produces a shortlist of qualified candidates to be reviewed by a hiring manager. If your resume doesn’t meet the requirements of a company’s ATS, your application will likely be rejected before a hiring manager even gets to look at it.
|
||||
|
||||
Applicant tracking systems (ATS) eliminate over 70% of applicants before their resume even reaches a hiring manager. Make sure your application makes the cut by learning how to write an ATS-friendly resume with our expert tips, examples, and ATS resume templates.
|
||||
|
||||
#### What is an ATS-compliant Resume?
|
||||
|
||||
An ATS-compliant resume is a resume designed specifically to make it easier for ATS software to find the information it’s looking for.
|
||||
|
||||
For example, this could mean using an easy-to-read resume format, or removing objects such as tables or images because they’re difficult for the ATS to parse. Resumes designed to be compliant with ATS software have a much higher chance of getting into the hands of a human hiring manager, which is one step closer to an interview.
|
||||
|
||||
#### How to design an ATS-friendly Resume
|
||||
|
||||
Here are six tips to help you make a more ATS-friendly resume and ultimately beat the applicant tracking system.
|
||||
|
||||
#### 1. Follow a standard resume format
|
||||
|
||||
Use a chronological resume to ensure the software can parse your experience section.
|
||||
|
||||
#### 2. Correctly label your section
|
||||
|
||||
By sticking to common headings, you prevent the bot from placing your qualifications under the wrong categories, or misreading your sections altogether.
|
||||
|
||||
#### 3. Include job-related keywords
|
||||
|
||||
To help determine whether your qualifications are relevant to the position, ATS software scans your resume for specific job-related resume keywords. To increase your chance of getting into the interview pool, look through the job listing for these words to include on your resume.
|
||||
|
||||
#### 4. Use an ATS-friendly resume template
|
||||
|
||||
Many job seekers use fancy resume templates to help them stand out from other candidates. However, templates with graphic elements, tables, or unique fonts are difficult for most ATS software to read.
|
||||
|
||||
#### 5. Use a common resume font
|
||||
|
||||
Most ATS software is programmed to read more common typefaces. Using an unusual or outdated looking font can result in your resume being rendered incorrectly, with large chunks of your information left unreadable.
|
||||
|
||||
#### 6. Save your resume as the proper file type
|
||||
|
||||
PDFs are the preferred file format for most companies today, and are easily understood by any modern applicant tracking system.
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
slug: "/blog/design-beautiful-resumes"
|
||||
date: "2020-07-13"
|
||||
title: "Designing Beautiful Resumes"
|
||||
---
|
||||
|
||||
Follow these 16 pro tips to help your design resume stand out from the crowd.
|
||||
|
||||
With designers fighting it out for every job that comes along, it's important that you stand out from the crowd. Whether you're just starting out or a seasoned pro applying for a better position, your design resume needs to be first rate for you to stand a chance of getting an interview.
|
||||
|
||||
For your design resume to really shine, you need to think carefully about how it's designed as well as what's written. Here, we'll cover both, as we walk you through the process of creating a stellar designer resume. You'll be landing that dream design job in no time.
|
||||
|
||||
#### 1. Avoid word processors
|
||||
|
||||
Microsoft Word might be okay if you’re applying for an admin position, but if you’re after a design job or something creative, its limited and idiosyncratic layout options just won't cut it. Art directors will be paying close attention to the layout of your resume as much as the content, so use InDesign CC or even Illustrator CC to design something special.
|
||||
|
||||
Whatever program you use to design your resume in, PDF is the best format to supply it in. This enables you to create good-looking documents that are completely cross-platform.
|
||||
|
||||
#### 2. Choose your fonts wisely
|
||||
|
||||
You’re a designer, so your resume should follow the latest trends in typography, right? Wrong! The aim of any resume should be legibility, so it’s generally a wise idea to stick to simple, readable fonts. You don't need to shell out lots of cash to find something suitable either – take a look at our list of the best free fonts for designers.
|
||||
|
||||
And if you would like to use more than one font, you can also check our perfect font pairings.
|
||||
|
||||
#### 3. Consider using colour
|
||||
|
||||
For most non-design-related jobs, a resume designed or printed in colour is probably a waste of time. However, for design positions, touches of colour are an acceptable way to add a discreet personal touch. Use colour carefully, however, and don't go over the top. Green type on a yellow page will stand out for all the wrong reasons. See our post on colour theory for more info on this.
|
||||
|
||||
#### 4. Be brief
|
||||
|
||||
Art directors do not have the time or the inclination to read your entire life story. Your resume should ideally fit onto one side of A4, and if it's any longer than two pages, you’re waffling and including too much stuff.
|
||||
|
||||
Don’t be tempted to mask a lack of experience with verbosity. Clean, well-laid-out resumes will always win over flabby ones – remember, the aim is to intrigue and impress. Point the recipient in the direction of an online portfolio to see more.
|
||||
|
||||
#### 5. Include your contact info
|
||||
|
||||
As a minimum, your resume should include your name and contact details, including your email address, phone number and online portfolio URL. Don't assume that because these are at the bottom of the email you sent, you don't need to include them. Make life easier for your potential employer.
|
||||
|
||||
This should be followed by a breakdown of your work experience, then your education. In both cases, this should be most recent first. Work experience should include dates, job title and a brief synopsis of your role. Don't bother including jobs you did years ago that are irrelevant to the job you're applying for. References are generally optional.
|
||||
|
||||
#### 6. Don't lie on your resume
|
||||
|
||||
We once received a resume from an unnamed individual who claimed to have created quite a stunning website. We would have been extremely impressed were it not for the fact that we had actually designed the site.
|
||||
|
||||
Needless to say, that resume went straight in the bin and the sender was rewarded with a strongly worded email. Honesty is always the best policy, as you stand a good chance of being found out if you start 'elaborating' in your resume.
|
||||
|
||||
#### 6. Include samples of work
|
||||
|
||||
By not including any samples of your work with your resume, you’re pretty much guaranteeing that the recipient will not consider you for the post. If you work with motion, stills are perfect, unless you’ve been specifically asked to include a showreel. On the other hand, don't go overboard with images – that's a job for your online graphic design portfolio, which you can provide a link to. Alternatively, you can provide a curated version of your portfolio in PDF format.
|
||||
|
||||
#### 7. Keep it simple
|
||||
|
||||
Unless you’re really confident and sure about what you’re doing, keep the typographic flourishes and fanciful designs at bay, ensure the layout is simple and clear and the information is cleanly presented. After all, the last thing you want is the recipient squinting because you thought dark grey text on a black background was a great idea.
|
||||
|
||||
#### 8. Show your personality
|
||||
|
||||
Simple does not have to mean dull. A resume is a reflection of your disposition and persona, and the recipient will be scanning it, consciously or not, for elements that distinguish your resume from the other hundreds they have to wade through. Make your resume stand out with an idiosyncratic design and personal touches... just don't overdo it.
|
||||
|
||||
#### 9. Beware the novelty approach to resumes
|
||||
|
||||
We’ve had resumes written on scrunched up paper; arriving in the form of a jigsaw; and playing cards. We’ve had giant resume posters, inflatable resumes and resumes crafted using delicate and complex paper engineering.
|
||||
|
||||
Off-the-wall resumes stick in the mind (you can see some of the best examples in our roundup of creative resumes) but they're a risky proposition. On the one hand you might appear like a creative thinker, on the other it might seem pretentious and excessive. It depends on the recipient.
|
||||
|
||||
#### 10. Don't plagiarise
|
||||
|
||||
We've all seen this clever resume concept... so don't try to pass it off as your idea
|
||||
|
||||
A surprising number of graduates see an inspiring resume design concept and copy it. What can they be thinking? We all have access to the same internet, and if a particularly inventive resume design has caught your eye, there's a strong chance it's been shared virally within the industry and will have caught the eye of your potential employer, too. Your resume should showcase your creativity, not someone else's.
|
||||
|
||||
#### 11. Use proper prints, not photocopies
|
||||
|
||||
Photocopies are cheap, but sadly they also look cheap, especially second and third generation copies. Type starts to break up, images are contrasty and full of noise, fingerprints and other blemishes begin to show up, and the results can look slightly askew. Fresh laser prints or sharp inkjet prints on the best quality paper available are the minimum standard. For more info, check out our designer's guide to printing.
|
||||
|
||||
#### 12. Demonstrate consistency
|
||||
|
||||
Real-world design projects are usually centred around a single, consistent theme or concept that runs throughout the logo, branding, literature and so on. Your résumé, portfolio and covering letter need to demonstrate the same consistency. For example, are bulleted lists presented in the same style across each of your pages? Is the colour scheme consistent?
|
||||
|
||||
#### 13. Spend time on the covering letter
|
||||
|
||||
Most of the time, when you apply for a job, your resume will need to be accompanied by a covering letter. This should look formal and business-like: this isn't the place to showcase your creativity and imagination. The text should complement the CV and it's best to keep it short and to the point (three paragraphs is a good rule of thumb).
|
||||
|
||||
Make it obvious you haven't just copied and pasted the same letter you've used to apply for a hundred other jobs. Write it in a way that's personal to the particular job and company you're applying for.
|
||||
|
||||
#### 14. Create multiple resumes
|
||||
|
||||
If you're applying for multiple jobs, you should create multiple resumes, each targeting a specific role and the kind of experience and skills the prospective employers are looking for. To take an obvious example, if the job specifically mentions InDesign as a requirement then you should make this first on your list of skills, and possibly expand the description of how and where you've used it.
|
||||
|
||||
#### 15. Check your spelling!
|
||||
|
||||
If you're getting this one wrong, you're in trouble
|
||||
If you're applying for a job as a designer, does it matter how well you write? The simple answer is yes. Spelling and grammar mistakes will make you appear uneducated, ignorant and/or lazy – and none of these represent the image you're trying to convey. So, always double-check your grammar and spelling, and get others to check it too (it's easy to miss your own mistakes)
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
slug: "/blog/jobs-during-covid-19"
|
||||
date: "2020-07-16"
|
||||
title: "Jobs During COVID-19"
|
||||
---
|
||||
|
||||
As companies move to remote work to fight the coronavirus pandemic and an increasing number of workers are being laid off or furloughed, you might be wondering if you should continue to send out resumes or just assume that no one is hiring for the foreseeable future. It’s true that economists are predicting a recession, but career experts say it’s best to keep networking and applying, provided you change your approach a bit to acknowledge these are uncertain times.
|
||||
|
||||
Be prepared for job openings to be put on hold or disappear, even if they’ve been open for a while. That doesn’t mean they won’t open up again in a few months. Landers admits she herself was getting ready to hire someone but decided to put that on hold for a few weeks.
|
||||
|
||||
With all that said, you can still be actively working on your job search. These tips will help you navigate the process during the pandemic and the accompanying economic slowdown.
|
||||
|
||||
#### 1. Consider How Urgent You Are Searching
|
||||
|
||||
While many industries have and will continue to be hit hard by the COVID-19 pandemic, others are still hiring. If you’re unemployed and need a stopgap, consider looking there or wherever else you can find an opportunity that makes sense for you—and pays the rent and puts food on the table—in the meantime.
|
||||
|
||||
#### 2. Get Comfortable Networking Online
|
||||
|
||||
Events will be cancelled for a while, so you’ll need to find a new networking strategy. Seek out like-minded professionals online and ask about virtual events.
|
||||
Look for professional groups to join on Facebook and LinkedIn. Both platforms offer a wide range of options with groups for every profession. For instance, if you’re looking for a job in marketing, you could join LinkedIn’s Global Marketing and Communications Professionals group. Join in the conversation, post and comment, and make yourself visible.
|
||||
|
||||
#### 3. Stay In Touch
|
||||
|
||||
Maybe you recently had a promising interview and a job offer seemed to be on the horizon, but now the company has moved to remote work and you haven’t heard from the hiring manager. What should you do? Check in with the hiring manager by email, acknowledging that they might be scrambling to help their employees get used to the new setup.
|
||||
|
||||
#### 4. Gather Intel
|
||||
|
||||
The COVID-19 crisis can provide a unique glimpse into company culture. Take note of how leadership deals with this emergency and treats its employees by following the company on social media and watching for any media coverage.
|
||||
You can mention what you read and listened to and use your specific knowledge to drive home how you could help the company achieve its goals if hired.
|
||||
|
||||
#### 5. Use the Time to Reflect
|
||||
|
||||
Job seekers often jump at the first available opportunity or go into their search without fully considering what they want to do next. Take advantage of the slowing job market by getting clarity about where you want to work and the type of role and title you're seeking.
|
||||
|
||||
#### 6. Boost Your Skills
|
||||
|
||||
Now is the perfect time to work on bolstering your qualifications, Moser says. Analyze job descriptions by listing each required skill and experience. Then consider whether you have that exact skill, if you have the skill but haven’t used it in a few years, or if you’re lacking the skill entirely. Use that information to determine what you need to brush up on to make yourself an even better candidate when the job market picks up again.
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* eslint-disable react/no-danger */
|
||||
import { graphql } from 'gatsby';
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import styles from './Blog.module.css';
|
||||
import Wrapper from './shared/Wrapper';
|
||||
import Hero from './landing/Hero';
|
||||
|
||||
export default function Template({ data }) {
|
||||
const { markdownRemark } = data;
|
||||
const { frontmatter, html } = markdownRemark;
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className="my-24 container">
|
||||
<Hero />
|
||||
|
||||
<Helmet>
|
||||
<title>{frontmatter.title} | Reactive Resume</title>
|
||||
<link
|
||||
rel="canonical"
|
||||
href={`https://rxresu.me/blog/${frontmatter.slug}`}
|
||||
/>
|
||||
</Helmet>
|
||||
|
||||
<h1 className="mt-16 text-3xl">{frontmatter.title}</h1>
|
||||
<h2>{frontmatter.date}</h2>
|
||||
<div
|
||||
className={styles.container}
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
/>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export const pageQuery = graphql`
|
||||
query($slug: String!) {
|
||||
markdownRemark(frontmatter: { slug: { eq: $slug } }) {
|
||||
html
|
||||
frontmatter {
|
||||
date(formatString: "MMMM DD, YYYY")
|
||||
slug
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,11 @@
|
|||
.container {
|
||||
@apply leading-loose my-8;
|
||||
}
|
||||
|
||||
.container p {
|
||||
@apply my-4;
|
||||
}
|
||||
|
||||
.container h4 {
|
||||
@apply font-medium text-xl;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import React, { memo } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from '../../../contexts/ResumeContext';
|
||||
import Castform from '../../../templates/Castform';
|
||||
import Celebi from '../../../templates/Celebi';
|
||||
|
@ -11,17 +12,20 @@ import styles from './Artboard.module.css';
|
|||
|
||||
const Artboard = () => {
|
||||
const state = useSelector();
|
||||
const { t } = useTranslation();
|
||||
const { id, name, metadata } = state;
|
||||
const { template } = metadata;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{name} | Reactive Resume</title>
|
||||
<title>
|
||||
{name} | {t('shared.appName')}
|
||||
</title>
|
||||
<link rel="canonical" href={`https://rxresu.me/app/builder/${id}`} />
|
||||
</Helmet>
|
||||
|
||||
<div id="artboard" className={styles.container}>
|
||||
<div className={styles.container}>
|
||||
{template === 'onyx' && <Onyx data={state} />}
|
||||
{template === 'pikachu' && <Pikachu data={state} />}
|
||||
{template === 'gengar' && <Gengar data={state} />}
|
||||
|
@ -29,7 +33,7 @@ const Artboard = () => {
|
|||
{template === 'glalie' && <Glalie data={state} />}
|
||||
{template === 'celebi' && <Celebi data={state} />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
width: 210mm;
|
||||
height: 297mm;
|
||||
zoom: 0.8;
|
||||
overflow: scroll;
|
||||
-moz-transform: scale(0.8);
|
||||
transform-origin: 50% 18%;
|
||||
overflow-y: scroll;
|
||||
box-shadow: var(--shadow);
|
||||
@apply bg-white rounded;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +1,43 @@
|
|||
import { Link } from 'gatsby';
|
||||
import React, { memo } from 'react';
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import sections from '../../../data/leftSections';
|
||||
import Avatar from '../../shared/Avatar';
|
||||
import Logo from '../../shared/Logo';
|
||||
import SectionIcon from '../../shared/SectionIcon';
|
||||
import styles from './LeftNavbar.module.css';
|
||||
|
||||
const LeftNavbar = () => (
|
||||
<div className={styles.container}>
|
||||
<Tooltip title="Go Back to Dashboard" placement="right">
|
||||
<div>
|
||||
<Link to="/app/dashboard">
|
||||
<Logo size="40px" />
|
||||
</Link>
|
||||
const LeftNavbar = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Tooltip title={t('builder.tooltips.backToDashboard')} placement="right">
|
||||
<div>
|
||||
<Link to="/app/dashboard">
|
||||
<Logo size="40px" />
|
||||
</Link>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 text-primary-500">
|
||||
{sections.map((x) => (
|
||||
<SectionIcon
|
||||
key={x.id}
|
||||
section={x}
|
||||
containerId="LeftSidebar"
|
||||
tooltipPlacement="right"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<hr className="my-6" />
|
||||
<hr className="mt-auto my-6" />
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 text-primary-500">
|
||||
{sections.map((x) => (
|
||||
<SectionIcon
|
||||
key={x.id}
|
||||
section={x}
|
||||
containerId="LeftSidebar"
|
||||
tooltipPlacement="right"
|
||||
/>
|
||||
))}
|
||||
<Avatar />
|
||||
</div>
|
||||
|
||||
<hr className="mt-auto my-6" />
|
||||
|
||||
<Avatar />
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(LeftNavbar);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { Fragment, memo } from 'react';
|
||||
import { Element } from 'react-scroll';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import sections from '../../../data/leftSections';
|
||||
import LeftNavbar from './LeftNavbar';
|
||||
import styles from './LeftSidebar.module.css';
|
||||
|
@ -48,18 +49,24 @@ const getComponent = (id) => {
|
|||
};
|
||||
|
||||
const LeftSidebar = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<LeftNavbar />
|
||||
|
||||
<div id="LeftSidebar" className={styles.container}>
|
||||
{sections.map(({ id, name, event }) => {
|
||||
{sections.map(({ id, event }) => {
|
||||
const Component = getComponent(id);
|
||||
|
||||
return (
|
||||
<Fragment key={id}>
|
||||
<Element name={id}>
|
||||
<Component id={id} name={name} event={event} />
|
||||
<Component
|
||||
id={id}
|
||||
name={t(`builder.sections.${id}`)}
|
||||
event={event}
|
||||
/>
|
||||
</Element>
|
||||
<hr />
|
||||
</Fragment>
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Heading from '../../../shared/Heading';
|
||||
import Input from '../../../shared/Input';
|
||||
|
||||
const Objective = () => {
|
||||
const Objective = ({ name }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Objective</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<Input type="textarea" label="Objective" path="objective.body" />
|
||||
<Input
|
||||
type="textarea"
|
||||
label={t('shared.forms.summary')}
|
||||
path="objective.body"
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,45 +1,80 @@
|
|||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Heading from '../../../shared/Heading';
|
||||
import Input from '../../../shared/Input';
|
||||
import PhotoUpload from '../../../shared/PhotoUpload';
|
||||
|
||||
const Profile = () => {
|
||||
const Profile = ({ name }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Profile</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<PhotoUpload />
|
||||
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<Input name="firstName" label="First Name" path="profile.firstName" />
|
||||
<Input name="lastName" label="Last Name" path="profile.lastName" />
|
||||
<Input
|
||||
name="firstName"
|
||||
label={t('builder.profile.firstName')}
|
||||
path="profile.firstName"
|
||||
/>
|
||||
<Input
|
||||
name="lastName"
|
||||
label={t('builder.profile.lastName')}
|
||||
path="profile.lastName"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Input name="subtitle" label="Subtitle" path="profile.subtitle" />
|
||||
<Input
|
||||
name="subtitle"
|
||||
label={t('shared.forms.subtitle')}
|
||||
path="profile.subtitle"
|
||||
/>
|
||||
|
||||
<hr />
|
||||
|
||||
<Input
|
||||
name="addressLine1"
|
||||
label="Address Line 1"
|
||||
label={t('builder.profile.address.line1')}
|
||||
path="profile.address.line1"
|
||||
/>
|
||||
<Input
|
||||
name="addressLine2"
|
||||
label="Address Line 2"
|
||||
label={t('builder.profile.address.line2')}
|
||||
path="profile.address.line2"
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<Input name="city" label="City" path="profile.address.city" />
|
||||
<Input name="pincode" label="Pincode" path="profile.address.pincode" />
|
||||
<Input
|
||||
name="city"
|
||||
label={t('builder.profile.address.city')}
|
||||
path="profile.address.city"
|
||||
/>
|
||||
<Input
|
||||
name="pincode"
|
||||
label={t('builder.profile.address.pincode')}
|
||||
path="profile.address.pincode"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<Input name="phone" label="Phone Number" path="profile.phone" />
|
||||
<Input name="website" label="Website" path="profile.website" />
|
||||
<Input name="email" label="Email Address" path="profile.email" />
|
||||
<Input
|
||||
name="phone"
|
||||
label={t('shared.forms.phone')}
|
||||
path="profile.phone"
|
||||
/>
|
||||
<Input
|
||||
name="website"
|
||||
label={t('shared.forms.website')}
|
||||
path="profile.website"
|
||||
/>
|
||||
<Input
|
||||
name="email"
|
||||
label={t('shared.forms.email')}
|
||||
path="profile.email"
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const EmptyList = () => (
|
||||
<div className="py-6 opacity-75 text-center">This list is empty.</div>
|
||||
);
|
||||
const EmptyList = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="py-6 opacity-75 text-center">{t('builder.emptyList')}</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(EmptyList);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Menu, MenuItem } from '@material-ui/core';
|
||||
import React, { memo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IoIosArrowDown, IoIosArrowUp } from 'react-icons/io';
|
||||
import { MdMoreVert } from 'react-icons/md';
|
||||
import { useDispatch } from '../../../contexts/ResumeContext';
|
||||
|
@ -15,8 +16,9 @@ const ListItem = ({
|
|||
isLast,
|
||||
onEdit,
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
const handleClick = (event) => setAnchorEl(event.currentTarget);
|
||||
|
||||
|
@ -98,9 +100,11 @@ const ListItem = ({
|
|||
<IoIosArrowDown size="18px" />
|
||||
</MenuItem>
|
||||
</div>
|
||||
<MenuItem onClick={handleEdit}>Edit</MenuItem>
|
||||
<MenuItem onClick={handleEdit}>{t('shared.buttons.edit')}</MenuItem>
|
||||
<MenuItem onClick={handleDelete}>
|
||||
<span className="text-red-600 font-medium">Delete</span>
|
||||
<span className="text-red-600 font-medium">
|
||||
{t('shared.buttons.delete')}
|
||||
</span>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import React, { Fragment, memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Element } from 'react-scroll';
|
||||
import sections from '../../../data/rightSections';
|
||||
import RightNavbar from './RightNavbar';
|
||||
import styles from './RightSidebar.module.css';
|
||||
import About from './sections/About';
|
||||
import Actions from './sections/Actions';
|
||||
import Colors from './sections/Colors';
|
||||
import Fonts from './sections/Fonts';
|
||||
import Layout from './sections/Layout';
|
||||
import Templates from './sections/Templates';
|
||||
import Actions from './sections/Actions';
|
||||
import Settings from './sections/Settings';
|
||||
import About from './sections/About';
|
||||
import Templates from './sections/Templates';
|
||||
|
||||
const getComponent = (id) => {
|
||||
switch (id) {
|
||||
|
@ -33,16 +34,22 @@ const getComponent = (id) => {
|
|||
};
|
||||
|
||||
const RightSidebar = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<div id="RightSidebar" className={styles.container}>
|
||||
{sections.map(({ id, name, event }) => {
|
||||
{sections.map(({ id, event }) => {
|
||||
const Component = getComponent(id);
|
||||
|
||||
return (
|
||||
<Fragment key={id}>
|
||||
<Element name={id}>
|
||||
<Component id={id} name={name} event={event} />
|
||||
<Component
|
||||
id={id}
|
||||
name={t(`builder.sections.${id}`)}
|
||||
event={event}
|
||||
/>
|
||||
</Element>
|
||||
<hr />
|
||||
</Fragment>
|
||||
|
|
|
@ -1,31 +1,25 @@
|
|||
import React, { memo } from 'react';
|
||||
import { FaCoffee, FaBug } from 'react-icons/fa';
|
||||
import { FaCoffee, FaBug, FaExternalLinkAlt } from 'react-icons/fa';
|
||||
import { MdCode } from 'react-icons/md';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import Button from '../../../shared/Button';
|
||||
import Heading from '../../../shared/Heading';
|
||||
import styles from './About.module.css';
|
||||
|
||||
const About = () => {
|
||||
const About = ({ name }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>About</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Donate to Reactive Resume</h5>
|
||||
<h5>{t('builder.about.donate.heading')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
As you know, every nook and cranny of this app is free and
|
||||
open-source, but servers don't pay for themselves.
|
||||
</p>
|
||||
|
||||
<p className="leading-loose">
|
||||
I try to do what I can, but if you found the app helpful, or
|
||||
you're in a better position than the others who depend on this
|
||||
project for their first job, please consider donating{' '}
|
||||
<span className="font-semibold">
|
||||
as little as $5 to help keep the project alive
|
||||
</span>{' '}
|
||||
:)
|
||||
<Trans t={t} i18nKey="builder.about.donate.text">
|
||||
A<span className="font-bold">B</span>C
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
|
@ -34,19 +28,15 @@ const About = () => {
|
|||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<Button icon={FaCoffee}>Buy me a coffee!</Button>
|
||||
<Button icon={FaCoffee}>{t('builder.about.donate.button')}</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Bug? Feature Request?</h5>
|
||||
<h5>{t('builder.about.bugFeature.heading')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
Something halting your progress from making a resume? Found a pesky
|
||||
bug that just won't quit? Talk about it on the GitHub Issues
|
||||
section, or send me and email using the actions below.
|
||||
</p>
|
||||
<p className="leading-loose">{t('builder.about.bugFeature.text')}</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<a
|
||||
|
@ -54,19 +44,27 @@ const About = () => {
|
|||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<Button icon={FaBug}>Raise an Issue</Button>
|
||||
<Button icon={FaBug}>{t('builder.about.bugFeature.button')}</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Source Code</h5>
|
||||
<h5>{t('builder.about.appreciate.heading')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
Want to run the project from its source? Are you a developer willing
|
||||
to contribute to the open-source development of this project? Click
|
||||
the button below.
|
||||
</p>
|
||||
<p className="leading-loose">{t('builder.about.appreciate.text')}</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<a href="https://amruthpillai.com" rel="noreferrer" target="_blank">
|
||||
<Button icon={FaExternalLinkAlt}>amruthpillai.com</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>{t('builder.about.sourceCode.heading')}</h5>
|
||||
|
||||
<p className="leading-loose">{t('builder.about.sourceCode.text')}</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<a
|
||||
|
@ -74,38 +72,20 @@ const About = () => {
|
|||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<Button icon={MdCode}>GitHub Repo</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>License Information</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
The project is governed under the MIT License, which you can read more
|
||||
about below. Basically, you are allowed to use the project anywhere
|
||||
provided you give credits to the original author.
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<a
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/blob/master/LICENSE"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<Button icon={MdCode}>MIT License</Button>
|
||||
<Button icon={MdCode}>
|
||||
{t('builder.about.sourceCode.button')}
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-4 text-center opacity-50 text-sm">
|
||||
<p>
|
||||
Made with Love by{' '}
|
||||
<Trans t={t} i18nKey="builder.about.footer">
|
||||
A
|
||||
<a href="https://amruthpillai.com" rel="noreferrer" target="_blank">
|
||||
Amruth Pillai
|
||||
B
|
||||
</a>
|
||||
</p>
|
||||
</Trans>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { memo, useContext, useState } from 'react';
|
||||
import { FaFileExport, FaFileImport } from 'react-icons/fa';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ModalContext from '../../../../contexts/ModalContext';
|
||||
import { useDispatch, useSelector } from '../../../../contexts/ResumeContext';
|
||||
import Button from '../../../shared/Button';
|
||||
|
@ -7,9 +8,15 @@ import Heading from '../../../shared/Heading';
|
|||
import Input from '../../../shared/Input';
|
||||
import styles from './Actions.module.css';
|
||||
|
||||
const Actions = () => {
|
||||
const [loadDemoText, setLoadDemoText] = useState('Load Demo Data');
|
||||
const [resetText, setResetText] = useState('Reset Everything');
|
||||
const Actions = ({ name }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [loadDemoText, setLoadDemoText] = useState(
|
||||
t('builder.actions.loadDemoData.button'),
|
||||
);
|
||||
const [resetText, setResetText] = useState(
|
||||
t('builder.actions.resetEverything.button'),
|
||||
);
|
||||
|
||||
const state = useSelector();
|
||||
const dispatch = useDispatch();
|
||||
|
@ -31,66 +38,57 @@ const Actions = () => {
|
|||
};
|
||||
|
||||
const handleLoadDemo = () => {
|
||||
if (loadDemoText === 'Load Demo Data') {
|
||||
setLoadDemoText('Are you sure?');
|
||||
if (loadDemoText === t('builder.actions.loadDemoData.button')) {
|
||||
setLoadDemoText(t('shared.buttons.confirmation'));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({ type: 'load_demo_data' });
|
||||
setLoadDemoText('Load Demo Data');
|
||||
setLoadDemoText(t('builder.actions.loadDemoData.button'));
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
if (resetText === 'Reset Everything') {
|
||||
setResetText('Are you sure?');
|
||||
if (resetText === t('builder.actions.resetEverything.button')) {
|
||||
setResetText(t('shared.buttons.confirmation'));
|
||||
return;
|
||||
}
|
||||
|
||||
setResetText('Reset Everything');
|
||||
setResetText(t('builder.actions.resetEverything.button'));
|
||||
dispatch({ type: 'reset_data' });
|
||||
};
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Actions</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Import Your Resume</h5>
|
||||
<h5>{t('builder.actions.import.heading')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
You can import your information from various sources like JSON Resume
|
||||
or your LinkedIn to autofill most of the data for your resume.
|
||||
</p>
|
||||
<p className="leading-loose">{t('builder.actions.import.text')}</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<Button icon={FaFileImport} onClick={handleImport}>
|
||||
Import
|
||||
{t('builder.actions.import.button')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Export Your Resume</h5>
|
||||
<h5>{t('builder.actions.export.heading')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
Export your resume as a PDF to share with recruiters or a JSON that
|
||||
you will be able to import back onto this app on another computer.
|
||||
</p>
|
||||
<p className="leading-loose">{t('builder.actions.export.text')}</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<Button icon={FaFileExport} onClick={handleExport}>
|
||||
Export
|
||||
{t('builder.actions.export.button')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Share Your Resume</h5>
|
||||
<h5>{t('builder.actions.share.heading')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
The link below will be accessible publicly if you choose to share it,
|
||||
and viewers would see the latest version of your resume at any time.
|
||||
</p>
|
||||
<p className="leading-loose">{t('builder.actions.export.text')}</p>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
|
@ -102,11 +100,10 @@ const Actions = () => {
|
|||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Load Demo Data</h5>
|
||||
<h5>{t('builder.actions.loadDemoData.button')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
Unclear on what to do with a fresh blank page? Load some demo data to
|
||||
see how a resume should look and you can start editing from there.
|
||||
{t('builder.actions.loadDemoData.text')}
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
|
@ -115,11 +112,10 @@ const Actions = () => {
|
|||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Reset Everything</h5>
|
||||
<h5>{t('builder.actions.resetEverything.button')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
Feels like you made too many mistakes? No worries, clear everything
|
||||
with just one click, but be careful if there are no backups.
|
||||
{t('builder.actions.resetEverything.text')}
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable jsx-a11y/control-has-associated-label */
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from '../../../../contexts/ResumeContext';
|
||||
import colorOptions from '../../../../data/colorOptions';
|
||||
import { handleKeyUp } from '../../../../utils';
|
||||
|
@ -7,8 +8,9 @@ import Heading from '../../../shared/Heading';
|
|||
import Input from '../../../shared/Input';
|
||||
import styles from './Colors.module.css';
|
||||
|
||||
const Colors = () => {
|
||||
const Colors = ({ name }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClick = (value) => {
|
||||
dispatch({
|
||||
|
@ -22,7 +24,7 @@ const Colors = () => {
|
|||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Colors</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<div className="mb-6 grid grid-cols-8 col-gap-2 row-gap-6">
|
||||
{colorOptions.map((color) => (
|
||||
|
@ -41,7 +43,7 @@ const Colors = () => {
|
|||
<Input
|
||||
type="color"
|
||||
name="primary"
|
||||
label="Primary Color"
|
||||
label={t('builder.colors.primary')}
|
||||
placeholder="#FF4081"
|
||||
path="metadata.colors.primary"
|
||||
/>
|
||||
|
@ -49,7 +51,7 @@ const Colors = () => {
|
|||
<Input
|
||||
type="color"
|
||||
name="text"
|
||||
label="Text Color"
|
||||
label={t('builder.colors.text')}
|
||||
placeholder="#444444"
|
||||
path="metadata.colors.text"
|
||||
/>
|
||||
|
@ -57,7 +59,7 @@ const Colors = () => {
|
|||
<Input
|
||||
type="color"
|
||||
name="background"
|
||||
label="Background Color"
|
||||
label={t('builder.colors.background')}
|
||||
placeholder="#FFFFFF"
|
||||
path="metadata.colors.background"
|
||||
/>
|
||||
|
|
|
@ -6,7 +6,7 @@ import { handleKeyUp } from '../../../../utils';
|
|||
import Heading from '../../../shared/Heading';
|
||||
import styles from './Fonts.module.css';
|
||||
|
||||
const Fonts = () => {
|
||||
const Fonts = ({ name }) => {
|
||||
const dispatch = useDispatch();
|
||||
const font = useSelector('metadata.font');
|
||||
|
||||
|
@ -22,7 +22,7 @@ const Fonts = () => {
|
|||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Fonts</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
{fontOptions.map((x) => (
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import React, { memo, useState } from 'react';
|
||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from '../../../../contexts/ResumeContext';
|
||||
import { move, reorder } from '../../../../utils';
|
||||
import Button from '../../../shared/Button';
|
||||
import Heading from '../../../shared/Heading';
|
||||
import styles from './Layout.module.css';
|
||||
|
||||
const Layout = () => {
|
||||
const [resetLayoutText, setResetLayoutText] = useState('Reset Layout');
|
||||
const Layout = ({ name }) => {
|
||||
const { t } = useTranslation();
|
||||
const [resetLayoutText, setResetLayoutText] = useState(
|
||||
t('builder.layout.reset'),
|
||||
);
|
||||
|
||||
const template = useSelector('metadata.template');
|
||||
const blocks = useSelector(`metadata.layout.${template}`, [[]]);
|
||||
|
@ -49,22 +53,21 @@ const Layout = () => {
|
|||
};
|
||||
|
||||
const handleResetLayout = () => {
|
||||
if (resetLayoutText === 'Reset Layout') {
|
||||
setResetLayoutText('Are you sure?');
|
||||
if (resetLayoutText === t('builder.layout.reset')) {
|
||||
setResetLayoutText(t('shared.buttons.confirmation'));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({ type: 'reset_layout' });
|
||||
setResetLayoutText('Reset Layout');
|
||||
setResetLayoutText(t('builder.layout.reset'));
|
||||
};
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Layout</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<p className="leading-loose">
|
||||
This template supports {blocks.length} blocks. You can re-order or move
|
||||
sections by dragging/dropping the section names across lists.
|
||||
{t('builder.layout.text', { count: blocks.length })}
|
||||
</p>
|
||||
|
||||
<div className={`grid gap-8 grid-cols-${blocks.length}`}>
|
||||
|
@ -79,7 +82,7 @@ const Layout = () => {
|
|||
>
|
||||
<div className="grid gap-3">
|
||||
<span className="uppercase font-semibold text-xs">
|
||||
Block {ind + 1}
|
||||
{t('builder.layout.block')} {ind + 1}
|
||||
</span>
|
||||
{el.map((item, index) => (
|
||||
<Draggable key={item} index={index} draggableId={item}>
|
||||
|
@ -90,7 +93,7 @@ const Layout = () => {
|
|||
{...dragProvided.draggableProps}
|
||||
{...dragProvided.dragHandleProps}
|
||||
>
|
||||
{item}
|
||||
{t(`builder.sections.${item}`)}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
}
|
||||
|
||||
.draggable {
|
||||
@apply px-4 py-2 capitalize font-medium rounded bg-primary-200;
|
||||
@apply px-4 py-2 font-medium rounded bg-primary-200;
|
||||
}
|
||||
|
|
|
@ -1,30 +1,42 @@
|
|||
import React, { memo, useContext, useState } from 'react';
|
||||
import { FaAngleDown } from 'react-icons/fa';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import UserContext from '../../../../contexts/UserContext';
|
||||
import Button from '../../../shared/Button';
|
||||
import Heading from '../../../shared/Heading';
|
||||
import styles from './Settings.module.css';
|
||||
import Input from '../../../shared/Input';
|
||||
import ThemeContext from '../../../../contexts/ThemeContext';
|
||||
import SettingsContext from '../../../../contexts/SettingsContext';
|
||||
import themeConfig from '../../../../data/themeConfig';
|
||||
import languageConfig from '../../../../data/languageConfig';
|
||||
import { languages } from '../../../../i18n';
|
||||
import { useDispatch } from '../../../../contexts/ResumeContext';
|
||||
|
||||
const Settings = () => {
|
||||
const [deleteText, setDeleteText] = useState('Delete Account');
|
||||
const Settings = ({ name }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [deleteText, setDeleteText] = useState(
|
||||
t('builder.settings.dangerZone.button'),
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { deleteAccount } = useContext(UserContext);
|
||||
const { theme, setTheme } = useContext(ThemeContext);
|
||||
const { theme, setTheme, language, setLanguage } = useContext(
|
||||
SettingsContext,
|
||||
);
|
||||
|
||||
const handleChangeTheme = (e) => {
|
||||
setTheme(e.target.value);
|
||||
};
|
||||
|
||||
const handleChangeLanguage = (e) => {
|
||||
console.log(e.target.value);
|
||||
const lang = e.target.value;
|
||||
setLanguage(lang);
|
||||
dispatch({ type: 'change_language', payload: lang });
|
||||
};
|
||||
|
||||
const handleDeleteAccount = () => {
|
||||
if (deleteText === 'Delete Account') {
|
||||
setDeleteText('Are you sure?');
|
||||
if (deleteText === t('builder.settings.dangerZone.button')) {
|
||||
setDeleteText(t('shared.buttons.confirmation'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -36,10 +48,10 @@ const Settings = () => {
|
|||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Settings</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<Input
|
||||
label="Theme"
|
||||
label={t('builder.settings.theme')}
|
||||
type="dropdown"
|
||||
options={Object.keys(themeConfig)}
|
||||
value={theme}
|
||||
|
@ -47,11 +59,11 @@ const Settings = () => {
|
|||
/>
|
||||
|
||||
<label>
|
||||
<span>Language</span>
|
||||
<span>{t('builder.settings.language')}</span>
|
||||
<div className="relative grid items-center">
|
||||
<select onChange={handleChangeLanguage}>
|
||||
{languageConfig.map((x) => (
|
||||
<option key={x.key} value={x.key}>
|
||||
<select onChange={handleChangeLanguage} value={language}>
|
||||
{languages.map((x) => (
|
||||
<option key={x.code} value={x.code}>
|
||||
{x.name}
|
||||
</option>
|
||||
))}
|
||||
|
@ -65,26 +77,23 @@ const Settings = () => {
|
|||
</label>
|
||||
|
||||
<p className="text-sm leading-loose">
|
||||
If you would like to contribute by providing translations to Reactive
|
||||
Resume in your language,{' '}
|
||||
<a
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/blob/master/TRANSLATING.md"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
please visit this link
|
||||
</a>
|
||||
.
|
||||
<Trans t={t} i18nKey="builder.settings.translate">
|
||||
A
|
||||
<a
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/blob/master/TRANSLATING.md"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
B
|
||||
</a>
|
||||
C
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Danger Zone</h5>
|
||||
<h5>{t('builder.settings.dangerZone.heading')}</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
If you would like to delete your account and erase all your resumes,
|
||||
it’s just one button away. Please be weary as this is an irreversible
|
||||
process.
|
||||
</p>
|
||||
<p className="leading-loose">{t('builder.settings.dangerZone.text')}</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<Button isDelete onClick={handleDeleteAccount}>
|
||||
|
|
|
@ -8,7 +8,7 @@ import { handleKeyUp } from '../../../../utils';
|
|||
import Heading from '../../../shared/Heading';
|
||||
import styles from './Templates.module.css';
|
||||
|
||||
const Templates = () => {
|
||||
const Templates = ({ name }) => {
|
||||
const dispatch = useDispatch();
|
||||
const template = useSelector('metadata.template');
|
||||
|
||||
|
@ -71,7 +71,7 @@ const Templates = () => {
|
|||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Templates</Heading>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
{templateOptions.map((x) => (
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import React, { memo, useContext } from 'react';
|
||||
import { MdAdd } from 'react-icons/md';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ModalContext from '../../contexts/ModalContext';
|
||||
import { handleKeyUp } from '../../utils';
|
||||
import styles from './CreateResume.module.css';
|
||||
|
||||
const CreateResume = () => {
|
||||
const { t } = useTranslation();
|
||||
const { emitter, events } = useContext(ModalContext);
|
||||
|
||||
const handleClick = () => emitter.emit(events.CREATE_RESUME_MODAL);
|
||||
|
@ -24,7 +26,7 @@ const CreateResume = () => {
|
|||
<MdAdd size="48" />
|
||||
</div>
|
||||
<div className={styles.meta}>
|
||||
<p>Create Resume</p>
|
||||
<p>{t('dashboard.createResume')}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,11 +4,13 @@ import moment from 'moment';
|
|||
import React, { useContext, useState } from 'react';
|
||||
import { MdMoreHoriz, MdOpenInNew } from 'react-icons/md';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DatabaseContext from '../../contexts/DatabaseContext';
|
||||
import ModalContext from '../../contexts/ModalContext';
|
||||
import styles from './ResumePreview.module.css';
|
||||
|
||||
const ResumePreview = ({ resume }) => {
|
||||
const { t } = useTranslation();
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const { emitter, events } = useContext(ModalContext);
|
||||
const { duplicateResume, deleteResume } = useContext(DatabaseContext);
|
||||
|
@ -31,7 +33,7 @@ const ResumePreview = ({ resume }) => {
|
|||
|
||||
const handleDelete = () => {
|
||||
deleteResume(resume.id);
|
||||
toast(`${resume.name} was deleted successfully`);
|
||||
toast(t('dashboard.toasts.deleted', { name: resume.name }));
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
|
@ -42,10 +44,7 @@ const ResumePreview = ({ resume }) => {
|
|||
return (
|
||||
<div className={styles.resume}>
|
||||
<div className={styles.backdrop}>
|
||||
<img
|
||||
src="https://source.unsplash.com/random/210x297"
|
||||
alt="Resume Preview"
|
||||
/>
|
||||
<img src={resume.preview} alt={resume.name} />
|
||||
</div>
|
||||
<div className={styles.page}>
|
||||
<MdOpenInNew
|
||||
|
@ -67,17 +66,27 @@ const ResumePreview = ({ resume }) => {
|
|||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
>
|
||||
<MenuItem onClick={handleDuplicate}>Duplicate</MenuItem>
|
||||
<MenuItem onClick={handleRename}>Rename</MenuItem>
|
||||
<MenuItem onClick={handleDuplicate}>
|
||||
{t('dashboard.buttons.duplicate')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleRename}>
|
||||
{t('dashboard.buttons.rename')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleDelete}>
|
||||
<span className="text-red-600 font-medium">Delete</span>
|
||||
<span className="text-red-600 font-medium">
|
||||
{t('shared.buttons.delete')}
|
||||
</span>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
<div className={styles.meta}>
|
||||
<span>{resume.name}</span>
|
||||
{resume.updatedAt && (
|
||||
<span>Last updated {moment(resume.updatedAt).fromNow()}</span>
|
||||
<span>
|
||||
{t('dashboard.lastUpdated', {
|
||||
timestamp: moment(resume.updatedAt).fromNow(),
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { navigate } from 'gatsby';
|
||||
import TypeIt from 'typeit-react';
|
||||
import React, { memo, useContext } from 'react';
|
||||
import { FaGithub } from 'react-icons/fa';
|
||||
import TypeIt from 'typeit-react';
|
||||
import { Link } from '@reach/router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ModalContext from '../../contexts/ModalContext';
|
||||
import UserContext from '../../contexts/UserContext';
|
||||
import Button from '../shared/Button';
|
||||
import Logo from '../shared/Logo';
|
||||
|
||||
const Hero = () => {
|
||||
const { t } = useTranslation();
|
||||
const { emitter, events } = useContext(ModalContext);
|
||||
const { user, loading } = useContext(UserContext);
|
||||
|
||||
|
@ -17,10 +19,11 @@ const Hero = () => {
|
|||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Logo className="shadow-lg" size="256px" />
|
||||
<Link to="/">
|
||||
<Logo className="shadow-lg" size="256px" />
|
||||
</Link>
|
||||
|
||||
<div className="ml-12">
|
||||
<h1 className="sr-only">Reactive Resume</h1>
|
||||
<div className="text-5xl font-bold">
|
||||
<TypeIt
|
||||
getBeforeInit={(instance) => {
|
||||
|
@ -36,29 +39,19 @@ const Hero = () => {
|
|||
/>
|
||||
</div>
|
||||
<h2 className="mt-1 text-3xl text-primary-500">
|
||||
A free and open-source resume builder.
|
||||
{t('shared.shortDescription')}
|
||||
</h2>
|
||||
|
||||
<div className="mt-12 flex">
|
||||
{user ? (
|
||||
<Button onClick={handleGotoApp} isLoading={loading}>
|
||||
Go to App
|
||||
{t('landing.hero.goToApp')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={handleLogin} isLoading={loading}>
|
||||
Login
|
||||
{t('shared.buttons.login')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<a
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<Button outline icon={FaGithub} className="ml-8">
|
||||
Source Code
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,11 +2,13 @@ import cx from 'classnames';
|
|||
import { toUrl } from 'gatsby-source-gravatar';
|
||||
import React, { memo, useContext, useMemo, useState } from 'react';
|
||||
import { Menu, MenuItem } from '@material-ui/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import UserContext from '../../contexts/UserContext';
|
||||
import styles from './Avatar.module.css';
|
||||
import { handleKeyUp } from '../../utils';
|
||||
|
||||
const Avatar = ({ className }) => {
|
||||
const { t } = useTranslation();
|
||||
const { user, logout } = useContext(UserContext);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
|
@ -43,7 +45,7 @@ const Avatar = ({ className }) => {
|
|||
onClose={handleClose}
|
||||
open={Boolean(anchorEl)}
|
||||
>
|
||||
<MenuItem onClick={handleLogout}>Logout</MenuItem>
|
||||
<MenuItem onClick={handleLogout}>{t('shared.buttons.logout')}</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import cx from 'classnames';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { handleKeyUp } from '../../utils';
|
||||
import styles from './Button.module.css';
|
||||
|
||||
|
@ -12,6 +13,7 @@ const Button = ({
|
|||
isLoading,
|
||||
isDelete,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const Icon = icon;
|
||||
|
||||
return (
|
||||
|
@ -24,7 +26,7 @@ const Button = ({
|
|||
})}
|
||||
>
|
||||
{icon && <Icon size="14" className="mr-3" />}
|
||||
{isLoading ? 'Loading...' : children}
|
||||
{isLoading ? t('shared.buttons.loading') : children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { memo } from 'react';
|
||||
|
||||
const Heading = ({ children }) => {
|
||||
return <h2 className="text-4xl">{children}</h2>;
|
||||
return <h2 className="text-4xl focus:outline-none">{children}</h2>;
|
||||
};
|
||||
|
||||
export default memo(Heading);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import cx from 'classnames';
|
||||
import { isFunction } from 'lodash';
|
||||
import React, { memo, useEffect, useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { FaAngleDown } from 'react-icons/fa';
|
||||
import { MdClose, MdOpenInNew } from 'react-icons/md';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
@ -24,6 +25,7 @@ const Input = ({
|
|||
placeholder,
|
||||
type = 'text',
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [uuid, setUuid] = useState(null);
|
||||
const stateValue = useSelector(path, '');
|
||||
const dispatch = useDispatch();
|
||||
|
@ -51,7 +53,9 @@ const Input = ({
|
|||
<span>
|
||||
{label}{' '}
|
||||
{isRequired && (
|
||||
<span className="opacity-75 font-normal lowercase">(Required)</span>
|
||||
<span className="opacity-75 font-normal lowercase">
|
||||
({t('shared.forms.required')})
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
|
||||
|
@ -92,15 +96,17 @@ const Input = ({
|
|||
/>
|
||||
|
||||
<p className="mt-2 text-sm opacity-75">
|
||||
This text block supports{' '}
|
||||
<a
|
||||
href="https://www.markdownguide.org/basic-syntax/"
|
||||
className="text-blue-600"
|
||||
target="blank"
|
||||
>
|
||||
markdown
|
||||
</a>
|
||||
.
|
||||
<Trans t={t} i18nKey="shared.forms.markdown">
|
||||
A
|
||||
<a
|
||||
href="https://www.markdownguide.org/basic-syntax/"
|
||||
className="text-blue-600"
|
||||
target="blank"
|
||||
>
|
||||
B
|
||||
</a>
|
||||
C
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Tooltip } from '@material-ui/core';
|
||||
import React, { memo, useContext, useRef } from 'react';
|
||||
import { MdFileUpload } from 'react-icons/md';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import StorageContext from '../../contexts/StorageContext';
|
||||
import { handleKeyUp } from '../../utils';
|
||||
import Input from './Input';
|
||||
|
@ -8,6 +9,7 @@ import styles from './PhotoUpload.module.css';
|
|||
|
||||
const PhotoUpload = () => {
|
||||
const fileInputRef = useRef(null);
|
||||
const { t } = useTranslation();
|
||||
const { uploadPhotograph } = useContext(StorageContext);
|
||||
|
||||
const handleIconClick = () => {
|
||||
|
@ -21,7 +23,10 @@ const PhotoUpload = () => {
|
|||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Tooltip title="Upload Photograph" placement="right-start">
|
||||
<Tooltip
|
||||
title={t('builder.tooltips.uploadPhotograph')}
|
||||
placement="right-start"
|
||||
>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
|
@ -42,7 +47,7 @@ const PhotoUpload = () => {
|
|||
|
||||
<Input
|
||||
name="photograph"
|
||||
label="Photograph"
|
||||
label={t('builder.profile.photograph')}
|
||||
className="pl-6 w-full"
|
||||
path="profile.photograph"
|
||||
/>
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
import { Tooltip } from '@material-ui/core';
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import { Link, scrollSpy } from 'react-scroll';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './SectionIcon.module.css';
|
||||
|
||||
const SectionIcon = ({ section, containerId, tooltipPlacement }) => {
|
||||
const { id, name, icon: Icon } = section;
|
||||
const { t } = useTranslation();
|
||||
const { id, icon: Icon } = section;
|
||||
|
||||
useEffect(() => {
|
||||
scrollSpy.update();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Tooltip title={name} placement={tooltipPlacement} arrow>
|
||||
<Tooltip
|
||||
title={t(`builder.sections.${id}`)}
|
||||
placement={tooltipPlacement}
|
||||
arrow
|
||||
>
|
||||
<Link
|
||||
spy
|
||||
smooth
|
||||
|
|
|
@ -4,13 +4,14 @@ import ShortUniqueId from 'short-unique-id';
|
|||
import React, { createContext, memo, useContext, useState } from 'react';
|
||||
import UserContext from './UserContext';
|
||||
import initialState from '../data/initialState.json';
|
||||
import { getUnsplashPhoto } from '../utils';
|
||||
|
||||
const DEBOUNCE_WAIT_TIME = 4000;
|
||||
|
||||
const defaultState = {
|
||||
isUpdating: false,
|
||||
createResume: () => {},
|
||||
duplicateResume: () => {},
|
||||
createResume: async () => {},
|
||||
duplicateResume: async () => {},
|
||||
deleteResume: () => {},
|
||||
getResume: async () => {},
|
||||
getResumes: async () => {},
|
||||
|
@ -39,8 +40,9 @@ const DatabaseProvider = ({ children }) => {
|
|||
}
|
||||
};
|
||||
|
||||
const createResume = ({ name }) => {
|
||||
const createResume = async ({ name }) => {
|
||||
const id = uuid();
|
||||
const preview = await getUnsplashPhoto();
|
||||
const createdAt = firebase.database.ServerValue.TIMESTAMP;
|
||||
|
||||
let firstName;
|
||||
|
@ -55,6 +57,7 @@ const DatabaseProvider = ({ children }) => {
|
|||
id,
|
||||
name,
|
||||
user: user.uid,
|
||||
preview,
|
||||
profile: {
|
||||
...initialState.profile,
|
||||
firstName: firstName || '',
|
||||
|
@ -67,14 +70,16 @@ const DatabaseProvider = ({ children }) => {
|
|||
firebase.database().ref(`resumes/${id}`).set(resume);
|
||||
};
|
||||
|
||||
const duplicateResume = (originalResume) => {
|
||||
const duplicateResume = async (originalResume) => {
|
||||
const id = uuid();
|
||||
const preview = await getUnsplashPhoto();
|
||||
const createdAt = firebase.database.ServerValue.TIMESTAMP;
|
||||
|
||||
const resume = {
|
||||
...originalResume,
|
||||
id,
|
||||
name: `${originalResume.name} Copy`,
|
||||
preview,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
import arrayMove from 'array-move';
|
||||
import { clone, findIndex, get, isUndefined, merge, setWith } from 'lodash';
|
||||
import {
|
||||
clone,
|
||||
findIndex,
|
||||
get,
|
||||
isUndefined,
|
||||
merge,
|
||||
setWith,
|
||||
set,
|
||||
has,
|
||||
} from 'lodash';
|
||||
import React, {
|
||||
createContext,
|
||||
memo,
|
||||
|
@ -7,6 +16,7 @@ import React, {
|
|||
useContext,
|
||||
useReducer,
|
||||
} from 'react';
|
||||
import i18next from 'i18next';
|
||||
import demoState from '../data/demoState.json';
|
||||
import initialState from '../data/initialState.json';
|
||||
import DatabaseContext from './DatabaseContext';
|
||||
|
@ -73,6 +83,18 @@ const ResumeProvider = ({ children }) => {
|
|||
debouncedUpdateResume(newState);
|
||||
return newState;
|
||||
|
||||
case 'change_language':
|
||||
newState = clone(state);
|
||||
items = get(
|
||||
i18next.getDataByLanguage(payload),
|
||||
'translation.builder.sections',
|
||||
);
|
||||
Object.keys(items).forEach((key) => {
|
||||
has(newState, `${key}.heading`) &&
|
||||
set(newState, `${key}.heading`, items[key]);
|
||||
});
|
||||
return newState;
|
||||
|
||||
case 'reset_layout':
|
||||
temp = get(state, 'metadata.template');
|
||||
items = get(initialState, `metadata.layout.${temp}`);
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import i18next from 'i18next';
|
||||
import moment from 'moment';
|
||||
import React, { createContext, memo, useEffect, useState } from 'react';
|
||||
import themeConfig from '../data/themeConfig';
|
||||
|
||||
const defaultState = {
|
||||
theme: 'Dark',
|
||||
setTheme: () => {},
|
||||
language: 'en',
|
||||
setLanguage: () => {},
|
||||
};
|
||||
|
||||
const SettingsContext = createContext(defaultState);
|
||||
|
||||
const SettingsProvider = ({ children }) => {
|
||||
const [theme, setTheme] = useState(defaultState.theme);
|
||||
const [language, setLanguage] = useState(defaultState.theme);
|
||||
|
||||
useEffect(() => {
|
||||
const prefTheme = localStorage.getItem('theme') || defaultState.theme;
|
||||
const prefLanguage =
|
||||
localStorage.getItem('language') || defaultState.language;
|
||||
setTheme(prefTheme);
|
||||
setLanguage(prefLanguage);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('theme', theme);
|
||||
const colorConfig = themeConfig[theme];
|
||||
for (const [key, value] of Object.entries(colorConfig)) {
|
||||
document.documentElement.style.setProperty(key, value);
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('language', language);
|
||||
i18next.changeLanguage(language);
|
||||
moment.locale(language);
|
||||
}, [language]);
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider
|
||||
value={{
|
||||
theme,
|
||||
setTheme,
|
||||
language,
|
||||
setLanguage,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SettingsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsContext;
|
||||
|
||||
const memoizedProvider = memo(SettingsProvider);
|
||||
|
||||
export { memoizedProvider as SettingsProvider };
|
|
@ -1,47 +0,0 @@
|
|||
import React, { createContext, memo, useEffect, useState } from 'react';
|
||||
import themeConfig from '../data/themeConfig';
|
||||
|
||||
const defaultState = {
|
||||
theme: 'Dark',
|
||||
setTheme: () => {},
|
||||
};
|
||||
|
||||
const ThemeContext = createContext(defaultState);
|
||||
|
||||
const ThemeProvider = ({ children }) => {
|
||||
const [theme, setThemeX] = useState(defaultState.theme);
|
||||
|
||||
useEffect(() => {
|
||||
const prefTheme = localStorage.getItem('theme') || defaultState.theme;
|
||||
setThemeX(prefTheme);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const colorConfig = themeConfig[theme];
|
||||
for (const [key, value] of Object.entries(colorConfig)) {
|
||||
document.documentElement.style.setProperty(key, value);
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
const setTheme = (themeRef) => {
|
||||
setThemeX(themeRef);
|
||||
localStorage.setItem('theme', themeRef);
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
theme,
|
||||
setTheme,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeContext;
|
||||
|
||||
const memoizedProvider = memo(ThemeProvider);
|
||||
|
||||
export { memoizedProvider as ThemeProvider };
|
|
@ -1,8 +0,0 @@
|
|||
const languageConfig = [
|
||||
{
|
||||
key: 'en',
|
||||
name: 'English',
|
||||
},
|
||||
];
|
||||
|
||||
export default languageConfig;
|
|
@ -1,6 +1,6 @@
|
|||
import { AiFillSafetyCertificate, AiOutlineTwitter } from 'react-icons/ai';
|
||||
import { BsTools } from 'react-icons/bs';
|
||||
import { FaAward, FaUserFriends, FaProjectDiagram } from 'react-icons/fa';
|
||||
import { FaAward, FaProjectDiagram, FaUserFriends } from 'react-icons/fa';
|
||||
import {
|
||||
IoLogoGameControllerB,
|
||||
IoMdBriefcase,
|
||||
|
@ -12,73 +12,61 @@ import ModalEvents from '../constants/ModalEvents';
|
|||
export default [
|
||||
{
|
||||
id: 'profile',
|
||||
name: 'Profile',
|
||||
icon: MdPerson,
|
||||
fixed: true,
|
||||
},
|
||||
{
|
||||
id: 'social',
|
||||
name: 'Social Network',
|
||||
icon: AiOutlineTwitter,
|
||||
event: ModalEvents.SOCIAL_MODAL,
|
||||
fixed: true,
|
||||
},
|
||||
{
|
||||
id: 'objective',
|
||||
name: 'Objective',
|
||||
icon: IoMdDocument,
|
||||
},
|
||||
{
|
||||
id: 'work',
|
||||
name: 'Work Experience',
|
||||
icon: IoMdBriefcase,
|
||||
event: ModalEvents.WORK_MODAL,
|
||||
},
|
||||
{
|
||||
id: 'education',
|
||||
name: 'Education',
|
||||
icon: MdSchool,
|
||||
event: ModalEvents.EDUCATION_MODAL,
|
||||
},
|
||||
{
|
||||
id: 'projects',
|
||||
name: 'Projects',
|
||||
icon: FaProjectDiagram,
|
||||
event: ModalEvents.PROJECT_MODAL,
|
||||
},
|
||||
{
|
||||
id: 'awards',
|
||||
name: 'Awards',
|
||||
icon: FaAward,
|
||||
event: ModalEvents.AWARD_MODAL,
|
||||
},
|
||||
{
|
||||
id: 'certifications',
|
||||
name: 'Certifications',
|
||||
icon: AiFillSafetyCertificate,
|
||||
event: ModalEvents.CERTIFICATION_MODAL,
|
||||
},
|
||||
{
|
||||
id: 'skills',
|
||||
name: 'Skills',
|
||||
icon: BsTools,
|
||||
event: ModalEvents.SKILL_MODAL,
|
||||
},
|
||||
{
|
||||
id: 'hobbies',
|
||||
name: 'Hobbies',
|
||||
icon: IoLogoGameControllerB,
|
||||
event: ModalEvents.HOBBY_MODAL,
|
||||
},
|
||||
{
|
||||
id: 'languages',
|
||||
name: 'Languages',
|
||||
icon: MdTranslate,
|
||||
event: ModalEvents.LANGUAGE_MODAL,
|
||||
},
|
||||
{
|
||||
id: 'references',
|
||||
name: 'References',
|
||||
icon: FaUserFriends,
|
||||
event: ModalEvents.REFERENCE_MODAL,
|
||||
},
|
||||
|
|
|
@ -11,37 +11,30 @@ import {
|
|||
export default [
|
||||
{
|
||||
id: 'templates',
|
||||
name: 'Templates',
|
||||
icon: MdStyle,
|
||||
},
|
||||
{
|
||||
id: 'layout',
|
||||
name: 'Layout',
|
||||
icon: MdDashboard,
|
||||
},
|
||||
{
|
||||
id: 'colors',
|
||||
name: 'Colors',
|
||||
icon: MdColorLens,
|
||||
},
|
||||
{
|
||||
id: 'fonts',
|
||||
name: 'Fonts',
|
||||
icon: MdFontDownload,
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
name: 'Actions',
|
||||
icon: MdImportExport,
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
name: 'Settings',
|
||||
icon: MdSettings,
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
name: 'About',
|
||||
icon: MdInfo,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import resources from './locales';
|
||||
|
||||
const languages = [
|
||||
{
|
||||
code: 'en',
|
||||
name: 'English (US)',
|
||||
},
|
||||
{
|
||||
code: 'kn',
|
||||
name: 'Kannada (ಕನ್ನಡ)',
|
||||
},
|
||||
];
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources,
|
||||
lng: 'en',
|
||||
debug: true,
|
||||
fallbackLng: 'en',
|
||||
});
|
||||
|
||||
export { languages };
|
||||
|
||||
export default i18n;
|
|
@ -0,0 +1,247 @@
|
|||
{
|
||||
"shared": {
|
||||
"appName": "Reactive Resume",
|
||||
"shortDescription": "A free and open source resume builder.",
|
||||
"forms": {
|
||||
"name": "Name",
|
||||
"title": "Title",
|
||||
"subtitle": "Subtitle",
|
||||
"required": "required",
|
||||
"website": "Website",
|
||||
"date": "Date",
|
||||
"position": "Position",
|
||||
"startDate": "Start Date",
|
||||
"endDate": "End Date",
|
||||
"address": "Address",
|
||||
"phone": "Phone Number",
|
||||
"email": "Email Address",
|
||||
"summary": "Summary",
|
||||
"markdown": "This text block supports <1>markdown</1>.",
|
||||
"validation": {
|
||||
"min": "Please enter at least {{number}} characters.",
|
||||
"dateRange": "End Date must be later than Start Date.",
|
||||
"email": "Must be a valid email address.",
|
||||
"required": "This is a required field.",
|
||||
"url": "Must be a valid URL."
|
||||
}
|
||||
},
|
||||
"buttons": {
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"loading": "Loading...",
|
||||
"confirmation": "Are you sure?",
|
||||
"login": "Login",
|
||||
"logout": "Logout"
|
||||
}
|
||||
},
|
||||
"landing": {
|
||||
"hero": {
|
||||
"goToApp": "Go To App"
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"createResume": "Create Resume",
|
||||
"editResume": "Edit Resume",
|
||||
"lastUpdated": "Last Updated {{timestamp}}",
|
||||
"toasts": {
|
||||
"deleted": "{{name}} was deleted successfully"
|
||||
},
|
||||
"buttons": {
|
||||
"duplicate": "Duplicate",
|
||||
"rename": "Rename"
|
||||
},
|
||||
"helpText": "You are going to be creating a new resume from scratch, but first, let's give it a name. This can be the name of the role you want to apply for, or if you're making a resume for a friend, you could call it Alex's Resume."
|
||||
},
|
||||
"builder": {
|
||||
"toasts": {
|
||||
"doesNotExist": "The resume you were looking for does not exist anymore... or maybe it never did?",
|
||||
"loadDemoData": "Not sure where to begin? Try loading demo data to see what Reactive Resume has to offer."
|
||||
},
|
||||
"sections": {
|
||||
"profile": "Profile",
|
||||
"social": "Social Network",
|
||||
"objective": "Objective",
|
||||
"work": "Work Experience",
|
||||
"education": "Education",
|
||||
"project": "Project",
|
||||
"projects": "Projects",
|
||||
"award": "Award",
|
||||
"awards": "Awards",
|
||||
"certification": "Certification",
|
||||
"certifications": "Certifications",
|
||||
"skill": "Skill",
|
||||
"skills": "Skills",
|
||||
"hobby": "Hobby",
|
||||
"hobbies": "Hobbies",
|
||||
"language": "Language",
|
||||
"languages": "Languages",
|
||||
"reference": "Reference",
|
||||
"references": "References",
|
||||
"templates": "Templates",
|
||||
"layout": "Layout",
|
||||
"colors": "Colors",
|
||||
"fonts": "Fonts",
|
||||
"actions": "Actions",
|
||||
"settings": "Settings",
|
||||
"about": "About"
|
||||
},
|
||||
"profile": {
|
||||
"photograph": "Photograph",
|
||||
"firstName": "First Name",
|
||||
"lastName": "Last Name",
|
||||
"address": {
|
||||
"line1": "Address Line 1",
|
||||
"line2": "Address Line 2",
|
||||
"city": "City",
|
||||
"pincode": "Pincode"
|
||||
}
|
||||
},
|
||||
"social": {
|
||||
"network": "Network",
|
||||
"username": "Username",
|
||||
"url": "URL"
|
||||
},
|
||||
"work": {
|
||||
"company": "Company"
|
||||
},
|
||||
"education": {
|
||||
"institution": "Institution",
|
||||
"field": "Field of Study",
|
||||
"degree": "Type of Degree",
|
||||
"gpa": "GPA"
|
||||
},
|
||||
"awards": {
|
||||
"awarder": "Awarder"
|
||||
},
|
||||
"certifications": {
|
||||
"issuer": "Issuer"
|
||||
},
|
||||
"skills": {
|
||||
"level": "Level"
|
||||
},
|
||||
"languages": {
|
||||
"fluency": "Fluency"
|
||||
},
|
||||
"layout": {
|
||||
"block": "Block",
|
||||
"reset": "Reset Layout",
|
||||
"text": "This template supports {{count}} blocks."
|
||||
},
|
||||
"colors": {
|
||||
"primary": "Primary Color",
|
||||
"text": "Text Color",
|
||||
"background": "Background Color"
|
||||
},
|
||||
"actions": {
|
||||
"import": {
|
||||
"heading": "Import Your Resume",
|
||||
"text": "You can import your information from various sources like JSON Resume or your LinkedIn to autofill most of the data for your resume.",
|
||||
"button": "Import"
|
||||
},
|
||||
"export": {
|
||||
"heading": "Export Your Resume",
|
||||
"text": "Export your resume as a PDF to share with recruiters or a JSON that you will be able to import back onto this app on another computer.",
|
||||
"button": "Export"
|
||||
},
|
||||
"share": {
|
||||
"heading": "Share Your Resume",
|
||||
"text": "The link below will be accessible publicly if you choose to share it, and viewers would see the latest version of your resume at any time."
|
||||
},
|
||||
"loadDemoData": {
|
||||
"text": "Unclear on what to do with a fresh blank page? Load some demo data to see how a resume should look and you can start editing from there.",
|
||||
"button": "Load Demo Data"
|
||||
},
|
||||
"resetEverything": {
|
||||
"text": "Feels like you made too many mistakes? No worries, clear everything with just one click, but be careful if there are no backups.",
|
||||
"button": "Reset Everything"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"theme": "Theme",
|
||||
"language": "Language",
|
||||
"translate": "If you would like to contribute by providing translations in your language, <1>please visit this link</1>.",
|
||||
"dangerZone": {
|
||||
"heading": "Danger Zone",
|
||||
"text": " If you would like to delete your account and erase all your resumes, it’s just one button away. Please be weary as this is an irreversible process.",
|
||||
"button": "Delete Account"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"donate": {
|
||||
"heading": "Donate to Reactive Resume",
|
||||
"text": "I try to do what I can, but if you found the app helpful, or you're in a better position than the others who depend on this project for their first job, <1>please consider donating as little as $5 to help keep the project alive</1> :)",
|
||||
"button": "Buy me a Coffee!"
|
||||
},
|
||||
"bugFeature": {
|
||||
"heading": "Bug? Feature Request?",
|
||||
"text": "Something halting your progress from making a resume? Found a pesky bug that just won't quit? Talk about it on the GitHub Issues section using the actions below.",
|
||||
"button": "Raise an Issue"
|
||||
},
|
||||
"appreciate": {
|
||||
"heading": "Loved Reactive Resume?",
|
||||
"text": "I never get tired of hearing stories of how this app helped people, and if it helped you, or you just found Reactive Resume to be an awesome tool, do let me know. You can reach out to me on my website."
|
||||
},
|
||||
"sourceCode": {
|
||||
"heading": "Source Code",
|
||||
"text": "Want to run the project from its source? Are you a developer willing to contribute to the open-source development of this project? Click the button below.",
|
||||
"button": "GitHub Repo"
|
||||
},
|
||||
"footer": "Made with Love by <1>Amruth Pillai</1>"
|
||||
},
|
||||
"tooltips": {
|
||||
"uploadPhotograph": "Upload Photograph",
|
||||
"backToDashboard": "Go Back to Dashboard"
|
||||
},
|
||||
"emptyList": "This list is empty."
|
||||
},
|
||||
"modals": {
|
||||
"auth": {
|
||||
"whoAreYou": "Who are you?",
|
||||
"welcome": "Welcome, {{name}}!",
|
||||
"loggedOutText": "Reactive Resume needs to know who you are so it can securely authenticate you into the app and show you only your information. Once you are in, you can start building your resume, editing it to add new skills or sharing it with the world!",
|
||||
"loggedInText": "Awesome. Now that you've authenticated yourself, we can get on with the real reason you're here. Click on the Go to App button to start building your resume!",
|
||||
"buttons": {
|
||||
"google": "Sign in with Google",
|
||||
"anonymous": "Sign in Anonymously"
|
||||
}
|
||||
},
|
||||
"import": {
|
||||
"button": "Select File",
|
||||
"reactiveResume": {
|
||||
"heading": "Import from Reactive Resume",
|
||||
"text": "Reactive Resume has it's own schema format to make the most of all the customizable capabilities it has to offer. If you'd like to import a backup of your resume made with this app, just upload the file using the button below."
|
||||
},
|
||||
"jsonResume": {
|
||||
"heading": "Import from JSON Resume",
|
||||
"text": "JSON Resume is an open standard for resume schema structure. If you are one of the many enthusiasts who have their resume ready in this format, all it takes it just one click to get started with Reactive Resume."
|
||||
},
|
||||
"linkedIn": {
|
||||
"heading": "Import from LinkedIn",
|
||||
"text": "You can import a JSON that was exported from Reactive Resume by clicking on the button below and selecting the appropriate file."
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"printDialog": {
|
||||
"heading": "Use Browser's Print Dialog",
|
||||
"text": "For those of you who want a quick solution, you need not look any further than your browser. All you have to do is press Ctrl/Cmd + P and open up the print dialog on your browser and get your resume printed immediately.",
|
||||
"button": "Print Resume"
|
||||
},
|
||||
"downloadPDF": {
|
||||
"heading": "Download PDF",
|
||||
"text": "These options allow you to print a single page, unconstrained version of your resume, perfect for those who have a lot of content. Alternatively, you could download a multi-page version of your resume as well with just one click.",
|
||||
"buttons": {
|
||||
"single": "Single Page Resume",
|
||||
"multi": "Multi Page Resume"
|
||||
}
|
||||
},
|
||||
"jsonFormat": {
|
||||
"heading": "Export to JSON Format",
|
||||
"text": "You can also export your data into JSON format for safe keeping so that you can easily import it back into Reactive Resume whenever you want to edit or generate a resume.",
|
||||
"button": "Export JSON"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import en from './en.json';
|
||||
import kn from './kn.json';
|
||||
|
||||
export default {
|
||||
en: { translation: en },
|
||||
kn: { translation: kn },
|
||||
};
|
|
@ -0,0 +1,247 @@
|
|||
{
|
||||
"shared": {
|
||||
"appName": "ಪ್ರತಿಕ್ರಿಯಾತ್ಮಕ ಪುನರಾರಂಭ",
|
||||
"shortDescription": "ಉಚಿತ ಮತ್ತು ಮುಕ್ತ ಮೂಲ ಪುನರಾರಂಭ ಬಿಲ್ಡರ್.",
|
||||
"forms": {
|
||||
"name": "ಹೆಸರು",
|
||||
"title": "ಶೀರ್ಷಿಕೆ",
|
||||
"subtitle": "ಉಪಶೀರ್ಷಿಕೆ",
|
||||
"required": "ಅಗತ್ಯವಿದೆ",
|
||||
"website": "ಜಾಲತಾಣ",
|
||||
"date": "ದಿನಾಂಕ",
|
||||
"position": "ಸ್ಥಾನ",
|
||||
"startDate": "ಪ್ರಾರಂಭ ದಿನಾಂಕ",
|
||||
"endDate": "ಅಂತಿಮ ದಿನಾಂಕ",
|
||||
"address": "ವಿಳಾಸ",
|
||||
"phone": "ದೂರವಾಣಿ ಸಂಖ್ಯೆ",
|
||||
"email": "ಇಮೇಲ್ ವಿಳಾಸ",
|
||||
"summary": "ಸಾರಾಂಶ",
|
||||
"markdown": "ಈ ಪಠ್ಯ ಬ್ಲಾಕ್ <1> ಮಾರ್ಕ್ಡೌನ್ ಅನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ </1> .",
|
||||
"validation": {
|
||||
"min": "ದಯವಿಟ್ಟು ಕನಿಷ್ಠ {{number}} ಅಕ್ಷರಗಳನ್ನು ನಮೂದಿಸಿ.",
|
||||
"dateRange": "ಅಂತಿಮ ದಿನಾಂಕವು ಪ್ರಾರಂಭ ದಿನಾಂಕಕ್ಕಿಂತ ನಂತರ ಇರಬೇಕು.",
|
||||
"email": "ಮಾನ್ಯವಾದ ಇಮೇಲ್ ವಿಳಾಸವಾಗಿರಬೇಕು.",
|
||||
"required": "ಇದು ಅಗತ್ಯವಾದ ಕ್ಷೇತ್ರವಾಗಿದೆ.",
|
||||
"url": "ಮಾನ್ಯವಾದ URL ಆಗಿರಬೇಕು."
|
||||
}
|
||||
},
|
||||
"buttons": {
|
||||
"add": "ಸೇರಿಸಿ",
|
||||
"edit": "ತಿದ್ದು",
|
||||
"cancel": "ರದ್ದುಮಾಡಿ",
|
||||
"delete": "ಅಳಿಸಿ",
|
||||
"loading": "ಲೋಡ್ ಆಗುತ್ತಿದೆ ...",
|
||||
"confirmation": "ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ?",
|
||||
"login": "ಲಾಗಿನ್ ಮಾಡಿ",
|
||||
"logout": "ಲಾಗ್ ಔಟ್"
|
||||
}
|
||||
},
|
||||
"landing": {
|
||||
"hero": {
|
||||
"goToApp": "ಅಪ್ಲಿಕೇಶನ್ಗೆ ಹೋಗಿ"
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "ಡ್ಯಾಶ್ಬೋರ್ಡ್",
|
||||
"createResume": "ಪುನರಾರಂಭವನ್ನು ರಚಿಸಿ",
|
||||
"editResume": "ಪುನರಾರಂಭವನ್ನು ಸಂಪಾದಿಸಿ",
|
||||
"lastUpdated": "ಕೊನೆಯದಾಗಿ ನವೀಕರಿಸಲಾಗಿದೆ {{timestamp}}",
|
||||
"toasts": {
|
||||
"deleted": "{{name}} ಅನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಅಳಿಸಲಾಗಿದೆ"
|
||||
},
|
||||
"buttons": {
|
||||
"duplicate": "ನಕಲು",
|
||||
"rename": "ಮರುಹೆಸರಿಸಿ"
|
||||
},
|
||||
"helpText": "ನೀವು ಮೊದಲಿನಿಂದ ಹೊಸ ಪುನರಾರಂಭವನ್ನು ರಚಿಸಲಿದ್ದೀರಿ, ಆದರೆ ಮೊದಲು, ಅದಕ್ಕೆ ಹೆಸರನ್ನು ನೀಡೋಣ. ಇದು ನೀವು ಅರ್ಜಿ ಸಲ್ಲಿಸಲು ಬಯಸುವ ಪಾತ್ರದ ಹೆಸರಾಗಿರಬಹುದು ಅಥವಾ ನೀವು ಸ್ನೇಹಿತರಿಗಾಗಿ ಪುನರಾರಂಭವನ್ನು ಮಾಡುತ್ತಿದ್ದರೆ, ನೀವು ಅದನ್ನು ಅಲೆಕ್ಸ್ ಪುನರಾರಂಭ ಎಂದು ಕರೆಯಬಹುದು."
|
||||
},
|
||||
"builder": {
|
||||
"toasts": {
|
||||
"doesNotExist": "ನೀವು ಹುಡುಕುತ್ತಿದ್ದ ಪುನರಾರಂಭವು ಇನ್ನು ಮುಂದೆ ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲ ... ಅಥವಾ ಬಹುಶಃ ಅದು ಎಂದಿಗೂ ಆಗಲಿಲ್ಲವೇ?",
|
||||
"loadDemoData": "ಎಲ್ಲಿಂದ ಪ್ರಾರಂಭಿಸಬೇಕು ಎಂದು ಖಚಿತವಾಗಿಲ್ಲವೇ? ರಿಯಾಕ್ಟಿವ್ ಪುನರಾರಂಭವು ಏನು ನೀಡುತ್ತದೆ ಎಂಬುದನ್ನು ನೋಡಲು ಡೆಮೊ ಡೇಟಾವನ್ನು ಲೋಡ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."
|
||||
},
|
||||
"sections": {
|
||||
"profile": "ಪ್ರೊಫೈಲ್",
|
||||
"social": "ಸಾಮಾಜಿಕ ತಾಣ",
|
||||
"objective": "ಉದ್ದೇಶ",
|
||||
"work": "ಕೆಲಸದ ಅನುಭವ",
|
||||
"education": "ಶಿಕ್ಷಣ",
|
||||
"project": "ಯೋಜನೆ",
|
||||
"projects": "ಯೋಜನೆಗಳು",
|
||||
"award": "ಪ್ರಶಸ್ತಿ",
|
||||
"awards": "ಪ್ರಶಸ್ತಿಗಳು",
|
||||
"certification": "ಪ್ರಮಾಣೀಕರಣ",
|
||||
"certifications": "ಪ್ರಮಾಣೀಕರಣಗಳು",
|
||||
"skill": "ಕೌಶಲ್ಯ",
|
||||
"skills": "ಕೌಶಲ್ಯಗಳು",
|
||||
"hobby": "ಹವ್ಯಾಸ",
|
||||
"hobbies": "ಹವ್ಯಾಸಗಳು",
|
||||
"language": "ಭಾಷೆ",
|
||||
"languages": "ಭಾಷೆಗಳು",
|
||||
"reference": "ಉಲ್ಲೇಖ",
|
||||
"references": "ಉಲ್ಲೇಖಗಳು",
|
||||
"templates": "ಟೆಂಪ್ಲೇಟ್ಗಳು",
|
||||
"layout": "ಲೆಔಟ್",
|
||||
"colors": "ಬಣ್ಣಗಳು",
|
||||
"fonts": "ಫಾಂಟ್ಗಳು",
|
||||
"actions": "ಕ್ರಿಯೆಗಳು",
|
||||
"settings": "ಸಂಯೋಜನೆಗಳು",
|
||||
"about": "ಬಗ್ಗೆ"
|
||||
},
|
||||
"profile": {
|
||||
"firstName": "ಮೊದಲ ಹೆಸರು",
|
||||
"lastName": "ಕೊನೆಯ ಹೆಸರು",
|
||||
"address": {
|
||||
"line1": "ವಿಳಾಸ ಸಾಲು 1",
|
||||
"line2": "ವಿಳಾಸ ಸಾಲು 2",
|
||||
"city": "ನಗರ",
|
||||
"pincode": "ಪಿನ್ಕೋಡ್"
|
||||
},
|
||||
"photograph": ""
|
||||
},
|
||||
"social": {
|
||||
"network": "ನೆಟ್ವರ್ಕ್",
|
||||
"username": "ಬಳಕೆದಾರ ಹೆಸರು",
|
||||
"url": "URL"
|
||||
},
|
||||
"work": {
|
||||
"company": "ಕಂಪನಿ"
|
||||
},
|
||||
"education": {
|
||||
"institution": "ಸಂಸ್ಥೆ",
|
||||
"field": "ಅಧ್ಯಯನದ ಕ್ಷೇತ್ರ",
|
||||
"degree": "ಪದವಿ ಪ್ರಕಾರ",
|
||||
"gpa": "ಜಿಪಿಎ"
|
||||
},
|
||||
"awards": {
|
||||
"awarder": "ಪ್ರಶಸ್ತಿ ಪುರಸ್ಕೃತ"
|
||||
},
|
||||
"certifications": {
|
||||
"issuer": "ನೀಡುವವರು"
|
||||
},
|
||||
"skills": {
|
||||
"level": "ಮಟ್ಟ"
|
||||
},
|
||||
"languages": {
|
||||
"fluency": "ನಿರರ್ಗಳತೆ"
|
||||
},
|
||||
"layout": {
|
||||
"block": "ನಿರ್ಬಂಧಿಸಿ",
|
||||
"reset": "ವಿನ್ಯಾಸವನ್ನು ಮರುಹೊಂದಿಸಿ",
|
||||
"text": "ಈ ಟೆಂಪ್ಲೇಟ್ {{count}} ಬ್ಲಾಕ್ಗಳನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ."
|
||||
},
|
||||
"colors": {
|
||||
"primary": "ಪ್ರಾಥಮಿಕ ಬಣ್ಣ",
|
||||
"text": "ಪಠ್ಯ ಬಣ್ಣ",
|
||||
"background": "ಹಿನ್ನೆಲೆ ಬಣ್ಣ"
|
||||
},
|
||||
"actions": {
|
||||
"import": {
|
||||
"heading": "ನಿಮ್ಮ ಪುನರಾರಂಭವನ್ನು ಆಮದು ಮಾಡಿ",
|
||||
"text": "ನಿಮ್ಮ ಪುನರಾರಂಭಕ್ಕಾಗಿ ಹೆಚ್ಚಿನ ಡೇಟಾವನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಭರ್ತಿ ಮಾಡಲು ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು JSON ಪುನರಾರಂಭ ಅಥವಾ ನಿಮ್ಮ ಲಿಂಕ್ಡ್ಇನ್ನಂತಹ ವಿವಿಧ ಮೂಲಗಳಿಂದ ಆಮದು ಮಾಡಿಕೊಳ್ಳಬಹುದು.",
|
||||
"button": "ಆಮದು"
|
||||
},
|
||||
"export": {
|
||||
"heading": "ನಿಮ್ಮ ಪುನರಾರಂಭವನ್ನು ರಫ್ತು ಮಾಡಿ",
|
||||
"text": "ನೇಮಕಾತಿಗಾರರೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಲು ನಿಮ್ಮ ಪುನರಾರಂಭವನ್ನು ಪಿಡಿಎಫ್ ಆಗಿ ರಫ್ತು ಮಾಡಿ ಅಥವಾ ಇನ್ನೊಂದು ಕಂಪ್ಯೂಟರ್ನಲ್ಲಿ ಈ ಅಪ್ಲಿಕೇಶನ್ಗೆ ಮರಳಿ ಆಮದು ಮಾಡಲು ನಿಮಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ.",
|
||||
"button": "ರಫ್ತು ಮಾಡಿ"
|
||||
},
|
||||
"share": {
|
||||
"heading": "ನಿಮ್ಮ ಪುನರಾರಂಭವನ್ನು ಹಂಚಿಕೊಳ್ಳಿ",
|
||||
"text": "ನೀವು ಅದನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಆರಿಸಿದರೆ ಕೆಳಗಿನ ಲಿಂಕ್ ಅನ್ನು ಸಾರ್ವಜನಿಕವಾಗಿ ಪ್ರವೇಶಿಸಬಹುದು ಮತ್ತು ವೀಕ್ಷಕರು ನಿಮ್ಮ ಪುನರಾರಂಭದ ಇತ್ತೀಚಿನ ಆವೃತ್ತಿಯನ್ನು ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ನೋಡುತ್ತಾರೆ."
|
||||
},
|
||||
"loadDemoData": {
|
||||
"text": "ಹೊಸ ಖಾಲಿ ಪುಟದೊಂದಿಗೆ ಏನು ಮಾಡಬೇಕೆಂದು ಸ್ಪಷ್ಟವಾಗಿಲ್ಲವೇ? ಪುನರಾರಂಭವು ಹೇಗೆ ಕಾಣುತ್ತದೆ ಎಂಬುದನ್ನು ನೋಡಲು ಕೆಲವು ಡೆಮೊ ಡೇಟಾವನ್ನು ಲೋಡ್ ಮಾಡಿ ಮತ್ತು ನೀವು ಅಲ್ಲಿಂದ ಸಂಪಾದನೆಯನ್ನು ಪ್ರಾರಂಭಿಸಬಹ<E0B2AC><E0B2B9>ದು.",
|
||||
"button": "ಡೆಮೊ ಡೇಟಾವನ್ನು ಲೋಡ್ ಮಾಡಿ"
|
||||
},
|
||||
"resetEverything": {
|
||||
"text": "ನೀವು ತುಂಬಾ ತಪ್ಪುಗಳನ್ನು ಮಾಡಿದ್ದೀರಿ ಎಂದು ಅನಿಸುತ್ತದೆಯೇ? ಚಿಂತಿಸಬೇಡಿ, ಕೇವಲ ಒಂದು ಕ್ಲಿಕ್ನಲ್ಲಿ ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ, ಆದರೆ ಬ್ಯಾಕಪ್ಗಳಿಲ್ಲದಿದ್ದರೆ ಜಾಗರೂಕರಾಗಿರಿ.",
|
||||
"button": "ಎಲ್ಲವನ್ನೂ ಮರುಹೊಂದಿಸಿ"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"theme": "ಥೀಮ್",
|
||||
"language": "ಭಾಷೆ",
|
||||
"translate": "ನಿಮ್ಮ ಭಾಷೆಯಲ್ಲಿ ಅನುವಾದಗಳನ್ನು ಒದಗಿಸುವ ಮೂಲಕ ನೀವು ಕೊಡುಗೆ ನೀಡಲು ಬಯಸಿದರೆ, <1> ದಯವಿಟ್ಟು ಈ ಲಿಂಕ್ಗೆ ಭೇಟಿ ನೀಡಿ </1> .",
|
||||
"dangerZone": {
|
||||
"heading": "ಅಪಾಯ ವಲಯ",
|
||||
"text": "ನಿಮ್ಮ ಖಾತೆಯನ್ನು ಅಳಿಸಲು ಮತ್ತು ನಿಮ್ಮ ಎಲ್ಲಾ ಮುಂದುವರಿಕೆಗಳನ್ನು ಅಳಿಸಲು ನೀವು ಬಯಸಿದರೆ, ಅದು ಕೇವಲ ಒಂದು ಬಟನ್ ದೂರದಲ್ಲಿದೆ. ಇದು ಬದಲಾಯಿಸಲಾಗದ ಪ್ರಕ್ರಿಯೆಯಾಗಿರುವುದರಿಂದ ದಯವಿಟ್ಟು ದಣಿದಿರಿ.",
|
||||
"button": "ಖಾತೆಯನ್ನು ಅಳಿಸಿ"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"donate": {
|
||||
"heading": "ಪ್ರತಿಕ್ರಿಯಾತ್ಮಕ ಪುನರಾರಂಭಕ್ಕೆ ದಾನ ಮಾಡಿ",
|
||||
"text": "ನಾನು ಏನು ಮಾಡಬಹುದೆಂಬುದನ್ನು ಮಾಡಲು ನಾನು ಪ್ರಯತ್ನಿಸುತ್ತೇನೆ, ಆದರೆ ನೀವು ಅಪ್ಲಿಕೇಶನ್ ಸಹಾಯಕವಾಗಿದೆಯೆಂದು ಕಂಡುಕೊಂಡರೆ ಅಥವಾ ಅವರ ಮೊದಲ ಕೆಲಸಕ್ಕಾಗಿ ಈ ಯೋಜನೆಯನ್ನು ಅವಲಂಬಿಸಿರುವ ಇತರರಿಗಿಂತ ನೀವು ಉತ್ತಮ ಸ್ಥಾನದಲ್ಲಿದ್ದರೆ, <1> ದಯವಿಟ್ಟು ಸಹಾಯ ಮಾಡಲು $ 5 ರಷ್ಟನ್ನು ದಾನ ಮಾಡುವುದನ್ನು ಪರಿಗಣಿಸಿ ಯೋಜನೆ ಜೀವಂತವಾಗಿದೆ </1> :)",
|
||||
"button": "ನನಗೆ ಕಾಫಿ ಖರೀದಿಸಿ!"
|
||||
},
|
||||
"bugFeature": {
|
||||
"heading": "ದೋಷ? ವೈಶಿಷ್ಟ್ಯ ವಿನಂತಿ?",
|
||||
"text": "ಪುನರಾರಂಭವನ್ನು ಮಾಡುವುದರಿಂದ ನಿಮ್ಮ ಪ್ರಗತಿಯನ್ನು ಏನಾದರೂ ತಡೆಯುತ್ತೀರಾ? ತೊರೆಯದಂತಹ ತೊಂದರೆ ದೋಷ ಕಂಡುಬಂದಿದೆ? ಕೆಳಗಿನ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಗಿಟ್ಹಬ್ ಸಮಸ್ಯೆಗಳ ವಿಭಾಗದಲ್ಲಿ ಇದರ ಬಗ್ಗೆ ಮಾತನಾಡಿ.",
|
||||
"button": "ಸಮಸ್ಯೆಯನ್ನು ಹೆಚ್ಚಿಸಿ"
|
||||
},
|
||||
"appreciate": {
|
||||
"heading": "ಪ್ರತಿಕ್ರಿಯಾತ್ಮಕ ಪುನರಾರಂಭವನ್ನು ಇಷ್ಟಪಟ್ಟಿದ್ದೀರಾ?",
|
||||
"text": "ಈ ಅಪ್ಲಿಕೇಶನ್ ಜನರಿಗೆ ಹೇಗೆ ಸಹಾಯ ಮಾಡಿದೆ ಎಂಬ ಕಥೆಗಳನ್ನು ಕೇಳಿದಾಗ ನಾನು ಎಂದಿಗೂ ಸುಸ್ತಾಗುವುದಿಲ್ಲ, ಮತ್ತು ಅದು ನಿಮಗೆ ಸಹಾಯ ಮಾಡಿದರೆ ಅಥವಾ ರಿಯಾಕ್ಟಿವ್ ಪುನರಾರಂಭವನ್ನು ಅದ್ಭುತ ಸಾಧನವೆಂದು ನೀವು ಕಂಡುಕೊಂಡಿದ್ದರೆ, ನನಗೆ ತಿಳಿಸಿ. ನನ್ನ ವೆಬ್ಸೈಟ್ನಲ್ಲಿ ನೀವು ನನ್ನನ್ನು ಸಂಪರ್ಕಿಸಬಹುದು."
|
||||
},
|
||||
"sourceCode": {
|
||||
"heading": "ಮೂಲ ಕೋಡ್",
|
||||
"text": "ಯೋಜನೆಯನ್ನು ಅದರ ಮೂಲದಿಂದ ಚಲಾಯಿಸಲು ಬಯಸುವಿರಾ? ಈ ಯೋಜನೆಯ ಮುಕ್ತ ಮೂಲ ಅಭಿವೃದ್ಧಿಗೆ ಕೊಡುಗೆ ನೀಡಲು ನೀವು ಸಿದ್ಧರಿದ್ದೀರಾ? ಕೆಳಗಿನ ಬಟನ್ ಕ್ಲಿಕ್ ಮಾಡಿ.",
|
||||
"button": "ಗಿಟ್ಹಬ್ ರೆಪೊ"
|
||||
},
|
||||
"footer": "<1> ಅಮೃತ್ ಪಿಳ್ಳೈ ಅವರಿಂದ ಪ್ರೀತಿಯಿಂದ ಮಾಡಲ್ಪಟ್ಟಿದೆ </1>"
|
||||
},
|
||||
"tooltips": {
|
||||
"uploadPhotograph": "Ograph ಾಯಾಚಿತ್ರವನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ",
|
||||
"backToDashboard": "ಡ್ಯಾಶ್ಬೋರ್ಡ್ಗೆ ಹಿಂತಿರುಗಿ"
|
||||
},
|
||||
"emptyList": "ಈ ಪಟ್ಟಿ ಖಾಲಿಯಾಗಿದೆ."
|
||||
},
|
||||
"modals": {
|
||||
"auth": {
|
||||
"whoAreYou": "ನೀವು ಯಾರು?",
|
||||
"welcome": "ಸ್ವಾಗತ, {{name}}!",
|
||||
"loggedOutText": "ಪ್ರತಿಕ್ರಿಯಾತ್ಮಕ ಪುನರಾರಂಭವು ನೀವು ಯಾರೆಂದು ತಿಳಿದುಕೊಳ್ಳಬೇಕು ಆದ್ದರಿಂದ ಅದು ನಿಮ್ಮನ್ನು ಅಪ್ಲಿಕೇಶನ್ನಲ್ಲಿ ಸುರಕ್ಷಿತವಾಗಿ ದೃ ate ೀಕರಿಸುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಮಾತ್ರ ತೋರಿಸುತ್ತದೆ. ನೀವು ಪ್ರವೇಶಿಸಿದ ನಂತರ, ನಿಮ್ಮ ಪುನರಾರಂಭವನ್ನು ನಿರ್ಮಿಸಲು, ಹೊಸ ಕೌಶಲ್ಯಗಳನ್ನು ಸೇರಿಸಲು ಅದನ್ನು ಸಂಪಾದಿಸಲು ಅಥವಾ ಅದನ್ನು ಜಗತ್ತಿನೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಲು ನೀವು ಪ್ರಾರಂಭಿಸಬಹುದು!",
|
||||
"loggedInText": "ಅದ್ಭುತ. ಈಗ ನೀವು ನಿಮ್ಮನ್ನು ದೃ ated ೀಕರಿಸಿದ್ದೀರಿ, ನೀವು ಇಲ್ಲಿರುವ ನಿಜವಾದ ಕಾರಣವನ್ನು ನಾವು ಪಡೆಯಬಹುದು. ನಿಮ್ಮ ಪುನರಾರಂಭವನ್ನು ನಿರ್ಮಿಸಲು ಪ್ರಾರಂಭಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಹೋಗಿ ಬಟನ್ ಕ್ಲಿಕ್ ಮಾಡಿ!",
|
||||
"buttons": {
|
||||
"google": "Google ನೊಂದಿಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ",
|
||||
"anonymous": "ಅನಾಮಧೇಯವಾಗಿ ಸೈನ್ ಇನ್ ಮಾಡಿ"
|
||||
}
|
||||
},
|
||||
"import": {
|
||||
"button": "ಫೈಲ್ ಆಯ್ಕೆಮಾಡಿ",
|
||||
"reactiveResume": {
|
||||
"heading": "ಪ್ರತಿಕ್ರಿಯಾತ್ಮಕ ಪುನರಾರಂಭದಿಂದ ಆಮದು ಮಾಡಿ",
|
||||
"text": "ರಿಯಾಕ್ಟಿವ್ ರೆಸ್ಯೂಮ್ ತನ್ನದೇ ಆದ ಸ್ಕೀಮಾ ಸ್ವರೂಪವನ್ನು ಹೊಂದಿದ್ದು ಅದು ನೀಡುವ ಎಲ್ಲಾ ಗ್ರಾಹಕೀಯಗೊಳಿಸಬಹುದಾದ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ಹೆಚ್ಚು ಮಾಡುತ್ತದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್ನೊಂದಿಗೆ ಮಾಡಿದ ನಿಮ್ಮ ಪುನರಾರಂಭದ ಬ್ಯಾಕಪ್ ಅನ್ನು ಆಮದು ಮಾಡಲು ನೀವು ಬಯಸಿದರೆ, ಕೆಳಗಿನ ಬಟನ್ ಬಳಸಿ ಫೈಲ್ ಅನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ."
|
||||
},
|
||||
"jsonResume": {
|
||||
"heading": "JSON ಪುನರಾರಂಭದಿಂದ ಆಮದು ಮಾಡಿ",
|
||||
"text": "JSON ಪುನರಾರಂಭವು ಪುನರಾರಂಭ ಸ್ಕೀಮಾ ರಚನೆಗೆ ಮುಕ್ತ ಮಾನದಂಡವಾಗಿದೆ. ಈ ಸ್ವರೂಪದಲ್ಲಿ ತಮ್ಮ ಪುನರಾರಂಭವನ್ನು ಸಿದ್ಧಪಡಿಸಿದ ಅನೇಕ ಉತ್ಸಾಹಿಗಳಲ್ಲಿ ನೀವು ಒಬ್ಬರಾಗಿದ್ದರೆ, ಪ್ರತಿಕ್ರಿಯಾತ್ಮಕ ಪುನರಾರಂಭದೊಂದಿಗೆ ಪ್ರಾರಂಭಿಸಲು ಕೇವಲ ಒಂದು ಕ್ಲಿಕ್ ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ."
|
||||
},
|
||||
"linkedIn": {
|
||||
"heading": "ಲಿಂಕ್ಡ್ಇನ<E0B287><E0B2A8>ನಿಂದ ಆಮದು ಮಾಡಿ",
|
||||
"text": "ಕೆಳಗಿನ ಬಟನ್ ಕ್ಲಿಕ್ ಮಾಡಿ ಮತ್ತು ಸೂಕ್ತವಾದ ಫೈಲ್ ಅನ್ನು ಆರಿಸುವ ಮೂಲಕ ರಿಯಾಕ್ಟಿವ್ ಪುನರಾರಂಭದಿಂದ ರಫ್ತು ಮಾಡಲಾದ JSON ಅನ್ನು ನೀವು ಆಮದು ಮಾಡಿಕೊಳ್ಳಬಹುದು."
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"printDialog": {
|
||||
"heading": "ಬ್ರೌಸರ್ನ ಮುದ್ರಣ ಸಂವಾದವನ್ನು ಬಳಸಿ",
|
||||
"text": "ತ್ವರಿತ ಪರಿಹಾರವನ್ನು ಬಯಸುವ ನಿಮ್ಮಲ್ಲಿ, ನಿಮ್ಮ ಬ್ರೌಸರ್ಗಿಂತ ಹೆಚ್ಚಿನದನ್ನು ನೀವು ನೋಡಬೇಕಾಗಿಲ್ಲ. ನೀವು ಮಾಡಬೇಕಾಗಿರುವುದು Ctrl / Cmd + P ಅನ್ನು ಒತ್ತಿ ಮತ್ತು ನಿಮ್ಮ ಬ್ರೌಸರ್ನಲ್ಲಿ ಮುದ್ರಣ ಸಂವಾದವನ್ನು ತೆರೆಯಿರಿ ಮತ್ತು ನಿಮ್ಮ ಪುನರಾರಂಭವನ್ನು ತಕ್ಷಣ ಮುದ್ರಿಸಿ.",
|
||||
"button": "ಪುನರಾರಂಭವನ್ನು ಮುದ್ರಿಸಿ"
|
||||
},
|
||||
"downloadPDF": {
|
||||
"heading": "ಪಿಡಿಎಫ್ ಡೌನ್ಲೋಡ್ ಮಾಡಿ",
|
||||
"text": "ಈ ಆಯ್ಕೆಗಳು ಒಂದೇ ಪುಟವನ್ನು ಮುದ್ರಿಸಲು ನಿಮಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ, ನಿಮ್ಮ ಪುನರಾರಂಭದ ನಿರ್ಬಂಧಿಸದ ಆವೃತ್ತಿ, ಬಹಳಷ್ಟು ವಿಷಯವನ್ನು ಹೊಂದಿರುವವರಿಗೆ ಇದು ಸೂಕ್ತವಾಗಿದೆ. ಪರ್ಯಾಯವಾಗಿ, ನಿಮ್ಮ ಪುನರಾರಂಭದ ಬಹು-ಪುಟ ಆವೃತ್ತಿಯನ್ನು ನೀವು ಕೇವಲ ಒಂದು ಕ್ಲಿಕ್ನಲ್ಲಿ ಡೌನ್ಲೋಡ್ ಮಾಡಬಹುದು.",
|
||||
"buttons": {
|
||||
"single": "ಏಕ ಪುಟ ಪುನರಾರಂಭ",
|
||||
"multi": "ಬಹು ಪುಟ ಪುನರಾರಂಭ"
|
||||
}
|
||||
},
|
||||
"jsonFormat": {
|
||||
"heading": "JSON ಸ್ವರೂಪಕ್ಕೆ ರಫ್ತು ಮಾಡಿ",
|
||||
"text": "ಸುರಕ್ಷಿತವಾಗಿಡಲು ನಿಮ್ಮ ಡೇಟಾವನ್ನು ನೀವು JSON ಸ್ವರೂಪಕ್ಕೆ ರಫ್ತು ಮಾಡಬಹುದು, ಇದರಿಂದಾಗಿ ನೀವು ಪುನರಾರಂಭವನ್ನು ಸಂಪಾದಿಸಲು ಅಥವಾ ಉತ್ಪಾದಿಸಲು ಬಯಸಿದಾಗಲೆಲ್ಲಾ ಅದನ್ನು ಸುಲಭವಾಗಿ ಪ್ರತಿಕ್ರಿಯಾತ್ಮಕ ಪುನರಾರಂಭಕ್ಕೆ ಆಮದು ಮಾಡಿಕೊಳ್ಳಬಹುದು.",
|
||||
"button": "JSON ರಫ್ತು ಮಾಡಿ"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
import { navigate } from 'gatsby';
|
||||
import React, { memo, useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../components/shared/Button';
|
||||
import ModalContext from '../contexts/ModalContext';
|
||||
import UserContext from '../contexts/UserContext';
|
||||
import BaseModal from './BaseModal';
|
||||
|
||||
const AuthModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isLoadingGoogle, setLoadingGoogle] = useState(false);
|
||||
const [isLoadingAnonymous, setLoadingAnonymous] = useState(false);
|
||||
|
@ -39,20 +41,20 @@ const AuthModal = () => {
|
|||
};
|
||||
|
||||
const getTitle = () =>
|
||||
user ? `Welcome, ${user.displayName || 'Agent 47'}` : 'Who are you?';
|
||||
user
|
||||
? t('modals.auth.welcome', { name: user.displayName || 'Agent 47' })
|
||||
: t('modals.auth.whoAreYou');
|
||||
|
||||
const getMessage = () =>
|
||||
user
|
||||
? `Awesome. Now that you've authenticated yourself, we can get on with the real reason you're here. Click on the Go to App button to start building your resume!`
|
||||
: `Reactive Resume needs to know who you are so it can securely authenticate you into the app and show you only your information. Once you are in, you can start building your resume, editing it to add new skills or sharing it with the world!`;
|
||||
user ? t('modals.auth.loggedInText') : t('modals.auth.loggedOutText');
|
||||
|
||||
const loggedInAction = (
|
||||
<>
|
||||
<Button outline className="mr-8" onClick={logout}>
|
||||
Logout
|
||||
{t('shared.buttons.logout')}
|
||||
</Button>
|
||||
<Button title="" onClick={handleGotoApp}>
|
||||
Go to App
|
||||
{t('landing.hero.goToApp')}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
@ -60,14 +62,14 @@ const AuthModal = () => {
|
|||
const loggedOutAction = (
|
||||
<div className="flex">
|
||||
<Button isLoading={isLoadingGoogle} onClick={handleSignInWithGoogle}>
|
||||
Sign in with Google
|
||||
{t('modals.auth.buttons.google')}
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-8"
|
||||
isLoading={isLoadingAnonymous}
|
||||
onClick={handleSignInAnonymously}
|
||||
>
|
||||
Sign in Anonymously
|
||||
{t('modals.auth.buttons.anonymous')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@ import Modal from '@material-ui/core/Modal';
|
|||
import { isFunction } from 'lodash';
|
||||
import React, { forwardRef, memo, useImperativeHandle } from 'react';
|
||||
import { MdClose } from 'react-icons/md';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../components/shared/Button';
|
||||
import { handleKeyUp } from '../utils';
|
||||
import styles from './BaseModal.module.css';
|
||||
|
@ -11,6 +12,7 @@ import styles from './BaseModal.module.css';
|
|||
const BaseModal = forwardRef(
|
||||
({ title, state, children, action, hideActions = false, onDestroy }, ref) => {
|
||||
const [open, setOpen] = state;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
|
@ -47,7 +49,7 @@ const BaseModal = forwardRef(
|
|||
{!hideActions && (
|
||||
<div className={styles.actions}>
|
||||
<Button outline className="mr-8" onClick={handleClose}>
|
||||
Cancel
|
||||
{t('shared.buttons.cancel')}
|
||||
</Button>
|
||||
|
||||
{action}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useFormikContext } from 'formik';
|
|||
import { isEmpty, isFunction } from 'lodash';
|
||||
import React, { memo, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../components/shared/Button';
|
||||
import ModalContext from '../contexts/ModalContext';
|
||||
import { useDispatch } from '../contexts/ResumeContext';
|
||||
|
@ -19,9 +20,11 @@ const DataModal = ({
|
|||
}) => {
|
||||
const modalRef = useRef(null);
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [data, setData] = useState(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isEditMode, setEditMode] = useState(false);
|
||||
|
||||
const { emitter } = useContext(ModalContext);
|
||||
|
@ -41,11 +44,13 @@ const DataModal = ({
|
|||
}, [data]);
|
||||
|
||||
const onSubmit = async (newData) => {
|
||||
setLoading(true);
|
||||
|
||||
if (isEmpty(await validateForm())) {
|
||||
if (isEditMode) {
|
||||
if (data !== newData) {
|
||||
isFunction(onEdit)
|
||||
? onEdit(newData)
|
||||
? await onEdit(newData)
|
||||
: dispatch({
|
||||
type: 'on_edit_item',
|
||||
payload: {
|
||||
|
@ -58,7 +63,7 @@ const DataModal = ({
|
|||
newData.id = uuidv4();
|
||||
|
||||
isFunction(onCreate)
|
||||
? onCreate(newData)
|
||||
? await onCreate(newData)
|
||||
: dispatch({
|
||||
type: 'on_add_item',
|
||||
payload: {
|
||||
|
@ -68,6 +73,7 @@ const DataModal = ({
|
|||
});
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
modalRef.current.handleClose();
|
||||
}
|
||||
};
|
||||
|
@ -80,7 +86,7 @@ const DataModal = ({
|
|||
|
||||
const submitAction = (
|
||||
<Button type="submit" onClick={() => onSubmit(values)}>
|
||||
{getTitle}
|
||||
{loading ? t('shared.buttons.loading') : getTitle}
|
||||
</Button>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,36 +1,27 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo, useContext } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../components/shared/Input';
|
||||
import ModalEvents from '../constants/ModalEvents';
|
||||
import DatabaseContext from '../contexts/DatabaseContext';
|
||||
import { getFieldProps } from '../utils';
|
||||
import DataModal from './DataModal';
|
||||
import leftSections from '../data/leftSections';
|
||||
|
||||
const initialValues = {
|
||||
name: '',
|
||||
metadata: {
|
||||
template: 'onyx',
|
||||
font: 'Montserrat',
|
||||
layout: [leftSections.map(({ id, name }) => ({ id, name }))],
|
||||
colors: {
|
||||
text: '#444444',
|
||||
primary: '#5875DB',
|
||||
background: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.min(5, 'Please enter at least 5 characters.')
|
||||
.required('This is a required field.'),
|
||||
});
|
||||
|
||||
const ResumeModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const { createResume, updateResume } = useContext(DatabaseContext);
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.min(5, t('shared.forms.validation.min', { number: 5 }))
|
||||
.required(t('shared.forms.validation.required')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -39,28 +30,22 @@ const ResumeModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Resume"
|
||||
title={{
|
||||
create: 'Create Resume',
|
||||
edit: 'Edit Resume',
|
||||
create: t('dashboard.createResume'),
|
||||
edit: t('dashboard.editResume'),
|
||||
}}
|
||||
onEdit={updateResume}
|
||||
onCreate={createResume}
|
||||
event={ModalEvents.CREATE_RESUME_MODAL}
|
||||
>
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
className="mb-8"
|
||||
placeholder="Full Stack Web Developer"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<p className="leading-loose">
|
||||
You are going to be creating a new resume from scratch, but first,
|
||||
let's give it a name. This can be the name of the role you want
|
||||
to apply for, or if you're making a resume for a friend, you
|
||||
could call it Alex's Resume.
|
||||
</p>
|
||||
<p className="leading-loose">{t('dashboard.helpText')}</p>
|
||||
</DataModal>
|
||||
)}
|
||||
</Formik>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
|
@ -13,14 +14,16 @@ const initialValues = {
|
|||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required('This is a required field.'),
|
||||
awarder: Yup.string().required('This is a required field.'),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
const AwardModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required(t('shared.forms.validation.required')),
|
||||
awarder: Yup.string().required(t('shared.forms.validation.required')),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -29,33 +32,33 @@ const AwardModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Award"
|
||||
name={t('builder.sections.award')}
|
||||
path="awards.items"
|
||||
event={ModalEvents.AWARD_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Title"
|
||||
label={t('shared.forms.title')}
|
||||
className="col-span-2"
|
||||
placeholder="Intl. Flutter Hackathon '19"
|
||||
{...getFieldProps(formik, schema, 'title')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Awarder"
|
||||
label={t('builder.awards.awarder')}
|
||||
placeholder="Google"
|
||||
{...getFieldProps(formik, schema, 'awarder')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Date"
|
||||
label={t('shared.forms.date')}
|
||||
{...getFieldProps(formik, schema, 'date')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
|
@ -13,14 +14,16 @@ const initialValues = {
|
|||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required('This is a required field.'),
|
||||
issuer: Yup.string().required('This is a required field.'),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
const CertificateModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required(t('shared.forms.validation.required')),
|
||||
issuer: Yup.string().required(t('shared.forms.validation.required')),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -29,33 +32,33 @@ const CertificateModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Certificate"
|
||||
name={t('builder.sections.certification')}
|
||||
path="certifications.items"
|
||||
event={ModalEvents.CERTIFICATION_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Title"
|
||||
label={t('shared.forms.title')}
|
||||
className="col-span-2"
|
||||
placeholder="CCNP"
|
||||
{...getFieldProps(formik, schema, 'title')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Issuer"
|
||||
label={t('builder.certifications.issuer')}
|
||||
placeholder="Cisco Systems"
|
||||
{...getFieldProps(formik, schema, 'issuer')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Date"
|
||||
label={t('shared.forms.date')}
|
||||
{...getFieldProps(formik, schema, 'date')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
|
@ -16,22 +17,27 @@ const initialValues = {
|
|||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
institution: Yup.string().required('This is a required field.'),
|
||||
field: Yup.string().required('This is a required field.'),
|
||||
degree: Yup.string(),
|
||||
gpa: Yup.string(),
|
||||
startDate: Yup.date().required('This is a required field.'),
|
||||
endDate: Yup.date().when(
|
||||
'startDate',
|
||||
(startDate, yupSchema) =>
|
||||
startDate &&
|
||||
yupSchema.min(startDate, 'End Date must be later than Start Date'),
|
||||
),
|
||||
summary: Yup.string().min(10, 'Please enter at least 10 characters.'),
|
||||
});
|
||||
|
||||
const EducationModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
institution: Yup.string().required(t('shared.forms.validation.required')),
|
||||
field: Yup.string().required(t('shared.forms.validation.required')),
|
||||
degree: Yup.string(),
|
||||
gpa: Yup.string(),
|
||||
startDate: Yup.date().required(t('shared.forms.validation.required')),
|
||||
endDate: Yup.date().when(
|
||||
'startDate',
|
||||
(startDate, yupSchema) =>
|
||||
startDate &&
|
||||
yupSchema.min(startDate, t('shared.forms.validation.dateRange')),
|
||||
),
|
||||
summary: Yup.string().min(
|
||||
10,
|
||||
t('shared.forms.validation.min', { number: 10 }),
|
||||
),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -40,54 +46,54 @@ const EducationModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Education"
|
||||
name={t('builder.sections.education')}
|
||||
path="education.items"
|
||||
event={ModalEvents.EDUCATION_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Institution"
|
||||
label={t('builder.education.institution')}
|
||||
className="col-span-2"
|
||||
placeholder="Dayananda Sagar College of Engineering"
|
||||
{...getFieldProps(formik, schema, 'institution')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Field of Study"
|
||||
label={t('builder.education.field')}
|
||||
className="col-span-2"
|
||||
placeholder="Computer Science & Engineering"
|
||||
{...getFieldProps(formik, schema, 'field')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Degree Type"
|
||||
label={t('builder.education.degree')}
|
||||
placeholder="Bachelor's Degree"
|
||||
{...getFieldProps(formik, schema, 'degree')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="GPA"
|
||||
label={t('builder.education.gpa')}
|
||||
placeholder="8.8"
|
||||
{...getFieldProps(formik, schema, 'gpa')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Start Date"
|
||||
label={t('shared.forms.startDate')}
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, schema, 'startDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="End Date"
|
||||
label={t('shared.forms.endDate')}
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, schema, 'endDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import firebase from 'gatsby-plugin-firebase';
|
||||
import { clone } from 'lodash';
|
||||
import React, { memo, useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaPrint } from 'react-icons/fa';
|
||||
import Button from '../../components/shared/Button';
|
||||
import ModalContext from '../../contexts/ModalContext';
|
||||
|
@ -10,6 +11,7 @@ import BaseModal from '../BaseModal';
|
|||
|
||||
const ExportModal = () => {
|
||||
const state = useSelector();
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isLoadingSingle, setLoadingSingle] = useState(false);
|
||||
const [isLoadingMulti, setLoadingMulti] = useState(false);
|
||||
|
@ -72,35 +74,31 @@ const ExportModal = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<BaseModal hideActions state={[open, setOpen]} title="Export Your Resume">
|
||||
<BaseModal
|
||||
hideActions
|
||||
state={[open, setOpen]}
|
||||
title={t('builder.actions.export.heading')}
|
||||
>
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
Use Browser's Print Dialog
|
||||
{t('modals.export.printDialog.heading')}
|
||||
</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
For those of you who want a quick solution, you need not look any
|
||||
further than your browser. All you have to do is press Ctrl/Cmd + P
|
||||
and open up the print dialog on your browser and get your resume
|
||||
printed immediately.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.export.printDialog.text')}</p>
|
||||
|
||||
<Button icon={FaPrint} className="mt-5" onClick={handleOpenPrintDialog}>
|
||||
Print Resume
|
||||
{t('modals.export.printDialog.button')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<hr className="my-8" />
|
||||
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">Download PDF</h5>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
{t('modals.export.downloadPDF.heading')}
|
||||
</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
These options allow you to print a single page, unconstrained version
|
||||
of your resume, perfect for those who have a lot of content.
|
||||
Alternatively, you could download a multi-page version of your resume
|
||||
as well with just one click.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.export.downloadPDF.text')}</p>
|
||||
|
||||
<div className="mt-5 mb-4">
|
||||
<div className="flex">
|
||||
|
@ -108,14 +106,14 @@ const ExportModal = () => {
|
|||
isLoading={isLoadingSingle}
|
||||
onClick={handleSinglePageDownload}
|
||||
>
|
||||
Single Page Resume
|
||||
{t('modals.export.downloadPDF.buttons.single')}
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-8"
|
||||
isLoading={isLoadingMulti}
|
||||
onClick={handleMultiPageDownload}
|
||||
>
|
||||
Multi Page Resume
|
||||
{t('modals.export.downloadPDF.buttons.multi')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -124,18 +122,18 @@ const ExportModal = () => {
|
|||
<hr className="my-8" />
|
||||
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">Export to JSON Format</h5>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
{t('modals.export.jsonFormat.heading')}
|
||||
</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
You can also export your data into JSON format for safe keeping so
|
||||
that you can easily import it back into Reactive Resume whenever you
|
||||
want to edit or generate a resume.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.export.jsonFormat.text')}</p>
|
||||
|
||||
<div className="mt-5">
|
||||
<Button onClick={handleExportToJson}>Export JSON</Button>
|
||||
<Button onClick={handleExportToJson}>
|
||||
{t('modals.export.jsonFormat.button')}
|
||||
</Button>
|
||||
<a id="downloadAnchor" className="hidden">
|
||||
Export JSON
|
||||
{t('modals.export.jsonFormat.button')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
|
@ -10,11 +11,13 @@ const initialValues = {
|
|||
name: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required('This is a required field.'),
|
||||
});
|
||||
|
||||
const HobbyModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(t('shared.forms.validation.required')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -23,13 +26,13 @@ const HobbyModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Hobby"
|
||||
name={t('builder.sections.hobby')}
|
||||
path="hobbies.items"
|
||||
event={ModalEvents.HOBBY_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
placeholder="Fishing"
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Tooltip } from '@material-ui/core';
|
||||
import Ajv from 'ajv';
|
||||
import React, { memo, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'react-toastify';
|
||||
import Button from '../../components/shared/Button';
|
||||
import ModalContext from '../../contexts/ModalContext';
|
||||
|
@ -10,6 +11,7 @@ import BaseModal from '../BaseModal';
|
|||
|
||||
const ImportModal = () => {
|
||||
const ajv = new Ajv();
|
||||
const { t } = useTranslation();
|
||||
const fileInputRef = useRef(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
@ -38,21 +40,22 @@ const ImportModal = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<BaseModal hideActions state={[open, setOpen]} title="Import Your Resume">
|
||||
<BaseModal
|
||||
hideActions
|
||||
state={[open, setOpen]}
|
||||
title={t('builder.actions.import.heading')}
|
||||
>
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
Import from Reactive Resume
|
||||
{t('modals.import.reactiveResume.heading')}
|
||||
</h5>
|
||||
|
||||
<p>
|
||||
Reactive Resume has it's own schema format to make the most of
|
||||
all the customizable capabilities it has to offer. If you'd like
|
||||
to import a backup of your resume made with this app, just upload the
|
||||
file using the button below.
|
||||
<p className="leading-loose">
|
||||
{t('modals.import.reactiveResume.text')}
|
||||
</p>
|
||||
|
||||
<Button className="mt-5" onClick={() => fileInputRef.current.click()}>
|
||||
Select File
|
||||
{t('modals.import.button')}
|
||||
</Button>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
|
@ -65,18 +68,15 @@ const ImportModal = () => {
|
|||
<hr className="my-8" />
|
||||
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">Import from JSON Resume</h5>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
{t('modals.import.jsonResume.heading')}
|
||||
</h5>
|
||||
|
||||
<p>
|
||||
<a href="https://jsonresume.org/">JSON Resume</a> is an open standard
|
||||
for resume schema structure. If you are one of the many enthusiasts
|
||||
who have their resume ready in this format, all it takes it just one
|
||||
click to get started with Reactive Resume.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.import.jsonResume.text')}</p>
|
||||
|
||||
<Tooltip title="Coming Soon" placement="right" arrow>
|
||||
<div className="mt-5 inline-block">
|
||||
<Button className="opacity-50">Select File</Button>
|
||||
<Button className="opacity-50">{t('modals.import.button')}</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
@ -84,16 +84,15 @@ const ImportModal = () => {
|
|||
<hr className="my-8" />
|
||||
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">Import from LinkedIn</h5>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
{t('modals.import.linkedIn.heading')}
|
||||
</h5>
|
||||
|
||||
<p>
|
||||
You can import a JSON that was exported from Reactive Resume by
|
||||
clicking on the button below and selecting the appropriate file.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.import.linkedIn.text')}</p>
|
||||
|
||||
<Tooltip title="Coming Soon" placement="right" arrow>
|
||||
<div className="mt-5 inline-block">
|
||||
<Button className="opacity-50">Select File</Button>
|
||||
<Button className="opacity-50">{t('modals.import.button')}</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
|
@ -11,12 +12,14 @@ const initialValues = {
|
|||
fluency: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required('This is a required field.'),
|
||||
fluency: Yup.string().required('This is a required field.'),
|
||||
});
|
||||
|
||||
const LanguageModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(t('shared.forms.validation.required')),
|
||||
fluency: Yup.string().required(t('shared.forms.validation.required')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -25,19 +28,19 @@ const LanguageModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Language"
|
||||
name={t('builder.sections.language')}
|
||||
path="languages.items"
|
||||
event={ModalEvents.LANGUAGE_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
placeholder="German"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Fluency"
|
||||
label={t('builder.languages.fluency')}
|
||||
placeholder="Native/B1"
|
||||
{...getFieldProps(formik, schema, 'fluency')}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
|
@ -13,14 +14,16 @@ const initialValues = {
|
|||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required('This is a required field.'),
|
||||
link: Yup.string().url('Must be a valid URL'),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
const ProjectModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required(t('shared.forms.validation.required')),
|
||||
link: Yup.string().url(t('shared.forms.validation.url')),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -29,33 +32,33 @@ const ProjectModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Project"
|
||||
name={t('builder.sections.project')}
|
||||
path="projects.items"
|
||||
event={ModalEvents.PROJECT_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Title"
|
||||
label={t('shared.forms.title')}
|
||||
className="col-span-2"
|
||||
placeholder="Reactive Resume"
|
||||
{...getFieldProps(formik, schema, 'title')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Link"
|
||||
label={t('shared.forms.website')}
|
||||
placeholder="https://github.com/AmruthPillai/Reactive-Resume"
|
||||
{...getFieldProps(formik, schema, 'link')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Date"
|
||||
label={t('shared.forms.date')}
|
||||
{...getFieldProps(formik, schema, 'date')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
|
@ -14,15 +15,17 @@ const initialValues = {
|
|||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required('This is a required field.'),
|
||||
position: Yup.string().required('This is a required field.'),
|
||||
phone: Yup.string(),
|
||||
email: Yup.string().email('Must be a valid email address.'),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
const ReferenceModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(t('shared.forms.validation.required')),
|
||||
position: Yup.string().required(t('shared.forms.validation.required')),
|
||||
phone: Yup.string(),
|
||||
email: Yup.string().email(t('shared.forms.validation.email')),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -31,38 +34,38 @@ const ReferenceModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Reference"
|
||||
name={t('builder.sections.reference')}
|
||||
path="references.items"
|
||||
event={ModalEvents.REFERENCE_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
placeholder="Jane Doe"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="position"
|
||||
label={t('shared.forms.position')}
|
||||
placeholder="Assistant Manager"
|
||||
{...getFieldProps(formik, schema, 'position')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Phone Number"
|
||||
label={t('shared.forms.phone')}
|
||||
placeholder="+1 (708) 756-6065"
|
||||
{...getFieldProps(formik, schema, 'phone')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Email Address"
|
||||
label={t('shared.forms.email')}
|
||||
placeholder="janedoe@example.com"
|
||||
{...getFieldProps(formik, schema, 'email')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
|
@ -19,14 +20,16 @@ const initialValues = {
|
|||
level: SKILL_LEVELS[0],
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required('This is a required field.'),
|
||||
level: Yup.string()
|
||||
.oneOf(SKILL_LEVELS, 'Must be one of the options above.')
|
||||
.required('This is a required field.'),
|
||||
});
|
||||
|
||||
const SkillModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(t('shared.forms.validation.required')),
|
||||
level: Yup.string()
|
||||
.oneOf(SKILL_LEVELS)
|
||||
.required(t('shared.forms.validation.required')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -35,19 +38,19 @@ const SkillModal = () => {
|
|||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Skill"
|
||||
name={t('builder.sections.skill')}
|
||||
path="skills.items"
|
||||
event={ModalEvents.SKILL_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
placeholder="ReactJS"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Level"
|
||||
label={t('builder.skills.level')}
|
||||
type="dropdown"
|
||||
options={SKILL_LEVELS}
|
||||
{...getFieldProps(formik, schema, 'level')}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
|
@ -12,18 +13,20 @@ const initialValues = {
|
|||
username: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
network: Yup.string()
|
||||
.min(5, 'Please enter at least 5 characters.')
|
||||
.required('This is a required field.'),
|
||||
username: Yup.string().required('This is a required field.'),
|
||||
url: Yup.string()
|
||||
.min(5, 'Please enter at least 5 characters.')
|
||||
.required('This is a required field.')
|
||||
.url('Must be a valid URL'),
|
||||
});
|
||||
|
||||
const SocialModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
network: Yup.string()
|
||||
.min(5, t('shared.forms.validation.min', { number: 5 }))
|
||||
.required(t('shared.forms.validation.required')),
|
||||
username: Yup.string().required(t('shared.forms.validation.required')),
|
||||
url: Yup.string()
|
||||
.min(5, t('shared.forms.validation.min', { number: 5 }))
|
||||
.required(t('shared.forms.validation.required'))
|
||||
.url(t('shared.forms.validation.url')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -33,24 +36,24 @@ const SocialModal = () => {
|
|||
{(formik) => (
|
||||
<DataModal
|
||||
path="social.items"
|
||||
name="Social Network"
|
||||
name={t('builder.sections.social')}
|
||||
event={ModalEvents.SOCIAL_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Network"
|
||||
label={t('builder.social.network')}
|
||||
placeholder="Twitter"
|
||||
{...getFieldProps(formik, schema, 'network')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Username"
|
||||
label={t('builder.social.username')}
|
||||
placeholder="KingOKings"
|
||||
{...getFieldProps(formik, schema, 'username')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="URL"
|
||||
label={t('builder.social.url')}
|
||||
className="col-span-2"
|
||||
placeholder="https://twitter.com/KingOKings"
|
||||
{...getFieldProps(formik, schema, 'url')}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
|
@ -15,21 +16,26 @@ const initialValues = {
|
|||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
company: Yup.string().required('This is a required field.'),
|
||||
position: Yup.string().required('This is a required field.'),
|
||||
website: Yup.string().url('Must be a valid URL'),
|
||||
startDate: Yup.date().required('This is a required field.'),
|
||||
endDate: Yup.date().when(
|
||||
'startDate',
|
||||
(startDate, yupSchema) =>
|
||||
startDate &&
|
||||
yupSchema.min(startDate, 'End Date must be later than Start Date'),
|
||||
),
|
||||
summary: Yup.string().min(10, 'Please enter at least 10 characters.'),
|
||||
});
|
||||
|
||||
const WorkModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
company: Yup.string().required(t('shared.forms.validation.required')),
|
||||
position: Yup.string().required(t('shared.forms.validation.required')),
|
||||
website: Yup.string().url(t('shared.forms.validation.url')),
|
||||
startDate: Yup.date().required(t('shared.forms.validation.required')),
|
||||
endDate: Yup.date().when(
|
||||
'startDate',
|
||||
(startDate, yupSchema) =>
|
||||
startDate &&
|
||||
yupSchema.min(startDate, t('shared.forms.validation.dateRange')),
|
||||
),
|
||||
summary: Yup.string().min(
|
||||
10,
|
||||
t('shared.forms.validation.min', { number: 10 }),
|
||||
),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
|
@ -39,46 +45,46 @@ const WorkModal = () => {
|
|||
{(formik) => (
|
||||
<DataModal
|
||||
path="work.items"
|
||||
name="Work Experience"
|
||||
name={t('builder.sections.work')}
|
||||
event={ModalEvents.WORK_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Company"
|
||||
label={t('builder.work.company')}
|
||||
className="col-span-2"
|
||||
placeholder="Postdot Technologies Pvt. Ltd."
|
||||
{...getFieldProps(formik, schema, 'company')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Position"
|
||||
label={t('shared.forms.position')}
|
||||
placeholder="Full Stack Web Developer"
|
||||
{...getFieldProps(formik, schema, 'position')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Website"
|
||||
label={t('shared.forms.website')}
|
||||
placeholder="https://example.com/"
|
||||
{...getFieldProps(formik, schema, 'website')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Start Date"
|
||||
label={t('shared.forms.startDate')}
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, schema, 'startDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="End Date"
|
||||
label={t('shared.forms.endDate')}
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, schema, 'endDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { navigate } from 'gatsby';
|
||||
import React, { memo, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Artboard from '../../components/builder/center/Artboard';
|
||||
import LeftSidebar from '../../components/builder/left/LeftSidebar';
|
||||
import RightSidebar from '../../components/builder/right/RightSidebar';
|
||||
|
@ -11,6 +12,7 @@ import Button from '../../components/shared/Button';
|
|||
|
||||
const Builder = ({ id }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { getResume } = useContext(DatabaseContext);
|
||||
|
||||
|
@ -24,22 +26,17 @@ const Builder = ({ id }) => {
|
|||
|
||||
if (!resume) {
|
||||
navigate('/app/dashboard');
|
||||
toast.error(
|
||||
`The resume you were looking for does not exist anymore... or maybe it never did?`,
|
||||
);
|
||||
toast.error(t('builder.toasts.doesNotExist'));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resume.createdAt === resume.updatedAt) {
|
||||
toast.dark(() => (
|
||||
<div className="py-2">
|
||||
<p className="leading-loose">
|
||||
Not sure where to begin? Try <strong>Loading Demo Data</strong> to
|
||||
see what Reactive Resume has to offer.
|
||||
</p>
|
||||
<p className="leading-loose">{t('builder.toasts.loadDemoData')}</p>
|
||||
|
||||
<Button className="mt-4" onClick={handleLoadDemoData}>
|
||||
Load Demo Data
|
||||
{t('builder.actions.loadDemoData.button')}
|
||||
</Button>
|
||||
</div>
|
||||
));
|
||||
|
@ -60,7 +57,7 @@ const Builder = ({ id }) => {
|
|||
<div className="col-span-3">
|
||||
<LeftSidebar />
|
||||
</div>
|
||||
<div className="h-screen overflow-scroll col-span-5 bg-primary-100 grid items-center justify-center">
|
||||
<div className="col-span-5 h-screen overflow-hidden bg-primary-100 grid items-center justify-center">
|
||||
<Artboard />
|
||||
</div>
|
||||
<div className="col-span-3">
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import firebase from 'gatsby-plugin-firebase';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CreateResume from '../../components/dashboard/CreateResume';
|
||||
import ResumePreview from '../../components/dashboard/ResumePreview';
|
||||
import TopNavbar from '../../components/dashboard/TopNavbar';
|
||||
import LoadingScreen from '../../components/router/LoadingScreen';
|
||||
|
||||
const Dashboard = ({ user }) => {
|
||||
const { t } = useTranslation();
|
||||
const [resumes, setResumes] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
@ -61,7 +63,9 @@ const Dashboard = ({ user }) => {
|
|||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>Dashboard | Reactive Resume</title>
|
||||
<title>
|
||||
{t('dashboard.title')} | {t('shared.appName')}
|
||||
</title>
|
||||
<link rel="canonical" href="https://rxresu.me/app/dashboard" />
|
||||
</Helmet>
|
||||
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import React from 'react';
|
||||
import { MdKeyboardArrowLeft } from 'react-icons/md';
|
||||
import { Link } from '@reach/router';
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { MdKeyboardArrowLeft } from 'react-icons/md';
|
||||
import Wrapper from '../components/shared/Wrapper';
|
||||
|
||||
const FrequentlyAskedQuestions = () => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Helmet>
|
||||
<title>Frequently Asked Questions | Reactive Resume</title>
|
||||
<link rel="canonical" href="https://rxresu.me/app/dashboard" />
|
||||
</Helmet>
|
||||
|
||||
<div className="md:w-1/2 container px-8 md:px-0 py-16 grid gap-12">
|
||||
<div className="flex items-center">
|
||||
<Link to="/">
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
import React, { memo } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from '@reach/router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Hero from '../components/landing/Hero';
|
||||
import Wrapper from '../components/shared/Wrapper';
|
||||
|
||||
const Home = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Helmet>
|
||||
<title>Reactive Resume</title>
|
||||
<title>{t('shared.appName')}</title>
|
||||
<link rel="canonical" href="https://rxresu.me/" />
|
||||
</Helmet>
|
||||
|
||||
<div className="container mt-24">
|
||||
<Hero />
|
||||
|
||||
<div className="my-24">
|
||||
<h4 className="text-xl uppercase font-bold mb-8">Screenshots</h4>
|
||||
</div>
|
||||
|
||||
<div className="pt-8">
|
||||
<Feature title="Create a resume that’s worthy of who you are.">
|
||||
Keep up with the latest trends in resume design without having to
|
||||
|
@ -37,19 +36,12 @@ const Home = () => {
|
|||
</Feature>
|
||||
|
||||
<Feature title="Kickstarting your career shouldn’t come at a cost.">
|
||||
There are brilliant alternatives to this app like{' '}
|
||||
<a href="/" target="blank">
|
||||
Novoresume
|
||||
</a>{' '}
|
||||
and{' '}
|
||||
<a href="/" target="blank">
|
||||
Zety
|
||||
</a>
|
||||
, but they come at a cost, mainly because of the time the developers
|
||||
and the marketing they had to incur to make the product. This app
|
||||
might not be better than them, but it does cater to people who are
|
||||
just not in a position to pay hundreds of dollars to create a resume
|
||||
to bootstrap their career.
|
||||
There are brilliant alternatives to this app like Novoresume and
|
||||
Zety , but they come at a cost, mainly because of the time the
|
||||
developers and the marketing they had to incur to make the product.
|
||||
This app might not be better than them, but it does cater to people
|
||||
who are just not in a position to pay hundreds of dollars to create
|
||||
a resume to bootstrap their career.
|
||||
</Feature>
|
||||
|
||||
<Feature title="Your data is your data, none of my data.">
|
||||
|
@ -69,11 +61,29 @@ const Home = () => {
|
|||
</h4>
|
||||
<div className="grid grid-cols-4 gap-8">
|
||||
<Link to="/faq">Frequently Asked Questions</Link>
|
||||
<Link to="/faq">Checkout Source Code</Link>
|
||||
<a
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Check Out Source Code
|
||||
</a>
|
||||
<Link to="/faq">Upvote on Product Hunt</Link>
|
||||
<Link to="/faq">Raise an Issue on GitHub</Link>
|
||||
<Link to="/faq">Donate to Reactive Resume</Link>
|
||||
<Link to="/faq">Building Great Looking Resumes</Link>
|
||||
<a
|
||||
href="https://www.buymeacoffee.com/AmruthPillai"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Donate to Reactive Resume
|
||||
</a>
|
||||
<Link to="/blog/design-beautiful-resumes">
|
||||
Design Beautiful Resumes
|
||||
</Link>
|
||||
<Link to="/blog/ats-friendly-resumes">ATS-Friendly Resumes</Link>
|
||||
<Link to="/blog/acing-video-interviews">
|
||||
Acing Video Interviews
|
||||
</Link>
|
||||
<Link to="/blog/jobs-during-covid-19">Jobs During COVID-19</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Link, navigate } from '@reach/router';
|
|||
import React, { memo, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import LoadingScreen from '../../components/router/LoadingScreen';
|
||||
import DatabaseContext from '../../contexts/DatabaseContext';
|
||||
import Castform from '../../templates/Castform';
|
||||
|
@ -13,6 +14,7 @@ import styles from './view.module.css';
|
|||
import Celebi from '../../templates/Celebi';
|
||||
|
||||
const ResumeViewer = ({ id }) => {
|
||||
const { t } = useTranslation();
|
||||
const [resume, setResume] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { getResume } = useContext(DatabaseContext);
|
||||
|
@ -42,7 +44,9 @@ const ResumeViewer = ({ id }) => {
|
|||
return (
|
||||
<div className={styles.container}>
|
||||
<Helmet>
|
||||
<title>{resume.name} | Reactive Resume</title>
|
||||
<title>
|
||||
{resume.name} | {t('shared.appName')}
|
||||
</title>
|
||||
<link rel="canonical" href={`https://rxresu.me/r/${id}`} />
|
||||
</Helmet>
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { memo, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PageContext from '../../../contexts/PageContext';
|
||||
import { safetyCheck } from '../../../utils';
|
||||
|
||||
|
@ -18,13 +19,16 @@ const ContactItem = ({ value, label, link }) => {
|
|||
};
|
||||
|
||||
const ContactC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data } = useContext(PageContext);
|
||||
|
||||
return (
|
||||
<div className="text-xs grid gap-2">
|
||||
{data.profile.address.line1 && (
|
||||
<div>
|
||||
<h6 className="capitalize font-semibold">Address</h6>
|
||||
<h6 className="capitalize font-semibold">
|
||||
{t('shared.forms.address')}
|
||||
</h6>
|
||||
<div className="flex flex-col text-xs">
|
||||
<span>{data.profile.address.line1}</span>
|
||||
<span>{data.profile.address.line2}</span>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { memo, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MdFlare } from 'react-icons/md';
|
||||
import PageContext from '../../../contexts/PageContext';
|
||||
import { safetyCheck } from '../../../utils';
|
||||
|
@ -19,6 +20,7 @@ const ContactItem = ({ value, label, link }) => {
|
|||
};
|
||||
|
||||
const ContactD = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data } = useContext(PageContext);
|
||||
|
||||
return (
|
||||
|
@ -42,7 +44,9 @@ const ContactD = () => {
|
|||
|
||||
{data.profile.address.line1 && (
|
||||
<div>
|
||||
<h6 className="capitalize font-semibold">Address</h6>
|
||||
<h6 className="capitalize font-semibold">
|
||||
{t('shared.forms.address')}
|
||||
</h6>
|
||||
<div className="flex flex-col text-xs">
|
||||
<span>{data.profile.address.line1}</span>
|
||||
<span>{data.profile.address.line2}</span>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { memo, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PageContext from '../../../contexts/PageContext';
|
||||
import { safetyCheck } from '../../../utils';
|
||||
|
||||
|
@ -18,14 +19,17 @@ const ContactItem = ({ value, label, link }) => {
|
|||
};
|
||||
|
||||
const ContactE = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data, heading: Heading } = useContext(PageContext);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Heading>Profile</Heading>
|
||||
<Heading>{t('builder.sections.profile')}</Heading>
|
||||
<div className="relative w-full grid gap-2 text-xs">
|
||||
<div>
|
||||
<h6 className="capitalize font-semibold">Address</h6>
|
||||
<h6 className="capitalize font-semibold">
|
||||
{t('shared.forms.address')}
|
||||
</h6>
|
||||
<div className="flex flex-col text-xs">
|
||||
<span>{data.profile.address.line1}</span>
|
||||
<span>{data.profile.address.line2}</span>
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { get, isEmpty } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const getModalText = (isEditMode, type) => {
|
||||
return isEditMode ? `Edit ${type}` : `Add ${type}`;
|
||||
const { t } = useTranslation();
|
||||
return isEditMode
|
||||
? `${t('shared.buttons.edit')} ${type}`
|
||||
: `${t('shared.buttons.add')} ${type}`;
|
||||
};
|
||||
|
||||
export const safetyCheck = (section, path = 'items') => {
|
||||
|
@ -30,6 +34,11 @@ export const getFieldProps = (formik, schema, name) => ({
|
|||
...formik.getFieldProps(name),
|
||||
});
|
||||
|
||||
export const getUnsplashPhoto = async () => {
|
||||
const response = await fetch('https://source.unsplash.com/featured/400x600');
|
||||
return response.url;
|
||||
};
|
||||
|
||||
export const hexToRgb = (hex) => {
|
||||
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
||||
hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
|
||||
|
|
Loading…
Reference in New Issue