mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-10 12:32:28 +10:00
Compare commits
216 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| a037a091e7 | |||
| f3a4c17cb4 | |||
| f06f7ad2e5 | |||
| aab2e5c8a9 | |||
| 4318dbe762 | |||
| ae3ff274ee | |||
| 164403c495 | |||
| 8595c92fb7 | |||
| 8f75f32f88 | |||
| 0d44189a5f | |||
| cd16a6d360 | |||
| 7b795bfaa4 | |||
| 8f78d47661 | |||
| 0b5e5a2ece | |||
| 9eade9514c | |||
| d744e06e96 | |||
| 9657c199d2 | |||
| 2dbe737b73 | |||
| f624699efa | |||
| e46f473754 | |||
| 767f4bf4bc | |||
| 1c5d025c15 | |||
| 8de8d89290 | |||
| 83662122a5 | |||
| 126482a760 | |||
| b04c22a27b | |||
| 63f88a3d1c | |||
| bd519db14f | |||
| a49aa42176 | |||
| 1a382db4d9 | |||
| c68f75dc8c | |||
| c12de0c013 | |||
| 4cafaf306a | |||
| 0238cf18a5 | |||
| 2f6072a7ba | |||
| 55dd2c5925 | |||
| a3e25f87fa | |||
| 9e82ea11c3 | |||
| 62fd63e41f | |||
| b91c175352 | |||
| 898e2314fc | |||
| bca2aa2fe5 | |||
| 427fdb717a | |||
| ee5b0187e2 | |||
| 94d05f33b4 | |||
| 35fe4e2774 | |||
| 317901a4d2 | |||
| 350ffcbc43 | |||
| 2c074a96c8 | |||
| 79f140b2d0 | |||
| 649c655ad5 | |||
| d5284a90d1 | |||
| bd18c53ab8 | |||
| 704c1ab7d4 | |||
| 1dbd7f221e | |||
| e1a47ffbe2 | |||
| 2add629970 | |||
| a48fcd9c97 | |||
| df7b00cb2c | |||
| 27fc939101 | |||
| 7c574d17e4 | |||
| 86a105f5a5 | |||
| 327bcc2b32 | |||
| a6cbd85010 | |||
| 371b820923 | |||
| 1d47fd0267 | |||
| 276fc95bb0 | |||
| 34c8861321 | |||
| 780b782579 | |||
| 9daa99fd5b | |||
| 76b3aa29cf | |||
| 25d4913fab | |||
| 0efeff3a4f | |||
| f56089925e | |||
| 5afae08f20 | |||
| 4bf114dfd6 | |||
| 23a3c2e624 | |||
| 71862f4354 | |||
| 6861c0f0fa | |||
| 9a18e74b90 | |||
| 4dd1b70079 | |||
| f9580fe716 | |||
| 3545f7939f | |||
| 9caad3bc0b | |||
| 5bdb92b1cf | |||
| 87d381fe8e | |||
| ccfb4d3cb0 | |||
| 763074a86c | |||
| 0f46895711 | |||
| aa736af0f5 | |||
| 1d9056f935 | |||
| 9cadd603f3 | |||
| b7b62d7bd0 | |||
| 820e6c90d3 | |||
| ea642d1b60 | |||
| ec006779a8 | |||
| 515be23c44 | |||
| c11aec8b44 | |||
| 3c2147e72c | |||
| 15a35e6243 | |||
| d53a5a492c | |||
| 0810e5ae6a | |||
| 881b183db5 | |||
| 15cea02872 | |||
| c195561df0 | |||
| fc725cfc0c | |||
| 9f54516e8c | |||
| 68a4cd9635 | |||
| ff01802f2f | |||
| bb900bc2e1 | |||
| 459f82b66b | |||
| 4b382243e4 | |||
| af074085d1 | |||
| 0c8c872668 | |||
| ddb29bb40d | |||
| aecb627ab7 | |||
| b8cd53cb59 | |||
| e61f6153c3 | |||
| 386e8ab902 | |||
| 5e8f02e3ca | |||
| f219562e72 | |||
| 29d94dfc14 | |||
| 622f5fc28c | |||
| 647f01e25c | |||
| 5a79c0e5c2 | |||
| 2a4c298572 | |||
| 1e59f73f79 | |||
| feb911aea0 | |||
| d0863d68c6 | |||
| 447d9b3ca1 | |||
| 86e66eb6a0 | |||
| b2c9515a63 | |||
| db04c5caee | |||
| 33526d5d13 | |||
| fc77b548d8 | |||
| bf7a168f2e | |||
| 17b1551bab | |||
| 8864243558 | |||
| 37aab7a16f | |||
| 86e1bdf7ea | |||
| 4547fd213d | |||
| 5aacec40cc | |||
| 1df78100ca | |||
| 9cd36fcb9b | |||
| 24b32eb917 | |||
| dec0e41fec | |||
| 42700ad2b2 | |||
| df51d79f6b | |||
| be1673a6a7 | |||
| 648f182e76 | |||
| 3aa56f0886 | |||
| b795534da7 | |||
| c67e2ac9f8 | |||
| ac569324cf | |||
| 357d197bb3 |
@ -1,4 +1,4 @@
|
||||
ARG VARIANT="16-bullseye"
|
||||
ARG VARIANT="lts-bullseye"
|
||||
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
||||
|
||||
|
||||
23
.env.example
23
.env.example
@ -1,26 +1,33 @@
|
||||
# Shared
|
||||
# Server + Client
|
||||
TZ=UTC
|
||||
PUBLIC_URL=http://localhost
|
||||
PUBLIC_SERVER_URL=http://localhost/api
|
||||
PUBLIC_URL=http://localhost:3000
|
||||
PUBLIC_SERVER_URL=http://localhost:3000/api
|
||||
PUBLIC_GOOGLE_CLIENT_ID=
|
||||
|
||||
# Server + Database
|
||||
POSTGRES_DB=postgres
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres
|
||||
|
||||
# Server
|
||||
SECRET_KEY=
|
||||
POSTGRES_HOST=postgres
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_DATABASE=postgres
|
||||
POSTGRES_SSL_CERT=
|
||||
JWT_SECRET=
|
||||
JWT_EXPIRY_TIME=604800
|
||||
PUBLIC_GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
GOOGLE_API_KEY=
|
||||
SENDGRID_API_KEY=
|
||||
SENDGRID_FORGOT_PASSWORD_TEMPLATE_ID=
|
||||
SENDGRID_FROM_NAME=
|
||||
SENDGRID_FROM_EMAIL=
|
||||
STORAGE_BUCKET=
|
||||
STORAGE_REGION=
|
||||
STORAGE_ENDPOINT=
|
||||
STORAGE_URL_PREFIX=
|
||||
STORAGE_ACCESS_KEY=
|
||||
STORAGE_SECRET_KEY=
|
||||
|
||||
# Flags
|
||||
# Flags (Client)
|
||||
PUBLIC_FLAG_DISABLE_SIGNUPS=false
|
||||
16
.github/workflows/close-stale.yml
vendored
16
.github/workflows/close-stale.yml
vendored
@ -1,16 +0,0 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v5.0.0
|
||||
with:
|
||||
stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity. Remove the stale label or comment on this PR, otherwise it would be closed in 5 days.'
|
||||
stale-issue-message: 'This issue is stale because it has been open for 30 days with no activity. Remove the stale label or comment on this issue, otherwise it would be closed in 5 days.'
|
||||
days-before-stale: 30
|
||||
days-before-close: 5
|
||||
96
.github/workflows/docker-build-push.yml
vendored
96
.github/workflows/docker-build-push.yml
vendored
@ -5,26 +5,33 @@ on:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
docker_client:
|
||||
name: Docker (Client)
|
||||
client:
|
||||
name: Client
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3.0.0
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- id: version
|
||||
name: Get Version
|
||||
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||
|
||||
- name: Login to Docker
|
||||
uses: docker/login-action@v1.14.1
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.0.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: $GITHUB_REPOSITORY_OWNER
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Build and Push Client Image
|
||||
uses: docker/build-push-action@v2.10.0
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@ -32,27 +39,36 @@ jobs:
|
||||
tags: |
|
||||
amruthpillai/reactive-resume:client-latest
|
||||
amruthpillai/reactive-resume:client-${{ steps.version.outputs.tag }}
|
||||
ghcr.io/amruthpillai/reactive-resume:client-latest
|
||||
ghcr.io/amruthpillai/reactive-resume:client-${{ steps.version.outputs.tag }}
|
||||
|
||||
docker_server:
|
||||
name: Docker (Server)
|
||||
server:
|
||||
name: Server
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3.0.0
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- id: version
|
||||
name: Get Version
|
||||
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||
|
||||
- name: Login to Docker
|
||||
uses: docker/login-action@v1.14.1
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.0.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: $GITHUB_REPOSITORY_OWNER
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Build and Push Server Image
|
||||
uses: docker/build-push-action@v2.10.0
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@ -60,61 +76,5 @@ jobs:
|
||||
tags: |
|
||||
amruthpillai/reactive-resume:server-latest
|
||||
amruthpillai/reactive-resume:server-${{ steps.version.outputs.tag }}
|
||||
|
||||
github_client:
|
||||
name: GitHub (Client)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3.0.0
|
||||
|
||||
- id: version
|
||||
name: Get Version
|
||||
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1.14.1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: $GITHUB_REPOSITORY_OWNER
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Build and Push Client Image
|
||||
uses: docker/build-push-action@v2.10.0
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: client/Dockerfile
|
||||
tags: |
|
||||
ghcr.io/amruthpillai/reactive-resume:client-latest
|
||||
ghcr.io/amruthpillai/reactive-resume:client-${{ steps.version.outputs.tag }}
|
||||
|
||||
github_server:
|
||||
name: GitHub (Server)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3.0.0
|
||||
|
||||
- id: version
|
||||
name: Get Version
|
||||
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1.14.1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: $GITHUB_REPOSITORY_OWNER
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Build and Push Server Image
|
||||
uses: docker/build-push-action@v2.10.0
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: server/Dockerfile
|
||||
tags: |
|
||||
ghcr.io/amruthpillai/reactive-resume:server-latest
|
||||
ghcr.io/amruthpillai/reactive-resume:server-${{ steps.version.outputs.tag }}
|
||||
|
||||
2
.npmrc
Normal file
2
.npmrc
Normal file
@ -0,0 +1,2 @@
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@ -21,5 +21,11 @@
|
||||
"i18n-ally.namespace": true,
|
||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"scss.validate": false
|
||||
"scss.validate": false,
|
||||
"conventionalCommits.scopes": [
|
||||
"client",
|
||||
"server",
|
||||
"docker",
|
||||
"dependencies"
|
||||
]
|
||||
}
|
||||
137
CHANGELOG.md
137
CHANGELOG.md
@ -2,6 +2,143 @@
|
||||
|
||||
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.5.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.5.2...v3.5.3) (2022-08-11)
|
||||
|
||||
### [3.5.2](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.5.1...v3.5.2) (2022-08-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **client:** :dizzy: add Finnish language support ([16d19eb](https://github.com/AmruthPillai/Reactive-Resume/commit/16d19eb70f64f768304f352d0f87102d328b57c1))
|
||||
|
||||
### [3.5.1](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.5.0...v3.5.1) (2022-07-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **client:** :sparkles: ask for confirmation when resetting a resume ([4d43f6a](https://github.com/AmruthPillai/Reactive-Resume/commit/4d43f6a6427198e62e9fcb995f1a28c0ee4de71e))
|
||||
* **docker:** :zap: remove ports from postgres docker instance ([07c91e9](https://github.com/AmruthPillai/Reactive-Resume/commit/07c91e9ac21e8ef120d08ab92363d8e48a55aaba))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **server:** :zap: don't initialize sendgrid if the apikey is empty ([05d3f1f](https://github.com/AmruthPillai/Reactive-Resume/commit/05d3f1f06fbffd899269a5c4dea3c52cf408125f))
|
||||
|
||||
## [3.5.0](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.8...v3.5.0) (2022-07-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **client:** :bug: attempt to fix the one-off date issue ([5197f95](https://github.com/AmruthPillai/Reactive-Resume/commit/5197f954c0baed3daf1c7e2c79b607354ef42024))
|
||||
* **client:** :bug: fix mui rendering of utc dates ([977fa72](https://github.com/AmruthPillai/Reactive-Resume/commit/977fa72ddeeeebf7463d43a820e85f783489a4dc))
|
||||
|
||||
### [3.4.8](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.7...v3.4.8) (2022-07-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **google:** add toast to display error message from google ([25cf594](https://github.com/AmruthPillai/Reactive-Resume/commit/25cf594eb948e1c2d6157028ee1fff2799df5f92))
|
||||
|
||||
### [3.4.7](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.4.6...v3.4.7) (2022-06-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mui:** update mui datepickers to newer package ([bfb48e3](https://github.com/AmruthPillai/Reactive-Resume/commit/bfb48e3aa7e0575922841522edc1d38544d1884f))
|
||||
|
||||
### [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)
|
||||
|
||||
29
README.md
29
README.md
@ -15,6 +15,23 @@ Reactive Resume is a free and open source resume builder that’s built to make
|
||||
|
||||
You have complete control over what goes into your resume, how it looks, what colors, what templates, even the layout in which sections placed. Want a dark mode resume? It’s as easy as editing 3 values and you’re done. You don’t need to wait to see your changes either. Everything you type, everything you change, appears immediately on your resume and gets updated in real time.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Reactive Resume](#reactive-resume)
|
||||
- [Go to App | [Docs](https://docs.rxresu.me)](#go-to-app--docs)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Features](#features)
|
||||
- [Languages](#languages)
|
||||
- [Tutorial](#tutorial)
|
||||
- [Build from Source](#build-from-source)
|
||||
- [Contributing](#contributing)
|
||||
- [Report Bugs and Feature Requests](#report-bugs-and-feature-requests)
|
||||
- [Donations](#donations)
|
||||
- [💸 PayPal](#-paypal)
|
||||
- [Infrastructure](#infrastructure)
|
||||
- [Contributors Wall](#contributors-wall)
|
||||
- [License](#license)
|
||||
|
||||
## Features
|
||||
|
||||
- Free, forever
|
||||
@ -39,20 +56,30 @@ You have complete control over what goes into your resume, how it looks, what co
|
||||
|
||||
- Arabic (اَلْعَرَبِيَّةُ)
|
||||
- Bengali (বাংলা)
|
||||
- Bulgarian (български)
|
||||
- Chinese (中文)
|
||||
- Czech (čeština)
|
||||
- Danish (Dansk)
|
||||
- Dutch (Nederlands)
|
||||
- English
|
||||
- Finnish (Suomi)
|
||||
- French (Français)
|
||||
- German (Deutsch)
|
||||
- Greek (Ελληνικά)
|
||||
- Hebrew (Ivrit)
|
||||
- Hindi (हिन्दी)
|
||||
- Hungarian (Magyar)
|
||||
- Indonesian (Bahasa Indonesia)
|
||||
- Italian (Italiano)
|
||||
- Kannada (ಕನ್ನಡ)
|
||||
- Malayalam (മലയാളം)
|
||||
- Odia (ଓଡ଼ିଆ)
|
||||
- Persian (Farsi)
|
||||
- Polish (Polski)
|
||||
- Portuguese (Português)
|
||||
- Russian (русский)
|
||||
- Spanish (Español)
|
||||
- Swedish (Svenska)
|
||||
- Tamil (தமிழ்)
|
||||
- Turkish (Türkçe)
|
||||
- Vietnamese (Tiếng Việt)
|
||||
@ -79,7 +106,7 @@ This project makes use of [conventional commits](https://www.conventionalcommits
|
||||
|
||||
NOTE: Be sure to merge the latest from `main` before making a pull request!
|
||||
|
||||
## Bugs? Feature Requests?
|
||||
## Report Bugs and Feature Requests
|
||||
|
||||
Use the [GitHub Issues](https://github.com/AmruthPillai/Reactive-Resume/issues/new/choose) platform to notify me about bugs or new features that you would like to see in Reactive Resume. Please check before creating new issues as there might already be one.
|
||||
|
||||
|
||||
13
SECURITY.md
Normal file
13
SECURITY.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 3.x.x | :white_check_mark: |
|
||||
| 2.x.x | :x: |
|
||||
| 1.x.x | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Create an issue on GitHub or send me an email through the contact form on my website at https://amruthpillai.com/
|
||||
@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application' version '7.1.2' apply false
|
||||
id 'com.android.library' version '7.1.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"ignorePatterns": [".next", "__ENV.js"],
|
||||
"rules": {
|
||||
"@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"]
|
||||
}
|
||||
}
|
||||
|
||||
5
client/.gitignore
vendored
5
client/.gitignore
vendored
@ -36,4 +36,7 @@ yarn-error.log*
|
||||
*.tsbuildinfo
|
||||
|
||||
# react-env
|
||||
__ENV.js
|
||||
__ENV.js
|
||||
|
||||
# next-sitemap
|
||||
sitemap*.xml
|
||||
@ -1,4 +1,4 @@
|
||||
FROM node:17-alpine as dependencies
|
||||
FROM node:lts-alpine as dependencies
|
||||
|
||||
RUN apk add --no-cache curl g++ make python3 \
|
||||
&& curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
|
||||
@ -11,7 +11,7 @@ COPY ./client/package.json ./client/package.json
|
||||
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
FROM node:17-alpine as builder
|
||||
FROM node:lts-alpine as builder
|
||||
|
||||
RUN apk add --no-cache curl g++ make python3 \
|
||||
&& curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
|
||||
@ -27,7 +27,7 @@ COPY --from=dependencies /app/client/node_modules ./client/node_modules
|
||||
RUN pnpm run build:schema
|
||||
RUN pnpm run build:client
|
||||
|
||||
FROM node:17-alpine as production
|
||||
FROM node:lts-alpine as production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
||||
const url = getResumeUrl(resume, { withHost: true });
|
||||
await navigator.clipboard.writeText(url);
|
||||
|
||||
toast.success(t('common.toast.success.resume-link-copied'));
|
||||
toast.success(t<string>('common.toast.success.resume-link-copied'));
|
||||
};
|
||||
|
||||
const handleExportPDF = async () => {
|
||||
@ -75,19 +75,19 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
||||
})}
|
||||
>
|
||||
<div className={styles.controller}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.zoom-in') as string}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-in')}>
|
||||
<ButtonBase onClick={() => zoomIn(0.25)}>
|
||||
<ZoomIn fontSize="medium" />
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.zoom-out') as string}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-out')}>
|
||||
<ButtonBase onClick={() => zoomOut(0.25)}>
|
||||
<ZoomOut fontSize="medium" />
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.center-artboard') as string}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.center-artboard')}>
|
||||
<ButtonBase onClick={() => centerView(0.95)}>
|
||||
<FilterCenterFocus fontSize="medium" />
|
||||
</ButtonBase>
|
||||
@ -98,7 +98,7 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
||||
{isDesktop && (
|
||||
<>
|
||||
{pages.length > 1 && (
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-orientation') as string}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-orientation')}>
|
||||
<ButtonBase onClick={handleTogglePageOrientation}>
|
||||
{orientation === 'vertical' ? (
|
||||
<AlignHorizontalCenter fontSize="medium" />
|
||||
@ -109,13 +109,13 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-page-break-line') as string}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-page-break-line')}>
|
||||
<ButtonBase onClick={handleTogglePageBreakLine}>
|
||||
<InsertPageBreak fontSize="medium" />
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-sidebars') as string}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-sidebars')}>
|
||||
<ButtonBase onClick={handleToggleSidebar}>
|
||||
<ViewSidebar fontSize="medium" />
|
||||
</ButtonBase>
|
||||
@ -125,13 +125,13 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
||||
</>
|
||||
)}
|
||||
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.copy-link') as string}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.copy-link')}>
|
||||
<ButtonBase onClick={handleCopyLink}>
|
||||
<Link fontSize="medium" />
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.export-pdf') as string}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.export-pdf')}>
|
||||
<ButtonBase onClick={handleExportPDF} disabled={isLoading}>
|
||||
<Download fontSize="medium" />
|
||||
</ButtonBase>
|
||||
|
||||
@ -133,7 +133,7 @@ const Header = () => {
|
||||
const url = getResumeUrl(resume, { withHost: true });
|
||||
await navigator.clipboard.writeText(url);
|
||||
|
||||
toast.success(t('common.toast.success.resume-link-copied'));
|
||||
toast.success(t<string>('common.toast.success.resume-link-copied'));
|
||||
};
|
||||
|
||||
return (
|
||||
@ -166,14 +166,14 @@ const Header = () => {
|
||||
<ListItemIcon>
|
||||
<DriveFileRenameOutline className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('builder.header.menu.rename')}</ListItemText>
|
||||
<ListItemText>{t<string>('builder.header.menu.rename')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleDuplicate}>
|
||||
<ListItemIcon>
|
||||
<CopyAll className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('builder.header.menu.duplicate')}</ListItemText>
|
||||
<ListItemText>{t<string>('builder.header.menu.duplicate')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
{resume.public ? (
|
||||
@ -181,27 +181,27 @@ const Header = () => {
|
||||
<ListItemIcon>
|
||||
<LinkIcon className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('builder.header.menu.share-link')}</ListItemText>
|
||||
<ListItemText>{t<string>('builder.header.menu.share-link')}</ListItemText>
|
||||
</MenuItem>
|
||||
) : (
|
||||
<Tooltip arrow placement="right" title={t('builder.header.menu.tooltips.share-link') as string}>
|
||||
<Tooltip arrow placement="right" title={t<string>('builder.header.menu.tooltips.share-link')}>
|
||||
<div>
|
||||
<MenuItem>
|
||||
<ListItemIcon>
|
||||
<LinkIcon className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('builder.header.menu.share-link')}</ListItemText>
|
||||
<ListItemText>{t<string>('builder.header.menu.share-link')}</ListItemText>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Tooltip arrow placement="right" title={t('builder.header.menu.tooltips.delete') as string}>
|
||||
<Tooltip arrow placement="right" title={t<string>('builder.header.menu.tooltips.delete')}>
|
||||
<MenuItem onClick={handleDelete}>
|
||||
<ListItemIcon>
|
||||
<Delete className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('builder.header.menu.delete')}</ListItemText>
|
||||
<ListItemText>{t<string>('builder.header.menu.delete')}</ListItemText>
|
||||
</MenuItem>
|
||||
</Tooltip>
|
||||
</Menu>
|
||||
|
||||
@ -48,9 +48,7 @@ const Page: React.FC<Props> = ({ page, showPageNumbers = false }) => {
|
||||
</div>
|
||||
|
||||
{showPageNumbers && (
|
||||
<h4 className={styles.pageNumber}>
|
||||
{t('builder.common.glossary.page')} {page + 1}
|
||||
</h4>
|
||||
<h4 className={styles.pageNumber}>{`${t<string>('builder.common.glossary.page')} ${page + 1}`}</h4>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -81,14 +81,14 @@ const LeftSidebar = () => {
|
||||
arrow
|
||||
key={id}
|
||||
placement="right"
|
||||
title={get(sections, `${id}.name`, t<string>(`builder.leftSidebar.sections.${id}.heading`))}
|
||||
title={get(sections, `${id}.name`, t<string>(`builder.leftSidebar.sections.${id}.heading`)) as string}
|
||||
>
|
||||
<IconButton onClick={() => handleClick(id)}>{icon}</IconButton>
|
||||
</Tooltip>
|
||||
))}
|
||||
|
||||
{customSections.map(({ id }) => (
|
||||
<Tooltip key={id} title={get(sections, `${id}.name`, '')} placement="right" arrow>
|
||||
<Tooltip key={id} title={get(sections, `${id}.name`, '') as string} placement="right" arrow>
|
||||
<IconButton onClick={() => handleClick(id)}>
|
||||
<Star />
|
||||
</IconButton>
|
||||
@ -114,7 +114,9 @@ const LeftSidebar = () => {
|
||||
|
||||
<div className="py-6 text-right">
|
||||
<Button fullWidth variant="outlined" startIcon={<Add />} onClick={handleAddSection}>
|
||||
{t('builder.common.actions.add', { token: t('builder.leftSidebar.sections.section.heading') })}
|
||||
{t<string>('builder.common.actions.add', {
|
||||
token: t<string>('builder.leftSidebar.sections.section.heading'),
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@ -24,7 +24,7 @@ const Basics = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="sections.basics" name={t('builder.leftSidebar.sections.basics.heading')} />
|
||||
<Heading path="sections.basics" name={t<string>('builder.leftSidebar.sections.basics.heading')} />
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<div className="grid items-center gap-4 sm:col-span-2 sm:grid-cols-3">
|
||||
@ -33,10 +33,10 @@ const Basics = () => {
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2 w-full sm:col-span-2">
|
||||
<ResumeInput label={t('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}>
|
||||
{t('builder.leftSidebar.sections.basics.actions.photo-filters')}
|
||||
{t<string>('builder.leftSidebar.sections.basics.actions.photo-filters')}
|
||||
</Button>
|
||||
|
||||
<Popover
|
||||
@ -57,20 +57,30 @@ const Basics = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ResumeInput label={t('builder.common.form.email.label')} path="basics.email" className="sm:col-span-2" />
|
||||
<ResumeInput label={t('builder.common.form.phone.label')} path="basics.phone" />
|
||||
<ResumeInput label={t('builder.common.form.url.label')} path="basics.website" />
|
||||
<ResumeInput
|
||||
type="date"
|
||||
label={t<string>('builder.leftSidebar.sections.basics.birthdate.label')}
|
||||
path="basics.birthdate"
|
||||
className="sm:col-span-2"
|
||||
/>
|
||||
<ResumeInput
|
||||
label={t<string>('builder.common.form.email.label')}
|
||||
path="basics.email"
|
||||
className="sm:col-span-2"
|
||||
/>
|
||||
<ResumeInput label={t<string>('builder.common.form.phone.label')} path="basics.phone" />
|
||||
<ResumeInput label={t<string>('builder.common.form.url.label')} path="basics.website" />
|
||||
|
||||
<Divider className="sm:col-span-2" />
|
||||
|
||||
<ResumeInput
|
||||
label={t('builder.leftSidebar.sections.basics.headline.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.basics.headline.label')}
|
||||
path="basics.headline"
|
||||
className="sm:col-span-2"
|
||||
/>
|
||||
<ResumeInput
|
||||
type="textarea"
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
path="basics.summary"
|
||||
className="sm:col-span-2"
|
||||
markdownSupported
|
||||
|
||||
@ -8,19 +8,28 @@ const Location = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="sections.location" name={t('builder.leftSidebar.sections.location.heading')} />
|
||||
<Heading path="sections.location" name={t<string>('builder.leftSidebar.sections.location.heading')} />
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<ResumeInput
|
||||
label={t('builder.leftSidebar.sections.location.address.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.location.address.label')}
|
||||
path="basics.location.address"
|
||||
className="sm:col-span-2"
|
||||
/>
|
||||
<ResumeInput label={t('builder.leftSidebar.sections.location.city.label')} path="basics.location.city" />
|
||||
<ResumeInput label={t('builder.leftSidebar.sections.location.region.label')} path="basics.location.region" />
|
||||
<ResumeInput label={t('builder.leftSidebar.sections.location.country.label')} path="basics.location.country" />
|
||||
<ResumeInput
|
||||
label={t('builder.leftSidebar.sections.location.postal-code.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.location.city.label')}
|
||||
path="basics.location.city"
|
||||
/>
|
||||
<ResumeInput
|
||||
label={t<string>('builder.leftSidebar.sections.location.region.label')}
|
||||
path="basics.location.region"
|
||||
/>
|
||||
<ResumeInput
|
||||
label={t<string>('builder.leftSidebar.sections.location.country.label')}
|
||||
path="basics.location.country"
|
||||
/>
|
||||
<ResumeInput
|
||||
label={t<string>('builder.leftSidebar.sections.location.postal-code.label')}
|
||||
path="basics.location.postalCode"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -32,7 +32,7 @@ const PhotoFilters = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 p-5 dark:bg-neutral-800">
|
||||
<div>
|
||||
<h4 className="font-medium">{t('builder.leftSidebar.sections.basics.photo-filters.size.heading')}</h4>
|
||||
<h4 className="font-medium">{t<string>('builder.leftSidebar.sections.basics.photo-filters.size.heading')}</h4>
|
||||
|
||||
<div className="mx-2">
|
||||
<Slider
|
||||
@ -54,18 +54,20 @@ const PhotoFilters = () => {
|
||||
<Divider />
|
||||
|
||||
<div>
|
||||
<h4 className="font-medium">{t('builder.leftSidebar.sections.basics.photo-filters.effects.heading')}</h4>
|
||||
<h4 className="font-medium">
|
||||
{t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.heading')}
|
||||
</h4>
|
||||
|
||||
<div className="flex items-center">
|
||||
<FormControlLabel
|
||||
label={t('builder.leftSidebar.sections.basics.photo-filters.effects.grayscale.label') as string}
|
||||
label={t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.grayscale.label')}
|
||||
control={
|
||||
<Checkbox color="secondary" checked={grayscale} onChange={(_, value) => handleSetGrayscale(value)} />
|
||||
}
|
||||
/>
|
||||
|
||||
<FormControlLabel
|
||||
label={t('builder.leftSidebar.sections.basics.photo-filters.effects.border.label') as string}
|
||||
label={t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.border.label')}
|
||||
control={<Checkbox color="secondary" checked={border} onChange={(_, value) => handleSetBorder(value)} />}
|
||||
/>
|
||||
</div>
|
||||
@ -74,7 +76,7 @@ const PhotoFilters = () => {
|
||||
<Divider />
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<h4 className="font-medium">{t('builder.leftSidebar.sections.basics.photo-filters.shape.heading')}</h4>
|
||||
<h4 className="font-medium">{t<string>('builder.leftSidebar.sections.basics.photo-filters.shape.heading')}</h4>
|
||||
|
||||
<ToggleButtonGroup exclusive value={shape} onChange={(_, value) => handleChangeShape(value)}>
|
||||
<ToggleButton size="small" value="square" className="w-14">
|
||||
|
||||
@ -49,7 +49,7 @@ const PhotoUpload: React.FC = () => {
|
||||
const file = event.target.files[0];
|
||||
|
||||
if (file.size > FILE_UPLOAD_MAX_SIZE) {
|
||||
toast.error(t('common.toast.error.upload-photo-size'));
|
||||
toast.error(t<string>('common.toast.error.upload-photo-size'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -67,8 +67,8 @@ const PhotoUpload: React.FC = () => {
|
||||
<Tooltip
|
||||
title={
|
||||
isEmpty(photo.url)
|
||||
? (t('builder.leftSidebar.sections.basics.photo-upload.tooltip.upload') as string)
|
||||
: (t('builder.leftSidebar.sections.basics.photo-upload.tooltip.remove') as string)
|
||||
? (t<string>('builder.leftSidebar.sections.basics.photo-upload.tooltip.upload') as string)
|
||||
: (t<string>('builder.leftSidebar.sections.basics.photo-upload.tooltip.remove') as string)
|
||||
}
|
||||
>
|
||||
<Avatar sx={{ width: 96, height: 96 }} src={photo.url} />
|
||||
|
||||
@ -28,7 +28,7 @@ const Profiles = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="sections.profiles" name={t('builder.leftSidebar.sections.profiles.heading')} />
|
||||
<Heading path="sections.profiles" name={t<string>('builder.leftSidebar.sections.profiles.heading')} />
|
||||
|
||||
<List
|
||||
path="basics.profiles"
|
||||
@ -40,8 +40,8 @@ const Profiles = () => {
|
||||
|
||||
<footer className="flex justify-end">
|
||||
<Button variant="outlined" startIcon={<Add />} onClick={handleAdd}>
|
||||
{t('builder.common.actions.add', {
|
||||
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
|
||||
{t<string>('builder.common.actions.add', {
|
||||
token: t<string>('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
|
||||
})}
|
||||
</Button>
|
||||
</footer>
|
||||
|
||||
@ -74,7 +74,7 @@ const Section: React.FC<Props> = ({
|
||||
<SectionSettings path={path} />
|
||||
|
||||
<Button variant="outlined" startIcon={<Add />} onClick={handleAdd}>
|
||||
{t('builder.common.actions.add', { token: heading })}
|
||||
{t<string>('builder.common.actions.add', { token: heading })}
|
||||
</Button>
|
||||
</footer>
|
||||
</>
|
||||
|
||||
@ -32,7 +32,7 @@ const SectionSettings: React.FC<Props> = ({ path }) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tooltip title={t('builder.common.columns.tooltip') as string}>
|
||||
<Tooltip title={t<string>('builder.common.columns.tooltip')}>
|
||||
<ButtonBase onClick={handleClick} sx={{ padding: 1, borderRadius: 1 }} className="opacity-50 hover:opacity-75">
|
||||
<ViewWeek /> <span className="ml-1.5 text-xs">{columns}</span>
|
||||
</ButtonBase>
|
||||
@ -48,7 +48,7 @@ const SectionSettings: React.FC<Props> = ({ path }) => {
|
||||
}}
|
||||
>
|
||||
<div className="p-5 dark:bg-neutral-800">
|
||||
<h4 className="mb-2 font-medium">{t('builder.common.columns.heading')}</h4>
|
||||
<h4 className="mb-2 font-medium">{t<string>('builder.common.columns.heading')}</h4>
|
||||
|
||||
<ToggleButtonGroup exclusive value={columns} onChange={(_, value: number) => handleSetColumns(value)}>
|
||||
{[1, 2, 3, 4].map((index) => (
|
||||
|
||||
@ -25,7 +25,7 @@ const CustomCSS = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="metadata.css" name={t('builder.rightSidebar.sections.css.heading')} isHideable />
|
||||
<Heading path="metadata.css" name={t<string>('builder.rightSidebar.sections.css.heading')} isHideable />
|
||||
|
||||
<Editor
|
||||
height="200px"
|
||||
|
||||
@ -19,12 +19,12 @@ const Export = () => {
|
||||
|
||||
const pdfListItemText = {
|
||||
normal: {
|
||||
primary: t('builder.rightSidebar.sections.export.pdf.normal.primary'),
|
||||
secondary: t('builder.rightSidebar.sections.export.pdf.normal.secondary'),
|
||||
primary: t<string>('builder.rightSidebar.sections.export.pdf.normal.primary'),
|
||||
secondary: t<string>('builder.rightSidebar.sections.export.pdf.normal.secondary'),
|
||||
},
|
||||
loading: {
|
||||
primary: t('builder.rightSidebar.sections.export.pdf.loading.primary'),
|
||||
secondary: t('builder.rightSidebar.sections.export.pdf.loading.secondary'),
|
||||
primary: t<string>('builder.rightSidebar.sections.export.pdf.loading.primary'),
|
||||
secondary: t<string>('builder.rightSidebar.sections.export.pdf.loading.secondary'),
|
||||
},
|
||||
};
|
||||
|
||||
@ -53,7 +53,7 @@ const Export = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="metadata.export" name={t('builder.rightSidebar.sections.export.heading')} />
|
||||
<Heading path="metadata.export" name={t<string>('builder.rightSidebar.sections.export.heading')} />
|
||||
|
||||
<List sx={{ padding: 0 }}>
|
||||
<ListItem sx={{ padding: 0 }}>
|
||||
@ -61,8 +61,8 @@ const Export = () => {
|
||||
<Schema />
|
||||
|
||||
<ListItemText
|
||||
primary={t('builder.rightSidebar.sections.export.json.primary')}
|
||||
secondary={t('builder.rightSidebar.sections.export.json.secondary')}
|
||||
primary={t<string>('builder.rightSidebar.sections.export.json.primary')}
|
||||
secondary={t<string>('builder.rightSidebar.sections.export.json.secondary')}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
|
||||
@ -60,9 +60,9 @@ const Layout = () => {
|
||||
<>
|
||||
<Heading
|
||||
path="metadata.layout"
|
||||
name={t('builder.rightSidebar.sections.layout.heading')}
|
||||
name={t<string>('builder.rightSidebar.sections.layout.heading')}
|
||||
action={
|
||||
<Tooltip title={t('builder.rightSidebar.sections.layout.tooltip.reset-layout') as string}>
|
||||
<Tooltip title={t<string>('builder.rightSidebar.sections.layout.tooltip.reset-layout')}>
|
||||
<IconButton onClick={handleResetLayout}>
|
||||
<Restore />
|
||||
</IconButton>
|
||||
@ -76,12 +76,16 @@ const Layout = () => {
|
||||
<div key={pageIndex} className={styles.page}>
|
||||
<div className="flex items-center justify-between pr-3">
|
||||
<p className={styles.heading}>
|
||||
{t('builder.common.glossary.page')} {pageIndex + 1}
|
||||
{t<string>('builder.common.glossary.page')} {pageIndex + 1}
|
||||
</p>
|
||||
|
||||
<div className={clsx(styles.delete, { hidden: pageIndex === 0 })}>
|
||||
<Tooltip
|
||||
title={t('builder.common.actions.delete', { token: t('builder.common.glossary.page') }) as string}
|
||||
title={
|
||||
t<string>('builder.common.actions.delete', {
|
||||
token: t<string>('builder.common.glossary.page'),
|
||||
}) as string
|
||||
}
|
||||
>
|
||||
<IconButton size="small" onClick={() => handleDeletePage(pageIndex)}>
|
||||
<Close fontSize="small" />
|
||||
@ -113,7 +117,7 @@ const Layout = () => {
|
||||
[styles.disabled]: !get(resumeSections, `${sectionId}.visible`, true),
|
||||
})}
|
||||
>
|
||||
{get(resumeSections, `${sectionId}.name`)}
|
||||
{get(resumeSections, `${sectionId}.name`, '') as string}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -132,7 +136,7 @@ const Layout = () => {
|
||||
|
||||
<div className="flex items-center justify-end">
|
||||
<Button variant="outlined" startIcon={<Add />} onClick={handleAddPage}>
|
||||
{t('builder.common.actions.add', { token: t('builder.common.glossary.page') })}
|
||||
{t<string>('builder.common.actions.add', { token: t<string>('builder.common.glossary.page') })}
|
||||
</Button>
|
||||
</div>
|
||||
</DragDropContext>
|
||||
|
||||
@ -12,39 +12,41 @@ const Links = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="metadata.links" name={t('builder.rightSidebar.sections.links.heading')} />
|
||||
<Heading path="metadata.links" name={t<string>('builder.rightSidebar.sections.links.heading')} />
|
||||
|
||||
<div className={styles.container}>
|
||||
<div className={styles.section}>
|
||||
<h2>
|
||||
<Savings fontSize="small" />
|
||||
{t('builder.rightSidebar.sections.links.donate.heading')}
|
||||
{t<string>('builder.rightSidebar.sections.links.donate.heading')}
|
||||
</h2>
|
||||
|
||||
<p>{t('builder.rightSidebar.sections.links.donate.body')}</p>
|
||||
<p>{t<string>('builder.rightSidebar.sections.links.donate.body')}</p>
|
||||
|
||||
<a href={DONATION_URL} target="_blank" rel="noreferrer">
|
||||
<Button startIcon={<Coffee />}>{t('builder.rightSidebar.sections.links.donate.button')}</Button>
|
||||
<Button startIcon={<Coffee />}>{t<string>('builder.rightSidebar.sections.links.donate.button')}</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h2>
|
||||
<BugReport fontSize="small" />
|
||||
{t('builder.rightSidebar.sections.links.bugs-features.heading')}
|
||||
{t<string>('builder.rightSidebar.sections.links.bugs-features.heading')}
|
||||
</h2>
|
||||
|
||||
<p>{t('builder.rightSidebar.sections.links.bugs-features.body')}</p>
|
||||
<p>{t<string>('builder.rightSidebar.sections.links.bugs-features.body')}</p>
|
||||
|
||||
<a href={GITHUB_ISSUES_URL} target="_blank" rel="noreferrer">
|
||||
<Button startIcon={<GitHub />}>{t('builder.rightSidebar.sections.links.bugs-features.button')}</Button>
|
||||
<Button startIcon={<GitHub />}>
|
||||
{t<string>('builder.rightSidebar.sections.links.bugs-features.button')}
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a href={GITHUB_URL} target="_blank" rel="noreferrer">
|
||||
<Button variant="text" startIcon={<Link />}>
|
||||
{t('builder.rightSidebar.sections.links.github')}
|
||||
{t<string>('builder.rightSidebar.sections.links.github')}
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -15,7 +15,7 @@ import dayjs from 'dayjs';
|
||||
import get from 'lodash/get';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useMemo } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
|
||||
import Heading from '@/components/shared/Heading';
|
||||
@ -36,6 +36,8 @@ const Settings = () => {
|
||||
|
||||
const { locale, ...router } = useRouter();
|
||||
|
||||
const [confirmReset, setConfirmReset] = useState(false);
|
||||
|
||||
const resume = useAppSelector((state) => state.resume);
|
||||
const theme = useAppSelector((state) => state.build.theme);
|
||||
const pages = useAppSelector((state) => state.resume.metadata.layout);
|
||||
@ -48,7 +50,7 @@ const Settings = () => {
|
||||
const dateConfig: DateConfig = useMemo(() => get(resume, 'metadata.date'), [resume]);
|
||||
|
||||
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 { mutateAsync: loadSampleDataMutation } = useMutation<Resume, ServerError, LoadSampleDataParams>(
|
||||
@ -78,20 +80,25 @@ const Settings = () => {
|
||||
};
|
||||
|
||||
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 (
|
||||
<>
|
||||
<Heading path="metadata.settings" name={t('builder.rightSidebar.sections.settings.heading')} />
|
||||
<Heading path="metadata.settings" name={t<string>('builder.rightSidebar.sections.settings.heading')} />
|
||||
|
||||
<List sx={{ padding: 0 }}>
|
||||
{/* Global Settings */}
|
||||
<>
|
||||
<ListSubheader className="rounded">
|
||||
{t('builder.rightSidebar.sections.settings.global.heading')}
|
||||
{t<string>('builder.rightSidebar.sections.settings.global.heading')}
|
||||
</ListSubheader>
|
||||
|
||||
<ListItem>
|
||||
@ -99,7 +106,7 @@ const Settings = () => {
|
||||
<Palette />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t('builder.rightSidebar.sections.settings.global.theme.primary')}
|
||||
primary={t<string>('builder.rightSidebar.sections.settings.global.theme.primary')}
|
||||
secondary={themeString}
|
||||
/>
|
||||
<ThemeSwitch checked={isDarkMode} onChange={(_, value: boolean) => handleSetTheme(value)} />
|
||||
@ -108,8 +115,8 @@ const Settings = () => {
|
||||
<ListItem className="flex-col">
|
||||
<ListItemText
|
||||
className="w-full"
|
||||
primary={t('builder.rightSidebar.sections.settings.global.date.primary')}
|
||||
secondary={t('builder.rightSidebar.sections.settings.global.date.secondary')}
|
||||
primary={t<string>('builder.rightSidebar.sections.settings.global.date.primary')}
|
||||
secondary={t<string>('builder.rightSidebar.sections.settings.global.date.secondary')}
|
||||
/>
|
||||
<Autocomplete<string, false, boolean, false>
|
||||
disableClearable
|
||||
@ -124,8 +131,8 @@ const Settings = () => {
|
||||
<ListItem className="flex-col">
|
||||
<ListItemText
|
||||
className="w-full"
|
||||
primary={t('builder.rightSidebar.sections.settings.global.language.primary')}
|
||||
secondary={t('builder.rightSidebar.sections.settings.global.language.secondary')}
|
||||
primary={t<string>('builder.rightSidebar.sections.settings.global.language.primary')}
|
||||
secondary={t<string>('builder.rightSidebar.sections.settings.global.language.secondary')}
|
||||
/>
|
||||
<Autocomplete<Language, false, boolean, false>
|
||||
disableClearable
|
||||
@ -148,15 +155,17 @@ const Settings = () => {
|
||||
|
||||
{/* Page Settings */}
|
||||
<>
|
||||
<ListSubheader className="rounded">{t('builder.rightSidebar.sections.settings.page.heading')}</ListSubheader>
|
||||
<ListSubheader className="rounded">
|
||||
{t<string>('builder.rightSidebar.sections.settings.page.heading')}
|
||||
</ListSubheader>
|
||||
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={t('builder.rightSidebar.sections.settings.page.orientation.primary')}
|
||||
primary={t<string>('builder.rightSidebar.sections.settings.page.orientation.primary')}
|
||||
secondary={
|
||||
pages.length === 1
|
||||
? t('builder.rightSidebar.sections.settings.page.orientation.disabled')
|
||||
: t('builder.rightSidebar.sections.settings.page.orientation.secondary')
|
||||
? t<string>('builder.rightSidebar.sections.settings.page.orientation.disabled')
|
||||
: t<string>('builder.rightSidebar.sections.settings.page.orientation.secondary')
|
||||
}
|
||||
/>
|
||||
<Switch
|
||||
@ -169,8 +178,8 @@ const Settings = () => {
|
||||
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={t('builder.rightSidebar.sections.settings.page.break-line.primary')}
|
||||
secondary={t('builder.rightSidebar.sections.settings.page.break-line.secondary')}
|
||||
primary={t<string>('builder.rightSidebar.sections.settings.page.break-line.primary')}
|
||||
secondary={t<string>('builder.rightSidebar.sections.settings.page.break-line.secondary')}
|
||||
/>
|
||||
<Switch color="secondary" checked={breakLine} onChange={() => dispatch(togglePageBreakLine())} />
|
||||
</ListItem>
|
||||
@ -179,7 +188,7 @@ const Settings = () => {
|
||||
{/* Resume Settings */}
|
||||
<>
|
||||
<ListSubheader className="rounded">
|
||||
{t('builder.rightSidebar.sections.settings.resume.heading')}
|
||||
{t<string>('builder.rightSidebar.sections.settings.resume.heading')}
|
||||
</ListSubheader>
|
||||
|
||||
<ListItem>
|
||||
@ -188,8 +197,8 @@ const Settings = () => {
|
||||
<Anchor />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t('builder.rightSidebar.sections.settings.resume.sample.primary')}
|
||||
secondary={t('builder.rightSidebar.sections.settings.resume.sample.secondary')}
|
||||
primary={t<string>('builder.rightSidebar.sections.settings.resume.sample.primary')}
|
||||
secondary={t<string>('builder.rightSidebar.sections.settings.resume.sample.secondary')}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
@ -200,8 +209,12 @@ const Settings = () => {
|
||||
<DeleteForever />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t('builder.rightSidebar.sections.settings.resume.reset.primary')}
|
||||
secondary={t('builder.rightSidebar.sections.settings.resume.reset.secondary')}
|
||||
primary={
|
||||
confirmReset
|
||||
? 'Are you sure?'
|
||||
: t<string>('builder.rightSidebar.sections.settings.resume.reset.primary')
|
||||
}
|
||||
secondary={t<string>('builder.rightSidebar.sections.settings.resume.reset.secondary')}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
|
||||
@ -29,19 +29,19 @@ const Sharing = () => {
|
||||
|
||||
await navigator.clipboard.writeText(text);
|
||||
|
||||
toast.success(t('common.toast.success.resume-link-copied'));
|
||||
toast.success(t<string>('common.toast.success.resume-link-copied'));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="metadata.sharing" name={t('builder.rightSidebar.sections.sharing.heading')} />
|
||||
<Heading path="metadata.sharing" name={t<string>('builder.rightSidebar.sections.sharing.heading')} />
|
||||
|
||||
<List sx={{ padding: 0 }}>
|
||||
<ListItem className="flex flex-col" sx={{ padding: 0 }}>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<ListItemText
|
||||
primary={t('builder.rightSidebar.sections.sharing.visibility.title')}
|
||||
secondary={t('builder.rightSidebar.sections.sharing.visibility.subtitle')}
|
||||
primary={t<string>('builder.rightSidebar.sections.sharing.visibility.title')}
|
||||
secondary={t<string>('builder.rightSidebar.sections.sharing.visibility.subtitle')}
|
||||
/>
|
||||
<Switch color="secondary" checked={isPublic} onChange={(_, value) => handleSetVisibility(value)} />
|
||||
</div>
|
||||
@ -63,7 +63,7 @@ const Sharing = () => {
|
||||
|
||||
<div className="mt-1 flex w-full">
|
||||
<FormControlLabel
|
||||
label={t('builder.rightSidebar.sections.sharing.short-url.label') as string}
|
||||
label={t<string>('builder.rightSidebar.sections.sharing.short-url.label')}
|
||||
control={
|
||||
<Checkbox className="mr-1" checked={showShortUrl} onChange={(_, value) => setShowShortUrl(value)} />
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ const Templates = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="metadata.templates" name={t('builder.rightSidebar.sections.templates.heading')} />
|
||||
<Heading path="metadata.templates" name={t<string>('builder.rightSidebar.sections.templates.heading')} />
|
||||
|
||||
<div className={styles.container}>
|
||||
{Object.values(templateMap).map((template) => (
|
||||
|
||||
@ -24,7 +24,7 @@ const Theme = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="metadata.theme" name={t('builder.rightSidebar.sections.theme.heading')} />
|
||||
<Heading path="metadata.theme" name={t<string>('builder.rightSidebar.sections.theme.heading')} />
|
||||
|
||||
<div className={styles.container}>
|
||||
<div className={styles.colorOptions}>
|
||||
@ -34,18 +34,18 @@ const Theme = () => {
|
||||
</div>
|
||||
|
||||
<ColorPicker
|
||||
label={t('builder.rightSidebar.sections.theme.form.primary.label')}
|
||||
label={t<string>('builder.rightSidebar.sections.theme.form.primary.label')}
|
||||
color={primary}
|
||||
className="col-span-2"
|
||||
onChange={(color) => handleChange('primary', color)}
|
||||
/>
|
||||
<ColorPicker
|
||||
label={t('builder.rightSidebar.sections.theme.form.background.label')}
|
||||
label={t<string>('builder.rightSidebar.sections.theme.form.background.label')}
|
||||
color={background}
|
||||
onChange={(color) => handleChange('background', color)}
|
||||
/>
|
||||
<ColorPicker
|
||||
label={t('builder.rightSidebar.sections.theme.form.text.label')}
|
||||
label={t<string>('builder.rightSidebar.sections.theme.form.text.label')}
|
||||
color={text}
|
||||
onChange={(color) => handleChange('text', color)}
|
||||
/>
|
||||
|
||||
@ -64,7 +64,7 @@ const Widgets: React.FC<WidgetProps> = ({ label, category }) => {
|
||||
step={1}
|
||||
marks={[
|
||||
{ value: 12, label: '12px' },
|
||||
{ value: 24, label: t('builder.rightSidebar.sections.typography.form.font-size.label') },
|
||||
{ value: 24, label: t<string>('builder.rightSidebar.sections.typography.form.font-size.label') },
|
||||
{ value: 36, label: '36px' },
|
||||
]}
|
||||
valueLabelDisplay="auto"
|
||||
@ -82,7 +82,10 @@ const Widgets: React.FC<WidgetProps> = ({ label, category }) => {
|
||||
value={fonts.find((font) => font.family === family[category])}
|
||||
onChange={(_, font: Font | null) => handleChange('family', font)}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label={t('builder.rightSidebar.sections.typography.form.font-family.label')} />
|
||||
<TextField
|
||||
{...params}
|
||||
label={t<string>('builder.rightSidebar.sections.typography.form.font-family.label')}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -95,10 +98,13 @@ const Typography = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading path="metadata.typography" name={t('builder.rightSidebar.sections.typography.heading')} />
|
||||
<Heading path="metadata.typography" name={t<string>('builder.rightSidebar.sections.typography.heading')} />
|
||||
|
||||
<Widgets label={t('builder.rightSidebar.sections.typography.widgets.headings.label')} category="heading" />
|
||||
<Widgets label={t('builder.rightSidebar.sections.typography.widgets.body.label')} category="body" />
|
||||
<Widgets
|
||||
label={t<string>('builder.rightSidebar.sections.typography.widgets.headings.label')}
|
||||
category="heading"
|
||||
/>
|
||||
<Widgets label={t<string>('builder.rightSidebar.sections.typography.widgets.body.label')} category="body" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -94,7 +94,7 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
|
||||
const url = getResumeUrl(resume, { withHost: true });
|
||||
await navigator.clipboard.writeText(url);
|
||||
|
||||
toast.success(t('common.toast.success.resume-link-copied'));
|
||||
toast.success(t<string>('common.toast.success.resume-link-copied'));
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
@ -124,7 +124,7 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
|
||||
<footer>
|
||||
<div className={styles.meta}>
|
||||
<p>{resume.name}</p>
|
||||
<p>{t('dashboard.resume.timestamp', { timestamp: getRelativeTime(resume.updatedAt) })}</p>
|
||||
<p>{t<string>('dashboard.resume.timestamp', { timestamp: getRelativeTime(resume.updatedAt) })}</p>
|
||||
</div>
|
||||
|
||||
<ButtonBase className={styles.menu} onClick={handleOpenMenu}>
|
||||
@ -136,21 +136,21 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
|
||||
<ListItemIcon>
|
||||
<OpenInNew className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('dashboard.resume.menu.open')}</ListItemText>
|
||||
<ListItemText>{t<string>('dashboard.resume.menu.open')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleRename}>
|
||||
<ListItemIcon>
|
||||
<DriveFileRenameOutline className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('dashboard.resume.menu.rename')}</ListItemText>
|
||||
<ListItemText>{t<string>('dashboard.resume.menu.rename')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={handleDuplicate}>
|
||||
<ListItemIcon>
|
||||
<ContentCopy className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('dashboard.resume.menu.duplicate')}</ListItemText>
|
||||
<ListItemText>{t<string>('dashboard.resume.menu.duplicate')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
{resume.public ? (
|
||||
@ -158,27 +158,27 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
|
||||
<ListItemIcon>
|
||||
<LinkIcon className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('dashboard.resume.menu.share-link')}</ListItemText>
|
||||
<ListItemText>{t<string>('dashboard.resume.menu.share-link')}</ListItemText>
|
||||
</MenuItem>
|
||||
) : (
|
||||
<Tooltip arrow placement="right" title={t('dashboard.resume.menu.tooltips.share-link') as string}>
|
||||
<Tooltip arrow placement="right" title={t<string>('dashboard.resume.menu.tooltips.share-link')}>
|
||||
<div>
|
||||
<MenuItem>
|
||||
<ListItemIcon>
|
||||
<LinkIcon className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('dashboard.resume.menu.share-link')}</ListItemText>
|
||||
<ListItemText>{t<string>('dashboard.resume.menu.share-link')}</ListItemText>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Tooltip arrow placement="right" title={t('dashboard.resume.menu.tooltips.delete') as string}>
|
||||
<Tooltip arrow placement="right" title={t<string>('dashboard.resume.menu.tooltips.delete')}>
|
||||
<MenuItem onClick={handleDelete}>
|
||||
<ListItemIcon>
|
||||
<DeleteOutline className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('dashboard.resume.menu.delete')}</ListItemText>
|
||||
<ListItemText>{t<string>('dashboard.resume.menu.delete')}</ListItemText>
|
||||
</MenuItem>
|
||||
</Tooltip>
|
||||
</Menu>
|
||||
|
||||
@ -56,12 +56,12 @@ const Avatar: React.FC<Props> = ({ size = 64 }) => {
|
||||
<Menu anchorEl={anchorEl} onClose={handleClose} open={Boolean(anchorEl)}>
|
||||
<MenuItem>
|
||||
<div>
|
||||
<span className="text-xs opacity-50">{t('common.avatar.menu.greeting')}</span>
|
||||
<span className="text-xs opacity-50">{t<string>('common.avatar.menu.greeting')}</span>
|
||||
<p>{user?.name}</p>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={handleLogout}>{t('common.avatar.menu.logout')}</MenuItem>
|
||||
<MenuItem onClick={handleLogout}>{t<string>('common.avatar.menu.logout')}</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -5,11 +5,12 @@ import { useRouter } from 'next/router';
|
||||
import styles from './BaseModal.module.scss';
|
||||
|
||||
type Props = {
|
||||
icon?: React.ReactNode;
|
||||
isOpen: boolean;
|
||||
heading: string;
|
||||
handleClose: () => void;
|
||||
icon?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
footerChildren?: React.ReactNode;
|
||||
handleClose: () => void;
|
||||
};
|
||||
|
||||
const BaseModal: React.FC<Props> = ({ icon, isOpen, heading, children, handleClose, footerChildren }) => {
|
||||
|
||||
@ -10,7 +10,7 @@ const Footer: React.FC<Props> = ({ className }) => {
|
||||
|
||||
return (
|
||||
<div className={clsx('text-xs', className)}>
|
||||
<p>{t('common.footer.license')}</p>
|
||||
<p>{t<string>('common.footer.license')}</p>
|
||||
|
||||
<p>
|
||||
<Trans t={t} i18nKey="common.footer.credit">
|
||||
|
||||
@ -72,19 +72,19 @@ const Heading: React.FC<Props> = ({
|
||||
})}
|
||||
>
|
||||
{isEditable && (
|
||||
<Tooltip title={t('builder.common.tooltip.rename-section') as string}>
|
||||
<Tooltip title={t<string>('builder.common.tooltip.rename-section')}>
|
||||
<IconButton onClick={toggleEditMode}>{editMode ? <Check /> : <DriveFileRenameOutline />}</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{isHideable && (
|
||||
<Tooltip title={t('builder.common.tooltip.toggle-visibility') as string}>
|
||||
<Tooltip title={t<string>('builder.common.tooltip.toggle-visibility')}>
|
||||
<IconButton onClick={toggleVisibility}>{visibility ? <Visibility /> : <VisibilityOff />}</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{isDeletable && (
|
||||
<Tooltip title={t('builder.common.tooltip.delete-section') as string}>
|
||||
<Tooltip title={t<string>('builder.common.tooltip.delete-section')}>
|
||||
<IconButton onClick={handleDelete}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
|
||||
@ -66,7 +66,7 @@ const List: React.FC<Props> = ({
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<div className={clsx(styles.container, className)}>
|
||||
{isEmpty(list) && <div className={styles.empty}>{t('builder.common.list.empty-text')}</div>}
|
||||
{isEmpty(list) && <div className={styles.empty}>{t<string>('builder.common.list.empty-text')}</div>}
|
||||
|
||||
{list.map((item, index) => {
|
||||
const title = get(item, titleKey, '');
|
||||
@ -76,6 +76,7 @@ const List: React.FC<Props> = ({
|
||||
return (
|
||||
<ListItem
|
||||
key={item.id}
|
||||
path={path}
|
||||
item={item}
|
||||
index={index}
|
||||
title={title}
|
||||
|
||||
@ -17,6 +17,7 @@ interface DragItem {
|
||||
|
||||
type Props = {
|
||||
item: ListItemType;
|
||||
path: string;
|
||||
index: number;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
@ -26,14 +27,14 @@ type Props = {
|
||||
onDuplicate?: (item: ListItemType) => void;
|
||||
};
|
||||
|
||||
const ListItem: React.FC<Props> = ({ item, index, title, subtitle, onMove, onEdit, onDelete, onDuplicate }) => {
|
||||
const ListItem: React.FC<Props> = ({ item, path, index, title, subtitle, onMove, onEdit, onDelete, onDuplicate }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
|
||||
|
||||
const [{ handlerId }, drop] = useDrop<DragItem, any, any>({
|
||||
accept: 'ListItem',
|
||||
accept: path,
|
||||
collect(monitor) {
|
||||
return { handlerId: monitor.getHandlerId() };
|
||||
},
|
||||
@ -68,7 +69,7 @@ const ListItem: React.FC<Props> = ({ item, index, title, subtitle, onMove, onEdi
|
||||
});
|
||||
|
||||
const [{ isDragging }, drag] = useDrag({
|
||||
type: 'ListItem',
|
||||
type: path,
|
||||
item: () => {
|
||||
return { id: item.id, index };
|
||||
},
|
||||
@ -125,25 +126,25 @@ const ListItem: React.FC<Props> = ({ item, index, title, subtitle, onMove, onEdi
|
||||
<ListItemIcon>
|
||||
<DriveFileRenameOutline className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('builder.common.list.actions.edit')}</ListItemText>
|
||||
<ListItemText>{t<string>('builder.common.list.actions.edit')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={() => handleDuplicate(item)}>
|
||||
<ListItemIcon>
|
||||
<FileCopy className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('builder.common.list.actions.duplicate')}</ListItemText>
|
||||
<ListItemText>{t<string>('builder.common.list.actions.duplicate')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Tooltip arrow placement="right" title={t('builder.common.tooltip.delete-item') as string}>
|
||||
<Tooltip arrow placement="right" title={t<string>('builder.common.tooltip.delete-item')}>
|
||||
<div>
|
||||
<MenuItem onClick={() => handleDelete(item)}>
|
||||
<ListItemIcon>
|
||||
<DeleteOutline className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>{t('builder.common.list.actions.delete')}</ListItemText>
|
||||
<ListItemText>{t<string>('builder.common.list.actions.delete')}</ListItemText>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const NoSSR: React.FC = ({ children }) => <>{children}</>;
|
||||
|
||||
export default dynamic(() => Promise.resolve(NoSSR), { ssr: false });
|
||||
@ -1,4 +1,7 @@
|
||||
import { TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import dayjs from 'dayjs';
|
||||
import { isEmpty } from 'lodash';
|
||||
import get from 'lodash/get';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
@ -8,7 +11,7 @@ import { setResumeState } from '@/store/resume/resumeSlice';
|
||||
import MarkdownSupported from './MarkdownSupported';
|
||||
|
||||
interface Props {
|
||||
type?: 'text' | 'textarea';
|
||||
type?: 'text' | 'textarea' | 'date';
|
||||
label: string;
|
||||
path: string;
|
||||
className?: string;
|
||||
@ -31,6 +34,11 @@ const ResumeInput: React.FC<Props> = ({ type = 'text', label, path, className, m
|
||||
dispatch(setResumeState({ path, value: event.target.value }));
|
||||
};
|
||||
|
||||
const onChangeValue = (value: string) => {
|
||||
setValue(value);
|
||||
dispatch(setResumeState({ path, value }));
|
||||
};
|
||||
|
||||
if (type === 'textarea') {
|
||||
return (
|
||||
<TextField
|
||||
@ -45,6 +53,22 @@ const ResumeInput: React.FC<Props> = ({ type = 'text', label, path, className, m
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'date') {
|
||||
return (
|
||||
<DatePicker
|
||||
openTo="year"
|
||||
label={label}
|
||||
value={value}
|
||||
views={['year', 'month', 'day']}
|
||||
renderInput={(params) => <TextField {...params} error={false} className={className} />}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
isEmpty(keyboardInputValue) && onChangeValue('');
|
||||
date && dayjs(date).utc().isValid() && onChangeValue(dayjs(date).utc().toISOString());
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <TextField type={type} label={label} value={value} onChange={onChange} className={className} />;
|
||||
};
|
||||
|
||||
|
||||
@ -6,20 +6,30 @@ export type Language = {
|
||||
|
||||
export const languages: Language[] = [
|
||||
{ code: 'ar', name: 'Arabic', localName: 'اَلْعَرَبِيَّةُ' },
|
||||
{ code: 'bg', name: 'Bulgarian', localName: 'български' },
|
||||
{ code: 'bn', name: 'Bengali', localName: 'বাংলা' },
|
||||
{ code: 'cs', name: 'Czech', localName: 'čeština' },
|
||||
{ code: 'da', name: 'Danish', localName: 'Dansk' },
|
||||
{ code: 'de', name: 'German', localName: 'Deutsch' },
|
||||
{ code: 'el', name: 'Greek', localName: 'Ελληνικά' },
|
||||
{ code: 'en', name: 'English' },
|
||||
{ code: 'es', name: 'Spanish', localName: 'Español' },
|
||||
{ code: 'fi', name: 'Finnish', localName: 'Suomi' },
|
||||
{ code: 'fr', name: 'French', localName: 'Français' },
|
||||
{ code: 'he', name: 'Hebrew', localName: 'Ivrit' },
|
||||
{ code: 'hi', name: 'Hindi', localName: 'हिन्दी' },
|
||||
{ code: 'hu', name: 'Hungarian', localName: 'Magyar' },
|
||||
{ code: 'id', name: 'Indonesian', localName: 'Bahasa Indonesia' },
|
||||
{ code: 'it', name: 'Italian', localName: 'Italiano' },
|
||||
{ code: 'kn', name: 'Kannada', localName: 'ಕನ್ನಡ' },
|
||||
{ code: 'ml', name: 'Malayalam', localName: 'മലയാളം' },
|
||||
{ code: 'nl', name: 'Dutch', localName: 'Nederlands' },
|
||||
{ code: 'or', name: 'Odia', localName: 'ଓଡ଼ିଆ' },
|
||||
{ code: 'fa', name: 'Persian', localName: 'Farsi' },
|
||||
{ code: 'pl', name: 'Polish', localName: 'Polski' },
|
||||
{ code: 'pt', name: 'Portuguese', localName: 'Português' },
|
||||
{ code: 'ru', name: 'Russian', localName: 'русский' },
|
||||
{ code: 'sv', name: 'Swedish', localName: 'Svenska' },
|
||||
{ code: 'ta', name: 'Tamil', localName: 'தமிழ்' },
|
||||
{ code: 'tr', name: 'Turkish', localName: 'Türkçe' },
|
||||
{ code: 'vi', name: 'Vietnamese', localName: 'Tiếng Việt' },
|
||||
|
||||
@ -54,16 +54,16 @@ const ForgotPasswordModal: React.FC = () => {
|
||||
<BaseModal
|
||||
icon={<Password />}
|
||||
isOpen={isOpen}
|
||||
heading={t('modals.auth.forgot-password.heading')}
|
||||
heading={t<string>('modals.auth.forgot-password.heading')}
|
||||
handleClose={handleClose}
|
||||
footerChildren={
|
||||
<Button type="submit" disabled={isLoading} onClick={handleSubmit(onSubmit)}>
|
||||
{t('modals.auth.forgot-password.actions.send-email')}
|
||||
{t<string>('modals.auth.forgot-password.actions.send-email')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<div className="grid gap-4">
|
||||
<p>{t('modals.auth.forgot-password.body')}</p>
|
||||
<p>{t<string>('modals.auth.forgot-password.body')}</p>
|
||||
|
||||
<form className="grid gap-4 xl:w-2/3">
|
||||
<Controller
|
||||
@ -72,7 +72,7 @@ const ForgotPasswordModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
autoFocus
|
||||
label={t('modals.auth.forgot-password.form.email.label')}
|
||||
label={t<string>('modals.auth.forgot-password.form.email.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -81,7 +81,7 @@ const ForgotPasswordModal: React.FC = () => {
|
||||
/>
|
||||
</form>
|
||||
|
||||
<p className="text-xs">{t('modals.auth.forgot-password.help-text')}</p>
|
||||
<p className="text-xs">{t<string>('modals.auth.forgot-password.help-text')}</p>
|
||||
</div>
|
||||
</BaseModal>
|
||||
</>
|
||||
|
||||
@ -6,7 +6,6 @@ import Joi from 'joi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Trans, useTranslation } from 'next-i18next';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { GoogleLoginResponse, GoogleLoginResponseOffline, useGoogleLogin } from 'react-google-login';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useIsMutating, useMutation } from 'react-query';
|
||||
@ -18,6 +17,8 @@ import { ServerError } from '@/services/axios';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
|
||||
declare const google: any;
|
||||
|
||||
type FormData = {
|
||||
identifier: string;
|
||||
password: string;
|
||||
@ -56,15 +57,6 @@ const LoginModal: React.FC = () => {
|
||||
loginWithGoogle
|
||||
);
|
||||
|
||||
const { signIn } = useGoogleLogin({
|
||||
clientId: env('GOOGLE_CLIENT_ID'),
|
||||
onSuccess: async (response: GoogleLoginResponse | GoogleLoginResponseOffline) => {
|
||||
await loginWithGoogleMutation({ accessToken: (response as GoogleLoginResponse).accessToken });
|
||||
|
||||
handleClose();
|
||||
},
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
dispatch(setModalState({ modal: 'auth.login', state: { open: false } }));
|
||||
reset();
|
||||
@ -93,8 +85,28 @@ const LoginModal: React.FC = () => {
|
||||
dispatch(setModalState({ modal: 'auth.forgot', state: { open: true } }));
|
||||
};
|
||||
|
||||
const handleLoginWithGoogle = () => {
|
||||
signIn();
|
||||
const handleLoginWithGoogle = async () => {
|
||||
console.log(process.env.PUBLIC_GOOGLE_CLIENT_ID, env('GOOGLE_CLIENT_ID'));
|
||||
|
||||
google.accounts.id.initialize({
|
||||
auto_select: true,
|
||||
itp_support: true,
|
||||
client_id: env('GOOGLE_CLIENT_ID'),
|
||||
callback: async (response: any) => {
|
||||
await loginWithGoogleMutation({ credential: response.credential });
|
||||
|
||||
handleClose();
|
||||
},
|
||||
});
|
||||
|
||||
google.accounts.id.prompt((notification: any) => {
|
||||
if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
|
||||
const reason = notification.getNotDisplayedReason() || notification.getSkippedReason();
|
||||
|
||||
toast.error(`Google returned an error while trying to sign in: ${reason}.`);
|
||||
toast("Please try logging in using email/password, or use another browser that supports Google's One Tap API.");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const PasswordVisibility = (): React.ReactElement => {
|
||||
@ -113,7 +125,7 @@ const LoginModal: React.FC = () => {
|
||||
<BaseModal
|
||||
icon={<Login />}
|
||||
isOpen={isOpen}
|
||||
heading={t('modals.auth.login.heading')}
|
||||
heading={t<string>('modals.auth.login.heading')}
|
||||
handleClose={handleClose}
|
||||
footerChildren={
|
||||
<div className="flex gap-4">
|
||||
@ -125,17 +137,17 @@ const LoginModal: React.FC = () => {
|
||||
startIcon={<Google />}
|
||||
onClick={handleLoginWithGoogle}
|
||||
>
|
||||
{t('modals.auth.login.actions.google')}
|
||||
{t<string>('modals.auth.login.actions.google')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
|
||||
{t('modals.auth.login.actions.login')}
|
||||
{t<string>('modals.auth.login.actions.login')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<p>{t('modals.auth.login.body')}</p>
|
||||
<p>{t<string>('modals.auth.login.body')}</p>
|
||||
|
||||
<form className="grid gap-4 xl:w-2/3">
|
||||
<Controller
|
||||
@ -144,9 +156,9 @@ const LoginModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
autoFocus
|
||||
label={t('modals.auth.login.form.username.label')}
|
||||
label={t<string>('modals.auth.login.form.username.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || t('modals.auth.login.form.username.help-text')}
|
||||
helperText={fieldState.error?.message || t<string>('modals.auth.login.form.username.help-text')}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
@ -158,7 +170,7 @@ const LoginModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
label={t('modals.auth.login.form.password.label')}
|
||||
label={t<string>('modals.auth.login.form.password.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
InputProps={{ endAdornment: <PasswordVisibility /> }}
|
||||
|
||||
@ -5,7 +5,6 @@ import { Button, TextField } from '@mui/material';
|
||||
import Joi from 'joi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Trans, useTranslation } from 'next-i18next';
|
||||
import { GoogleLoginResponse, GoogleLoginResponseOffline, useGoogleLogin } from 'react-google-login';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useMutation } from 'react-query';
|
||||
|
||||
@ -15,6 +14,8 @@ import { ServerError } from '@/services/axios';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
|
||||
declare const google: any;
|
||||
|
||||
type FormData = {
|
||||
name: string;
|
||||
username: string;
|
||||
@ -63,15 +64,6 @@ const RegisterModal: React.FC = () => {
|
||||
loginWithGoogle
|
||||
);
|
||||
|
||||
const { signIn } = useGoogleLogin({
|
||||
clientId: env('GOOGLE_CLIENT_ID'),
|
||||
onSuccess: async (response: GoogleLoginResponse | GoogleLoginResponseOffline) => {
|
||||
await loginWithGoogleMutation({ accessToken: (response as GoogleLoginResponse).accessToken });
|
||||
|
||||
handleClose();
|
||||
},
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
dispatch(setModalState({ modal: 'auth.register', state: { open: false } }));
|
||||
reset();
|
||||
@ -87,15 +79,25 @@ const RegisterModal: React.FC = () => {
|
||||
dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
|
||||
};
|
||||
|
||||
const handleLoginWithGoogle = () => {
|
||||
signIn();
|
||||
const handleLoginWithGoogle = async () => {
|
||||
google.accounts.id.initialize({
|
||||
client_id: env('GOOGLE_CLIENT_ID'),
|
||||
callback: async (response: any) => {
|
||||
await loginWithGoogleMutation({ credential: response.credential });
|
||||
|
||||
handleClose();
|
||||
},
|
||||
auto_select: false,
|
||||
});
|
||||
|
||||
google.accounts.id.prompt();
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseModal
|
||||
icon={<HowToReg />}
|
||||
isOpen={isOpen}
|
||||
heading={t('modals.auth.register.heading')}
|
||||
heading={t<string>('modals.auth.register.heading')}
|
||||
handleClose={handleClose}
|
||||
footerChildren={
|
||||
<div className="flex gap-4">
|
||||
@ -107,17 +109,17 @@ const RegisterModal: React.FC = () => {
|
||||
startIcon={<Google />}
|
||||
onClick={handleLoginWithGoogle}
|
||||
>
|
||||
{t('modals.auth.register.actions.google')}
|
||||
{t<string>('modals.auth.register.actions.google')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
|
||||
{t('modals.auth.register.actions.register')}
|
||||
{t<string>('modals.auth.register.actions.register')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<p>{t('modals.auth.register.body')}</p>
|
||||
<p>{t<string>('modals.auth.register.body')}</p>
|
||||
|
||||
<form className="grid gap-4 md:grid-cols-2">
|
||||
<Controller
|
||||
@ -126,7 +128,7 @@ const RegisterModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
autoFocus
|
||||
label={t('modals.auth.register.form.name.label')}
|
||||
label={t<string>('modals.auth.register.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -139,7 +141,7 @@ const RegisterModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('modals.auth.register.form.username.label')}
|
||||
label={t<string>('modals.auth.register.form.username.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -153,7 +155,7 @@ const RegisterModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
type="email"
|
||||
label={t('modals.auth.register.form.email.label')}
|
||||
label={t<string>('modals.auth.register.form.email.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
@ -168,7 +170,7 @@ const RegisterModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
type="password"
|
||||
label={t('modals.auth.register.form.password.label')}
|
||||
label={t<string>('modals.auth.register.form.password.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -182,7 +184,7 @@ const RegisterModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
type="password"
|
||||
label={t('modals.auth.register.form.confirm-password.label')}
|
||||
label={t<string>('modals.auth.register.form.confirm-password.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
|
||||
@ -65,15 +65,15 @@ const ResetPasswordModal: React.FC = () => {
|
||||
<BaseModal
|
||||
icon={<LockReset />}
|
||||
isOpen={isOpen}
|
||||
heading={t('modals.auth.reset-password.heading')}
|
||||
heading={t<string>('modals.auth.reset-password.heading')}
|
||||
handleClose={handleClose}
|
||||
footerChildren={
|
||||
<Button type="submit" disabled={isLoading} onClick={handleSubmit(onSubmit)}>
|
||||
{t('modals.auth.reset-password.actions.set-password')}
|
||||
{t<string>('modals.auth.reset-password.actions.set-password')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<p>{t('modals.auth.reset-password.body')}</p>
|
||||
<p>{t<string>('modals.auth.reset-password.body')}</p>
|
||||
|
||||
<form className="grid gap-4 md:grid-cols-2">
|
||||
<Controller
|
||||
@ -83,7 +83,7 @@ const ResetPasswordModal: React.FC = () => {
|
||||
<TextField
|
||||
autoFocus
|
||||
type="password"
|
||||
label={t('modals.auth.reset-password.form.password.label')}
|
||||
label={t<string>('modals.auth.reset-password.form.password.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -97,7 +97,7 @@ const ResetPasswordModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
type="password"
|
||||
label={t('modals.auth.reset-password.form.confirm-password.label')}
|
||||
label={t<string>('modals.auth.reset-password.form.confirm-password.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { joiResolver } from '@hookform/resolvers/joi';
|
||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||
import DatePicker from '@mui/lab/DatePicker';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import { Award, SectionPath } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import Joi from 'joi';
|
||||
@ -50,8 +50,8 @@ const AwardModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -101,7 +101,7 @@ const AwardModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.title.label')}
|
||||
label={t<string>('builder.common.form.title.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -115,7 +115,7 @@ const AwardModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.leftSidebar.sections.awards.form.awarder.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.awards.form.awarder.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -130,11 +130,11 @@ const AwardModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.date.label')}
|
||||
label={t<string>('builder.common.form.date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -152,7 +152,7 @@ const AwardModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
@ -169,7 +169,7 @@ const AwardModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { joiResolver } from '@hookform/resolvers/joi';
|
||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||
import DatePicker from '@mui/lab/DatePicker';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import { Certificate, SectionPath } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import Joi from 'joi';
|
||||
@ -50,8 +50,8 @@ const CertificateModal: React.FC = () => {
|
||||
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -101,7 +101,7 @@ const CertificateModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.name.label')}
|
||||
label={t<string>('builder.common.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -115,7 +115,7 @@ const CertificateModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.leftSidebar.sections.certifications.form.issuer.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.certifications.form.issuer.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -130,11 +130,11 @@ const CertificateModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.date.label')}
|
||||
label={t<string>('builder.common.form.date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -152,7 +152,7 @@ const CertificateModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
@ -169,7 +169,7 @@ const CertificateModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { joiResolver } from '@hookform/resolvers/joi';
|
||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||
import DatePicker from '@mui/lab/DatePicker';
|
||||
import { Button, Slider, TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import { Custom } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import Joi from 'joi';
|
||||
@ -67,8 +67,8 @@ const CustomModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -118,7 +118,7 @@ const CustomModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.title.label')}
|
||||
label={t<string>('builder.common.form.title.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -131,7 +131,7 @@ const CustomModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.subtitle.label')}
|
||||
label={t<string>('builder.common.form.subtitle.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -146,11 +146,11 @@ const CustomModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.start-date.label')}
|
||||
label={t<string>('builder.common.form.start-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -170,11 +170,11 @@ const CustomModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.end-date.label')}
|
||||
label={t<string>('builder.common.form.end-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -192,7 +192,7 @@ const CustomModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
@ -207,7 +207,7 @@ const CustomModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.level.label')}
|
||||
label={t<string>('builder.common.form.level.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
@ -221,7 +221,7 @@ const CustomModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="col-span-2">
|
||||
<h4 className="mb-3 font-semibold">{t('builder.common.form.levelNum.label')}</h4>
|
||||
<h4 className="mb-3 font-semibold">{t<string>('builder.common.form.levelNum.label')}</h4>
|
||||
|
||||
<div className="px-10">
|
||||
<Slider
|
||||
@ -245,7 +245,7 @@ const CustomModal: React.FC = () => {
|
||||
defaultValue={0}
|
||||
color="secondary"
|
||||
valueLabelDisplay="auto"
|
||||
aria-label={t('builder.common.form.levelNum.label')}
|
||||
aria-label={t<string>('builder.common.form.levelNum.label')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -260,7 +260,7 @@ const CustomModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
@ -274,7 +274,7 @@ const CustomModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<ArrayInput
|
||||
label={t('builder.common.form.keywords.label')}
|
||||
label={t<string>('builder.common.form.keywords.label')}
|
||||
value={field.value as string[]}
|
||||
onChange={field.onChange}
|
||||
errors={fieldState.error}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { joiResolver } from '@hookform/resolvers/joi';
|
||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||
import DatePicker from '@mui/lab/DatePicker';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import { Education, SectionPath } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import Joi from 'joi';
|
||||
@ -63,8 +63,8 @@ const EducationModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -114,7 +114,7 @@ const EducationModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.leftSidebar.sections.education.form.institution.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.education.form.institution.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -128,7 +128,7 @@ const EducationModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.leftSidebar.sections.education.form.degree.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.education.form.degree.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -141,7 +141,7 @@ const EducationModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.leftSidebar.sections.education.form.area-study.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.education.form.area-study.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -154,7 +154,7 @@ const EducationModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.leftSidebar.sections.education.form.grade.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.education.form.grade.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -169,11 +169,11 @@ const EducationModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.start-date.label')}
|
||||
label={t<string>('builder.common.form.start-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -193,17 +193,17 @@ const EducationModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.end-date.label')}
|
||||
label={t<string>('builder.common.form.end-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
{...params}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || t('builder.common.form.end-date.help-text')}
|
||||
helperText={fieldState.error?.message || t<string>('builder.common.form.end-date.help-text')}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -215,7 +215,7 @@ const EducationModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
@ -233,7 +233,7 @@ const EducationModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
@ -247,7 +247,7 @@ const EducationModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<ArrayInput
|
||||
label={t('builder.leftSidebar.sections.education.form.courses.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.education.form.courses.label')}
|
||||
value={field.value as string[]}
|
||||
onChange={field.onChange}
|
||||
errors={fieldState.error}
|
||||
|
||||
@ -41,8 +41,8 @@ const InterestModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -92,7 +92,7 @@ const InterestModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.name.label')}
|
||||
label={t<string>('builder.common.form.name.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
@ -106,7 +106,7 @@ const InterestModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<ArrayInput
|
||||
label={t('builder.common.form.keywords.label')}
|
||||
label={t<string>('builder.common.form.keywords.label')}
|
||||
value={field.value as string[]}
|
||||
onChange={field.onChange}
|
||||
errors={fieldState.error}
|
||||
|
||||
@ -42,8 +42,8 @@ const LanguageModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -93,7 +93,7 @@ const LanguageModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.name.label')}
|
||||
label={t<string>('builder.common.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -107,7 +107,7 @@ const LanguageModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.common.form.level.label')}
|
||||
label={t<string>('builder.common.form.level.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -120,7 +120,7 @@ const LanguageModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="col-span-2">
|
||||
<h4 className="mb-3 font-semibold">{t('builder.common.form.levelNum.label')}</h4>
|
||||
<h4 className="mb-3 font-semibold">{t<string>('builder.common.form.levelNum.label')}</h4>
|
||||
|
||||
<div className="px-10">
|
||||
<Slider
|
||||
@ -144,7 +144,7 @@ const LanguageModal: React.FC = () => {
|
||||
defaultValue={0}
|
||||
color="secondary"
|
||||
valueLabelDisplay="auto"
|
||||
aria-label={t('builder.common.form.levelNum.label')}
|
||||
aria-label={t<string>('builder.common.form.levelNum.label')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -42,11 +42,11 @@ const ProfileModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = t('builder.common.actions.add', {
|
||||
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
|
||||
const addText = t<string>('builder.common.actions.add', {
|
||||
token: t<string>('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
|
||||
});
|
||||
const editText = t('builder.common.actions.edit', {
|
||||
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
|
||||
const editText = t<string>('builder.common.actions.edit', {
|
||||
token: t<string>('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
|
||||
});
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
@ -97,7 +97,7 @@ const ProfileModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.leftSidebar.sections.profiles.form.network.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.profiles.form.network.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -111,7 +111,7 @@ const ProfileModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.leftSidebar.sections.profiles.form.username.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.profiles.form.username.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
InputProps={{
|
||||
@ -127,7 +127,7 @@ const ProfileModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
className="col-span-2"
|
||||
placeholder="https://"
|
||||
error={!!fieldState.error}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { joiResolver } from '@hookform/resolvers/joi';
|
||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||
import DatePicker from '@mui/lab/DatePicker';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import { Project, SectionPath } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import Joi from 'joi';
|
||||
@ -59,8 +59,8 @@ const ProjectModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -110,7 +110,7 @@ const ProjectModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.name.label')}
|
||||
label={t<string>('builder.common.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -124,7 +124,7 @@ const ProjectModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.common.form.description.label')}
|
||||
label={t<string>('builder.common.form.description.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -139,11 +139,11 @@ const ProjectModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.start-date.label')}
|
||||
label={t<string>('builder.common.form.start-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -163,11 +163,11 @@ const ProjectModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.end-date.label')}
|
||||
label={t<string>('builder.common.form.end-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -185,7 +185,7 @@ const ProjectModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
@ -203,7 +203,7 @@ const ProjectModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
@ -217,7 +217,7 @@ const ProjectModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<ArrayInput
|
||||
label={t('builder.common.form.keywords.label')}
|
||||
label={t<string>('builder.common.form.keywords.label')}
|
||||
value={field.value as string[]}
|
||||
onChange={field.onChange}
|
||||
errors={fieldState.error}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { joiResolver } from '@hookform/resolvers/joi';
|
||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||
import DatePicker from '@mui/lab/DatePicker';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import { Publication, SectionPath } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import Joi from 'joi';
|
||||
@ -50,8 +50,8 @@ const PublicationModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -101,7 +101,7 @@ const PublicationModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.name.label')}
|
||||
label={t<string>('builder.common.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -115,7 +115,7 @@ const PublicationModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.leftSidebar.sections.publications.form.publisher.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.publications.form.publisher.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -130,11 +130,11 @@ const PublicationModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.date.label')}
|
||||
label={t<string>('builder.common.form.date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -152,7 +152,7 @@ const PublicationModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
@ -169,7 +169,7 @@ const PublicationModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
|
||||
@ -47,8 +47,8 @@ const ReferenceModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -98,7 +98,7 @@ const ReferenceModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.name.label')}
|
||||
label={t<string>('builder.common.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -112,7 +112,7 @@ const ReferenceModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.leftSidebar.sections.references.form.relationship.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.references.form.relationship.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -125,7 +125,7 @@ const ReferenceModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.phone.label')}
|
||||
label={t<string>('builder.common.form.phone.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -138,7 +138,7 @@ const ReferenceModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.email.label')}
|
||||
label={t<string>('builder.common.form.email.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -154,7 +154,7 @@ const ReferenceModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
|
||||
@ -45,8 +45,8 @@ const SkillModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -96,7 +96,7 @@ const SkillModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.name.label')}
|
||||
label={t<string>('builder.common.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -109,7 +109,7 @@ const SkillModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.level.label')}
|
||||
label={t<string>('builder.common.form.level.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -122,7 +122,7 @@ const SkillModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="col-span-2">
|
||||
<h4 className="mb-3 font-semibold">{t('builder.common.form.levelNum.label')}</h4>
|
||||
<h4 className="mb-3 font-semibold">{t<string>('builder.common.form.levelNum.label')}</h4>
|
||||
|
||||
<div className="px-3">
|
||||
<Slider
|
||||
@ -146,7 +146,7 @@ const SkillModal: React.FC = () => {
|
||||
defaultValue={0}
|
||||
color="secondary"
|
||||
valueLabelDisplay="auto"
|
||||
aria-label={t('builder.common.form.levelNum.label')}
|
||||
aria-label={t<string>('builder.common.form.levelNum.label')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -158,7 +158,7 @@ const SkillModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<ArrayInput
|
||||
label={t('builder.common.form.keywords.label')}
|
||||
label={t<string>('builder.common.form.keywords.label')}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
errors={fieldState.error}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { joiResolver } from '@hookform/resolvers/joi';
|
||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||
import DatePicker from '@mui/lab/DatePicker';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import { SectionPath, Volunteer } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import Joi from 'joi';
|
||||
@ -56,8 +56,8 @@ const VolunteerModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -107,7 +107,7 @@ const VolunteerModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.leftSidebar.sections.volunteer.form.organization.label')}
|
||||
label={t<string>('builder.leftSidebar.sections.volunteer.form.organization.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -121,7 +121,7 @@ const VolunteerModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.common.form.position.label')}
|
||||
label={t<string>('builder.common.form.position.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -136,11 +136,11 @@ const VolunteerModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.start-date.label')}
|
||||
label={t<string>('builder.common.form.start-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -160,11 +160,11 @@ const VolunteerModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.end-date.label')}
|
||||
label={t<string>('builder.common.form.end-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -182,7 +182,7 @@ const VolunteerModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
@ -200,7 +200,7 @@ const VolunteerModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { joiResolver } from '@hookform/resolvers/joi';
|
||||
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
|
||||
import DatePicker from '@mui/lab/DatePicker';
|
||||
import { Button, TextField } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
import { SectionPath, WorkExperience } from '@reactive-resume/schema';
|
||||
import dayjs from 'dayjs';
|
||||
import Joi from 'joi';
|
||||
@ -56,8 +56,8 @@ const WorkModal: React.FC = () => {
|
||||
const item: FormData = get(payload, 'item', null);
|
||||
const isEditMode = useMemo(() => !!item, [item]);
|
||||
|
||||
const addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
const addText = useMemo(() => t<string>('builder.common.actions.add', { token: heading }), [t, heading]);
|
||||
const editText = useMemo(() => t<string>('builder.common.actions.edit', { token: heading }), [t, heading]);
|
||||
|
||||
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||
defaultValues: defaultState,
|
||||
@ -107,7 +107,7 @@ const WorkModal: React.FC = () => {
|
||||
<TextField
|
||||
required
|
||||
autoFocus
|
||||
label={t('builder.common.form.name.label')}
|
||||
label={t<string>('builder.common.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -121,7 +121,7 @@ const WorkModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
required
|
||||
label={t('builder.common.form.position.label')}
|
||||
label={t<string>('builder.common.form.position.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -136,11 +136,11 @@ const WorkModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.start-date.label')}
|
||||
label={t<string>('builder.common.form.start-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
@ -160,17 +160,17 @@ const WorkModal: React.FC = () => {
|
||||
<DatePicker
|
||||
{...field}
|
||||
openTo="year"
|
||||
label={t('builder.common.form.end-date.label')}
|
||||
label={t<string>('builder.common.form.end-date.label')}
|
||||
views={['year', 'month', 'day']}
|
||||
onChange={(date: Date | null, keyboardInputValue: string | undefined) => {
|
||||
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) => (
|
||||
<TextField
|
||||
{...params}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || t('builder.common.form.end-date.help-text')}
|
||||
helperText={fieldState.error?.message || t<string>('builder.common.form.end-date.help-text')}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -182,7 +182,7 @@ const WorkModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
label={t<string>('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
@ -200,7 +200,7 @@ const WorkModal: React.FC = () => {
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
label={t('builder.common.form.summary.label')}
|
||||
label={t<string>('builder.common.form.summary.label')}
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message || <MarkdownSupported />}
|
||||
|
||||
@ -85,15 +85,15 @@ const CreateResumeModal: React.FC = () => {
|
||||
<BaseModal
|
||||
isOpen={isOpen}
|
||||
icon={<Add />}
|
||||
heading={t('modals.dashboard.create-resume.heading')}
|
||||
heading={t<string>('modals.dashboard.create-resume.heading')}
|
||||
handleClose={handleClose}
|
||||
footerChildren={
|
||||
<Button type="submit" disabled={isLoading} onClick={handleSubmit(onSubmit)}>
|
||||
{t('modals.dashboard.create-resume.actions.create-resume')}
|
||||
{t<string>('modals.dashboard.create-resume.actions.create-resume')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<p>{t('modals.dashboard.create-resume.body')}</p>
|
||||
<p>{t<string>('modals.dashboard.create-resume.body')}</p>
|
||||
|
||||
<form className="grid gap-4">
|
||||
<Controller
|
||||
@ -102,7 +102,7 @@ const CreateResumeModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
autoFocus
|
||||
label={t('modals.dashboard.create-resume.form.name.label')}
|
||||
label={t<string>('modals.dashboard.create-resume.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -115,7 +115,7 @@ const CreateResumeModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('modals.dashboard.create-resume.form.slug.label')}
|
||||
label={t<string>('modals.dashboard.create-resume.form.slug.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -125,7 +125,7 @@ const CreateResumeModal: React.FC = () => {
|
||||
|
||||
<FormGroup>
|
||||
<FormControlLabel
|
||||
label={t('modals.dashboard.create-resume.form.public.label') as string}
|
||||
label={t<string>('modals.dashboard.create-resume.form.public.label')}
|
||||
control={
|
||||
<Controller
|
||||
name="isPublic"
|
||||
|
||||
@ -63,7 +63,7 @@ const ImportExternalModal: React.FC = () => {
|
||||
const file = event.target.files[0];
|
||||
|
||||
if (file.size > FILE_UPLOAD_MAX_SIZE) {
|
||||
toast.error(t('common.toast.error.upload-file-size'));
|
||||
toast.error(t<string>('common.toast.error.upload-file-size'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -78,13 +78,13 @@ const ImportExternalModal: React.FC = () => {
|
||||
<BaseModal
|
||||
isOpen={isOpen}
|
||||
icon={<ImportExport />}
|
||||
heading={t('modals.dashboard.import-external.heading')}
|
||||
heading={t<string>('modals.dashboard.import-external.heading')}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="grid gap-5">
|
||||
<h2 className="inline-flex items-center gap-2 text-lg font-medium">
|
||||
<LinkedIn />
|
||||
{t('modals.dashboard.import-external.linkedin.heading')}
|
||||
{t<string>('modals.dashboard.import-external.linkedin.heading')}
|
||||
</h2>
|
||||
|
||||
<p className="mb-2">
|
||||
@ -110,7 +110,7 @@ const ImportExternalModal: React.FC = () => {
|
||||
startIcon={<UploadFile />}
|
||||
onClick={() => handleClick('linkedin')}
|
||||
>
|
||||
{t('modals.dashboard.import-external.linkedin.actions.upload-archive')}
|
||||
{t<string>('modals.dashboard.import-external.linkedin.actions.upload-archive')}
|
||||
</Button>
|
||||
|
||||
<input
|
||||
@ -128,7 +128,7 @@ const ImportExternalModal: React.FC = () => {
|
||||
<div className="grid gap-5">
|
||||
<h2 className="inline-flex items-center gap-2 text-lg font-medium">
|
||||
<Code />
|
||||
{t('modals.dashboard.import-external.json-resume.heading')}
|
||||
{t<string>('modals.dashboard.import-external.json-resume.heading')}
|
||||
</h2>
|
||||
|
||||
<p className="mb-2">
|
||||
@ -154,7 +154,7 @@ const ImportExternalModal: React.FC = () => {
|
||||
startIcon={<UploadFile />}
|
||||
onClick={() => handleClick('json-resume')}
|
||||
>
|
||||
{t('modals.dashboard.import-external.json-resume.actions.upload-json')}
|
||||
{t<string>('modals.dashboard.import-external.json-resume.actions.upload-json')}
|
||||
</Button>
|
||||
|
||||
<input
|
||||
@ -172,10 +172,10 @@ const ImportExternalModal: React.FC = () => {
|
||||
<div className="grid gap-5">
|
||||
<h2 className="inline-flex items-center gap-2 text-lg font-medium">
|
||||
<TrackChanges />
|
||||
{t('modals.dashboard.import-external.reactive-resume.heading')}
|
||||
{t<string>('modals.dashboard.import-external.reactive-resume.heading')}
|
||||
</h2>
|
||||
|
||||
<p className="mb-2">{t('modals.dashboard.import-external.reactive-resume.body')}</p>
|
||||
<p className="mb-2">{t<string>('modals.dashboard.import-external.reactive-resume.body')}</p>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
@ -184,7 +184,7 @@ const ImportExternalModal: React.FC = () => {
|
||||
startIcon={<UploadFile />}
|
||||
onClick={() => handleClick('reactive-resume')}
|
||||
>
|
||||
{t('modals.dashboard.import-external.reactive-resume.actions.upload-json')}
|
||||
{t<string>('modals.dashboard.import-external.reactive-resume.actions.upload-json')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@ -193,7 +193,7 @@ const ImportExternalModal: React.FC = () => {
|
||||
startIcon={<UploadFile />}
|
||||
onClick={() => handleClick('reactive-resume-v2')}
|
||||
>
|
||||
{t('modals.dashboard.import-external.reactive-resume.actions.upload-json-v2')}
|
||||
{t<string>('modals.dashboard.import-external.reactive-resume.actions.upload-json-v2')}
|
||||
</Button>
|
||||
|
||||
<input
|
||||
|
||||
@ -92,11 +92,11 @@ const RenameResumeModal: React.FC = () => {
|
||||
<BaseModal
|
||||
icon={<DriveFileRenameOutline />}
|
||||
isOpen={isOpen}
|
||||
heading={t('modals.dashboard.rename-resume.heading')}
|
||||
heading={t<string>('modals.dashboard.rename-resume.heading')}
|
||||
handleClose={handleClose}
|
||||
footerChildren={
|
||||
<Button type="submit" disabled={isLoading} onClick={handleSubmit(onSubmit)}>
|
||||
{t('modals.dashboard.rename-resume.actions.rename-resume')}
|
||||
{t<string>('modals.dashboard.rename-resume.actions.rename-resume')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
@ -107,7 +107,7 @@ const RenameResumeModal: React.FC = () => {
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
autoFocus
|
||||
label={t('modals.dashboard.rename-resume.form.name.label')}
|
||||
label={t<string>('modals.dashboard.rename-resume.form.name.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
@ -120,7 +120,7 @@ const RenameResumeModal: React.FC = () => {
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('modals.dashboard.rename-resume.form.slug.label')}
|
||||
label={t<string>('modals.dashboard.rename-resume.form.slug.label')}
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
|
||||
@ -5,20 +5,30 @@ const i18nConfig = {
|
||||
defaultLocale: 'en',
|
||||
locales: [
|
||||
'ar',
|
||||
'bg',
|
||||
'bn',
|
||||
'cs',
|
||||
'da',
|
||||
'de',
|
||||
'el',
|
||||
'en',
|
||||
'es',
|
||||
'fa',
|
||||
'fi',
|
||||
'fr',
|
||||
'he',
|
||||
'hi',
|
||||
'hu',
|
||||
'id',
|
||||
'it',
|
||||
'kn',
|
||||
'ml',
|
||||
'nl',
|
||||
'or',
|
||||
'pl',
|
||||
'pt',
|
||||
'ru',
|
||||
'sv',
|
||||
'ta',
|
||||
'tr',
|
||||
'vi',
|
||||
|
||||
@ -12,7 +12,7 @@ const nextConfig = {
|
||||
},
|
||||
|
||||
images: {
|
||||
domains: ['www.gravatar.com'],
|
||||
domains: ['cdn.rxresu.me', 'www.gravatar.com'],
|
||||
},
|
||||
|
||||
async rewrites() {
|
||||
|
||||
@ -9,70 +9,74 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@beam-australia/react-env": "^3.1.1",
|
||||
"@emotion/css": "^11.7.1",
|
||||
"@emotion/react": "^11.8.2",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@hookform/resolvers": "2.8.8",
|
||||
"@monaco-editor/react": "^4.3.1",
|
||||
"@mui/icons-material": "^5.5.1",
|
||||
"@mui/lab": "^5.0.0-alpha.74",
|
||||
"@mui/material": "^5.5.2",
|
||||
"@reduxjs/toolkit": "^1.8.0",
|
||||
"axios": "^0.26.1",
|
||||
"clsx": "^1.1.1",
|
||||
"dayjs": "^1.11.0",
|
||||
"@date-io/dayjs": "^2.15.0",
|
||||
"@emotion/css": "^11.10.0",
|
||||
"@emotion/react": "^11.10.0",
|
||||
"@emotion/styled": "^11.10.0",
|
||||
"@hookform/resolvers": "2.9.7",
|
||||
"@monaco-editor/react": "^4.4.5",
|
||||
"@mui/icons-material": "^5.8.4",
|
||||
"@mui/lab": "^5.0.0-alpha.94",
|
||||
"@mui/material": "^5.10.0",
|
||||
"@mui/system": "^5.10.0",
|
||||
"@mui/x-date-pickers": "5.0.0-beta.5",
|
||||
"@next/env": "^12.2.4",
|
||||
"@reduxjs/toolkit": "^1.8.4",
|
||||
"axios": "^0.27.2",
|
||||
"clsx": "^1.2.1",
|
||||
"dayjs": "^1.11.4",
|
||||
"downloadjs": "^1.4.7",
|
||||
"joi": "^17.6.0",
|
||||
"lodash": "^4.17.21",
|
||||
"md5-hex": "^4.0.0",
|
||||
"monaco-editor": "^0.33.0",
|
||||
"nanoid": "^3.3.1",
|
||||
"next": "12.1.0",
|
||||
"next-i18next": "^10.5.0",
|
||||
"react": ">=17",
|
||||
"monaco-editor": "^0.34.0",
|
||||
"nanoid": "^3.3.4",
|
||||
"next": "12.2.4",
|
||||
"next-i18next": "^11.3.0",
|
||||
"react": "18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.0",
|
||||
"react-colorful": "^5.5.1",
|
||||
"react-dnd": "^15.1.1",
|
||||
"react-dnd-html5-backend": "^15.1.2",
|
||||
"react-dom": ">=17",
|
||||
"react-google-login": "^5.2.2",
|
||||
"react-hook-form": "^7.28.1",
|
||||
"react-hot-toast": "2.2.0",
|
||||
"react-hotkeys-hook": "^3.4.4",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-markdown": "^8.0.1",
|
||||
"react-query": "^3.34.16",
|
||||
"react-redux": "^7.2.6",
|
||||
"react-colorful": "^5.6.0",
|
||||
"react-dnd": "16.0.1",
|
||||
"react-dnd-html5-backend": "16.0.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.34.0",
|
||||
"react-hot-toast": "2.3.0",
|
||||
"react-hotkeys-hook": "^3.4.7",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-query": "^3.39.2",
|
||||
"react-redux": "^8.0.2",
|
||||
"react-zoom-pan-pinch": "^2.1.3",
|
||||
"redux": "^4.1.2",
|
||||
"redux": "^4.2.0",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-saga": "^1.1.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "^0.30.3",
|
||||
"sharp": "^0.30.7",
|
||||
"uuid": "^8.3.2",
|
||||
"webfontloader": "^1.6.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.8",
|
||||
"@babel/core": "^7.18.10",
|
||||
"@reactive-resume/schema": "workspace:*",
|
||||
"@tailwindcss/typography": "^0.5.2",
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"@types/downloadjs": "^1.4.3",
|
||||
"@types/lodash": "^4.14.180",
|
||||
"@types/node": "17.0.23",
|
||||
"@types/react": "17.0.42",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/node": "18.7.1",
|
||||
"@types/react": "18.0.17",
|
||||
"@types/react-beautiful-dnd": "^13.1.2",
|
||||
"@types/react-redux": "^7.1.23",
|
||||
"@types/tailwindcss": "^3.0.9",
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"@types/tailwindcss": "^3.0.11",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/webfontloader": "^1.6.34",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-config-next": "12.1.0",
|
||||
"next-sitemap": "^2.5.13",
|
||||
"postcss": "^8.4.12",
|
||||
"prettier": "^2.6.0",
|
||||
"sass": "^1.49.9",
|
||||
"tailwindcss": "^3.0.23",
|
||||
"typescript": "<4.6.0"
|
||||
"autoprefixer": "^10.4.8",
|
||||
"csstype": "^3.1.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-next": "12.2.4",
|
||||
"next-sitemap": "^3.1.17",
|
||||
"postcss": "^8.4.16",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.54.4",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ const Build: NextPage<Props> = ({ username, slug }) => {
|
||||
<div className={styles.container}>
|
||||
<Head>
|
||||
<title>
|
||||
{resume.name} | {t('common.title')}
|
||||
{resume.name} | {t<string>('common.title')}
|
||||
</title>
|
||||
</Head>
|
||||
|
||||
|
||||
@ -59,6 +59,14 @@ const Preview: NextPage<Props> = ({ username, slug, resume: initialData }) => {
|
||||
}
|
||||
}, [dispatch, initialData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmpty(resume) && router.locale !== resume.metadata.locale) {
|
||||
const { pathname, asPath, query } = router;
|
||||
|
||||
router.push({ pathname, query }, asPath, { locale: resume.metadata.locale });
|
||||
}
|
||||
}, [resume, router]);
|
||||
|
||||
useQuery<Resume>(`resume/${username}/${slug}`, () => fetchResumeByIdentifier({ username, slug }), {
|
||||
initialData,
|
||||
retry: false,
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import '@/styles/globals.scss';
|
||||
|
||||
import DateAdapter from '@mui/lab/AdapterDayjs';
|
||||
import LocalizationProvider from '@mui/lab/LocalizationProvider';
|
||||
import DayjsAdapter from '@date-io/dayjs';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers';
|
||||
import type { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
import Script from 'next/script';
|
||||
import { appWithTranslation } from 'next-i18next';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { QueryClientProvider } from 'react-query';
|
||||
@ -31,7 +32,7 @@ const App: React.FC<AppProps> = ({ Component, pageProps }) => {
|
||||
</Head>
|
||||
|
||||
<ReduxProvider store={store}>
|
||||
<LocalizationProvider dateAdapter={DateAdapter}>
|
||||
<LocalizationProvider dateAdapter={DayjsAdapter}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<WrapperRegistry>
|
||||
@ -52,6 +53,8 @@ const App: React.FC<AppProps> = ({ Component, pageProps }) => {
|
||||
</PersistGate>
|
||||
</LocalizationProvider>
|
||||
</ReduxProvider>
|
||||
|
||||
<Script src="https://accounts.google.com/gsi/client" async defer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -33,7 +33,7 @@ const Dashboard: NextPage = () => {
|
||||
<div className={styles.container}>
|
||||
<Head>
|
||||
<title>
|
||||
{t('dashboard.title')} | {t('common.title')}
|
||||
{t<string>('dashboard.title')} | {t<string>('common.title')}
|
||||
</title>
|
||||
</Head>
|
||||
|
||||
@ -51,15 +51,15 @@ const Dashboard: NextPage = () => {
|
||||
<ResumeCard
|
||||
modal="dashboard.create-resume"
|
||||
icon={Add}
|
||||
title={t('dashboard.create-resume.title')}
|
||||
subtitle={t('dashboard.create-resume.subtitle')}
|
||||
title={t<string>('dashboard.create-resume.title')}
|
||||
subtitle={t<string>('dashboard.create-resume.subtitle')}
|
||||
/>
|
||||
|
||||
<ResumeCard
|
||||
modal="dashboard.import-external"
|
||||
icon={ImportExport}
|
||||
title={t('dashboard.import-external.title')}
|
||||
subtitle={t('dashboard.import-external.subtitle')}
|
||||
title={t<string>('dashboard.import-external.title')}
|
||||
subtitle={t<string>('dashboard.import-external.subtitle')}
|
||||
/>
|
||||
|
||||
{data.map((resume) => (
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { DarkMode, LightMode, Link as LinkIcon } from '@mui/icons-material';
|
||||
import { Masonry } from '@mui/lab';
|
||||
import { Button, IconButton } from '@mui/material';
|
||||
import { Button, IconButton, NoSsr } from '@mui/material';
|
||||
import type { GetStaticProps, NextPage } from 'next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
@ -11,7 +11,6 @@ import Testimony from '@/components/landing/Testimony';
|
||||
import Footer from '@/components/shared/Footer';
|
||||
import LanguageSwitcher from '@/components/shared/LanguageSwitcher';
|
||||
import Logo from '@/components/shared/Logo';
|
||||
import NoSSR from '@/components/shared/NoSSR';
|
||||
import { screenshots } from '@/config/screenshots';
|
||||
import { FLAG_DISABLE_SIGNUPS } from '@/constants/flags';
|
||||
import testimonials from '@/data/testimonials';
|
||||
@ -55,52 +54,52 @@ const Home: NextPage = () => {
|
||||
</div>
|
||||
|
||||
<div className={styles.main}>
|
||||
<h1>{t('common.title')}</h1>
|
||||
<h1>{t<string>('common.title')}</h1>
|
||||
|
||||
<h2>{t('common.subtitle')}</h2>
|
||||
<h2>{t<string>('common.subtitle')}</h2>
|
||||
|
||||
<NoSSR>
|
||||
<NoSsr>
|
||||
<div className={styles.buttonWrapper}>
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
<Link href="/dashboard" passHref>
|
||||
<Button>{t('landing.actions.app')}</Button>
|
||||
<Button>{t<string>('landing.actions.app')}</Button>
|
||||
</Link>
|
||||
|
||||
<Button variant="outlined" onClick={handleLogout}>
|
||||
{t('landing.actions.logout')}
|
||||
{t<string>('landing.actions.logout')}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button onClick={handleLogin}>{t('landing.actions.login')}</Button>
|
||||
<Button onClick={handleLogin}>{t<string>('landing.actions.login')}</Button>
|
||||
|
||||
<Button variant="outlined" onClick={handleRegister} disabled={FLAG_DISABLE_SIGNUPS}>
|
||||
{t('landing.actions.register')}
|
||||
{t<string>('landing.actions.register')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</NoSSR>
|
||||
</NoSsr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h6>{t('landing.summary.heading')}</h6>
|
||||
<h6>{t<string>('landing.summary.heading')}</h6>
|
||||
|
||||
<p>{t('landing.summary.body')}</p>
|
||||
<p>{t<string>('landing.summary.body')}</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h6>{t('landing.features.heading')}</h6>
|
||||
<h6>{t<string>('landing.features.heading')}</h6>
|
||||
|
||||
<ul className="list-inside list-disc leading-loose">
|
||||
<li>{t('landing.features.list.free')}</li>
|
||||
<li>{t('landing.features.list.ads')}</li>
|
||||
<li>{t('landing.features.list.tracking')}</li>
|
||||
<li>{t('landing.features.list.languages')}</li>
|
||||
<li>{t('landing.features.list.import')}</li>
|
||||
<li>{t('landing.features.list.export')}</li>
|
||||
<li>{t<string>('landing.features.list.free')}</li>
|
||||
<li>{t<string>('landing.features.list.ads')}</li>
|
||||
<li>{t<string>('landing.features.list.tracking')}</li>
|
||||
<li>{t<string>('landing.features.list.languages')}</li>
|
||||
<li>{t<string>('landing.features.list.import')}</li>
|
||||
<li>{t<string>('landing.features.list.export')}</li>
|
||||
<li>
|
||||
<Trans t={t} i18nKey="landing.features.list.more">
|
||||
And a lot of exciting features,
|
||||
@ -113,7 +112,7 @@ const Home: NextPage = () => {
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h6>{t('landing.screenshots.heading')}</h6>
|
||||
<h6>{t<string>('landing.screenshots.heading')}</h6>
|
||||
|
||||
<div className={styles.screenshots}>
|
||||
{screenshots.map(({ src, alt }) => (
|
||||
@ -125,7 +124,7 @@ const Home: NextPage = () => {
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h6>{t('landing.testimonials.heading')}</h6>
|
||||
<h6>{t<string>('landing.testimonials.heading')}</h6>
|
||||
|
||||
<p className="my-3">
|
||||
<Trans t={t} i18nKey="landing.testimonials.body">
|
||||
@ -150,30 +149,30 @@ const Home: NextPage = () => {
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h6>{t('landing.links.heading')}</h6>
|
||||
<h6>{t<string>('landing.links.heading')}</h6>
|
||||
|
||||
<div>
|
||||
<Link href="/meta/privacy" passHref>
|
||||
<Button variant="text" startIcon={<LinkIcon />}>
|
||||
{t('landing.links.links.privacy')}
|
||||
{t<string>('landing.links.links.privacy')}
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Link href="/meta/service" passHref>
|
||||
<Button variant="text" startIcon={<LinkIcon />}>
|
||||
{t('landing.links.links.service')}
|
||||
{t<string>('landing.links.links.service')}
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<a href={GITHUB_URL} target="_blank" rel="noreferrer">
|
||||
<Button variant="text" startIcon={<LinkIcon />}>
|
||||
{t('landing.links.links.github')}
|
||||
{t<string>('landing.links.links.github')}
|
||||
</Button>
|
||||
</a>
|
||||
|
||||
<a href={DONATION_URL} target="_blank" rel="noreferrer">
|
||||
<Button variant="text" startIcon={<LinkIcon />}>
|
||||
{t('landing.links.links.donate')}
|
||||
{t<string>('landing.links.links.donate')}
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -7,6 +7,7 @@ import get from 'lodash/get';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { GetServerSideProps, NextPage } from 'next';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
@ -35,6 +36,8 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ query, loc
|
||||
};
|
||||
|
||||
const Preview: NextPage<Props> = ({ shortId }) => {
|
||||
const router = useRouter();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { data: resume } = useQuery<Resume>(`resume/${shortId}`, () => fetchResumeByShortId({ shortId }), {
|
||||
@ -52,6 +55,14 @@ const Preview: NextPage<Props> = ({ shortId }) => {
|
||||
if (resume) dispatch(setResume(resume));
|
||||
}, [resume, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (resume && !isEmpty(resume) && router.locale !== resume.metadata.locale) {
|
||||
const { pathname, asPath, query } = router;
|
||||
|
||||
router.push({ pathname, query }, asPath, { locale: resume.metadata.locale });
|
||||
}
|
||||
}, [resume, router]);
|
||||
|
||||
if (!resume || isEmpty(resume)) return null;
|
||||
|
||||
const layout: string[][][] = get(resume, 'metadata.layout', []);
|
||||
|
||||
BIN
client/public/images/templates/leafish.jpg
Normal file
BIN
client/public/images/templates/leafish.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 187 KiB |
@ -1,9 +1,9 @@
|
||||
{
|
||||
"common": {
|
||||
"actions": {
|
||||
"add": "إضافة {{فقرة}} جديدة",
|
||||
"delete": "حذة {{الفقرة}}",
|
||||
"edit": "تحرير {{الفقرة}}"
|
||||
"add": "إضافة {{token}} جديدة",
|
||||
"delete": "حذف {{token}}",
|
||||
"edit": "تحرير {{token}}"
|
||||
},
|
||||
"columns": {
|
||||
"heading": "الأعمدة",
|
||||
@ -24,13 +24,13 @@
|
||||
"label": "تاريخ الانتهاء"
|
||||
},
|
||||
"keywords": {
|
||||
"label": "الكلمات الرئيسية"
|
||||
"label": "الكلمات الدالة"
|
||||
},
|
||||
"level": {
|
||||
"label": "مستوى"
|
||||
},
|
||||
"levelNum": {
|
||||
"label": "المستوى (Number)"
|
||||
"label": "المستوى (العدد)"
|
||||
},
|
||||
"name": {
|
||||
"label": "الاسم"
|
||||
@ -42,7 +42,7 @@
|
||||
"label": "المنصب"
|
||||
},
|
||||
"start-date": {
|
||||
"label": "تاريخ البداية"
|
||||
"label": "تاريخ البدء"
|
||||
},
|
||||
"subtitle": {
|
||||
"label": "العنوان الفرعي"
|
||||
@ -119,6 +119,9 @@
|
||||
"name": {
|
||||
"label": "الاسم الكامل"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "تاريخ الميلاد"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
|
||||
361
client/public/locales/bg/builder.json
Normal file
361
client/public/locales/bg/builder.json
Normal file
@ -0,0 +1,361 @@
|
||||
{
|
||||
"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": "Намали"
|
||||
}
|
||||
},
|
||||
"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": "Програмен код",
|
||||
"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": "Контролен панел"
|
||||
}
|
||||
41
client/public/locales/bg/landing.json
Normal file
41
client/public/locales/bg/landing.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"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": "Програмен код",
|
||||
"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": "Обобщение"
|
||||
}
|
||||
}
|
||||
136
client/public/locales/bg/modals.json
Normal file
136
client/public/locales/bg/modals.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"auth": {
|
||||
"forgot-password": {
|
||||
"actions": {
|
||||
"send-email": "Възстановяване на парола"
|
||||
},
|
||||
"body": "Просто въведете имейл адреса, свързан с акаунта, който искате да възстановите.",
|
||||
"form": {
|
||||
"email": {
|
||||
"label": "Имейл адрес"
|
||||
}
|
||||
},
|
||||
"heading": "Забравена парола?",
|
||||
"help-text": "Ако профилът ви съществува, ще получите линк за възстановяване на паролата."
|
||||
},
|
||||
"login": {
|
||||
"actions": {
|
||||
"login": "Вход",
|
||||
"google": "Вход чрез Google"
|
||||
},
|
||||
"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": "Преименувайте автобиографията си"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,6 +119,9 @@
|
||||
"name": {
|
||||
"label": "পূর্ণ নাম"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "জন্ম তারিখ"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
|
||||
@ -119,6 +119,9 @@
|
||||
"name": {
|
||||
"label": "Celé jméno"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Datum narození"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
|
||||
29
client/public/locales/cs/common.json
Normal file
29
client/public/locales/cs/common.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"avatar": {
|
||||
"menu": {
|
||||
"greeting": "Dobrý den",
|
||||
"logout": "Odhlásit se"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"credit": "Vášnivý projekt <1>Amrutha Pillaie</1>",
|
||||
"license": "Od komunity, pro komunitu."
|
||||
},
|
||||
"markdown": {
|
||||
"help-text": "Tato sekce podporuje <1>markdown</1> formátování."
|
||||
},
|
||||
"date": {
|
||||
"present": "Současnost"
|
||||
},
|
||||
"subtitle": "Bezplatný a open source tvůrce životopisů.",
|
||||
"title": "Reactive Resume",
|
||||
"toast": {
|
||||
"error": {
|
||||
"upload-file-size": "Prosím nahrajte pouze soubory pod 2 megabajty.",
|
||||
"upload-photo-size": "Nahrávejte prosím pouze fotografie o velikosti do 2 megabajtů, nejlépe čtvercové."
|
||||
},
|
||||
"success": {
|
||||
"resume-link-copied": "Odkaz na váš životopis byl zkopírován do schránky."
|
||||
}
|
||||
}
|
||||
}
|
||||
25
client/public/locales/cs/dashboard.json
Normal file
25
client/public/locales/cs/dashboard.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"create-resume": {
|
||||
"subtitle": "Začít od začátku",
|
||||
"title": "Vytvořit nový životopis"
|
||||
},
|
||||
"import-external": {
|
||||
"subtitle": "LinkedIn, JSON Resume, Reactive Resume",
|
||||
"title": "Importovat z externích zdrojů"
|
||||
},
|
||||
"resume": {
|
||||
"menu": {
|
||||
"delete": "Smazat",
|
||||
"duplicate": "Duplikovat",
|
||||
"open": "Otevřít",
|
||||
"rename": "Přejmenovat",
|
||||
"share-link": "Sdílet odkaz",
|
||||
"tooltips": {
|
||||
"delete": "Opravdu chcete smazat tento životopis? Toto je nevratná akce.",
|
||||
"share-link": "Musíte změnit viditelnost svého životopisu na veřejnou, aby byl viditelný pro ostatní."
|
||||
}
|
||||
},
|
||||
"timestamp": "Naposledy aktualizováno před {{timestamp}}"
|
||||
},
|
||||
"title": "Přístrojová deska"
|
||||
}
|
||||
41
client/public/locales/cs/landing.json
Normal file
41
client/public/locales/cs/landing.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"actions": {
|
||||
"app": "Přejděte do aplikace",
|
||||
"login": "Přihlásit se",
|
||||
"logout": "Odhlásit se",
|
||||
"register": "Registrovat"
|
||||
},
|
||||
"features": {
|
||||
"heading": "Funkce",
|
||||
"list": {
|
||||
"ads": "Žádná reklama",
|
||||
"export": "Exportujte svůj životopis do formátu JSON nebo PDF",
|
||||
"free": "Zdarma, navždy",
|
||||
"import": "Import dat z LinkedIn, JSON Resume",
|
||||
"languages": "Dostupné ve více jazycích",
|
||||
"more": "A mnohem více vzrušujících funkcí, <1>o tom si vše přečtěte zde</1>",
|
||||
"tracking": "Žádné sledování uživatelů"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"heading": "Odkazy",
|
||||
"links": {
|
||||
"donate": "Darovat",
|
||||
"github": "Zdrojový kód",
|
||||
"privacy": "Zásady ochrany osobních údajů",
|
||||
"service": "Podmínky služby"
|
||||
}
|
||||
},
|
||||
"screenshots": {
|
||||
"heading": "Snímky obrazovky"
|
||||
},
|
||||
"testimonials": {
|
||||
"heading": "Posudky",
|
||||
"body": "Ať je to dobře nebo špatně, rád bych slyšel váš názor na Reactive Resume a jaké to bylo pro vás.<br/>Zde jsou některé zprávy zaslané uživateli z celého světa.",
|
||||
"contact": "Můžete mě kontaktovat prostřednictvím <1>mého e-mailu</1> nebo prostřednictvím kontaktního formuláře na <3>mých webových stránkách</3> ."
|
||||
},
|
||||
"summary": {
|
||||
"body": "Reactive Resume je bezplatný a open source tvůrce životopisů, který je vytvořen tak, aby zjednodušil každodenní úkoly vytváření, aktualizace a sdílení vašeho životopisu jako 1, 2, 3. Pomocí této aplikace můžete vytvořit více životopisů a sdílet je s náboráři nebo přáteli. prostřednictvím jedinečného odkazu a vytiskněte si jej jako PDF, vše zdarma, bez reklam, bez sledování, bez ztráty integrity a soukromí vašich dat.",
|
||||
"heading": "souhrn"
|
||||
}
|
||||
}
|
||||
136
client/public/locales/cs/modals.json
Normal file
136
client/public/locales/cs/modals.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"auth": {
|
||||
"forgot-password": {
|
||||
"actions": {
|
||||
"send-email": "Odeslat e-mail pro obnovení hesla"
|
||||
},
|
||||
"body": "Stačí zadat e-mailovou adresu spojenou s účtem, který chcete obnovit.",
|
||||
"form": {
|
||||
"email": {
|
||||
"label": "Emailová adresa"
|
||||
}
|
||||
},
|
||||
"heading": "Zapomněli jste heslo?",
|
||||
"help-text": "Pokud účet existuje, obdržíte e-mail s odkazem na resetování hesla."
|
||||
},
|
||||
"login": {
|
||||
"actions": {
|
||||
"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.",
|
||||
"form": {
|
||||
"password": {
|
||||
"label": "Heslo"
|
||||
},
|
||||
"username": {
|
||||
"help-text": "Můžete také zadat svou e-mailovou adresu",
|
||||
"label": "uživatelské jméno"
|
||||
}
|
||||
},
|
||||
"heading": "Přihlaste se ke svému účtu",
|
||||
"recover-text": "V případě, že jste zapomněli své heslo, můžete <1>obnovit svůj účet</1> tady.",
|
||||
"register-text": "Pokud jej nemáte, můžete si <1>vytvořit účet</1> tady."
|
||||
},
|
||||
"register": {
|
||||
"actions": {
|
||||
"register": "Registrovat",
|
||||
"google": "Zaregistrujte se u Google"
|
||||
},
|
||||
"body": "Chcete-li vytvořit účet, zadejte své osobní údaje.",
|
||||
"form": {
|
||||
"confirm-password": {
|
||||
"label": "Potvrďte heslo"
|
||||
},
|
||||
"email": {
|
||||
"label": "Emailová adresa"
|
||||
},
|
||||
"name": {
|
||||
"label": "Celé jméno"
|
||||
},
|
||||
"password": {
|
||||
"label": "Heslo"
|
||||
},
|
||||
"username": {
|
||||
"label": "uživatelské jméno"
|
||||
}
|
||||
},
|
||||
"heading": "Vytvořit účet",
|
||||
"loginText": "Pokud již máte účet, můžete se <1>přihlásit zde</1> ."
|
||||
},
|
||||
"reset-password": {
|
||||
"actions": {
|
||||
"set-password": "Nastavit nové heslo"
|
||||
},
|
||||
"body": "Zadejte nové heslo ke svému účtu.",
|
||||
"form": {
|
||||
"confirm-password": {
|
||||
"label": "Potvrďte heslo"
|
||||
},
|
||||
"password": {
|
||||
"label": "Heslo"
|
||||
}
|
||||
},
|
||||
"heading": "Obnovit heslo"
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"create-resume": {
|
||||
"actions": {
|
||||
"create-resume": "Vytvořit životopis"
|
||||
},
|
||||
"body": "Začněte budovat svůj životopis tím, že mu dáte jméno. Může to být odkaz na roli, o kterou se ucházíte, nebo jen na vaši oblíbenou svačinu.",
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "název"
|
||||
},
|
||||
"public": {
|
||||
"label": "Je veřejně přístupný?"
|
||||
},
|
||||
"slug": {
|
||||
"label": "Slimák"
|
||||
}
|
||||
},
|
||||
"heading": "Vytvořte nový životopis"
|
||||
},
|
||||
"import-external": {
|
||||
"heading": "Import z externích zdrojů",
|
||||
"json-resume": {
|
||||
"actions": {
|
||||
"upload-json": "Nahrajte JSON"
|
||||
},
|
||||
"body": "Pokud máte <1>ověřený životopis JSON</1> připraven k použití, můžete jej použít k rychlému sledování svého vývoje na Reactive Resume. Začněte kliknutím na tlačítko níže a nahráním platného souboru JSON.",
|
||||
"heading": "Import z obnovení JSON"
|
||||
},
|
||||
"linkedin": {
|
||||
"actions": {
|
||||
"upload-archive": "Nahrát archiv ZIP"
|
||||
},
|
||||
"body": "Můžete ušetřit čas tím, že exportujete svá data z LinkedIn a použijete je k automatickému vyplňování polí na Reactive Resume. Přejděte na <1>Ochrana osobních údajů</1> sekce na LinkedIn a vyžádejte si archiv vašich dat. Jakmile bude k dispozici, nahrajte níže uvedený soubor ZIP.",
|
||||
"heading": "Import z LinkedIn"
|
||||
},
|
||||
"reactive-resume": {
|
||||
"actions": {
|
||||
"upload-json": "Nahrajte JSON",
|
||||
"upload-json-v2": "Nahrajte JSON z v2"
|
||||
},
|
||||
"body": "Pokud máte JSON, který byl exportován s aktuální verzí Reactive Resume, můžete jej sem importovat zpět a znovu získat upravitelnou verzi.",
|
||||
"heading": "Import z reaktivního obnovení"
|
||||
}
|
||||
},
|
||||
"rename-resume": {
|
||||
"actions": {
|
||||
"rename-resume": "Přejmenovat Resume"
|
||||
},
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "název"
|
||||
},
|
||||
"slug": {
|
||||
"label": "Slimák"
|
||||
}
|
||||
},
|
||||
"heading": "Přejmenujte svůj životopis"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,6 +119,9 @@
|
||||
"name": {
|
||||
"label": "Fulde navn"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Fødselsdato"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
|
||||
@ -119,6 +119,9 @@
|
||||
"name": {
|
||||
"label": "Voller Name"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Geburtsdatum"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
|
||||
361
client/public/locales/el/builder.json
Normal file
361
client/public/locales/el/builder.json
Normal file
@ -0,0 +1,361 @@
|
||||
{
|
||||
"common": {
|
||||
"actions": {
|
||||
"add": "Προσθήκη νέου {{token}}",
|
||||
"delete": "Διαγραφή {{token}}",
|
||||
"edit": "Επεξεργασία {{token}}"
|
||||
},
|
||||
"columns": {
|
||||
"heading": "Στήλες",
|
||||
"tooltip": "Αλλαγή αριθμού στηλών"
|
||||
},
|
||||
"form": {
|
||||
"date": {
|
||||
"label": "Ημερομηνία"
|
||||
},
|
||||
"description": {
|
||||
"label": "Περιγραφή"
|
||||
},
|
||||
"email": {
|
||||
"label": "Διεύθυνση Email"
|
||||
},
|
||||
"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": "Σμίκρυνση"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"menu": {
|
||||
"delete": "Διαγραφή",
|
||||
"duplicate": "Διπλότυπο",
|
||||
"rename": "Μετονομασία",
|
||||
"share-link": "Κοινοποίηση συνδέσμου",
|
||||
"tooltips": {
|
||||
"delete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το βιογραφικό; Αυτή είναι μια μη αναστρέψιμη ενέργεια.",
|
||||
"share-link": "Πρέπει να αλλάξετε την ορατότητα του βιογραφικού σας σε δημόσιο για να το κάνετε ορατό σε άλλους."
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": "PDF",
|
||||
"secondary": "Κατεβάστε ένα PDF του βιογραφικού σας που μπορείτε να εκτυπώσετε και να στείλετε στην εργασία των ονείρων σας. Αυτό το αρχείο δεν μπορεί να εισαχθεί ξανά για περαιτέρω επεξεργασία."
|
||||
}
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"heading": "Διάταξη",
|
||||
"tooltip": {
|
||||
"reset-layout": "Επαναφορά διάταξης"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"bugs-features": {
|
||||
"body": "Υπάρχει κάτι που σας εμποδίζει να φτιάξετε ένα βιογραφικό σημείωμα; Ή μήπως έχετε μια καταπληκτική ιδέα να προσθέσετε; Θέστε το ως σφάλμα στο GitHub για να ξεκινήσετε.",
|
||||
"button": "Σφάλματα GitHub",
|
||||
"heading": "Σφάλματα; Αιτήματα δυνατοτήτων;"
|
||||
},
|
||||
"donate": {
|
||||
"body": "Αν σας άρεσε να χρησιμοποιείτε το Reactive Resume, σκεφτείτε το ενδεχόμενο να δωρίσετε όσο το δυνατόν περισσότερα για τον σκοπό της διατήρησης και λειτουργίας της εφαρμογής, χωρίς διαφημίσεις και δωρεάν για πάντα.",
|
||||
"button": "Κεράστε με ένα καφεδάκι",
|
||||
"heading": "Κάντε δωρεά στο Reactive βιογραφικό"
|
||||
},
|
||||
"github": "Πηγαίος κώδικας",
|
||||
"heading": "Σύνδεσμοι"
|
||||
},
|
||||
"settings": {
|
||||
"global": {
|
||||
"date": {
|
||||
"primary": "Ημερομηνία",
|
||||
"secondary": "Μορφή ημερομηνίας για χρήση στην εφαρμογή"
|
||||
},
|
||||
"heading": "Καθολικό",
|
||||
"language": {
|
||||
"primary": "Γλώσσα",
|
||||
"secondary": "Εμφάνιση γλώσσας για χρήση στην εφαρμογή"
|
||||
},
|
||||
"theme": {
|
||||
"primary": "Θέμα"
|
||||
}
|
||||
},
|
||||
"heading": "Ρυθμίσεις",
|
||||
"page": {
|
||||
"break-line": {
|
||||
"primary": "Γραμμή διακοπής",
|
||||
"secondary": "Εμφάνιση μιας γραμμής σε όλες τις σελίδες για να επισημάνετε το ύψος μιας σελίδας Α4"
|
||||
},
|
||||
"heading": "Σελίδα",
|
||||
"orientation": {
|
||||
"disabled": "Δεν έχει αποτέλεσμα όταν υπάρχει μόνο μία σελίδα",
|
||||
"primary": "Προσανατολισμός",
|
||||
"secondary": "Αν θα εμφανίζονται οι σελίδες οριζόντια ή κάθετα"
|
||||
}
|
||||
},
|
||||
"resume": {
|
||||
"heading": "Βιογραφικό σημείωμα",
|
||||
"reset": {
|
||||
"primary": "Επαναφορά όλων",
|
||||
"secondary": "Κάνατε πάρα πολλά λάθη; Κάντε κλικ εδώ για να επαναφέρετε όλες τις αλλαγές και να ξεκινήσετε από την αρχή. Προσέξτε, αυτή η ενέργεια δεν μπορεί να αντιστραφεί."
|
||||
},
|
||||
"sample": {
|
||||
"primary": "Φόρτωση δειγμάτων δεδομένων",
|
||||
"secondary": "Δεν είστε σίγουροι από πού να ξεκινήσετε; Κάντε κλικ εδώ για να φορτώσετε μερικά δείγματα δεδομένων για να δείτε πώς φαίνεται ένα πλήρες βιογραφικό."
|
||||
}
|
||||
}
|
||||
},
|
||||
"sharing": {
|
||||
"heading": "Κοινοποίηση",
|
||||
"short-url": {
|
||||
"label": "Προτίμηση σύντομης διεύθυνσης"
|
||||
},
|
||||
"visibility": {
|
||||
"subtitle": "Επιτρέψτε σε οποιονδήποτε έχει σύνδεσμο να δει το βιογραφικό σας",
|
||||
"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/el/common.json
Normal file
29
client/public/locales/el/common.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"avatar": {
|
||||
"menu": {
|
||||
"greeting": "Γειά σου",
|
||||
"logout": "Αποσύνδεση"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"credit": "Ένα έργο με πάθος από τον <1>Amruth Pillai</1>",
|
||||
"license": "Από την κοινότητα, για την κοινότητα."
|
||||
},
|
||||
"markdown": {
|
||||
"help-text": "Αυτή η ενότητα υποστηρίζει τη μορφοποίηση <1>markdown</1>."
|
||||
},
|
||||
"date": {
|
||||
"present": "Τώρα"
|
||||
},
|
||||
"subtitle": "Ένας δωρεάν κατασκευαστής βιογραφικών σημειωμάτων ανοικτού κώδικα.",
|
||||
"title": "Reactive Resume",
|
||||
"toast": {
|
||||
"error": {
|
||||
"upload-file-size": "Παρακαλούμε ανεβάζετε μόνο αρχεία κάτω των 2 megabytes.",
|
||||
"upload-photo-size": "Παρακαλούμε ανεβάστε μόνο φωτογραφίες κάτω των 2 megabytes, κατά προτίμηση τετράγωνες."
|
||||
},
|
||||
"success": {
|
||||
"resume-link-copied": "Ένας σύνδεσμος για το βιογραφικό σας αντιγράφηκε."
|
||||
}
|
||||
}
|
||||
}
|
||||
25
client/public/locales/el/dashboard.json
Normal file
25
client/public/locales/el/dashboard.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"create-resume": {
|
||||
"subtitle": "Επανεκκίνηση από την αρχή",
|
||||
"title": "Δημιουργία νέου βιογραφικού"
|
||||
},
|
||||
"import-external": {
|
||||
"subtitle": "LinkedIn, JSON Resume, Reactive Resume",
|
||||
"title": "Εισαγωγή από εξωτερικές πηγές"
|
||||
},
|
||||
"resume": {
|
||||
"menu": {
|
||||
"delete": "Διαγραφή",
|
||||
"duplicate": "Διπλότυπο",
|
||||
"open": "Άνοιγμα",
|
||||
"rename": "Μετονομασία",
|
||||
"share-link": "Κοινοποίηση συνδέσμου",
|
||||
"tooltips": {
|
||||
"delete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το βιογραφικό; Αυτή είναι μια μη αναστρέψιμη ενέργεια.",
|
||||
"share-link": "Πρέπει να αλλάξετε την ορατότητα του βιογραφικού σας σε δημόσιο για να το κάνετε ορατό σε άλλους."
|
||||
}
|
||||
},
|
||||
"timestamp": "Τελευταία ενημέρωση πριν από {{timestamp}}"
|
||||
},
|
||||
"title": "Πίνακας ελέγχου"
|
||||
}
|
||||
41
client/public/locales/el/landing.json
Normal file
41
client/public/locales/el/landing.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"actions": {
|
||||
"app": "Μετάβαση στην εφαρμογή",
|
||||
"login": "Σύνδεση",
|
||||
"logout": "Αποσύνδεση",
|
||||
"register": "Εγγραφή"
|
||||
},
|
||||
"features": {
|
||||
"heading": "Δυνατότητες",
|
||||
"list": {
|
||||
"ads": "Χωρίς διαφημίσεις",
|
||||
"export": "Εξαγωγή του βιογραφικού σας σε μορφή JSON ή PDF",
|
||||
"free": "Δωρεάν για πάντα",
|
||||
"import": "Εισαγωγή δεδομένων από το LinkedIn, JSON Resume",
|
||||
"languages": "Προσβάσιμο σε πολλές γλώσσες",
|
||||
"more": "Και πολλά άλλα συναρπαστικά χαρακτηριστικά, <1>διαβάστε τα όλα εδώ</1>",
|
||||
"tracking": "Χωρίς παρακολούθηση χρήστη"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"heading": "Σύνδεσμοι",
|
||||
"links": {
|
||||
"donate": "Δωρεά",
|
||||
"github": "Πηγαίος Κώδικας",
|
||||
"privacy": "Πολιτική Απορρήτου",
|
||||
"service": "Όρους παροχής υπηρεσιών"
|
||||
}
|
||||
},
|
||||
"screenshots": {
|
||||
"heading": "Στιγμιότυπα οθόνης"
|
||||
},
|
||||
"testimonials": {
|
||||
"heading": "Αναφορές",
|
||||
"body": "Καλή ή κακή, θα ήθελα πολύ να ακούσω τη γνώμη σας για το Reactive Resume και πώς ήταν η εμπειρία σας.<br/>Ακολουθούν μερικά από τα μηνύματα που έστειλαν χρήστες από όλο τον κόσμο.",
|
||||
"contact": "Μπορείτε να επικοινωνήσετε μαζί μου μέσω <1>του email μου</1> ή μέσω της φόρμας επικοινωνίας στην <3>ιστοσελίδα μου</3>."
|
||||
},
|
||||
"summary": {
|
||||
"body": "Το Reactive Resume είναι ένας δωρεάν κατασκευαστής βιογραφικών σημειωμάτων ανοικτού κώδικα που έχει δημιουργηθεί για να κάνει τις καθημερινές εργασίες δημιουργίας, ενημέρωσης και κοινοποίησης του βιογραφικού σας σημειώματος τόσο εύκολες όσο το 1, 2, 3. Με αυτή την εφαρμογή, μπορείτε να δημιουργήσετε πολλαπλά βιογραφικά, να τα μοιραστείτε με τους υπεύθυνους προσλήψεων ή τους φίλους σας μέσω ενός μοναδικού συνδέσμου και να τα εκτυπώσετε σε μορφή PDF, και όλα αυτά δωρεάν, χωρίς διαφημίσεις, χωρίς παρακολούθηση, χωρίς να χάσετε την ακεραιότητα και το απόρρητο των δεδομένων σας.",
|
||||
"heading": "Σύνοψη"
|
||||
}
|
||||
}
|
||||
136
client/public/locales/el/modals.json
Normal file
136
client/public/locales/el/modals.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"auth": {
|
||||
"forgot-password": {
|
||||
"actions": {
|
||||
"send-email": "Αποστολή email επαναφοράς κωδικού πρόσβασης"
|
||||
},
|
||||
"body": "Απλώς εισάγετε τη διεύθυνση ηλεκτρονικού ταχυδρομείου που σχετίζεται με τον λογαριασμό που θέλετε να ανακτήσετε.",
|
||||
"form": {
|
||||
"email": {
|
||||
"label": "Διεύθυνση Email"
|
||||
}
|
||||
},
|
||||
"heading": "Ξεχάσατε τον κωδικό σας;",
|
||||
"help-text": "Εάν ο λογαριασμός υπάρχει, θα λάβετε ένα email με έναν σύνδεσμο για να επαναφέρετε τον κωδικό πρόσβασής σας."
|
||||
},
|
||||
"login": {
|
||||
"actions": {
|
||||
"login": "Σύνδεση",
|
||||
"google": "Συνδεθείτε με το Google"
|
||||
},
|
||||
"body": "Εισαγάγετε το όνομα χρήστη και τον κωδικό πρόσβασης που σχετίζονται με τον λογαριασμό σας για να συνδεθείτε και να αποκτήσετε πρόσβαση, να διαχειριστείτε και να μοιραστείτε τα βιογραφικά σας.",
|
||||
"form": {
|
||||
"password": {
|
||||
"label": "Κωδικός πρόσβασης"
|
||||
},
|
||||
"username": {
|
||||
"help-text": "Μπορείτε επίσης να εισάγετε τη διεύθυνση email σας",
|
||||
"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": "Δημιουργία βιογραφικού σημειώματος"
|
||||
},
|
||||
"body": "Ξεκινήστε να δημιουργείτε το βιογραφικό σας δίνοντάς του ένα όνομα. Θα μπορούσε να αναφέρεται στον ρόλο για τον οποίο κάνετε αίτηση ή απλώς στο αγαπημένο σας σνακ.",
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Όνομα"
|
||||
},
|
||||
"public": {
|
||||
"label": "Είναι δημόσια προσβάσιμο;"
|
||||
},
|
||||
"slug": {
|
||||
"label": "Slug"
|
||||
}
|
||||
},
|
||||
"heading": "Δημιουργία νέου βιογραφικού σημειώματος"
|
||||
},
|
||||
"import-external": {
|
||||
"heading": "Εισαγωγή από εξωτερικές πηγές",
|
||||
"json-resume": {
|
||||
"actions": {
|
||||
"upload-json": "Μεταφόρτωση JSON"
|
||||
},
|
||||
"body": "Αν έχετε έτοιμο ένα <1>επικυρωμένο JSON Resume</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": "Slug"
|
||||
}
|
||||
},
|
||||
"heading": "Μετονομασία του βιογραφικού σας"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,6 +119,9 @@
|
||||
"name": {
|
||||
"label": "Full Name"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Date of Birth"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
|
||||
@ -119,6 +119,9 @@
|
||||
"name": {
|
||||
"label": "Nombre Completo"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Fecha de cumpleaños"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
|
||||
361
client/public/locales/fa/builder.json
Normal file
361
client/public/locales/fa/builder.json
Normal file
@ -0,0 +1,361 @@
|
||||
{
|
||||
"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": "کوچکنمایی"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"menu": {
|
||||
"delete": "حذف",
|
||||
"duplicate": "کپی",
|
||||
"rename": "تغییر نام",
|
||||
"share-link": "اشتراکگذاری لینک",
|
||||
"tooltips": {
|
||||
"delete": "آیا از حذف این رزومه اطمینان دارید؟ این عمل غیرقابل برگشت است.",
|
||||
"share-link": "باید وضعیت نمایش رزومه خود را به عمومی تغییر دهید تا برای دیگران قابل مشاهده باشد."
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": "اندازه (بر حسب پیکسل)"
|
||||
}
|
||||
},
|
||||
"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 شما تولید شود، ممکن است تا ۱۵ ثانیه طول بکشد."
|
||||
},
|
||||
"normal": {
|
||||
"primary": "PDF",
|
||||
"secondary": "یک PDF از رزومه خود را دانلود کنید که میتوانید آن را چاپ کرده و به شغل رویایی خود ارسال کنید. این فایل را نمیتوان برای ویرایش بیشتر بارگذاری کرد."
|
||||
}
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"heading": "چیدمان",
|
||||
"tooltip": {
|
||||
"reset-layout": "بازنشانی چیدمان"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"bugs-features": {
|
||||
"body": "چیزی شما را از ایجاد رزومه باز میدارد؟ یا ایده جذابی برای اضافه کردن دارید؟ برای شروع یک issue در GitHub مطرح کنید.",
|
||||
"button": "GitHub Issues",
|
||||
"heading": "باگها؟ درخواست ویژگی جدید؟"
|
||||
},
|
||||
"donate": {
|
||||
"body": "اگر استفاده از Reactive Resume را دوست داشتید، لطفاً تا جایی که می توانید کمک مالی کنید تا برنامه را بدون تبلیغات و برای همیشه رایگان نگه دارید.",
|
||||
"button": "برای من یک قهوه بخر",
|
||||
"heading": "کمک مالی به Reactive Resume"
|
||||
},
|
||||
"github": "کد منبع",
|
||||
"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": "هرکسی با این لینک بتواند رزومه شما را ببیند",
|
||||
"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/fa/common.json
Normal file
29
client/public/locales/fa/common.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"avatar": {
|
||||
"menu": {
|
||||
"greeting": "سلام",
|
||||
"logout": "خروج"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"credit": "پروژهای پرشور توسط <1>Amruth Pillai</1>",
|
||||
"license": "ساختهشده توسط جامعه، برای جامعه."
|
||||
},
|
||||
"markdown": {
|
||||
"help-text": "این بخش از <1>markdown</1> پشتیبانی میکند."
|
||||
},
|
||||
"date": {
|
||||
"present": "امروز"
|
||||
},
|
||||
"subtitle": "یک رزومهساز رایگان و متنباز.",
|
||||
"title": "Reactive Resume",
|
||||
"toast": {
|
||||
"error": {
|
||||
"upload-file-size": "لطفا تنها فایلهایی با حجم کمتر از ۲ مگابایت آپلود کنید.",
|
||||
"upload-photo-size": "لطفا تنها عکس هایی با حجم کمتر از 2 مگابایت و ترجیحا مربع آپلود کنید."
|
||||
},
|
||||
"success": {
|
||||
"resume-link-copied": "لینک رزومه شما در کلیپبورد کپی شد."
|
||||
}
|
||||
}
|
||||
}
|
||||
25
client/public/locales/fa/dashboard.json
Normal file
25
client/public/locales/fa/dashboard.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"create-resume": {
|
||||
"subtitle": "از ابتدا شروع کنید",
|
||||
"title": "ساخت رزومه جدید"
|
||||
},
|
||||
"import-external": {
|
||||
"subtitle": "LinkedIn، JSON Resume، Reactive Resume",
|
||||
"title": "وارد کردن از منابع خارجی"
|
||||
},
|
||||
"resume": {
|
||||
"menu": {
|
||||
"delete": "حذف",
|
||||
"duplicate": "کپی",
|
||||
"open": "باز کردن",
|
||||
"rename": "تغییر نام",
|
||||
"share-link": "اشتراکگذاری لینک",
|
||||
"tooltips": {
|
||||
"delete": "آیا از حذف این رزومه اطمینان دارید؟ این عمل غیرقابل برگشت است.",
|
||||
"share-link": "باید وضعیت نمایش رزومه خود را به عمومی تغییر دهید تا برای دیگران قابل مشاهده باشد."
|
||||
}
|
||||
},
|
||||
"timestamp": "آخرین بهروزرسانی {{timestamp}} قبل"
|
||||
},
|
||||
"title": "داشبورد"
|
||||
}
|
||||
41
client/public/locales/fa/landing.json
Normal file
41
client/public/locales/fa/landing.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"actions": {
|
||||
"app": "رفتن به برنامه",
|
||||
"login": "ورود",
|
||||
"logout": "خروج",
|
||||
"register": "ثبت نام"
|
||||
},
|
||||
"features": {
|
||||
"heading": "ویژگیها",
|
||||
"list": {
|
||||
"ads": "بدون تبلیغات",
|
||||
"export": "گرفتن خروجی از رزومه خود به فرمت JSON یا PDF",
|
||||
"free": "رایگان، برای همیشه",
|
||||
"import": "وارد کردن داده از LinkedIn، JSON Resume",
|
||||
"languages": "قابل دسترسی به چندین زبان",
|
||||
"more": "و تعداد زیادی ویژگی هیجان انگیز دیگر، <1>همهچیز را درباره آنها اینجا بخوانید</1>",
|
||||
"tracking": "بدون ردیابی کاربر"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"heading": "لینکها",
|
||||
"links": {
|
||||
"donate": "حمایت مالی",
|
||||
"github": "کد منبع",
|
||||
"privacy": "حریم خصوصی",
|
||||
"service": "شرایط سرویسدهی"
|
||||
}
|
||||
},
|
||||
"screenshots": {
|
||||
"heading": "اسکرینشاتها"
|
||||
},
|
||||
"testimonials": {
|
||||
"heading": "نظرات کاربران",
|
||||
"body": "خوب یا بد، من دوست دارم نظر شما را در مورد Reactive Resume و اینکه تجربه کار با آن برای شما چگونه بوده است را بدانم.<br/>تعدادی از پیام های ارسال شده توسط کاربران در سراسر جهان را اینجا میبینید.",
|
||||
"contact": "میتوانید از طریق <1>ایمیل من</1> یا فرم تماس در <3>وبسایت من</3> با من در ارتباط باشید."
|
||||
},
|
||||
"summary": {
|
||||
"body": "Reactive Resume یک رزومه ساز رایگان و متنباز است که برای ایجاد، به روز رسانی و به اشتراک گذاری رزومه شما به آسانی شمردن ۱، ۲، ۳ ساخته شده است. با این برنامه، می توانید چندین رزومه ایجاد کنید و آنها را با کارفرماها یا دوستان از طریق یک لینک منحصر به فرد و چاپ آن به صورت PDF، همه به صورت رایگان، بدون تبلیغات، بدون ردیابی، بدون از دست دادن امنیت و حریم خصوصی داده های شما، به اشتراک بگذارید.",
|
||||
"heading": "درباره من"
|
||||
}
|
||||
}
|
||||
136
client/public/locales/fa/modals.json
Normal file
136
client/public/locales/fa/modals.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"auth": {
|
||||
"forgot-password": {
|
||||
"actions": {
|
||||
"send-email": "ارسال ایمیل بازنشانی رمز عبور"
|
||||
},
|
||||
"body": "آدرس ایمیل حسابی را که می خواهید بازیابی کنید، وارد کنید.",
|
||||
"form": {
|
||||
"email": {
|
||||
"label": "آدرس ایمیل"
|
||||
}
|
||||
},
|
||||
"heading": "رمز عبور خود را فراموش کردهاید؟",
|
||||
"help-text": "اگر حساب مورد نظر موجود باشد، یک ایمیل با لینک بازنشانی رمز عبور خود دریافت میکنید."
|
||||
},
|
||||
"login": {
|
||||
"actions": {
|
||||
"login": "ورود",
|
||||
"google": "ورود با گوگل"
|
||||
},
|
||||
"body": "لطفا نام کاربری و رمز عبور مرتبط با حساب کاربری خود را وارد کنید تا وارد شوید و به رزومه خود دسترسی پیدا کنید، آن را مدیریت کنید و به اشتراک بگذارید.",
|
||||
"form": {
|
||||
"password": {
|
||||
"label": "رمز عبور"
|
||||
},
|
||||
"username": {
|
||||
"help-text": "همچنین می توانید آدرس ایمیل خود را وارد کنید",
|
||||
"label": "نام کاربری"
|
||||
}
|
||||
},
|
||||
"heading": "ورود به حساب کابری",
|
||||
"recover-text": "در صورتی که رمز عبور خود را فراموش کردهاید، می توانید از اینجا <1>حساب خود را بازیابی کنید</1>.",
|
||||
"register-text": "اگر حسابی ندارید، میتوانید از اینجا <1>یک حساب ایجاد کنید</1>."
|
||||
},
|
||||
"register": {
|
||||
"actions": {
|
||||
"register": "ثبت نام",
|
||||
"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": "ایجاد رزومه"
|
||||
},
|
||||
"body": "ساخت رزومه خود را با نامگذاری آن شروع کنید. این نام میتواند به سمتی که برای آن درخواست میکنید مرتبط باشد، یا میانوعده مورد علاقه شما باشد.",
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "نام"
|
||||
},
|
||||
"public": {
|
||||
"label": "آیا به صورت عمومی قابل دسترسی است؟"
|
||||
},
|
||||
"slug": {
|
||||
"label": "نام یکتا"
|
||||
}
|
||||
},
|
||||
"heading": "ایجاد یک رزومه جدید"
|
||||
},
|
||||
"import-external": {
|
||||
"heading": "وارد کردن از منابع خارجی",
|
||||
"json-resume": {
|
||||
"actions": {
|
||||
"upload-json": "آپلود JSON"
|
||||
},
|
||||
"body": "اگر یک <1>JSON Resume معتبر</1> که آماده کار است، می توانید از آن برای پیگیری سریع توسعه خود در Reactive Resume استفاده کنید. روی دکمه زیر کلیک کنید و برای شروع یک فایل JSON معتبر آپلود کنید.",
|
||||
"heading": "بارگذاری از JSON Resume"
|
||||
},
|
||||
"linkedin": {
|
||||
"actions": {
|
||||
"upload-archive": "آپلود آرشیو ZIP"
|
||||
},
|
||||
"body": "میتوانید با خروجی گرفتن از دادههای خود در LinkedIn و استفاده از آن برای پر کردن خودکار فیلدهای Reactive Resume در زمانصرفهجویی کنید. به بخش <1>Data Privacy</1> در LinkedIn بروید و یک آرشیو از دادههای خود را درخواست کنید. پس از در دسترس قرار گرفتن، فایل ZIP را در زیر آپلود کنید.",
|
||||
"heading": "بارگذاری از LinkedIn"
|
||||
},
|
||||
"reactive-resume": {
|
||||
"actions": {
|
||||
"upload-json": "آپلود JSON",
|
||||
"upload-json-v2": "آپلود JSON از ورژن ۲"
|
||||
},
|
||||
"body": "اگر یک فایل JSON دارید که با نسخه فعلی Reactive Resume خروجی گرفتهشده است، میتوانید آن را اینجا بارگذاری کنید تا دوباره نسخه قابل ویرایش دریافت کنید.",
|
||||
"heading": "بارگذاری از Reactive Resume"
|
||||
}
|
||||
},
|
||||
"rename-resume": {
|
||||
"actions": {
|
||||
"rename-resume": "تغییر نام رزومه"
|
||||
},
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "نام"
|
||||
},
|
||||
"slug": {
|
||||
"label": "نام یکتا"
|
||||
}
|
||||
},
|
||||
"heading": "نام رزومه خود را تغییر دهید"
|
||||
}
|
||||
}
|
||||
}
|
||||
361
client/public/locales/fi/builder.json
Normal file
361
client/public/locales/fi/builder.json
Normal file
@ -0,0 +1,361 @@
|
||||
{
|
||||
"common": {
|
||||
"actions": {
|
||||
"add": "Lisää uusi",
|
||||
"delete": "Poista",
|
||||
"edit": "Muokkaa"
|
||||
},
|
||||
"columns": {
|
||||
"heading": "Palstat",
|
||||
"tooltip": "Muut palstojen määrää"
|
||||
},
|
||||
"form": {
|
||||
"date": {
|
||||
"label": "Päivämäärä"
|
||||
},
|
||||
"description": {
|
||||
"label": "Kuvaus"
|
||||
},
|
||||
"email": {
|
||||
"label": "Sähköposti"
|
||||
},
|
||||
"end-date": {
|
||||
"help-text": "Jätä tämä kenttä tyhjäksi, jos se on vielä esillä",
|
||||
"label": "Päättymispäivä"
|
||||
},
|
||||
"keywords": {
|
||||
"label": "Avainsanat"
|
||||
},
|
||||
"level": {
|
||||
"label": "Taso"
|
||||
},
|
||||
"levelNum": {
|
||||
"label": "Taso (numero)"
|
||||
},
|
||||
"name": {
|
||||
"label": "Nimi"
|
||||
},
|
||||
"phone": {
|
||||
"label": "Puhelinnumero"
|
||||
},
|
||||
"position": {
|
||||
"label": "Sijainti"
|
||||
},
|
||||
"start-date": {
|
||||
"label": "Alkamispäivä"
|
||||
},
|
||||
"subtitle": {
|
||||
"label": "Alaotsikko"
|
||||
},
|
||||
"summary": {
|
||||
"label": "Yhteenveto"
|
||||
},
|
||||
"title": {
|
||||
"label": "Otsikko"
|
||||
},
|
||||
"url": {
|
||||
"label": "Verkkosivusto"
|
||||
}
|
||||
},
|
||||
"glossary": {
|
||||
"page": "Sivu"
|
||||
},
|
||||
"list": {
|
||||
"actions": {
|
||||
"delete": "Poista",
|
||||
"duplicate": "Monista",
|
||||
"edit": "Muokkaa"
|
||||
},
|
||||
"empty-text": "Tämä luettelo on tyhjä."
|
||||
},
|
||||
"tooltip": {
|
||||
"delete-item": "Haluatko varmasti poistaa tämän kohteen? Tämä on peruuttamaton toimenpide.",
|
||||
"delete-section": "Poista osio",
|
||||
"rename-section": "Nimeä osio uudelleen",
|
||||
"toggle-visibility": "Näkyvyys päälle/pois"
|
||||
}
|
||||
},
|
||||
"controller": {
|
||||
"tooltip": {
|
||||
"center-artboard": "Keskitä Kuvataulu",
|
||||
"copy-link": "Kopioi linkki ansioluetteloon",
|
||||
"export-pdf": "Vie PDF",
|
||||
"toggle-orientation": "Vaihda sivun suunta",
|
||||
"toggle-page-break-line": "Vaihda sivunvaihtoviivaa",
|
||||
"toggle-sidebars": "Vaihda Sivupalkkeja",
|
||||
"zoom-in": "Suurenna",
|
||||
"zoom-out": "Pienennä"
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"menu": {
|
||||
"delete": "Poista",
|
||||
"duplicate": "Kopioi",
|
||||
"rename": "Nimeä uudelleen",
|
||||
"share-link": "Jaa linkki",
|
||||
"tooltips": {
|
||||
"delete": "Haluatko varmasti poistaa tämän ansioluettelon? Tämä on peruuttamaton toimenpide.",
|
||||
"share-link": "Sinun on muutettava ansioluettelosi näkyvyys julkiseksi, jotta se näkyisi muille."
|
||||
}
|
||||
}
|
||||
},
|
||||
"leftSidebar": {
|
||||
"sections": {
|
||||
"awards": {
|
||||
"form": {
|
||||
"awarder": {
|
||||
"label": "Tunnustuksen antaja"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basics": {
|
||||
"actions": {
|
||||
"photo-filters": "Valokuvasuodattimet"
|
||||
},
|
||||
"heading": "Perustiedot",
|
||||
"headline": {
|
||||
"label": "Otsikko"
|
||||
},
|
||||
"name": {
|
||||
"label": "Koko nimi"
|
||||
},
|
||||
"birthdate": {
|
||||
"label": "Syntymäaika"
|
||||
},
|
||||
"photo-filters": {
|
||||
"effects": {
|
||||
"border": {
|
||||
"label": "Kehys"
|
||||
},
|
||||
"grayscale": {
|
||||
"label": "Harmaasävy"
|
||||
},
|
||||
"heading": "Efektit"
|
||||
},
|
||||
"shape": {
|
||||
"heading": "Muoto"
|
||||
},
|
||||
"size": {
|
||||
"heading": "Koko (px)"
|
||||
}
|
||||
},
|
||||
"photo-upload": {
|
||||
"tooltip": {
|
||||
"remove": "Poista kuva",
|
||||
"upload": "Lataa valokuva"
|
||||
}
|
||||
}
|
||||
},
|
||||
"certifications": {
|
||||
"form": {
|
||||
"issuer": {
|
||||
"label": "Myöntäjä"
|
||||
}
|
||||
}
|
||||
},
|
||||
"education": {
|
||||
"form": {
|
||||
"area-study": {
|
||||
"label": "Opintojen alue"
|
||||
},
|
||||
"courses": {
|
||||
"label": "Kurssit"
|
||||
},
|
||||
"degree": {
|
||||
"label": "Tutkinto"
|
||||
},
|
||||
"grade": {
|
||||
"label": "Arvosana"
|
||||
},
|
||||
"institution": {
|
||||
"label": "Oppilaitos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"address": {
|
||||
"label": "Osoite"
|
||||
},
|
||||
"city": {
|
||||
"label": "Kaupunki"
|
||||
},
|
||||
"country": {
|
||||
"label": "Maa"
|
||||
},
|
||||
"heading": "Sijainti",
|
||||
"postal-code": {
|
||||
"label": "Postinumero"
|
||||
},
|
||||
"region": {
|
||||
"label": "Paikkakunta"
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"form": {
|
||||
"network": {
|
||||
"label": "Verkosto"
|
||||
},
|
||||
"username": {
|
||||
"label": "Käyttäjätunnus"
|
||||
}
|
||||
},
|
||||
"heading": "Profiilit",
|
||||
"heading_one": "Profiili"
|
||||
},
|
||||
"publications": {
|
||||
"form": {
|
||||
"publisher": {
|
||||
"label": "Julkaisija"
|
||||
}
|
||||
}
|
||||
},
|
||||
"references": {
|
||||
"form": {
|
||||
"relationship": {
|
||||
"label": "Suhde suosittelijaan"
|
||||
}
|
||||
}
|
||||
},
|
||||
"section": {
|
||||
"heading": "Osio"
|
||||
},
|
||||
"volunteer": {
|
||||
"form": {
|
||||
"organization": {
|
||||
"label": "Organisaatio"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"rightSidebar": {
|
||||
"sections": {
|
||||
"css": {
|
||||
"heading": "Mukautettu CSS"
|
||||
},
|
||||
"export": {
|
||||
"heading": "Vie",
|
||||
"json": {
|
||||
"primary": "JSON",
|
||||
"secondary": "Lataa JSON-versio ansioluettelostasi, jonka voit tuoda takaisin Reactive Resumeen."
|
||||
},
|
||||
"pdf": {
|
||||
"loading": {
|
||||
"primary": "Luodaan PDF-tiedostoa",
|
||||
"secondary": "Odota, että PDF-tiedostosi luodaan. Tämä voi kestää jopa 15 sekuntia."
|
||||
},
|
||||
"normal": {
|
||||
"primary": "PDF",
|
||||
"secondary": "Lataa PDF-tiedosto ansioluettelostasi, jonka voit tulostaa ja lähettää unelmiesi työpaikkaan. Tätä tiedostoa ei voi tuoda takaisin muokattavaksi."
|
||||
}
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"heading": "Asettelu",
|
||||
"tooltip": {
|
||||
"reset-layout": "Nollaa asettelu"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"bugs-features": {
|
||||
"body": "Estääkö jokin sinua tekemästä ansioluetteloa? Vai onko sinulla mahtava idea lisättäväksi? Nosta ongelma GitHubissa päästäksesi alkuun.",
|
||||
"button": "GitHub Issues",
|
||||
"heading": "Bugeja? Ominaisuuspyyntöjä?"
|
||||
},
|
||||
"donate": {
|
||||
"body": "Jos pidit Reactive Resume -sovelluksen käytöstä, harkitse lahjoitusta niin paljon kuin voit, jotta sovellus pysyy toiminnassa ilman mainoksia ja ikuisesti ilmaisena.",
|
||||
"button": "Osta minulle kahvi",
|
||||
"heading": "Lahjoita Reaktiiviselle ansioluettelolle"
|
||||
},
|
||||
"github": "Lähdekoodi",
|
||||
"heading": "Linkit"
|
||||
},
|
||||
"settings": {
|
||||
"global": {
|
||||
"date": {
|
||||
"primary": "Päivämäärä",
|
||||
"secondary": "Sovelluksessa käytettävä päivämäärämuoto"
|
||||
},
|
||||
"heading": "Yleinen",
|
||||
"language": {
|
||||
"primary": "Kieli",
|
||||
"secondary": "Koko sovelluksessa käytettävä näyttökieli"
|
||||
},
|
||||
"theme": {
|
||||
"primary": "Teema"
|
||||
}
|
||||
},
|
||||
"heading": "Asetukset",
|
||||
"page": {
|
||||
"break-line": {
|
||||
"primary": "Katkoviiva",
|
||||
"secondary": "Näytä viiva kaikilla sivuilla A4-sivun korkeuden merkitsemiseksi"
|
||||
},
|
||||
"heading": "Sivu",
|
||||
"orientation": {
|
||||
"disabled": "Ei vaikuta, kun sivuja on vain yksi",
|
||||
"primary": "Suunta",
|
||||
"secondary": "Näytetäänkö sivut vaaka- vai pystysuunnassa"
|
||||
}
|
||||
},
|
||||
"resume": {
|
||||
"heading": "Ansioluettelo",
|
||||
"reset": {
|
||||
"primary": "Tyhjennä Kaikki",
|
||||
"secondary": "Oletko tehnyt liikaa virheitä? Napsauta tätä nollataksesi kaikki muutokset ja aloittaaksesi alusta. Ole varovainen, tätä toimintoa ei voi peruuttaa."
|
||||
},
|
||||
"sample": {
|
||||
"primary": "Lataa näytetiedot",
|
||||
"secondary": "Etkö ole varma mistä aloittaa? Napsauta tätä ladataksesi näytetietoja nähdäksesi, miltä täydellinen ansioluettelo näyttää."
|
||||
}
|
||||
}
|
||||
},
|
||||
"sharing": {
|
||||
"heading": "Jakaminen",
|
||||
"short-url": {
|
||||
"label": "Valitse lyhyt URL-osoite"
|
||||
},
|
||||
"visibility": {
|
||||
"subtitle": "Salli kenelle tahansa linkin avulla mahdollisuus tarkastella ansioluetteloasi.",
|
||||
"title": "Julkinen"
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"heading": "Mallipohjat"
|
||||
},
|
||||
"theme": {
|
||||
"form": {
|
||||
"background": {
|
||||
"label": "Taustakuva"
|
||||
},
|
||||
"primary": {
|
||||
"label": "Ensisijainen"
|
||||
},
|
||||
"text": {
|
||||
"label": "Teksti"
|
||||
}
|
||||
},
|
||||
"heading": "Teema"
|
||||
},
|
||||
"typography": {
|
||||
"form": {
|
||||
"font-family": {
|
||||
"label": "Kirjasintyyli"
|
||||
},
|
||||
"font-size": {
|
||||
"label": "Fontin koko"
|
||||
}
|
||||
},
|
||||
"heading": "Painatus",
|
||||
"widgets": {
|
||||
"body": {
|
||||
"label": "Runko"
|
||||
},
|
||||
"headings": {
|
||||
"label": "Otsikot"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user