Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,102 @@ __Tip__: Notice that you can use Markdown in the cover letter, summaries and des
## Thanks to
- SEO resume photo by [Markus Winkler](https://unsplash.com/@markuswinkler?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/resume?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText).

### Creating New Templates

Creating a new resume template allows for further customization and variety in how your resume data is presented. Here's how to add a new template to the Resume Builder:

**1. File Structure:**

Templates are located within the `src/components/ResumeTemplates/` directory. Each new template must reside in its own subdirectory. For example, if your new template is named "Elegant", its files would be in `src/components/ResumeTemplates/Elegant/`.

**2. Main Template File:**

Each template directory must contain an `Index.jsx` file (e.g., `src/components/ResumeTemplates/Elegant/Index.jsx`). This file is the entry point for your template and should export the main React component that renders the resume.

**3. Component Props:**

Your template component (e.g., `Elegant`) will receive several props:

* `jsonResume`: This object contains the parsed resume data, structured according to the JSON Resume schema (with any custom extensions used by this project). You'll use this data to populate your template.
* `isPrinting`: A boolean flag that indicates if the resume is currently being rendered for a print view. This can be used to apply print-specific styles.
* `customTranslations`: An object containing user-defined translations for various fields, allowing for internationalization of template labels.
* `coverLetterVariables`: An object containing variables extracted from the cover letter, which can be used for dynamic content if your template supports cover letters.

**4. Styling and Sub-components:**

You can style your template using Material-UI's `makeStyles` hook or any other CSS-in-JS solution you prefer. For more complex templates, you can break down the structure into smaller, reusable sub-components within your template's directory.

**5. Registering the Template in `gatsby-node.js`:**

For the build system and the frontend to recognize your new template, it needs to be "registered":

* Open the `gatsby-node.js` file located in the root of the project.
* Locate the `TEMPLATES_PATH` constant. This constant points to the directory where template folders are stored (i.e., `src/components/ResumeTemplates`).
* Find the `disabledTemplates` array. If you want your template to be active immediately, ensure its directory name is *not* included in this array.
* The build process reads the subdirectories within `TEMPLATES_PATH` and, after filtering out any `disabledTemplates`, creates a global variable called `TEMPLATES_LIST`. This list is then available to the frontend, allowing users to select your new template.

**6. Basic Example Structure (Conceptual):**

```jsx
// src/components/ResumeTemplates/MyNewTemplate/Index.jsx
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme) => ({
// Your styles here
container: {
padding: theme.spacing(2),
fontFamily: 'Arial, sans-serif', // Example style
},
header: {
textAlign: 'center',
marginBottom: theme.spacing(2),
},
sectionTitle: {
fontWeight: 'bold',
marginTop: theme.spacing(2),
marginBottom: theme.spacing(1),
borderBottom: '1px solid #ccc',
paddingBottom: theme.spacing(0.5),
}
}));

const MyNewTemplate = ({ jsonResume, isPrinting, customTranslations, coverLetterVariables }) => {
const classes = useStyles();
// Safely access data, providing fallbacks if jsonResume is null or parts are missing
const basics = jsonResume?.basics || {};
const work = jsonResume?.work || [];
// Example of using customTranslations or defaulting
const workTitle = customTranslations?.work || 'Work Experience';

return (
<div className={classes.container}>
<header className={classes.header}>
<h1>{basics.name || 'Your Name'}</h1>
<p>{basics.label || 'Your Title'}</p>
<p>{basics.email || '[email protected]'}</p>
</header>

<section>
<h2 className={classes.sectionTitle}>{workTitle}</h2>
{work.map((job, index) => (
<div key={index}>
<h3>{job.name} - {job.position}</h3>
<p>{job.startDate} - {job.endDate}</p>
<p>{job.summary}</p>
</div>
))}
</section>

{/* Add more sections for education, skills, etc. */}
</div>
);
};

export default MyNewTemplate;
```
This provides a basic framework. You would then flesh out the JSX to render all relevant parts of the `jsonResume` data according to your new template's design.

## F.A.Q.
**Q: Can you implement <???> function?**

Expand All @@ -393,7 +489,6 @@ A: Thank you! You can contribute by coding new features, creating pull requests,
## TODO
- Fix all TODOs in the code.
- Add PropTypes to the components (or even better, migrate to Typescript).
- Document how to create new templates.
- Add a form to manually input resume data.
- Support Redux Tools extension.
- Use [jsPDF](https://github.com/MrRio/jsPDF).
Expand Down
6 changes: 0 additions & 6 deletions README_V1.MD
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,6 @@ A: I can try. Open a issue and I'll see what I can do.

A: Thank you! You can help by codding more features, creating pull requests, or donating via [https://bunq.me/BuyMeASoda](https://bunq.me/BuyMeASoda)

## TODO
- Add list of recent used documents
- Add option to save a built resume
- Remove bootstrap include from template.html
- Make build.js smaller / add lazyloader

## License
MIT License

Expand Down
11 changes: 2 additions & 9 deletions README_V2.MD
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ First of all you need to create a Google Spreadsheet following the same rules as
- **countryCode:** Country code where you're located
- **network:** Social media name
- **username:** Social media name

TODO: list all
- **coverLetter**: Cover letter template
- **__translation__**: Custom translations

## Libraries
- I'm using [react.js](https://github.com/facebook/react), [js-xlsx](https://github.com/sheetjs/js-xlsx) and [Material UI](https://material-ui.com/).
Expand Down Expand Up @@ -187,13 +187,6 @@ A: I can try. Open a issue and I'll see what I can do.

A: Thank you! You can help by codding more features, creating pull requests, or donating via [https://bunq.me/BuyMeASoda](https://bunq.me/BuyMeASoda)

## TODO
- Add list of recent used documents
- Add option to save a built resume
- Add check for valid spreadsheet URL on text input
- Add various missing error handlers
- Add job offers based on uploaded CV? (check for online APIs)

## License
MIT License

Expand Down
9 changes: 0 additions & 9 deletions README_V3.MD
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,6 @@ A: I can try. Open an issue, and I'll see what I can do.

A: Thank you! You can help by codding more features, creating pull requests, or donating via [https://bunq.me/BuyMeASoda](https://bunq.me/BuyMeASoda)

## TODO
- Actually create a second template
- Add unit tests
- Add list of recent used documents
- Add option to save a built resume
- Add check for valid spreadsheet URL on text input
- Add various missing error handlers
- Add job offers based on uploaded CV? (check for online APIs)

## License
MIT License

Expand Down
11 changes: 7 additions & 4 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@ exports.onCreatePage = async ({ page, actions }) => {
exports.onCreateWebpackConfig = async ({ plugins, actions }) => {
const templates = await fs.readdir(TEMPLATES_PATH);

// TODO this fixes the 'React Refresh Babel' error when NODE_ENV is 'local' for some reason
if (process.env.NODE_ENV !== 'production') {
process.env.NODE_ENV = 'development';
}
// TODO: The following lines were a workaround for a 'React Refresh Babel' error that occurred when NODE_ENV was set to 'local'.
// This workaround forced NODE_ENV to 'development' in such cases.
// It has been commented out to see if the underlying issue has been resolved or to encourage a more proper fix.
// If the 'React Refresh Babel' error reappears when NODE_ENV is 'local', this workaround might need to be reinstated or, preferably, the root cause addressed.
// if (process.env.NODE_ENV !== 'production') {
// process.env.NODE_ENV = 'development';
// }

actions.setWebpackConfig({
plugins: [
Expand Down
4 changes: 2 additions & 2 deletions src/components/DropZone.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ const DropZone = ({ handleFile, disabled, maxLength }) => {
}

if (files.length > maxLength) {
// TODO
alert(intl.formatMessage({ id: 'file_limit_exceeded' }));
} else {
// do what ever you want
handleFile(files[0]);
}
},
[handleFile, maxLength]
[handleFile, intl, maxLength] // Added intl to dependency array
);

return (
Expand Down
4 changes: 3 additions & 1 deletion src/components/LanguageSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MenuItem, Select } from '@material-ui/core';
import { IconFlagBR, IconFlagUS, IconFlagES, IconFlagFR, IconFlagRU, IconFlagDE } from 'material-ui-flags';
import { makeStyles } from '@material-ui/core/styles';
import { changeLocale } from 'gatsby-plugin-react-intl';
import PropTypes from 'prop-types';
import IconFlagJP from './IconFlagJA';

const useStyles = makeStyles((theme) => ({
Expand Down Expand Up @@ -64,7 +65,8 @@ const LanguageSelector = ({ currentLocale, onLanguageChange }) => {
};

LanguageSelector.propTypes = {
// TODO
currentLocale: PropTypes.string.isRequired,
onLanguageChange: PropTypes.func,
};

export default LanguageSelector;
3 changes: 1 addition & 2 deletions src/components/ResumeDrawerItems/Items/Basics.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ function Basics({ basics }) {
/>
)}
<ItemsList
// TODO varNameToString({ location })
label="location"
label={varNameToString({ location })}
checked={locationEnabled}
onClick={toggleBasicsDetail('location')}
/>
Expand Down
3 changes: 1 addition & 2 deletions src/components/ResumeDrawerItems/Items/Education.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ function Education({ education: educations }) {
return (
<div className={classes.resumeDrawerItem}>
<ItemInput
// TODO varNameToString({ education })
label="education"
label={varNameToString({ education: educations })}
checked={educations?.enabled}
onChange={toggleEducations}
/>
Expand Down
3 changes: 1 addition & 2 deletions src/components/ResumeDrawerItems/Items/Interests.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ function Interest({ interests }) {
return (
<div className={classes.resumeDrawerItem}>
<ItemInput
// TODO varNameToString({ interest })
label="interest"
label={varNameToString({ interests })}
onChange={toggleInterests}
checked={interests?.enabled}
/>
Expand Down
3 changes: 1 addition & 2 deletions src/components/ResumeDrawerItems/Items/Volunteer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ function Volunteer({ volunteer: volunteerData }) {
return (
<div className={classes.resumeDrawerItem}>
<ItemInput
// TODO varNameToString({ volunteer })
label="volunteer"
label={varNameToString({ volunteer: volunteerData })}
onChange={toggleVolunteers}
checked={volunteerEnabled}
/>
Expand Down
3 changes: 1 addition & 2 deletions src/components/ResumeDrawerItems/Items/Work.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ function Work({ work: workData }) {
return (
<div className={classes.resumeDrawerItem}>
<ItemInput
// TODO varNameToString({ work })
label="work"
label={varNameToString({ work: workData })}
onChange={toggleWorks}
checked={workEnabled}
/>
Expand Down
8 changes: 4 additions & 4 deletions src/components/TemplateSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ import { MenuItem, Select } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { v4 as uuid } from 'uuid';
import { useIntl } from 'gatsby-plugin-react-intl';
import PropTypes from 'prop-types';

// Hooks
import { useSelector } from '../store/StoreProvider';

// Actions
import { selectResumeTemplate } from '../store/selectors';

const useStyles = makeStyles((theme) => ({
// TODO
}));
const useStyles = makeStyles((theme) => ({}));

const TemplateSelector = ({ onSelect, className }) => {
const intl = useIntl();
Expand Down Expand Up @@ -41,7 +40,8 @@ const TemplateSelector = ({ onSelect, className }) => {
};

TemplateSelector.propTypes = {
// TODO
onSelect: PropTypes.func.isRequired,
className: PropTypes.string,
};

export default TemplateSelector;
10 changes: 8 additions & 2 deletions src/intl/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@
},
"error": {
"something_went_wrong_parsing": "Hoppla, beim Parsen Ihrer Tabellen-URL ist etwas schiefgelaufen. Versuchen Sie, die Tabelle herunterzuladen und hier hochzuladen.",
"something_went_wrong_loading": "Hoppla, etwas ist schiefgelaufen, bitte versuchen Sie es erneut."
"something_went_wrong_loading": "Hoppla, etwas ist schiefgelaufen, bitte versuchen Sie es erneut.",
"invalid_google_sheet_url": "Please enter a valid Google Spreadsheet URL.",
"resume_fetch_failed": "Could not fetch the resume data. Please check the username and repository.",
"resume_invalid_json": "The fetched resume data is not valid JSON.",
"resume_empty_json": "The fetched resume data is empty or invalid.",
"resume_processing_failed": "Failed to process the resume data."
},
"notfound": {
"title": "404: Nicht gefunden",
Expand Down Expand Up @@ -186,5 +191,6 @@
"third_party_cookies_item_2": "Von Zeit zu Zeit testen wir neue Funktionen und nehmen subtile Änderungen an der Art und Weise vor, wie die Website bereitgestellt wird. Wenn wir noch neue Funktionen testen, können diese Cookies verwendet werden, um sicherzustellen, dass Sie während der Nutzung der Seite eine konsistente Erfahrung erhalten und um sicherzustellen, dass wir verstehen, welche Optimierungen unsere Benutzer am meisten schätzen.",
"more_information": "Weitere Informationen",
"more_information_text": "Hoffentlich hat dies die Dinge für Sie geklärt. Wie bereits erwähnt, ist es in der Regel sicherer, Cookies aktiviert zu lassen, falls sie mit einer der Funktionen interagieren, die Sie auf unserer Seite verwenden."
}
},
"file_limit_exceeded": "You have exceeded the maximum number of files allowed."
}
10 changes: 8 additions & 2 deletions src/intl/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@
},
"error": {
"something_went_wrong_parsing": "Whoops, something went wrong when parsing your spreadsheet URL. Try downloading the spreadsheet and uploading it here.",
"something_went_wrong_loading": "Whoops, something went wrong, please try again."
"something_went_wrong_loading": "Whoops, something went wrong, please try again.",
"invalid_google_sheet_url": "Please enter a valid Google Spreadsheet URL.",
"resume_fetch_failed": "Could not fetch the resume data. Please check the username and repository.",
"resume_invalid_json": "The fetched resume data is not valid JSON.",
"resume_empty_json": "The fetched resume data is empty or invalid.",
"resume_processing_failed": "Failed to process the resume data."
},
"notfound": {
"title": "404: Not found",
Expand Down Expand Up @@ -186,5 +191,6 @@
"third_party_cookies_item_2": "From time to time we test new features and make subtle changes to the way that the site is delivered. When we are still testing new features these cookies may be used to ensure that you receive a consistent experience whilst on the site whilst ensuring we understand which optimisations our users appreciate the most.",
"more_information": "More information",
"more_information_text": "Hopefully that has clarified things for you and as was previously mentioned if there is something that you aren't sure whether you need or not it's usually safer to leave cookies enabled in case it does interact with one of the features you use on our site."
}
},
"file_limit_exceeded": "You have exceeded the maximum number of files allowed."
}
14 changes: 10 additions & 4 deletions src/intl/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@
},
"error": {
"something_went_wrong_parsing": "¡Vaya! Se produjo un error al descargar la hoja de cálculo desde la URL proporcionada. Intente descargar la hoja de cálculo manualmente y cárguela aquí.",
"something_went_wrong_loading": "Vaya, algo salió mal. Vuelve a intentarlo."
"something_went_wrong_loading": "Vaya, algo salió mal. Vuelve a intentarlo.",
"invalid_google_sheet_url": "Please enter a valid Google Spreadsheet URL.",
"resume_fetch_failed": "Could not fetch the resume data. Please check the username and repository.",
"resume_invalid_json": "The fetched resume data is not valid JSON.",
"resume_empty_json": "The fetched resume data is empty or invalid.",
"resume_processing_failed": "Failed to process the resume data."
},
"notfound": {
"title": "404: No Encontrado",
Expand All @@ -182,9 +187,10 @@
"site_preferences_cookie_text": "Con el fin de proveerte una gran experiencia en este sitio te proveemos la capacidad de configurar cómo se correra este sitio cuando tu lo uses. Con el fin de recordar tus configuraciones necesitamos almacenar cookies para que esta información pueda ser invocada cada vez que interactuas con una página que es afectada por tus configuraciones.",
"third_party_cookies": "Cookies de terceros",
"third_party_cookies_text": "En algunos casos especiales también utilizamos cookies proveidas por terceros de confianza. La sección a continuación indica con detalles qué cookies de terceros podrás encontrar en este sitio.",
"third_party_cookies_item_1": "Este sitio usa las herramientas de analítica de Google que es una de las soluciones de analítica más reconocida y de confianza en la web para ayudarnos a entender cómo usas el sitio y cómo podemos mejorar tu experiencia. Estas cookies puden rastrear cosas como cuánto tiempo permaneces en el sitio y las páginas que visitas para que podamos seguir produciendo contenido atractivo. Para más información sobre cookies de las herramientas de analítica de Google, visite la página oficial.",
"third_party_cookies_item_2": "De vez en cuando probamos nuevas funcionalidades y realizamos cambios sutiles en la forma en como mostramos el sitio. Cuando seguimos probando nuevas funcionalidades estas cookies pueden ser usadas para asegurar que recibas una experiencia consistente mientras que nos aseguramos de entender que optimizaciones son más apreciadas por los usuarios.",
"third_party_cookies_item_1": "Este sitio usa las herramientas de analítica de Google che è una delle soluzioni di analítica más reconocida y de confianza en la web para ayudarnos a entender cómo usas el sitio y cómo podemos mejorar tu experiencia. Estas cookies puden rastrear cosas como cuánto tiempo permaneces en el sitio y las páginas que visitas para que podamos seguir produciendo contenido atractivo. Para más información sobre cookies de las herramientas de analítica de Google, visite la página oficial.",
"third_party_cookies_item_2": "De vez en quando probamos nuevas funcionalidades y realizamos cambios sutiles en la forma en como mostramos el sitio. Cuando seguimos probando nuevas funcionalidades estas cookies pueden ser usadas para asegurar que recibas una experiencia consistente mientras que nos aseguramos de entender que optimizaciones son más apreciadas por los usuarios.",
"more_information": "Más información",
"more_information_text": "Ojalá esto halla aclarado las cosas para ti y como mencionamos anteriormente si hay algo de lo que no estás seguro si es necesaria o no, usualmente es más seguro dejar las cookies habilitadas en caso que estas interactuen con alguna de las características que usas en nuestro sitio."
}
},
"file_limit_exceeded": "You have exceeded the maximum number of files allowed."
}
Loading