mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-10 04:22:27 +10:00
Compare commits
283 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b2d9d7026 | |||
| 045145ed67 | |||
| ec27e5e6ab | |||
| 2faa15db5a | |||
| 2c2893d5fc | |||
| 19c7ebe8a4 | |||
| c24847ac0b | |||
| 7137694832 | |||
| 049de38da2 | |||
| 17019e446b | |||
| d73ee7b7f8 | |||
| 2c95dc2ac8 | |||
| e148dd3e82 | |||
| 0aa2d61c55 | |||
| 0b2c1ffd26 | |||
| a531e8cd89 | |||
| 152e386141 | |||
| 87189cd045 | |||
| 114b04a740 | |||
| 383cde53df | |||
| 9bf98d3c49 | |||
| e62f0a3f5e | |||
| 10fb7b143a | |||
| 67ba58e798 | |||
| 179cf99f83 | |||
| 81a51d487b | |||
| b41b50565a | |||
| 8cd073eb62 | |||
| f4f8502703 | |||
| 0d079d7b24 | |||
| 167f7c902f | |||
| 7c630df927 | |||
| b391c561e5 | |||
| 4dbe015fbf | |||
| bae35b2614 | |||
| 8b7719a198 | |||
| 39cf238de3 | |||
| 98855ae230 | |||
| ab92cbf21e | |||
| 388ab4e29a | |||
| bb18c59018 | |||
| 217ab6ab93 | |||
| 12690b33d7 | |||
| bff5173701 | |||
| 821813d90d | |||
| b1d3c4da5b | |||
| 39f962b440 | |||
| b1cfd4b7c8 | |||
| c98d4a6004 | |||
| a5ec1f8609 | |||
| b2c897660d | |||
| c1a7fe7354 | |||
| b628c4a21b | |||
| 5fb4935146 | |||
| ae5280435d | |||
| 6451609d8f | |||
| edfe79f580 | |||
| 5d7318d46d | |||
| 77428c1661 | |||
| a2e075df39 | |||
| 63af1d2b69 | |||
| 99c5016762 | |||
| 44ff6caf27 | |||
| 7d2981f7ce | |||
| fcc5dd4bad | |||
| a9fb995d39 | |||
| 31a85bfaa6 | |||
| 51151a601e | |||
| 9931b22313 | |||
| fdf6b76c21 | |||
| b4696301ed | |||
| 294d7b5dab | |||
| 0430920f56 | |||
| 5444b4f5ab | |||
| d649b7fc08 | |||
| 20b39c0b35 | |||
| 8b87b054ee | |||
| 5eb68e9e21 | |||
| ec2606d625 | |||
| 9055010f61 | |||
| 9763b5c270 | |||
| 75c3bfe9e5 | |||
| 7f39247655 | |||
| d6f11e7807 | |||
| 361a1e65d0 | |||
| 6fddbe0c59 | |||
| 3412711f27 | |||
| a4bfc17431 | |||
| 7c698ef9d2 | |||
| e929faf9b0 | |||
| e3ff18b6dd | |||
| 2734493ca4 | |||
| f0015143c6 | |||
| 8d97b195a0 | |||
| f30692196a | |||
| 242278edd1 | |||
| 162759c716 | |||
| f0c6bd16f5 | |||
| fac8a9d4ff | |||
| 9ff1ffb0b9 | |||
| 79d3ef1306 | |||
| f4a12285f5 | |||
| 120ad827ad | |||
| a129b2033f | |||
| 372e508936 | |||
| ce8ada2621 | |||
| d0563d2ec9 | |||
| dacd4e311c | |||
| d6d016ba5d | |||
| 407ac990ac | |||
| 895e9845fc | |||
| 859a44197d | |||
| 0b80f33d46 | |||
| f246a17038 | |||
| 10c13d54be | |||
| 66316b740b | |||
| ae94748abe | |||
| fe11be60d3 | |||
| 1d3e47adb2 | |||
| fdd5f373c4 | |||
| 0f99d6cdfb | |||
| 742865a66a | |||
| 12dcf04981 | |||
| c0d76eaf0e | |||
| 2f430a1d07 | |||
| 576b942027 | |||
| 7df777ad0c | |||
| e5e30f290a | |||
| dcb476c28b | |||
| e09f281461 | |||
| 293f008f0a | |||
| f9cd1c779f | |||
| d931590374 | |||
| 907ffacca0 | |||
| a263d54319 | |||
| 0d478e1286 | |||
| 08997a1728 | |||
| 688bb11844 | |||
| 0bf4e0b2ae | |||
| 60bbfb6703 | |||
| 706307b073 | |||
| 3be18636ff | |||
| b2ee2f9d09 | |||
| fe54a2388e | |||
| be170dd985 | |||
| 7fd26ad2c3 | |||
| ec30aff4d1 | |||
| 0b3023989b | |||
| 3b96348183 | |||
| 30080b23cd | |||
| b3ba1e5b56 | |||
| bf72b557ca | |||
| 344fcb1078 | |||
| ddd71567c1 | |||
| adc679a6e5 | |||
| 8f49536119 | |||
| 750fedbd74 | |||
| cd59ea7e9b | |||
| 5c1b44ddea | |||
| eb6450a9de | |||
| 0b620f41fc | |||
| 64e0e677d7 | |||
| 19ae1cf036 | |||
| c221cef77f | |||
| 77e3dc2b16 | |||
| 9c9368acd5 | |||
| 22b91d3f94 | |||
| d844092d0f | |||
| b3e118fb8b | |||
| fe37eb2791 | |||
| 7902f67f4f | |||
| 57dd110187 | |||
| 829375e87a | |||
| 0a15b4ebc9 | |||
| 2bff3fc20b | |||
| 1e997fe67c | |||
| dbf06455e4 | |||
| 42c7c9ade1 | |||
| 36c19bac3f | |||
| 44a9300aff | |||
| 610b5ba9d4 | |||
| 769e8811cd | |||
| 676fbcafe7 | |||
| 3935ae1e04 | |||
| ef6b765266 | |||
| 647dd6e682 | |||
| 43841e9962 | |||
| e2236c3207 | |||
| 7389d33ee5 | |||
| 4b21eabec9 | |||
| 1815b0fa21 | |||
| 6c4d3cbd56 | |||
| 8c2f3c8504 | |||
| 3aa7a98d9d | |||
| 5519ec898d | |||
| 1cd4c5d733 | |||
| 73d11c323f | |||
| 38812fcf25 | |||
| c22de12f12 | |||
| c94c971599 | |||
| c9a71a5917 | |||
| 8a29387470 | |||
| 592511b090 | |||
| af63fd38d4 | |||
| bf38b1b254 | |||
| 4a1c0079db | |||
| 5b6f6b7621 | |||
| 02587255fe | |||
| 9ef2a84ac2 | |||
| 77b1c5b536 | |||
| bf956fe18c | |||
| 4114f1e1dd | |||
| 668d39fa87 | |||
| 0d88a18757 | |||
| 0630369087 | |||
| 73af4a6859 | |||
| 99ddeb25a9 | |||
| 685aa06778 | |||
| 460abc6f1d | |||
| 04f02157ac | |||
| 828a4a8715 | |||
| 5b3141cd49 | |||
| 779d22101f | |||
| ef240b2110 | |||
| 32bb7354a4 | |||
| 0dcbad1f8a | |||
| a74921b27a | |||
| d4f47423c9 | |||
| 03f9a6543c | |||
| eb89cfcf5d | |||
| c52ef9ecb7 | |||
| c499abbb88 | |||
| 1a7ee88ecd | |||
| 16d19eb70f | |||
| 331346b99c | |||
| 95d265f672 | |||
| 315c7d6328 | |||
| 490e174564 | |||
| b5cde79f8b | |||
| d50f14bb78 | |||
| c13a751c1a | |||
| 5c37fc55d5 | |||
| 48a0f90597 | |||
| 05d3f1f06f | |||
| 4d43f6a642 | |||
| f7363ccdd7 | |||
| 07c91e9ac2 | |||
| cbe08f1d2c | |||
| c2617a8277 | |||
| fe72d2de41 | |||
| 23667e218f | |||
| 977fa72dde | |||
| 5197f954c0 | |||
| 58341e4cd2 | |||
| 1559703567 | |||
| 0a1fd50d07 | |||
| 1c19062c63 | |||
| 25cf594eb9 | |||
| 1c3beee6cd | |||
| 95c3d4c315 | |||
| 85df339e56 | |||
| d61ad44ebc | |||
| ccb1eff749 | |||
| bfb48e3aa7 | |||
| e2e08ad390 | |||
| f0dda06af3 | |||
| 4c4e77e21d | |||
| f364ae8929 | |||
| b52f292d89 | |||
| 8cac7f907c | |||
| a18a60679f | |||
| 5cc6a81b8c | |||
| 6ff212b698 | |||
| 56bcec5196 | |||
| 12019f90e9 | |||
| 7e6e69ed49 | |||
| a09a945e17 | |||
| df714dc8de | |||
| 28b63ef0c7 | |||
| 1b594dac61 | |||
| dd34a30ee0 | |||
| 0af398ceed | |||
| 04abd2cacc |
@ -1,5 +0,0 @@
|
|||||||
ARG VARIANT="lts-bullseye"
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
|
||||||
|
|
||||||
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Node.js",
|
|
||||||
|
|
||||||
"build": {
|
|
||||||
"dockerfile": "Dockerfile",
|
|
||||||
"args": { "VARIANT": "16-bullseye" }
|
|
||||||
},
|
|
||||||
|
|
||||||
// Set *default* container specific settings.json values on container create.
|
|
||||||
"settings": {},
|
|
||||||
|
|
||||||
// Add the IDs of extensions you want installed when the container is created.
|
|
||||||
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "lokalise.i18n-ally"],
|
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
||||||
"forwardPorts": [80, 5432],
|
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
|
||||||
"postCreateCommand": "pnpm install",
|
|
||||||
|
|
||||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
|
||||||
"remoteUser": "node",
|
|
||||||
|
|
||||||
"features": {
|
|
||||||
"docker-from-docker": "latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +1,22 @@
|
|||||||
|
# Android App
|
||||||
|
/app
|
||||||
|
|
||||||
# Build Artifacts
|
# Build Artifacts
|
||||||
dist
|
dist
|
||||||
.next
|
.next
|
||||||
|
.turbo
|
||||||
|
|
||||||
# IDEs
|
# IDEs
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# Project Metadata
|
# Project Metadata
|
||||||
|
.crowdin.yml
|
||||||
|
|
||||||
|
# Documentation
|
||||||
README.md
|
README.md
|
||||||
|
SECURITY.md
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
|
CODE_OF_CONDUCT.md
|
||||||
|
|
||||||
# Project Dependencies
|
# Project Dependencies
|
||||||
node_modules
|
node_modules
|
||||||
@ -16,6 +25,3 @@ node_modules
|
|||||||
Dockerfile
|
Dockerfile
|
||||||
.dockerignore
|
.dockerignore
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
|
|
||||||
# Android App
|
|
||||||
/app
|
|
||||||
16
.env.example
16
.env.example
@ -1,7 +1,11 @@
|
|||||||
|
# Turbo Cache (Optional)
|
||||||
|
TURBO_TEAM=
|
||||||
|
TURBO_TOKEN=
|
||||||
|
|
||||||
# Server + Client
|
# Server + Client
|
||||||
TZ=UTC
|
TZ=UTC
|
||||||
PUBLIC_URL=http://localhost:3000
|
PUBLIC_URL=http://localhost:3000
|
||||||
PUBLIC_SERVER_URL=http://localhost:3000/api
|
PUBLIC_SERVER_URL=http://localhost:3100
|
||||||
PUBLIC_GOOGLE_CLIENT_ID=
|
PUBLIC_GOOGLE_CLIENT_ID=
|
||||||
|
|
||||||
# Server + Database
|
# Server + Database
|
||||||
@ -18,10 +22,12 @@ JWT_SECRET=
|
|||||||
JWT_EXPIRY_TIME=604800
|
JWT_EXPIRY_TIME=604800
|
||||||
GOOGLE_CLIENT_SECRET=
|
GOOGLE_CLIENT_SECRET=
|
||||||
GOOGLE_API_KEY=
|
GOOGLE_API_KEY=
|
||||||
SENDGRID_API_KEY=
|
MAIL_FROM_NAME=
|
||||||
SENDGRID_FORGOT_PASSWORD_TEMPLATE_ID=
|
MAIL_FROM_EMAIL=
|
||||||
SENDGRID_FROM_NAME=
|
MAIL_HOST=
|
||||||
SENDGRID_FROM_EMAIL=
|
MAIL_PORT=
|
||||||
|
MAIL_USERNAME=
|
||||||
|
MAIL_PASSWORD=
|
||||||
STORAGE_BUCKET=
|
STORAGE_BUCKET=
|
||||||
STORAGE_REGION=
|
STORAGE_REGION=
|
||||||
STORAGE_ENDPOINT=
|
STORAGE_ENDPOINT=
|
||||||
|
|||||||
@ -1,31 +1,22 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
|
||||||
"ignorePatterns": ["/app"],
|
"ignorePatterns": ["/app"],
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"extends": ["plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
|
"extends": ["plugin:@typescript-eslint/recommended"],
|
||||||
"plugins": ["@typescript-eslint/eslint-plugin", "simple-import-sort", "unused-imports"],
|
"plugins": ["@typescript-eslint/eslint-plugin", "simple-import-sort"],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
// ESLint
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
|
||||||
|
// Simple Import Sort
|
||||||
|
"simple-import-sort/imports": "error",
|
||||||
|
"simple-import-sort/exports": "error",
|
||||||
|
|
||||||
// TypeScript ESLint
|
// TypeScript ESLint
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
"@typescript-eslint/interface-name-prefix": "off",
|
"@typescript-eslint/interface-name-prefix": "off",
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||||
// Simple Import Sort
|
|
||||||
"simple-import-sort/imports": "error",
|
|
||||||
"simple-import-sort/exports": "error",
|
|
||||||
// Unused Imports
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"unused-imports/no-unused-imports": "error",
|
|
||||||
"unused-imports/no-unused-vars": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
"vars": "all",
|
|
||||||
"varsIgnorePattern": "^_",
|
|
||||||
"args": "none",
|
|
||||||
"argsIgnorePattern": "^_"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
|||||||
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +1,2 @@
|
|||||||
|
github: AmruthPillai
|
||||||
custom: https://paypal.me/RajaRajanA
|
custom: https://paypal.me/RajaRajanA
|
||||||
|
|||||||
17
.github/ISSUE_TEMPLATE/bug-report.md
vendored
17
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -1,36 +1,43 @@
|
|||||||
---
|
---
|
||||||
name: Bug Report
|
name: Bug Report
|
||||||
about: Create a report to help improve
|
about: Create a report to help improve
|
||||||
title: "[BUG] "
|
title: '[BUG] '
|
||||||
labels: bug
|
labels: bug
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
|
||||||
<!-- A clear and concise description of what the bug is. -->
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
|
||||||
**Product Flavor**
|
**Product Flavor**
|
||||||
|
|
||||||
- [ ] Managed (https://rxresu.me)
|
- [ ] Managed (https://rxresu.me)
|
||||||
- [ ] Self Hosted
|
- [ ] Self Hosted
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
|
|
||||||
<!-- Steps to reproduce the behavior: -->
|
<!-- Steps to reproduce the behavior: -->
|
||||||
|
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
**Expected behavior**
|
**Expected behavior**
|
||||||
|
|
||||||
<!-- A clear and concise description of what you expected to happen. -->
|
<!-- A clear and concise description of what you expected to happen. -->
|
||||||
|
|
||||||
**Screenshots**
|
**Screenshots**
|
||||||
|
|
||||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Desktop (please complete the following information):**
|
||||||
- OS: <!--[e.g. iOS]-->
|
|
||||||
- Browser <!--[e.g. chrome, safari]-->
|
- OS: <!--[e.g. iOS]-->
|
||||||
- Version <!--[e.g. 22]-->
|
- Browser <!--[e.g. chrome, safari]-->
|
||||||
|
- Version <!--[e.g. 22]-->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
|
|
||||||
<!-- Add any other context about the problem here. -->
|
<!-- Add any other context about the problem here. -->
|
||||||
|
|||||||
7
.github/ISSUE_TEMPLATE/feature-request.md
vendored
7
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@ -1,20 +1,23 @@
|
|||||||
---
|
---
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: "[FEATURE] "
|
title: '[FEATURE] '
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
|
||||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
|
|
||||||
<!-- A clear and concise description of what you want to happen. -->
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
**Describe alternatives you've considered**
|
||||||
|
|
||||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
|
|
||||||
<!-- Add any other context or screenshots about the feature request here. -->
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|||||||
36
.github/workflows/docker-build-push.yml
vendored
36
.github/workflows/docker-build-push.yml
vendored
@ -9,14 +9,29 @@ jobs:
|
|||||||
name: Client
|
name: Client
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3.0.2
|
uses: actions/checkout@v3.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
- id: version
|
- id: version
|
||||||
name: Get Version
|
name: Get Version
|
||||||
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2.0.0
|
||||||
|
|
||||||
|
- id: buildx
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2.0.0
|
||||||
|
with:
|
||||||
|
install: true
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2.0.0
|
uses: docker/login-action@v2.0.0
|
||||||
with:
|
with:
|
||||||
@ -31,11 +46,12 @@ jobs:
|
|||||||
password: ${{ secrets.GH_TOKEN }}
|
password: ${{ secrets.GH_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Push Client Image
|
- name: Build and Push Client Image
|
||||||
uses: docker/build-push-action@v3.0.0
|
uses: docker/build-push-action@v3.1.1
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
file: client/Dockerfile
|
file: client/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
tags: |
|
tags: |
|
||||||
amruthpillai/reactive-resume:client-latest
|
amruthpillai/reactive-resume:client-latest
|
||||||
amruthpillai/reactive-resume:client-${{ steps.version.outputs.tag }}
|
amruthpillai/reactive-resume:client-${{ steps.version.outputs.tag }}
|
||||||
@ -46,14 +62,29 @@ jobs:
|
|||||||
name: Server
|
name: Server
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3.0.2
|
uses: actions/checkout@v3.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
- id: version
|
- id: version
|
||||||
name: Get Version
|
name: Get Version
|
||||||
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2.0.0
|
||||||
|
|
||||||
|
- id: buildx
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2.0.0
|
||||||
|
with:
|
||||||
|
install: true
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2.0.0
|
uses: docker/login-action@v2.0.0
|
||||||
with:
|
with:
|
||||||
@ -68,11 +99,12 @@ jobs:
|
|||||||
password: ${{ secrets.GH_TOKEN }}
|
password: ${{ secrets.GH_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Push Server Image
|
- name: Build and Push Server Image
|
||||||
uses: docker/build-push-action@v3.0.0
|
uses: docker/build-push-action@v3.1.1
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
file: server/Dockerfile
|
file: server/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
tags: |
|
tags: |
|
||||||
amruthpillai/reactive-resume:server-latest
|
amruthpillai/reactive-resume:server-latest
|
||||||
amruthpillai/reactive-resume:server-${{ steps.version.outputs.tag }}
|
amruthpillai/reactive-resume:server-${{ steps.version.outputs.tag }}
|
||||||
|
|||||||
84
.github/workflows/docker-build.yml
vendored
Normal file
84
.github/workflows/docker-build.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
name: Build Docker Image
|
||||||
|
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
client:
|
||||||
|
name: Client
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v3.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2.0.0
|
||||||
|
|
||||||
|
- id: buildx
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2.0.0
|
||||||
|
with:
|
||||||
|
install: true
|
||||||
|
|
||||||
|
- id: variables
|
||||||
|
name: Get Short SHA
|
||||||
|
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
- name: Build Client Image
|
||||||
|
uses: docker/build-push-action@v3.1.1
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: false
|
||||||
|
file: client/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tags: |
|
||||||
|
amruthpillai/reactive-resume:client-latest
|
||||||
|
amruthpillai/reactive-resume:client-${{ steps.variables.outputs.sha_short }}
|
||||||
|
ghcr.io/amruthpillai/reactive-resume:client-latest
|
||||||
|
ghcr.io/amruthpillai/reactive-resume:client-${{ steps.variables.outputs.sha_short }}
|
||||||
|
|
||||||
|
server:
|
||||||
|
name: Server
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v3.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2.0.0
|
||||||
|
|
||||||
|
- id: buildx
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2.0.0
|
||||||
|
with:
|
||||||
|
install: true
|
||||||
|
|
||||||
|
- id: variables
|
||||||
|
name: Get Short SHA
|
||||||
|
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
- name: Build Server Image
|
||||||
|
uses: docker/build-push-action@v3.1.1
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: false
|
||||||
|
file: server/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tags: |
|
||||||
|
amruthpillai/reactive-resume:server-latest
|
||||||
|
amruthpillai/reactive-resume:server-${{ steps.variables.outputs.sha_short }}
|
||||||
|
ghcr.io/amruthpillai/reactive-resume:server-latest
|
||||||
|
ghcr.io/amruthpillai/reactive-resume:server-${{ steps.variables.outputs.sha_short }}
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -7,4 +7,7 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Turbo
|
||||||
|
.turbo
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
pnpm install
|
|
||||||
pnpm run lint
|
|
||||||
pnpm run format
|
|
||||||
2
.npmrc
Normal file
2
.npmrc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
auto-install-peers=true
|
||||||
|
strict-peer-dependencies=false
|
||||||
@ -1,3 +1,6 @@
|
|||||||
|
# Android App
|
||||||
|
/app
|
||||||
|
|
||||||
# Schema
|
# Schema
|
||||||
schema/dist
|
schema/dist
|
||||||
|
|
||||||
@ -18,15 +21,9 @@ CHANGELOG.md
|
|||||||
|
|
||||||
# Project Dependencies
|
# Project Dependencies
|
||||||
node_modules
|
node_modules
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
# Docker
|
# Docker
|
||||||
Dockerfile
|
Dockerfile
|
||||||
.dockerignore
|
.dockerignore
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
|
|
||||||
# Android App
|
|
||||||
/app
|
|
||||||
|
|
||||||
# Docs
|
|
||||||
docs/build
|
|
||||||
docs/.docusaurus
|
|
||||||
8
.vscode/extensions.json
vendored
8
.vscode/extensions.json
vendored
@ -1,3 +1,7 @@
|
|||||||
{
|
{
|
||||||
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "lokalise.i18n-ally"]
|
"recommendations": [
|
||||||
}
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"lokalise.i18n-ally"
|
||||||
|
]
|
||||||
|
}
|
||||||
26
.vscode/launch.json
vendored
26
.vscode/launch.json
vendored
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"request": "attach",
|
|
||||||
"name": "Debug: Server",
|
|
||||||
"port": 9229,
|
|
||||||
"restart": true,
|
|
||||||
"stopOnEntry": false,
|
|
||||||
"protocol": "inspector"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Debug: Client",
|
|
||||||
"type": "node-terminal",
|
|
||||||
"request": "launch",
|
|
||||||
"command": "pnpm run dev:client",
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"serverReadyAction": {
|
|
||||||
"pattern": "started server on .+, url: (https?://.+)",
|
|
||||||
"uriFormat": "%s",
|
|
||||||
"action": "debugWithChrome"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
25
.vscode/settings.json
vendored
25
.vscode/settings.json
vendored
@ -1,25 +1,22 @@
|
|||||||
{
|
{
|
||||||
"css.validate": false,
|
"css.validate": false,
|
||||||
|
"scss.validate": false,
|
||||||
|
"editor.wordWrap": "on",
|
||||||
|
"npm.packageManager": "pnpm",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": true
|
||||||
},
|
},
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.wordWrap": "on",
|
|
||||||
"eslint.workingDirectories": [
|
"eslint.workingDirectories": [
|
||||||
"schema",
|
"schema",
|
||||||
"client",
|
"client",
|
||||||
"server"
|
"server"
|
||||||
],
|
],
|
||||||
"i18n-ally.enabledFrameworks": [
|
"conventionalCommits.scopes": [
|
||||||
"react"
|
"client",
|
||||||
],
|
"server",
|
||||||
"i18n-ally.keystyle": "nested",
|
"docker",
|
||||||
"i18n-ally.localesPaths": [
|
"dependencies"
|
||||||
"client/public/locales"
|
]
|
||||||
],
|
|
||||||
"i18n-ally.namespace": true,
|
|
||||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
|
|
||||||
"i18n-ally.sortKeys": true,
|
|
||||||
"scss.validate": false
|
|
||||||
}
|
}
|
||||||
279
CHANGELOG.md
279
CHANGELOG.md
@ -1,279 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
||||||
|
|
||||||
### [3.4.6](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.5...v3.4.6) (2022-06-19)
|
|
||||||
|
|
||||||
## [3.6.0](https://github.com/dvd741-a/Reactive-Resume/compare/v3.3.4...v3.6.0) (2022-06-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **all:** upgrade to v3.4.0 ([87d381f](https://github.com/dvd741-a/Reactive-Resume/commit/87d381fe8eab9ca4624df5de6e8b9ab18a072b67))
|
|
||||||
* **i18n:** add Hungrarian (Magyar) language ([35fe4e2](https://github.com/dvd741-a/Reactive-Resume/commit/35fe4e27744b6f7325b25db2cf3b626ed8598623))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **i18n:** fix language mismatch in exported pdf ([62fd63e](https://github.com/dvd741-a/Reactive-Resume/commit/62fd63e41fe10fba843a40fb08191f5944f2b2fc))
|
|
||||||
* **typeorm:** update typeorm to latest 0.2.x for secpatch ([5bdb92b](https://github.com/dvd741-a/Reactive-Resume/commit/5bdb92b1cff9e56879f9bbf31801d6554a00a8d5))
|
|
||||||
|
|
||||||
## [3.5.0](https://github.com/dvd741-a/Reactive-Resume/compare/v3.3.4...v3.5.0) (2022-06-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **all:** upgrade to v3.4.0 ([87d381f](https://github.com/dvd741-a/Reactive-Resume/commit/87d381fe8eab9ca4624df5de6e8b9ab18a072b67))
|
|
||||||
* **i18n:** add Hungrarian (Magyar) language ([35fe4e2](https://github.com/dvd741-a/Reactive-Resume/commit/35fe4e27744b6f7325b25db2cf3b626ed8598623))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **i18n:** fix language mismatch in exported pdf ([62fd63e](https://github.com/dvd741-a/Reactive-Resume/commit/62fd63e41fe10fba843a40fb08191f5944f2b2fc))
|
|
||||||
* **typeorm:** update typeorm to latest 0.2.x for secpatch ([5bdb92b](https://github.com/dvd741-a/Reactive-Resume/commit/5bdb92b1cff9e56879f9bbf31801d6554a00a8d5))
|
|
||||||
|
|
||||||
### [3.4.5](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.4...v3.4.5) (2022-05-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **i18n:** fix language mismatch in exported pdf ([62fd63e](https://github.com/AmruthPillai/Reactive-Resume/commit/62fd63e41fe10fba843a40fb08191f5944f2b2fc))
|
|
||||||
|
|
||||||
## [3.4.4](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.3...v3.4.4) (2022-05-02)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **i18n:** add Hungrarian (Magyar) language ([35fe4e2](https://github.com/AmruthPillai/Reactive-Resume/commit/35fe4e27744b6f7325b25db2cf3b626ed8598623))
|
|
||||||
|
|
||||||
### [3.4.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.2...v3.4.3) (2022-05-01)
|
|
||||||
|
|
||||||
### [3.4.2](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.1...v3.4.2) (2022-04-30)
|
|
||||||
|
|
||||||
### [3.4.1](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.0...v3.4.1) (2022-04-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **typeorm:** update typeorm to latest 0.2.x for secpatch ([5bdb92b](https://github.com/AmruthPillai/Reactive-Resume/commit/5bdb92b1cff9e56879f9bbf31801d6554a00a8d5))
|
|
||||||
|
|
||||||
### [3.4.0](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.3.3...v3.4.0) (2022-04-30)
|
|
||||||
|
|
||||||
### [3.3.4](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.3.3...v3.3.4) (2022-04-09)
|
|
||||||
|
|
||||||
### [3.3.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.3.2...v3.3.3) (2022-04-09)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **profile:** add XING profile icon ([1e59f73](https://github.com/AmruthPillai/Reactive-Resume/commit/1e59f73f79a91d0264c0d2108906ee89d4eb27f8)), closes [#821](https://github.com/AmruthPillai/Reactive-Resume/issues/821)
|
|
||||||
* **s3:** implement non-ephemeral storage through S3/DO Spaces ([feb911a](https://github.com/AmruthPillai/Reactive-Resume/commit/feb911aea06bacf58ea933d2803a2a89fe36e57b))
|
|
||||||
|
|
||||||
### [3.3.2](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.3.1...v3.3.2) (2022-04-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **types/react:** downgrade to <18 ([fc77b54](https://github.com/AmruthPillai/Reactive-Resume/commit/fc77b548d8d61530b2d158ff83f088bed12d5080))
|
|
||||||
|
|
||||||
### [3.3.1](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.3.0...v3.3.1) (2022-04-08)
|
|
||||||
|
|
||||||
## [3.3.0](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.11...v3.3.0) (2022-04-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **upgrade:** changes to code to support new template ([1df7810](https://github.com/AmruthPillai/Reactive-Resume/commit/1df78100ca0667ce9b7834cf2c25384eb21c67c2))
|
|
||||||
|
|
||||||
### What's Changed
|
|
||||||
* New Crowdin updates by @AmruthPillai in https://github.com/AmruthPillai/Reactive-Resume/pull/791
|
|
||||||
* Bump org.jetbrains.kotlin.android from 1.6.10 to 1.6.20 in /app by @dependabot in https://github.com/AmruthPillai/Reactive-Resume/pull/812
|
|
||||||
* New Crowdin updates by @AmruthPillai in https://github.com/AmruthPillai/Reactive-Resume/pull/806
|
|
||||||
* A new template - Leafish by @klejejs in https://github.com/AmruthPillai/Reactive-Resume/pull/811
|
|
||||||
* Automatic multi-platform Docker image build by @schklom in https://github.com/AmruthPillai/Reactive-Resume/pull/817
|
|
||||||
|
|
||||||
### New Contributors
|
|
||||||
* @klejejs made their first contribution in https://github.com/AmruthPillai/Reactive-Resume/pull/811
|
|
||||||
* @schklom made their first contribution in https://github.com/AmruthPillai/Reactive-Resume/pull/817
|
|
||||||
|
|
||||||
### [3.2.11](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.10...v3.2.11) (2022-03-28)
|
|
||||||
|
|
||||||
### [3.2.10](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.9...v3.2.10) (2022-03-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **i18n:** add portuguese (pt) language to i18n locales ([7f1c82c](https://github.com/AmruthPillai/Reactive-Resume/commit/7f1c82cd9185ebb44486a16132eb44d5c2fb747a))
|
|
||||||
|
|
||||||
### [3.2.9](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.8...v3.2.9) (2022-03-21)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **i18n:** add nl and ru i18n locales to app ([03cbf22](https://github.com/AmruthPillai/Reactive-Resume/commit/03cbf22c9bee96cac8f228830b67b44529b7ecee))
|
|
||||||
|
|
||||||
### [3.2.8](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.7...v3.2.8) (2022-03-18)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **client/theme:** add theme switcher to landing page ([8f5632c](https://github.com/AmruthPillai/Reactive-Resume/commit/8f5632c5ad0bc8a4b3028c2806365717fedd78c9))
|
|
||||||
* **flags:** introduce flags, disable_user_signups ([b6267d0](https://github.com/AmruthPillai/Reactive-Resume/commit/b6267d07ba2dcaed0da3946d136a0a9a01c441d5)), closes [#698](https://github.com/AmruthPillai/Reactive-Resume/issues/698)
|
|
||||||
* **i18n:** add Vietnamese language to i18n locales ([4390bcc](https://github.com/AmruthPillai/Reactive-Resume/commit/4390bccfb9764f2d2730ec3a124b7befb6792e9a))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **client/create-rename-slug:** fix slug accepting apostrophes and other special characters ([1facd2a](https://github.com/AmruthPillai/Reactive-Resume/commit/1facd2ad111cd9d990c808b3956d3915e8711acd)), closes [#706](https://github.com/AmruthPillai/Reactive-Resume/issues/706)
|
|
||||||
* **disable_user_signups:** hide create account link under flag ([80acfe9](https://github.com/AmruthPillai/Reactive-Resume/commit/80acfe97c74bfa05b719285b19144144f3f7c5ba))
|
|
||||||
|
|
||||||
### [3.2.7](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.6...v3.2.7) (2022-03-18)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **i18n:** add Malayalam (മലയാളം) language to i18n locales ([3a2e62b](https://github.com/AmruthPillai/Reactive-Resume/commit/3a2e62be4c9acc14f17277c060cc9ea2c417a478))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **printer/i18n:** fix dates not showing up in resume language when printing ([90321e1](https://github.com/AmruthPillai/Reactive-Resume/commit/90321e1284409ab9442883c04a9b4c591d36f95d)), closes [#729](https://github.com/AmruthPillai/Reactive-Resume/issues/729)
|
|
||||||
|
|
||||||
### [3.2.6](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.5...v3.2.6) (2022-03-17)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **client/auth/google:** disable google login/registration if GOOGLE_CLIENT_ID is not in ENV ([7f0ee40](https://github.com/AmruthPillai/Reactive-Resume/commit/7f0ee40af4acc7eb41514406ecee3218ace9e891)), closes [#724](https://github.com/AmruthPillai/Reactive-Resume/issues/724)
|
|
||||||
* **i18n:** add arabic language to i18n locale ([39fa6da](https://github.com/AmruthPillai/Reactive-Resume/commit/39fa6da5dd77ce2e12e81530fa18c2eac722c1f2))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **i18n:** add missing languages to dayjs date wrapper locales ([9e6dafc](https://github.com/AmruthPillai/Reactive-Resume/commit/9e6dafc8cada5c01559894905996b81004bedaec)), closes [#719](https://github.com/AmruthPillai/Reactive-Resume/issues/719)
|
|
||||||
* **json-export:** add mimeType and charset to JSON export ([b3ff780](https://github.com/AmruthPillai/Reactive-Resume/commit/b3ff7805cd856a52900d9acef0554867d8ce0b01)), closes [#726](https://github.com/AmruthPillai/Reactive-Resume/issues/726)
|
|
||||||
* **linkedin:** fix skill modal crashing when importing from linkedin ([a02b85b](https://github.com/AmruthPillai/Reactive-Resume/commit/a02b85b4bb1c4a1499aacddeac7bc59bcb1f7adb)), closes [#718](https://github.com/AmruthPillai/Reactive-Resume/issues/718)
|
|
||||||
|
|
||||||
### [3.2.5](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.4...v3.2.5) (2022-03-16)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **i18n:** add danish, polish and turkish locales to i18n ([97e9432](https://github.com/AmruthPillai/Reactive-Resume/commit/97e9432d6bd887e666a3443fbfde9a92cef53965))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **client/templates:** fix text veering off of artboard in most templates ([b2f1fb3](https://github.com/AmruthPillai/Reactive-Resume/commit/b2f1fb3a5502988a49c5cd3e496d9d165f5c1792)), closes [#702](https://github.com/AmruthPillai/Reactive-Resume/issues/702)
|
|
||||||
|
|
||||||
### [3.2.4](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.3...v3.2.4) (2022-03-14)
|
|
||||||
|
|
||||||
### [3.2.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.2...v3.2.3) (2022-03-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **client/import:** implement import json from reactive resume v2 ([42408ce](https://github.com/AmruthPillai/Reactive-Resume/commit/42408ce8c5ce55904854f9f6e0481889a01edfb8))
|
|
||||||
|
|
||||||
### [3.2.2](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.1...v3.2.2) (2022-03-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **client/skills:** make skill level optional ([02e396b](https://github.com/AmruthPillai/Reactive-Resume/commit/02e396bfdbf07ae75661f1e7e4e55060cacee7d0))
|
|
||||||
|
|
||||||
### [3.2.1](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.0...v3.2.1) (2022-03-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **i18n:** add Chinese (Simplified) language to locales ([549363b](https://github.com/AmruthPillai/Reactive-Resume/commit/549363bbe5bdd781699dea9506bd4baedf5740d1))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **client/basics:** fix issue with overlapping photo filters on safari/webkit/iOS ([e6bda68](https://github.com/AmruthPillai/Reactive-Resume/commit/e6bda688ac3ba1c04e82721add92e755ea5386c3))
|
|
||||||
* **docker:** fix docker-compose for production grade deployments ([57f7edc](https://github.com/AmruthPillai/Reactive-Resume/commit/57f7edc13432a038c907afc6cb74b5182a9b2333))
|
|
||||||
|
|
||||||
## [3.2.0](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.4...v3.2.0) (2022-03-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **i18n:** add Bengali, Italian and other languages ([21931bc](https://github.com/AmruthPillai/Reactive-Resume/commit/21931bc324b5e2440baaaaa2e52a93b4f2c766f8))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **app:** fix issue with external link redirection in android app ([b18120b](https://github.com/AmruthPillai/Reactive-Resume/commit/b18120b3f7223981e28c0441a6b7725787186edb))
|
|
||||||
* **client:** fix issue with react-query cache ([ed75a85](https://github.com/AmruthPillai/Reactive-Resume/commit/ed75a858279047dfd43152e041c1a09a625417f5))
|
|
||||||
|
|
||||||
### [3.1.4](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.3...v3.1.4) (2022-03-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **client:** exported pdf did not contain "Present" keyword with translations ([cf670af](https://github.com/AmruthPillai/Reactive-Resume/commit/cf670af4035dc9b462cf5b1aad06ca089cf1d40c))
|
|
||||||
* **client:** fix issues raised through lgtm alerts ([dfccb31](https://github.com/AmruthPillai/Reactive-Resume/commit/dfccb3130f889934d31196226be3d33e772f323b))
|
|
||||||
|
|
||||||
### [3.1.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.2...v3.1.3) (2022-03-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **server:** reform url for pdf generation and download ([6d55f91](https://github.com/AmruthPillai/Reactive-Resume/commit/6d55f917eab3cb2f5f3a90c5a18f03b625d60021)), closes [#661](https://github.com/AmruthPillai/Reactive-Resume/issues/661)
|
|
||||||
|
|
||||||
### [3.1.2](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.1...v3.1.2) (2022-03-12)
|
|
||||||
|
|
||||||
|
|
||||||
### CI
|
|
||||||
|
|
||||||
* **docker:**: include traefik routing and proxy to ensure server connections pass in local ([11cb066](https://github.com/AmruthPillai/Reactive-Resume/commit/11cb066573c6917857b79c028b97fcda1acaf90a))
|
|
||||||
|
|
||||||
### [3.1.1](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.0...v3.1.1) (2022-03-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **client:** add product hunt announcement banner ([b515fc3](https://github.com/AmruthPillai/Reactive-Resume/commit/b515fc36e7f282db92e8eb509b6c5004a944fa95))
|
|
||||||
|
|
||||||
## [3.1.0](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.0.0...v3.1.0) (2022-03-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **client:** add "spanish (es)" language to i18n locales ([bf167f8](https://github.com/AmruthPillai/Reactive-Resume/commit/bf167f81a3659677dada55856f5eaf0fc469e697))
|
|
||||||
* **client:** add mm/yyyy date option to date format options ([82bf44d](https://github.com/AmruthPillai/Reactive-Resume/commit/82bf44daa24422156779e9b38d3dc695176eaa09)), closes [#656](https://github.com/AmruthPillai/Reactive-Resume/issues/656)
|
|
||||||
* **client:** add sitemap generation to next app ([2cbc582](https://github.com/AmruthPillai/Reactive-Resume/commit/2cbc582a12b72b3012246022d4b518ed657d4c08))
|
|
||||||
* **client:** disable "Toggle Page Orientation" when there's only one page on the artboard ([01da1a0](https://github.com/AmruthPillai/Reactive-Resume/commit/01da1a06b802f1063a41d7a9a682e76b1daf9461)), closes [#655](https://github.com/AmruthPillai/Reactive-Resume/issues/655)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **client:** remove hard-coded "keywords:" in certain templates ([dda42b4](https://github.com/AmruthPillai/Reactive-Resume/commit/dda42b4c6b3bc359ac4f2bb91ca8118ddc84ec07)), closes [#650](https://github.com/AmruthPillai/Reactive-Resume/issues/650)
|
|
||||||
* **client:** show "present" string if end date is not entered, also add to i18n locales ([b5cd6c4](https://github.com/AmruthPillai/Reactive-Resume/commit/b5cd6c412b5b6b6ca7bb43c3801762de451f06b4)), closes [#653](https://github.com/AmruthPillai/Reactive-Resume/issues/653)
|
|
||||||
* **server:** photo uploads not working, fix save location and returned url ([799f208](https://github.com/AmruthPillai/Reactive-Resume/commit/799f20823e6d97a1ff0ba2c45c61d56304d0fa58)), closes [#658](https://github.com/AmruthPillai/Reactive-Resume/issues/658)
|
|
||||||
|
|
||||||
## [3.0.0](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.0.0-beta.6...v3.0.0) (2022-03-11)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **lang**: add German, Kannada and Tamil languages to the app ([3a524f9](https://github.com/AmruthPillai/Reactive-Resume/commit/3a524f9c9c7a0e446491265b2242ad3dfeae188c))
|
|
||||||
* **docs:** add docusaurus workspace, initial setup of docs ([dc4aa0b](https://github.com/AmruthPillai/Reactive-Resume/commit/dc4aa0b496096bd59c45426bfcea6ba7db5f5c01))
|
|
||||||
|
|
||||||
## [3.0.0-beta.6](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.0.0-beta.5...v3.0.0-beta.6) (2022-03-11)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **lang:** add language switcher on the landing page, in the footer ([8bc7d25](https://github.com/AmruthPillai/Reactive-Resume/commit/8bc7d2599ef6af7a07bfbe886c43844152b0d9f7))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **i18n:** add missing translation keys, update lang/locale logic ([7d8828a](https://github.com/AmruthPillai/Reactive-Resume/commit/7d8828a358d653bb162877a64c75028eb82678cd))
|
|
||||||
* **webkit:** fix issue with webkit not supporting .at() ([2654cba](https://github.com/AmruthPillai/Reactive-Resume/commit/2654cba039eb73d33257c36fa90a52cabc9fda96))
|
|
||||||
|
|
||||||
## [3.0.0-beta.5](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.0.0-beta.4...v3.0.0-beta.5) (2022-03-10)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **app:** fix issue with using swipelayout ([972e8b1](https://github.com/AmruthPillai/Reactive-Resume/commit/972e8b1bcf9ad44d8915bf23d189711672937bc0))
|
|
||||||
@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
|||||||
Examples of behavior that contributes to a positive environment for our
|
Examples of behavior that contributes to a positive environment for our
|
||||||
community include:
|
community include:
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
- Demonstrating empathy and kindness toward other people
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
* Giving and gracefully accepting constructive feedback
|
- Giving and gracefully accepting constructive feedback
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
and learning from the experience
|
and learning from the experience
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
- Focusing on what is best not just for us as individuals, but for the
|
||||||
overall community
|
overall community
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
- The use of sexualized language or imagery, and sexual attention or
|
||||||
advances of any kind
|
advances of any kind
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or email
|
- Publishing others' private information, such as a physical or email
|
||||||
address, without their explicit permission
|
address, without their explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
- Other conduct which could reasonably be considered inappropriate in a
|
||||||
professional setting
|
professional setting
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
## Enforcement Responsibilities
|
||||||
@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
|
|||||||
### 4. Permanent Ban
|
### 4. Permanent Ban
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
|||||||
21
README.md
21
README.md
@ -1,4 +1,4 @@
|
|||||||
<img src="https://github.com/AmruthPillai/Reactive-Resume/blob/main/docs/static/logo.svg" alt="Reactive Resume" width="256px" height="256px" />
|
<img src="https://rxresu.me/images/logos/logo.png" alt="Reactive Resume" width="256px" height="256px" />
|
||||||
|
|
||||||
# Reactive Resume
|
# Reactive Resume
|
||||||
|
|
||||||
@ -18,7 +18,6 @@ You have complete control over what goes into your resume, how it looks, what co
|
|||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Reactive Resume](#reactive-resume)
|
- [Reactive Resume](#reactive-resume)
|
||||||
- [Go to App | [Docs](https://docs.rxresu.me)](#go-to-app--docs)
|
|
||||||
- [Table of Contents](#table-of-contents)
|
- [Table of Contents](#table-of-contents)
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Languages](#languages)
|
- [Languages](#languages)
|
||||||
@ -27,7 +26,6 @@ You have complete control over what goes into your resume, how it looks, what co
|
|||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
- [Report Bugs and Feature Requests](#report-bugs-and-feature-requests)
|
- [Report Bugs and Feature Requests](#report-bugs-and-feature-requests)
|
||||||
- [Donations](#donations)
|
- [Donations](#donations)
|
||||||
- [💸 PayPal](#-paypal)
|
|
||||||
- [Infrastructure](#infrastructure)
|
- [Infrastructure](#infrastructure)
|
||||||
- [Contributors Wall](#contributors-wall)
|
- [Contributors Wall](#contributors-wall)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
@ -56,11 +54,14 @@ You have complete control over what goes into your resume, how it looks, what co
|
|||||||
|
|
||||||
- Arabic (اَلْعَرَبِيَّةُ)
|
- Arabic (اَلْعَرَبِيَّةُ)
|
||||||
- Bengali (বাংলা)
|
- Bengali (বাংলা)
|
||||||
|
- Bulgarian (български)
|
||||||
|
- Catalan (Valencian)
|
||||||
- Chinese (中文)
|
- Chinese (中文)
|
||||||
- Czech (čeština)
|
- Czech (čeština)
|
||||||
- Danish (Dansk)
|
- Danish (Dansk)
|
||||||
- Dutch (Nederlands)
|
- Dutch (Nederlands)
|
||||||
- English
|
- English
|
||||||
|
- Finnish (Suomi)
|
||||||
- French (Français)
|
- French (Français)
|
||||||
- German (Deutsch)
|
- German (Deutsch)
|
||||||
- Greek (Ελληνικά)
|
- Greek (Ελληνικά)
|
||||||
@ -69,17 +70,26 @@ You have complete control over what goes into your resume, how it looks, what co
|
|||||||
- Hungarian (Magyar)
|
- Hungarian (Magyar)
|
||||||
- Indonesian (Bahasa Indonesia)
|
- Indonesian (Bahasa Indonesia)
|
||||||
- Italian (Italiano)
|
- Italian (Italiano)
|
||||||
|
- Japanese (日本語)
|
||||||
- Kannada (ಕನ್ನಡ)
|
- Kannada (ಕನ್ನಡ)
|
||||||
|
- Khmer (ភាសាខ្មែរ)
|
||||||
|
- Korean (한국어)
|
||||||
- Malayalam (മലയാളം)
|
- Malayalam (മലയാളം)
|
||||||
|
- Marathi (मराठी)
|
||||||
|
- Nepali (नेपाली)
|
||||||
|
- Norwegian (Norsk)
|
||||||
- Odia (ଓଡ଼ିଆ)
|
- Odia (ଓଡ଼ିଆ)
|
||||||
- Persian (Farsi)
|
- Persian (فارسی)
|
||||||
- Polish (Polski)
|
- Polish (Polski)
|
||||||
- Portuguese (Português)
|
- Portuguese (Português)
|
||||||
|
- Romanian (limba română)
|
||||||
- Russian (русский)
|
- Russian (русский)
|
||||||
|
- Serbian (српски језик)
|
||||||
- Spanish (Español)
|
- Spanish (Español)
|
||||||
- Swedish (Svenska)
|
- Swedish (Svenska)
|
||||||
- Tamil (தமிழ்)
|
- Tamil (தமிழ்)
|
||||||
- Turkish (Türkçe)
|
- Turkish (Türkçe)
|
||||||
|
- Ukranian (Українська мова)
|
||||||
- Vietnamese (Tiếng Việt)
|
- Vietnamese (Tiếng Việt)
|
||||||
|
|
||||||
Help by [translating Reactive Resume](https://translate.rxresu.me) to your language!
|
Help by [translating Reactive Resume](https://translate.rxresu.me) to your language!
|
||||||
@ -112,7 +122,8 @@ Use the [GitHub Issues](https://github.com/AmruthPillai/Reactive-Resume/issues/n
|
|||||||
|
|
||||||
Reactive Resume would be nothing without the folks who supported me and kept the project alive in the beginning, and your continued support is what keeps me going. If you found Reactive Resume to be useful, helpful or just insightful and appreciate the effort I took to make the project, please consider donating as little or as much as you can.
|
Reactive Resume would be nothing without the folks who supported me and kept the project alive in the beginning, and your continued support is what keeps me going. If you found Reactive Resume to be useful, helpful or just insightful and appreciate the effort I took to make the project, please consider donating as little or as much as you can.
|
||||||
|
|
||||||
### [💸 PayPal](https://paypal.me/RajaRajanA)
|
### [GitHub Sponsor](https://github.com/sponsors/AmruthPillai)
|
||||||
|
### [PayPal](https://paypal.me/RajaRajanA)
|
||||||
|
|
||||||
## Infrastructure
|
## Infrastructure
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '7.1.2' apply false
|
id 'com.android.application' version '7.1.2' apply false
|
||||||
id 'com.android.library' version '7.1.2' apply false
|
id 'com.android.library' version '7.1.2' apply false
|
||||||
id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
|
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
|
|||||||
@ -1,9 +1,33 @@
|
|||||||
{
|
{
|
||||||
"extends": ["../.eslintrc.json", "next/core-web-vitals"],
|
"extends": ["../.eslintrc.json", "next/core-web-vitals", "plugin:tailwindcss/recommended"],
|
||||||
|
"plugins": ["unused-imports"],
|
||||||
"ignorePatterns": [".next", "__ENV.js"],
|
"ignorePatterns": [".next", "__ENV.js"],
|
||||||
|
"settings": {
|
||||||
|
"next": {
|
||||||
|
"rootDir": "client"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
|
// Next.js
|
||||||
"@next/next/no-img-element": "off",
|
"@next/next/no-img-element": "off",
|
||||||
"@next/next/no-sync-scripts": "off",
|
"@next/next/no-sync-scripts": "off",
|
||||||
"@next/next/no-html-link-for-pages": ["error", "pages"]
|
|
||||||
|
// React Hooks
|
||||||
|
"react-hooks/exhaustive-deps": "off",
|
||||||
|
|
||||||
|
// Unused Imports
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"unused-imports/no-unused-vars": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"vars": "all",
|
||||||
|
"args": "none",
|
||||||
|
"varsIgnorePattern": "^_",
|
||||||
|
"argsIgnorePattern": "^_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Tailwind CSS
|
||||||
|
"tailwindcss/no-custom-classname": ["warn", { "whitelist": ["preview-mode", "printer-mode", "markdown"] }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,19 @@
|
|||||||
FROM node:lts-alpine as dependencies
|
FROM node:lts-alpine AS base
|
||||||
|
|
||||||
RUN apk add --no-cache curl g++ make python3 \
|
|
||||||
&& curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json pnpm-*.yaml ./
|
RUN apk add --no-cache g++ git curl make python3 \
|
||||||
|
&& curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
|
||||||
|
|
||||||
|
FROM base as dependencies
|
||||||
|
|
||||||
|
COPY package.json pnpm-*.yaml turbo.json ./
|
||||||
COPY ./schema/package.json ./schema/package.json
|
COPY ./schema/package.json ./schema/package.json
|
||||||
COPY ./client/package.json ./client/package.json
|
COPY ./client/package.json ./client/package.json
|
||||||
|
|
||||||
RUN pnpm install --frozen-lockfile
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
FROM node:lts-alpine as builder
|
FROM base as builder
|
||||||
|
|
||||||
RUN apk add --no-cache curl g++ make python3 \
|
|
||||||
&& curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
@ -24,21 +21,20 @@ COPY --from=dependencies /app/node_modules ./node_modules
|
|||||||
COPY --from=dependencies /app/schema/node_modules ./schema/node_modules
|
COPY --from=dependencies /app/schema/node_modules ./schema/node_modules
|
||||||
COPY --from=dependencies /app/client/node_modules ./client/node_modules
|
COPY --from=dependencies /app/client/node_modules ./client/node_modules
|
||||||
|
|
||||||
RUN pnpm run build:schema
|
ARG TURBO_TEAM
|
||||||
RUN pnpm run build:client
|
ARG TURBO_TOKEN
|
||||||
|
|
||||||
FROM node:lts-alpine as production
|
ENV TURBO_TEAM $TURBO_TEAM
|
||||||
|
ENV TURBO_TOKEN $TURBO_TOKEN
|
||||||
|
|
||||||
WORKDIR /app
|
RUN pnpm run build --filter client
|
||||||
|
|
||||||
RUN apk add --no-cache curl \
|
FROM base as production
|
||||||
&& curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
|
|
||||||
|
|
||||||
COPY --from=builder /app/pnpm-*.yaml ./
|
COPY --from=builder /app/package.json /app/pnpm-*.yaml /app/turbo.json ./
|
||||||
COPY --from=builder /app/package.json ./
|
|
||||||
COPY --from=builder /app/client/package.json ./client/package.json
|
COPY --from=builder /app/client/package.json ./client/package.json
|
||||||
|
|
||||||
RUN pnpm install -F client --frozen-lockfile --prod
|
RUN pnpm install --filter client --prod --frozen-lockfile --workspace-root
|
||||||
|
|
||||||
COPY --from=builder /app/client/.next ./client/.next
|
COPY --from=builder /app/client/.next ./client/.next
|
||||||
COPY --from=builder /app/client/public ./client/public
|
COPY --from=builder /app/client/public ./client/public
|
||||||
@ -52,4 +48,4 @@ ENV PORT 3000
|
|||||||
HEALTHCHECK --interval=30s --timeout=20s --retries=3 --start-period=15s \
|
HEALTHCHECK --interval=30s --timeout=20s --retries=3 --start-period=15s \
|
||||||
CMD curl -fSs 127.0.0.1:3000 || exit 1
|
CMD curl -fSs 127.0.0.1:3000 || exit 1
|
||||||
|
|
||||||
CMD [ "pnpm", "run", "start:client" ]
|
CMD [ "pnpm", "run", "start", "--filter", "client" ]
|
||||||
@ -5,6 +5,8 @@ import {
|
|||||||
FilterCenterFocus,
|
FilterCenterFocus,
|
||||||
InsertPageBreak,
|
InsertPageBreak,
|
||||||
Link,
|
Link,
|
||||||
|
RedoOutlined,
|
||||||
|
UndoOutlined,
|
||||||
ViewSidebar,
|
ViewSidebar,
|
||||||
ZoomIn,
|
ZoomIn,
|
||||||
ZoomOut,
|
ZoomOut,
|
||||||
@ -16,6 +18,7 @@ import { useTranslation } from 'next-i18next';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
import { ReactZoomPanPinchRef } from 'react-zoom-pan-pinch';
|
import { ReactZoomPanPinchRef } from 'react-zoom-pan-pinch';
|
||||||
|
import { ActionCreators } from 'redux-undo';
|
||||||
|
|
||||||
import { ServerError } from '@/services/axios';
|
import { ServerError } from '@/services/axios';
|
||||||
import { printResumeAsPdf, PrintResumeAsPdfParams } from '@/services/printer';
|
import { printResumeAsPdf, PrintResumeAsPdfParams } from '@/services/printer';
|
||||||
@ -31,14 +34,18 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
|
||||||
const isDesktop = useMediaQuery(theme.breakpoints.up('sm'));
|
const isDesktop = useMediaQuery(theme.breakpoints.up('sm'));
|
||||||
const pages = useAppSelector((state) => state.resume.metadata.layout);
|
|
||||||
|
const { past, present: resume, future } = useAppSelector((state) => state.resume);
|
||||||
|
const pages = get(resume, 'metadata.layout');
|
||||||
const { left, right } = useAppSelector((state) => state.build.sidebar);
|
const { left, right } = useAppSelector((state) => state.build.sidebar);
|
||||||
const orientation = useAppSelector((state) => state.build.page.orientation);
|
const orientation = useAppSelector((state) => state.build.page.orientation);
|
||||||
|
|
||||||
const { mutateAsync, isLoading } = useMutation<string, ServerError, PrintResumeAsPdfParams>(printResumeAsPdf);
|
const { mutateAsync, isLoading } = useMutation<string, ServerError, PrintResumeAsPdfParams>(printResumeAsPdf);
|
||||||
|
|
||||||
|
const handleUndo = () => dispatch(ActionCreators.undo());
|
||||||
|
const handleRedo = () => dispatch(ActionCreators.redo());
|
||||||
|
|
||||||
const handleTogglePageBreakLine = () => dispatch(togglePageBreakLine());
|
const handleTogglePageBreakLine = () => dispatch(togglePageBreakLine());
|
||||||
|
|
||||||
const handleTogglePageOrientation = () => dispatch(togglePageOrientation());
|
const handleTogglePageOrientation = () => dispatch(togglePageOrientation());
|
||||||
@ -63,7 +70,7 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||||||
|
|
||||||
const url = await mutateAsync({ username, slug });
|
const url = await mutateAsync({ username, slug });
|
||||||
|
|
||||||
download(`/api${url}`);
|
download(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -75,6 +82,20 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className={styles.controller}>
|
<div className={styles.controller}>
|
||||||
|
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.undo')}>
|
||||||
|
<ButtonBase onClick={handleUndo} className={clsx({ 'pointer-events-none opacity-50': past.length < 2 })}>
|
||||||
|
<UndoOutlined fontSize="medium" />
|
||||||
|
</ButtonBase>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.redo')}>
|
||||||
|
<ButtonBase onClick={handleRedo} className={clsx({ 'pointer-events-none opacity-50': future.length === 0 })}>
|
||||||
|
<RedoOutlined fontSize="medium" />
|
||||||
|
</ButtonBase>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-in')}>
|
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-in')}>
|
||||||
<ButtonBase onClick={() => zoomIn(0.25)}>
|
<ButtonBase onClick={() => zoomIn(0.25)}>
|
||||||
<ZoomIn fontSize="medium" />
|
<ZoomIn fontSize="medium" />
|
||||||
@ -97,17 +118,18 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||||||
|
|
||||||
{isDesktop && (
|
{isDesktop && (
|
||||||
<>
|
<>
|
||||||
{pages.length > 1 && (
|
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-orientation')}>
|
||||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-orientation')}>
|
<ButtonBase
|
||||||
<ButtonBase onClick={handleTogglePageOrientation}>
|
onClick={handleTogglePageOrientation}
|
||||||
{orientation === 'vertical' ? (
|
className={clsx({ 'pointer-events-none opacity-50': pages.length === 1 })}
|
||||||
<AlignHorizontalCenter fontSize="medium" />
|
>
|
||||||
) : (
|
{orientation === 'vertical' ? (
|
||||||
<AlignVerticalCenter fontSize="medium" />
|
<AlignHorizontalCenter fontSize="medium" />
|
||||||
)}
|
) : (
|
||||||
</ButtonBase>
|
<AlignVerticalCenter fontSize="medium" />
|
||||||
</Tooltip>
|
)}
|
||||||
)}
|
</ButtonBase>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-page-break-line')}>
|
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-page-break-line')}>
|
||||||
<ButtonBase onClick={handleTogglePageBreakLine}>
|
<ButtonBase onClick={handleTogglePageBreakLine}>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import Page from './Page';
|
|||||||
const Center = () => {
|
const Center = () => {
|
||||||
const orientation = useAppSelector((state) => state.build.page.orientation);
|
const orientation = useAppSelector((state) => state.build.page.orientation);
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const resume = useAppSelector((state) => state.resume.present);
|
||||||
const layout: string[][][] = get(resume, 'metadata.layout');
|
const layout: string[][][] = get(resume, 'metadata.layout');
|
||||||
|
|
||||||
if (isEmpty(resume)) return null;
|
if (isEmpty(resume)) return null;
|
||||||
|
|||||||
@ -57,7 +57,7 @@ const Header = () => {
|
|||||||
|
|
||||||
const { mutateAsync: deleteMutation } = useMutation<void, ServerError, DeleteResumeParams>(deleteResume);
|
const { mutateAsync: deleteMutation } = useMutation<void, ServerError, DeleteResumeParams>(deleteResume);
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const resume = useAppSelector((state) => state.resume.present);
|
||||||
const { left, right } = useAppSelector((state) => state.build.sidebar);
|
const { left, right } = useAppSelector((state) => state.build.sidebar);
|
||||||
|
|
||||||
const name = useMemo(() => get(resume, 'name'), [resume]);
|
const name = useMemo(() => get(resume, 'name'), [resume]);
|
||||||
|
|||||||
@ -27,6 +27,13 @@
|
|||||||
@apply hidden;
|
@apply hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown {
|
||||||
|
ul {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
text-indent: -1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pageNumber {
|
.pageNumber {
|
||||||
|
|||||||
@ -20,7 +20,7 @@ type Props = PageProps & {
|
|||||||
const Page: React.FC<Props> = ({ page, showPageNumbers = false }) => {
|
const Page: React.FC<Props> = ({ page, showPageNumbers = false }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const resume = useAppSelector((state) => state.resume.present);
|
||||||
const breakLine: boolean = useAppSelector((state) => state.build.page.breakLine);
|
const breakLine: boolean = useAppSelector((state) => state.build.page.breakLine);
|
||||||
|
|
||||||
const theme: Theme = get(resume, 'metadata.theme');
|
const theme: Theme = get(resume, 'metadata.theme');
|
||||||
|
|||||||
@ -25,7 +25,7 @@ const LeftSidebar = () => {
|
|||||||
|
|
||||||
const isDesktop = useMediaQuery(theme.breakpoints.up('lg'));
|
const isDesktop = useMediaQuery(theme.breakpoints.up('lg'));
|
||||||
|
|
||||||
const sections = useAppSelector((state) => state.resume.sections);
|
const sections = useAppSelector((state) => state.resume.present.sections);
|
||||||
const { open } = useAppSelector((state) => state.build.sidebar.left);
|
const { open } = useAppSelector((state) => state.build.sidebar.left);
|
||||||
|
|
||||||
const customSections = useMemo(() => getCustomSections(sections), [sections]);
|
const customSections = useMemo(() => getCustomSections(sections), [sections]);
|
||||||
@ -65,7 +65,7 @@ const LeftSidebar = () => {
|
|||||||
variant={isDesktop ? 'persistent' : 'temporary'}
|
variant={isDesktop ? 'persistent' : 'temporary'}
|
||||||
>
|
>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<nav>
|
<nav className="overflow-y-scroll">
|
||||||
<div>
|
<div>
|
||||||
<Link href="/dashboard">
|
<Link href="/dashboard">
|
||||||
<a className="inline-flex">
|
<a className="inline-flex">
|
||||||
|
|||||||
@ -32,7 +32,7 @@ const Basics = () => {
|
|||||||
<PhotoUpload />
|
<PhotoUpload />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-2 w-full sm:col-span-2">
|
<div className="grid w-full gap-2 sm:col-span-2">
|
||||||
<ResumeInput label={t<string>('builder.leftSidebar.sections.basics.name.label')} path="basics.name" />
|
<ResumeInput label={t<string>('builder.leftSidebar.sections.basics.name.label')} path="basics.name" />
|
||||||
|
|
||||||
<Button variant="outlined" startIcon={<PhotoFilter />} onClick={handleClick}>
|
<Button variant="outlined" startIcon={<PhotoFilter />} onClick={handleClick}>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ const PhotoFilters = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const photo: Photo = useAppSelector((state) => get(state.resume, 'basics.photo'));
|
const photo: Photo = useAppSelector((state) => get(state.resume.present, 'basics.photo'));
|
||||||
const size: number = get(photo, 'filters.size', 128);
|
const size: number = get(photo, 'filters.size', 128);
|
||||||
const shape: PhotoShape = get(photo, 'filters.shape', 'square');
|
const shape: PhotoShape = get(photo, 'filters.shape', 'square');
|
||||||
const grayscale: boolean = get(photo, 'filters.grayscale', false);
|
const grayscale: boolean = get(photo, 'filters.grayscale', false);
|
||||||
|
|||||||
@ -21,8 +21,8 @@ const PhotoUpload: React.FC = () => {
|
|||||||
|
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const id: number = useAppSelector((state) => get(state.resume, 'id'));
|
const id: number = useAppSelector((state) => get(state.resume.present, 'id'));
|
||||||
const photo: Photo = useAppSelector((state) => get(state.resume, 'basics.photo'));
|
const photo: Photo = useAppSelector((state) => get(state.resume.present, 'basics.photo'));
|
||||||
|
|
||||||
const { mutateAsync: uploadMutation, isLoading } = useMutation<Resume, ServerError, UploadPhotoParams>(uploadPhoto);
|
const { mutateAsync: uploadMutation, isLoading } = useMutation<Resume, ServerError, UploadPhotoParams>(uploadPhoto);
|
||||||
|
|
||||||
|
|||||||
@ -37,8 +37,8 @@ const Section: React.FC<Props> = ({
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector<string>((state) => get(state.resume, `${path}.name`, name));
|
const heading = useAppSelector<string>((state) => get(state.resume.present, `${path}.name`, name));
|
||||||
const visibility = useAppSelector<boolean>((state) => get(state.resume, `${path}.visible`, true));
|
const visibility = useAppSelector<boolean>((state) => get(state.resume.present, `${path}.visible`, true));
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
const id = path.split('.')[1];
|
const id = path.split('.')[1];
|
||||||
|
|||||||
@ -18,7 +18,7 @@ const SectionSettings: React.FC<Props> = ({ path }) => {
|
|||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
const columns = useAppSelector<number>((state) => get(state.resume, `${path}.columns`, 2));
|
const columns = useAppSelector<number>((state) => get(state.resume.present, `${path}.columns`, 2));
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
|
|||||||
@ -43,7 +43,7 @@ const RightSidebar = () => {
|
|||||||
variant={isDesktop ? 'persistent' : 'temporary'}
|
variant={isDesktop ? 'persistent' : 'temporary'}
|
||||||
>
|
>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<nav>
|
<nav className="overflow-y-scroll">
|
||||||
<div>
|
<div>
|
||||||
<Avatar size={40} />
|
<Avatar size={40} />
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const CustomCSS = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const customCSS: CustomCSSType = useAppSelector((state) => get(state.resume, 'metadata.css', {}));
|
const customCSS: CustomCSSType = useAppSelector((state) => get(state.resume.present, 'metadata.css', {}));
|
||||||
|
|
||||||
const handleChange = (value: string | undefined) => {
|
const handleChange = (value: string | undefined) => {
|
||||||
dispatch(setResumeState({ path: 'metadata.css.value', value }));
|
dispatch(setResumeState({ path: 'metadata.css.value', value }));
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { useAppSelector } from '@/store/hooks';
|
|||||||
const Export = () => {
|
const Export = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const resume = useAppSelector((state) => state.resume.present);
|
||||||
|
|
||||||
const { mutateAsync, isLoading } = useMutation<string, ServerError, PrintResumeAsPdfParams>(printResumeAsPdf);
|
const { mutateAsync, isLoading } = useMutation<string, ServerError, PrintResumeAsPdfParams>(printResumeAsPdf);
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ const Export = () => {
|
|||||||
|
|
||||||
const url = await mutateAsync({ username, slug });
|
const url = await mutateAsync({ username, slug });
|
||||||
|
|
||||||
download(`/api${url}`);
|
download(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
|
import { DragDropContext, Draggable, DraggableLocation, Droppable, DropResult } from '@hello-pangea/dnd';
|
||||||
import { Add, Close, Restore } from '@mui/icons-material';
|
import { Add, Close, Restore } from '@mui/icons-material';
|
||||||
import { Button, IconButton, Tooltip } from '@mui/material';
|
import { Button, IconButton, Tooltip } from '@mui/material';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { DragDropContext, Draggable, DraggableLocation, Droppable, DropResult } from 'react-beautiful-dnd';
|
|
||||||
|
|
||||||
import Heading from '@/components/shared/Heading';
|
import Heading from '@/components/shared/Heading';
|
||||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||||
@ -23,8 +23,8 @@ const Layout = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const layout = useAppSelector((state) => state.resume.metadata.layout);
|
const layout = useAppSelector((state) => state.resume.present.metadata.layout);
|
||||||
const resumeSections = useAppSelector((state) => state.resume.sections);
|
const resumeSections = useAppSelector((state) => state.resume.present.sections);
|
||||||
|
|
||||||
const onDragEnd = (dropResult: DropResult) => {
|
const onDragEnd = (dropResult: DropResult) => {
|
||||||
const { source: srcLoc, destination: destLoc } = dropResult;
|
const { source: srcLoc, destination: destLoc } = dropResult;
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Button } from '@mui/material';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import Heading from '@/components/shared/Heading';
|
import Heading from '@/components/shared/Heading';
|
||||||
import { DONATION_URL, GITHUB_ISSUES_URL, GITHUB_URL } from '@/constants/index';
|
import { DOCS_URL, DONATION_URL, GITHUB_ISSUES_URL, GITHUB_URL } from '@/constants/index';
|
||||||
|
|
||||||
import styles from './Links.module.scss';
|
import styles from './Links.module.scss';
|
||||||
|
|
||||||
@ -49,6 +49,12 @@ const Links = () => {
|
|||||||
{t<string>('builder.rightSidebar.sections.links.github')}
|
{t<string>('builder.rightSidebar.sections.links.github')}
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href={DOCS_URL} target="_blank" rel="noreferrer">
|
||||||
|
<Button variant="text" startIcon={<Link />}>
|
||||||
|
{t<string>('builder.rightSidebar.sections.links.docs')}
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import dayjs from 'dayjs';
|
|||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useMemo } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
|
|
||||||
import Heading from '@/components/shared/Heading';
|
import Heading from '@/components/shared/Heading';
|
||||||
@ -36,9 +36,11 @@ const Settings = () => {
|
|||||||
|
|
||||||
const { locale, ...router } = useRouter();
|
const { locale, ...router } = useRouter();
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const [confirmReset, setConfirmReset] = useState(false);
|
||||||
|
|
||||||
|
const resume = useAppSelector((state) => state.resume.present);
|
||||||
const theme = useAppSelector((state) => state.build.theme);
|
const theme = useAppSelector((state) => state.build.theme);
|
||||||
const pages = useAppSelector((state) => state.resume.metadata.layout);
|
const pages = useAppSelector((state) => state.resume.present.metadata.layout);
|
||||||
const breakLine = useAppSelector((state) => state.build.page.breakLine);
|
const breakLine = useAppSelector((state) => state.build.page.breakLine);
|
||||||
const orientation = useAppSelector((state) => state.build.page.orientation);
|
const orientation = useAppSelector((state) => state.build.page.orientation);
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ const Settings = () => {
|
|||||||
const dateConfig: DateConfig = useMemo(() => get(resume, 'metadata.date'), [resume]);
|
const dateConfig: DateConfig = useMemo(() => get(resume, 'metadata.date'), [resume]);
|
||||||
|
|
||||||
const isDarkMode = useMemo(() => theme === 'dark', [theme]);
|
const isDarkMode = useMemo(() => theme === 'dark', [theme]);
|
||||||
const exampleString = useMemo(() => `Eg. ${dayjs().format(dateConfig.format)}`, [dateConfig.format]);
|
const exampleString = useMemo(() => `Eg. ${dayjs().utc().format(dateConfig.format)}`, [dateConfig.format]);
|
||||||
const themeString = useMemo(() => (isDarkMode ? 'Matte Black Everything' : 'As bright as your future'), [isDarkMode]);
|
const themeString = useMemo(() => (isDarkMode ? 'Matte Black Everything' : 'As bright as your future'), [isDarkMode]);
|
||||||
|
|
||||||
const { mutateAsync: loadSampleDataMutation } = useMutation<Resume, ServerError, LoadSampleDataParams>(
|
const { mutateAsync: loadSampleDataMutation } = useMutation<Resume, ServerError, LoadSampleDataParams>(
|
||||||
@ -78,9 +80,14 @@ const Settings = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleResetResume = async () => {
|
const handleResetResume = async () => {
|
||||||
await resetResumeMutation({ id });
|
if (!confirmReset) {
|
||||||
|
return setConfirmReset(true);
|
||||||
|
}
|
||||||
|
|
||||||
queryClient.invalidateQueries(`resume/${username}/${slug}`);
|
await resetResumeMutation({ id });
|
||||||
|
await queryClient.invalidateQueries(`resume/${username}/${slug}`);
|
||||||
|
|
||||||
|
setConfirmReset(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -90,7 +97,7 @@ const Settings = () => {
|
|||||||
<List sx={{ padding: 0 }}>
|
<List sx={{ padding: 0 }}>
|
||||||
{/* Global Settings */}
|
{/* Global Settings */}
|
||||||
<>
|
<>
|
||||||
<ListSubheader className="rounded">
|
<ListSubheader disableSticky className="rounded">
|
||||||
{t<string>('builder.rightSidebar.sections.settings.global.heading')}
|
{t<string>('builder.rightSidebar.sections.settings.global.heading')}
|
||||||
</ListSubheader>
|
</ListSubheader>
|
||||||
|
|
||||||
@ -148,7 +155,7 @@ const Settings = () => {
|
|||||||
|
|
||||||
{/* Page Settings */}
|
{/* Page Settings */}
|
||||||
<>
|
<>
|
||||||
<ListSubheader className="rounded">
|
<ListSubheader disableSticky className="rounded">
|
||||||
{t<string>('builder.rightSidebar.sections.settings.page.heading')}
|
{t<string>('builder.rightSidebar.sections.settings.page.heading')}
|
||||||
</ListSubheader>
|
</ListSubheader>
|
||||||
|
|
||||||
@ -180,7 +187,7 @@ const Settings = () => {
|
|||||||
|
|
||||||
{/* Resume Settings */}
|
{/* Resume Settings */}
|
||||||
<>
|
<>
|
||||||
<ListSubheader className="rounded">
|
<ListSubheader disableSticky className="rounded">
|
||||||
{t<string>('builder.rightSidebar.sections.settings.resume.heading')}
|
{t<string>('builder.rightSidebar.sections.settings.resume.heading')}
|
||||||
</ListSubheader>
|
</ListSubheader>
|
||||||
|
|
||||||
@ -202,7 +209,11 @@ const Settings = () => {
|
|||||||
<DeleteForever />
|
<DeleteForever />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={t<string>('builder.rightSidebar.sections.settings.resume.reset.primary')}
|
primary={
|
||||||
|
confirmReset
|
||||||
|
? 'Are you sure?'
|
||||||
|
: t<string>('builder.rightSidebar.sections.settings.resume.reset.primary')
|
||||||
|
}
|
||||||
secondary={t<string>('builder.rightSidebar.sections.settings.resume.reset.secondary')}
|
secondary={t<string>('builder.rightSidebar.sections.settings.resume.reset.secondary')}
|
||||||
/>
|
/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const Sharing = () => {
|
|||||||
|
|
||||||
const [showShortUrl, setShowShortUrl] = useState(false);
|
const [showShortUrl, setShowShortUrl] = useState(false);
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const resume = useAppSelector((state) => state.resume.present);
|
||||||
const isPublic = useMemo(() => get(resume, 'public'), [resume]);
|
const isPublic = useMemo(() => get(resume, 'public'), [resume]);
|
||||||
const url = useMemo(() => getResumeUrl(resume, { withHost: true }), [resume]);
|
const url = useMemo(() => getResumeUrl(resume, { withHost: true }), [resume]);
|
||||||
const shortUrl = useMemo(() => getResumeUrl(resume, { withHost: true, shortUrl: true }), [resume]);
|
const shortUrl = useMemo(() => getResumeUrl(resume, { withHost: true, shortUrl: true }), [resume]);
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const Templates = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const currentTemplate: string = useAppSelector((state) => get(state.resume, 'metadata.template'));
|
const currentTemplate: string = useAppSelector((state) => get(state.resume.present, 'metadata.template'));
|
||||||
|
|
||||||
const handleChange = (template: TemplateMeta) => {
|
const handleChange = (template: TemplateMeta) => {
|
||||||
dispatch(setResumeState({ path: 'metadata.template', value: template.id }));
|
dispatch(setResumeState({ path: 'metadata.template', value: template.id }));
|
||||||
@ -31,7 +31,7 @@ const Templates = () => {
|
|||||||
<div key={template.id} className={styles.template}>
|
<div key={template.id} className={styles.template}>
|
||||||
<div className={clsx(styles.preview, { [styles.selected]: template.id === currentTemplate })}>
|
<div className={clsx(styles.preview, { [styles.selected]: template.id === currentTemplate })}>
|
||||||
<ButtonBase onClick={() => handleChange(template)}>
|
<ButtonBase onClick={() => handleChange(template)}>
|
||||||
<Image src={template.preview} alt={template.name} className="rounded-sm" layout="fill" />
|
<Image src={template.preview} alt={template.name} className="rounded-sm" layout="fill" priority />
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
.container {
|
.container {
|
||||||
@apply grid sm:grid-cols-2 gap-4;
|
@apply grid gap-4 sm:grid-cols-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorOptions {
|
.colorOptions {
|
||||||
@apply col-span-2 mb-4;
|
@apply col-span-2 mb-4;
|
||||||
@apply grid grid-cols-8 gap-y-2 justify-items-center;
|
@apply grid grid-cols-8 justify-items-center gap-y-2;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,9 @@ const Theme = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { background, text, primary } = useAppSelector<ThemeType>((state) => get(state.resume, 'metadata.theme'));
|
const { background, text, primary } = useAppSelector<ThemeType>((state) =>
|
||||||
|
get(state.resume.present, 'metadata.theme')
|
||||||
|
);
|
||||||
|
|
||||||
const handleChange = (property: string, color: string) => {
|
const handleChange = (property: string, color: string) => {
|
||||||
dispatch(setResumeState({ path: `metadata.theme.${property}`, value: color[0] !== '#' ? `#${color}` : color }));
|
dispatch(setResumeState({ path: `metadata.theme.${property}`, value: color[0] !== '#' ? `#${color}` : color }));
|
||||||
|
|||||||
@ -33,7 +33,7 @@ const Widgets: React.FC<WidgetProps> = ({ label, category }) => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { family, size } = useAppSelector<TypographyType>((state) => get(state.resume, 'metadata.typography'));
|
const { family, size } = useAppSelector<TypographyType>((state) => get(state.resume.present, 'metadata.typography'));
|
||||||
|
|
||||||
const { data: fonts } = useQuery(FONTS_QUERY, fetchFonts, {
|
const { data: fonts } = useQuery(FONTS_QUERY, fetchFonts, {
|
||||||
select: (fonts) => fonts.sort((a, b) => a.category.localeCompare(b.category)),
|
select: (fonts) => fonts.sort((a, b) => a.category.localeCompare(b.category)),
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
.testimony {
|
.testimony {
|
||||||
@apply grid gap-2;
|
@apply grid gap-2;
|
||||||
@apply border-2 rounded p-4 dark:border-neutral-800;
|
@apply rounded border-2 p-4 dark:border-neutral-800;
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
@apply text-xs leading-normal text-justify opacity-90;
|
@apply text-justify text-xs leading-normal opacity-90;
|
||||||
}
|
}
|
||||||
|
|
||||||
figcaption {
|
figcaption {
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@apply sticky top-0 left-0 right-0 z-50 pt-6 bg-neutral-50 dark:bg-neutral-900;
|
@apply sticky top-0 left-0 right-0 z-50 bg-neutral-50 pt-6 dark:bg-neutral-900;
|
||||||
@apply flex items-center justify-between;
|
@apply flex items-center justify-between;
|
||||||
@apply w-full border-b pb-5 dark:border-white/10;
|
@apply w-full border-b pb-5 dark:border-white/10;
|
||||||
|
|
||||||
@ -33,7 +33,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
@apply sticky bottom-0 left-0 right-0 z-50 pb-6 bg-neutral-50 dark:bg-neutral-900;
|
@apply sticky bottom-0 left-0 right-0 z-50 bg-neutral-50 pb-6 dark:bg-neutral-900;
|
||||||
@apply flex items-center justify-end gap-x-4;
|
@apply flex items-center justify-end gap-x-4;
|
||||||
@apply w-full border-t pt-5 dark:border-white/10;
|
@apply w-full border-t pt-5 dark:border-white/10;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,8 +32,8 @@ const Heading: React.FC<Props> = ({
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`, name));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`, name));
|
||||||
const visibility = useAppSelector((state) => get(state.resume, `${path}.visible`, true));
|
const visibility = useAppSelector((state) => get(state.resume.present, `${path}.visible`, true));
|
||||||
|
|
||||||
const [editMode, setEditMode] = useState(false);
|
const [editMode, setEditMode] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.language {
|
.language {
|
||||||
@apply py-2 px-4 cursor-pointer text-center hover:underline;
|
@apply cursor-pointer py-2 px-4 text-center hover:underline;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ const List: React.FC<Props> = ({
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const list: Array<ListItemType> = useAppSelector((state) => get(state.resume, path, []));
|
const list: Array<ListItemType> = useAppSelector((state) => get(state.resume.present, path, []));
|
||||||
|
|
||||||
const handleEdit = (item: ListItemType) => {
|
const handleEdit = (item: ListItemType) => {
|
||||||
isFunction(onEdit) && onEdit(item);
|
isFunction(onEdit) && onEdit(item);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import styles from './Loading.module.scss';
|
|||||||
|
|
||||||
const Loading: React.FC = () => {
|
const Loading: React.FC = () => {
|
||||||
const { isReady } = useRouter();
|
const { isReady } = useRouter();
|
||||||
|
|
||||||
const isFetching = useIsFetching();
|
const isFetching = useIsFetching();
|
||||||
const isMutating = useIsMutating();
|
const isMutating = useIsMutating();
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { DatePicker } from '@mui/lab';
|
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
@ -21,7 +21,7 @@ interface Props {
|
|||||||
const ResumeInput: React.FC<Props> = ({ type = 'text', label, path, className, markdownSupported = false }) => {
|
const ResumeInput: React.FC<Props> = ({ type = 'text', label, path, className, markdownSupported = false }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const stateValue = useAppSelector((state) => get(state.resume, path, ''));
|
const stateValue = useAppSelector((state) => get(state.resume.present, path, ''));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(stateValue);
|
setValue(stateValue);
|
||||||
@ -63,7 +63,7 @@ const ResumeInput: React.FC<Props> = ({ type = 'text', label, path, className, m
|
|||||||
renderInput={(params) => <TextField {...params} error={false} className={className} />}
|
renderInput={(params) => <TextField {...params} error={false} className={className} />}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && onChangeValue('');
|
isEmpty(keyboardInputValue) && onChangeValue('');
|
||||||
date && dayjs(date).isValid() && onChangeValue(date.toISOString());
|
date && dayjs(date).utc().isValid() && onChangeValue(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,30 +6,42 @@ export type Language = {
|
|||||||
|
|
||||||
export const languages: Language[] = [
|
export const languages: Language[] = [
|
||||||
{ code: 'ar', name: 'Arabic', localName: 'اَلْعَرَبِيَّةُ' },
|
{ code: 'ar', name: 'Arabic', localName: 'اَلْعَرَبِيَّةُ' },
|
||||||
|
{ code: 'bg', name: 'Bulgarian', localName: 'български' },
|
||||||
{ code: 'bn', name: 'Bengali', localName: 'বাংলা' },
|
{ code: 'bn', name: 'Bengali', localName: 'বাংলা' },
|
||||||
|
{ code: 'ca', name: 'Catalan', localName: 'Valencian' },
|
||||||
{ code: 'cs', name: 'Czech', localName: 'čeština' },
|
{ code: 'cs', name: 'Czech', localName: 'čeština' },
|
||||||
{ code: 'da', name: 'Danish', localName: 'Dansk' },
|
{ code: 'da', name: 'Danish', localName: 'Dansk' },
|
||||||
{ code: 'de', name: 'German', localName: 'Deutsch' },
|
{ code: 'de', name: 'German', localName: 'Deutsch' },
|
||||||
{ code: 'el', name: 'Greek', localName: 'Ελληνικά' },
|
{ code: 'el', name: 'Greek', localName: 'Ελληνικά' },
|
||||||
{ code: 'en', name: 'English' },
|
{ code: 'en', name: 'English' },
|
||||||
{ code: 'es', name: 'Spanish', localName: 'Español' },
|
{ code: 'es', name: 'Spanish', localName: 'Español' },
|
||||||
|
{ code: 'fa', name: 'Persian', localName: 'فارسی' },
|
||||||
|
{ code: 'fi', name: 'Finnish', localName: 'Suomi' },
|
||||||
{ code: 'fr', name: 'French', localName: 'Français' },
|
{ code: 'fr', name: 'French', localName: 'Français' },
|
||||||
{ code: 'he', name: 'Hebrew', localName: 'Ivrit' },
|
{ code: 'he', name: 'Hebrew', localName: 'Ivrit' },
|
||||||
{ code: 'hi', name: 'Hindi', localName: 'हिन्दी' },
|
{ code: 'hi', name: 'Hindi', localName: 'हिन्दी' },
|
||||||
{ code: 'hu', name: 'Hungarian', localName: 'Magyar' },
|
{ code: 'hu', name: 'Hungarian', localName: 'Magyar' },
|
||||||
{ code: 'id', name: 'Indonesian', localName: 'Bahasa Indonesia' },
|
{ code: 'id', name: 'Indonesian', localName: 'Bahasa Indonesia' },
|
||||||
{ code: 'it', name: 'Italian', localName: 'Italiano' },
|
{ code: 'it', name: 'Italian', localName: 'Italiano' },
|
||||||
|
{ code: 'ja', name: 'Japanese', localName: '日本語' },
|
||||||
|
{ code: 'km', name: 'Khmer', localName: 'ភាសាខ្មែរ' },
|
||||||
{ code: 'kn', name: 'Kannada', localName: 'ಕನ್ನಡ' },
|
{ code: 'kn', name: 'Kannada', localName: 'ಕನ್ನಡ' },
|
||||||
|
{ code: 'ko', name: 'Korean', localName: '한국어' },
|
||||||
{ code: 'ml', name: 'Malayalam', localName: 'മലയാളം' },
|
{ code: 'ml', name: 'Malayalam', localName: 'മലയാളം' },
|
||||||
|
{ code: 'mr', name: 'Marathi', localName: 'मराठी' },
|
||||||
|
{ code: 'ne', name: 'Nepali', localName: 'नेपाली' },
|
||||||
{ code: 'nl', name: 'Dutch', localName: 'Nederlands' },
|
{ code: 'nl', name: 'Dutch', localName: 'Nederlands' },
|
||||||
|
{ code: 'no', name: 'Norwegian', localName: 'Norsk' },
|
||||||
{ code: 'or', name: 'Odia', localName: 'ଓଡ଼ିଆ' },
|
{ code: 'or', name: 'Odia', localName: 'ଓଡ଼ିଆ' },
|
||||||
{ code: 'fa', name: 'Persian', localName: 'Farsi' },
|
|
||||||
{ code: 'pl', name: 'Polish', localName: 'Polski' },
|
{ code: 'pl', name: 'Polish', localName: 'Polski' },
|
||||||
{ code: 'pt', name: 'Portuguese', localName: 'Português' },
|
{ code: 'pt', name: 'Portuguese', localName: 'Português' },
|
||||||
|
{ code: 'ro', name: 'Romanian', localName: 'limba română' },
|
||||||
{ code: 'ru', name: 'Russian', localName: 'русский' },
|
{ code: 'ru', name: 'Russian', localName: 'русский' },
|
||||||
|
{ code: 'sr', name: 'Serbian', localName: 'српски језик' },
|
||||||
{ code: 'sv', name: 'Swedish', localName: 'Svenska' },
|
{ code: 'sv', name: 'Swedish', localName: 'Svenska' },
|
||||||
{ code: 'ta', name: 'Tamil', localName: 'தமிழ்' },
|
{ code: 'ta', name: 'Tamil', localName: 'தமிழ்' },
|
||||||
{ code: 'tr', name: 'Turkish', localName: 'Türkçe' },
|
{ code: 'tr', name: 'Turkish', localName: 'Türkçe' },
|
||||||
|
{ code: 'uk', name: 'Ukranian', localName: 'Українська мова' },
|
||||||
{ code: 'vi', name: 'Vietnamese', localName: 'Tiếng Việt' },
|
{ code: 'vi', name: 'Vietnamese', localName: 'Tiếng Việt' },
|
||||||
{ code: 'zh', name: 'Chinese', localName: '中文' },
|
{ code: 'zh', name: 'Chinese', localName: '中文' },
|
||||||
].sort((a, b) => a.name.localeCompare(b.name));
|
].sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createTheme } from '@mui/material';
|
import { createTheme, ThemeOptions } from '@mui/material/styles';
|
||||||
|
|
||||||
const theme = createTheme({
|
const theme: ThemeOptions = {
|
||||||
typography: {
|
typography: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontFamily: 'Inter, sans-serif',
|
fontFamily: 'Inter, sans-serif',
|
||||||
@ -49,7 +49,7 @@ const theme = createTheme({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
export const lightTheme = createTheme({
|
export const lightTheme = createTheme({
|
||||||
...theme,
|
...theme,
|
||||||
|
|||||||
@ -9,6 +9,7 @@ export const VALID_URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}
|
|||||||
export const FILENAME_TIMESTAMP = 'DDMMYYYYHHmmss';
|
export const FILENAME_TIMESTAMP = 'DDMMYYYYHHmmss';
|
||||||
|
|
||||||
// Links
|
// Links
|
||||||
|
export const DOCS_URL = 'https://docs.rxresu.me';
|
||||||
export const DONATION_URL = 'https://paypal.me/RajaRajanA';
|
export const DONATION_URL = 'https://paypal.me/RajaRajanA';
|
||||||
export const TRANSLATE_URL = 'https://translate.rxresu.me/';
|
export const TRANSLATE_URL = 'https://translate.rxresu.me/';
|
||||||
export const DIGITALOCEAN_URL = 'https://pillai.xyz/digitalocean';
|
export const DIGITALOCEAN_URL = 'https://pillai.xyz/digitalocean';
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import env from '@beam-australia/react-env';
|
import env from '@beam-australia/react-env';
|
||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Google, Login, Visibility, VisibilityOff } from '@mui/icons-material';
|
import { Login, Visibility, VisibilityOff } from '@mui/icons-material';
|
||||||
import { Button, IconButton, InputAdornment, TextField } from '@mui/material';
|
import { Button, IconButton, InputAdornment, TextField } from '@mui/material';
|
||||||
|
import { CredentialResponse, GoogleLogin } from '@react-oauth/google';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { Trans, useTranslation } from 'next-i18next';
|
import { Trans, useTranslation } from 'next-i18next';
|
||||||
@ -17,8 +18,6 @@ import { ServerError } from '@/services/axios';
|
|||||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||||
import { setModalState } from '@/store/modal/modalSlice';
|
import { setModalState } from '@/store/modal/modalSlice';
|
||||||
|
|
||||||
declare const google: any;
|
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
identifier: string;
|
identifier: string;
|
||||||
password: string;
|
password: string;
|
||||||
@ -85,18 +84,16 @@ const LoginModal: React.FC = () => {
|
|||||||
dispatch(setModalState({ modal: 'auth.forgot', state: { open: true } }));
|
dispatch(setModalState({ modal: 'auth.forgot', state: { open: true } }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoginWithGoogle = async () => {
|
const handleLoginWithGoogle = async (response: CredentialResponse) => {
|
||||||
google.accounts.id.initialize({
|
if (response.credential) {
|
||||||
client_id: env('GOOGLE_CLIENT_ID'),
|
await loginWithGoogleMutation({ credential: response.credential }, { onError: handleLoginWithGoogleError });
|
||||||
callback: async (response: any) => {
|
|
||||||
await loginWithGoogleMutation({ credential: response.credential });
|
|
||||||
|
|
||||||
handleClose();
|
handleClose();
|
||||||
},
|
}
|
||||||
auto_select: false,
|
};
|
||||||
});
|
|
||||||
|
|
||||||
google.accounts.id.prompt();
|
const handleLoginWithGoogleError = () => {
|
||||||
|
toast("Please try logging in using email/password, or use another browser that supports Google's One Tap API.");
|
||||||
};
|
};
|
||||||
|
|
||||||
const PasswordVisibility = (): React.ReactElement => {
|
const PasswordVisibility = (): React.ReactElement => {
|
||||||
@ -120,15 +117,7 @@ const LoginModal: React.FC = () => {
|
|||||||
footerChildren={
|
footerChildren={
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
{!isEmpty(env('GOOGLE_CLIENT_ID')) && (
|
{!isEmpty(env('GOOGLE_CLIENT_ID')) && (
|
||||||
<Button
|
<GoogleLogin onSuccess={handleLoginWithGoogle} onError={handleLoginWithGoogleError} />
|
||||||
type="submit"
|
|
||||||
variant="outlined"
|
|
||||||
disabled={isLoading}
|
|
||||||
startIcon={<Google />}
|
|
||||||
onClick={handleLoginWithGoogle}
|
|
||||||
>
|
|
||||||
{t<string>('modals.auth.login.actions.google')}
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
|
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import env from '@beam-australia/react-env';
|
import env from '@beam-australia/react-env';
|
||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Google, HowToReg } from '@mui/icons-material';
|
import { HowToReg } from '@mui/icons-material';
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
|
import { CredentialResponse, GoogleLogin } from '@react-oauth/google';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { Trans, useTranslation } from 'next-i18next';
|
import { Trans, useTranslation } from 'next-i18next';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
|
|
||||||
import BaseModal from '@/components/shared/BaseModal';
|
import BaseModal from '@/components/shared/BaseModal';
|
||||||
@ -14,8 +16,6 @@ import { ServerError } from '@/services/axios';
|
|||||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||||
import { setModalState } from '@/store/modal/modalSlice';
|
import { setModalState } from '@/store/modal/modalSlice';
|
||||||
|
|
||||||
declare const google: any;
|
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
name: string;
|
name: string;
|
||||||
username: string;
|
username: string;
|
||||||
@ -79,18 +79,16 @@ const RegisterModal: React.FC = () => {
|
|||||||
dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
|
dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoginWithGoogle = async () => {
|
const handleLoginWithGoogle = async (response: CredentialResponse) => {
|
||||||
google.accounts.id.initialize({
|
if (response.credential) {
|
||||||
client_id: env('GOOGLE_CLIENT_ID'),
|
await loginWithGoogleMutation({ credential: response.credential }, { onError: handleLoginWithGoogleError });
|
||||||
callback: async (response: any) => {
|
|
||||||
await loginWithGoogleMutation({ credential: response.credential });
|
|
||||||
|
|
||||||
handleClose();
|
handleClose();
|
||||||
},
|
}
|
||||||
auto_select: false,
|
};
|
||||||
});
|
|
||||||
|
|
||||||
google.accounts.id.prompt();
|
const handleLoginWithGoogleError = () => {
|
||||||
|
toast("Please try logging in using email/password, or use another browser that supports Google's One Tap API.");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -102,15 +100,7 @@ const RegisterModal: React.FC = () => {
|
|||||||
footerChildren={
|
footerChildren={
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
{!isEmpty(env('GOOGLE_CLIENT_ID')) && (
|
{!isEmpty(env('GOOGLE_CLIENT_ID')) && (
|
||||||
<Button
|
<GoogleLogin onSuccess={handleLoginWithGoogle} onError={handleLoginWithGoogleError} />
|
||||||
type="submit"
|
|
||||||
variant="outlined"
|
|
||||||
disabled={isLoading}
|
|
||||||
startIcon={<Google />}
|
|
||||||
onClick={handleLoginWithGoogle}
|
|
||||||
>
|
|
||||||
{t<string>('modals.auth.register.actions.google')}
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
|
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||||
import DatePicker from '@mui/lab/DatePicker';
|
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import { Award, SectionPath } from '@reactive-resume/schema';
|
import { Award, SectionPath } from '@reactive-resume/schema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
@ -44,7 +44,7 @@ const AwardModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
@ -134,7 +134,7 @@ const AwardModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||||
import DatePicker from '@mui/lab/DatePicker';
|
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import { Certificate, SectionPath } from '@reactive-resume/schema';
|
import { Certificate, SectionPath } from '@reactive-resume/schema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
@ -44,7 +44,7 @@ const CertificateModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ const CertificateModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||||
import DatePicker from '@mui/lab/DatePicker';
|
|
||||||
import { Button, Slider, TextField } from '@mui/material';
|
import { Button, Slider, TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import { Custom } from '@reactive-resume/schema';
|
import { Custom } from '@reactive-resume/schema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
@ -60,7 +60,7 @@ const CustomModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal['builder.sections.custom']);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal['builder.sections.custom']);
|
||||||
|
|
||||||
const path: string = get(payload, 'path', '');
|
const path: string = get(payload, 'path', '');
|
||||||
@ -150,7 +150,7 @@ const CustomModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
@ -174,7 +174,7 @@ const CustomModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||||
import DatePicker from '@mui/lab/DatePicker';
|
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import { Education, SectionPath } from '@reactive-resume/schema';
|
import { Education, SectionPath } from '@reactive-resume/schema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
@ -57,7 +57,7 @@ const EducationModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
@ -173,7 +173,7 @@ const EducationModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
@ -197,7 +197,7 @@ const EducationModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@ -35,7 +35,7 @@ const InterestModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
|
|||||||
@ -36,7 +36,7 @@ const LanguageModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||||
import DatePicker from '@mui/lab/DatePicker';
|
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import { Project, SectionPath } from '@reactive-resume/schema';
|
import { Project, SectionPath } from '@reactive-resume/schema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
@ -53,7 +53,7 @@ const ProjectModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
@ -143,7 +143,7 @@ const ProjectModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
@ -167,7 +167,7 @@ const ProjectModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||||
import DatePicker from '@mui/lab/DatePicker';
|
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import { Publication, SectionPath } from '@reactive-resume/schema';
|
import { Publication, SectionPath } from '@reactive-resume/schema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
@ -44,7 +44,7 @@ const PublicationModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
@ -134,7 +134,7 @@ const PublicationModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@ -41,7 +41,7 @@ const ReferenceModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const SkillModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||||
import DatePicker from '@mui/lab/DatePicker';
|
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import { SectionPath, Volunteer } from '@reactive-resume/schema';
|
import { SectionPath, Volunteer } from '@reactive-resume/schema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
@ -50,7 +50,7 @@ const VolunteerModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
@ -140,7 +140,7 @@ const VolunteerModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
@ -164,7 +164,7 @@ const VolunteerModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
import { joiResolver } from '@hookform/resolvers/joi';
|
||||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||||
import DatePicker from '@mui/lab/DatePicker';
|
|
||||||
import { Button, TextField } from '@mui/material';
|
import { Button, TextField } from '@mui/material';
|
||||||
|
import { DatePicker } from '@mui/x-date-pickers';
|
||||||
import { SectionPath, WorkExperience } from '@reactive-resume/schema';
|
import { SectionPath, WorkExperience } from '@reactive-resume/schema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
@ -50,7 +50,7 @@ const WorkModal: React.FC = () => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const heading = useAppSelector((state) => get(state.resume, `${path}.name`));
|
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
|
||||||
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
const { open: isOpen, payload } = useAppSelector((state) => state.modal[`builder.${path}`]);
|
||||||
|
|
||||||
const item: FormData = get(payload, 'item', null);
|
const item: FormData = get(payload, 'item', null);
|
||||||
@ -140,7 +140,7 @@ const WorkModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
@ -164,7 +164,7 @@ const WorkModal: React.FC = () => {
|
|||||||
views={['year', 'month', 'day']}
|
views={['year', 'month', 'day']}
|
||||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||||
isEmpty(keyboardInputValue) && field.onChange('');
|
isEmpty(keyboardInputValue) && field.onChange('');
|
||||||
date && dayjs(date).isValid() && field.onChange(date.toISOString());
|
date && dayjs(date).utc().isValid() && field.onChange(dayjs(date).utc().toISOString());
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
|||||||
@ -69,7 +69,8 @@ const CreateResumeModal: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
await mutateAsync({ name, slug, public: isPublic });
|
await mutateAsync({ name, slug, public: isPublic });
|
||||||
|
|
||||||
queryClient.invalidateQueries(RESUMES_QUERY);
|
await queryClient.invalidateQueries(RESUMES_QUERY);
|
||||||
|
|
||||||
handleClose();
|
handleClose();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
|
|||||||
@ -5,7 +5,9 @@ const i18nConfig = {
|
|||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: [
|
locales: [
|
||||||
'ar',
|
'ar',
|
||||||
|
'bg',
|
||||||
'bn',
|
'bn',
|
||||||
|
'ca',
|
||||||
'cs',
|
'cs',
|
||||||
'da',
|
'da',
|
||||||
'de',
|
'de',
|
||||||
@ -13,22 +15,32 @@ const i18nConfig = {
|
|||||||
'en',
|
'en',
|
||||||
'es',
|
'es',
|
||||||
'fa',
|
'fa',
|
||||||
|
'fi',
|
||||||
'fr',
|
'fr',
|
||||||
'he',
|
'he',
|
||||||
'hi',
|
'hi',
|
||||||
'hu',
|
'hu',
|
||||||
'id',
|
'id',
|
||||||
'it',
|
'it',
|
||||||
|
'ja',
|
||||||
|
'km',
|
||||||
'kn',
|
'kn',
|
||||||
|
'ko',
|
||||||
'ml',
|
'ml',
|
||||||
|
'mr',
|
||||||
|
'ne',
|
||||||
'nl',
|
'nl',
|
||||||
|
'no',
|
||||||
'or',
|
'or',
|
||||||
'pl',
|
'pl',
|
||||||
'pt',
|
'pt',
|
||||||
|
'ro',
|
||||||
'ru',
|
'ru',
|
||||||
|
'sr',
|
||||||
'sv',
|
'sv',
|
||||||
'ta',
|
'ta',
|
||||||
'tr',
|
'tr',
|
||||||
|
'uk',
|
||||||
'vi',
|
'vi',
|
||||||
'zh',
|
'zh',
|
||||||
],
|
],
|
||||||
|
|||||||
@ -15,19 +15,6 @@ const nextConfig = {
|
|||||||
domains: ['cdn.rxresu.me', 'www.gravatar.com'],
|
domains: ['cdn.rxresu.me', 'www.gravatar.com'],
|
||||||
},
|
},
|
||||||
|
|
||||||
async rewrites() {
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
source: '/api/:path*',
|
|
||||||
destination: 'http://localhost:3100/:path*',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Hack to make Tailwind darkMode 'class' strategy with CSS Modules
|
// Hack to make Tailwind darkMode 'class' strategy with CSS Modules
|
||||||
// Ref: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomment-968368156
|
// Ref: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomment-968368156
|
||||||
webpack: (config) => {
|
webpack: (config) => {
|
||||||
|
|||||||
@ -2,81 +2,83 @@
|
|||||||
"name": "@reactive-resume/client",
|
"name": "@reactive-resume/client",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "react-env --prefix PUBLIC -- next dev",
|
"dev": "react-env --prefix PUBLIC -- next dev",
|
||||||
|
"lint": "next lint --fix",
|
||||||
"build": "next build && npm run sitemap",
|
"build": "next build && npm run sitemap",
|
||||||
"start": "react-env --prefix PUBLIC -- next start",
|
"start": "react-env --prefix PUBLIC -- next start",
|
||||||
"lint": "next lint --fix",
|
|
||||||
"sitemap": "next-sitemap --config next-sitemap.config.js"
|
"sitemap": "next-sitemap --config next-sitemap.config.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@beam-australia/react-env": "^3.1.1",
|
"@beam-australia/react-env": "^3.1.1",
|
||||||
"@date-io/dayjs": "^2.14.0",
|
"@date-io/dayjs": "^2.15.0",
|
||||||
"@emotion/css": "^11.9.0",
|
"@emotion/css": "^11.10.0",
|
||||||
"@emotion/react": "^11.9.3",
|
"@emotion/react": "^11.10.4",
|
||||||
"@emotion/styled": "^11.9.3",
|
"@emotion/styled": "^11.10.4",
|
||||||
"@hookform/resolvers": "2.9.1",
|
"@hello-pangea/dnd": "^16.0.0",
|
||||||
|
"@hookform/resolvers": "2.9.8",
|
||||||
"@monaco-editor/react": "^4.4.5",
|
"@monaco-editor/react": "^4.4.5",
|
||||||
"@mui/icons-material": "^5.8.4",
|
"@mui/icons-material": "^5.10.3",
|
||||||
"@mui/lab": "^5.0.0-alpha.86",
|
"@mui/lab": "^5.0.0-alpha.99",
|
||||||
"@mui/material": "^5.8.4",
|
"@mui/material": "^5.10.5",
|
||||||
"@mui/system": "^5.8.4",
|
"@mui/system": "^5.10.5",
|
||||||
"@mui/x-date-pickers": "5.0.0-alpha.6",
|
"@mui/x-date-pickers": "5.0.1",
|
||||||
"@next/env": "^12.1.6",
|
"@next/env": "^12.3.0",
|
||||||
"@reduxjs/toolkit": "^1.8.2",
|
"@react-oauth/google": "^0.2.6",
|
||||||
|
"@reduxjs/toolkit": "^1.8.5",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.2.1",
|
||||||
"dayjs": "^1.11.3",
|
"dayjs": "^1.11.5",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"joi": "^17.6.0",
|
"joi": "^17.6.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"md5-hex": "^4.0.0",
|
"md5-hex": "^4.0.0",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.34.0",
|
||||||
"nanoid": "^3.3.4",
|
"nanoid": "^3.3.4",
|
||||||
"next": "12.1.6",
|
"next": "12.3.0",
|
||||||
"next-i18next": "^11.0.0",
|
"next-i18next": "^12.0.1",
|
||||||
"react": "18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-colorful": "^5.6.1",
|
||||||
"react-colorful": "^5.5.1",
|
|
||||||
"react-dnd": "16.0.1",
|
"react-dnd": "16.0.1",
|
||||||
"react-dnd-html5-backend": "16.0.1",
|
"react-dnd-html5-backend": "16.0.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.32.2",
|
"react-hook-form": "^7.35.0",
|
||||||
"react-hot-toast": "2.2.0",
|
"react-hot-toast": "2.4.0",
|
||||||
"react-hotkeys-hook": "^3.4.6",
|
"react-hotkeys-hook": "^3.4.7",
|
||||||
"react-icons": "^4.4.0",
|
"react-icons": "^4.4.0",
|
||||||
"react-markdown": "^8.0.3",
|
"react-markdown": "^8.0.3",
|
||||||
"react-query": "^3.39.1",
|
"react-query": "^3.39.2",
|
||||||
"react-redux": "^8.0.2",
|
"react-redux": "^8.0.2",
|
||||||
"react-zoom-pan-pinch": "^2.1.3",
|
"react-zoom-pan-pinch": "^2.1.3",
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"redux-saga": "^1.1.3",
|
"redux-saga": "^1.2.1",
|
||||||
|
"redux-undo": "^1.0.1",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"sharp": "^0.30.6",
|
"sharp": "^0.31.0",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^9.0.0",
|
||||||
"webfontloader": "^1.6.28"
|
"webfontloader": "^1.6.28"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.18.5",
|
"@babel/core": "^7.19.1",
|
||||||
"@reactive-resume/schema": "workspace:*",
|
"@reactive-resume/schema": "workspace:*",
|
||||||
"@tailwindcss/typography": "^0.5.2",
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
|
"@tailwindcss/typography": "^0.5.7",
|
||||||
"@types/downloadjs": "^1.4.3",
|
"@types/downloadjs": "^1.4.3",
|
||||||
"@types/lodash": "^4.14.182",
|
"@types/lodash": "^4.14.185",
|
||||||
"@types/node": "18.0.0",
|
"@types/node": "^18.7.18",
|
||||||
"@types/react": "18.0.14",
|
"@types/react": "^18.0.20",
|
||||||
"@types/react-beautiful-dnd": "^13.1.2",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/react-redux": "^7.1.24",
|
"@types/react-redux": "^7.1.24",
|
||||||
"@types/tailwindcss": "^3.0.10",
|
"@types/tailwindcss": "^3.0.11",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@types/webfontloader": "^1.6.34",
|
"@types/webfontloader": "^1.6.34",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.11",
|
||||||
"csstype": "^3.1.0",
|
"csstype": "^3.1.1",
|
||||||
"eslint": "^8.18.0",
|
"eslint-config-next": "^12.3.0",
|
||||||
"eslint-config-next": "12.1.6",
|
"eslint-plugin-tailwindcss": "^3.6.1",
|
||||||
"next-sitemap": "^3.1.1",
|
"next-sitemap": "^3.1.22",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.16",
|
||||||
"prettier": "^2.7.1",
|
"sass": "^1.54.9",
|
||||||
"sass": "^1.52.3",
|
"tailwindcss": "^3.1.8",
|
||||||
"tailwindcss": "^3.1.3",
|
"typescript": "^4.8.3"
|
||||||
"typescript": "^4.7.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ const Preview: NextPage<Props> = ({ username, slug, resume: initialData }) => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const resume = useAppSelector((state) => state.resume.present);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialData && !isEmpty(initialData)) {
|
if (initialData && !isEmpty(initialData)) {
|
||||||
@ -98,7 +98,7 @@ const Preview: NextPage<Props> = ({ username, slug, resume: initialData }) => {
|
|||||||
try {
|
try {
|
||||||
const url = await mutateAsync({ username, slug });
|
const url = await mutateAsync({ username, slug });
|
||||||
|
|
||||||
download(`/api${url}`);
|
download(url);
|
||||||
} catch {
|
} catch {
|
||||||
toast.error('Something went wrong, please try again later.');
|
toast.error('Something went wrong, please try again later.');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,18 +59,20 @@ const Printer: NextPage<Props> = ({ resume: initialData, locale }) => {
|
|||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const resume = useAppSelector((state) => state.resume);
|
const resume = useAppSelector((state) => state.resume.present);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (router.locale !== locale) {
|
||||||
|
const { pathname, asPath, query } = router;
|
||||||
|
|
||||||
|
router.push({ pathname, query }, asPath, { locale });
|
||||||
|
}
|
||||||
|
}, [router, locale]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialData) dispatch(setResume(initialData));
|
if (initialData) dispatch(setResume(initialData));
|
||||||
}, [dispatch, initialData]);
|
}, [dispatch, initialData]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const { pathname, asPath, query } = router;
|
|
||||||
|
|
||||||
router.push({ pathname, query }, asPath, { locale });
|
|
||||||
}, [router, locale]);
|
|
||||||
|
|
||||||
if (!resume || isEmpty(resume)) return null;
|
if (!resume || isEmpty(resume)) return null;
|
||||||
|
|
||||||
const layout: string[][][] = get(resume, 'metadata.layout', []);
|
const layout: string[][][] = get(resume, 'metadata.layout', []);
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import '@/styles/globals.scss';
|
import '@/styles/globals.scss';
|
||||||
|
|
||||||
|
import env from '@beam-australia/react-env';
|
||||||
import DayjsAdapter from '@date-io/dayjs';
|
import DayjsAdapter from '@date-io/dayjs';
|
||||||
import { LocalizationProvider } from '@mui/lab';
|
import { LocalizationProvider } from '@mui/x-date-pickers';
|
||||||
|
import { GoogleOAuthProvider } from '@react-oauth/google';
|
||||||
import type { AppProps } from 'next/app';
|
import type { AppProps } from 'next/app';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Script from 'next/script';
|
|
||||||
import { appWithTranslation } from 'next-i18next';
|
import { appWithTranslation } from 'next-i18next';
|
||||||
import { Toaster } from 'react-hot-toast';
|
import { Toaster } from 'react-hot-toast';
|
||||||
import { QueryClientProvider } from 'react-query';
|
import { QueryClientProvider } from 'react-query';
|
||||||
@ -17,23 +18,23 @@ import queryClient from '@/services/react-query';
|
|||||||
import store, { persistor } from '@/store/index';
|
import store, { persistor } from '@/store/index';
|
||||||
import WrapperRegistry from '@/wrappers/index';
|
import WrapperRegistry from '@/wrappers/index';
|
||||||
|
|
||||||
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
|
const App = ({ Component, pageProps }: AppProps): JSX.Element => (
|
||||||
return (
|
<>
|
||||||
<>
|
<Head>
|
||||||
<Head>
|
<title>Reactive Resume</title>
|
||||||
<title>Reactive Resume</title>
|
|
||||||
|
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="Reactive Resume is a free and open source resume builder that's built to make the mundane tasks of creating, updating and sharing your resume as easy as 1, 2, 3."
|
content="Reactive Resume is a free and open source resume builder that's built to make the mundane tasks of creating, updating and sharing your resume as easy as 1, 2, 3."
|
||||||
/>
|
/>
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<ReduxProvider store={store}>
|
<ReduxProvider store={store}>
|
||||||
<LocalizationProvider dateAdapter={DayjsAdapter}>
|
<LocalizationProvider dateAdapter={DayjsAdapter}>
|
||||||
<PersistGate loading={null} persistor={persistor}>
|
<PersistGate loading={null} persistor={persistor}>
|
||||||
|
<GoogleOAuthProvider clientId={env('GOOGLE_CLIENT_ID')}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<WrapperRegistry>
|
<WrapperRegistry>
|
||||||
<Loading />
|
<Loading />
|
||||||
@ -50,13 +51,11 @@ const App: React.FC<AppProps> = ({ Component, pageProps }) => {
|
|||||||
/>
|
/>
|
||||||
</WrapperRegistry>
|
</WrapperRegistry>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</PersistGate>
|
</GoogleOAuthProvider>
|
||||||
</LocalizationProvider>
|
</PersistGate>
|
||||||
</ReduxProvider>
|
</LocalizationProvider>
|
||||||
|
</ReduxProvider>
|
||||||
<Script src="https://accounts.google.com/gsi/client" />
|
</>
|
||||||
</>
|
);
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default appWithTranslation(App);
|
export default appWithTranslation(App);
|
||||||
|
|||||||
@ -4,7 +4,9 @@ import Head from 'next/head';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
import { ActionCreators } from 'redux-undo';
|
||||||
|
|
||||||
import ResumeCard from '@/components/dashboard/ResumeCard';
|
import ResumeCard from '@/components/dashboard/ResumeCard';
|
||||||
import ResumePreview from '@/components/dashboard/ResumePreview';
|
import ResumePreview from '@/components/dashboard/ResumePreview';
|
||||||
@ -12,6 +14,7 @@ import Avatar from '@/components/shared/Avatar';
|
|||||||
import Logo from '@/components/shared/Logo';
|
import Logo from '@/components/shared/Logo';
|
||||||
import { RESUMES_QUERY } from '@/constants/index';
|
import { RESUMES_QUERY } from '@/constants/index';
|
||||||
import { fetchResumes } from '@/services/resume';
|
import { fetchResumes } from '@/services/resume';
|
||||||
|
import { useAppDispatch } from '@/store/hooks';
|
||||||
import styles from '@/styles/pages/Dashboard.module.scss';
|
import styles from '@/styles/pages/Dashboard.module.scss';
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => {
|
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => {
|
||||||
@ -25,8 +28,14 @@ export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => {
|
|||||||
const Dashboard: NextPage = () => {
|
const Dashboard: NextPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { data } = useQuery(RESUMES_QUERY, fetchResumes);
|
const { data } = useQuery(RESUMES_QUERY, fetchResumes);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(ActionCreators.clearHistory());
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
|||||||
import { setModalState } from '@/store/modal/modalSlice';
|
import { setModalState } from '@/store/modal/modalSlice';
|
||||||
import styles from '@/styles/pages/Home.module.scss';
|
import styles from '@/styles/pages/Home.module.scss';
|
||||||
|
|
||||||
import { DIGITALOCEAN_URL, DONATION_URL, GITHUB_URL } from '../constants';
|
import { DIGITALOCEAN_URL, DOCS_URL, DONATION_URL, GITHUB_URL } from '../constants';
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => {
|
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => {
|
||||||
return {
|
return {
|
||||||
@ -170,6 +170,12 @@ const Home: NextPage = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href={DOCS_URL} target="_blank" rel="noreferrer">
|
||||||
|
<Button variant="text" startIcon={<LinkIcon />}>
|
||||||
|
{t<string>('landing.links.links.docs')}
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href={DONATION_URL} target="_blank" rel="noreferrer">
|
<a href={DONATION_URL} target="_blank" rel="noreferrer">
|
||||||
<Button variant="text" startIcon={<LinkIcon />}>
|
<Button variant="text" startIcon={<LinkIcon />}>
|
||||||
{t<string>('landing.links.links.donate')}
|
{t<string>('landing.links.links.donate')}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { NextPage } from 'next';
|
import { NextPage } from 'next';
|
||||||
|
|
||||||
const PrivacyPolicy: NextPage = () => (
|
const PrivacyPolicy: NextPage = () => (
|
||||||
<div className="mx-auto my-12 prose dark:prose-invert">
|
<div className="prose mx-auto my-12 dark:prose-invert">
|
||||||
<h1>Privacy Policy</h1>
|
<h1>Privacy Policy</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { NextPage } from 'next';
|
import { NextPage } from 'next';
|
||||||
|
|
||||||
const TermsOfService: NextPage = () => (
|
const TermsOfService: NextPage = () => (
|
||||||
<div className="mx-auto my-12 prose dark:prose-invert">
|
<div className="prose mx-auto my-12 dark:prose-invert">
|
||||||
<h1>Terms of Service</h1>
|
<h1>Terms of Service</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -71,7 +71,7 @@ const Preview: NextPage<Props> = ({ shortId }) => {
|
|||||||
try {
|
try {
|
||||||
const url = await mutateAsync({ username: resume.user.username, slug: resume.slug });
|
const url = await mutateAsync({ username: resume.user.username, slug: resume.slug });
|
||||||
|
|
||||||
download(`/api${url}`);
|
download(url);
|
||||||
} catch {
|
} catch {
|
||||||
toast.error('Something went wrong, please try again later.');
|
toast.error('Something went wrong, please try again later.');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,7 +84,9 @@
|
|||||||
"toggle-page-break-line": "تبديل سطر الصفحة",
|
"toggle-page-break-line": "تبديل سطر الصفحة",
|
||||||
"toggle-sidebars": "تبديل الشريط الجانبي",
|
"toggle-sidebars": "تبديل الشريط الجانبي",
|
||||||
"zoom-in": "تكبير",
|
"zoom-in": "تكبير",
|
||||||
"zoom-out": "تصغير"
|
"zoom-out": "تصغير",
|
||||||
|
"undo": "الغاء التحميل",
|
||||||
|
"redo": "إعادة"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
@ -268,6 +270,7 @@
|
|||||||
"heading": "تبرع الى Reactive Resume"
|
"heading": "تبرع الى Reactive Resume"
|
||||||
},
|
},
|
||||||
"github": "الشفرة المصدرية",
|
"github": "الشفرة المصدرية",
|
||||||
|
"docs": "توثيق",
|
||||||
"heading": "الروابط"
|
"heading": "الروابط"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
"links": {
|
"links": {
|
||||||
"donate": "تبرّع",
|
"donate": "تبرّع",
|
||||||
"github": "الشفرة المصدرية",
|
"github": "الشفرة المصدرية",
|
||||||
|
"docs": "توثيق",
|
||||||
"privacy": "سياسة الخصوصية",
|
"privacy": "سياسة الخصوصية",
|
||||||
"service": "شروط الإستخدام"
|
"service": "شروط الإستخدام"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"login": "تسجيل الدخول",
|
"login": "تسجيل الدخول"
|
||||||
"google": "تسجيل الدخول باستخدام حساب جوجل"
|
|
||||||
},
|
},
|
||||||
"body": "يرجى إدخال اسم المستخدم وكلمة المرور المرتبطين بحسابك لتسجيل الدخول والوصول إلى السير الذاتية وإدارتها ومشاركتها.",
|
"body": "يرجى إدخال اسم المستخدم وكلمة المرور المرتبطين بحسابك لتسجيل الدخول والوصول إلى السير الذاتية وإدارتها ومشاركتها.",
|
||||||
"form": {
|
"form": {
|
||||||
|
|||||||
364
client/public/locales/bg/builder.json
Normal file
364
client/public/locales/bg/builder.json
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"actions": {
|
||||||
|
"add": "Добави нов {{token}}",
|
||||||
|
"delete": "Изтрий {{token}}",
|
||||||
|
"edit": "Редакция на {{token}}"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"heading": "Колони",
|
||||||
|
"tooltip": "Променете броя на колоните"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"date": {
|
||||||
|
"label": "Дата"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"label": "Описание"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"label": "Имейл адрес"
|
||||||
|
},
|
||||||
|
"end-date": {
|
||||||
|
"help-text": "Оставете това поле празно, ако все още е налице",
|
||||||
|
"label": "Крайна Дата"
|
||||||
|
},
|
||||||
|
"keywords": {
|
||||||
|
"label": "Ключови думи"
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"label": "Ниво"
|
||||||
|
},
|
||||||
|
"levelNum": {
|
||||||
|
"label": "Ниво (номер)"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "Име"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"label": "Телефон"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"label": "Длъжност"
|
||||||
|
},
|
||||||
|
"start-date": {
|
||||||
|
"label": "Начална дата"
|
||||||
|
},
|
||||||
|
"subtitle": {
|
||||||
|
"label": "Подзаглавие"
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"label": "Резюме"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"label": "Заглавие"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"label": "Уебсайт"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glossary": {
|
||||||
|
"page": "Страница"
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"actions": {
|
||||||
|
"delete": "Изтрии",
|
||||||
|
"duplicate": "Дубликирай",
|
||||||
|
"edit": "Редактирай"
|
||||||
|
},
|
||||||
|
"empty-text": "Този списък е празен."
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"delete-item": "Наистина ли искате да изтриете този запис? Това действие не може да бъде отменено.",
|
||||||
|
"delete-section": "Изтриване на раздел",
|
||||||
|
"rename-section": "Преименуване на раздел",
|
||||||
|
"toggle-visibility": "Видим/Невидим"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"controller": {
|
||||||
|
"tooltip": {
|
||||||
|
"center-artboard": "Централна табла",
|
||||||
|
"copy-link": "Копирай линка в резюмето",
|
||||||
|
"export-pdf": "Експорт в PDF",
|
||||||
|
"toggle-orientation": "Превключване на ориентацията на страницата",
|
||||||
|
"toggle-page-break-line": "Линия за прекъсване на страницата",
|
||||||
|
"toggle-sidebars": "Включване на страничната лента",
|
||||||
|
"zoom-in": "Увеличи",
|
||||||
|
"zoom-out": "Намали",
|
||||||
|
"undo": "Отмяна на",
|
||||||
|
"redo": "Redo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"menu": {
|
||||||
|
"delete": "Изтрии",
|
||||||
|
"duplicate": "Дубликирай",
|
||||||
|
"rename": "Преименувай",
|
||||||
|
"share-link": "Споделяне на връзка",
|
||||||
|
"tooltips": {
|
||||||
|
"delete": "Наистина ли искате да изтриете това CV? Това действие не може да бъде отменено.",
|
||||||
|
"share-link": "Трябва да промените видимостта на CV-то си на публична, за да я направите видима за другите."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"leftSidebar": {
|
||||||
|
"sections": {
|
||||||
|
"awards": {
|
||||||
|
"form": {
|
||||||
|
"awarder": {
|
||||||
|
"label": "Награждаващ"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"basics": {
|
||||||
|
"actions": {
|
||||||
|
"photo-filters": "Филтри за снимата"
|
||||||
|
},
|
||||||
|
"heading": "Основни",
|
||||||
|
"headline": {
|
||||||
|
"label": "Заглавие"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "Пълно име"
|
||||||
|
},
|
||||||
|
"birthdate": {
|
||||||
|
"label": "Дата на раждане"
|
||||||
|
},
|
||||||
|
"photo-filters": {
|
||||||
|
"effects": {
|
||||||
|
"border": {
|
||||||
|
"label": "Рамка"
|
||||||
|
},
|
||||||
|
"grayscale": {
|
||||||
|
"label": "Нива на сивото"
|
||||||
|
},
|
||||||
|
"heading": "Ефекти"
|
||||||
|
},
|
||||||
|
"shape": {
|
||||||
|
"heading": "Форма"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"heading": "Размер (в px)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"photo-upload": {
|
||||||
|
"tooltip": {
|
||||||
|
"remove": "Премахване на снимка",
|
||||||
|
"upload": "Качи снимка"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"certifications": {
|
||||||
|
"form": {
|
||||||
|
"issuer": {
|
||||||
|
"label": "Издател"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"education": {
|
||||||
|
"form": {
|
||||||
|
"area-study": {
|
||||||
|
"label": "Специалност"
|
||||||
|
},
|
||||||
|
"courses": {
|
||||||
|
"label": "Курсове"
|
||||||
|
},
|
||||||
|
"degree": {
|
||||||
|
"label": "Степен"
|
||||||
|
},
|
||||||
|
"grade": {
|
||||||
|
"label": "Клас"
|
||||||
|
},
|
||||||
|
"institution": {
|
||||||
|
"label": "Институция"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"address": {
|
||||||
|
"label": "Адрес"
|
||||||
|
},
|
||||||
|
"city": {
|
||||||
|
"label": "Град"
|
||||||
|
},
|
||||||
|
"country": {
|
||||||
|
"label": "Държава"
|
||||||
|
},
|
||||||
|
"heading": "Местоположение",
|
||||||
|
"postal-code": {
|
||||||
|
"label": "Пощенски код"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"label": "Регион"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"form": {
|
||||||
|
"network": {
|
||||||
|
"label": "Социална мрежа"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"label": "Потребителско име"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Профили",
|
||||||
|
"heading_one": "Профил"
|
||||||
|
},
|
||||||
|
"publications": {
|
||||||
|
"form": {
|
||||||
|
"publisher": {
|
||||||
|
"label": "Издател"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"references": {
|
||||||
|
"form": {
|
||||||
|
"relationship": {
|
||||||
|
"label": "Връзка"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"section": {
|
||||||
|
"heading": "Раздел"
|
||||||
|
},
|
||||||
|
"volunteer": {
|
||||||
|
"form": {
|
||||||
|
"organization": {
|
||||||
|
"label": "Организация"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rightSidebar": {
|
||||||
|
"sections": {
|
||||||
|
"css": {
|
||||||
|
"heading": "Персонализиран CSS"
|
||||||
|
},
|
||||||
|
"export": {
|
||||||
|
"heading": "Експортиране",
|
||||||
|
"json": {
|
||||||
|
"primary": "JSON",
|
||||||
|
"secondary": "Изтеглете JSON версия на вашата автобиография, която може да бъде импортирана обратно в Reactive Resume."
|
||||||
|
},
|
||||||
|
"pdf": {
|
||||||
|
"loading": {
|
||||||
|
"primary": "Генериране на PDF",
|
||||||
|
"secondary": "Моля, изчакайте, докато вашият PDF се генерира, това може да отнеме до 15 секунди."
|
||||||
|
},
|
||||||
|
"normal": {
|
||||||
|
"primary": "РDF",
|
||||||
|
"secondary": "Изтеглете PDF файл на вашата автобиография, който можете да отпечатате и изпратите до мечтаната работа. Този файл не може да бъде импортиран обратно за по-нататъшно редактиране."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"heading": "Оформление",
|
||||||
|
"tooltip": {
|
||||||
|
"reset-layout": "Рестартирай оформлението"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"bugs-features": {
|
||||||
|
"body": "Нещо ви пречи да си направите автобиография? Или имате невероятна идея, която да добавите? Повдигнете въпрос в GitHub, за да започнете.",
|
||||||
|
"button": "GitHub общност",
|
||||||
|
"heading": "Бъгове? Искания за функции?"
|
||||||
|
},
|
||||||
|
"donate": {
|
||||||
|
"body": "Ако ви е харесало да използвате Reactive Resume, моля, помислете дали да не дарите колкото можете повече за поддръжка на приложението, без реклами и безплатно завинаги.",
|
||||||
|
"button": "Почерпете ме с кафе",
|
||||||
|
"heading": "Направи дарение и подкрепи Reactive Resume"
|
||||||
|
},
|
||||||
|
"github": "Програмен код",
|
||||||
|
"docs": "Документация",
|
||||||
|
"heading": "Връзки"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"global": {
|
||||||
|
"date": {
|
||||||
|
"primary": "Дата",
|
||||||
|
"secondary": "Формат на датата, който да се използва в приложението"
|
||||||
|
},
|
||||||
|
"heading": "Глобално",
|
||||||
|
"language": {
|
||||||
|
"primary": "Език",
|
||||||
|
"secondary": "Език за показване, който да се използва в приложението"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"primary": "Тема"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Настройки",
|
||||||
|
"page": {
|
||||||
|
"break-line": {
|
||||||
|
"primary": "Линия на прекъсване",
|
||||||
|
"secondary": "Показване на линия на всички страници за обозначаване на височината на страница A4"
|
||||||
|
},
|
||||||
|
"heading": "Страница",
|
||||||
|
"orientation": {
|
||||||
|
"disabled": "Няма ефект, когато има само една страница",
|
||||||
|
"primary": "Ориентация",
|
||||||
|
"secondary": "Дали страниците да се показват хоризонтално или вертикално"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resume": {
|
||||||
|
"heading": "Възобновяване",
|
||||||
|
"reset": {
|
||||||
|
"primary": "Нулирайте всичко",
|
||||||
|
"secondary": "Направихте твърде много грешки? Щракнете тук, за да нулирате всички промени и да започнете от нулата. Внимавайте, това действие не може да бъде отменено."
|
||||||
|
},
|
||||||
|
"sample": {
|
||||||
|
"primary": "Зареждане на примерни данни",
|
||||||
|
"secondary": "Не сте сигурни откъде да започнете? Щракнете тук, за да заредите някои примерни данни и да видите как изглежда една пълна автобиография."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sharing": {
|
||||||
|
"heading": "Споделяне",
|
||||||
|
"short-url": {
|
||||||
|
"label": "Предпочитам кратък URL адрес"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"subtitle": "Позволете на всеки с връзка да види това CV",
|
||||||
|
"title": "Публичен"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"templates": {
|
||||||
|
"heading": "Шаблони"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"form": {
|
||||||
|
"background": {
|
||||||
|
"label": "Фон"
|
||||||
|
},
|
||||||
|
"primary": {
|
||||||
|
"label": "Основен"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"label": "Текст"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Тема"
|
||||||
|
},
|
||||||
|
"typography": {
|
||||||
|
"form": {
|
||||||
|
"font-family": {
|
||||||
|
"label": "Шрифтово семейство"
|
||||||
|
},
|
||||||
|
"font-size": {
|
||||||
|
"label": "Размер на шрифта"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Типография",
|
||||||
|
"widgets": {
|
||||||
|
"body": {
|
||||||
|
"label": "Тяло"
|
||||||
|
},
|
||||||
|
"headings": {
|
||||||
|
"label": "Заглавия"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
client/public/locales/bg/common.json
Normal file
29
client/public/locales/bg/common.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"avatar": {
|
||||||
|
"menu": {
|
||||||
|
"greeting": "Здравейте",
|
||||||
|
"logout": "Изход"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"credit": "Проект от <1>Амрут Пилай</1>",
|
||||||
|
"license": "От общността, за общността."
|
||||||
|
},
|
||||||
|
"markdown": {
|
||||||
|
"help-text": "Този раздел поддържа <1>markdown</1> форматиране."
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"present": "Настояще"
|
||||||
|
},
|
||||||
|
"subtitle": "Безплатен инструмент за създаване на автобиография с отворен код.",
|
||||||
|
"title": "Reactive Resume",
|
||||||
|
"toast": {
|
||||||
|
"error": {
|
||||||
|
"upload-file-size": "Моля, качвайте само файлове с размер под 2 мегабайта.",
|
||||||
|
"upload-photo-size": "Моля, качвайте само снимки под 2 мегабайта, за предпочитане квадратни."
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"resume-link-copied": "Връзката към автобиографията ви е копирана в клипборда."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
client/public/locales/bg/dashboard.json
Normal file
25
client/public/locales/bg/dashboard.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"create-resume": {
|
||||||
|
"subtitle": "Започване отначало",
|
||||||
|
"title": "Създаване на ново CV"
|
||||||
|
},
|
||||||
|
"import-external": {
|
||||||
|
"subtitle": "LinkedIn, JSON резюме, Reactive Resume",
|
||||||
|
"title": "Импортиране от външни източници"
|
||||||
|
},
|
||||||
|
"resume": {
|
||||||
|
"menu": {
|
||||||
|
"delete": "Изтрии",
|
||||||
|
"duplicate": "Дубликирай",
|
||||||
|
"open": "Отвори",
|
||||||
|
"rename": "Преименувай",
|
||||||
|
"share-link": "Споделяне на връзка",
|
||||||
|
"tooltips": {
|
||||||
|
"delete": "Наистина ли искате да изтриете това CV? Това действие не може да бъде отменено.",
|
||||||
|
"share-link": "Трябва да промените видимостта на CV-то си на публична, за да я направите видима за другите."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timestamp": "Последната промяна е преди {{timestamp}}"
|
||||||
|
},
|
||||||
|
"title": "Контролен панел"
|
||||||
|
}
|
||||||
42
client/public/locales/bg/landing.json
Normal file
42
client/public/locales/bg/landing.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"actions": {
|
||||||
|
"app": "Към приложението",
|
||||||
|
"login": "Вход",
|
||||||
|
"logout": "Изход",
|
||||||
|
"register": "Регистрация"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"heading": "Функции",
|
||||||
|
"list": {
|
||||||
|
"ads": "Без реклами.",
|
||||||
|
"export": "Експортирайте автобиографията си в JSON или PDF формат",
|
||||||
|
"free": "Безплатно завинаги",
|
||||||
|
"import": "Импортиране на данни от LinkedIn, JSON резюме",
|
||||||
|
"languages": "Достъпно на множество езици",
|
||||||
|
"more": "И още много вълнуващи функции, <1>прочетете всичко за тях тук</1>",
|
||||||
|
"tracking": "Без проследяване на потребители"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"heading": "Връзки",
|
||||||
|
"links": {
|
||||||
|
"donate": "Дарение",
|
||||||
|
"github": "Програмен код",
|
||||||
|
"docs": "Документация",
|
||||||
|
"privacy": "Политика за поверителност",
|
||||||
|
"service": "Условия на ползване"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"screenshots": {
|
||||||
|
"heading": "Екранни снимки"
|
||||||
|
},
|
||||||
|
"testimonials": {
|
||||||
|
"heading": "Препоръки",
|
||||||
|
"body": "Позитивно или негативно, ще се радвам да чуя мнението ви за Reactive Resume и какъв е вашия опитът.<br/>Ето някои от съобщенията, изпратени от потребители по целия свят.",
|
||||||
|
"contact": "Можете да се свържете с мен чрез <1>моя имейл</1> или чрез формата за контакт на <3>моя уебсайт</3> ."
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"body": "Reactive Resume е безплатен инструмент за създаване на автобиография/CV с отворен код, който е създаден, за да улесни обикновените задачи за създаване, актуализиране и споделяне на вашата автобиография като 1, 2, 3. С това приложение можете да създавате множество автобиографии, да ги споделяте директно със специалистите по подбор на персонал или приятели чрез уникална връзка, както и ги отпечатате като PDF. Всичко е безплатно, без реклами, без проследяване, без да губите целостта и поверителността на вашите данни.",
|
||||||
|
"heading": "Обобщение"
|
||||||
|
}
|
||||||
|
}
|
||||||
135
client/public/locales/bg/modals.json
Normal file
135
client/public/locales/bg/modals.json
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"forgot-password": {
|
||||||
|
"actions": {
|
||||||
|
"send-email": "Възстановяване на парола"
|
||||||
|
},
|
||||||
|
"body": "Просто въведете имейл адреса, свързан с акаунта, който искате да възстановите.",
|
||||||
|
"form": {
|
||||||
|
"email": {
|
||||||
|
"label": "Имейл адрес"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Забравена парола?",
|
||||||
|
"help-text": "Ако профилът ви съществува, ще получите линк за възстановяване на паролата."
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"actions": {
|
||||||
|
"login": "Вход"
|
||||||
|
},
|
||||||
|
"body": "Моля, въведете вашето потребителско име и парола, свързани с вашия акаунт, за да влезете и получите достъп, управлявате и споделяте вашите автобиографии.",
|
||||||
|
"form": {
|
||||||
|
"password": {
|
||||||
|
"label": "Парола"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"help-text": "Можете също да въведете своя имейл адрес",
|
||||||
|
"label": "Потребителско име"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Влезте във Вашият профил",
|
||||||
|
"recover-text": "В случай, че сте забравили паролата си, можете <1>да възстановите акаунта си</1> тук.",
|
||||||
|
"register-text": "Ако нямате такъв, можете <1>да си създадете акаунт</1> тук."
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"actions": {
|
||||||
|
"register": "Регистрация",
|
||||||
|
"google": "Регистрация с Google"
|
||||||
|
},
|
||||||
|
"body": "Моля, въведете вашата лична информация, за да създадете акаунт.",
|
||||||
|
"form": {
|
||||||
|
"confirm-password": {
|
||||||
|
"label": "Потвърждение на парола"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"label": "Имейл адрес"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "Пълно име"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"label": "Парола"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"label": "Потребителско име"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Създаване на профил",
|
||||||
|
"loginText": "Ако вече имате акаунт, можете <1>да влезете от тук</1> ."
|
||||||
|
},
|
||||||
|
"reset-password": {
|
||||||
|
"actions": {
|
||||||
|
"set-password": "Задайте нова парола"
|
||||||
|
},
|
||||||
|
"body": "Въведете нова парола за вашия акаунт.",
|
||||||
|
"form": {
|
||||||
|
"confirm-password": {
|
||||||
|
"label": "Потвърждение на парола"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"label": "Парола"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Нулиране на паролата"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"create-resume": {
|
||||||
|
"actions": {
|
||||||
|
"create-resume": "Създай CV/Резюме"
|
||||||
|
},
|
||||||
|
"body": "Започнете да създавате автобиографията си, като й дадете име. Може да е във връзка с позицията, за която кандидатствате, или просто любимата ви закуска.",
|
||||||
|
"form": {
|
||||||
|
"name": {
|
||||||
|
"label": "Име"
|
||||||
|
},
|
||||||
|
"public": {
|
||||||
|
"label": "Публично достъпна ли е?"
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"label": "Слъг"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Създаване на ново CV"
|
||||||
|
},
|
||||||
|
"import-external": {
|
||||||
|
"heading": "Импортиране от външни източници",
|
||||||
|
"json-resume": {
|
||||||
|
"actions": {
|
||||||
|
"upload-json": "Качване на JSON"
|
||||||
|
},
|
||||||
|
"body": "Ако имате готова <1>валидирана JSON автобиография</1>, можете да я използвате, за да стартирате бързо в Reactive Resume. Щракнете върху бутона по-долу и качете валиден JSON файл, за да започнете.",
|
||||||
|
"heading": "Качване на JSON Resume"
|
||||||
|
},
|
||||||
|
"linkedin": {
|
||||||
|
"actions": {
|
||||||
|
"upload-archive": "Качете ZIP архив"
|
||||||
|
},
|
||||||
|
"body": "Можете да спестите време, като експортирате данните си от LinkedIn и ги използвате за автоматично попълване на полета в Reactive Resume. Отидете в раздел <1>Поверителност на данните</1> в LinkedIn и поискайте архив на вашите данни. След като е наличен, качете ZIP файла по-долу.",
|
||||||
|
"heading": "Импортиране от LinkedIn"
|
||||||
|
},
|
||||||
|
"reactive-resume": {
|
||||||
|
"actions": {
|
||||||
|
"upload-json": "Качване на JSON",
|
||||||
|
"upload-json-v2": "Качете JSON от v2"
|
||||||
|
},
|
||||||
|
"body": "Ако имате JSON, който е бил експортиран с текущата версия на Reactive Resume, можете да го импортирате обратно тук, за да получите отново редактируема версия.",
|
||||||
|
"heading": "Импортиране от Reactive Resume"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rename-resume": {
|
||||||
|
"actions": {
|
||||||
|
"rename-resume": "Преименуване на резюмето"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"name": {
|
||||||
|
"label": "Име"
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"label": "Слъг"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Преименувайте автобиографията си"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -84,7 +84,9 @@
|
|||||||
"toggle-page-break-line": "পৃষ্ঠা বিরতি লাইন টগল করুন",
|
"toggle-page-break-line": "পৃষ্ঠা বিরতি লাইন টগল করুন",
|
||||||
"toggle-sidebars": "সাইডবার টগল করুন",
|
"toggle-sidebars": "সাইডবার টগল করুন",
|
||||||
"zoom-in": "বড় কর",
|
"zoom-in": "বড় কর",
|
||||||
"zoom-out": "ছোট করা"
|
"zoom-out": "ছোট করা",
|
||||||
|
"undo": "পূর্বাবস্থায় ফেরান",
|
||||||
|
"redo": "আবার করুন"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
@ -268,6 +270,7 @@
|
|||||||
"heading": "Reactive Resume -তে দান করুন"
|
"heading": "Reactive Resume -তে দান করুন"
|
||||||
},
|
},
|
||||||
"github": "সোর্স কোড",
|
"github": "সোর্স কোড",
|
||||||
|
"docs": "ডকুমেন্টেশন",
|
||||||
"heading": "লিঙ্ক"
|
"heading": "লিঙ্ক"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
"links": {
|
"links": {
|
||||||
"donate": "দান করুন",
|
"donate": "দান করুন",
|
||||||
"github": "সোর্স কোড",
|
"github": "সোর্স কোড",
|
||||||
|
"docs": "ডকুমেন্টেশন",
|
||||||
"privacy": "গোপনীয়তা নীতি",
|
"privacy": "গোপনীয়তা নীতি",
|
||||||
"service": "সেবা পাবার শর্ত"
|
"service": "সেবা পাবার শর্ত"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"login": "লগইন",
|
"login": "লগইন"
|
||||||
"google": "গুগল দিয়ে লগইন করুন"
|
|
||||||
},
|
},
|
||||||
"body": "লগইন এবং অ্যাক্সেস, পরিচালনা এবং আপনার জীবনবৃত্তান্ত শেয়ার করতে আপনার অ্যাকাউন্টের সাথে যুক্ত আপনার ব্যবহারকারীর নাম এবং পাসওয়ার্ড লিখুন।",
|
"body": "লগইন এবং অ্যাক্সেস, পরিচালনা এবং আপনার জীবনবৃত্তান্ত শেয়ার করতে আপনার অ্যাকাউন্টের সাথে যুক্ত আপনার ব্যবহারকারীর নাম এবং পাসওয়ার্ড লিখুন।",
|
||||||
"form": {
|
"form": {
|
||||||
|
|||||||
364
client/public/locales/ca/builder.json
Normal file
364
client/public/locales/ca/builder.json
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"actions": {
|
||||||
|
"add": "Afegeix nou {{token}}",
|
||||||
|
"delete": "Suprimeix {{token}}",
|
||||||
|
"edit": "Edita {{token}}"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"heading": "Columnes",
|
||||||
|
"tooltip": "Canvia el nombre de columnes"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"date": {
|
||||||
|
"label": "Data"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"label": "Descripció"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"label": "Correu electrònic"
|
||||||
|
},
|
||||||
|
"end-date": {
|
||||||
|
"help-text": "Deixeu aquest camp en blanc, si encara està present",
|
||||||
|
"label": "Data de finalització"
|
||||||
|
},
|
||||||
|
"keywords": {
|
||||||
|
"label": "Paraules clau"
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"label": "Nivell"
|
||||||
|
},
|
||||||
|
"levelNum": {
|
||||||
|
"label": "Nivell (nombre)"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "Nom"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"label": "Número de telèfon"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"label": "Posició"
|
||||||
|
},
|
||||||
|
"start-date": {
|
||||||
|
"label": "Data d'inici"
|
||||||
|
},
|
||||||
|
"subtitle": {
|
||||||
|
"label": "Subtítol"
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"label": "Resum"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"label": "Títol"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"label": "Pàgina web"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glossary": {
|
||||||
|
"page": "Pàgina"
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"actions": {
|
||||||
|
"delete": "Elimina",
|
||||||
|
"duplicate": "Duplica",
|
||||||
|
"edit": "Edita"
|
||||||
|
},
|
||||||
|
"empty-text": "Aquesta llista està buida."
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"delete-item": "Estàs segur que vols esborrar aquest element? Aquesta acció és irreversible.",
|
||||||
|
"delete-section": "Suprimeix una secció",
|
||||||
|
"rename-section": "Canvia el nom de la secció",
|
||||||
|
"toggle-visibility": "Commuta la visibilitat"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"controller": {
|
||||||
|
"tooltip": {
|
||||||
|
"center-artboard": "Centrar tauler de dibuix",
|
||||||
|
"copy-link": "Copia l'enllaç al currículum",
|
||||||
|
"export-pdf": "Exporta com a PDF",
|
||||||
|
"toggle-orientation": "Commuta l'orientació de la pàgina",
|
||||||
|
"toggle-page-break-line": "Commuta la línia de salt de pàgina",
|
||||||
|
"toggle-sidebars": "Mostra o oculta la barra lateral",
|
||||||
|
"zoom-in": "Amplia",
|
||||||
|
"zoom-out": "Allunya",
|
||||||
|
"undo": "Desfer",
|
||||||
|
"redo": "Refer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"menu": {
|
||||||
|
"delete": "Elimina",
|
||||||
|
"duplicate": "Duplica",
|
||||||
|
"rename": "Canvia el nom",
|
||||||
|
"share-link": "Comparteix l'enllaç",
|
||||||
|
"tooltips": {
|
||||||
|
"delete": "Estàs segur que vols esborrar aquest currículum? Aquesta acció és irreversible.",
|
||||||
|
"share-link": "Heu de canviar la visibilitat del vostre currículum a pública per fer-lo visible per als altres."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"leftSidebar": {
|
||||||
|
"sections": {
|
||||||
|
"awards": {
|
||||||
|
"form": {
|
||||||
|
"awarder": {
|
||||||
|
"label": "Otorgat per"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"basics": {
|
||||||
|
"actions": {
|
||||||
|
"photo-filters": "Filtres fotogràfics"
|
||||||
|
},
|
||||||
|
"heading": "Bàsics",
|
||||||
|
"headline": {
|
||||||
|
"label": "Títol"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "Nom complet"
|
||||||
|
},
|
||||||
|
"birthdate": {
|
||||||
|
"label": "Data de naixement"
|
||||||
|
},
|
||||||
|
"photo-filters": {
|
||||||
|
"effects": {
|
||||||
|
"border": {
|
||||||
|
"label": "Vora"
|
||||||
|
},
|
||||||
|
"grayscale": {
|
||||||
|
"label": "Escala de grisos"
|
||||||
|
},
|
||||||
|
"heading": "Efectes"
|
||||||
|
},
|
||||||
|
"shape": {
|
||||||
|
"heading": "Forma"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"heading": "Mida (en px)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"photo-upload": {
|
||||||
|
"tooltip": {
|
||||||
|
"remove": "Esborrar foto",
|
||||||
|
"upload": "Pugeu la foto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"certifications": {
|
||||||
|
"form": {
|
||||||
|
"issuer": {
|
||||||
|
"label": "Emissor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"education": {
|
||||||
|
"form": {
|
||||||
|
"area-study": {
|
||||||
|
"label": "Àrea d'estudi"
|
||||||
|
},
|
||||||
|
"courses": {
|
||||||
|
"label": "Cursos"
|
||||||
|
},
|
||||||
|
"degree": {
|
||||||
|
"label": "Graus"
|
||||||
|
},
|
||||||
|
"grade": {
|
||||||
|
"label": "Qualificació"
|
||||||
|
},
|
||||||
|
"institution": {
|
||||||
|
"label": "Institució"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"address": {
|
||||||
|
"label": "Adreça"
|
||||||
|
},
|
||||||
|
"city": {
|
||||||
|
"label": "Ciutat"
|
||||||
|
},
|
||||||
|
"country": {
|
||||||
|
"label": "País"
|
||||||
|
},
|
||||||
|
"heading": "Ubicació",
|
||||||
|
"postal-code": {
|
||||||
|
"label": "Codi Postal"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"label": "Regió"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"form": {
|
||||||
|
"network": {
|
||||||
|
"label": "Xarxa"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"label": "Nom d'usuari"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Perfils",
|
||||||
|
"heading_one": "Perfil"
|
||||||
|
},
|
||||||
|
"publications": {
|
||||||
|
"form": {
|
||||||
|
"publisher": {
|
||||||
|
"label": "Editor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"references": {
|
||||||
|
"form": {
|
||||||
|
"relationship": {
|
||||||
|
"label": "Relació"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"section": {
|
||||||
|
"heading": "Secció"
|
||||||
|
},
|
||||||
|
"volunteer": {
|
||||||
|
"form": {
|
||||||
|
"organization": {
|
||||||
|
"label": "Organització"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rightSidebar": {
|
||||||
|
"sections": {
|
||||||
|
"css": {
|
||||||
|
"heading": "CSS personalitzat"
|
||||||
|
},
|
||||||
|
"export": {
|
||||||
|
"heading": "Exporta",
|
||||||
|
"json": {
|
||||||
|
"primary": "JSON",
|
||||||
|
"secondary": "Baixeu una versió JSON del vostre currículum que es pot tornar a importar a Reactive Resume."
|
||||||
|
},
|
||||||
|
"pdf": {
|
||||||
|
"loading": {
|
||||||
|
"primary": "Generació de PDF",
|
||||||
|
"secondary": "Si us plau, espereu mentre es generi el vostre PDF; això pot trigar fins a 15 segons."
|
||||||
|
},
|
||||||
|
"normal": {
|
||||||
|
"primary": "PDF",
|
||||||
|
"secondary": "Descarrega un PDF del teu currículum que pots imprimir i enviar a la feina dels teus somnis. Aquest fitxer no es pot tornar a importar per editar-lo més."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"heading": "Disseny",
|
||||||
|
"tooltip": {
|
||||||
|
"reset-layout": "Restableix el disseny"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"bugs-features": {
|
||||||
|
"body": "Alguna cosa t'impedeix fer un currículum? O tens una idea sorprenent per afegir? Planteja un problema a GitHub per començar.",
|
||||||
|
"button": "Problemes de GitHub",
|
||||||
|
"heading": "Errors? Sol·licituds de funcions?"
|
||||||
|
},
|
||||||
|
"donate": {
|
||||||
|
"body": "Si us ha agradat utilitzar Reactive Resume, considereu donar tant com pugueu per mantenir l'aplicació en funcionament, sense anuncis i gratuïta per sempre.",
|
||||||
|
"button": "Compra'm un cafè",
|
||||||
|
"heading": "Dona a Reactive Curriculum vitae"
|
||||||
|
},
|
||||||
|
"github": "Codi font",
|
||||||
|
"docs": "Documentació",
|
||||||
|
"heading": "Enllaços"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"global": {
|
||||||
|
"date": {
|
||||||
|
"primary": "Data",
|
||||||
|
"secondary": "Format de data per utilitzar a l'aplicació"
|
||||||
|
},
|
||||||
|
"heading": "Global",
|
||||||
|
"language": {
|
||||||
|
"primary": "Llenguatge",
|
||||||
|
"secondary": "Mostra l'idioma per utilitzar a l'aplicació"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"primary": "Tema"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Configuració",
|
||||||
|
"page": {
|
||||||
|
"break-line": {
|
||||||
|
"primary": "Línia de trencament",
|
||||||
|
"secondary": "Mostra una línia a totes les pàgines per marcar l'alçada d'una pàgina A4"
|
||||||
|
},
|
||||||
|
"heading": "Pàgina",
|
||||||
|
"orientation": {
|
||||||
|
"disabled": "No té efecte quan només hi ha una pàgina",
|
||||||
|
"primary": "Orientació",
|
||||||
|
"secondary": "Si voleu mostrar les pàgines horitzontalment o verticalment"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resume": {
|
||||||
|
"heading": "Resum",
|
||||||
|
"reset": {
|
||||||
|
"primary": "Restablir-ho tot",
|
||||||
|
"secondary": "S'ha equivocat massa? Feu clic aquí per restablir tots els canvis i començar des de zero. Aneu amb compte, aquesta acció no es pot revertir."
|
||||||
|
},
|
||||||
|
"sample": {
|
||||||
|
"primary": "Carregueu dades de mostra",
|
||||||
|
"secondary": "No saps per on començar? Feu clic aquí per carregar algunes dades de mostra per veure com es veu un currículum complet."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sharing": {
|
||||||
|
"heading": "Compartint",
|
||||||
|
"short-url": {
|
||||||
|
"label": "Prefereix URL curt"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"subtitle": "Permet que qualsevol persona amb un enllaç vegi el teu currículum",
|
||||||
|
"title": "Públic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"templates": {
|
||||||
|
"heading": "Plantilles"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"form": {
|
||||||
|
"background": {
|
||||||
|
"label": "Fons"
|
||||||
|
},
|
||||||
|
"primary": {
|
||||||
|
"label": "primària"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"label": "Text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Tema"
|
||||||
|
},
|
||||||
|
"typography": {
|
||||||
|
"form": {
|
||||||
|
"font-family": {
|
||||||
|
"label": "Família de lletres"
|
||||||
|
},
|
||||||
|
"font-size": {
|
||||||
|
"label": "Mida de la font"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Tipografia",
|
||||||
|
"widgets": {
|
||||||
|
"body": {
|
||||||
|
"label": "Cos"
|
||||||
|
},
|
||||||
|
"headings": {
|
||||||
|
"label": "Encapçalaments"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
client/public/locales/ca/common.json
Normal file
29
client/public/locales/ca/common.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"avatar": {
|
||||||
|
"menu": {
|
||||||
|
"greeting": "Hola",
|
||||||
|
"logout": "Tancar sessió"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"credit": "Un projecte de passió d'<1>Amruth Pillai</1>",
|
||||||
|
"license": "Per la comunitat, per a la comunitat."
|
||||||
|
},
|
||||||
|
"markdown": {
|
||||||
|
"help-text": "Aquesta secció admet <1>reducció</1> formatació."
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"present": "Present"
|
||||||
|
},
|
||||||
|
"subtitle": "Un creador de currículums gratuït i de codi obert.",
|
||||||
|
"title": "Currículum reactiu",
|
||||||
|
"toast": {
|
||||||
|
"error": {
|
||||||
|
"upload-file-size": "Pengeu només fitxers de menys de 2 megabytes.",
|
||||||
|
"upload-photo-size": "Pengeu només fotos de menys de 2 megabytes, preferiblement quadrades."
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"resume-link-copied": "S'ha copiat un enllaç al vostre currículum al porta-retalls."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
client/public/locales/ca/dashboard.json
Normal file
25
client/public/locales/ca/dashboard.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"create-resume": {
|
||||||
|
"subtitle": "Començar de zero",
|
||||||
|
"title": "Crea un currículum nou"
|
||||||
|
},
|
||||||
|
"import-external": {
|
||||||
|
"subtitle": "LinkedIn, currículum JSON, currículum reactiu",
|
||||||
|
"title": "Importa des de fonts externes"
|
||||||
|
},
|
||||||
|
"resume": {
|
||||||
|
"menu": {
|
||||||
|
"delete": "Suprimeix",
|
||||||
|
"duplicate": "Duplicat",
|
||||||
|
"open": "Obert",
|
||||||
|
"rename": "Canvia el nom",
|
||||||
|
"share-link": "Compartir Enllaç",
|
||||||
|
"tooltips": {
|
||||||
|
"delete": "Esteu segur que voleu suprimir aquest currículum? Aquesta és una acció irreversible.",
|
||||||
|
"share-link": "Heu de canviar la visibilitat del vostre currículum a pública per fer-lo visible per als altres."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timestamp": "Última actualització fa {{timestamp}}"
|
||||||
|
},
|
||||||
|
"title": "panell"
|
||||||
|
}
|
||||||
42
client/public/locales/ca/landing.json
Normal file
42
client/public/locales/ca/landing.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"actions": {
|
||||||
|
"app": "Vés a l'aplicació",
|
||||||
|
"login": "iniciar Sessió",
|
||||||
|
"logout": "Tancar sessió",
|
||||||
|
"register": "Registra't"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"heading": "Característiques",
|
||||||
|
"list": {
|
||||||
|
"ads": "No hi ha publicitat",
|
||||||
|
"export": "Exporteu el vostre currículum a format JSON o PDF",
|
||||||
|
"free": "Lliure, per sempre",
|
||||||
|
"import": "Importeu dades de LinkedIn, Currículum JSON",
|
||||||
|
"languages": "Accessible en diversos idiomes",
|
||||||
|
"more": "I moltes més funcions interessants, <1>llegiu-ne tot aquí</1>",
|
||||||
|
"tracking": "Sense seguiment d'usuaris"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"heading": "Enllaços",
|
||||||
|
"links": {
|
||||||
|
"donate": "Donar",
|
||||||
|
"github": "Codi font",
|
||||||
|
"docs": "Documentació",
|
||||||
|
"privacy": "Política de privacitat",
|
||||||
|
"service": "Termes del servei"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"screenshots": {
|
||||||
|
"heading": "Captures de pantalla"
|
||||||
|
},
|
||||||
|
"testimonials": {
|
||||||
|
"heading": "Testimonis",
|
||||||
|
"body": "Bo o dolent, m'encantaria saber la teva opinió sobre Reactive Curriculum vitae i com ha estat l'experiència per a tu.<br/>Aquests són alguns dels missatges enviats pels usuaris de tot el món.",
|
||||||
|
"contact": "Podeu posar-vos en contacte amb mi mitjançant <1>el meu correu electrònic</1> o mitjançant el formulari de contacte al <3>el meu lloc web</3> ."
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"body": "Reactive Resume és un creador de currículums gratuït i de codi obert que s'ha creat per fer que les tasques mundanes de crear, actualitzar i compartir el vostre currículum siguin tan fàcils com 1, 2, 3. Amb aquesta aplicació, podeu crear diversos currículums, compartir-los amb reclutadors o amics. mitjançant un enllaç únic i imprimiu-lo com a PDF, tot de franc, sense anuncis, sense seguiment, sense perdre la integritat i la privadesa de les vostres dades.",
|
||||||
|
"heading": "Resum"
|
||||||
|
}
|
||||||
|
}
|
||||||
135
client/public/locales/ca/modals.json
Normal file
135
client/public/locales/ca/modals.json
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"forgot-password": {
|
||||||
|
"actions": {
|
||||||
|
"send-email": "Envieu un correu electrònic de restabliment de la contrasenya"
|
||||||
|
},
|
||||||
|
"body": "Només cal que introduïu l'adreça de correu electrònic associada al compte que voleu recuperar.",
|
||||||
|
"form": {
|
||||||
|
"email": {
|
||||||
|
"label": "Correu electrònic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Has oblidat la teva contrasenya?",
|
||||||
|
"help-text": "Si el compte existeix, rebràs un correu electrònic amb un enllaç per restablir la teva contrasenya."
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"actions": {
|
||||||
|
"login": "iniciar Sessió"
|
||||||
|
},
|
||||||
|
"body": "Introduïu el vostre nom d'usuari i contrasenya associats al vostre compte per iniciar sessió i accedir, gestionar i compartir els vostres currículums.",
|
||||||
|
"form": {
|
||||||
|
"password": {
|
||||||
|
"label": "Contrasenya"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"help-text": "També podeu introduir la vostra adreça de correu electrònic",
|
||||||
|
"label": "Nom d'usuari"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Inicieu sessió al vostre compte",
|
||||||
|
"recover-text": "En cas que hagis oblidat la teva contrasenya, pots <1>recuperar el teu compte</1> aquí.",
|
||||||
|
"register-text": "Si no en teniu cap, podeu <1>crear un compte</1> aquí."
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"actions": {
|
||||||
|
"register": "Registra't",
|
||||||
|
"google": "Registra't a Google"
|
||||||
|
},
|
||||||
|
"body": "Introduïu la vostra informació personal per crear un compte.",
|
||||||
|
"form": {
|
||||||
|
"confirm-password": {
|
||||||
|
"label": "Confirma la contrassenya"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"label": "Correu electrònic"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"label": "Nom complet"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"label": "Contrasenya"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"label": "Nom d'usuari"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Crear un compte",
|
||||||
|
"loginText": "Si ja teniu un compte, podeu <1>iniciar sessió aquí</1> ."
|
||||||
|
},
|
||||||
|
"reset-password": {
|
||||||
|
"actions": {
|
||||||
|
"set-password": "Estableix una contrasenya nova"
|
||||||
|
},
|
||||||
|
"body": "Introduïu una contrasenya nova per al vostre compte.",
|
||||||
|
"form": {
|
||||||
|
"confirm-password": {
|
||||||
|
"label": "Confirma la contrassenya"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"label": "Contrasenya"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Restablir la contrasenya"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"create-resume": {
|
||||||
|
"actions": {
|
||||||
|
"create-resume": "Crea un currículum"
|
||||||
|
},
|
||||||
|
"body": "Comenceu a crear el vostre currículum posant-li un nom. Pot ser en referència a la funció que esteu sol·licitant, o només al vostre berenar preferit.",
|
||||||
|
"form": {
|
||||||
|
"name": {
|
||||||
|
"label": "Nom"
|
||||||
|
},
|
||||||
|
"public": {
|
||||||
|
"label": "És accessible públicament?"
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"label": "Llimac"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Crea un currículum nou"
|
||||||
|
},
|
||||||
|
"import-external": {
|
||||||
|
"heading": "Importa des de fonts externes",
|
||||||
|
"json-resume": {
|
||||||
|
"actions": {
|
||||||
|
"upload-json": "Carregueu JSON"
|
||||||
|
},
|
||||||
|
"body": "Si teniu un currículum JSON <1>validat</1> a punt per començar, podeu utilitzar-lo per accelerar el vostre desenvolupament a Reactive Resume. Feu clic al botó següent i carregueu un fitxer JSON vàlid per començar.",
|
||||||
|
"heading": "Importa des del currículum JSON"
|
||||||
|
},
|
||||||
|
"linkedin": {
|
||||||
|
"actions": {
|
||||||
|
"upload-archive": "Carregueu l'arxiu ZIP"
|
||||||
|
},
|
||||||
|
"body": "Podeu estalviar temps exportant les vostres dades de LinkedIn i utilitzant-les per omplir automàticament els camps de Reactive Resume. Aneu a <1>Privadesa de dades</1> secció a LinkedIn i sol·liciteu un arxiu de les vostres dades. Un cop estigui disponible, carregueu el fitxer ZIP a continuació.",
|
||||||
|
"heading": "Importa des de LinkedIn"
|
||||||
|
},
|
||||||
|
"reactive-resume": {
|
||||||
|
"actions": {
|
||||||
|
"upload-json": "Carregueu JSON",
|
||||||
|
"upload-json-v2": "Carregueu JSON des de la v2"
|
||||||
|
},
|
||||||
|
"body": "Si teniu un JSON que s'ha exportat amb la versió actual de Reactive Resume, podeu tornar-lo a importar aquí per tornar a obtenir una versió editable.",
|
||||||
|
"heading": "Importa des del currículum reactiu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rename-resume": {
|
||||||
|
"actions": {
|
||||||
|
"rename-resume": "Canvia el nom del currículum"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"name": {
|
||||||
|
"label": "Nom"
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"label": "Llimac"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heading": "Canvieu el nom del vostre currículum"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -84,7 +84,9 @@
|
|||||||
"toggle-page-break-line": "Přepnout řádek zalomení stránky",
|
"toggle-page-break-line": "Přepnout řádek zalomení stránky",
|
||||||
"toggle-sidebars": "Přepnout boční panely",
|
"toggle-sidebars": "Přepnout boční panely",
|
||||||
"zoom-in": "Přiblížit",
|
"zoom-in": "Přiblížit",
|
||||||
"zoom-out": "Oddálit"
|
"zoom-out": "Oddálit",
|
||||||
|
"undo": "Zrušit",
|
||||||
|
"redo": "Přepracovat"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
@ -268,6 +270,7 @@
|
|||||||
"heading": "Přispějte na Reactive Resume"
|
"heading": "Přispějte na Reactive Resume"
|
||||||
},
|
},
|
||||||
"github": "Zdrojový kód",
|
"github": "Zdrojový kód",
|
||||||
|
"docs": "Dokumentace",
|
||||||
"heading": "Odkazy"
|
"heading": "Odkazy"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
"links": {
|
"links": {
|
||||||
"donate": "Darovat",
|
"donate": "Darovat",
|
||||||
"github": "Zdrojový kód",
|
"github": "Zdrojový kód",
|
||||||
|
"docs": "Dokumentace",
|
||||||
"privacy": "Zásady ochrany osobních údajů",
|
"privacy": "Zásady ochrany osobních údajů",
|
||||||
"service": "Podmínky služby"
|
"service": "Podmínky služby"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,7 @@
|
|||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"login": "Přihlásit se",
|
"login": "Přihlásit se"
|
||||||
"google": "Přihlaste se pomocí Google"
|
|
||||||
},
|
},
|
||||||
"body": "Zadejte prosím své uživatelské jméno a heslo spojené s vaším účtem, abyste se mohli přihlásit a získat přístup, spravovat a sdílet své životopisy.",
|
"body": "Zadejte prosím své uživatelské jméno a heslo spojené s vaším účtem, abyste se mohli přihlásit a získat přístup, spravovat a sdílet své životopisy.",
|
||||||
"form": {
|
"form": {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user