├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── README.md ├── docs ├── CONFIGURATION.md ├── DEPLOYMENT.md └── assets │ ├── logo.png │ ├── promo-1.png │ ├── promo-2.png │ ├── promo-3.png │ ├── promo-4.png │ └── spacer.png ├── index.html ├── npm ├── npm-resume-clear.js └── snippets │ ├── _npm-files.js │ ├── _npm-json.js │ └── _npm-log.js ├── package-lock.json ├── package.json ├── public ├── data │ ├── categories.json │ ├── profile.json │ ├── sections.json │ ├── sections │ │ ├── achievements.json │ │ ├── contact.json │ │ ├── cover.json │ │ ├── education.json │ │ ├── experience.json │ │ ├── hobbies.json │ │ ├── portfolio.json │ │ └── skills.json │ ├── settings.json │ └── strings.json └── images │ ├── flags │ ├── _more-flags.txt │ ├── ar-dz.png │ ├── ar-ma.png │ ├── ar-tn.png │ ├── cs.png │ ├── da.png │ ├── de-ch.png │ ├── de-li.png │ ├── de.png │ ├── dk.png │ ├── el.png │ ├── en-au.png │ ├── en-ca.png │ ├── en-nz.png │ ├── en-uk.png │ ├── en-us.png │ ├── es-ar.png │ ├── es-cl.png │ ├── es-co.png │ ├── es-mx.png │ ├── es-pr.png │ ├── es-uy.png │ ├── es-vz.png │ ├── es.png │ ├── et.png │ ├── fi.png │ ├── fr-be.png │ ├── fr.png │ ├── ge.png │ ├── hi.png │ ├── hr.png │ ├── hu.png │ ├── id.png │ ├── it.png │ ├── ja.png │ ├── ko.png │ ├── ml.png │ ├── nl.png │ ├── no.png │ ├── pk.png │ ├── pl.png │ ├── pt-br.png │ ├── pt-pt.png │ ├── ro.png │ ├── ru-kz.png │ ├── ru.png │ ├── sk.png │ ├── sl.png │ ├── sv.png │ ├── th.png │ ├── tn.png │ ├── tr.png │ ├── tw.png │ ├── un.png │ ├── uz.png │ ├── vi.png │ ├── zh-hk.png │ ├── zh-sg.png │ ├── zh-tw.png │ └── zh.png │ ├── icons │ ├── resume.ico │ └── resume.svg │ ├── pictures │ └── avatar.png │ ├── places │ ├── logo-adobe.png │ ├── logo-apple.png │ ├── logo-coursera.png │ ├── logo-harvard.png │ ├── logo-paris-university.png │ ├── logo-udacity.png │ └── logo-ux-studio.png │ └── portfolio │ ├── project-logo-1.png │ ├── project-logo-2.png │ ├── project-logo-3.png │ ├── project-logo-4.png │ ├── project-logo-5.png │ ├── project-logo-6.png │ ├── project-logo-7.png │ ├── project-logo-8.png │ └── project-logo-9.png ├── src ├── composables │ ├── constants.js │ ├── emails.js │ ├── scheduler.js │ └── utils.js ├── main.js ├── models │ ├── Article.js │ ├── ArticleItem.js │ ├── Category.js │ ├── ContactOption.js │ ├── Language.js │ ├── Locales.js │ ├── Profile.js │ ├── Section.js │ └── Settings.js ├── scss │ ├── _layout.scss │ ├── _mixins.scss │ ├── _theming.scss │ ├── _typography.scss │ ├── _variables.scss │ └── style.scss └── vue │ ├── components │ ├── articles │ │ ├── base │ │ │ ├── Article.vue │ │ │ ├── ArticleTitle.vue │ │ │ └── ArticleWidgetLinkList.vue │ │ ├── contact │ │ │ ├── ArticleContactForm.vue │ │ │ ├── ArticleContactFormFields.vue │ │ │ ├── ArticleContactFormThankYou.vue │ │ │ └── ArticleContactOptions.vue │ │ ├── portfolio │ │ │ ├── ArticlePortfolio.vue │ │ │ └── ArticlePortfolioItem.vue │ │ ├── profile │ │ │ ├── ArticleProfile.vue │ │ │ └── ArticleProfileItem.vue │ │ ├── skills │ │ │ ├── ArticleSkills.vue │ │ │ ├── ArticleSkillsItem.vue │ │ │ └── ArticleSkillsPieChart.vue │ │ ├── thread │ │ │ ├── ArticleThread.vue │ │ │ └── ArticleThreadItem.vue │ │ ├── timeline │ │ │ ├── ArticleTimeline.vue │ │ │ ├── ArticleTimelineItem.vue │ │ │ └── ArticleTimelineItemContent.vue │ │ └── unavailable │ │ │ └── ArticleUnavailable.vue │ ├── loaders │ │ ├── ActivitySpinner.vue │ │ └── Loader.vue │ ├── modals │ │ ├── base │ │ │ └── Modal.vue │ │ └── project │ │ │ ├── ProjectModal.vue │ │ │ └── ProjectModalContent.vue │ ├── navigation │ │ ├── NavigationWrapper.vue │ │ ├── controls │ │ │ ├── NavControlAllAtOnce.vue │ │ │ └── NavControlOneAtOnce.vue │ │ ├── layout │ │ │ ├── NavMobileHeader.vue │ │ │ ├── NavProfileCard.vue │ │ │ └── NavToggleButton.vue │ │ ├── sidebar │ │ │ ├── NavSidebar.vue │ │ │ ├── NavSidebarFooter.vue │ │ │ └── NavSidebarLinkList.vue │ │ └── tabs │ │ │ ├── NavPillsController.vue │ │ │ ├── NavPillsControllerFixed.vue │ │ │ └── NavTabController.vue │ ├── sections │ │ ├── Section.vue │ │ ├── SectionBody.vue │ │ └── SectionHeader.vue │ └── widgets │ │ ├── Alert.vue │ │ ├── FaButton.vue │ │ ├── FilterTabs.vue │ │ ├── IconView.vue │ │ ├── ImageView.vue │ │ ├── InfoBadge.vue │ │ ├── InlineInfoList.vue │ │ ├── InlineLinkList.vue │ │ ├── LanguagePicker.vue │ │ ├── OptionsList.vue │ │ ├── ProgressBar.vue │ │ ├── SocialLinks.vue │ │ ├── SolidDivider.vue │ │ ├── Spinner.vue │ │ ├── Tags.vue │ │ └── XLButton.vue │ └── stack │ ├── App.vue │ ├── DataManager.vue │ ├── FeedbacksManager.vue │ ├── LanguageManager.vue │ ├── LocationManager.vue │ ├── ModalManager.vue │ ├── Resume.vue │ └── WindowObserver.vue └── vite.config.js /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy resume to GitHub Pages 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build-deploy: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | # Checkout the repository 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | # Setup Node.js 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 18 23 | 24 | # Install dependencies 25 | - name: Install dependencies 26 | run: npm install 27 | 28 | # Build the project 29 | - name: Build project 30 | run: npm run build 31 | 32 | # Deploy to GitHub pages 33 | - name: Deploy to GitHub pages 34 | uses: peaceiris/actions-gh-pages@v3 35 | with: 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | publish_dir: dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist-ssr 12 | *.local 13 | dist/ 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /docs/DEPLOYMENT.md: -------------------------------------------------------------------------------- 1 | ## Deployment 2 | 3 | ### 1. Vite configuration 4 | Open `vite.config.js` and set the base directory for your application. This setting defines the main path that your website will be hosted under. 5 | 6 | ```js 7 | export default defineConfig({ 8 | base: '/vue-resume-template/', 9 | plugins: [vue()], 10 | }) 11 | ``` 12 | 13 | In simple terms, if you consider GitHub hosting the GitHub Pages site for this repo at the URL `https://ryanbalieiro.github.io/vue-resume-template/`, the correct base directory to set is `/vue-resume-template/`. 14 | 15 | If you're deploying to Netlify or your own custom domain where your website is located at the root, you can leave the `base` setting as `'/'`. 16 | 17 | ### 2. Building for production 18 | To compile your project for production, execute: 19 | 20 | ``` 21 | npm run build 22 | ``` 23 | 24 | This command triggers a series of processes that package your code, assets, and other necessary files, ultimately creating a production-ready version of your project. After running the command, you'll find the compiled files within the `dist` folder. 25 | 26 | ### 3. Deploying to GitHub pages 27 | 28 | The project comes with a preconfigured GitHub Actions workflow that automatically builds the project and deploys it to GitHub Pages. You can find and customize this workflow in the `.github` folder if needed. 29 | 30 | Once you push the project to GitHub, the workflow will handle the build process and publish your site to a branch named `gh-pages`. Make sure you've correctly set up the required repository permissions and GitHub Pages settings. If you encounter a build error, it's likely related to missing or misconfigured permissions. 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/docs/assets/logo.png -------------------------------------------------------------------------------- /docs/assets/promo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/docs/assets/promo-1.png -------------------------------------------------------------------------------- /docs/assets/promo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/docs/assets/promo-2.png -------------------------------------------------------------------------------- /docs/assets/promo-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/docs/assets/promo-3.png -------------------------------------------------------------------------------- /docs/assets/promo-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/docs/assets/promo-4.png -------------------------------------------------------------------------------- /docs/assets/spacer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/docs/assets/spacer.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Resume Template – Vue 3 + Bootstrap 5 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /npm/npm-resume-clear.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Ryan Balieiro 3 | * @description Use this script to wipe all existing resume data and start fresh with a blank resume. 4 | * 5 | * @usage 6 | * npm run resume:clear 7 | */ 8 | import {useNpmLogger} from "./snippets/_npm-log.js" 9 | import {useNpmFileUtils} from "./snippets/_npm-files.js" 10 | import {useNpmJsonUtils} from "./snippets/_npm-json.js" 11 | 12 | const logger = useNpmLogger() 13 | const fileUtils = useNpmFileUtils() 14 | const jsonUtils = useNpmJsonUtils() 15 | 16 | const foldersToDelete = [ 17 | 'public/images/pictures', 18 | 'public/images/places', 19 | 'public/images/portfolio' 20 | ] 21 | 22 | const foldersToEmpty = [ 23 | 'public/data/sections' 24 | ] 25 | 26 | foldersToDelete.forEach(fileUtils.deleteFolder) 27 | foldersToEmpty.forEach(fileUtils.emptyFolder) 28 | 29 | const jSettingsPath = "public/data/settings.json" 30 | const jSettings = jsonUtils.open(jSettingsPath) 31 | jSettings.supportedLanguages = [jSettings.supportedLanguages[0]] 32 | jsonUtils.save(jSettingsPath, jSettings) 33 | 34 | const jStringsPath = "public/data/strings.json" 35 | const jStrings = jsonUtils.open(jStringsPath) 36 | delete jStrings.locales["es"] 37 | delete jStrings.locales["fr"] 38 | delete jStrings.locales["zh"] 39 | jsonUtils.save(jStringsPath, jStrings) 40 | 41 | const jSectionsPath = "public/data/sections.json" 42 | jsonUtils.update(jSectionsPath, { 43 | sections: [] 44 | }) 45 | 46 | const jCategoriesPath = "public/data/categories.json" 47 | jsonUtils.update(jCategoriesPath, { 48 | categories: [] 49 | }) 50 | 51 | const jProfilePath = "public/data/profile.json" 52 | jsonUtils.update(jProfilePath, { 53 | name: "John Doe", 54 | profilePictureUrl: "", 55 | locales: { 56 | en: { 57 | name: "John Doe", 58 | credits: "Designed by Ryan Balieiro", 59 | role: "Role" 60 | } 61 | }, 62 | contactOptions: [] 63 | }) 64 | 65 | logger.log(logger.LogTypes.SUCCESS_FINISHED, "Successfully cleared resume data!") 66 | -------------------------------------------------------------------------------- /npm/snippets/_npm-files.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Ryan Balieiro 3 | * @description Handy utilities to help manage files within your npm scripts. 4 | */ 5 | import {useNpmLogger} from "./_npm-log.js" 6 | import path from "path" 7 | import fs from "fs" 8 | 9 | const logger = useNpmLogger() 10 | const baseDir = process.cwd() 11 | 12 | export const useNpmFileUtils = () => { 13 | /** 14 | * @param {string} folderPath 15 | */ 16 | const deleteFolder = (folderPath) => { 17 | const fullPath = path.resolve(baseDir, folderPath) 18 | if(!fs.existsSync(fullPath)) { 19 | logger.log(logger.LogTypes.SKIP, `Skipped folder: ${folderPath}`) 20 | return 21 | } 22 | 23 | fs.rmSync(fullPath, { recursive: true, force: true }) 24 | logger.log(logger.LogTypes.SUCCESS, `Deleted folder: ${folderPath}`) 25 | } 26 | 27 | /** 28 | * @param {string} folderPath 29 | */ 30 | const emptyFolder = (folderPath) => { 31 | const fullPath = path.resolve(baseDir, folderPath) 32 | if(!fs.existsSync(fullPath)) { 33 | logger.log(logger.LogTypes.SKIP, `Skipped folder: ${folderPath}`) 34 | return 35 | } 36 | 37 | const contents = fs.readdirSync(fullPath) 38 | contents.forEach(item => { 39 | const itemPath = path.join(fullPath, item) 40 | fs.rmSync(itemPath, { recursive: true, force: true }) 41 | }) 42 | logger.log(logger.LogTypes.SUCCESS, `Emptied folder: ${folderPath}`) 43 | } 44 | 45 | return { 46 | deleteFolder, 47 | emptyFolder 48 | } 49 | } -------------------------------------------------------------------------------- /npm/snippets/_npm-json.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Ryan Balieiro 3 | * @description Handy utilities to help manage JSON files within your npm scripts. 4 | */ 5 | import {useNpmLogger} from "./_npm-log.js" 6 | import path from "path" 7 | import fs from "fs" 8 | 9 | const logger = useNpmLogger() 10 | const baseDir = process.cwd() 11 | 12 | export const useNpmJsonUtils = () => { 13 | /** 14 | * @param {String} jsonPath 15 | * @return {*} 16 | */ 17 | const open = (jsonPath) => { 18 | let jsonData = {} 19 | const evaluation = evaluatePath(jsonPath) 20 | 21 | if(!evaluation.fileExists) { 22 | logger.log(logger.LogTypes.WARNING, `Couldn't find JSON file: ${jsonPath}. Returning empty object instead.`) 23 | return jsonData 24 | } 25 | 26 | try { 27 | jsonData = JSON.parse(fs.readFileSync(jsonPath, 'utf-8')) 28 | } catch (e) { 29 | logger.log(logger.LogTypes.WARNING, `Couldn't parse JSON data: ${jsonPath}.`) 30 | } 31 | 32 | return jsonData 33 | } 34 | 35 | /** 36 | * @param {String} jsonPath 37 | * @param {Object} jsonData 38 | */ 39 | const save = (jsonPath, jsonData) => { 40 | const evaluation = evaluatePath(jsonPath) 41 | if(!evaluation.fileExists) { 42 | logger.log(logger.LogTypes.WARNING, `Couldn't find JSON file: ${jsonPath}.`) 43 | return 44 | } 45 | 46 | try { 47 | fs.writeFileSync(jsonPath, JSON.stringify(jsonData, null, 4)) 48 | logger.log(logger.LogTypes.SUCCESS, `Updated JSON file: ${jsonPath}`) 49 | } 50 | catch (err) { 51 | logger.log(logger.LogTypes.WARNING, `Error saving JSON: ${jsonPath}`) 52 | } 53 | } 54 | 55 | /** 56 | * @param {String} jsonPath 57 | * @param {Object} newValues 58 | */ 59 | const update = (jsonPath, newValues) => { 60 | const jLoaded = open(jsonPath) 61 | if(!jLoaded) 62 | return 63 | 64 | for(let field in newValues) 65 | jLoaded[field] = newValues[field] 66 | save(jsonPath, jLoaded) 67 | } 68 | 69 | /** 70 | * @param {String} jsonPath 71 | * @return {{resolvedPath: String, jsonPath: String, fileExists: Boolean}} 72 | */ 73 | const evaluatePath = (jsonPath) => { 74 | const resolvedPath = path.resolve(baseDir, jsonPath) 75 | const fileExists = fs.existsSync(resolvedPath) 76 | return {jsonPath, resolvedPath, fileExists} 77 | } 78 | 79 | return { 80 | open, 81 | save, 82 | update, 83 | evaluatePath, 84 | } 85 | } -------------------------------------------------------------------------------- /npm/snippets/_npm-log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Ryan Balieiro 3 | * @description Handy utilities to present console messages within your npm scripts. 4 | */ 5 | export const useNpmLogger = () => { 6 | /** 7 | * @enum 8 | */ 9 | const LogTypes = { 10 | DEFAULT: "log_type_default", 11 | WARNING: "log_type_warning", 12 | SKIP: "log_type_skip", 13 | SUCCESS: "log_type_success", 14 | SUCCESS_FINISHED: "log_type_success_finished", 15 | } 16 | 17 | /** 18 | * @constant 19 | * @dictionary 20 | */ 21 | const LOG_TYPES_MAPPING = { 22 | [LogTypes.DEFAULT]: { emoji: "⬛" }, 23 | [LogTypes.WARNING]: { emoji: "⚠️" }, 24 | [LogTypes.SKIP]: { emoji: "⏭️" }, 25 | [LogTypes.SUCCESS]: { emoji: "✅" }, 26 | [LogTypes.SUCCESS_FINISHED]: { emoji: "🎉" }, 27 | } 28 | 29 | /** 30 | * @param {LogTypes} type 31 | * @param {String} message 32 | */ 33 | const log = (type, message) => { 34 | const logTypeData = LOG_TYPES_MAPPING[type] || LOG_TYPES_MAPPING[LogTypes.DEFAULT] 35 | console.log(`${logTypeData.emoji} · ${message}`) 36 | } 37 | 38 | return { 39 | LogTypes, 40 | log 41 | } 42 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resume-template", 3 | "private": true, 4 | "version": "2.1.3", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "deploy": "gh-pages -d dist", 11 | "resume:clear": "node npm/npm-resume-clear.js" 12 | }, 13 | "dependencies": { 14 | "@emailjs/browser": "^4.4.1", 15 | "@fortawesome/fontawesome-free": "^6.4.0", 16 | "@popperjs/core": "^2.11.7", 17 | "bootstrap": "^5.2.3", 18 | "chart.js": "^4.4.8", 19 | "emailjs": "^4.0.3", 20 | "gh-pages": "^6.3.0", 21 | "swiper": "^10.0.4", 22 | "vue": "^3.2.47", 23 | "vue-chartjs": "^5.3.2", 24 | "vue-router": "^4.2.4" 25 | }, 26 | "devDependencies": { 27 | "@vitejs/plugin-vue": "^5.2.3", 28 | "sass": "^1.62.1", 29 | "vite": "^6.2.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/data/categories.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories": [ 3 | { 4 | "id": "home", 5 | "faIcon": "fa-regular fa-user", 6 | "locales": { 7 | "en": {"nav_label": "Home"}, 8 | "es": {"nav_label": "Inicio"}, 9 | "fr": {"nav_label": "Accueil"}, 10 | "zh": {"nav_label": "主页"} 11 | } 12 | }, 13 | 14 | { 15 | "id": "background", 16 | "faIcon": "fa-regular fa-clock", 17 | "locales": { 18 | "en": {"nav_label": "Background"}, 19 | "es": {"nav_label": "Historial"}, 20 | "fr": {"nav_label": "Historique"}, 21 | "zh": {"nav_label": "背景"} 22 | } 23 | }, 24 | 25 | { 26 | "id": "showcase", 27 | "faIcon": "fa-regular fa-folder-open", 28 | "locales": { 29 | "en": {"nav_label": "Showcase"}, 30 | "es": {"nav_label": "Vitrina"}, 31 | "fr": {"nav_label": "Vitrine"}, 32 | "zh": {"nav_label": "展示"} 33 | } 34 | }, 35 | 36 | { 37 | "id": "more", 38 | "faIcon": "fa-regular fa-object-ungroup", 39 | "locales": { 40 | "en": {"nav_label": "More"}, 41 | "es": {"nav_label": "Más"}, 42 | "fr": {"nav_label": "Plus"}, 43 | "zh": {"nav_label": "查看更多"} 44 | } 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /public/data/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Roy Sheppard", 3 | "profilePictureUrl": "images/pictures/avatar.png", 4 | 5 | "locales": { 6 | "en": { 7 | "credits": "Designed by Ryan Balieiro", 8 | "role": "UX Designer" 9 | }, 10 | 11 | "es": { 12 | "credits": "Hecho por Ryan Balieiro", 13 | "role": "Designer de UX" 14 | }, 15 | 16 | "fr": { 17 | "credits": "Conçu par Ryan Balieiro", 18 | "role": "Designer UX" 19 | }, 20 | 21 | "zh": { 22 | "name": "罗伊谢帕德", 23 | "credits": "设计:Ryan Balieiro", 24 | "role": "用户体验设计师" 25 | } 26 | }, 27 | 28 | "contactOptions": [ 29 | { 30 | "id": "address", 31 | "value": "123 Main Street, Cupertino, CA 01234", 32 | "valueShort": "Cupertino – CA", 33 | "faIcon": "fa-regular fa-map", 34 | "href": null 35 | }, 36 | 37 | { 38 | "id": "email", 39 | "value": "roy.sheppard@genius.com", 40 | "valueShort": "roy.sheppard@genius.com", 41 | "faIcon": "fa-regular fa-envelope", 42 | "href": "mailto:roy.sheppard@genius.com" 43 | }, 44 | 45 | { 46 | "id": "facebook", 47 | "value": "Roy Sheppard", 48 | "valueShort": "Roy S.", 49 | "faIcon": "fa-brands fa-facebook", 50 | "href": "https://facebook.com" 51 | }, 52 | 53 | { 54 | "id": "github", 55 | "value": "@roy.sheppard.official", 56 | "valueShort": "@roy.sheppard.official", 57 | "faIcon": "fa-brands fa-github", 58 | "href": "https://github.com" 59 | }, 60 | 61 | { 62 | "id": "instagram", 63 | "value": "@roy.sheppard.official", 64 | "valueShort": "@roy.sheppard.official", 65 | "faIcon": "fa-brands fa-instagram", 66 | "href": "https://instagram.com" 67 | }, 68 | 69 | { 70 | "id": "linkedin", 71 | "value": "@roy.sheppard.official", 72 | "valueShort": "@roy.sheppard.official", 73 | "faIcon": "fa-brands fa-linkedin", 74 | "href": "https://linkedin.com" 75 | }, 76 | 77 | { 78 | "id": "phone", 79 | "value": "+1 (123) 456-7890", 80 | "valueShort": "(123) 456-7890", 81 | "faIcon": "fa-brands fa-whatsapp", 82 | "href": "tel:1234567890" 83 | } 84 | ] 85 | } -------------------------------------------------------------------------------- /public/data/sections/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "articles": [ 3 | { 4 | "id": 1, 5 | "component": "ArticleContactForm", 6 | "locales": { 7 | "en": { 8 | "contact_thank_you": "*Thanks* for the contact!", 9 | "contact_thank_you_description": "Your message has been received, and I'll be reaching out to you soon. I appreciate your time and look forward to connecting with you!", 10 | "contact_thank_you_reply": "The reply will be sent to your email address *{email}*." 11 | }, 12 | "es": { 13 | "contact_thank_you": "*¡Gracias* por contactar!", 14 | "contact_thank_you_description": "Tu mensaje ha sido recibido y me pondré en contacto contigo pronto. Aprecio tu tiempo y espero poder conectar contigo.", 15 | "contact_thank_you_reply": "Mi respuesta será enviada a tu correo electrónico *{email}*." 16 | }, 17 | "fr": { 18 | "contact_thank_you": "*Merci* pour le contact !", 19 | "contact_thank_you_description": "Votre message a été reçu, et je vous contacterai bientôt. J'apprécie votre temps et j’ai hâte d’échanger avec vous !", 20 | "contact_thank_you_reply": "Notre réponse sera envoyée à votre adresse e-mail *{email}*." 21 | }, 22 | "zh": { 23 | "contact_thank_you": "*感谢*您的联系!", 24 | "contact_thank_you_description": "您的消息已收到,我会尽快与您联系。感谢您的时间,期待与您交流!", 25 | "contact_thank_you_reply": "我们的回复将发送至您的电子邮件地址 *{email}*。" 26 | } 27 | }, 28 | "settings": { 29 | "order_items_by": "id", 30 | "order_items_sort": "asc", 31 | "contact_js_public_key": "tzObcsmOwHvPy6Rhp", 32 | "contact_js_service_id": "service_4h8g7vb", 33 | "contact_js_template_id": "template_kplo17d" 34 | }, 35 | "items": null 36 | }, 37 | 38 | { 39 | "id": 2, 40 | "component": "ArticleContactOptions", 41 | "locales": { 42 | "en": { 43 | "title": "Or...", 44 | "description": "If you prefer, you can reach out to me through the following options:" 45 | }, 46 | "es": { 47 | "title": "O...", 48 | "description": "Si prefieres, puedes ponerte en contacto conmigo a través de las siguientes opciones:" 49 | }, 50 | "fr": { 51 | "title": "Ou...", 52 | "description": "Si vous préférez, vous pouvez me contacter grâce aux options suivantes:" 53 | }, 54 | "zh": { 55 | "title": "或者...", 56 | "description": "如果您愿意,您可以通过以下方式与我取得联系:" 57 | } 58 | }, 59 | "settings": { 60 | "order_items_by": "id", 61 | "order_items_sort": "asc", 62 | "contact_ids": ["address", "phone", "email", "linkedin", "facebook", "github"] 63 | }, 64 | "items": null 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /public/data/sections/cover.json: -------------------------------------------------------------------------------- 1 | { 2 | "articles": [ 3 | { 4 | "id": 1, 5 | "component": "ArticleProfile", 6 | "locales": {}, 7 | "settings": { 8 | "order_items_by": "id", 9 | "order_items_sort": "asc", 10 | "contact_list_ids": ["address", "email", "phone"], 11 | "contact_circles_ids": ["instagram", "facebook", "linkedin", "github"] 12 | }, 13 | "items": [ 14 | { 15 | "id": 1, 16 | "locales": { 17 | "en": { 18 | "description": "I've been a UX designer for 8 years, crafting user-friendly interfaces that feel natural, intuitive, and seamless across different devices. My passion lies in designing digital experiences that not only look great but also function effortlessly, making interactions smoother and more enjoyable." 19 | }, 20 | 21 | "es": { 22 | "description": "Llevo 8 años trabajando como diseñador UX, creando interfaces intuitivas, fluidas y fáciles de usar en distintos dispositivos. Me apasiona diseñar experiencias digitales que no solo sean atractivas, sino que también funcionen sin esfuerzo, haciendo que la interacción sea más fluida y agradable." 23 | }, 24 | 25 | "fr": { 26 | "description": "Depuis 8 ans, je conçois des interfaces UX intuitives, naturelles et fluides sur différents appareils. Ce qui me passionne, c'est de créer des expériences numériques qui sont non seulement esthétiques, mais aussi parfaitement fonctionnelles, rendant chaque interaction plus fluide et agréable." 27 | }, 28 | 29 | "zh": { 30 | "description": "我做 UX 设计已经 8 年了,专注于打造直观、自然且在不同设备上都流畅的用户界面。我热衷于设计既美观又高效的数字体验,让交互变得更加顺畅和愉悦。" 31 | } 32 | } 33 | }, 34 | 35 | { 36 | "id": 2, 37 | "locales": { 38 | "en": { 39 | "description": "I believe that good design should enhance people's lives, helping them navigate technology with ease and confidence. Let’s work together to bring ideas to life and build something truly amazing!" 40 | }, 41 | 42 | "es": { 43 | "description": "Creo que un buen diseño debe mejorar la vida de las personas, ayudándolas a interactuar con la tecnología de manera fácil y segura. ¡Trabajemos juntos para dar vida a nuevas ideas y crear algo increíble!" 44 | }, 45 | 46 | "fr": { 47 | "description": "Je suis convaincu qu’un bon design doit améliorer la vie des gens en leur permettant d’utiliser la technologie avec facilité et confiance. Travaillons ensemble pour donner vie à de nouvelles idées et créer quelque chose d’exceptionnel !" 48 | }, 49 | 50 | "zh": { 51 | "description": "我坚信优秀的设计应该提升人们的生活,让他们能够轻松、自信地使用科技。无论是网站、应用还是复杂系统,我都致力于打造能真正与用户产生深层连接的产品。让我们携手合作,将想法变为现实,创造非凡的作品!" 52 | } 53 | } 54 | } 55 | ] 56 | } 57 | ] 58 | } -------------------------------------------------------------------------------- /public/data/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "preloaderEnabled": true, 3 | "navToggleEnabled": true, 4 | 5 | "supportedLanguages": [ 6 | { 7 | "name": "English", 8 | "id": "en", 9 | "flagUrl": "images/flags/en-us.png", 10 | "default": true 11 | }, 12 | 13 | { 14 | "name": "Español", 15 | "id": "es", 16 | "flagUrl": "images/flags/es.png" 17 | }, 18 | 19 | { 20 | "name": "Français", 21 | "id": "fr", 22 | "flagUrl": "images/flags/fr.png" 23 | }, 24 | 25 | { 26 | "name": "中文", 27 | "id": "zh", 28 | "flagUrl": "images/flags/zh.png" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /public/images/flags/_more-flags.txt: -------------------------------------------------------------------------------- 1 | Need another flag that isn't here? 2 | Download it from here for free: https://www.flaticon.com/packs/countrys-flags -------------------------------------------------------------------------------- /public/images/flags/ar-dz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ar-dz.png -------------------------------------------------------------------------------- /public/images/flags/ar-ma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ar-ma.png -------------------------------------------------------------------------------- /public/images/flags/ar-tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ar-tn.png -------------------------------------------------------------------------------- /public/images/flags/cs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/cs.png -------------------------------------------------------------------------------- /public/images/flags/da.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/da.png -------------------------------------------------------------------------------- /public/images/flags/de-ch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/de-ch.png -------------------------------------------------------------------------------- /public/images/flags/de-li.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/de-li.png -------------------------------------------------------------------------------- /public/images/flags/de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/de.png -------------------------------------------------------------------------------- /public/images/flags/dk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/dk.png -------------------------------------------------------------------------------- /public/images/flags/el.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/el.png -------------------------------------------------------------------------------- /public/images/flags/en-au.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/en-au.png -------------------------------------------------------------------------------- /public/images/flags/en-ca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/en-ca.png -------------------------------------------------------------------------------- /public/images/flags/en-nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/en-nz.png -------------------------------------------------------------------------------- /public/images/flags/en-uk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/en-uk.png -------------------------------------------------------------------------------- /public/images/flags/en-us.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/en-us.png -------------------------------------------------------------------------------- /public/images/flags/es-ar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/es-ar.png -------------------------------------------------------------------------------- /public/images/flags/es-cl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/es-cl.png -------------------------------------------------------------------------------- /public/images/flags/es-co.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/es-co.png -------------------------------------------------------------------------------- /public/images/flags/es-mx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/es-mx.png -------------------------------------------------------------------------------- /public/images/flags/es-pr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/es-pr.png -------------------------------------------------------------------------------- /public/images/flags/es-uy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/es-uy.png -------------------------------------------------------------------------------- /public/images/flags/es-vz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/es-vz.png -------------------------------------------------------------------------------- /public/images/flags/es.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/es.png -------------------------------------------------------------------------------- /public/images/flags/et.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/et.png -------------------------------------------------------------------------------- /public/images/flags/fi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/fi.png -------------------------------------------------------------------------------- /public/images/flags/fr-be.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/fr-be.png -------------------------------------------------------------------------------- /public/images/flags/fr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/fr.png -------------------------------------------------------------------------------- /public/images/flags/ge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ge.png -------------------------------------------------------------------------------- /public/images/flags/hi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/hi.png -------------------------------------------------------------------------------- /public/images/flags/hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/hr.png -------------------------------------------------------------------------------- /public/images/flags/hu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/hu.png -------------------------------------------------------------------------------- /public/images/flags/id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/id.png -------------------------------------------------------------------------------- /public/images/flags/it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/it.png -------------------------------------------------------------------------------- /public/images/flags/ja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ja.png -------------------------------------------------------------------------------- /public/images/flags/ko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ko.png -------------------------------------------------------------------------------- /public/images/flags/ml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ml.png -------------------------------------------------------------------------------- /public/images/flags/nl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/nl.png -------------------------------------------------------------------------------- /public/images/flags/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/no.png -------------------------------------------------------------------------------- /public/images/flags/pk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/pk.png -------------------------------------------------------------------------------- /public/images/flags/pl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/pl.png -------------------------------------------------------------------------------- /public/images/flags/pt-br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/pt-br.png -------------------------------------------------------------------------------- /public/images/flags/pt-pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/pt-pt.png -------------------------------------------------------------------------------- /public/images/flags/ro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ro.png -------------------------------------------------------------------------------- /public/images/flags/ru-kz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ru-kz.png -------------------------------------------------------------------------------- /public/images/flags/ru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/ru.png -------------------------------------------------------------------------------- /public/images/flags/sk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/sk.png -------------------------------------------------------------------------------- /public/images/flags/sl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/sl.png -------------------------------------------------------------------------------- /public/images/flags/sv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/sv.png -------------------------------------------------------------------------------- /public/images/flags/th.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/th.png -------------------------------------------------------------------------------- /public/images/flags/tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/tn.png -------------------------------------------------------------------------------- /public/images/flags/tr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/tr.png -------------------------------------------------------------------------------- /public/images/flags/tw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/tw.png -------------------------------------------------------------------------------- /public/images/flags/un.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/un.png -------------------------------------------------------------------------------- /public/images/flags/uz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/uz.png -------------------------------------------------------------------------------- /public/images/flags/vi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/vi.png -------------------------------------------------------------------------------- /public/images/flags/zh-hk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/zh-hk.png -------------------------------------------------------------------------------- /public/images/flags/zh-sg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/zh-sg.png -------------------------------------------------------------------------------- /public/images/flags/zh-tw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/zh-tw.png -------------------------------------------------------------------------------- /public/images/flags/zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/flags/zh.png -------------------------------------------------------------------------------- /public/images/icons/resume.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/icons/resume.ico -------------------------------------------------------------------------------- /public/images/icons/resume.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 10 | 12 | 13 | 14 | 15 | 17 | 19 | 21 | 23 | 25 | 26 | -------------------------------------------------------------------------------- /public/images/pictures/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/pictures/avatar.png -------------------------------------------------------------------------------- /public/images/places/logo-adobe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/places/logo-adobe.png -------------------------------------------------------------------------------- /public/images/places/logo-apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/places/logo-apple.png -------------------------------------------------------------------------------- /public/images/places/logo-coursera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/places/logo-coursera.png -------------------------------------------------------------------------------- /public/images/places/logo-harvard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/places/logo-harvard.png -------------------------------------------------------------------------------- /public/images/places/logo-paris-university.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/places/logo-paris-university.png -------------------------------------------------------------------------------- /public/images/places/logo-udacity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/places/logo-udacity.png -------------------------------------------------------------------------------- /public/images/places/logo-ux-studio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/places/logo-ux-studio.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-1.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-2.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-3.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-4.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-5.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-6.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-7.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-8.png -------------------------------------------------------------------------------- /public/images/portfolio/project-logo-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanbalieiro/vue-resume-template/b0d776ba47a99bae1ede376d3f5005d3f8236508/public/images/portfolio/project-logo-9.png -------------------------------------------------------------------------------- /src/composables/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Ryan Balieiro on 08.23.2023 3 | * Hub for defining app constants. 4 | */ 5 | export function useConstants() { 6 | /** @type {string} */ 7 | const BASE_URL = import.meta.env.BASE_URL 8 | 9 | /** @const **/ 10 | const BOOTSTRAP_BREAKPOINTS = { 11 | xs: 0, 12 | sm: 576, 13 | md: 768, 14 | lg: 992, 15 | xl: 1200, 16 | xxl: 1400, 17 | } 18 | 19 | /** @type {number} */ 20 | const NAV_SECTION_PICKER_HEIGHT = 50 21 | 22 | /** @type {number} */ 23 | const NAV_SECTION_PICKER_COMPRESSED_HEIGHT = 45 24 | 25 | /** @enum **/ 26 | const PresentationModes = { 27 | ONE_AT_ONCE: "mode_one_at_once", 28 | ALL_AT_ONCE: "mode_all_at_once", 29 | NONE: "mode_none" 30 | } 31 | 32 | return { 33 | // Global Constants... 34 | BASE_URL, 35 | BOOTSTRAP_BREAKPOINTS, 36 | NAV_SECTION_PICKER_HEIGHT, 37 | NAV_SECTION_PICKER_COMPRESSED_HEIGHT, 38 | 39 | // Enums... 40 | PresentationModes, 41 | } 42 | } -------------------------------------------------------------------------------- /src/composables/emails.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Ryan Balieiro on 03.29.2025 3 | * API integration with EmailJS for sending e-mails. 4 | */ 5 | import emailjs from "@emailjs/browser" 6 | 7 | const _params = { 8 | publicKey: null, 9 | serviceId: null, 10 | templateId : null 11 | } 12 | 13 | export const useEmails = () => { 14 | /** 15 | * @constructs 16 | * @param {String} publicKey 17 | * @param {String} serviceId 18 | * @param {String} templateId 19 | */ 20 | const init = (publicKey, serviceId, templateId) => { 21 | _params.publicKey = publicKey 22 | _params.serviceId = serviceId 23 | _params.templateId = templateId 24 | 25 | if(!publicKey || !serviceId || !templateId) 26 | throw new Error("Error initializing emails.js! Make sure you informed all parameters correctly.") 27 | 28 | emailjs.init(_params.publicKey) 29 | } 30 | 31 | /** 32 | * @param {String} fromName 33 | * @param {String} fromEmail 34 | * @param {String} customSubject 35 | * @param {String} message 36 | * @return {Promise} 37 | */ 38 | const sendContact = async (fromName, fromEmail, customSubject, message) => { 39 | if(!_params.serviceId || !_params.templateId) 40 | throw new Error("EmailJS hasn't been initialized!") 41 | 42 | const requestParams = { 43 | from_name: fromName, 44 | from_email: fromEmail, 45 | custom_subject: customSubject, 46 | message: message 47 | } 48 | 49 | try { 50 | const response = await emailjs.send( 51 | _params.serviceId, 52 | _params.templateId, 53 | requestParams 54 | ) 55 | return true 56 | } catch (error) { 57 | return false 58 | } 59 | } 60 | 61 | return { 62 | init, 63 | sendContact 64 | } 65 | } -------------------------------------------------------------------------------- /src/composables/scheduler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Ryan Balieiro on 03.05.2025 3 | * Bulk manager for timeouts and intervals. 4 | */ 5 | const timeouts = [] 6 | const intervals = [] 7 | 8 | export const useScheduler = () => { 9 | /** 10 | * @param {function} callback 11 | * @param {Number} timeInMilliseconds 12 | * @param {string} tag 13 | */ 14 | const schedule = (callback, timeInMilliseconds, tag) => { 15 | const timeoutId = setTimeout(callback, timeInMilliseconds) 16 | timeouts.push({id: timeoutId, tag: tag}) 17 | } 18 | 19 | /** 20 | * @param {function} callback 21 | * @param {Number} timeInMilliseconds 22 | * @param {string} tag 23 | */ 24 | const interval = (callback, timeInMilliseconds, tag) => { 25 | const intervalId = setInterval(callback, timeInMilliseconds) 26 | intervals.push({id: intervalId, tag: tag}) 27 | } 28 | 29 | /** 30 | * @param {String} tag 31 | */ 32 | const clearAllWithTag = (tag) => { 33 | for (let i = timeouts.length - 1; i >= 0; i--) { 34 | if (timeouts[i].tag === tag) { 35 | clearTimeout(timeouts[i].id) 36 | timeouts.splice(i, 1) 37 | } 38 | } 39 | 40 | for (let i = intervals.length - 1; i >= 0; i--) { 41 | if (intervals[i].tag === tag) { 42 | clearInterval(intervals[i].id) 43 | intervals.splice(i, 1) 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * @return {void} 50 | */ 51 | const clearAll = () => { 52 | timeouts.forEach(timeout => { 53 | clearTimeout(timeout.id) 54 | }) 55 | 56 | intervals.forEach(interval => { 57 | clearInterval(interval.id) 58 | }) 59 | 60 | timeouts.length = 0 61 | intervals.length = 0 62 | } 63 | 64 | return { 65 | schedule, 66 | interval, 67 | clearAllWithTag, 68 | clearAll 69 | } 70 | } -------------------------------------------------------------------------------- /src/composables/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Ryan Balieiro on 08.26.2023 3 | * This composable will implement helper functions that can be used by multiple components within the architecture. 4 | */ 5 | import {useConstants} from "/src/composables/constants.js" 6 | 7 | const constants = useConstants() 8 | 9 | export function useUtils() { 10 | /** 11 | * @param {Number} value 12 | * @param {Number} min 13 | * @param {Number} max 14 | * @return {number} 15 | */ 16 | const clamp = (value, min, max) => { 17 | if(isNaN(Number(value)) || value === null || value === undefined) 18 | return min 19 | 20 | return Math.min(Math.max(value, min), max) 21 | } 22 | 23 | /** 24 | * @Boolean 25 | */ 26 | const isAndroid = () => { 27 | const userAgent = window.navigator.userAgent.toLowerCase(); 28 | return /android/.test(userAgent); 29 | } 30 | 31 | /** 32 | * @return {boolean} 33 | */ 34 | const isIOS = () => { 35 | const userAgent = window.navigator.userAgent.toLowerCase() 36 | return /iphone|ipad|ipod/.test(userAgent) 37 | || /^((?!chrome|android).)*safari/i.test(navigator.userAgent) 38 | } 39 | 40 | /** 41 | * @return {boolean} 42 | */ 43 | const isChromeOS = () => { 44 | const userAgent = window.navigator.userAgent.toLowerCase() 45 | return /cros/.test(userAgent); 46 | } 47 | 48 | /** 49 | * @param {String} string 50 | * @return {boolean} 51 | */ 52 | const isStringAnImageUrl = (string) => { 53 | return /\.(jpg|jpeg|png|gif|bmp|svg)$/i.test(string) 54 | } 55 | 56 | /** 57 | * @return {boolean} 58 | */ 59 | const isTouchDevice = () => { 60 | return (('ontouchstart' in window) || 61 | (navigator.maxTouchPoints > 0) || 62 | (navigator.msMaxTouchPoints > 0)) 63 | } 64 | 65 | /** 66 | * @param {String} string 67 | * @return {boolean} 68 | */ 69 | const isValidEmail = (string) => { 70 | return Boolean(String(string) 71 | .toLowerCase() 72 | .match( 73 | /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 74 | )) 75 | } 76 | 77 | /** 78 | * @param {HTMLElement} element 79 | * @return {boolean} 80 | */ 81 | const isElementOutsideBounds = (element) => { 82 | const rect = element.getBoundingClientRect() 83 | 84 | return ( 85 | rect.bottom < 0 || 86 | rect.right < 0 || 87 | rect.left > window.innerWidth || 88 | rect.top > window.innerHeight 89 | ) 90 | } 91 | 92 | /** 93 | * @param {Array} array 94 | * @param {string} key 95 | * @return {boolean} 96 | */ 97 | const hasDuplications = (array, key) => { 98 | const seen = new Set() 99 | for (const item of array) { 100 | if (seen.has(item[key])) return true 101 | seen.add(item[key]) 102 | } 103 | return false 104 | } 105 | 106 | /** @return {string} */ 107 | const getRootSCSSVariable = (colorName) => { 108 | const root = document.documentElement 109 | return getComputedStyle(root).getPropertyValue('--' + colorName).trim() 110 | } 111 | 112 | /** @return {Number} **/ 113 | const getYearsPassedSince = (date) => { 114 | const currentDate = new Date() 115 | const differenceInMilliseconds = currentDate - date 116 | const millisecondsPerYear = 365.25 * 24 * 60 * 60 * 1000 117 | return differenceInMilliseconds / millisecondsPerYear 118 | } 119 | 120 | /** 121 | * @param {String} [prefix] 122 | * @return {string} 123 | */ 124 | const generateUniqueRandomString = (prefix) => { 125 | prefix = prefix || 'key' 126 | window.randStrGenCount = window.randStrGenCount || 0 127 | window.randStrGenCount++ 128 | return prefix + "-rand-" + window.randStrGenCount 129 | } 130 | 131 | /** 132 | * @param {String} path 133 | * @return {String} 134 | */ 135 | const resolvePath = (path) => { 136 | const baseUrl = constants.BASE_URL || '' 137 | return baseUrl + path 138 | } 139 | 140 | return { 141 | clamp, 142 | isAndroid, 143 | isIOS, 144 | isChromeOS, 145 | isStringAnImageUrl, 146 | isTouchDevice, 147 | isValidEmail, 148 | isElementOutsideBounds, 149 | hasDuplications, 150 | getRootSCSSVariable, 151 | getYearsPassedSince, 152 | generateUniqueRandomString, 153 | resolvePath 154 | } 155 | } -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import "./scss/style.scss" 2 | import {createApp} from "vue" 3 | import App from "/src/vue/stack/App.vue" 4 | 5 | createApp(App).mount("#app") -------------------------------------------------------------------------------- /src/models/Article.js: -------------------------------------------------------------------------------- 1 | import ArticleItem from "/src/models/ArticleItem.js" 2 | import Locales from "/src/models/Locales.js" 3 | 4 | export default class Article { 5 | /** @constructs */ 6 | constructor(data, section) { 7 | this._id = data.id 8 | this._component = data.component 9 | this._settings = data.settings || {} 10 | this._locales = new Locales(data.locales || {}) 11 | this._faIcon = data.faIcon 12 | 13 | this._items = data.items?.map(rawItem => new ArticleItem(rawItem)) || [] 14 | this._section = section 15 | } 16 | 17 | /** 18 | * @enum 19 | * @constructor 20 | */ 21 | static get ItemSortingTypes() { 22 | return { 23 | ASCENDING: "asc", 24 | DESCENDING: "desc" 25 | } 26 | } 27 | 28 | /** @return {String} */ 29 | get id() { 30 | return this._id 31 | } 32 | 33 | /** @return {String} */ 34 | get faIcon() { 35 | return this._faIcon 36 | } 37 | 38 | /** @return {String} */ 39 | get componentName() { 40 | return this._component 41 | } 42 | 43 | /** @return {ArticleItem[]}*/ 44 | get items() { 45 | let itemsCopy = [...this._items] 46 | const sortingKey = this.getSetting("order_items_by", "id") 47 | const sortingType = this.getSetting("order_items_sort", Article.ItemSortingTypes.ASCENDING) 48 | 49 | itemsCopy.sort((a, b) => (a[sortingKey] > b[sortingKey] ? 1 : -1)) 50 | if (sortingType === Article.ItemSortingTypes.DESCENDING) 51 | itemsCopy.reverse() 52 | 53 | return itemsCopy 54 | } 55 | 56 | /** @return {Section} */ 57 | get section() { 58 | return this._section 59 | } 60 | 61 | /** @return {Locales} */ 62 | get locales() { 63 | return this._locales 64 | } 65 | 66 | /** 67 | * @param {String} key 68 | * @param {*} [fallback] 69 | * @return {*} 70 | */ 71 | getSetting(key, fallback) { 72 | return this._settings[key] || fallback 73 | } 74 | } -------------------------------------------------------------------------------- /src/models/ArticleItem.js: -------------------------------------------------------------------------------- 1 | import Locales from "/src/models/Locales" 2 | 3 | export default class ArticleItem { 4 | /** @constructs */ 5 | constructor(data) { 6 | this._id = data.id 7 | this._img = data.img 8 | this._fallbackFaIcon = data['fallbackFaIcon'] 9 | this._fallbackFaIconColor = data['fallbackFaIconColor'] 10 | this._dateStart = data['dateStart'] 11 | this._dateEnd = data['dateEnd'] 12 | this._percentage = data['percentage'] 13 | this._locales = new Locales(data.locales || {}) 14 | this._links = data.links || [] 15 | this._category = data['category'] 16 | } 17 | 18 | /** @return {String} */ 19 | get id() { 20 | return this._id 21 | } 22 | 23 | /** @return {String} */ 24 | get img() { 25 | return this._img 26 | } 27 | 28 | /** @return {String} */ 29 | get fallbackFaIcon() { 30 | return this._fallbackFaIcon 31 | } 32 | 33 | /** @return {String} */ 34 | get fallbackFaIconColor() { 35 | return this._fallbackFaIconColor 36 | } 37 | 38 | /** @return {Date|String} */ 39 | get dateStart() { 40 | return this._parseDate(this._dateStart) 41 | } 42 | 43 | /** @return {Date|String} */ 44 | get dateEnd() { 45 | return this._parseDate(this._dateEnd) 46 | } 47 | 48 | /** 49 | * @param {{year: Number, month: Number, day: Number, string: String}} [datePreset = null] 50 | * @return {null|Date|*} 51 | * @private 52 | */ 53 | _parseDate(datePreset) { 54 | if(!datePreset) return null 55 | if(datePreset.string) return datePreset.string 56 | 57 | return new Date( 58 | datePreset.year, 59 | datePreset.month, 60 | datePreset.day || 1 61 | ) 62 | } 63 | 64 | /** @return {Locales} */ 65 | get locales() { 66 | return this._locales 67 | } 68 | 69 | /** @return {Number} */ 70 | get percentage() { 71 | return this._percentage 72 | } 73 | 74 | /** @return {boolean} */ 75 | get hasPercentage() { 76 | return this._percentage !== null && this._percentage !== undefined 77 | } 78 | 79 | /** @return {Array} */ 80 | get links() { 81 | return this._links 82 | } 83 | 84 | /** @return {String} */ 85 | get categoryId() { 86 | return this._category 87 | } 88 | } -------------------------------------------------------------------------------- /src/models/Category.js: -------------------------------------------------------------------------------- 1 | import Locales from "/src/models/Locales.js" 2 | 3 | export default class Category { 4 | /** @constructs */ 5 | constructor(id, faIcon, locales) { 6 | this._id = id 7 | this._faIcon = faIcon 8 | this._locales = new Locales(locales) 9 | this._sections = [] 10 | } 11 | 12 | /** @return {String} */ 13 | get id() { 14 | return this._id 15 | } 16 | 17 | /** @return {String} */ 18 | get faIcon() { 19 | return this._faIcon 20 | } 21 | 22 | /** @return {Section[]} */ 23 | get sections() { 24 | return this._sections 25 | } 26 | 27 | /** @return {Locales} */ 28 | get locales() { 29 | return this._locales 30 | } 31 | 32 | /** @return {Section} */ 33 | get lastVisitedSection() { 34 | return this._lastVisitedSection 35 | } 36 | 37 | /** 38 | * @param {Section} section 39 | */ 40 | set lastVisitedSection(section) { 41 | this._lastVisitedSection = section 42 | } 43 | 44 | /** 45 | * @param {Section} section 46 | */ 47 | addSection(section) { 48 | if(this._sections.indexOf(section) === -1) { 49 | this._sections.push(section) 50 | } 51 | } 52 | 53 | /** 54 | * @param {Section} section 55 | */ 56 | removeSection(section) { 57 | const index = this._sections.indexOf(section) 58 | if(index !== -1) { 59 | this._sections.splice(index, 1) 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/models/ContactOption.js: -------------------------------------------------------------------------------- 1 | export default class ContactOption { 2 | /** @constructs */ 3 | constructor(jsonData) { 4 | this._id = jsonData["id"] 5 | this._value = jsonData["value"] 6 | this._valueShort = jsonData["valueShort"] 7 | this._faIcon = jsonData["faIcon"] 8 | this._href = jsonData["href"] || null 9 | } 10 | 11 | /** @return {String} */ 12 | get id() { 13 | return this._id 14 | } 15 | 16 | /** @return {String} */ 17 | getValue(shorten) { 18 | return shorten ? this._valueShort : this._value 19 | } 20 | 21 | /** @return {String} */ 22 | get faIcon() { 23 | return this._faIcon 24 | } 25 | 26 | /** @return {String|null} */ 27 | get href() { 28 | return this._href 29 | } 30 | } -------------------------------------------------------------------------------- /src/models/Language.js: -------------------------------------------------------------------------------- 1 | export default class Language { 2 | /** @constructs */ 3 | constructor(id, name, isDefault, flagUrl) { 4 | this._id = id 5 | this._name = name 6 | this._isDefault = isDefault 7 | this._flagUrl = flagUrl 8 | } 9 | 10 | /** @return {String} */ 11 | get id() { 12 | return this._id 13 | } 14 | 15 | /** @return {String} */ 16 | get name() { 17 | return this._name 18 | } 19 | 20 | /** @return {String} */ 21 | get flagUrl() { 22 | return this._flagUrl 23 | } 24 | 25 | /** @return {Boolean} */ 26 | get isDefault() { 27 | return Boolean(this._isDefault) 28 | } 29 | } -------------------------------------------------------------------------------- /src/models/Locales.js: -------------------------------------------------------------------------------- 1 | export default class Locales { 2 | /** @constructs */ 3 | constructor(localesHash) { 4 | this._localesHash = localesHash['locales'] || localesHash 5 | } 6 | 7 | /** 8 | * @param {String} key 9 | * @param {Language} [language] 10 | * @param {Language} [fallbackLanguage] 11 | */ 12 | getTranslation(key, language, fallbackLanguage) { 13 | const targetLanguageId = language?.id 14 | const defaultLanguageId = fallbackLanguage?.id 15 | 16 | const targetLanguageHash = this._localesHash[targetLanguageId] 17 | const defaultLanguageHash = this._localesHash[defaultLanguageId] 18 | 19 | if(targetLanguageHash && targetLanguageHash[key]) 20 | return this._parse(targetLanguageHash[key]) 21 | if(defaultLanguageHash && defaultLanguageHash[key]) 22 | return this._parse(defaultLanguageHash[key]) 23 | return `locales.${key}` 24 | } 25 | 26 | /** 27 | * @param {String} text 28 | * @return {string|*} 29 | * @private 30 | */ 31 | _parse(text) { 32 | if(!text) 33 | return `locales.error` 34 | 35 | if(!(typeof text === 'string')) 36 | return text 37 | 38 | return text.replace(/\*(.*?)\*/g, '$1') 39 | } 40 | } -------------------------------------------------------------------------------- /src/models/Profile.js: -------------------------------------------------------------------------------- 1 | import Locales from "/src/models/Locales.js" 2 | import ContactOption from "/src/models/ContactOption.js" 3 | 4 | export default class Profile { 5 | /** @constructs */ 6 | constructor(jsonData) { 7 | this._name = jsonData["name"] 8 | this._profilePictureUrl = jsonData["profilePictureUrl"] 9 | this._locales = new Locales(jsonData["locales"]) 10 | this._contactOptions = jsonData["contactOptions"].map(contactOptionData => { 11 | return new ContactOption(contactOptionData) 12 | }) 13 | } 14 | 15 | /** @return {String} */ 16 | get name() { 17 | return this._name 18 | } 19 | 20 | /** @return {String} */ 21 | get profilePictureUrl() { 22 | return this._profilePictureUrl 23 | } 24 | 25 | /** @return {ContactOption[]} */ 26 | get contactOptions() { 27 | return this._contactOptions 28 | } 29 | 30 | /** @return {ContactOption} */ 31 | getContactOptionWithId(id) { 32 | return this.contactOptions.find(contactOption => contactOption.id === id) 33 | } 34 | 35 | /** @return {Locales} */ 36 | get locales() { 37 | return this._locales 38 | } 39 | } -------------------------------------------------------------------------------- /src/models/Section.js: -------------------------------------------------------------------------------- 1 | import Locales from "/src/models/Locales.js" 2 | import Article from "/src/models/Article.js" 3 | 4 | export default class Section { 5 | /** @constructs */ 6 | constructor(id, isCover, faIcon, jsonPath, type, locales) { 7 | this._id = id 8 | this._faIcon = faIcon 9 | this._jsonPath = jsonPath 10 | this._category = null 11 | this._type = type 12 | this._locales = new Locales(locales) 13 | this._isCover = isCover 14 | this._articles = [] 15 | 16 | if(!Object.values(Section.Types).find(existingType => existingType === type)) { 17 | throw new Error(`Section with ID "${id}" has an invalid type "${type}"`) 18 | } 19 | } 20 | 21 | /** @enum */ 22 | static get Types() { 23 | return { 24 | CENTERED: "centered", 25 | COLUMN: "column", 26 | ROW: "row" 27 | } 28 | } 29 | 30 | /** @return {String} */ 31 | get id() { 32 | return this._id 33 | } 34 | 35 | /** @return {String} */ 36 | get faIcon() { 37 | return this._faIcon 38 | } 39 | 40 | /** @return {String} */ 41 | get type() { 42 | return this._type 43 | } 44 | 45 | /** @return {boolean} */ 46 | get isCentered() { 47 | return this._type === Section.Types.CENTERED 48 | } 49 | 50 | /** @return {boolean} */ 51 | get isDefault() { 52 | return this._type === Section.Types.ROW || this._type === Section.Types.COLUMN 53 | } 54 | 55 | /** @return {String} */ 56 | get jsonPath() { 57 | return this._jsonPath 58 | } 59 | 60 | /** @return {String} */ 61 | get urlHashId() { 62 | return this.id 63 | } 64 | 65 | /** @return {string} */ 66 | get htmlId() { 67 | return 'resume-section-' + this.id 68 | } 69 | 70 | /** @return {Boolean} */ 71 | get isCover() { 72 | return this._isCover 73 | } 74 | 75 | /** @param {Category} category */ 76 | set category(category) { 77 | this._category = category 78 | } 79 | 80 | /** @return {Category} */ 81 | get category() { 82 | return this._category 83 | } 84 | 85 | /** @param {Array} rawArticles */ 86 | set articles(rawArticles) { 87 | this._articles = rawArticles.map(rawArticle => { 88 | return new Article(rawArticle, this) 89 | }) 90 | } 91 | 92 | /** @return {Array} */ 93 | get articles() { 94 | return this._articles 95 | } 96 | 97 | /** @return {Locales} */ 98 | get locales() { 99 | return this._locales 100 | } 101 | } -------------------------------------------------------------------------------- /src/models/Settings.js: -------------------------------------------------------------------------------- 1 | import Language from "/src/models/Language.js" 2 | 3 | export default class Settings { 4 | /** @constructs */ 5 | constructor(jsonData) { 6 | this._preloaderEnabled = jsonData['preloaderEnabled'] 7 | this._navToggleEnabled = jsonData['navToggleEnabled'] 8 | this._supportedLanguages = jsonData['supportedLanguages'].map( 9 | ({ id, name, default: isDefault, flagUrl: flagUrl }) => new Language(id, name, isDefault, flagUrl) 10 | ) 11 | } 12 | 13 | /** @return {Boolean} */ 14 | get preloaderEnabled() { 15 | return this._preloaderEnabled 16 | } 17 | 18 | /** @return {Language[]} */ 19 | get supportedLanguages() { 20 | return this._supportedLanguages 21 | } 22 | 23 | /** @return {Boolean} */ 24 | get navToggleEnabled() { 25 | return this._navToggleEnabled 26 | } 27 | } -------------------------------------------------------------------------------- /src/scss/_layout.scss: -------------------------------------------------------------------------------- 1 | /** ------------ BODY ----------------- **/ 2 | html { 3 | scroll-padding-top: 0; 4 | } 5 | 6 | body { 7 | background-color: $loader-background; 8 | } 9 | 10 | body.body-default { 11 | background-color: $default-section-background; 12 | } 13 | 14 | body.body-nav-contrast { 15 | background-color: darken($nav-background, 3%)!important; 16 | } 17 | 18 | body.body-no-scroll { 19 | overflow-y: hidden!important; 20 | @include media-breakpoint-down(lg) { 21 | position: fixed!important; 22 | } 23 | } 24 | 25 | /** ----------- SELECTION ------------- **/ 26 | ::-moz-selection { 27 | color: $dark; 28 | background: lighten($primary, 15%); 29 | } 30 | 31 | ::selection { 32 | color: $dark; 33 | background: lighten($primary, 15%); 34 | } 35 | 36 | /** ----------- SHADOWS ---------------- **/ 37 | .trace-shadow { 38 | box-shadow: rgba(0, 0, 0, 0.1) 5px 5px, rgba(10, 10, 10, 0.15) 10px 10px; 39 | } -------------------------------------------------------------------------------- /src/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // This mixin generates dynamic styles based on a hash object containing breakpoint-specific styles. 2 | // It loops through the provided styles for each breakpoint and applies them within the appropriate media query. 3 | @mixin generate-dynamic-styles-with-hash($stylesHash) { 4 | @each $breakpoint, $styles in $stylesHash { 5 | @include media-breakpoint-down($breakpoint) { 6 | @each $property, $value in $styles { 7 | #{$property}: $value; 8 | } 9 | } 10 | } 11 | } 12 | 13 | // This mixin generates dynamic styles where the base sizes (e.g., font sizes, padding) are multiplied 14 | // by different factors for each breakpoint. This allows scaling of styles based on the screen size. 15 | @mixin generate-dynamic-styles-with-multipliers($baseSizes, $breakpointMultipliers) { 16 | @each $breakpoint, $multiplier in $breakpointMultipliers { 17 | @include media-breakpoint-down($breakpoint) { 18 | @each $property, $value in $baseSizes { 19 | #{$property}: $value * $multiplier; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/scss/_theming.scss: -------------------------------------------------------------------------------- 1 | // This file can be imported into a Vue component in order to access bootstrap/custom styling variables/resources. 2 | @import "./_variables"; 3 | @import "./mixins"; 4 | 5 | @import "bootstrap/scss/functions"; 6 | @import "bootstrap/scss/variables"; 7 | @import "bootstrap/scss/mixins"; -------------------------------------------------------------------------------- /src/scss/_typography.scss: -------------------------------------------------------------------------------- 1 | /** Headings **/ 2 | $headings-base-sizes: ( 3 | h1: (font-size: 2.75rem), 4 | h2: (font-size: 2.5rem), 5 | h3: (font-size: 2rem), 6 | h4: (font-size: 1.5rem), 7 | h5: (font-size: 1.3rem), 8 | h6: (font-size: 1.1rem) 9 | ); 10 | 11 | @each $heading, $size in $headings-base-sizes { 12 | #{$heading}, .eq-#{$heading} { 13 | @include generate-dynamic-styles-with-multipliers($size, $headings-breakpoint-multipliers); 14 | } 15 | } 16 | 17 | /** Custom Headings **/ 18 | $custom-headings-base-sizes: ( 19 | lead-2: (font-size: 1.4rem), 20 | lead: (font-size: 1.2rem), 21 | display-1: (font-size: 3rem), 22 | ); 23 | 24 | @each $heading, $size in $custom-headings-base-sizes { 25 | .#{$heading} { 26 | @include generate-dynamic-styles-with-multipliers($size, $headings-breakpoint-multipliers); 27 | } 28 | } 29 | 30 | /** Texts **/ 31 | $texts-base-sizes: ( 32 | text-1: (font-size: 0.85rem), 33 | text-2: (font-size: 0.9rem), 34 | text-3: (font-size: 0.95rem), 35 | text-4: (font-size: 1.0rem), 36 | text-5: (font-size: 1.05rem), 37 | ); 38 | 39 | @each $text, $size in $texts-base-sizes { 40 | .#{$text} { 41 | @include generate-dynamic-styles-with-multipliers($size, $texts-breakpoint-multipliers); 42 | } 43 | } 44 | 45 | .text { 46 | &-muted { 47 | color: $text-muted!important; 48 | } 49 | 50 | &-default { 51 | color: $text-default-color!important; 52 | } 53 | 54 | @for $i from 1 through length($light-shades-palette) { 55 | &-light-#{$i} { 56 | color: nth($light-shades-palette, $i)!important; 57 | } 58 | } 59 | } 60 | 61 | a { 62 | text-decoration: none; 63 | color: lighten($primary, 20%); 64 | &:hover { 65 | color: lighten($primary, 15%); 66 | } 67 | 68 | &.link-darkened { 69 | color: $primary; 70 | &:hover { 71 | color: lighten($primary, 5%); 72 | } 73 | } 74 | 75 | &.link-masked { 76 | color: $light-7; 77 | &:hover { 78 | color: lighten($primary, 5%); 79 | } 80 | } 81 | 82 | &.link-disabled { 83 | color: $text-default-color; 84 | opacity: 0.9; 85 | pointer-events: none; 86 | } 87 | } 88 | 89 | p { 90 | font-family: $font-family-base; 91 | } -------------------------------------------------------------------------------- /src/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | /** Font Awesome webfonts path **/ 2 | $fa-font-path:"@fortawesome/fontawesome-free/webfonts"; 3 | 4 | /** Overriding Bootstrap's contrast ratio. **/ 5 | $min-contrast-ratio: 1.5 !default; 6 | 7 | /** Typography **/ 8 | $font-family-base: 'Saira', sans-serif; 9 | 10 | /** Z-Indexes **/ 11 | $z-index-sidebar: 90; 12 | $z-index-tab-controller: 95; 13 | $z-index-loader: 99999; 14 | 15 | /** Bootstrap Colors **/ 16 | $primary: #7e45dc; 17 | $dark: #212529; 18 | $success: #06c203; 19 | $danger: #d91c1c; 20 | $light: #fcfcfc; 21 | 22 | /** Layout Colors **/ 23 | $default-section-background: #f1eff3; 24 | $loader-background: #261841; 25 | $text-default-color: #140f29; 26 | 27 | /** Nav Colors **/ 28 | $nav-background: #140f29; 29 | $nav-background-selected: lighten($nav-background, 5%); 30 | $nav-avatar-border-color: lighten($nav-background, 10%); 31 | $nav-highlight: lighten($primary, 30%); 32 | $nav-hover: lighten($primary, 30%); 33 | $nav-selected: lighten($primary, 20%); 34 | $nav-contrast: #fcfcfc; 35 | $nav-contrast-90: rgba($nav-contrast, 0.9); 36 | $nav-contrast-80: rgba($nav-contrast, 0.8); 37 | $nav-contrast-70: rgba($nav-contrast, 0.7); 38 | $nav-contrast-60: rgba($nav-contrast, 0.6); 39 | $nav-contrast-50: rgba($nav-contrast, 0.5); 40 | $nav-contrast-40: rgba($nav-contrast, 0.4); 41 | $nav-contrast-30: rgba($nav-contrast, 0.3); 42 | $nav-contrast-20: rgba($nav-contrast, 0.2); 43 | $nav-contrast-10: rgba($nav-contrast, 0.1); 44 | 45 | /** Light Shades **/ 46 | $light-1: #f8f9fa; 47 | $light-2: #e9ecef; 48 | $light-3: #dee2e6; 49 | $light-4: #ced4da; 50 | $light-5: #adb5bd; 51 | $light-6: #6c757d; 52 | $light-7: #495057; 53 | 54 | $light-shades-palette: ( 55 | $light-1, 56 | $light-2, 57 | $light-3, 58 | $light-4, 59 | $light-5, 60 | $light-6, 61 | $light-7, 62 | ); 63 | 64 | /** Layout Constraints **/ 65 | $navigation-sidebar-breakpoint: "lg"; 66 | $max-content-width: 2500px; 67 | $default-border-radius: 10px; 68 | 69 | /** Layout Constraints - Sidebar **/ 70 | $nav-sidebar-width: 320px; 71 | $nav-sidebar-width-lg: 290px; 72 | $nav-sidebar-width-shrink: 120px; 73 | 74 | /** Layout Constraints - Sidebar Footer **/ 75 | $nav-sidebar-footer-height: 100px; 76 | $nav-sidebar-footer-height-compressed: 60px; 77 | $nav-sidebar-footer-compress-screen-height: 560px; 78 | 79 | /** Layout Constraints - Mobile Navigation **/ 80 | $nav-tab-controller-height: 60px; 81 | $nav-tab-controller-with-bottom-offset-height: 65px; 82 | $nav-section-picker-height: 50px; 83 | $nav-section-picker-height-compressed: 45px; 84 | 85 | /** ArticleTimeline Parameters **/ 86 | $article-timeline-image-size: 100px; 87 | $article-timeline-image-size-md: 75px; 88 | $article-timeline-image-size-sm: 50px; 89 | 90 | $article-timeline-border-width: 7px; 91 | $article-timeline-border-width-md: 6px; 92 | $article-timeline-border-width-sm: 5px; 93 | 94 | $article-timeline-border-color: $light-3; 95 | $article-timeline-line-color: $light-4; 96 | $article-timeline-line-width: 3px; 97 | 98 | /** ArticleThread Parameters **/ 99 | $article-thread-scale-multipliers: ( 100 | xxxl: 1, 101 | xxl: 0.975, 102 | xl: 0.95, 103 | md: 0.9, 104 | sm: 0.8 105 | ); 106 | $article-thread-line-color: $article-timeline-line-color; 107 | $article-thread-circle-base-size: 32px; 108 | 109 | /** Chart JS Customization **/ 110 | :root { 111 | --chart-js-border-color: #{$default-section-background}; 112 | --chart-js-hover-border-color: #{$light}; 113 | --nav-background-color: #{$nav-background}; 114 | } 115 | 116 | /** Typography - Multipliers (Headers) **/ 117 | $headings-breakpoint-multipliers: ( 118 | xxxl: 1, 119 | xxl: 0.95, 120 | xl: 0.925, 121 | lg: 0.875, 122 | md: 0.825, 123 | sm: 0.75 124 | ); 125 | 126 | /** Typography - Multipliers (Texts) **/ 127 | $texts-breakpoint-multipliers: ( 128 | xxxl: 1, 129 | xxl: 0.95, 130 | xl: 0.925, 131 | lg: 0.9, 132 | md: 0.875, 133 | sm: 0.85 134 | ); -------------------------------------------------------------------------------- /src/scss/style.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | // importing bootstrap 5 | @import "bootstrap/scss/bootstrap"; 6 | 7 | // importing core styling file 8 | @import "@fortawesome/fontawesome-free/scss/fontawesome.scss"; 9 | // our project needs Solid + Brands 10 | @import "@fortawesome/fontawesome-free/scss/solid.scss"; 11 | @import "@fortawesome/fontawesome-free/scss/regular.scss"; 12 | @import "@fortawesome/fontawesome-free/scss/brands.scss"; 13 | 14 | @import "layout"; 15 | @import "typography"; -------------------------------------------------------------------------------- /src/vue/components/articles/base/Article.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 42 | 43 | -------------------------------------------------------------------------------- /src/vue/components/articles/base/ArticleTitle.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /src/vue/components/articles/base/ArticleWidgetLinkList.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 25 | 26 | -------------------------------------------------------------------------------- /src/vue/components/articles/contact/ArticleContactForm.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 129 | 130 | -------------------------------------------------------------------------------- /src/vue/components/articles/contact/ArticleContactFormThankYou.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 45 | 46 | -------------------------------------------------------------------------------- /src/vue/components/articles/contact/ArticleContactOptions.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 41 | 42 | -------------------------------------------------------------------------------- /src/vue/components/articles/portfolio/ArticlePortfolio.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 101 | 102 | -------------------------------------------------------------------------------- /src/vue/components/articles/profile/ArticleProfile.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 69 | 70 | -------------------------------------------------------------------------------- /src/vue/components/articles/profile/ArticleProfileItem.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 20 | 21 | -------------------------------------------------------------------------------- /src/vue/components/articles/skills/ArticleSkills.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 75 | 76 | -------------------------------------------------------------------------------- /src/vue/components/articles/skills/ArticleSkillsPieChart.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 76 | 77 | -------------------------------------------------------------------------------- /src/vue/components/articles/thread/ArticleThread.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 27 | 28 | -------------------------------------------------------------------------------- /src/vue/components/articles/timeline/ArticleTimeline.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | 32 | -------------------------------------------------------------------------------- /src/vue/components/articles/timeline/ArticleTimelineItem.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 43 | 44 | -------------------------------------------------------------------------------- /src/vue/components/articles/unavailable/ArticleUnavailable.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 72 | 73 | -------------------------------------------------------------------------------- /src/vue/components/loaders/ActivitySpinner.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 21 | 22 | -------------------------------------------------------------------------------- /src/vue/components/modals/base/Modal.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 92 | 93 | -------------------------------------------------------------------------------- /src/vue/components/modals/project/ProjectModal.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 44 | 45 | -------------------------------------------------------------------------------- /src/vue/components/modals/project/ProjectModalContent.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 94 | 95 | -------------------------------------------------------------------------------- /src/vue/components/navigation/NavigationWrapper.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 89 | 90 | -------------------------------------------------------------------------------- /src/vue/components/navigation/controls/NavControlOneAtOnce.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 143 | 144 | -------------------------------------------------------------------------------- /src/vue/components/navigation/layout/NavMobileHeader.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 30 | 31 | -------------------------------------------------------------------------------- /src/vue/components/navigation/layout/NavProfileCard.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 39 | 40 | -------------------------------------------------------------------------------- /src/vue/components/navigation/layout/NavToggleButton.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /src/vue/components/navigation/sidebar/NavSidebar.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 73 | 74 | -------------------------------------------------------------------------------- /src/vue/components/navigation/sidebar/NavSidebarFooter.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 21 | 22 | -------------------------------------------------------------------------------- /src/vue/components/navigation/tabs/NavPillsController.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 37 | 38 | -------------------------------------------------------------------------------- /src/vue/components/navigation/tabs/NavPillsControllerFixed.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 69 | 70 | -------------------------------------------------------------------------------- /src/vue/components/sections/Section.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 52 | 53 | -------------------------------------------------------------------------------- /src/vue/components/sections/SectionBody.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 50 | 51 | -------------------------------------------------------------------------------- /src/vue/components/sections/SectionHeader.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 48 | 49 | -------------------------------------------------------------------------------- /src/vue/components/widgets/Alert.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | -------------------------------------------------------------------------------- /src/vue/components/widgets/FaButton.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /src/vue/components/widgets/FilterTabs.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 37 | 38 | -------------------------------------------------------------------------------- /src/vue/components/widgets/IconView.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 69 | 70 | -------------------------------------------------------------------------------- /src/vue/components/widgets/ImageView.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 91 | 92 | -------------------------------------------------------------------------------- /src/vue/components/widgets/InfoBadge.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /src/vue/components/widgets/InlineInfoList.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /src/vue/components/widgets/InlineLinkList.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /src/vue/components/widgets/OptionsList.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 38 | 39 | -------------------------------------------------------------------------------- /src/vue/components/widgets/ProgressBar.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 35 | 36 | -------------------------------------------------------------------------------- /src/vue/components/widgets/SolidDivider.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /src/vue/components/widgets/Spinner.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /src/vue/components/widgets/Tags.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /src/vue/components/widgets/XLButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | -------------------------------------------------------------------------------- /src/vue/stack/App.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | 27 | -------------------------------------------------------------------------------- /src/vue/stack/DataManager.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 124 | 125 | -------------------------------------------------------------------------------- /src/vue/stack/FeedbacksManager.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 69 | 70 | -------------------------------------------------------------------------------- /src/vue/stack/LanguageManager.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 97 | 98 | -------------------------------------------------------------------------------- /src/vue/stack/LocationManager.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 118 | 119 | -------------------------------------------------------------------------------- /src/vue/stack/ModalManager.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | 25 | -------------------------------------------------------------------------------- /src/vue/stack/Resume.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | 36 | -------------------------------------------------------------------------------- /src/vue/stack/WindowObserver.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 113 | 114 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | base: '/vue-resume-template/', 7 | plugins: [vue()], 8 | css: { 9 | preprocessorOptions: { 10 | scss: { 11 | silenceDeprecations: ["mixed-decls", "color-functions", "global-builtin", "import"], 12 | }, 13 | }, 14 | } 15 | }) --------------------------------------------------------------------------------