Compare commits

...

252 Commits

Author SHA1 Message Date
a293d209de update dependencies, bump up version to 3.8.4 2023-08-07 11:48:56 +02:00
5b9ea43090 Merge pull request #1418 from AmruthPillai/dependabot/npm_and_yarn/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2
2023-08-07 09:48:05 +02:00
01b36ee8d8 Merge pull request #1420 from AmruthPillai/add-date-format-options
Add 'YYYY.MM' & 'YYYY.M' date format options
2023-08-07 09:30:07 +02:00
af1c314c36 Add 'YYYY.MM' & 'YYYY.M' date format options
closes #1419
2023-08-07 09:29:47 +02:00
d836e3d992 Bump semver from 5.7.1 to 5.7.2
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-03 06:19:13 +00:00
fb2db6839f Update README.md 2023-08-01 20:41:48 +02:00
787d0af9d1 Update README.md 2023-08-01 20:40:02 +02:00
95de63f387 Merge pull request #1411 from Jubair70/fix/work_modal_datepicker
fix: Work Modal Datepicker popup
2023-07-31 16:12:19 +02:00
456b896310 Merge branch 'main' into fix/work_modal_datepicker 2023-07-31 15:09:40 +06:00
974bf7e032 fix: Work Modal Datepicker 2023-07-31 15:02:17 +06:00
8fa5324a39 Merge pull request #1410 from AmruthPillai/dependabot/docker/server/playwright-v1.36.2-focal
Bump playwright from v1.36.1-focal to v1.36.2-focal in /server
2023-07-31 10:31:58 +02:00
4ac9289344 Bump playwright from v1.36.1-focal to v1.36.2-focal in /server
Bumps playwright from v1.36.1-focal to v1.36.2-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 02:45:02 +00:00
d67272cf9e bump up version to 3.8.3 2023-07-27 20:09:26 +02:00
f937a88b9d attempt to fix leafish template issues 2023-07-27 20:08:47 +02:00
7465a7ec78 Merge pull request #1407 from AmruthPillai/fix/id-null-when-section-duplicated-cloned
Fix issue when a section is duplicated/cloned, ID is null
2023-07-27 19:51:01 +02:00
b53d8854df bump up version to 3.8.2 2023-07-27 19:43:19 +02:00
bffa0be909 fix dev dotenv script 2023-07-27 19:42:48 +02:00
06fee1696e update dependencies, fix birthDate error outline 2023-07-27 19:42:25 +02:00
f9a11092a6 Fix issue when a section is duplicated/cloned, ID is null 2023-07-27 18:35:19 +02:00
9c76999945 add start pipeline command to turbo 2023-07-25 15:37:10 +02:00
d23d1a615e Merge pull request #1403 from Jubair70/fix_education_modal_datepicker
fix: Education Date picker modal UI component
2023-07-25 14:50:46 +02:00
a5701a37a6 Merge pull request #1400 from rcwell/main
Fixed Date entered on any <Date> field is one day earlier
2023-07-25 14:50:39 +02:00
a739b25f42 update all dependencies 2023-07-25 14:50:10 +02:00
145aa14ba0 fix: Education Date picker modal 2023-07-24 18:27:39 +06:00
94358bf61c don't convert date to local date 2023-07-23 07:46:19 +14:00
ce50df61a5 Merge pull request #1395 from AmruthPillai/i18n_main
New Crowdin updates
2023-07-21 22:14:17 +02:00
f18da54dfa Merge branch 'main' into i18n_main 2023-07-21 22:14:11 +02:00
14c5e36fae Merge pull request #1396 from Jubair70/Jubair70/fixes-date-selection
Fixed date selection by selecting month after year
2023-07-21 22:13:06 +02:00
1483f9b4f2 Merge branch 'main' into Jubair70/fixes-date-selection 2023-07-21 15:04:53 +06:00
f7d8e4ebb4 slots removed 2023-07-21 15:03:58 +06:00
7c42d6e607 New translations modals.json (Portuguese, Brazilian) 2023-07-20 23:34:56 +02:00
08dea8ad8b New translations landing.json (Portuguese, Brazilian) 2023-07-20 23:34:41 +02:00
950d7ea4e7 New translations common.json (Portuguese, Brazilian) 2023-07-20 23:34:15 +02:00
ebc12042a9 New translations builder.json (Nepali) 2023-07-20 23:34:05 +02:00
d8168d2a9d New translations builder.json (Amharic) 2023-07-20 23:34:04 +02:00
7cfda3c83d New translations builder.json (Odia) 2023-07-20 23:34:03 +02:00
8fcfbdd69d New translations builder.json (Kannada) 2023-07-20 23:34:03 +02:00
1eb52261f2 New translations builder.json (Malayalam) 2023-07-20 23:34:02 +02:00
88400b769d New translations builder.json (Hindi) 2023-07-20 23:34:01 +02:00
b6831fc532 New translations builder.json (Marathi) 2023-07-20 23:34:00 +02:00
b231b60b5a New translations builder.json (Tamil) 2023-07-20 23:33:59 +02:00
2679c9ebc2 New translations builder.json (Khmer) 2023-07-20 23:33:58 +02:00
278253b809 New translations builder.json (Persian) 2023-07-20 23:33:57 +02:00
8a933de0bd New translations builder.json (Indonesian) 2023-07-20 23:33:57 +02:00
704cba06f4 New translations builder.json (Portuguese, Brazilian) 2023-07-20 23:33:56 +02:00
b946098bd0 New translations builder.json (Vietnamese) 2023-07-20 23:33:55 +02:00
f7b95f7679 New translations builder.json (Ukrainian) 2023-07-20 23:33:54 +02:00
e38967874e New translations builder.json (Turkish) 2023-07-20 23:33:53 +02:00
8368c4e183 New translations builder.json (Serbian (Cyrillic)) 2023-07-20 23:33:52 +02:00
951f14ef69 New translations builder.json (Russian) 2023-07-20 23:33:51 +02:00
4a75be95ef New translations builder.json (Portuguese) 2023-07-20 23:33:51 +02:00
1125557fbc New translations builder.json (Polish) 2023-07-20 23:33:50 +02:00
db63138307 New translations builder.json (Korean) 2023-07-20 23:33:48 +02:00
a52feac93b New translations builder.json (Italian) 2023-07-20 23:33:47 +02:00
ad7b6ad2c6 New translations builder.json (Hungarian) 2023-07-20 23:33:46 +02:00
33e3850bb7 New translations builder.json (Hebrew) 2023-07-20 23:33:45 +02:00
c29605dbd0 New translations builder.json (Finnish) 2023-07-20 23:33:44 +02:00
14b2ba4f73 New translations builder.json (Greek) 2023-07-20 23:33:43 +02:00
8868684125 New translations builder.json (Danish) 2023-07-20 23:33:39 +02:00
c1ceb0cd50 New translations builder.json (Czech) 2023-07-20 23:33:38 +02:00
1105f672a5 New translations builder.json (Catalan) 2023-07-20 23:33:37 +02:00
67cc49c068 New translations builder.json (Bulgarian) 2023-07-20 23:33:36 +02:00
505406508b New translations builder.json (Arabic) 2023-07-20 23:33:35 +02:00
bfd37951df New translations builder.json (Spanish) 2023-07-20 23:33:34 +02:00
339cae05f1 New translations builder.json (French) 2023-07-20 23:33:34 +02:00
48069c10a4 New translations builder.json (Romanian) 2023-07-20 23:33:33 +02:00
51317b2901 New translations builder.json (Japanese) 2023-07-20 22:32:27 +02:00
e5ce53b2aa Merge pull request #1399 from thomasmazon/thomas-improve-pt-br-translation
Thomas: fixing some Portuguese Brazilian labels
2023-07-20 22:32:00 +02:00
2bc7c93174 Thomas: fixing some Portuguese Brazilian labels 2023-07-20 11:56:54 -03:00
1d97f01942 Jubair70:fix/date-selection 2023-07-19 17:58:13 +06:00
5ad517f1d3 Jubair70:fix/date-selection 2023-07-19 17:53:23 +06:00
8088c70038 New translations builder.json (Chinese Simplified) 2023-07-19 13:52:09 +02:00
e36df82ba9 Merge pull request #1393 from AmruthPillai/dependabot/docker/server/playwright-v1.36.1-focal
Bump playwright from v1.35.1-focal to v1.36.1-focal in /server
2023-07-19 08:45:18 +02:00
de513a12da Merge pull request #1392 from AmruthPillai/dependabot/github_actions/docker/setup-buildx-action-2.9.1
Bump docker/setup-buildx-action from 2.9.0 to 2.9.1
2023-07-19 08:45:11 +02:00
06f1a813ce Merge pull request #1387 from AmruthPillai/i18n_main
New Crowdin updates
2023-07-19 08:45:01 +02:00
1de9195f20 Merge branch 'main' into i18n_main 2023-07-19 08:44:54 +02:00
eaa21ead3e Update build-deploy.yml 2023-07-19 08:44:47 +02:00
3ea4a9b000 Bump playwright from v1.35.1-focal to v1.36.1-focal in /server
Bumps playwright from v1.35.1-focal to v1.36.1-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 02:36:05 +00:00
f0484c1c28 Bump docker/setup-buildx-action from 2.9.0 to 2.9.1
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.9.0...v2.9.1)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 02:11:08 +00:00
7ebda09a5f New translations builder.json (German) 2023-07-14 22:35:05 +02:00
161ca0ee28 run prettier formatting on all files 2023-07-13 08:54:37 +02:00
984078db76 parallelize ci workflow 2023-07-12 16:26:07 +02:00
fbc0ae8918 fix use of dev dependency 2023-07-12 16:21:46 +02:00
3e93656f1f update issue templates 2023-07-12 16:02:16 +02:00
01bf17d7c8 Update README.md 2023-07-12 16:01:09 +02:00
651013fcf2 Merge pull request #1381 from AmruthPillai/fix/issue-with-private-resumes
fix(client): 🐛 do not allow private resumes to be viewable or downloadable through the link
2023-07-12 15:59:57 +02:00
1c2d796c50 fix(client): 🐛 do not allow private resumes to be viewable or downloadable through the link 2023-07-12 15:59:22 +02:00
5ef4bfcb6b Merge pull request #1379 from AmruthPillai/dependabot/github_actions/docker/setup-buildx-action-2.9.0
Bump docker/setup-buildx-action from 2.8.0 to 2.9.0
2023-07-12 14:47:30 +02:00
a305b6419e Merge pull request #1378 from AmruthPillai/dependabot/gradle/app/org.jetbrains.kotlin.android-1.9.0
Bump org.jetbrains.kotlin.android from 1.8.22 to 1.9.0 in /app
2023-07-12 14:47:22 +02:00
dcfdff2abe Bump docker/setup-buildx-action from 2.8.0 to 2.9.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.8.0 to 2.9.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.8.0...v2.9.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 03:00:37 +00:00
de77a6039a Bump org.jetbrains.kotlin.android from 1.8.22 to 1.9.0 in /app
Bumps [org.jetbrains.kotlin.android](https://github.com/JetBrains/kotlin) from 1.8.22 to 1.9.0.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.8.22...v1.9.0)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin.android
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 02:23:11 +00:00
e44eab55c3 Merge pull request #1376 from AmruthPillai/dependabot/github_actions/docker/setup-buildx-action-2.8.0
Bump docker/setup-buildx-action from 2.7.0 to 2.8.0
2023-07-04 13:49:24 +02:00
c73ad9a627 Bump docker/setup-buildx-action from 2.7.0 to 2.8.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 02:45:58 +00:00
be3d4a4f7c Merge pull request #1374 from AmruthPillai/i18n_main
New Crowdin updates
2023-07-02 08:01:43 +02:00
c86792901b New translations builder.json (Swedish) 2023-06-29 15:56:08 +02:00
a2645a10f0 Merge pull request #1369 from AmruthPillai/dependabot/github_actions/docker/setup-buildx-action-2.7.0
Bump docker/setup-buildx-action from 2.6.0 to 2.7.0
2023-06-22 16:13:22 +02:00
772d8a0d41 Merge pull request #1368 from AmruthPillai/dependabot/github_actions/docker/build-push-action-4.1.1
Bump docker/build-push-action from 4.1.0 to 4.1.1
2023-06-22 16:13:14 +02:00
57348c13b2 Merge pull request #1367 from AmruthPillai/dependabot/github_actions/docker/metadata-action-4.6.0
Bump docker/metadata-action from 4.5.0 to 4.6.0
2023-06-22 16:13:03 +02:00
ffb92a967e Merge pull request #1366 from AmruthPillai/dependabot/docker/server/playwright-v1.35.1-focal
Bump playwright from v1.35.0-focal to v1.35.1-focal in /server
2023-06-22 16:10:59 +02:00
ed933f0452 Bump docker/setup-buildx-action from 2.6.0 to 2.7.0
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.6.0...v2.7.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 02:58:00 +00:00
26e67fe457 Bump docker/build-push-action from 4.1.0 to 4.1.1
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 02:57:55 +00:00
4507d2d032 Bump docker/metadata-action from 4.5.0 to 4.6.0
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v4.5.0...v4.6.0)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 02:57:50 +00:00
97b43d2fc9 Bump playwright from v1.35.0-focal to v1.35.1-focal in /server
Bumps playwright from v1.35.0-focal to v1.35.1-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 02:57:47 +00:00
88916a54d3 Merge pull request #1362 from AmruthPillai/i18n_main
New Crowdin updates
2023-06-15 10:04:54 +02:00
3aa57ebce8 Merge pull request #1358 from AmruthPillai/dependabot/gradle/app/org.jetbrains.kotlin.android-1.8.22
Bump org.jetbrains.kotlin.android from 1.8.21 to 1.8.22 in /app
2023-06-15 10:04:48 +02:00
623d300da3 Merge pull request #1357 from AmruthPillai/dependabot/github_actions/actions/checkout-3.5.3
Bump actions/checkout from 3.5.2 to 3.5.3
2023-06-15 10:04:40 +02:00
10d7562e7a Merge pull request #1356 from AmruthPillai/dependabot/docker/client/node-20-alpine
Bump node from 18-alpine to 20-alpine in /client
2023-06-15 10:04:33 +02:00
807e747018 Merge pull request #1355 from AmruthPillai/dependabot/github_actions/docker/build-push-action-4.1.0
Bump docker/build-push-action from 4.0.0 to 4.1.0
2023-06-15 10:04:26 +02:00
1301cdce12 Merge pull request #1354 from AmruthPillai/dependabot/docker/server/playwright-v1.35.0-focal
Bump playwright from v1.34.3-focal to v1.35.0-focal in /server
2023-06-15 10:04:17 +02:00
c5fcbf5982 New translations common.json (Dutch) 2023-06-14 17:29:51 +02:00
a9daaeba55 New translations builder.json (Dutch) 2023-06-14 17:29:49 +02:00
dc33c35433 Bump org.jetbrains.kotlin.android from 1.8.21 to 1.8.22 in /app
Bumps [org.jetbrains.kotlin.android](https://github.com/JetBrains/kotlin) from 1.8.21 to 1.8.22.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v1.8.22/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.8.21...v1.8.22)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin.android
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 03:00:24 +00:00
189605484a Bump actions/checkout from 3.5.2 to 3.5.3
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.2 to 3.5.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.5.2...v3.5.3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 02:58:29 +00:00
bb3e93d976 Bump node from 18-alpine to 20-alpine in /client
Bumps node from 18-alpine to 20-alpine.

---
updated-dependencies:
- dependency-name: node
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 02:58:18 +00:00
44d692aad1 Bump docker/build-push-action from 4.0.0 to 4.1.0
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 02:58:18 +00:00
bb0ca824b8 Bump playwright from v1.34.3-focal to v1.35.0-focal in /server
Bumps playwright from v1.34.3-focal to v1.35.0-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 02:57:59 +00:00
a0b8de4ab4 Merge pull request #1347 from AlexTu2/main
add Discord icon
2023-06-09 09:06:47 +02:00
f73a80c684 add Discord icon 2023-06-08 22:52:38 -04:00
0eddb7d5a3 remove old ci action workflows 2023-06-08 16:28:08 +02:00
6ff36cb1e4 fix build-deploy script 2023-06-08 16:27:14 +02:00
c513d68813 Add push: true to Build and Deploy Script 2023-06-08 15:25:16 +02:00
8d3f4e031c update dependencies, rework on github actions ci script 2023-06-08 15:05:15 +02:00
3aa8778a67 Update README.md 2023-06-08 13:51:31 +02:00
d4a3cec3c2 create new github actions workflow "Build and Push Multi-Arch Docker Images using Manifest" 2023-06-08 13:48:35 +02:00
96eca65ed0 add back health module to server 2023-06-07 20:45:21 +02:00
30fd283898 use matrix to build amd64/arm64 images 2023-06-07 20:25:12 +02:00
726ea7312b add arm to qemu platform 2023-06-07 20:10:25 +02:00
f3a7180d4b change run script for docker file 2023-06-07 19:52:48 +02:00
0173ce32c3 Implement Turborepo to include Build Caching in CI/CD 2023-06-07 19:50:48 +02:00
d4b6c16bf9 updating dependencies, fixing server reloads 2023-06-07 18:39:14 +02:00
c571f201d3 update version 2023-06-07 17:18:33 +02:00
e4ecf50ed4 add missing backslash 2023-06-07 17:18:18 +02:00
5ee99cfdab attempt to fix internal server error 2023-06-07 12:06:04 +02:00
72e610b50d Merge pull request #1323 from AmruthPillai/i18n_main
New Crowdin updates
2023-05-30 16:53:03 +02:00
ba34787333 Merge pull request #1326 from AmruthPillai/dependabot/docker/server/playwright-v1.34.3-focal
Bump playwright from v1.34.0-focal to v1.34.3-focal in /server
2023-05-30 16:52:54 +02:00
e11b0e6224 Bump playwright from v1.34.0-focal to v1.34.3-focal in /server
Bumps playwright from v1.34.0-focal to v1.34.3-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-29 02:58:15 +00:00
c78ee18e05 New translations common.json (Norwegian) 2023-05-26 14:15:52 +02:00
5f5b484243 New translations builder.json (Norwegian) 2023-05-26 14:15:50 +02:00
bcc451a6a1 Merge pull request #1320 from AmruthPillai/dependabot/docker/server/playwright-v1.34.0-focal
Bump playwright from v1.32.3-focal to v1.34.0-focal in /server
2023-05-23 21:09:04 +02:00
55a7f6a556 Merge pull request #1315 from dev-abir/main
add icons
2023-05-23 21:08:55 +02:00
e9b6265c60 Merge pull request #1304 from AmruthPillai/dependabot/gradle/app/org.jetbrains.kotlin.android-1.8.21
Bump org.jetbrains.kotlin.android from 1.8.20 to 1.8.21 in /app
2023-05-23 21:08:40 +02:00
2e2f3271c9 Bump playwright from v1.32.3-focal to v1.34.0-focal in /server
Bumps playwright from v1.32.3-focal to v1.34.0-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-22 02:58:19 +00:00
fa3e92d643 Merge pull request #1314 from jobayer12/issue-1309
Rename the Work Experience label.
2023-05-17 10:33:51 +02:00
1f9b52eda6 Added the experience section on the leftSidebar. 2023-05-13 17:58:27 +06:00
7074b6fc76 add icon packs according to #834 2023-05-09 22:10:31 +05:30
b4c4fb94f7 Renamed the Work Experience label. 2023-05-08 10:44:57 +06:00
22bdb64fa9 Added the experience section on the leftSidebar. 2023-05-08 10:42:27 +06:00
af02158d05 Bump org.jetbrains.kotlin.android from 1.8.20 to 1.8.21 in /app
Bumps [org.jetbrains.kotlin.android](https://github.com/JetBrains/kotlin) from 1.8.20 to 1.8.21.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.8.20...v1.8.21)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin.android
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-01 03:03:01 +00:00
6a8db92fc4 fix bugs 2023-04-21 01:13:32 +02:00
6f219ef17e bump up version to 3.7.2 2023-04-20 23:02:14 +02:00
667e51abdc implement larger pool size for db 2023-04-20 22:52:24 +02:00
7b98277c32 Merge pull request #1294 from AmruthPillai/dependabot/docker/server/playwright-v1.32.3-focal
Bump playwright from v1.32.1-focal to v1.32.3-focal in /server
2023-04-20 22:34:03 +02:00
77ed7ed8be Merge pull request #1293 from AmruthPillai/dependabot/github_actions/actions/checkout-3.5.2
Bump actions/checkout from 3.5.0 to 3.5.2
2023-04-20 22:33:56 +02:00
ce584d9326 Merge pull request #1289 from AmruthPillai/i18n_main
New Crowdin updates
2023-04-20 22:33:48 +02:00
5685352375 Merge pull request #1286 from AmruthPillai/dependabot/github_actions/martinbeentjes/npm-get-version-action-1.3.1
Bump martinbeentjes/npm-get-version-action from 1.2.3 to 1.3.1
2023-04-20 22:33:38 +02:00
036b2917a6 update dependencies, attempt to fix server restart issue 2023-04-20 22:33:11 +02:00
e972320722 Bump playwright from v1.32.1-focal to v1.32.3-focal in /server
Bumps playwright from v1.32.1-focal to v1.32.3-focal.

---
updated-dependencies:
- dependency-name: playwright
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-17 02:59:32 +00:00
4ac1e9db35 Bump actions/checkout from 3.5.0 to 3.5.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.5.0...v3.5.2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-17 02:58:06 +00:00
9fe4403b40 New translations modals.json (Nepali) 2023-04-11 08:37:33 +02:00
4f4084ab45 New translations modals.json (Amharic) 2023-04-11 08:37:32 +02:00
72227dc9ab New translations modals.json (Odia) 2023-04-11 08:37:31 +02:00
d44795a421 New translations modals.json (Kannada) 2023-04-11 08:37:30 +02:00
e9584144e4 New translations modals.json (Malayalam) 2023-04-11 08:37:30 +02:00
bbedfa3b75 New translations modals.json (Hindi) 2023-04-11 08:37:29 +02:00
03f7d74096 New translations modals.json (Marathi) 2023-04-11 08:37:28 +02:00
a62693d611 New translations modals.json (Bengali) 2023-04-11 08:37:27 +02:00
421f195e1e New translations modals.json (Tamil) 2023-04-11 08:37:26 +02:00
b22dff523f New translations modals.json (Khmer) 2023-04-11 08:37:25 +02:00
58d0c6e315 New translations modals.json (Persian) 2023-04-11 08:37:24 +02:00
36178cac22 New translations landing.json (Nepali) 2023-04-11 08:37:24 +02:00
376786fa25 New translations landing.json (Amharic) 2023-04-11 08:37:23 +02:00
efceda1c55 New translations landing.json (Odia) 2023-04-11 08:37:22 +02:00
047e317c51 New translations landing.json (Kannada) 2023-04-11 08:37:21 +02:00
36ad63adb9 New translations landing.json (Malayalam) 2023-04-11 08:37:20 +02:00
45c88caf58 New translations landing.json (Hindi) 2023-04-11 08:37:20 +02:00
ca11a9217a New translations landing.json (Marathi) 2023-04-11 08:37:19 +02:00
fd6fbbba77 New translations landing.json (Bengali) 2023-04-11 08:37:18 +02:00
e2fb83bda9 New translations landing.json (Tamil) 2023-04-11 08:37:17 +02:00
40567e8f61 New translations landing.json (Khmer) 2023-04-11 08:37:16 +02:00
64c899b159 New translations modals.json (Indonesian) 2023-04-11 08:37:12 +02:00
b267cc4097 New translations modals.json (Vietnamese) 2023-04-11 08:37:11 +02:00
f4657b6592 New translations modals.json (Chinese Simplified) 2023-04-11 08:37:10 +02:00
6a2f512638 New translations modals.json (Ukrainian) 2023-04-11 08:37:10 +02:00
499005c21f New translations modals.json (Turkish) 2023-04-11 08:37:09 +02:00
0e18d3fc48 New translations modals.json (Serbian (Cyrillic)) 2023-04-11 08:37:08 +02:00
3b831c4eb4 New translations modals.json (Russian) 2023-04-11 08:37:07 +02:00
40564944ef New translations modals.json (Polish) 2023-04-11 08:37:06 +02:00
fdbb6d2e5b New translations modals.json (Norwegian) 2023-04-11 08:37:06 +02:00
398cd63082 New translations modals.json (Dutch) 2023-04-11 08:37:05 +02:00
efd4af14e5 New translations modals.json (Japanese) 2023-04-11 08:37:03 +02:00
889697fc31 New translations modals.json (Hungarian) 2023-04-11 08:37:02 +02:00
3aedf6618d New translations modals.json (Finnish) 2023-04-11 08:37:01 +02:00
abf42e13af New translations modals.json (Greek) 2023-04-11 08:37:00 +02:00
40bcbebadd New translations modals.json (Danish) 2023-04-11 08:36:59 +02:00
364f2e6d49 New translations modals.json (Czech) 2023-04-11 08:36:58 +02:00
7e5dfd75f9 New translations modals.json (Catalan) 2023-04-11 08:36:57 +02:00
b94d10c614 New translations modals.json (Bulgarian) 2023-04-11 08:36:57 +02:00
8c40b417ec New translations modals.json (Arabic) 2023-04-11 08:36:56 +02:00
1f17dfe6ea New translations landing.json (Indonesian) 2023-04-11 08:36:55 +02:00
be6ea1a224 New translations landing.json (Vietnamese) 2023-04-11 08:36:54 +02:00
583e9effae New translations builder.json (Nepali) 2023-04-11 08:36:53 +02:00
619b2757c8 New translations builder.json (Amharic) 2023-04-11 08:36:52 +02:00
9e27eee029 New translations builder.json (Odia) 2023-04-11 08:36:51 +02:00
c2d3c611e1 New translations builder.json (Kannada) 2023-04-11 08:36:50 +02:00
735f589e54 New translations builder.json (Malayalam) 2023-04-11 08:36:49 +02:00
1e3d6fbb77 New translations builder.json (Hindi) 2023-04-11 08:36:48 +02:00
3995e7159a New translations builder.json (Marathi) 2023-04-11 08:36:47 +02:00
6662acf0b0 New translations builder.json (Bengali) 2023-04-11 08:36:47 +02:00
feb8abca95 New translations builder.json (Tamil) 2023-04-11 08:36:46 +02:00
75c83bd91d New translations builder.json (Khmer) 2023-04-11 08:36:45 +02:00
f6d5897ed3 New translations landing.json (Chinese Simplified) 2023-04-11 08:36:44 +02:00
ed356763a1 New translations landing.json (Ukrainian) 2023-04-11 08:36:43 +02:00
4847246d84 New translations landing.json (Turkish) 2023-04-11 08:36:42 +02:00
a0ae6cb77e New translations landing.json (Serbian (Cyrillic)) 2023-04-11 08:36:42 +02:00
2aa2550be0 New translations landing.json (Russian) 2023-04-11 08:36:41 +02:00
df39913d49 New translations landing.json (Polish) 2023-04-11 08:36:40 +02:00
2225505d48 New translations landing.json (Norwegian) 2023-04-11 08:36:39 +02:00
afe20e61ee New translations landing.json (Dutch) 2023-04-11 08:36:38 +02:00
794e9c6511 New translations landing.json (Japanese) 2023-04-11 08:36:37 +02:00
e7e423bf29 New translations landing.json (Hungarian) 2023-04-11 08:36:35 +02:00
2173297207 New translations landing.json (Finnish) 2023-04-11 08:36:34 +02:00
b091cfa474 New translations landing.json (Greek) 2023-04-11 08:36:33 +02:00
057bb3a414 New translations landing.json (Czech) 2023-04-11 08:36:33 +02:00
c1442c9acc New translations landing.json (Catalan) 2023-04-11 08:36:32 +02:00
977f1beafd New translations landing.json (Bulgarian) 2023-04-11 08:36:31 +02:00
39ee710e97 New translations landing.json (Arabic) 2023-04-11 08:36:30 +02:00
1d1841c8db New translations builder.json (Indonesian) 2023-04-11 08:36:29 +02:00
3e44774ed4 New translations builder.json (Vietnamese) 2023-04-11 08:36:28 +02:00
9e2fa01896 New translations builder.json (Chinese Simplified) 2023-04-11 08:36:28 +02:00
7811f9840c New translations builder.json (Ukrainian) 2023-04-11 08:36:27 +02:00
34425c6200 New translations builder.json (Turkish) 2023-04-11 08:36:26 +02:00
46f9fc549a New translations builder.json (Serbian (Cyrillic)) 2023-04-11 08:36:25 +02:00
237abf359b New translations builder.json (Russian) 2023-04-11 08:36:24 +02:00
c5e8739009 New translations builder.json (Polish) 2023-04-11 08:36:23 +02:00
0ea8040977 New translations builder.json (Norwegian) 2023-04-11 08:36:22 +02:00
1f10e8efe3 New translations builder.json (Dutch) 2023-04-11 08:36:21 +02:00
8c2688670e New translations builder.json (Japanese) 2023-04-11 08:36:20 +02:00
bc5d49b568 New translations builder.json (Hungarian) 2023-04-11 08:36:19 +02:00
27ea84e720 New translations builder.json (Finnish) 2023-04-11 08:36:17 +02:00
0becb66bfd New translations builder.json (Greek) 2023-04-11 08:36:16 +02:00
11f88492e9 New translations builder.json (Czech) 2023-04-11 08:36:15 +02:00
ae3e01466f New translations builder.json (Catalan) 2023-04-11 08:36:14 +02:00
5d04dd8a83 New translations builder.json (Bulgarian) 2023-04-11 08:36:13 +02:00
52c15a8151 New translations builder.json (Arabic) 2023-04-11 08:36:12 +02:00
f6104e7051 Bump martinbeentjes/npm-get-version-action from 1.2.3 to 1.3.1
Bumps [martinbeentjes/npm-get-version-action](https://github.com/martinbeentjes/npm-get-version-action) from 1.2.3 to 1.3.1.
- [Release notes](https://github.com/martinbeentjes/npm-get-version-action/releases)
- [Commits](https://github.com/martinbeentjes/npm-get-version-action/compare/v1.2.3...v1.3.1)

---
updated-dependencies:
- dependency-name: martinbeentjes/npm-get-version-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 03:09:34 +00:00
ed710f6fe5 hotfix for docker CMD script 2023-04-06 15:41:00 +02:00
7e6e239d7f Merge pull request #1279 from AmruthPillai/i18n_main
New Crowdin updates
2023-04-06 15:04:42 +02:00
835f453384 New translations dashboard.json (Italian) 2023-04-05 22:56:45 +02:00
cc475ae1e9 New translations common.json (Italian) 2023-04-05 22:56:44 +02:00
a5249ec646 New translations modals.json (Italian) 2023-04-05 22:56:42 +02:00
d0e3090421 New translations landing.json (Italian) 2023-04-05 22:56:39 +02:00
14f68c8937 New translations builder.json (Italian) 2023-04-05 22:56:37 +02:00
322 changed files with 6681 additions and 4987 deletions

View File

@ -2,6 +2,7 @@
/app
# Build Artifacts
/schema/dist
/server/dist
/client/.next

View File

@ -1,12 +1,23 @@
{
"ignorePatterns": ["/app"],
"parser": "@typescript-eslint/parser",
"extends": ["plugin:@typescript-eslint/recommended"],
"plugins": ["@typescript-eslint/eslint-plugin", "simple-import-sort"],
"plugins": ["@typescript-eslint/eslint-plugin", "unused-imports", "simple-import-sort"],
"rules": {
// ESLint
"no-unused-vars": "off",
// Unused Imports
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{
"vars": "all",
"args": "none",
"varsIgnorePattern": "^_",
"argsIgnorePattern": "^_"
}
],
// Simple Import Sort
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",

2
.github/FUNDING.yml vendored
View File

@ -1,2 +1,2 @@
github: AmruthPillai
custom: https://paypal.me/RajaRajanA
custom: https://paypal.me/amruthde

View File

@ -1,43 +0,0 @@
---
name: Bug Report
about: Create a report to help improve
title: '[BUG] '
labels: bug
assignees: ''
---
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**Product Flavor**
- [ ] Managed (https://rxresu.me)
- [ ] Self Hosted
**To Reproduce**
<!-- Steps to reproduce the behavior: -->
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Desktop (please complete the following information):**
- OS: <!--[e.g. iOS]-->
- Browser <!--[e.g. chrome, safari]-->
- Version <!--[e.g. 22]-->
**Additional context**
<!-- Add any other context about the problem here. -->

75
.github/ISSUE_TEMPLATE/bug-report.yaml vendored Normal file
View File

@ -0,0 +1,75 @@
name: 🐞 Bug Report
description: Create a bug report to help improve Reactive Resume.
title: '[Bug] <title>'
labels: [Bug, Needs Triage]
assignees: 'AmruthPillai'
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: Yes, I have searched the existing issues
required: true
- type: dropdown
id: variant
attributes:
label: Product Variant
description: What variant of Reactive Resume are you using?
options:
- Cloud (http://rxresu.me)
- Self-Hosted
validations:
required: true
- type: textarea
attributes:
label: Current Behavior
description: A concise description of what you're experiencing.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
validations:
required: false
- type: textarea
attributes:
label: Steps To Reproduce
description: Detailed steps to reproduce the behavior, so that it can be easily diagnosed.
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: false
- type: dropdown
id: browsers
attributes:
label: What browsers are you seeing the problem on?
multiple: true
options:
- Firefox
- Chrome
- Safari
- Microsoft Edge
validations:
required: false
- type: textarea
attributes:
label: Anything else?
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false

View File

@ -1,23 +0,0 @@
---
name: Feature Request
about: Suggest an idea for this project
title: '[FEATURE] '
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

View File

@ -0,0 +1,22 @@
name: ✨ Feature Request
description: Suggest an feature or idea that you would like to see in Reactive Resume.
title: '[Feature] <title>'
labels: [Feature, Needs Triage]
assignees: 'AmruthPillai'
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the feature you requested.
options:
- label: Yes, I have searched the existing issues and it doesn't exist
required: true
- type: textarea
attributes:
label: Feature Description
description: A concise description of what feature you would like to see in Reactive Resume.
validations:
required: true

142
.github/workflows/build-deploy.yml vendored Normal file
View File

@ -0,0 +1,142 @@
name: Build and Deploy
on:
workflow_dispatch:
release:
types: [published]
jobs:
build-amd64:
name: Build (amd64)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image:
- client
- server
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
- name: Retrieve version from package.json
id: version
uses: martinbeentjes/npm-get-version-action@v1.3.1
- name: Docker Metadaata
id: meta
uses: docker/metadata-action@v4.6.0
with:
images: amruthpillai/reactive-resume
tags: |
type=raw,value=${{ matrix.image }}-latest
type=raw,value=${{ matrix.image }}-${{ steps.version.outputs.current-version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.9.1
- name: Login to Docker Hub
uses: docker/login-action@v2.2.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2.2.0
with:
registry: ghcr.io
username: $GITHUB_REPOSITORY_OWNER
password: ${{ secrets.GH_TOKEN }}
- name: Build and Push
id: build
uses: docker/build-push-action@v4.1.1
with:
context: .
push: true
platforms: linux/amd64
file: ${{ matrix.image }}/Dockerfile
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta.outputs.tags }}
build-args: |
TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
build-arm64:
name: Build (arm64)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image:
- client
- server
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.3
- name: Retrieve version from package.json
id: version
uses: martinbeentjes/npm-get-version-action@v1.3.1
- name: Docker Metadaata
id: meta
uses: docker/metadata-action@v4.6.0
with:
images: amruthpillai/reactive-resume
tags: |
type=raw,value=${{ matrix.image }}-arm64-latest
type=raw,value=${{ matrix.image }}-arm64-${{ steps.version.outputs.current-version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.9.1
- name: Login to Docker Hub
uses: docker/login-action@v2.2.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2.2.0
with:
registry: ghcr.io
username: $GITHUB_REPOSITORY_OWNER
password: ${{ secrets.GH_TOKEN }}
- name: Build and Push
id: build
uses: docker/build-push-action@v4.1.1
with:
context: .
push: true
platforms: linux/arm64
file: ${{ matrix.image }}/Dockerfile
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta.outputs.tags }}
build-args: |
TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build-amd64
steps:
- name: Install DigitalOcean CLI
uses: digitalocean/action-doctl@v2.3.0
with:
token: ${{ secrets.DIGITALOCEAN_TOKEN }}
- name: Create Deployment with Latest Version
run: doctl apps create-deployment ${{ secrets.DIGITALOCEAN_APP_ID }} --wait --force-rebuild

View File

@ -1,28 +0,0 @@
name: Deploy Latest Version on DigitalOcean
on:
workflow_run:
workflows:
- Build and Push Docker Image
types:
- completed
jobs:
on-success:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Install DigitalOcean CLI
uses: digitalocean/action-doctl@v2.3.0
with:
token: ${{ secrets.DIGITALOCEAN_TOKEN }}
- name: Create Deployment with Latest Version
run: doctl apps create-deployment ${{ secrets.DIGITALOCEAN_APP_ID }} --wait --force-rebuild
on-failure:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- name: Abruptly end the worklfow
run: exit 1

View File

@ -1,58 +0,0 @@
name: Build and Push Docker Image
on:
workflow_dispatch:
release:
types: [published]
jobs:
build_matrix:
name: Build and Push Docker Image
runs-on: ubuntu-latest
strategy:
matrix:
image: [client, server]
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.0
- id: version
name: App Version
uses: martinbeentjes/npm-get-version-action@v1.2.3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.1.0
with:
platforms: amd64
- id: buildx
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.5.0
- name: Login to Docker Hub
uses: docker/login-action@v2.1.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2.1.0
with:
registry: ghcr.io
username: $GITHUB_REPOSITORY_OWNER
password: ${{ secrets.GH_TOKEN }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v4.0.0
with:
context: .
push: true
platforms: linux/amd64
file: ${{ matrix.image }}/Dockerfile
tags: |
amruthpillai/reactive-resume:${{ matrix.image }}-latest
amruthpillai/reactive-resume:${{ matrix.image }}-${{ steps.version.outputs.current-version }}
ghcr.io/amruthpillai/reactive-resume:${{ matrix.image }}-latest
ghcr.io/amruthpillai/reactive-resume:${{ matrix.image }}-${{ steps.version.outputs.current-version }}

4
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Environment Variables
.env
.env.*
*.env
!.env.gitpod
!.env.example
@ -12,3 +13,6 @@ node_modules
# Intellij
.idea
# Turborepo
.turbo

View File

@ -30,7 +30,7 @@ ports:
onOpen: ignore
visibility: private
# Client
# Server
- port: 3100
onOpen: ignore
visibility: public

View File

@ -1,4 +1,4 @@
<img src="https://rxresu.me/images/logos/logo.png" alt="Reactive Resume" width="256px" height="256px" />
<img src="/client/public/logo/dark.svg" alt="Reactive Resume" width="256px" height="256px" />
# Reactive Resume
@ -16,6 +16,16 @@ Reactive Resume is a free and open source resume builder thats 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? Its as easy as editing 3 values and youre done. You dont 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.
## ❗️ Important Notice
Due to increasing and recurring costs from Google Cloud, I would have to take down the Version 1 and Version 2 editions of the application that live on https://v1.rxresu.me/ and https://v2.rxresu.me/ respectively.
I plan to take down the servers on 1st September 2023, so if you have any data on the earlier versions, please migrate it over to the latest version on https://rxresu.me/ as soon as you can.
The current version will remain unchanged, and is still actively supported. It will have no effect on these changes.
Thank you to the 400,000+ people using the app! 🙏
## Table of Contents
- [Reactive Resume](#reactive-resume)
@ -30,6 +40,7 @@ You have complete control over what goes into your resume, how it looks, what co
- [Donations](#donations)
- [GitHub Sponsor](#github-sponsor)
- [PayPal](#paypal)
- [GitHub Star History](#github-star-history)
- [Infrastructure](#infrastructure)
- [Contributors Wall](#contributors-wall)
- [License](#license)

View File

@ -1,7 +1,7 @@
plugins {
id 'com.android.application' version '7.4.2' apply false
id 'com.android.library' version '7.4.2' apply false
id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
}
task clean(type: Delete) {

View File

@ -12,6 +12,9 @@
"@next/next/no-img-element": "off",
"@next/next/no-sync-scripts": "off",
// React
"react/no-unescaped-entities": "off",
// React Hooks
"react-hooks/exhaustive-deps": "off",

View File

@ -21,8 +21,10 @@ COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=dependencies /app/schema/node_modules ./schema/node_modules
COPY --from=dependencies /app/client/node_modules ./client/node_modules
RUN pnpm run --filter schema build \
&& pnpm run --filter client build
ARG TURBO_TOKEN
ENV TURBO_TOKEN=$TURBO_TOKEN
RUN pnpm exec turbo --filter client build
FROM base as production
@ -40,7 +42,4 @@ EXPOSE 3000
ENV PORT 3000
HEALTHCHECK --interval=30s --timeout=20s --retries=3 --start-period=15s \
CMD curl -fSs localhost:3000 || exit 1
CMD [ "pnpm", "run", "--filter", "client", "start" ]

View File

@ -0,0 +1,43 @@
FROM node:20-alpine AS base
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn global add pnpm && pnpm build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "client/server.js"]

View File

@ -15,7 +15,7 @@
.controller {
@apply z-20 flex items-center justify-center shadow-lg;
@apply flex rounded-l-full rounded-r-full px-4;
@apply bg-neutral-50 dark:bg-neutral-800;
@apply bg-zinc-50 dark:bg-zinc-900;
@apply opacity-70 transition-opacity duration-200 hover:opacity-100;
> button {
@ -23,6 +23,6 @@
}
> hr {
@apply mx-3 h-5 w-0.5 bg-neutral-900/40 dark:bg-neutral-50/20;
@apply mx-3 h-5 w-0.5 bg-zinc-900/40 dark:bg-zinc-50/20;
}
}

View File

@ -12,7 +12,6 @@ import {
ZoomOut,
} from '@mui/icons-material';
import { ButtonBase, Divider, Tooltip, useMediaQuery, useTheme } from '@mui/material';
import clsx from 'clsx';
import dayjs from 'dayjs';
import get from 'lodash/get';
import { useTranslation } from 'next-i18next';
@ -26,6 +25,7 @@ import { printResumeAsPdf, PrintResumeAsPdfParams } from '@/services/printer';
import { togglePageBreakLine, togglePageOrientation, toggleSidebar } from '@/store/build/buildSlice';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import getResumeUrl from '@/utils/getResumeUrl';
import { cn } from '@/utils/styles';
import styles from './ArtboardController.module.scss';
@ -60,7 +60,7 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
const url = getResumeUrl(resume, { withHost: true });
await navigator.clipboard.writeText(url);
toast.success(t<string>('common.toast.success.resume-link-copied'));
toast.success(t('common.toast.success.resume-link-copied'));
};
const handleExportPDF = async () => {
@ -77,40 +77,40 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
return (
<div
className={clsx({
className={cn({
[styles.container]: true,
[styles.pushLeft]: left.open,
[styles.pushRight]: right.open,
})}
>
<div className={styles.controller}>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.undo')}>
<ButtonBase onClick={handleUndo} className={clsx({ 'pointer-events-none opacity-50': past.length < 2 })}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.undo')}>
<ButtonBase onClick={handleUndo} className={cn({ 'pointer-events-none opacity-50': past.length < 2 })}>
<UndoOutlined fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.redo')}>
<ButtonBase onClick={handleRedo} className={clsx({ 'pointer-events-none opacity-50': future.length === 0 })}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.redo')}>
<ButtonBase onClick={handleRedo} className={cn({ 'pointer-events-none opacity-50': future.length === 0 })}>
<RedoOutlined fontSize="medium" />
</ButtonBase>
</Tooltip>
<Divider />
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-in')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.zoom-in')}>
<ButtonBase onClick={() => zoomIn(0.25)}>
<ZoomIn fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-out')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.zoom-out')}>
<ButtonBase onClick={() => zoomOut(0.25)}>
<ZoomOut fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.center-artboard')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.center-artboard')}>
<ButtonBase onClick={() => centerView(0.95)}>
<FilterCenterFocus fontSize="medium" />
</ButtonBase>
@ -120,10 +120,10 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
{isDesktop && (
<>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-orientation')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-orientation')}>
<ButtonBase
onClick={handleTogglePageOrientation}
className={clsx({ 'pointer-events-none opacity-50': pages.length === 1 })}
className={cn({ 'pointer-events-none opacity-50': pages.length === 1 })}
>
{orientation === 'vertical' ? (
<AlignHorizontalCenter fontSize="medium" />
@ -133,13 +133,13 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-page-break-line')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-page-break-line')}>
<ButtonBase onClick={handleTogglePageBreakLine}>
<InsertPageBreak fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-sidebars')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-sidebars')}>
<ButtonBase onClick={handleToggleSidebar}>
<ViewSidebar fontSize="medium" />
</ButtonBase>
@ -149,13 +149,13 @@ const ArtboardController: React.FC<ReactZoomPanPinchHandlers> = ({ zoomIn, zoomO
</>
)}
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.copy-link')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.copy-link')}>
<ButtonBase onClick={handleCopyLink}>
<Link fontSize="medium" />
</ButtonBase>
</Tooltip>
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.export-pdf')}>
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.export-pdf')}>
<ButtonBase onClick={handleExportPDF} disabled={isLoading}>
<Download fontSize="medium" />
</ButtonBase>

View File

@ -1,7 +1,7 @@
.center {
@apply mx-0 flex flex-grow pt-12 lg:pt-16;
@apply transition-[margin-left,margin-right] duration-200;
@apply bg-neutral-200 dark:bg-neutral-900;
@apply bg-zinc-100 dark:bg-zinc-900;
}
.wrapper {

View File

@ -1,9 +1,9 @@
import clsx from 'clsx';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
import { useAppSelector } from '@/store/hooks';
import { cn } from '@/utils/styles';
import ArtboardController from './ArtboardController';
import styles from './Center.module.scss';
@ -19,7 +19,7 @@ const Center = () => {
if (isEmpty(resume)) return null;
return (
<div className={clsx(styles.center)}>
<div className={cn(styles.center)}>
<Header />
<TransformWrapper
@ -35,7 +35,7 @@ const Center = () => {
<>
<TransformComponent wrapperClass={styles.wrapper}>
<div
className={clsx({
className={cn({
[styles.artboard]: true,
'flex-col': orientation === 'vertical',
})}

View File

@ -1,10 +1,10 @@
.header {
@apply mx-0 flex justify-between shadow;
@apply bg-neutral-800 text-neutral-100;
@apply bg-zinc-900 text-zinc-100;
@apply transition-[margin-left,margin-right] duration-200;
button > svg {
@apply text-base text-neutral-100;
@apply text-base text-zinc-100;
}
}

View File

@ -20,14 +20,13 @@ import {
useMediaQuery,
useTheme,
} from '@mui/material';
import { Resume } from '@reactive-resume/schema';
import clsx from 'clsx';
import get from 'lodash/get';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { useEffect, useMemo, useState } from 'react';
import toast from 'react-hot-toast';
import { useMutation } from 'react-query';
import { Resume } from 'schema';
import { RESUMES_QUERY } from '@/constants/index';
import { ServerError } from '@/services/axios';
@ -37,6 +36,7 @@ import { setSidebarState, toggleSidebar } from '@/store/build/buildSlice';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { setModalState } from '@/store/modal/modalSlice';
import getResumeUrl from '@/utils/getResumeUrl';
import { cn } from '@/utils/styles';
import styles from './Header.module.scss';
@ -102,7 +102,7 @@ const Header = () => {
},
},
},
})
}),
);
};
@ -132,14 +132,14 @@ const Header = () => {
const url = getResumeUrl(resume, { withHost: true });
await navigator.clipboard.writeText(url);
toast.success(t<string>('common.toast.success.resume-link-copied'));
toast.success(t('common.toast.success.resume-link-copied'));
};
return (
<AppBar elevation={0} position="fixed">
<Toolbar
variant="regular"
className={clsx({
className={cn({
[styles.header]: true,
[styles.pushLeft]: left.open,
[styles.pushRight]: right.open,
@ -165,14 +165,14 @@ const Header = () => {
<ListItemIcon>
<DriveFileRenameOutline className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.rename')}</ListItemText>
<ListItemText>{t('builder.header.menu.rename')}</ListItemText>
</MenuItem>
<MenuItem onClick={handleDuplicate}>
<ListItemIcon>
<CopyAll className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.duplicate')}</ListItemText>
<ListItemText>{t('builder.header.menu.duplicate')}</ListItemText>
</MenuItem>
{resume.public ? (
@ -180,27 +180,27 @@ const Header = () => {
<ListItemIcon>
<LinkIcon className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.share-link')}</ListItemText>
<ListItemText>{t('builder.header.menu.share-link')}</ListItemText>
</MenuItem>
) : (
<Tooltip arrow placement="right" title={t<string>('builder.header.menu.tooltips.share-link')}>
<Tooltip arrow placement="right" title={t('builder.header.menu.tooltips.share-link')}>
<div>
<MenuItem>
<ListItemIcon>
<LinkIcon className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.share-link')}</ListItemText>
<ListItemText>{t('builder.header.menu.share-link')}</ListItemText>
</MenuItem>
</div>
</Tooltip>
)}
<Tooltip arrow placement="right" title={t<string>('builder.header.menu.tooltips.delete')}>
<Tooltip arrow placement="right" title={t('builder.header.menu.tooltips.delete')}>
<MenuItem onClick={handleDelete}>
<ListItemIcon>
<Delete className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.header.menu.delete')}</ListItemText>
<ListItemText>{t('builder.header.menu.delete')}</ListItemText>
</MenuItem>
</Tooltip>
</Menu>

View File

@ -18,8 +18,8 @@
content: 'Page Break';
top: calc(297mm - 19px);
@apply absolute w-full border-b border-dashed border-neutral-800/75;
@apply flex items-end justify-end pr-2 pb-0.5 text-xs font-bold text-neutral-800/75;
@apply absolute w-full border-b border-dashed border-zinc-900/75;
@apply flex items-end justify-end pr-2 pb-0.5 text-xs font-bold text-zinc-900/75;
@apply print:hidden;
:global(.preview-mode) &,

View File

@ -1,10 +1,10 @@
import { css } from '@emotion/css';
import { CustomCSS, PageConfig, ThemeConfig, Typography } from '@reactive-resume/schema';
import clsx from 'clsx';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { useTranslation } from 'next-i18next';
import { useMemo } from 'react';
import { CustomCSS, PageConfig, ThemeConfig, Typography } from 'schema';
import { useAppSelector } from '@/store/hooks';
import templateMap from '@/templates/templateMap';
@ -49,9 +49,7 @@ const Page: React.FC<Props> = ({ page, showPageNumbers = false }) => {
{TemplatePage && <TemplatePage page={page} />}
</div>
{showPageNumbers && (
<h4 className={styles.pageNumber}>{`${t<string>('builder.common.glossary.page')} ${page + 1}`}</h4>
)}
{showPageNumbers && <h4 className={styles.pageNumber}>{`${t('builder.common.glossary.page')} ${page + 1}`}</h4>}
</div>
);
};

View File

@ -1,12 +1,12 @@
.container {
@apply h-screen w-[95vw] md:w-[70vw] lg:w-[50vw] xl:w-[30vw] 2xl:w-[28vw];
@apply bg-neutral-50 text-neutral-900 dark:bg-neutral-900 dark:text-neutral-50;
@apply relative flex border-r-2 border-neutral-50/10;
@apply bg-zinc-100 text-zinc-900 dark:bg-zinc-900 dark:text-zinc-100;
@apply relative flex border-r-2 border-zinc-100/10;
nav {
@apply absolute inset-y-0 left-0;
@apply w-14 py-4 md:w-16 md:px-2;
@apply bg-neutral-100 shadow dark:bg-neutral-800;
@apply bg-zinc-100 shadow dark:bg-zinc-900;
@apply flex flex-col items-center justify-between;
hr {
@ -29,7 +29,7 @@
> section {
@apply grid gap-4;
@apply pt-5 pb-7 first:pt-0;
@apply border-b border-neutral-900/10 last:border-b-0 dark:border-neutral-50/10;
@apply border-b border-zinc-900/10 last:border-b-0 dark:border-zinc-100/10;
hr {
@apply my-2;

View File

@ -1,14 +1,14 @@
import { Add, Star } from '@mui/icons-material';
import { Button, Divider, IconButton, SwipeableDrawer, Tooltip, useMediaQuery, useTheme } from '@mui/material';
import { Section as SectionRecord } from '@reactive-resume/schema';
import cloneDeep from 'lodash/cloneDeep';
import get from 'lodash/get';
import Link from 'next/link';
import { useTranslation } from 'next-i18next';
import React, { ReactComponentElement, useMemo } from 'react';
import { Section as SectionRecord } from 'schema';
import { validate } from 'uuid';
import Logo from '@/components/shared/Logo';
import Icon from '@/components/shared/Icon';
import { getCustomSections, getSectionsByType, left } from '@/config/sections';
import { setSidebarState } from '@/store/build/buildSlice';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
@ -69,7 +69,7 @@ const LeftSidebar = () => {
sectionsComponents.push(
<section key={id} id={id}>
{component}
</section>
</section>,
);
if (addMore) {
@ -89,7 +89,7 @@ const LeftSidebar = () => {
elements.push(
<section key={newId} id={`section-${newId}`}>
{newComponent}
</section>
</section>,
);
}
sectionsComponents.push(...elements);
@ -112,7 +112,9 @@ const LeftSidebar = () => {
<nav className="overflow-y-auto">
<div>
<Link href="/dashboard">
<Logo size={40} />
<IconButton>
<Icon size={24} />
</IconButton>
</Link>
<Divider />
</div>
@ -123,7 +125,7 @@ const LeftSidebar = () => {
arrow
key={id}
placement="right"
title={t<string>(`builder.leftSidebar.sections.${id}.heading`, get(sections, `${id}.name`))}
title={t(`builder.leftSidebar.sections.${id}.heading`, get(sections, `${id}.name`))}
>
<IconButton onClick={() => handleClick(id)}>{icon}</IconButton>
</Tooltip>
@ -132,7 +134,7 @@ const LeftSidebar = () => {
{customSections.map(({ id }) => (
<Tooltip
key={id}
title={t<string>(`builder.leftSidebar.sections.${id}.heading`, get(sections, `${id}.name`))}
title={t(`builder.leftSidebar.sections.${id}.heading`, get(sections, `${id}.name`))}
placement="right"
arrow
>
@ -157,8 +159,8 @@ const LeftSidebar = () => {
<div className="py-6 text-right">
<Button fullWidth variant="outlined" startIcon={<Add />} onClick={handleAddSection}>
{t<string>('builder.common.actions.add', {
token: t<string>('builder.leftSidebar.sections.section.heading'),
{t('builder.common.actions.add', {
token: t('builder.leftSidebar.sections.section.heading'),
})}
</Button>
</div>

View File

@ -24,7 +24,7 @@ const Basics = () => {
return (
<>
<Heading path="sections.basics" name={t<string>('builder.leftSidebar.sections.basics.heading')} />
<Heading path="sections.basics" name={t('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 w-full gap-2 sm:col-span-2">
<ResumeInput label={t<string>('builder.leftSidebar.sections.basics.name.label')} path="basics.name" />
<ResumeInput label={t('builder.leftSidebar.sections.basics.name.label')} path="basics.name" />
<Button variant="outlined" startIcon={<PhotoFilter />} onClick={handleClick}>
{t<string>('builder.leftSidebar.sections.basics.actions.photo-filters')}
{t('builder.leftSidebar.sections.basics.actions.photo-filters')}
</Button>
<Popover
@ -59,28 +59,24 @@ const Basics = () => {
<ResumeInput
type="date"
label={t<string>('builder.leftSidebar.sections.basics.birthdate.label')}
label={t('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" />
<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" />
<Divider className="sm:col-span-2" />
<ResumeInput
label={t<string>('builder.leftSidebar.sections.basics.headline.label')}
label={t('builder.leftSidebar.sections.basics.headline.label')}
path="basics.headline"
className="sm:col-span-2"
/>
<ResumeInput
type="textarea"
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
path="basics.summary"
className="sm:col-span-2"
markdownSupported

View File

@ -8,28 +8,19 @@ const Location = () => {
return (
<>
<Heading path="sections.location" name={t<string>('builder.leftSidebar.sections.location.heading')} />
<Heading path="sections.location" name={t('builder.leftSidebar.sections.location.heading')} />
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<ResumeInput
label={t<string>('builder.leftSidebar.sections.location.address.label')}
label={t('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<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')}
label={t('builder.leftSidebar.sections.location.postal-code.label')}
path="basics.location.postalCode"
/>
</div>

View File

@ -1,8 +1,8 @@
import { Circle, Square, SquareRounded } from '@mui/icons-material';
import { Checkbox, Divider, FormControlLabel, Slider, ToggleButton, ToggleButtonGroup } from '@mui/material';
import { Photo, PhotoShape } from '@reactive-resume/schema';
import get from 'lodash/get';
import { useTranslation } from 'next-i18next';
import { Photo, PhotoShape } from 'schema';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { setResumeState } from '@/store/resume/resumeSlice';
@ -30,9 +30,9 @@ const PhotoFilters = () => {
const handleSetBorder = (value: boolean) => dispatch(setResumeState({ path: 'basics.photo.filters.border', value }));
return (
<div className="flex flex-col gap-2 p-5 dark:bg-neutral-800">
<div className="flex flex-col gap-2 p-5 dark:bg-zinc-900">
<div>
<h4 className="font-medium">{t<string>('builder.leftSidebar.sections.basics.photo-filters.size.heading')}</h4>
<h4 className="font-medium">{t('builder.leftSidebar.sections.basics.photo-filters.size.heading')}</h4>
<div className="mx-2">
<Slider
@ -54,20 +54,18 @@ const PhotoFilters = () => {
<Divider />
<div>
<h4 className="font-medium">
{t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.heading')}
</h4>
<h4 className="font-medium">{t('builder.leftSidebar.sections.basics.photo-filters.effects.heading')}</h4>
<div className="flex items-center">
<FormControlLabel
label={t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.grayscale.label')}
label={t('builder.leftSidebar.sections.basics.photo-filters.effects.grayscale.label')}
control={
<Checkbox color="secondary" checked={grayscale} onChange={(_, value) => handleSetGrayscale(value)} />
}
/>
<FormControlLabel
label={t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.border.label')}
label={t('builder.leftSidebar.sections.basics.photo-filters.effects.border.label')}
control={<Checkbox color="secondary" checked={border} onChange={(_, value) => handleSetBorder(value)} />}
/>
</div>
@ -76,7 +74,7 @@ const PhotoFilters = () => {
<Divider />
<div className="flex flex-col gap-2">
<h4 className="font-medium">{t<string>('builder.leftSidebar.sections.basics.photo-filters.shape.heading')}</h4>
<h4 className="font-medium">{t('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">

View File

@ -1,11 +1,11 @@
import { Avatar, IconButton, Skeleton, Tooltip } from '@mui/material';
import { Photo, Resume } from '@reactive-resume/schema';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { useTranslation } from 'next-i18next';
import React, { useRef } from 'react';
import toast from 'react-hot-toast';
import { useMutation } from 'react-query';
import { Photo, Resume } from 'schema';
import { ServerError } from '@/services/axios';
import { deletePhoto, DeletePhotoParams, uploadPhoto, UploadPhotoParams } from '@/services/resume';
@ -49,7 +49,7 @@ const PhotoUpload: React.FC = () => {
const file = event.target.files[0];
if (file.size > FILE_UPLOAD_MAX_SIZE) {
toast.error(t<string>('common.toast.error.upload-photo-size'));
toast.error(t('common.toast.error.upload-photo-size'));
return;
}
@ -67,8 +67,8 @@ const PhotoUpload: React.FC = () => {
<Tooltip
title={
isEmpty(photo.url)
? (t<string>('builder.leftSidebar.sections.basics.photo-upload.tooltip.upload') as string)
: (t<string>('builder.leftSidebar.sections.basics.photo-upload.tooltip.remove') as string)
? (t('builder.leftSidebar.sections.basics.photo-upload.tooltip.upload') as string)
: (t('builder.leftSidebar.sections.basics.photo-upload.tooltip.remove') as string)
}
>
<Avatar sx={{ width: 96, height: 96 }} src={photo.url} />

View File

@ -1,7 +1,7 @@
import { Add } from '@mui/icons-material';
import { Button } from '@mui/material';
import { ListItem } from '@reactive-resume/schema';
import { useTranslation } from 'next-i18next';
import { ListItem } from 'schema';
import Heading from '@/components/shared/Heading';
import List from '@/components/shared/List';
@ -28,7 +28,7 @@ const Profiles = () => {
return (
<>
<Heading path="sections.profiles" name={t<string>('builder.leftSidebar.sections.profiles.heading')} />
<Heading path="sections.profiles" name={t('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<string>('builder.common.actions.add', {
token: t<string>('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
{t('builder.common.actions.add', {
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
})}
</Button>
</footer>

View File

@ -1,9 +1,9 @@
import { Add } from '@mui/icons-material';
import { Button } from '@mui/material';
import { ListItem, Section as SectionRecord, SectionType } from '@reactive-resume/schema';
import clsx from 'clsx';
import get from 'lodash/get';
import { useTranslation } from 'next-i18next';
import { ListItem, Section as SectionRecord, SectionType } from 'schema';
import { validate } from 'uuid';
import Heading from '@/components/shared/Heading';
@ -98,8 +98,8 @@ const Section: React.FC<Props> = ({
<SectionSettings path={path} />
<Button variant="outlined" startIcon={<Add />} onClick={handleAdd}>
{t<string>('builder.common.actions.add', {
token: t<string>(`builder.leftSidebar.${path}.heading`, heading),
{t('builder.common.actions.add', {
token: t(`builder.leftSidebar.${path}.heading`, { defaultValue: heading }),
})}
</Button>
</footer>
@ -107,7 +107,7 @@ const Section: React.FC<Props> = ({
{addMore ? (
<div className="py-6 text-right">
<Button fullWidth variant="outlined" startIcon={<Add />} onClick={handleDuplicateSection}>
{t<string>('builder.common.actions.duplicate')}
{t('builder.common.actions.duplicate')}
</Button>
</div>
) : (

View File

@ -32,7 +32,7 @@ const SectionSettings: React.FC<Props> = ({ path }) => {
return (
<div>
<Tooltip title={t<string>('builder.common.columns.tooltip')}>
<Tooltip title={t('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>
@ -47,8 +47,8 @@ const SectionSettings: React.FC<Props> = ({ path }) => {
horizontal: 'left',
}}
>
<div className="p-5 dark:bg-neutral-800">
<h4 className="mb-2 font-medium">{t<string>('builder.common.columns.heading')}</h4>
<div className="p-5 dark:bg-zinc-900">
<h4 className="mb-2 font-medium">{t('builder.common.columns.heading')}</h4>
<ToggleButtonGroup exclusive value={columns} onChange={(_, value: number) => handleSetColumns(value)}>
{[1, 2, 3, 4].map((index) => (

View File

@ -1,12 +1,12 @@
.container {
@apply h-screen w-[95vw] md:w-[70vw] lg:w-[50vw] xl:w-[30vw] 2xl:w-[28vw];
@apply bg-neutral-50 text-neutral-900 dark:bg-neutral-900 dark:text-neutral-50;
@apply relative flex border-l-2 border-neutral-50/10;
@apply bg-zinc-50 text-zinc-900 dark:bg-zinc-900 dark:text-zinc-50;
@apply relative flex border-l-2 border-zinc-50/10;
nav {
@apply absolute inset-y-0 right-0;
@apply w-12 py-4 md:w-16 md:px-2;
@apply bg-neutral-100 shadow dark:bg-neutral-800;
@apply bg-zinc-100 shadow dark:bg-zinc-900;
@apply flex flex-col items-center justify-between;
hr {
@ -29,7 +29,7 @@
> section {
@apply grid gap-4;
@apply pt-5 pb-7 first:pt-0;
@apply border-b border-neutral-900/10 last:border-b-0 dark:border-neutral-50/10;
@apply border-b border-zinc-900/10 last:border-b-0 dark:border-zinc-50/10;
hr {
@apply my-2;

View File

@ -45,7 +45,7 @@ const RightSidebar = () => {
<div className={styles.container}>
<nav className="overflow-y-auto">
<div>
<Avatar size={40} />
<Avatar size={24} />
<Divider />
</div>
@ -55,7 +55,7 @@ const RightSidebar = () => {
key={id}
arrow
placement="right"
title={t<string>(`builder.rightSidebar.sections.${id}.heading`, { defaultValue: capitalize(id) })}
title={t(`builder.rightSidebar.sections.${id}.heading`, { defaultValue: capitalize(id) })}
>
<IconButton onClick={() => handleClick(id)}>{icon}</IconButton>
</Tooltip>

View File

@ -1,10 +1,10 @@
import Editor from '@monaco-editor/react';
import { useTheme } from '@mui/material';
import { CustomCSS as CustomCSSType } from '@reactive-resume/schema';
import clsx from 'clsx';
import get from 'lodash/get';
import { useTranslation } from 'next-i18next';
import React from 'react';
import { CustomCSS as CustomCSSType } from 'schema';
import Heading from '@/components/shared/Heading';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
@ -18,7 +18,7 @@ const CustomCSS = () => {
const dispatch = useAppDispatch();
const customCSS: CustomCSSType = useAppSelector((state) =>
get(state.resume.present, 'metadata.css', {} as CustomCSSType)
get(state.resume.present, 'metadata.css', {} as CustomCSSType),
);
const handleChange = (value: string | undefined) => {
@ -27,7 +27,7 @@ const CustomCSS = () => {
return (
<>
<Heading path="metadata.css" name={t<string>('builder.rightSidebar.sections.css.heading')} isHideable />
<Heading path="metadata.css" name={t('builder.rightSidebar.sections.css.heading')} isHideable />
<Editor
height="200px"

View File

@ -20,17 +20,17 @@ const Export = () => {
const pdfListItemText = {
normal: {
primary: t<string>('builder.rightSidebar.sections.export.pdf.normal.primary'),
secondary: t<string>('builder.rightSidebar.sections.export.pdf.normal.secondary'),
primary: t('builder.rightSidebar.sections.export.pdf.normal.primary'),
secondary: t('builder.rightSidebar.sections.export.pdf.normal.secondary'),
},
loading: {
primary: t<string>('builder.rightSidebar.sections.export.pdf.loading.primary'),
secondary: t<string>('builder.rightSidebar.sections.export.pdf.loading.secondary'),
primary: t('builder.rightSidebar.sections.export.pdf.loading.primary'),
secondary: t('builder.rightSidebar.sections.export.pdf.loading.secondary'),
},
};
const handleExportJSON = async () => {
const { nanoid } = await import('nanoid');
const nanoid = (await import('nanoid')).nanoid;
const download = (await import('downloadjs')).default;
const redactedResume = pick(resume, ['basics', 'sections', 'metadata', 'public']);
@ -55,7 +55,7 @@ const Export = () => {
return (
<>
<Heading path="metadata.export" name={t<string>('builder.rightSidebar.sections.export.heading')} />
<Heading path="metadata.export" name={t('builder.rightSidebar.sections.export.heading')} />
<List sx={{ padding: 0 }}>
<ListItem sx={{ padding: 0 }}>
@ -63,8 +63,8 @@ const Export = () => {
<Schema />
<ListItemText
primary={t<string>('builder.rightSidebar.sections.export.json.primary')}
secondary={t<string>('builder.rightSidebar.sections.export.json.secondary')}
primary={t('builder.rightSidebar.sections.export.json.primary')}
secondary={t('builder.rightSidebar.sections.export.json.secondary')}
/>
</ListItemButton>
</ListItem>

View File

@ -1,6 +1,6 @@
.page {
@apply relative border pl-4 pb-4 dark:border-neutral-100/10;
@apply rounded bg-neutral-100 dark:bg-neutral-800;
@apply relative border pl-4 pb-4 dark:border-zinc-100/10;
@apply rounded bg-zinc-100 dark:bg-zinc-900;
.delete {
@apply opacity-50 hover:opacity-75;
@ -28,14 +28,14 @@
.base {
@apply absolute inset-0 w-4/5;
@apply rounded bg-neutral-200 dark:bg-neutral-700;
@apply rounded bg-zinc-200 dark:bg-zinc-800;
}
}
.section {
@apply relative my-3 w-full px-4 py-2;
@apply cursor-move break-all rounded text-xs capitalize;
@apply bg-neutral-800/90 text-neutral-50 dark:bg-neutral-50/90 dark:text-neutral-800;
@apply bg-zinc-900/90 text-zinc-50 dark:bg-zinc-50/90 dark:text-zinc-900;
&.disabled {
@apply opacity-60;

View File

@ -60,9 +60,9 @@ const Layout = () => {
<>
<Heading
path="metadata.layout"
name={t<string>('builder.rightSidebar.sections.layout.heading')}
name={t('builder.rightSidebar.sections.layout.heading')}
action={
<Tooltip title={t<string>('builder.rightSidebar.sections.layout.tooltip.reset-layout')}>
<Tooltip title={t('builder.rightSidebar.sections.layout.tooltip.reset-layout')}>
<IconButton onClick={handleResetLayout}>
<Restore />
</IconButton>
@ -76,14 +76,14 @@ const Layout = () => {
<div key={pageIndex} className={styles.page}>
<div className="flex items-center justify-between pr-3">
<p className={styles.heading}>
{t<string>('builder.common.glossary.page')} {pageIndex + 1}
{t('builder.common.glossary.page')} {pageIndex + 1}
</p>
<div className={clsx(styles.delete, { hidden: pageIndex === 0 })}>
<Tooltip
title={
t<string>('builder.common.actions.delete', {
token: t<string>('builder.common.glossary.page'),
t('builder.common.actions.delete', {
token: t('builder.common.glossary.page'),
}) as string
}
>
@ -136,7 +136,7 @@ const Layout = () => {
<div className="flex items-center justify-end">
<Button variant="outlined" startIcon={<Add />} onClick={handleAddPage}>
{t<string>('builder.common.actions.add', { token: t<string>('builder.common.glossary.page') })}
{t('builder.common.actions.add', { token: t('builder.common.glossary.page') })}
</Button>
</div>
</DragDropContext>

View File

@ -3,7 +3,7 @@
.section {
@apply grid gap-2 rounded p-6;
@apply bg-neutral-100 dark:bg-neutral-800;
@apply bg-zinc-100 dark:bg-zinc-900;
h2 {
@apply inline-flex items-center gap-2 text-base font-medium;

View File

@ -12,53 +12,51 @@ const Links = () => {
return (
<>
<Heading path="metadata.links" name={t<string>('builder.rightSidebar.sections.links.heading')} />
<Heading path="metadata.links" name={t('builder.rightSidebar.sections.links.heading')} />
<div className={styles.container}>
<div className={styles.section}>
<h2>
<Savings fontSize="small" />
{t<string>('builder.rightSidebar.sections.links.donate.heading')}
{t('builder.rightSidebar.sections.links.donate.heading')}
</h2>
<p>{t<string>('builder.rightSidebar.sections.links.donate.body')}</p>
<p>{t('builder.rightSidebar.sections.links.donate.body')}</p>
<a href={DONATION_URL} target="_blank" rel="noreferrer">
<Button startIcon={<Coffee />}>{t<string>('builder.rightSidebar.sections.links.donate.button')}</Button>
<Button startIcon={<Coffee />}>{t('builder.rightSidebar.sections.links.donate.button')}</Button>
</a>
</div>
<div className={styles.section}>
<h2>
<BugReport fontSize="small" />
{t<string>('builder.rightSidebar.sections.links.bugs-features.heading')}
{t('builder.rightSidebar.sections.links.bugs-features.heading')}
</h2>
<p>{t<string>('builder.rightSidebar.sections.links.bugs-features.body')}</p>
<p>{t('builder.rightSidebar.sections.links.bugs-features.body')}</p>
<a href={GITHUB_ISSUES_URL} target="_blank" rel="noreferrer">
<Button startIcon={<GitHub />}>
{t<string>('builder.rightSidebar.sections.links.bugs-features.button')}
</Button>
<Button startIcon={<GitHub />}>{t('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<string>('builder.rightSidebar.sections.links.github')}
{t('builder.rightSidebar.sections.links.github')}
</Button>
</a>
<a href={REDDIT_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<Link />}>
{t<string>('builder.rightSidebar.sections.links.reddit')}
{t('builder.rightSidebar.sections.links.reddit')}
</Button>
</a>
<a href={DOCS_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<Link />}>
{t<string>('builder.rightSidebar.sections.links.docs')}
{t('builder.rightSidebar.sections.links.docs')}
</Button>
</a>
</div>

View File

@ -10,13 +10,13 @@ import {
Switch,
TextField,
} from '@mui/material';
import { DateConfig, PageConfig, Resume } from '@reactive-resume/schema';
import dayjs from 'dayjs';
import get from 'lodash/get';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { useMemo, useState } from 'react';
import { useMutation } from 'react-query';
import { DateConfig, PageConfig, Resume } from 'schema';
import Heading from '@/components/shared/Heading';
import ThemeSwitch from '@/components/shared/ThemeSwitch';
@ -55,7 +55,7 @@ const Settings = () => {
const themeString = useMemo(() => (isDarkMode ? 'Matte Black Everything' : 'As bright as your future'), [isDarkMode]);
const { mutateAsync: loadSampleDataMutation } = useMutation<Resume, ServerError, LoadSampleDataParams>(
loadSampleData
loadSampleData,
);
const { mutateAsync: resetResumeMutation } = useMutation<Resume, ServerError, ResetResumeParams>(resetResume);
@ -96,13 +96,13 @@ const Settings = () => {
return (
<>
<Heading path="metadata.settings" name={t<string>('builder.rightSidebar.sections.settings.heading')} />
<Heading path="metadata.settings" name={t('builder.rightSidebar.sections.settings.heading')} />
<List disablePadding>
{/* Global Settings */}
<>
<ListSubheader disableSticky className="rounded">
{t<string>('builder.rightSidebar.sections.settings.global.heading')}
{t('builder.rightSidebar.sections.settings.global.heading')}
</ListSubheader>
<ListItem>
@ -110,7 +110,7 @@ const Settings = () => {
<Palette />
</ListItemIcon>
<ListItemText
primary={t<string>('builder.rightSidebar.sections.settings.global.theme.primary')}
primary={t('builder.rightSidebar.sections.settings.global.theme.primary')}
secondary={themeString}
/>
<ThemeSwitch checked={isDarkMode} onChange={(_, value: boolean) => handleSetTheme(value)} />
@ -119,8 +119,8 @@ const Settings = () => {
<ListItem className="flex-col">
<ListItemText
className="w-full"
primary={t<string>('builder.rightSidebar.sections.settings.global.date.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.global.date.secondary')}
primary={t('builder.rightSidebar.sections.settings.global.date.primary')}
secondary={t('builder.rightSidebar.sections.settings.global.date.secondary')}
/>
<Autocomplete<string, false, true, false>
disableClearable
@ -135,8 +135,8 @@ const Settings = () => {
<ListItem className="flex-col">
<ListItemText
className="w-full"
primary={t<string>('builder.rightSidebar.sections.settings.global.language.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.global.language.secondary')}
primary={t('builder.rightSidebar.sections.settings.global.language.primary')}
secondary={t('builder.rightSidebar.sections.settings.global.language.secondary')}
/>
<Autocomplete<Language, false, true, false>
disableClearable
@ -160,14 +160,14 @@ const Settings = () => {
{/* Page Settings */}
<>
<ListSubheader disableSticky className="rounded">
{t<string>('builder.rightSidebar.sections.settings.page.heading')}
{t('builder.rightSidebar.sections.settings.page.heading')}
</ListSubheader>
<ListItem className="flex-col">
<ListItemText
className="w-full"
primary={t<string>('builder.rightSidebar.sections.settings.page.format.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.page.format.secondary')}
primary={t('builder.rightSidebar.sections.settings.page.format.primary')}
secondary={t('builder.rightSidebar.sections.settings.page.format.secondary')}
/>
<Autocomplete<PageConfig['format'], false, true, false>
disableClearable
@ -182,11 +182,11 @@ const Settings = () => {
<ListItem>
<ListItemText
primary={t<string>('builder.rightSidebar.sections.settings.page.orientation.primary')}
primary={t('builder.rightSidebar.sections.settings.page.orientation.primary')}
secondary={
pages.length === 1
? t<string>('builder.rightSidebar.sections.settings.page.orientation.disabled')
: t<string>('builder.rightSidebar.sections.settings.page.orientation.secondary')
? t('builder.rightSidebar.sections.settings.page.orientation.disabled')
: t('builder.rightSidebar.sections.settings.page.orientation.secondary')
}
/>
<Switch
@ -199,8 +199,8 @@ const Settings = () => {
<ListItem>
<ListItemText
primary={t<string>('builder.rightSidebar.sections.settings.page.break-line.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.page.break-line.secondary')}
primary={t('builder.rightSidebar.sections.settings.page.break-line.primary')}
secondary={t('builder.rightSidebar.sections.settings.page.break-line.secondary')}
/>
<Switch color="secondary" checked={breakLine} onChange={() => dispatch(togglePageBreakLine())} />
</ListItem>
@ -209,7 +209,7 @@ const Settings = () => {
{/* Resume Settings */}
<>
<ListSubheader disableSticky className="rounded">
{t<string>('builder.rightSidebar.sections.settings.resume.heading')}
{t('builder.rightSidebar.sections.settings.resume.heading')}
</ListSubheader>
<ListItem disableGutters>
@ -218,8 +218,8 @@ const Settings = () => {
<Anchor />
</ListItemIcon>
<ListItemText
primary={t<string>('builder.rightSidebar.sections.settings.resume.sample.primary')}
secondary={t<string>('builder.rightSidebar.sections.settings.resume.sample.secondary')}
primary={t('builder.rightSidebar.sections.settings.resume.sample.primary')}
secondary={t('builder.rightSidebar.sections.settings.resume.sample.secondary')}
/>
</ListItemButton>
</ListItem>
@ -231,11 +231,9 @@ const Settings = () => {
</ListItemIcon>
<ListItemText
primary={
confirmReset
? 'Are you sure?'
: t<string>('builder.rightSidebar.sections.settings.resume.reset.primary')
confirmReset ? 'Are you sure?' : t('builder.rightSidebar.sections.settings.resume.reset.primary')
}
secondary={t<string>('builder.rightSidebar.sections.settings.resume.reset.secondary')}
secondary={t('builder.rightSidebar.sections.settings.resume.reset.secondary')}
/>
</ListItemButton>
</ListItem>

View File

@ -29,19 +29,19 @@ const Sharing = () => {
await navigator.clipboard.writeText(text);
toast.success(t<string>('common.toast.success.resume-link-copied'));
toast.success(t('common.toast.success.resume-link-copied'));
};
return (
<>
<Heading path="metadata.sharing" name={t<string>('builder.rightSidebar.sections.sharing.heading')} />
<Heading path="metadata.sharing" name={t('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<string>('builder.rightSidebar.sections.sharing.visibility.title')}
secondary={t<string>('builder.rightSidebar.sections.sharing.visibility.subtitle')}
primary={t('builder.rightSidebar.sections.sharing.visibility.title')}
secondary={t('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<string>('builder.rightSidebar.sections.sharing.short-url.label')}
label={t('builder.rightSidebar.sections.sharing.short-url.label')}
control={
<Checkbox className="mr-1" checked={showShortUrl} onChange={(_, value) => setShowShortUrl(value)} />
}

View File

@ -24,7 +24,7 @@ const Templates = () => {
return (
<>
<Heading path="metadata.templates" name={t<string>('builder.rightSidebar.sections.templates.heading')} />
<Heading path="metadata.templates" name={t('builder.rightSidebar.sections.templates.heading')} />
<div className={styles.container}>
{Object.values(templateMap).map((template) => (

View File

@ -1,6 +1,6 @@
import { ThemeConfig } from '@reactive-resume/schema';
import get from 'lodash/get';
import { useTranslation } from 'next-i18next';
import { ThemeConfig } from 'schema';
import ColorAvatar from '@/components/shared/ColorAvatar';
import ColorPicker from '@/components/shared/ColorPicker';
@ -17,7 +17,7 @@ const Theme = () => {
const dispatch = useAppDispatch();
const { background, text, primary } = useAppSelector<ThemeConfig>((state) =>
get(state.resume.present, 'metadata.theme')
get(state.resume.present, 'metadata.theme'),
);
const handleChange = (property: string, color: string) => {
@ -26,7 +26,7 @@ const Theme = () => {
return (
<>
<Heading path="metadata.theme" name={t<string>('builder.rightSidebar.sections.theme.heading')} />
<Heading path="metadata.theme" name={t('builder.rightSidebar.sections.theme.heading')} />
<div className={styles.container}>
<div className={styles.colorOptions}>
@ -36,18 +36,18 @@ const Theme = () => {
</div>
<ColorPicker
label={t<string>('builder.rightSidebar.sections.theme.form.primary.label')}
label={t('builder.rightSidebar.sections.theme.form.primary.label')}
color={primary}
className="col-span-2"
onChange={(color) => handleChange('primary', color)}
/>
<ColorPicker
label={t<string>('builder.rightSidebar.sections.theme.form.background.label')}
label={t('builder.rightSidebar.sections.theme.form.background.label')}
color={background}
onChange={(color) => handleChange('background', color)}
/>
<ColorPicker
label={t<string>('builder.rightSidebar.sections.theme.form.text.label')}
label={t('builder.rightSidebar.sections.theme.form.text.label')}
color={text}
onChange={(color) => handleChange('text', color)}
/>

View File

@ -1,9 +1,9 @@
import { Autocomplete, Skeleton, Slider, TextField } from '@mui/material';
import { Font, TypeCategory, TypeProperty, Typography as TypographyType } from '@reactive-resume/schema';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { useTranslation } from 'next-i18next';
import { useQuery } from 'react-query';
import { Font, TypeCategory, TypeProperty, Typography as TypographyType } from 'schema';
import Heading from '@/components/shared/Heading';
import { FONTS_QUERY } from '@/constants/index';
@ -46,7 +46,7 @@ const Widgets: React.FC<WidgetProps> = ({ label, category }) => {
setResumeState({
path: `metadata.typography.${property}.${category}`,
value: property === 'family' ? (value as Font).family : value,
})
}),
);
};
@ -64,7 +64,7 @@ const Widgets: React.FC<WidgetProps> = ({ label, category }) => {
step={1}
marks={[
{ value: 12, label: '12px' },
{ value: 24, label: t<string>('builder.rightSidebar.sections.typography.form.font-size.label') },
{ value: 24, label: t('builder.rightSidebar.sections.typography.form.font-size.label') },
{ value: 36, label: '36px' },
]}
valueLabelDisplay="auto"
@ -82,10 +82,7 @@ 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<string>('builder.rightSidebar.sections.typography.form.font-family.label')}
/>
<TextField {...params} label={t('builder.rightSidebar.sections.typography.form.font-family.label')} />
)}
/>
</div>
@ -98,13 +95,10 @@ const Typography = () => {
return (
<>
<Heading path="metadata.typography" name={t<string>('builder.rightSidebar.sections.typography.heading')} />
<Heading path="metadata.typography" name={t('builder.rightSidebar.sections.typography.heading')} />
<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" />
<Widgets label={t('builder.rightSidebar.sections.typography.widgets.headings.label')} category="heading" />
<Widgets label={t('builder.rightSidebar.sections.typography.widgets.body.label')} category="body" />
</>
);
};

View File

@ -5,7 +5,7 @@
aspect-ratio: 1 / 1.41;
@apply flex items-center justify-center shadow;
@apply cursor-pointer rounded-sm bg-neutral-100 transition-opacity hover:opacity-80 dark:bg-neutral-800;
@apply cursor-pointer rounded-sm bg-zinc-100 transition-opacity hover:opacity-80 dark:bg-zinc-900;
}
footer {

View File

@ -5,7 +5,7 @@
aspect-ratio: 1 / 1.41;
@apply relative cursor-pointer rounded-sm shadow;
@apply bg-neutral-100 transition-opacity hover:opacity-80 dark:bg-neutral-800;
@apply bg-zinc-100 transition-opacity hover:opacity-80 dark:bg-zinc-900;
}
footer {

View File

@ -7,7 +7,6 @@ import {
OpenInNew,
} from '@mui/icons-material';
import { ButtonBase, ListItemIcon, ListItemText, Menu, MenuItem, Tooltip } from '@mui/material';
import { Resume } from '@reactive-resume/schema';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
@ -15,6 +14,7 @@ import { useTranslation } from 'next-i18next';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { useMutation } from 'react-query';
import { Resume } from 'schema';
import { RESUMES_QUERY } from '@/constants/index';
import { ServerError } from '@/services/axios';
@ -76,7 +76,7 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
},
},
},
})
}),
);
};
@ -94,7 +94,7 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
const url = getResumeUrl(resume, { withHost: true });
await navigator.clipboard.writeText(url);
toast.success(t<string>('common.toast.success.resume-link-copied'));
toast.success(t('common.toast.success.resume-link-copied'));
};
const handleDelete = async () => {
@ -122,7 +122,7 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
<footer>
<div className={styles.meta}>
<p>{resume.name}</p>
<p>{t<string>('dashboard.resume.timestamp', { timestamp: getRelativeTime(resume.updatedAt) })}</p>
<p>{t('dashboard.resume.timestamp', { timestamp: getRelativeTime(resume.updatedAt) })}</p>
</div>
<ButtonBase className={styles.menu} onClick={handleOpenMenu}>
@ -134,21 +134,21 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
<ListItemIcon>
<OpenInNew className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('dashboard.resume.menu.open')}</ListItemText>
<ListItemText>{t('dashboard.resume.menu.open')}</ListItemText>
</MenuItem>
<MenuItem onClick={handleRename}>
<ListItemIcon>
<DriveFileRenameOutline className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('dashboard.resume.menu.rename')}</ListItemText>
<ListItemText>{t('dashboard.resume.menu.rename')}</ListItemText>
</MenuItem>
<MenuItem onClick={handleDuplicate}>
<ListItemIcon>
<ContentCopy className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('dashboard.resume.menu.duplicate')}</ListItemText>
<ListItemText>{t('dashboard.resume.menu.duplicate')}</ListItemText>
</MenuItem>
{resume.public ? (
@ -156,27 +156,27 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
<ListItemIcon>
<LinkIcon className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('dashboard.resume.menu.share-link')}</ListItemText>
<ListItemText>{t('dashboard.resume.menu.share-link')}</ListItemText>
</MenuItem>
) : (
<Tooltip arrow placement="right" title={t<string>('dashboard.resume.menu.tooltips.share-link')}>
<Tooltip arrow placement="right" title={t('dashboard.resume.menu.tooltips.share-link')}>
<div>
<MenuItem>
<ListItemIcon>
<LinkIcon className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('dashboard.resume.menu.share-link')}</ListItemText>
<ListItemText>{t('dashboard.resume.menu.share-link')}</ListItemText>
</MenuItem>
</div>
</Tooltip>
)}
<Tooltip arrow placement="right" title={t<string>('dashboard.resume.menu.tooltips.delete')}>
<Tooltip arrow placement="right" title={t('dashboard.resume.menu.tooltips.delete')}>
<MenuItem onClick={handleDelete}>
<ListItemIcon>
<DeleteOutline className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('dashboard.resume.menu.delete')}</ListItemText>
<ListItemText>{t('dashboard.resume.menu.delete')}</ListItemText>
</MenuItem>
</Tooltip>
</Menu>

View File

@ -0,0 +1,44 @@
import { Button } from '@mui/material';
import Link from 'next/link';
import { useTranslation } from 'next-i18next';
import { FLAG_DISABLE_SIGNUPS } from '@/constants/flags';
import { logout } from '@/store/auth/authSlice';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { setModalState } from '@/store/modal/modalSlice';
const HomeActions = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const isLoggedIn = useAppSelector((state) => state.auth.isLoggedIn);
const handleLogin = () => dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
const handleRegister = () => dispatch(setModalState({ modal: 'auth.register', state: { open: true } }));
const handleLogout = () => dispatch(logout());
return isLoggedIn ? (
<>
<Link href="/dashboard" passHref>
<Button size="large">{t('landing.actions.app')}</Button>
</Link>
<Button size="large" variant="outlined" onClick={handleLogout}>
{t('landing.actions.logout')}
</Button>
</>
) : (
<>
<Button size="large" onClick={handleLogin}>
{t('landing.actions.login')}
</Button>
<Button size="large" variant="outlined" onClick={handleRegister} disabled={FLAG_DISABLE_SIGNUPS}>
{t('landing.actions.register')}
</Button>
</>
);
};
export default HomeActions;

View File

@ -0,0 +1,16 @@
const HeroBackground = () => (
<div
aria-hidden="true"
className="absolute left-[calc(50%-4rem)] top-10 -z-10 transform-gpu blur-3xl sm:left-[calc(50%-18rem)] lg:left-48 lg:top-[calc(50%-30rem)] xl:left-[calc(50%-24rem)]"
>
<div
className="aspect-[1108/632] h-96 w-[69.25rem] bg-gradient-to-r from-[#6f8cbb] to-[#c93b37] opacity-40 dark:opacity-20"
style={{
clipPath:
'polygon(73.6% 51.7%, 91.7% 11.8%, 100% 46.4%, 97.4% 82.2%, 92.5% 84.9%, 75.7% 64%, 55.3% 47.5%, 46.5% 49.4%, 45% 62.9%, 50.3% 87.2%, 21.3% 64.1%, 0.1% 100%, 5.4% 51.1%, 21.4% 63.9%, 58.9% 0.2%, 73.6% 51.7%)',
}}
/>
</div>
);
export default HeroBackground;

View File

@ -0,0 +1,47 @@
import GitHubButton from 'react-github-btn';
import { useAppSelector } from '@/store/hooks';
import { Copyright } from '../shared/Copyright';
import Logo from '../shared/Logo';
import { Separator } from '../ui/Separator';
const Footer = () => {
const theme = useAppSelector((state) => state.build.theme);
return (
<footer className="fixed inset-x-0 bottom-0 -z-50 h-[450px] bg-zinc-50 dark:bg-zinc-950">
<Separator />
<div className="container grid py-12 sm:grid-cols-3 lg:grid-cols-4">
<div className="flex flex-col gap-y-2">
<Logo size={96} className="-ml-2" />
<h2 className="text-xl font-medium">Reactive Resume</h2>
<p className="prose prose-sm prose-zinc leading-relaxed opacity-60 dark:prose-invert">
A free and open-source resume builder that simplifies the tasks of creating, updating, and sharing your
resume.
</p>
<div className="mt-6">
<GitHubButton
data-size="large"
data-show-count="true"
data-icon="octicon-star"
data-color-scheme={theme ? 'dark' : 'light'}
href="https://github.com/AmruthPillai/Reactive-Resume"
aria-label="Star AmruthPillai/Reactive-Resume on GitHub"
>
Star
</GitHubButton>
</div>
<Copyright className="mt-4" />
</div>
</div>
</footer>
);
};
export default Footer;

View File

@ -0,0 +1,24 @@
import Link from 'next/link';
import Logo from '../shared/Logo';
import HomeActions from './Actions';
const Header = () => (
<header className="fixed inset-x-0 top-0 z-50">
<nav className="bg-gradient-to-b from-zinc-50 to-transparent py-3 dark:from-zinc-950">
<div className="container flex items-center justify-between">
<div className="lg:flex-1">
<Link href="/">
<Logo size={48} />
</Link>
</div>
<div className="space-x-4">
<HomeActions />
</div>
</div>
</nav>
</header>
);
export default Header;

View File

@ -0,0 +1,28 @@
const HeroPattern = () => (
<svg
aria-hidden="true"
className="absolute inset-0 -z-10 h-full w-full stroke-zinc-950/10 opacity-60 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)] dark:stroke-zinc-50/10 dark:opacity-40"
>
<defs>
<pattern
id="983e3e4c-de6d-4c3f-8d64-b9761d1534cc"
width={200}
height={200}
x="50%"
y={-1}
patternUnits="userSpaceOnUse"
>
<path d="M.5 200V.5H200" fill="none" />
</pattern>
</defs>
<svg x="50%" y={-1} className="overflow-visible fill-zinc-100/20 dark:fill-zinc-900/20">
<path
d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
strokeWidth={0}
/>
</svg>
<rect width="100%" height="100%" strokeWidth={0} fill="url(#983e3e4c-de6d-4c3f-8d64-b9761d1534cc)" />
</svg>
);
export default HeroPattern;

View File

@ -1,6 +1,6 @@
.testimony {
@apply grid gap-2;
@apply rounded border-2 p-4 dark:border-neutral-800;
@apply rounded border-2 p-4 dark:border-zinc-900;
blockquote {
@apply text-justify text-xs leading-normal opacity-90;

View File

@ -0,0 +1,50 @@
import Tilt from 'react-parallax-tilt';
import { defaultTiltProps } from '@/constants/tilt';
import HomeActions from '../Actions';
import HeroBackground from '../Background';
import HeroPattern from '../Pattern';
const HeroSection = () => (
<section className="relative">
<HeroPattern />
<HeroBackground />
<div className="mx-auto max-w-7xl px-6 pb-24 pt-10 sm:pb-32 lg:flex lg:px-8 lg:py-52">
<div className="mx-auto max-w-2xl shrink-0 lg:mx-0 lg:max-w-xl lg:pt-12">
<div className="mt-10 space-y-2">
<h6 className="text-base font-bold tracking-wide">Finally,</h6>
<h1 className="text-4xl font-bold !leading-[1.15] tracking-tight sm:text-6xl">
A free and open-source resume builder
</h1>
</div>
<p className="prose prose-base prose-zinc mt-6 text-lg leading-8 dark:prose-invert">
Reactive Resume is a free and open-source resume builder that simplifies the tasks of creating, updating, and
sharing your resume.
</p>
<div className="mt-12 space-x-4">
<HomeActions />
</div>
</div>
<div className="mx-auto mt-16 flex max-w-2xl sm:mt-24 lg:ml-10 lg:mr-0 lg:mt-0 lg:max-w-none lg:flex-none xl:ml-32">
<div className="max-w-3xl flex-none sm:max-w-5xl lg:max-w-none">
<Tilt {...defaultTiltProps}>
<img
width={2432}
height={1442}
src="/images/screenshots/builder.png"
alt="Reactive Resume Screenshot - Builder Screen"
className="w-[76rem] rounded-lg bg-zinc-50/5 shadow-2xl ring-1 ring-zinc-950/10 dark:bg-zinc-950/5 dark:ring-zinc-50/10"
/>
</Tilt>
</div>
</div>
</div>
</section>
);
export default HeroSection;

View File

@ -0,0 +1,51 @@
import { cn } from '@/utils/styles';
type LogoProps = { brand: string };
const Logo = ({ brand }: LogoProps) => (
<div className={cn('col-span-2 col-start-2 sm:col-start-auto lg:col-span-1', brand === 'twilio' && 'sm:col-start-2')}>
{/* Show on Light Theme */}
<img
className="block max-h-12 object-contain dark:hidden"
src={`/images/brand-logos/dark/${brand}.svg`}
alt={brand}
width={212}
height={48}
/>
{/* Show on Dark Theme */}
<img
className="hidden max-h-12 object-contain dark:block"
src={`/images/brand-logos/light/${brand}.svg`}
alt={brand}
width={212}
height={48}
/>
</div>
);
const logoList: string[] = ['amazon', 'google', 'postman', 'twilio', 'zalando'];
const LogoSection = () => (
<section className="relative py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<p className="text-center text-lg leading-relaxed">
Reactive Resume has helped people land jobs at these great companies:
</p>
<div className="mx-auto mt-10 grid max-w-lg grid-cols-4 items-center gap-x-8 gap-y-10 sm:max-w-xl sm:grid-cols-6 sm:gap-x-10 lg:mx-0 lg:max-w-none lg:grid-cols-5">
{logoList.map((brand) => (
<Logo key={brand} brand={brand} />
))}
</div>
<p className="mx-auto mt-8 max-w-sm text-center leading-relaxed">
If this app has helped you with your job hunt, let me know by reaching out through{' '}
<a href="https://www.amruthpillai.com/#contact" target="_blank" rel="noreferrer" className="hover:underline">
this contact form
</a>
.
</p>
</div>
</section>
);
export default LogoSection;

View File

@ -0,0 +1,27 @@
type Statistic = {
name: string;
value: string;
};
const stats: Statistic[] = [
{ name: 'GitHub Stars', value: '11,800+' },
{ name: 'Users Signed Up', value: '300,000+' },
{ name: 'Resumes Generated', value: '400,000+' },
];
const StatsSection = () => (
<section className="relative py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<dl className="grid grid-cols-1 gap-x-8 gap-y-16 text-center lg:grid-cols-3">
{stats.map((stat, index) => (
<div key={index} className="mx-auto flex max-w-xs flex-col gap-y-4">
<dt className="text-base leading-7 opacity-60">{stat.name}</dt>
<dd className="order-first text-3xl font-semibold tracking-tight sm:text-5xl">{stat.value}</dd>
</div>
))}
</dl>
</div>
</section>
);
export default StatsSection;

View File

@ -63,12 +63,12 @@ const Avatar: React.FC<Props> = ({ size = 64, interactive = true }) => {
<Menu anchorEl={anchorEl} onClose={handleClose} open={Boolean(anchorEl)}>
<MenuItem onClick={handleOpenProfile}>
<div>
<span className="text-xs opacity-50">{t<string>('common.avatar.menu.greeting')},</span>
<span className="text-xs opacity-50">{t('common.avatar.menu.greeting')},</span>
<p>{user?.name}</p>
</div>
</MenuItem>
<Divider />
<MenuItem onClick={handleLogout}>{t<string>('common.avatar.menu.logout')}</MenuItem>
<MenuItem onClick={handleLogout}>{t('common.avatar.menu.logout')}</MenuItem>
</Menu>
</>
);

View File

@ -1,7 +1,7 @@
.content {
@apply rounded px-6 text-sm shadow lg:w-1/2 xl:w-2/5;
@apply rounded px-6 text-sm shadow lg:w-2/3 xl:w-1/2;
@apply absolute inset-4 sm:inset-x-4 sm:inset-y-auto lg:inset-auto;
@apply overflow-scroll bg-neutral-50 dark:bg-neutral-900 lg:overflow-auto;
@apply overflow-scroll bg-zinc-100 dark:bg-zinc-900 lg:overflow-auto;
@apply max-h-[90vh] min-h-fit;
&::-webkit-scrollbar {
@ -10,7 +10,7 @@
}
.header {
@apply sticky top-0 left-0 right-0 z-50 bg-neutral-50 pt-6 dark:bg-neutral-900;
@apply sticky top-0 left-0 right-0 z-50 bg-zinc-100 pt-6 dark:bg-zinc-900;
@apply flex items-center justify-between;
@apply w-full border-b pb-5 dark:border-white/10;
@ -33,7 +33,7 @@
}
.footer {
@apply sticky bottom-0 left-0 right-0 z-50 bg-neutral-50 pb-6 dark:bg-neutral-900;
@apply sticky bottom-0 left-0 right-0 z-50 bg-zinc-100 pb-6 dark:bg-zinc-900;
@apply flex items-center justify-end gap-x-4;
@apply w-full border-t pt-5 dark:border-white/10;
}

View File

@ -0,0 +1,19 @@
import clsx from 'clsx';
type Props = {
className?: string;
};
export const Copyright = ({ className }: Props) => (
<div
className={clsx('prose prose-sm prose-zinc flex flex-col gap-y-1 text-xs opacity-40 dark:prose-invert', className)}
>
<span className="font-medium">v{process.env.appVersion}</span>
<span>
Licensed under <a href="https://github.com/AmruthPillai/Reactive-Resume/blob/main/LICENSE">MIT</a>
</span>
<span>
A passion project by <a href="https://www.amruthpillai.com/">Amruth Pillai</a>
</span>
</div>
);

View File

@ -10,7 +10,7 @@ const Footer: React.FC<Props> = ({ className }) => {
return (
<div className={clsx('text-xs', className)}>
<p>{t<string>('common.footer.license')}</p>
<p>{t('common.footer.license')}</p>
<p>
<Trans t={t} i18nKey="common.footer.credit">

View File

@ -62,7 +62,7 @@ const Heading: React.FC<Props> = ({
{editMode ? (
<TextField size="small" value={heading} className="w-3/4" onChange={handleChange} />
) : (
<h1>{t<string>(`builder.leftSidebar.${path}.heading`, heading)}</h1>
<h1>{t(`builder.leftSidebar.${path}.heading`, { defaultValue: heading })}</h1>
)}
</div>
@ -72,19 +72,19 @@ const Heading: React.FC<Props> = ({
})}
>
{isEditable && (
<Tooltip title={t<string>('builder.common.tooltip.rename-section')}>
<Tooltip title={t('builder.common.tooltip.rename-section')}>
<IconButton onClick={toggleEditMode}>{editMode ? <Check /> : <DriveFileRenameOutline />}</IconButton>
</Tooltip>
)}
{isHideable && (
<Tooltip title={t<string>('builder.common.tooltip.toggle-visibility')}>
<Tooltip title={t('builder.common.tooltip.toggle-visibility')}>
<IconButton onClick={toggleVisibility}>{visibility ? <Visibility /> : <VisibilityOff />}</IconButton>
</Tooltip>
)}
{isDeletable && (
<Tooltip title={t<string>('builder.common.tooltip.delete-section')}>
<Tooltip title={t('builder.common.tooltip.delete-section')}>
<IconButton onClick={handleDelete}>
<Delete />
</IconButton>

View File

@ -0,0 +1,27 @@
import clsx from 'clsx';
import Image from 'next/image';
import { useAppSelector } from '@/store/hooks';
type Props = {
className?: string;
size?: 256 | 96 | 64 | 48 | 40 | 32 | 24 | 16;
};
const Icon: React.FC<Props> = ({ size = 64, className }) => {
const theme = useAppSelector((state) => state.build.theme);
const iconTheme = theme === 'light' ? 'dark' : 'light';
return (
<Image
alt="Reactive Resume"
src={`/icon/${iconTheme}.svg`}
className={clsx('rounded', className)}
width={size}
height={size}
priority
/>
);
};
export default Icon;

View File

@ -1,5 +1,5 @@
.container {
@apply rounded-lg border dark:border-neutral-50/10;
@apply rounded-lg border dark:border-zinc-50/10;
.empty {
@apply py-8 text-center;

View File

@ -1,4 +1,3 @@
import { ListItem as ListItemType } from '@reactive-resume/schema';
import clsx from 'clsx';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
@ -8,6 +7,7 @@ import { useTranslation } from 'next-i18next';
import { useCallback } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { ListItem as ListItemType } from 'schema';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { deleteItem, setResumeState } from '@/store/resume/resumeSlice';
@ -60,13 +60,13 @@ const List: React.FC<Props> = ({
dispatch(setResumeState({ path, value: newList }));
},
[list, dispatch, path]
[list, dispatch, path],
);
return (
<DndProvider backend={HTML5Backend}>
<div className={clsx(styles.container, className)}>
{isEmpty(list) && <div className={styles.empty}>{t<string>('builder.common.list.empty-text')}</div>}
{isEmpty(list) && <div className={styles.empty}>{t('builder.common.list.empty-text')}</div>}
{list.map((item, index) => {
const title = get(item, titleKey, '');

View File

@ -1,7 +1,7 @@
.item {
@apply flex items-center justify-between;
@apply py-5 pl-5 pr-2;
@apply border-b border-neutral-900/10 last:border-0 dark:border-neutral-50/10;
@apply border-b border-zinc-900/10 last:border-0 dark:border-zinc-50/10;
@apply cursor-move transition-opacity;
.meta {

View File

@ -1,11 +1,11 @@
import { DeleteOutline, DriveFileRenameOutline, FileCopy, MoreVert } from '@mui/icons-material';
import { Divider, IconButton, ListItemIcon, ListItemText, Menu, MenuItem, Tooltip } from '@mui/material';
import { ListItem as ListItemType } from '@reactive-resume/schema';
import clsx from 'clsx';
import isFunction from 'lodash/isFunction';
import { useTranslation } from 'next-i18next';
import React, { useRef, useState } from 'react';
import { DropTargetMonitor, useDrag, useDrop, XYCoord } from 'react-dnd';
import { ListItem as ListItemType } from 'schema';
import styles from './ListItem.module.scss';
@ -126,25 +126,25 @@ const ListItem: React.FC<Props> = ({ item, path, index, title, subtitle, onMove,
<ListItemIcon>
<DriveFileRenameOutline className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.common.list.actions.edit')}</ListItemText>
<ListItemText>{t('builder.common.list.actions.edit')}</ListItemText>
</MenuItem>
<MenuItem onClick={() => handleDuplicate(item)}>
<ListItemIcon>
<FileCopy className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.common.list.actions.duplicate')}</ListItemText>
<ListItemText>{t('builder.common.list.actions.duplicate')}</ListItemText>
</MenuItem>
<Divider />
<Tooltip arrow placement="right" title={t<string>('builder.common.tooltip.delete-item')}>
<Tooltip arrow placement="right" title={t('builder.common.tooltip.delete-item')}>
<div>
<MenuItem onClick={() => handleDelete(item)}>
<ListItemIcon>
<DeleteOutline className="scale-90" />
</ListItemIcon>
<ListItemText>{t<string>('builder.common.list.actions.delete')}</ListItemText>
<ListItemText>{t('builder.common.list.actions.delete')}</ListItemText>
</MenuItem>
</div>
</Tooltip>

View File

@ -1,11 +1,26 @@
import clsx from 'clsx';
import Image from 'next/image';
import { useAppSelector } from '@/store/hooks';
type Props = {
size?: 256 | 64 | 48 | 40 | 32;
className?: string;
size?: 256 | 96 | 64 | 48 | 40 | 32 | 24 | 16;
};
const Logo: React.FC<Props> = ({ size = 64 }) => (
<Image alt="Reactive Resume" src="/images/logos/logo.svg" className="rounded" width={size} height={size} priority />
);
const Logo: React.FC<Props> = ({ size = 64, className }) => {
const theme = useAppSelector((state) => state.build.theme);
return (
<Image
alt="Reactive Resume"
src={`/logo/${theme}.svg`}
className={clsx('rounded', className)}
width={size}
height={size}
priority
/>
);
};
export default Logo;

View File

@ -15,9 +15,9 @@ const Markdown: React.FC<Props> = ({ className, children }) => {
return (
<ReactMarkdown
rehypePlugins={[rehypeKatex]}
className={clsx('markdown', className)}
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeKatex]}
>
{children}
</ReactMarkdown>

View File

@ -2,6 +2,7 @@ import { TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import dayjs from 'dayjs';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { useEffect, useState } from 'react';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
@ -57,13 +58,12 @@ const ResumeInput: React.FC<Props> = ({ type = 'text', label, path, className, m
<DatePicker
openTo="year"
label={label}
value={dayjs(value)}
className={className}
views={['year', 'month', 'day']}
slots={{
textField: (params) => <TextField {...params} error={false} className={className} />,
}}
value={isEmpty(value) ? null : dayjs(value)}
onChange={(date: dayjs.Dayjs | null) => {
date && dayjs(date).isValid() && onChangeValue(dayjs(date).format('YYYY-MM-DD'));
if (!date) return onChangeValue('');
if (dayjs(date).isValid()) return onChangeValue(dayjs(date).format('YYYY-MM-DD'));
}}
/>
);

View File

@ -13,7 +13,7 @@ const ThemeSwitch = styled(Switch)(({ theme }) => ({
transform: 'translateX(22px)',
'& .MuiSwitch-thumb:before': {
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
'#fff'
'#fff',
)}" d="M4.2 2.5l-.7 1.8-1.8.7 1.8.7.7 1.8.6-1.8L6.7 5l-1.9-.7-.6-1.8zm15 8.3a6.7 6.7 0 11-6.6-6.6 5.8 5.8 0 006.6 6.6z"/></svg>')`,
},
'& + .MuiSwitch-track': {
@ -36,7 +36,7 @@ const ThemeSwitch = styled(Switch)(({ theme }) => ({
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
'#fff'
'#fff',
)}" d="M9.305 1.667V3.75h1.389V1.667h-1.39zm-4.707 1.95l-.982.982L5.09 6.072l.982-.982-1.473-1.473zm10.802 0L13.927 5.09l.982.982 1.473-1.473-.982-.982zM10 5.139a4.872 4.872 0 00-4.862 4.86A4.872 4.872 0 0010 14.862 4.872 4.872 0 0014.86 10 4.872 4.872 0 0010 5.139zm0 1.389A3.462 3.462 0 0113.471 10a3.462 3.462 0 01-3.473 3.472A3.462 3.462 0 016.527 10 3.462 3.462 0 0110 6.528zM1.665 9.305v1.39h2.083v-1.39H1.666zm14.583 0v1.39h2.084v-1.39h-2.084zM5.09 13.928L3.616 15.4l.982.982 1.473-1.473-.982-.982zm9.82 0l-.982.982 1.473 1.473.982-.982-1.473-1.473zM9.305 16.25v2.083h1.389V16.25h-1.39z"/></svg>')`,
},
},

View File

@ -0,0 +1,25 @@
import * as SeparatorPrimitive from '@radix-ui/react-separator';
import * as React from 'react';
import { cn } from '@/utils/styles';
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
'shrink-0 dark:bg-zinc-900 bg-zinc-100',
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
className,
)}
{...props}
/>
));
Separator.displayName = SeparatorPrimitive.Root.displayName;
export { Separator };

View File

@ -54,5 +54,5 @@ export const languageMap: Record<string, Language> = languages.reduce(
...acc,
[lang.code]: lang,
}),
{}
{},
);

View File

@ -23,8 +23,8 @@ import {
VolunteerActivism,
Work,
} from '@mui/icons-material';
import { Section as SectionRecord, SectionType } from '@reactive-resume/schema';
import isEmpty from 'lodash/isEmpty';
import { Section as SectionRecord, SectionType } from 'schema';
import Basics from '@/components/build/LeftSidebar/sections/Basics';
import Location from '@/components/build/LeftSidebar/sections/Location';

View File

@ -3,7 +3,7 @@ import { createTheme, ThemeOptions } from '@mui/material/styles';
const theme: ThemeOptions = {
typography: {
fontSize: 12,
fontFamily: 'Inter, sans-serif',
fontFamily: '"IBM Plex Sans", sans-serif',
},
components: {
MuiButton: {
@ -24,6 +24,13 @@ const theme: ThemeOptions = {
variant: 'outlined',
},
},
MuiInputBase: {
styleOverrides: {
input: {
boxShadow: 'none !important',
},
},
},
MuiAppBar: {
styleOverrides: {
root: {
@ -48,6 +55,15 @@ const theme: ThemeOptions = {
},
},
},
MuiModal: {
defaultProps: {
componentsProps: {
backdrop: {
className: 'backdrop-blur-sm',
},
},
},
},
},
};
@ -55,8 +71,9 @@ export const lightTheme = createTheme({
...theme,
palette: {
mode: 'light',
primary: { main: '#404040' }, // neutral[700]
secondary: { main: '#0d9488' }, // teal[600]
background: { default: '#fafafa', paper: '#f4f4f5' },
primary: { main: '#18181b' },
secondary: { main: '#14b8a6' },
},
});
@ -64,7 +81,8 @@ export const darkTheme = createTheme({
...theme,
palette: {
mode: 'dark',
primary: { main: '#f5f5f5' }, // neutral[100]
secondary: { main: '#2dd4bf' }, // teal[400]
background: { default: '#09090b', paper: '#18181b' },
primary: { main: '#f4f4f5' },
secondary: { main: '#0d9488' },
},
});

View File

@ -10,7 +10,7 @@ export const FILENAME_TIMESTAMP = 'DDMMYYYYHHmmss';
// Links
export const DOCS_URL = 'https://docs.rxresu.me';
export const DONATION_URL = 'https://paypal.me/RajaRajanA';
export const DONATION_URL = 'https://paypal.me/amruthde';
export const TRANSLATE_URL = 'https://translate.rxresu.me/';
export const DIGITALOCEAN_URL = 'https://pillai.xyz/digitalocean';
export const REDDIT_URL = 'https://www.reddit.com/r/reactiveresume/';

11
client/constants/tilt.ts Normal file
View File

@ -0,0 +1,11 @@
import { ReactParallaxTiltProps } from 'react-parallax-tilt';
export const defaultTiltProps: ReactParallaxTiltProps = {
scale: 1.05,
tiltMaxAngleX: 8,
tiltMaxAngleY: 8,
perspective: 1400,
glareEnable: true,
glareMaxOpacity: 0.1,
glareColor: '#fafafa',
};

View File

@ -54,16 +54,16 @@ const ForgotPasswordModal: React.FC = () => {
<BaseModal
icon={<Password />}
isOpen={isOpen}
heading={t<string>('modals.auth.forgot-password.heading')}
heading={t('modals.auth.forgot-password.heading')}
handleClose={handleClose}
footerChildren={
<Button type="submit" disabled={isLoading} onClick={handleSubmit(onSubmit)}>
{t<string>('modals.auth.forgot-password.actions.send-email')}
{t('modals.auth.forgot-password.actions.send-email')}
</Button>
}
>
<div className="grid gap-4">
<p>{t<string>('modals.auth.forgot-password.body')}</p>
<p>{t('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<string>('modals.auth.forgot-password.form.email.label')}
label={t('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<string>('modals.auth.forgot-password.help-text')}</p>
<p className="text-xs">{t('modals.auth.forgot-password.help-text')}</p>
</div>
</BaseModal>
</>

View File

@ -53,7 +53,7 @@ const LoginModal: React.FC = () => {
const { mutateAsync: loginMutation } = useMutation<void, ServerError, LoginParams>(login);
const { mutateAsync: loginWithGoogleMutation } = useMutation<void, ServerError, LoginWithGoogleParams>(
loginWithGoogle
loginWithGoogle,
);
const handleClose = () => {
@ -105,7 +105,7 @@ const LoginModal: React.FC = () => {
<BaseModal
icon={<Login />}
isOpen={isOpen}
heading={t<string>('modals.auth.login.heading')}
heading={t('modals.auth.login.heading')}
handleClose={handleClose}
footerChildren={
<div className="flex gap-4">
@ -114,23 +114,23 @@ const LoginModal: React.FC = () => {
)}
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
{t<string>('modals.auth.login.actions.login')}
{t('modals.auth.login.actions.login')}
</Button>
</div>
}
>
<p>{t<string>('modals.auth.login.body')}</p>
<p>{t('modals.auth.login.body')}</p>
<form className="grid gap-4 xl:w-2/3">
<form className="grid gap-4">
<Controller
name="identifier"
control={control}
render={({ field, fieldState }) => (
<TextField
autoFocus
label={t<string>('modals.auth.login.form.username.label')}
label={t('modals.auth.login.form.username.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message || t<string>('modals.auth.login.form.username.help-text')}
helperText={fieldState.error?.message || t('modals.auth.login.form.username.help-text')}
{...field}
/>
)}
@ -142,7 +142,7 @@ const LoginModal: React.FC = () => {
render={({ field, fieldState }) => (
<TextField
type={showPassword ? 'text' : 'password'}
label={t<string>('modals.auth.login.form.password.label')}
label={t('modals.auth.login.form.password.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
InputProps={{ endAdornment: <PasswordVisibility /> }}

View File

@ -61,7 +61,7 @@ const RegisterModal: React.FC = () => {
const { mutateAsync, isLoading } = useMutation<void, ServerError, RegisterParams>(registerUser);
const { mutateAsync: loginWithGoogleMutation } = useMutation<void, ServerError, LoginWithGoogleParams>(
loginWithGoogle
loginWithGoogle,
);
const handleClose = () => {
@ -95,7 +95,7 @@ const RegisterModal: React.FC = () => {
<BaseModal
icon={<HowToReg />}
isOpen={isOpen}
heading={t<string>('modals.auth.register.heading')}
heading={t('modals.auth.register.heading')}
handleClose={handleClose}
footerChildren={
<div className="flex gap-4">
@ -104,12 +104,12 @@ const RegisterModal: React.FC = () => {
)}
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
{t<string>('modals.auth.register.actions.register')}
{t('modals.auth.register.actions.register')}
</Button>
</div>
}
>
<p>{t<string>('modals.auth.register.body')}</p>
<p>{t('modals.auth.register.body')}</p>
<form className="grid gap-4 md:grid-cols-2">
<Controller
@ -118,7 +118,7 @@ const RegisterModal: React.FC = () => {
render={({ field, fieldState }) => (
<TextField
autoFocus
label={t<string>('modals.auth.register.form.name.label')}
label={t('modals.auth.register.form.name.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -131,7 +131,7 @@ const RegisterModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('modals.auth.register.form.username.label')}
label={t('modals.auth.register.form.username.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -145,7 +145,7 @@ const RegisterModal: React.FC = () => {
render={({ field, fieldState }) => (
<TextField
type="email"
label={t<string>('modals.auth.register.form.email.label')}
label={t('modals.auth.register.form.email.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message}
@ -160,7 +160,7 @@ const RegisterModal: React.FC = () => {
render={({ field, fieldState }) => (
<TextField
type="password"
label={t<string>('modals.auth.register.form.password.label')}
label={t('modals.auth.register.form.password.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -174,7 +174,7 @@ const RegisterModal: React.FC = () => {
render={({ field, fieldState }) => (
<TextField
type="password"
label={t<string>('modals.auth.register.form.confirm-password.label')}
label={t('modals.auth.register.form.confirm-password.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}

View File

@ -65,15 +65,15 @@ const ResetPasswordModal: React.FC = () => {
<BaseModal
icon={<LockReset />}
isOpen={isOpen}
heading={t<string>('modals.auth.reset-password.heading')}
heading={t('modals.auth.reset-password.heading')}
handleClose={handleClose}
footerChildren={
<Button type="submit" disabled={isLoading} onClick={handleSubmit(onSubmit)}>
{t<string>('modals.auth.reset-password.actions.set-password')}
{t('modals.auth.reset-password.actions.set-password')}
</Button>
}
>
<p>{t<string>('modals.auth.reset-password.body')}</p>
<p>{t('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<string>('modals.auth.reset-password.form.password.label')}
label={t('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<string>('modals.auth.reset-password.form.confirm-password.label')}
label={t('modals.auth.reset-password.form.confirm-password.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}

View File

@ -2,7 +2,7 @@ import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { Award, SectionPath } from '@reactive-resume/schema';
import { Award, SectionPath } from 'schema';
import dayjs from 'dayjs';
import Joi from 'joi';
import get from 'lodash/get';
@ -50,8 +50,8 @@ const AwardModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -73,7 +73,7 @@ const AwardModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -101,7 +101,7 @@ const AwardModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.title.label')}
label={t('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<string>('builder.leftSidebar.sections.awards.form.awarder.label')}
label={t('builder.leftSidebar.sections.awards.form.awarder.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -130,7 +130,7 @@ const AwardModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.date.label')}
label={t('builder.common.form.date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -154,7 +154,7 @@ const AwardModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
placeholder="https://"
error={!!fieldState.error}
helperText={fieldState.error?.message}
@ -171,7 +171,7 @@ const AwardModal: React.FC = () => {
multiline
minRows={3}
maxRows={6}
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message || <MarkdownSupported />}

View File

@ -2,7 +2,7 @@ import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { Certificate, SectionPath } from '@reactive-resume/schema';
import { Certificate, SectionPath } from 'schema';
import dayjs from 'dayjs';
import Joi from 'joi';
import get from 'lodash/get';
@ -50,8 +50,8 @@ const CertificateModal: React.FC = () => {
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -73,7 +73,7 @@ const CertificateModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -101,7 +101,7 @@ const CertificateModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.name.label')}
label={t('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<string>('builder.leftSidebar.sections.certifications.form.issuer.label')}
label={t('builder.leftSidebar.sections.certifications.form.issuer.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -130,7 +130,7 @@ const CertificateModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.date.label')}
label={t('builder.common.form.date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -154,7 +154,7 @@ const CertificateModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
placeholder="https://"
error={!!fieldState.error}
helperText={fieldState.error?.message}
@ -171,7 +171,7 @@ const CertificateModal: React.FC = () => {
multiline
minRows={3}
maxRows={6}
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message || <MarkdownSupported />}

View File

@ -2,7 +2,6 @@ import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, Slider, TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { Custom } from '@reactive-resume/schema';
import dayjs from 'dayjs';
import Joi from 'joi';
import get from 'lodash/get';
@ -10,6 +9,7 @@ import isEmpty from 'lodash/isEmpty';
import { useTranslation } from 'next-i18next';
import { useEffect, useMemo } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Custom } from 'schema';
import ArrayInput from '@/components/shared/ArrayInput';
import BaseModal from '@/components/shared/BaseModal';
@ -68,8 +68,8 @@ const CustomModal: React.FC = () => {
const heading = useAppSelector((state) => get(state.resume.present, `${path}.name`));
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -91,7 +91,7 @@ const CustomModal: React.FC = () => {
setModalState({
modal: 'builder.sections.custom',
state: { open: false },
})
}),
);
reset(defaultState);
@ -119,7 +119,7 @@ const CustomModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.title.label')}
label={t('builder.common.form.title.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -132,7 +132,7 @@ const CustomModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.subtitle.label')}
label={t('builder.common.form.subtitle.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -147,7 +147,7 @@ const CustomModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.start-date.label')}
label={t('builder.common.form.start-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -173,7 +173,7 @@ const CustomModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.end-date.label')}
label={t('builder.common.form.end-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -181,7 +181,7 @@ const CustomModal: React.FC = () => {
<TextField
{...params}
error={!!fieldState.error}
helperText={fieldState.error?.message || t<string>('builder.common.form.end-date.help-text')}
helperText={fieldState.error?.message || t('builder.common.form.end-date.help-text')}
/>
),
}}
@ -197,7 +197,7 @@ const CustomModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
placeholder="https://"
className="col-span-2"
error={!!fieldState.error}
@ -212,7 +212,7 @@ const CustomModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.level.label')}
label={t('builder.common.form.level.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message}
@ -226,11 +226,13 @@ const CustomModal: React.FC = () => {
control={control}
render={({ field }) => (
<div className="col-span-2">
<h4 className="mb-3 font-semibold">{t<string>('builder.common.form.levelNum.label')}</h4>
<h4 className="mb-3 font-semibold">{t('builder.common.form.levelNum.label')}</h4>
<div className="px-10">
<Slider
{...field}
name={field.name}
value={field.value}
onChange={(_, value) => field.onChange(value as number)}
marks={[
{
value: 0,
@ -250,7 +252,7 @@ const CustomModal: React.FC = () => {
defaultValue={0}
color="secondary"
valueLabelDisplay="auto"
aria-label={t<string>('builder.common.form.levelNum.label')}
aria-label={t('builder.common.form.levelNum.label')}
/>
</div>
</div>
@ -267,7 +269,7 @@ const CustomModal: React.FC = () => {
maxRows={6}
className="col-span-2"
error={!!fieldState.error}
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
helperText={fieldState.error?.message || <MarkdownSupported />}
{...field}
/>
@ -279,7 +281,7 @@ const CustomModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<ArrayInput
label={t<string>('builder.common.form.keywords.label')}
label={t('builder.common.form.keywords.label')}
value={field.value as string[]}
onChange={field.onChange}
errors={fieldState.error}

View File

@ -2,7 +2,7 @@ import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { Education, SectionPath } from '@reactive-resume/schema';
import { Education, SectionPath } from 'schema';
import dayjs from 'dayjs';
import Joi from 'joi';
import get from 'lodash/get';
@ -63,8 +63,8 @@ const EducationModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -86,7 +86,7 @@ const EducationModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -114,7 +114,7 @@ const EducationModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.leftSidebar.sections.education.form.institution.label')}
label={t('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<string>('builder.leftSidebar.sections.education.form.degree.label')}
label={t('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<string>('builder.leftSidebar.sections.education.form.area-study.label')}
label={t('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<string>('builder.leftSidebar.sections.education.form.grade.label')}
label={t('builder.leftSidebar.sections.education.form.grade.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -169,17 +169,14 @@ const EducationModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.start-date.label')}
label={t('builder.common.form.start-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
textField: (params) => (
<TextField
{...params}
error={!!fieldState.error}
helperText={fieldState.error?.message || params.inputProps?.placeholder}
/>
),
slotProps={{
textField: {
error: !!fieldState.error,
helperText: fieldState.error?.message || t('builder.common.form.start-date.help-text')
},
}}
onChange={(date: dayjs.Dayjs | null) => {
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
@ -195,17 +192,14 @@ const EducationModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.end-date.label')}
label={t('builder.common.form.end-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
textField: (params) => (
<TextField
{...params}
error={!!fieldState.error}
helperText={fieldState.error?.message || t<string>('builder.common.form.end-date.help-text')}
/>
),
slotProps={{
textField: {
error: !!fieldState.error,
helperText: fieldState.error?.message || t('builder.common.form.end-date.help-text')
},
}}
onChange={(date: dayjs.Dayjs | null) => {
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
@ -219,7 +213,7 @@ const EducationModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
placeholder="https://"
className="col-span-2"
error={!!fieldState.error}
@ -237,7 +231,7 @@ const EducationModal: React.FC = () => {
multiline
minRows={3}
maxRows={6}
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message || <MarkdownSupported />}
@ -251,7 +245,7 @@ const EducationModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<ArrayInput
label={t<string>('builder.leftSidebar.sections.education.form.courses.label')}
label={t('builder.leftSidebar.sections.education.form.courses.label')}
value={field.value as string[]}
onChange={field.onChange}
errors={fieldState.error}

View File

@ -1,7 +1,7 @@
import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { Interest, SectionPath } from '@reactive-resume/schema';
import { Interest, SectionPath } from 'schema';
import Joi from 'joi';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
@ -41,8 +41,8 @@ const InterestModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -64,7 +64,7 @@ const InterestModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -92,7 +92,7 @@ const InterestModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.name.label')}
label={t('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<string>('builder.common.form.keywords.label')}
label={t('builder.common.form.keywords.label')}
value={field.value as string[]}
onChange={field.onChange}
errors={fieldState.error}

View File

@ -1,13 +1,13 @@
import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, Slider, TextField } from '@mui/material';
import { Language, SectionPath } from '@reactive-resume/schema';
import Joi from 'joi';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { useTranslation } from 'next-i18next';
import { useEffect, useMemo } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Language, SectionPath } from 'schema';
import BaseModal from '@/components/shared/BaseModal';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
@ -42,8 +42,8 @@ const LanguageModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -65,7 +65,7 @@ const LanguageModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -93,7 +93,7 @@ const LanguageModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.name.label')}
label={t('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<string>('builder.common.form.level.label')}
label={t('builder.common.form.level.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -120,11 +120,13 @@ const LanguageModal: React.FC = () => {
control={control}
render={({ field }) => (
<div className="col-span-2">
<h4 className="mb-3 font-semibold">{t<string>('builder.common.form.levelNum.label')}</h4>
<h4 className="mb-3 font-semibold">{t('builder.common.form.levelNum.label')}</h4>
<div className="px-10">
<Slider
{...field}
name={field.name}
value={field.value}
onChange={(_, value) => field.onChange(value as number)}
marks={[
{
value: 0,
@ -144,7 +146,7 @@ const LanguageModal: React.FC = () => {
defaultValue={0}
color="secondary"
valueLabelDisplay="auto"
aria-label={t<string>('builder.common.form.levelNum.label')}
aria-label={t('builder.common.form.levelNum.label')}
/>
</div>
</div>

View File

@ -1,7 +1,7 @@
import { joiResolver } from '@hookform/resolvers/joi';
import { Add, AlternateEmail, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { Profile } from '@reactive-resume/schema';
import { Profile } from 'schema';
import Joi from 'joi';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
@ -42,11 +42,11 @@ const ProfileModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
const addText = t<string>('builder.common.actions.add', {
token: t<string>('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
const addText = t('builder.common.actions.add', {
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 editText = t('builder.common.actions.edit', {
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
});
const { reset, control, handleSubmit } = useForm<FormData>({
@ -69,7 +69,7 @@ const ProfileModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -97,7 +97,7 @@ const ProfileModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.leftSidebar.sections.profiles.form.network.label')}
label={t('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<string>('builder.leftSidebar.sections.profiles.form.username.label')}
label={t('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<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
className="col-span-2"
placeholder="https://"
error={!!fieldState.error}

View File

@ -2,7 +2,7 @@ import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { Project, SectionPath } from '@reactive-resume/schema';
import { Project, SectionPath } from 'schema';
import dayjs from 'dayjs';
import Joi from 'joi';
import get from 'lodash/get';
@ -59,8 +59,8 @@ const ProjectModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -82,7 +82,7 @@ const ProjectModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -110,7 +110,7 @@ const ProjectModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.name.label')}
label={t('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<string>('builder.common.form.description.label')}
label={t('builder.common.form.description.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -139,7 +139,7 @@ const ProjectModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.start-date.label')}
label={t('builder.common.form.start-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -165,7 +165,7 @@ const ProjectModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.end-date.label')}
label={t('builder.common.form.end-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -173,7 +173,7 @@ const ProjectModal: React.FC = () => {
<TextField
{...params}
error={!!fieldState.error}
helperText={fieldState.error?.message || t<string>('builder.common.form.end-date.help-text')}
helperText={fieldState.error?.message || t('builder.common.form.end-date.help-text')}
/>
),
}}
@ -189,7 +189,7 @@ const ProjectModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
placeholder="https://"
className="col-span-2"
error={!!fieldState.error}
@ -207,7 +207,7 @@ const ProjectModal: React.FC = () => {
multiline
minRows={3}
maxRows={6}
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message || <MarkdownSupported />}
@ -221,7 +221,7 @@ const ProjectModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<ArrayInput
label={t<string>('builder.common.form.keywords.label')}
label={t('builder.common.form.keywords.label')}
value={field.value as string[]}
onChange={field.onChange}
errors={fieldState.error}

View File

@ -2,7 +2,7 @@ import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { Publication, SectionPath } from '@reactive-resume/schema';
import { Publication, SectionPath } from 'schema';
import dayjs from 'dayjs';
import Joi from 'joi';
import get from 'lodash/get';
@ -50,8 +50,8 @@ const PublicationModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -73,7 +73,7 @@ const PublicationModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -101,7 +101,7 @@ const PublicationModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.name.label')}
label={t('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<string>('builder.leftSidebar.sections.publications.form.publisher.label')}
label={t('builder.leftSidebar.sections.publications.form.publisher.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -130,7 +130,7 @@ const PublicationModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.date.label')}
label={t('builder.common.form.date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -154,7 +154,7 @@ const PublicationModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
placeholder="https://"
error={!!fieldState.error}
helperText={fieldState.error?.message}
@ -171,7 +171,7 @@ const PublicationModal: React.FC = () => {
multiline
minRows={3}
maxRows={6}
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message || <MarkdownSupported />}

View File

@ -1,7 +1,7 @@
import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { Reference, SectionPath } from '@reactive-resume/schema';
import { Reference, SectionPath } from 'schema';
import Joi from 'joi';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
@ -47,8 +47,8 @@ const ReferenceModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -70,7 +70,7 @@ const ReferenceModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -98,7 +98,7 @@ const ReferenceModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.name.label')}
label={t('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<string>('builder.leftSidebar.sections.references.form.relationship.label')}
label={t('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<string>('builder.common.form.phone.label')}
label={t('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<string>('builder.common.form.email.label')}
label={t('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<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message || <MarkdownSupported />}

View File

@ -1,13 +1,13 @@
import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, Slider, TextField } from '@mui/material';
import { SectionPath, Skill } from '@reactive-resume/schema';
import Joi from 'joi';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { useTranslation } from 'next-i18next';
import { useEffect, useMemo } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { SectionPath, Skill } from 'schema';
import ArrayInput from '@/components/shared/ArrayInput';
import BaseModal from '@/components/shared/BaseModal';
@ -45,8 +45,8 @@ const SkillModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -68,7 +68,7 @@ const SkillModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -96,7 +96,7 @@ const SkillModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.name.label')}
label={t('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<string>('builder.common.form.level.label')}
label={t('builder.common.form.level.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -122,11 +122,13 @@ const SkillModal: React.FC = () => {
control={control}
render={({ field }) => (
<div className="col-span-2">
<h4 className="mb-3 font-semibold">{t<string>('builder.common.form.levelNum.label')}</h4>
<h4 className="mb-3 font-semibold">{t('builder.common.form.levelNum.label')}</h4>
<div className="px-3">
<Slider
{...field}
name={field.name}
value={field.value}
onChange={(_, value) => field.onChange(value as number)}
marks={[
{
value: 0,
@ -146,7 +148,7 @@ const SkillModal: React.FC = () => {
defaultValue={0}
color="secondary"
valueLabelDisplay="auto"
aria-label={t<string>('builder.common.form.levelNum.label')}
aria-label={t('builder.common.form.levelNum.label')}
/>
</div>
</div>
@ -158,7 +160,7 @@ const SkillModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<ArrayInput
label={t<string>('builder.common.form.keywords.label')}
label={t('builder.common.form.keywords.label')}
value={field.value}
onChange={field.onChange}
errors={fieldState.error}

View File

@ -2,7 +2,7 @@ import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { SectionPath, Volunteer } from '@reactive-resume/schema';
import { SectionPath, Volunteer } from 'schema';
import dayjs from 'dayjs';
import Joi from 'joi';
import get from 'lodash/get';
@ -56,8 +56,8 @@ const VolunteerModal: React.FC = () => {
const item: FormData = get(payload, 'item', null);
const isEditMode = useMemo(() => !!item, [item]);
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 addText = useMemo(() => t('builder.common.actions.add', { token: heading }), [t, heading]);
const editText = useMemo(() => t('builder.common.actions.edit', { token: heading }), [t, heading]);
const { reset, control, handleSubmit } = useForm<FormData>({
defaultValues: defaultState,
@ -79,7 +79,7 @@ const VolunteerModal: React.FC = () => {
setModalState({
modal: `builder.${path}`,
state: { open: false },
})
}),
);
reset(defaultState);
@ -107,7 +107,7 @@ const VolunteerModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.leftSidebar.sections.volunteer.form.organization.label')}
label={t('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<string>('builder.common.form.position.label')}
label={t('builder.common.form.position.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -136,7 +136,7 @@ const VolunteerModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.start-date.label')}
label={t('builder.common.form.start-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -162,7 +162,7 @@ const VolunteerModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.end-date.label')}
label={t('builder.common.form.end-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
@ -170,7 +170,7 @@ const VolunteerModal: React.FC = () => {
<TextField
{...params}
error={!!fieldState.error}
helperText={fieldState.error?.message || t<string>('builder.common.form.end-date.help-text')}
helperText={fieldState.error?.message || t('builder.common.form.end-date.help-text')}
/>
),
}}
@ -186,7 +186,7 @@ const VolunteerModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
placeholder="https://"
className="col-span-2"
error={!!fieldState.error}
@ -204,7 +204,7 @@ const VolunteerModal: React.FC = () => {
multiline
minRows={3}
maxRows={6}
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message || <MarkdownSupported />}

View File

@ -2,7 +2,7 @@ import { joiResolver } from '@hookform/resolvers/joi';
import { Add, DriveFileRenameOutline } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { WorkExperience } from '@reactive-resume/schema';
import { WorkExperience } from 'schema';
import dayjs from 'dayjs';
import Joi from 'joi';
import get from 'lodash/get';
@ -57,13 +57,18 @@ const WorkModal: React.FC = () => {
const isEditMode = useMemo(() => !!item, [item]);
const addText = useMemo(
() => t<string>('builder.common.actions.add', { token: t<string>(`builder.leftSidebar.${path}.heading`, heading) }),
[t, heading]
() =>
t('builder.common.actions.add', {
token: t(`builder.leftSidebar.${path}.heading`, { defaultValue: heading }),
}),
[t, heading],
);
const editText = useMemo(
() =>
t<string>('builder.common.actions.edit', { token: t<string>(`builder.leftSidebar.${path}.heading`, heading) }),
[t, heading]
t('builder.common.actions.edit', {
token: t(`builder.leftSidebar.${path}.heading`, { defaultValue: heading }),
}),
[t, heading],
);
const { reset, control, handleSubmit } = useForm<FormData>({
@ -86,7 +91,7 @@ const WorkModal: React.FC = () => {
setModalState({
modal: 'builder.sections.work',
state: { open: false },
})
}),
);
reset(defaultState);
@ -114,7 +119,7 @@ const WorkModal: React.FC = () => {
<TextField
required
autoFocus
label={t<string>('builder.common.form.name.label')}
label={t('builder.leftSidebar.sections.experience.form.name.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -128,7 +133,7 @@ const WorkModal: React.FC = () => {
render={({ field, fieldState }) => (
<TextField
required
label={t<string>('builder.common.form.position.label')}
label={t('builder.common.form.position.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -143,17 +148,14 @@ const WorkModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.start-date.label')}
label={t('builder.common.form.start-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
textField: (params) => (
<TextField
{...params}
error={!!fieldState.error}
helperText={fieldState.error?.message || params.inputProps?.placeholder}
/>
),
slotProps={{
textField: {
error: !!fieldState.error,
helperText: fieldState.error?.message || t('builder.common.form.start-date.help-text')
},
}}
onChange={(date: dayjs.Dayjs | null) => {
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
@ -169,17 +171,14 @@ const WorkModal: React.FC = () => {
<DatePicker
openTo="year"
inputRef={field.ref}
label={t<string>('builder.common.form.end-date.label')}
label={t('builder.common.form.end-date.label')}
value={dayjs(field.value)}
views={['year', 'month', 'day']}
slots={{
textField: (params) => (
<TextField
{...params}
error={!!fieldState.error}
helperText={fieldState.error?.message || t<string>('builder.common.form.end-date.help-text')}
/>
),
slotProps={{
textField: {
error: !!fieldState.error,
helperText: fieldState.error?.message || t('builder.common.form.end-date.help-text')
},
}}
onChange={(date: dayjs.Dayjs | null) => {
date && dayjs(date).isValid() && field.onChange(dayjs(date).format('YYYY-MM-DD'));
@ -193,7 +192,7 @@ const WorkModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('builder.common.form.url.label')}
label={t('builder.common.form.url.label')}
placeholder="https://"
className="col-span-2"
error={!!fieldState.error}
@ -211,7 +210,7 @@ const WorkModal: React.FC = () => {
multiline
minRows={3}
maxRows={6}
label={t<string>('builder.common.form.summary.label')}
label={t('builder.common.form.summary.label')}
className="col-span-2"
error={!!fieldState.error}
helperText={fieldState.error?.message || <MarkdownSupported />}

View File

@ -1,7 +1,7 @@
import { joiResolver } from '@hookform/resolvers/joi';
import { Add } from '@mui/icons-material';
import { Button, FormControlLabel, FormGroup, Switch, TextField } from '@mui/material';
import { Resume } from '@reactive-resume/schema';
import { Resume } from 'schema';
import Joi from 'joi';
import { useTranslation } from 'next-i18next';
import { useEffect } from 'react';
@ -80,15 +80,15 @@ const CreateResumeModal: React.FC = () => {
<BaseModal
isOpen={isOpen}
icon={<Add />}
heading={t<string>('modals.dashboard.create-resume.heading')}
heading={t('modals.dashboard.create-resume.heading')}
handleClose={handleClose}
footerChildren={
<Button type="submit" disabled={isLoading} onClick={handleSubmit(onSubmit)}>
{t<string>('modals.dashboard.create-resume.actions.create-resume')}
{t('modals.dashboard.create-resume.actions.create-resume')}
</Button>
}
>
<p>{t<string>('modals.dashboard.create-resume.body')}</p>
<p>{t('modals.dashboard.create-resume.body')}</p>
<form className="grid gap-4">
<Controller
@ -97,7 +97,7 @@ const CreateResumeModal: React.FC = () => {
render={({ field, fieldState }) => (
<TextField
autoFocus
label={t<string>('modals.dashboard.create-resume.form.name.label')}
label={t('modals.dashboard.create-resume.form.name.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -110,7 +110,7 @@ const CreateResumeModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
label={t<string>('modals.dashboard.create-resume.form.slug.label')}
label={t('modals.dashboard.create-resume.form.slug.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
{...field}
@ -120,7 +120,7 @@ const CreateResumeModal: React.FC = () => {
<FormGroup>
<FormControlLabel
label={t<string>('modals.dashboard.create-resume.form.public.label')}
label={t('modals.dashboard.create-resume.form.public.label')}
control={
<Controller
name="isPublic"

View File

@ -1,6 +1,6 @@
import { Code, ImportExport, LinkedIn, TrackChanges, UploadFile } from '@mui/icons-material';
import { Button, Divider } from '@mui/material';
import { Integration, Resume } from '@reactive-resume/schema';
import { Integration, Resume } from 'schema';
import { Trans, useTranslation } from 'next-i18next';
import { useRef } from 'react';
import toast from 'react-hot-toast';
@ -63,7 +63,7 @@ const ImportExternalModal: React.FC = () => {
const file = event.target.files[0];
if (file.size > FILE_UPLOAD_MAX_SIZE) {
toast.error(t<string>('common.toast.error.upload-file-size'));
toast.error(t('common.toast.error.upload-file-size'));
return;
}
@ -78,13 +78,13 @@ const ImportExternalModal: React.FC = () => {
<BaseModal
isOpen={isOpen}
icon={<ImportExport />}
heading={t<string>('modals.dashboard.import-external.heading')}
heading={t('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<string>('modals.dashboard.import-external.linkedin.heading')}
{t('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<string>('modals.dashboard.import-external.linkedin.actions.upload-archive')}
{t('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<string>('modals.dashboard.import-external.json-resume.heading')}
{t('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<string>('modals.dashboard.import-external.json-resume.actions.upload-json')}
{t('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<string>('modals.dashboard.import-external.reactive-resume.heading')}
{t('modals.dashboard.import-external.reactive-resume.heading')}
</h2>
<p className="mb-2">{t<string>('modals.dashboard.import-external.reactive-resume.body')}</p>
<p className="mb-2">{t('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<string>('modals.dashboard.import-external.reactive-resume.actions.upload-json')}
{t('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<string>('modals.dashboard.import-external.reactive-resume.actions.upload-json-v2')}
{t('modals.dashboard.import-external.reactive-resume.actions.upload-json-v2')}
</Button>
<input

Some files were not shown because too many files have changed in this diff Show More