Compare commits

...

77 Commits

Author SHA1 Message Date
c7fffff495 chore(release): 3.2.3 2022-03-14 21:50:20 +01:00
42408ce8c5 feat(client/import): implement import json from reactive resume v2 2022-03-14 21:50:04 +01:00
ed78f8fc4e Merge pull request #687 from chandiwalaaadhar/fix/pikachu-masthead-asymmetric-basic-details
Fix-Pikachu Masthead Basic Details looks asymmetric Due to Centred Alignment
2022-03-14 15:51:16 +01:00
318145f007 Create CODE_OF_CONDUCT.md 2022-03-14 15:09:47 +01:00
c2a35a1066 Merge pull request #685 from chandiwalaaadhar/fix/work-experience-website-link-redirects-404
Fix - Work Experience Website Link Redirects to 404 if manually entered without http/https
2022-03-14 15:08:54 +01:00
541cfa784d Fix-Pikachu Masthead Asymmetric Basic Details 2022-03-14 19:38:49 +05:30
de53d8dfe7 Removed url && in the link prop 2022-03-14 19:32:12 +05:30
c28afbc75d Fix Work Experience Website Link redirects to 404 if entered without http or https 2022-03-14 19:12:36 +05:30
40e6227aa9 chore(release): 3.2.2 2022-03-14 10:28:29 +01:00
02e396bfdb fix(client/skills): make skill level optional 2022-03-14 10:26:57 +01:00
4dc83c1d7f docs(docker): update docs to add docker deployment example to droplet 2022-03-14 10:18:04 +01:00
143a123212 Create docker.mdx 2022-03-14 10:11:52 +01:00
c64b96619f docs(source-code): fix links to docker, local-build on source-code index page 2022-03-14 09:26:15 +01:00
ff35a2a95c chore(release): 3.2.1 2022-03-14 09:23:16 +01:00
549363bbe5 feat(i18n): add Chinese (Simplified) language to locales 2022-03-14 09:23:01 +01:00
e6bda688ac fix(client/basics): fix issue with overlapping photo filters on safari/webkit/iOS 2022-03-14 09:19:22 +01:00
64b0c5e7cf Merge pull request #681 from AmruthPillai/i18n_main
New Crowdin updates
2022-03-14 09:09:26 +01:00
57f7edc134 fix(docker): fix docker-compose for production grade deployments 2022-03-14 09:03:47 +01:00
c62a3c2dfd New translations modals.json (Chinese Simplified) 2022-03-14 07:38:30 +01:00
b7f024913c New translations dashboard.json (Chinese Simplified) 2022-03-14 07:38:28 +01:00
488631e6b0 New translations landing.json (Chinese Simplified) 2022-03-14 07:38:25 +01:00
ca5a866249 New translations builder.json (Chinese Simplified) 2022-03-14 07:38:23 +01:00
3a0cd4e150 New translations common.json (Chinese Simplified) 2022-03-14 07:38:19 +01:00
e82e714e41 chore(release): 3.2.0 2022-03-14 06:40:41 +01:00
21931bc324 feat(i18n): add Bengali, Italian and other languages 2022-03-14 06:40:16 +01:00
ed75a85827 fix(client): fix issue with react-query cache 2022-03-14 06:33:14 +01:00
fbb0285d0d Merge pull request #678 from AmruthPillai/i18n_main
New Crowdin updates
2022-03-14 06:10:51 +01:00
b056b002b7 New translations builder.json (Italian) 2022-03-14 02:04:09 +01:00
8b32bfb9f4 New translations modals.json (Italian) 2022-03-13 22:03:59 +01:00
cf3696c976 New translations dashboard.json (Italian) 2022-03-13 22:03:56 +01:00
aa0dc1d7fb New translations landing.json (Italian) 2022-03-13 22:03:54 +01:00
f5bf77cfd0 New translations builder.json (Italian) 2022-03-13 22:03:51 +01:00
9ddbc7cab2 New translations common.json (Italian) 2022-03-13 22:03:49 +01:00
f7d11c5fd2 New translations landing.json (Bengali) 2022-03-13 21:03:51 +01:00
bede07656b New translations dashboard.json (Bengali) 2022-03-13 21:03:48 +01:00
49b56f7a76 New translations modals.json (Bengali) 2022-03-13 20:08:25 +01:00
1421fc5183 New translations common.json (Bengali) 2022-03-13 20:08:20 +01:00
b3da226d24 New translations builder.json (Bengali) 2022-03-13 20:08:19 +01:00
3d7a5b9313 Merge pull request #675 from AmruthPillai/i18n_main
New Crowdin updates
2022-03-13 19:02:01 +01:00
86ca4602fd Merge pull request #667 from chandiwalaaadhar/fix/skillModal-disable-and-beginner-text-clashing
Fixed Near Overlapping of "Disable" & "Beginner" Label Text in Skills Modal for Desktop Screen
2022-03-13 19:01:52 +01:00
3dde7e5772 Merge pull request #669 from chandiwalaaadhar/fix/gengar-theme-masthead-icons-disapper
Fix-Gengar Theme MastHead Icons Disappear on Theme/Primary Color Change
2022-03-13 19:01:45 +01:00
0782c616ea Merge branch 'main' into fix/gengar-theme-masthead-icons-disapper 2022-03-13 18:56:14 +01:00
d1d3f240b4 Merge branch 'main' of github.com:AmruthPillai/Reactive-Resume 2022-03-13 18:54:03 +01:00
b18120b3f7 fix(app): fix issue with external link redirection in android app 2022-03-13 18:53:48 +01:00
b5809ea449 New translations common.json (Vietnamese) 2022-03-13 17:52:24 +01:00
01acec4a51 New translations common.json (Spanish) 2022-03-13 17:52:21 +01:00
9d076d384c Merge branch 'main' into fix/gengar-theme-masthead-icons-disapper 2022-03-13 21:35:16 +05:30
e7a8596456 Fix-Gengar Theme MastHead Icons Disappear on Theme Change 2022-03-13 12:47:57 +05:30
ab4df6193c Fixed Near Overlapping of Disable & Beginner Text in Skills Modal for Desktop 2022-03-13 12:00:25 +05:30
e4a9f269d2 Merge pull request #662 from AmruthPillai/i18n_main
New Crowdin updates
2022-03-13 07:24:36 +01:00
189cc702c2 New translations modals.json (Italian) 2022-03-13 07:13:32 +01:00
c348b6449b New translations landing.json (Italian) 2022-03-13 07:13:29 +01:00
708920df44 New translations landing.json (German) 2022-03-13 07:13:28 +01:00
81733e5855 New translations landing.json (Spanish) 2022-03-13 07:13:27 +01:00
794c7df374 New translations modals.json (German) 2022-03-13 07:13:24 +01:00
267f593ec2 New translations modals.json (Spanish) 2022-03-13 07:13:23 +01:00
048927a163 New translations dashboard.json (Spanish) 2022-03-13 07:13:16 +01:00
f4a65122c6 New translations dashboard.json (German) 2022-03-13 07:13:15 +01:00
6587c76397 New translations dashboard.json (Italian) 2022-03-13 07:13:14 +01:00
80223a240c New translations builder.json (Italian) 2022-03-13 07:13:12 +01:00
50faa5dff3 New translations modals.json (French) 2022-03-12 20:25:37 +01:00
6a4521b057 New translations landing.json (French) 2022-03-12 20:25:33 +01:00
81a4d7291a New translations dashboard.json (French) 2022-03-12 20:25:32 +01:00
381cfcc220 New translations builder.json (French) 2022-03-12 20:25:28 +01:00
0f555e4f88 New translations common.json (French) 2022-03-12 20:25:27 +01:00
ebd9253038 chore(release): 3.1.4 2022-03-12 19:46:45 +01:00
cf670af403 fix(client): exported pdf did not contain "Present" keyword with translations 2022-03-12 19:46:30 +01:00
dfccb3130f fix(client): fix issues raised through lgtm alerts 2022-03-12 18:56:00 +01:00
ef06240935 docs(readme): add localization percentage badge 2022-03-12 18:47:34 +01:00
55e57353a4 New translations modals.json (Italian) 2022-03-12 18:23:47 +01:00
f0144cc6e7 New translations landing.json (Italian) 2022-03-12 18:23:44 +01:00
e5150ab128 New translations common.json (Italian) 2022-03-12 18:23:40 +01:00
d61905db10 chore(release): 3.1.3 2022-03-12 17:41:29 +01:00
6d55f917ea fix(server): reform url for pdf generation and download
fix #661
2022-03-12 17:41:16 +01:00
4371f3b693 New translations dashboard.json (Italian) 2022-03-12 17:28:42 +01:00
c8c5916d02 Update local-build.mdx 2022-03-12 16:09:08 +01:00
3ca27f2326 docs(style): update CHANGELOG.md 2022-03-12 15:59:34 +01:00
95 changed files with 3629 additions and 553 deletions

View File

@ -3,15 +3,15 @@ TZ=UTC
SECRET_KEY=change-me
# URLs
PUBLIC_URL=http://localhost:3000
PUBLIC_SERVER_URL=http://localhost:3100
PUBLIC_URL=http://<SERVER-IP>
PUBLIC_SERVER_URL=http://<SERVER-IP>/api
# Database
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USERNAME=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DATABASE=reactive_resume
POSTGRES_DATABASE=postgres
POSTGRES_SSL_CERT=
# Auth

View File

@ -2,8 +2,68 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [3.2.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.2...v3.2.3) (2022-03-14)
### Features
* **client/import:** implement import json from reactive resume v2 ([42408ce](https://github.com/AmruthPillai/Reactive-Resume/commit/42408ce8c5ce55904854f9f6e0481889a01edfb8))
### [3.2.2](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.1...v3.2.2) (2022-03-14)
### Bug Fixes
* **client/skills:** make skill level optional ([02e396b](https://github.com/AmruthPillai/Reactive-Resume/commit/02e396bfdbf07ae75661f1e7e4e55060cacee7d0))
### [3.2.1](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.2.0...v3.2.1) (2022-03-14)
### Features
* **i18n:** add Chinese (Simplified) language to locales ([549363b](https://github.com/AmruthPillai/Reactive-Resume/commit/549363bbe5bdd781699dea9506bd4baedf5740d1))
### Bug Fixes
* **client/basics:** fix issue with overlapping photo filters on safari/webkit/iOS ([e6bda68](https://github.com/AmruthPillai/Reactive-Resume/commit/e6bda688ac3ba1c04e82721add92e755ea5386c3))
* **docker:** fix docker-compose for production grade deployments ([57f7edc](https://github.com/AmruthPillai/Reactive-Resume/commit/57f7edc13432a038c907afc6cb74b5182a9b2333))
## [3.2.0](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.4...v3.2.0) (2022-03-14)
### Features
* **i18n:** add Bengali, Italian and other languages ([21931bc](https://github.com/AmruthPillai/Reactive-Resume/commit/21931bc324b5e2440baaaaa2e52a93b4f2c766f8))
### Bug Fixes
* **app:** fix issue with external link redirection in android app ([b18120b](https://github.com/AmruthPillai/Reactive-Resume/commit/b18120b3f7223981e28c0441a6b7725787186edb))
* **client:** fix issue with react-query cache ([ed75a85](https://github.com/AmruthPillai/Reactive-Resume/commit/ed75a858279047dfd43152e041c1a09a625417f5))
### [3.1.4](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.3...v3.1.4) (2022-03-12)
### Bug Fixes
* **client:** exported pdf did not contain "Present" keyword with translations ([cf670af](https://github.com/AmruthPillai/Reactive-Resume/commit/cf670af4035dc9b462cf5b1aad06ca089cf1d40c))
* **client:** fix issues raised through lgtm alerts ([dfccb31](https://github.com/AmruthPillai/Reactive-Resume/commit/dfccb3130f889934d31196226be3d33e772f323b))
### [3.1.3](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.2...v3.1.3) (2022-03-12)
### Bug Fixes
* **server:** reform url for pdf generation and download ([6d55f91](https://github.com/AmruthPillai/Reactive-Resume/commit/6d55f917eab3cb2f5f3a90c5a18f03b625d60021)), closes [#661](https://github.com/AmruthPillai/Reactive-Resume/issues/661)
### [3.1.2](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.1...v3.1.2) (2022-03-12)
### CI
* **docker:**: include traefik routing and proxy to ensure server connections pass in local ([11cb066](https://github.com/AmruthPillai/Reactive-Resume/commit/11cb066573c6917857b79c028b97fcda1acaf90a))
### [3.1.1](https://github.com/AmruthPillai/Reactive-Resume/compare/v3.1.0...v3.1.1) (2022-03-12)

128
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
im.amruth@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -4,6 +4,7 @@
![Project Version](https://img.shields.io/github/package-json/v/AmruthPillai/Reactive-Resume?style=flat-square)
![Project License](https://img.shields.io/github/license/AmruthPillai/Reactive-Resume?style=flat-square)
[![Crowdin](https://badges.crowdin.net/reactive-resume/localized.svg)](https://translate.rxresu.me)
[![Docker Pulls](https://img.shields.io/docker/pulls/amruthpillai/reactive-resume?style=flat-square)](https://hub.docker.com/r/amruthpillai/reactive-resume)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FAmruthPillai%2FReactive-Resume.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FAmruthPillai%2FReactive-Resume?ref=badge_shield)
@ -35,11 +36,15 @@ You have complete control over what goes into your resume, how it looks, what co
## Languages
- Bengali (বাংলা)
- Chinese (中文)
- English
- French (Français)
- German (Deutsch)
- Hindi (हिन्दी)
- Kannada (ಕನ್ನಡ) (@aksh1251)
- Spanish (Español) (@seba11998)
- Italian (Italiano)
- Kannada (ಕನ್ನಡ)
- Spanish (Español)
- Tamil (தமிழ்)
Help by [translating Reactive Resume](https://translate.rxresu.me) to your language!

View File

@ -10,7 +10,7 @@ android {
applicationId "me.rxresu.app"
minSdk 21
targetSdk 32
versionCode 2
versionCode 3
versionName "1.0"
resConfigs "en"

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="me.rxresu.app">
<uses-permission android:name="android.permission.INTERNET"/>
@ -10,11 +11,11 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ReactiveResume.NoActionBar">
android:theme="@style/AppTheme">
<activity
android:configChanges="orientation|screenSize"
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.ReactiveResume.NoActionBar">
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -0,0 +1,21 @@
package me.rxresu.app
import android.content.Intent
import android.net.Uri
import android.webkit.WebView
import android.webkit.WebViewClient
internal class CustomWebViewClient : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
val hostname = "rxresu.me"
val uri = Uri.parse(url)
if (uri.host != null && uri.host!!.endsWith(hostname)) {
return false
}
view.context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
return true
}
}

View File

@ -1,21 +1,15 @@
package me.rxresu.app
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.os.Bundle
import android.view.KeyEvent
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
private var isLoaded: Boolean = false
private var webURL = "https://rxresu.me"
private var url = "https://rxresu.me"
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
@ -24,53 +18,18 @@ class MainActivity : AppCompatActivity() {
webView = findViewById(R.id.webview)
webView.webViewClient = CustomWebViewClient()
webView.settings.javaScriptEnabled = true
webView.settings.userAgentString = "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Mobile Safari/537.36"
webView.loadUrl(url)
}
override fun onResume() {
if (!isLoaded) loadWebView()
super.onResume()
}
private fun loadWebView() {
webView.loadUrl(webURL)
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
val url = request?.url.toString()
view?.loadUrl(url)
return super.shouldOverrideUrlLoading(view, request)
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
}
override fun onPageFinished(view: WebView?, url: String?) {
isLoaded = true
super.onPageFinished(view, url)
}
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
isLoaded = false
super.onReceivedError(view, request, error)
}
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
super.onBackPressed()
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (webView.canGoBack()) {
webView.goBack()
}
return true
}
}
return super.onKeyDown(keyCode, event)
}
}

View File

@ -2,7 +2,6 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

View File

@ -1,12 +1,6 @@
<resources>
<style name="Theme.ReactiveResume" parent="Theme.MaterialComponents.DayNight.DarkActionBar" />
<style name="Theme.ReactiveResume.NoActionBar">
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="Theme.ReactiveResume.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="Theme.ReactiveResume.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>

View File

@ -27,10 +27,12 @@ const Basics = () => {
<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="flex flex-col items-center gap-4 sm:col-span-2 sm:flex-row">
<PhotoUpload />
<div className="grid items-center gap-4 sm:col-span-2 sm:grid-cols-3">
<div className="mx-auto">
<PhotoUpload />
</div>
<div className="flex w-full flex-col-reverse gap-4 sm:flex-col sm:gap-2">
<div className="grid gap-2 w-full sm:col-span-2">
<ResumeInput label={t('builder.leftSidebar.sections.basics.name.label')} path="basics.name" />
<Button variant="outlined" startIcon={<PhotoFilter />} onClick={handleClick}>

View File

@ -66,6 +66,7 @@ const Settings = () => {
const code = value?.code || 'en';
document.cookie = `NEXT_LOCALE=${code}; path=/; expires=2147483647`;
dispatch(setResumeState({ path: 'metadata.locale', value: code }));
push({ pathname, query }, asPath, { locale: code });
};

View File

@ -1,4 +1,4 @@
import { Theme } from '@reactive-resume/schema';
import { Theme as ThemeType } from '@reactive-resume/schema';
import get from 'lodash/get';
import { useTranslation } from 'next-i18next';
@ -16,7 +16,7 @@ const Theme = () => {
const dispatch = useAppDispatch();
const { background, text, primary } = useAppSelector<Theme>((state) => get(state.resume, 'metadata.theme'));
const { background, text, primary } = useAppSelector<ThemeType>((state) => get(state.resume, 'metadata.theme'));
const handleChange = (property: string, color: string) => {
dispatch(setResumeState({ path: `metadata.theme.${property}`, value: color }));

View File

@ -1,8 +1,8 @@
import { Testimony } from '@/data/testimonials';
import { Testimony as TestimonyType } from '@/data/testimonials';
import styles from './Testimony.module.scss';
type Props = Testimony;
type Props = TestimonyType;
const Testimony: React.FC<Props> = ({ name, message }) => {
return (

View File

@ -1,7 +0,0 @@
.container {
@apply z-10 fixed top-0 left-0 right-0;
strong {
@apply font-semibold;
}
}

View File

@ -1,26 +0,0 @@
import { AnnouncementOutlined } from '@mui/icons-material';
import { Alert, Collapse } from '@mui/material';
import { useState } from 'react';
import { PRODUCT_HUNT_URL } from '@/constants/index';
import styles from './Announcement.module.scss';
const Announcement = () => {
const [open, setOpen] = useState(true);
return (
<div className={styles.container}>
<Collapse in={open}>
<Alert icon={<AnnouncementOutlined />} severity="info" onClose={() => setOpen(false)}>
<a href={PRODUCT_HUNT_URL} target="_blank" rel="noreferrer">
<strong>Reactive Resume is featured on Product Hunt.</strong> If you liked this app, please show your
support by <strong>upvoting</strong>!
</a>
</Alert>
</Collapse>
</div>
);
};
export default Announcement;

View File

@ -5,6 +5,8 @@ import { useTranslation } from 'next-i18next';
import { MouseEvent, useState } from 'react';
import { languages } from '@/config/languages';
import { useAppDispatch } from '@/store/hooks';
import { setResumeState } from '@/store/resume/resumeSlice';
import styles from './LanguageSwitcher.module.scss';
@ -13,6 +15,8 @@ const LanguageSwitcher = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const handleClick = (event: MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget);
@ -23,6 +27,8 @@ const LanguageSwitcher = () => {
const { pathname, asPath, query } = router;
document.cookie = `NEXT_LOCALE=${locale}; path=/; expires=2147483647`;
dispatch(setResumeState({ path: 'metadata.locale', value: locale }));
router.push({ pathname, query }, asPath, { locale });
};

View File

@ -5,10 +5,25 @@ export type Language = {
};
export const languages: Language[] = [
{
code: 'bn',
name: 'Bengali',
localName: 'বাংলা',
},
{
code: 'zh',
name: 'Chinese',
localName: '中文',
},
{
code: 'en',
name: 'English',
},
{
code: 'fr',
name: 'French',
localName: 'Français',
},
{
code: 'de',
name: 'German',
@ -19,6 +34,11 @@ export const languages: Language[] = [
name: 'Hindi',
localName: 'हिन्दी',
},
{
code: 'it',
name: 'Italian',
localName: 'Italiano',
},
{
code: 'kn',
name: 'Kannada',

View File

@ -3,7 +3,7 @@ export const FONTS_QUERY = 'fonts';
export const RESUMES_QUERY = 'resumes';
// Regular Expressions
export const VALID_URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
export const VALID_URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)/;
// Date Formats
export const FILENAME_TIMESTAMP = 'DDMMYYYYHHmmss';

View File

@ -29,7 +29,7 @@ const defaultState: FormData = {
const schema = Joi.object<FormData>().keys({
id: Joi.string(),
name: Joi.string().required(),
level: Joi.string().required(),
level: Joi.string().allow(''),
levelNum: Joi.number().min(0).max(10).required(),
keywords: Joi.array().items(Joi.string().optional()),
});
@ -109,7 +109,6 @@ const SkillModal: React.FC = () => {
control={control}
render={({ field, fieldState }) => (
<TextField
required
label={t('builder.common.form.level.label')}
error={!!fieldState.error}
helperText={fieldState.error?.message}
@ -125,7 +124,7 @@ const SkillModal: React.FC = () => {
<div className="col-span-2">
<h4 className="mb-3 font-semibold">{t('builder.common.form.levelNum.label')}</h4>
<div className="px-10">
<div className="px-3">
<Slider
{...field}
marks={[

View File

@ -24,6 +24,7 @@ const ImportExternalModal: React.FC = () => {
const linkedinInputRef = useRef<HTMLInputElement>(null);
const jsonResumeInputRef = useRef<HTMLInputElement>(null);
const reactiveResumeInputRef = useRef<HTMLInputElement>(null);
const reactiveResumeV2InputRef = useRef<HTMLInputElement>(null);
const { open: isOpen } = useAppSelector((state) => state.modal['dashboard.import-external']);
@ -49,6 +50,11 @@ const ImportExternalModal: React.FC = () => {
reactiveResumeInputRef.current.click();
reactiveResumeInputRef.current.value = '';
}
} else if (integration === 'reactive-resume-v2') {
if (reactiveResumeV2InputRef.current) {
reactiveResumeV2InputRef.current.click();
reactiveResumeV2InputRef.current.value = '';
}
}
};
@ -171,7 +177,7 @@ const ImportExternalModal: React.FC = () => {
<p className="mb-2">{t('modals.dashboard.import-external.reactive-resume.body')}</p>
<div>
<div className="flex gap-4">
<Button
variant="contained"
disabled={isLoading}
@ -181,6 +187,15 @@ const ImportExternalModal: React.FC = () => {
{t('modals.dashboard.import-external.reactive-resume.actions.upload-json')}
</Button>
<Button
variant="contained"
disabled={isLoading}
startIcon={<UploadFile />}
onClick={() => handleClick('reactive-resume-v2')}
>
{t('modals.dashboard.import-external.reactive-resume.actions.upload-json-v2')}
</Button>
<input
hidden
type="file"
@ -188,6 +203,14 @@ const ImportExternalModal: React.FC = () => {
onChange={(event) => handleChange(event, 'reactive-resume')}
accept="application/json"
/>
<input
hidden
type="file"
ref={reactiveResumeV2InputRef}
onChange={(event) => handleChange(event, 'reactive-resume-v2')}
accept="application/json"
/>
</div>
</div>
</BaseModal>

View File

@ -3,7 +3,7 @@ const path = require('path');
const i18nConfig = {
i18n: {
defaultLocale: 'en',
locales: ['de', 'en', 'es', 'kn', 'ta', 'hi'],
locales: ['bn', 'de', 'en', 'es', 'fr', 'hi', 'it', 'kn', 'ta', 'zh'],
},
nsSeparator: '.',
localePath: path.resolve('./public/locales'),

View File

@ -10,7 +10,7 @@
"dependencies": {
"@beam-australia/react-env": "^3.1.1",
"@emotion/css": "^11.7.1",
"@emotion/react": "^11.8.1",
"@emotion/react": "^11.8.2",
"@emotion/styled": "^11.8.1",
"@hookform/resolvers": "2.8.8",
"@monaco-editor/react": "^4.3.1",
@ -25,7 +25,7 @@
"joi": "^17.6.0",
"lodash": "^4.17.21",
"md5-hex": "^4.0.0",
"monaco-editor": "^0.32.1",
"monaco-editor": "^0.33.0",
"nanoid": "^3.3.1",
"next": "12.1.0",
"next-i18next": "^10.5.0",
@ -36,7 +36,7 @@
"react-dnd-html5-backend": "^15.1.2",
"react-dom": ">=17",
"react-google-login": "^5.2.2",
"react-hook-form": "^7.27.1",
"react-hook-form": "^7.28.0",
"react-hot-toast": "2.2.0",
"react-hotkeys-hook": "^3.4.4",
"react-icons": "^4.3.1",
@ -59,14 +59,14 @@
"@types/downloadjs": "^1.4.3",
"@types/lodash": "^4.14.179",
"@types/node": "17.0.21",
"@types/react": "17.0.39",
"@types/react": "17.0.40",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-redux": "^7.1.23",
"@types/tailwindcss": "^3.0.9",
"@types/uuid": "^8.3.4",
"@types/webfontloader": "^1.6.34",
"autoprefixer": "^10.4.2",
"eslint": "^8.10.0",
"eslint": "^8.11.0",
"eslint-config-next": "12.1.0",
"next-sitemap": "^2.5.7",
"postcss": "^8.4.8",

View File

@ -42,6 +42,7 @@ const Build: NextPage<Props> = ({ username, slug }) => {
`resume/${username}/${slug}`,
() => fetchResumeByIdentifier({ username, slug }),
{
cacheTime: 0,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,

View File

@ -3,6 +3,7 @@ import clsx from 'clsx';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import { GetServerSideProps, NextPage } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useEffect } from 'react';
import Page from '@/components/build/Center/Page';
@ -22,15 +23,19 @@ type Props = {
redirect?: any;
};
export const getServerSideProps: GetServerSideProps<Props | Promise<Props>, QueryParams> = async ({ query }) => {
export const getServerSideProps: GetServerSideProps<Props | Promise<Props>, QueryParams> = async ({
query,
locale,
}) => {
const { username, slug, secretKey } = query as QueryParams;
try {
if (isEmpty(secretKey)) throw new Error('There is no secret key!');
const resume = await fetchResumeByIdentifier({ username, slug, options: { secretKey } });
const displayLocale = resume.metadata.locale || locale || 'en';
return { props: { resume } };
return { props: { resume, ...(await serverSideTranslations(displayLocale, ['common'])) } };
} catch (error) {
return {
redirect: {

View File

@ -8,7 +8,6 @@ import { Trans, useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import Testimony from '@/components/landing/Testimony';
import Announcement from '@/components/shared/Announcement';
import Footer from '@/components/shared/Footer';
import LanguageSwitcher from '@/components/shared/LanguageSwitcher';
import Logo from '@/components/shared/Logo';
@ -45,8 +44,6 @@ const Home: NextPage = () => {
return (
<main className={styles.container}>
<Announcement />
<div className={styles.header}>
<div className={styles.logo}>
<Logo size={256} />

View File

@ -0,0 +1,358 @@
{
"common": {
"actions": {
"add": "নতুন {{token}} যোগ করুন",
"delete": "{{token}} মুছুন৷",
"edit": "{{token}} সম্পাদনা করুন"
},
"columns": {
"heading": "কলাম",
"tooltip": "কলামের সংখ্যা পরিবর্তন করুন"
},
"form": {
"date": {
"label": "তারিখ"
},
"description": {
"label": "বর্ণনা"
},
"email": {
"label": "ইমেইল ঠিকানা"
},
"end-date": {
"help-text": "এই ক্ষেত্রটি খালি রাখুন, যদি এখনও উপস্থিত থাকে",
"label": "শেষ তারিখ"
},
"keywords": {
"label": "কীওয়ার্ড"
},
"level": {
"label": "স্তর"
},
"levelNum": {
"label": "স্তর (সংখ্যা)"
},
"name": {
"label": "নাম"
},
"phone": {
"label": "ফোন নম্বর"
},
"position": {
"label": "অবস্থান"
},
"start-date": {
"label": "শুরুর তারিখ"
},
"subtitle": {
"label": "সাবটাইটেল"
},
"summary": {
"label": "সারসংক্ষেপ"
},
"title": {
"label": "শিরোনাম"
},
"url": {
"label": "ওয়েবসাইট"
}
},
"glossary": {
"page": "পাতা"
},
"list": {
"actions": {
"delete": "মুছুন",
"duplicate": "প্রতিলিপি",
"edit": "সম্পাদনা করুন"
},
"empty-text": "এই তালিকাটি খালি।"
},
"tooltip": {
"delete-item": "আপনি এই আইটেমটি মুছে ফেলতে চান আপনি কি নিশ্চিত? এটি একটি অপরিবর্তনীয় ক্রিয়া।",
"delete-section": "সেকশন মুছে ফেলুন",
"rename-section": "সেকশনের নাম পরিবর্তন করুন",
"toggle-visibility": "দৃশ্যমানতা টগল করুন"
}
},
"controller": {
"tooltip": {
"center-artboard": "কেন্দ্র আর্টবোর্ড",
"copy-link": "রিজিউমে লিঙ্ক কপি করুন",
"export-pdf": "পিডিএফ আকারে পাঠান",
"toggle-orientation": "পৃষ্ঠা ওরিয়েন্টেশন টগল করুন",
"toggle-page-break-line": "পৃষ্ঠা বিরতি লাইন টগল করুন",
"toggle-sidebars": "সাইডবার টগল করুন",
"zoom-in": "বড় কর",
"zoom-out": "ছোট করা"
}
},
"header": {
"menu": {
"delete": "মুছুন",
"duplicate": "প্রতিলিপি",
"rename": "নাম পরিবর্তন করুন",
"share-link": "লিঙ্ক শেয়ার করুন",
"tooltips": {
"delete": "আপনি কি এই জীবনবৃত্তান্ত মুছতে চান? এটি একটি অপরিবর্তনীয় ক্রিয়া।",
"share-link": "অন্যদের কাছে দৃশ্যমান করার জন্য আপনাকে আপনার জীবনবৃত্তান্তের দৃশ্যমানতা সর্বজনীনভাবে পরিবর্তন করতে হবে।"
}
}
},
"leftSidebar": {
"sections": {
"awards": {
"form": {
"awarder": {
"label": "পুরস্কারদাতা"
}
}
},
"basics": {
"actions": {
"photo-filters": "ফটো ফিল্টার"
},
"heading": "মৌলিক",
"headline": {
"label": "শিরোনাম"
},
"name": {
"label": "পূর্ণ নাম"
},
"photo-filters": {
"effects": {
"border": {
"label": "বর্ডার"
},
"grayscale": {
"label": "গ্রেস্কেল"
},
"heading": "প্রভাব"
},
"shape": {
"heading": "আকৃতি"
},
"size": {
"heading": "আকার (px এ)"
}
},
"photo-upload": {
"tooltip": {
"remove": "ছবি মুছে ফেলুন",
"upload": "ছবি আপলোড করুন"
}
}
},
"certifications": {
"form": {
"issuer": {
"label": "প্রদানকারী"
}
}
},
"education": {
"form": {
"area-study": {
"label": "শিক্ষার বিষয়বস্তু"
},
"courses": {
"label": "কোর্স"
},
"degree": {
"label": "ডিগ্রী"
},
"grade": {
"label": "গ্রেড"
},
"institution": {
"label": "প্রতিষ্ঠান"
}
}
},
"location": {
"address": {
"label": "ঠিকানা"
},
"city": {
"label": "শহর"
},
"country": {
"label": "দেশ"
},
"heading": "অবস্থান",
"postal-code": {
"label": "পোস্ট অফিসের নাম্বার"
},
"region": {
"label": "অঞ্চল"
}
},
"profiles": {
"form": {
"network": {
"label": "নেটওয়ার্ক"
},
"username": {
"label": "ইউজারনেম"
}
},
"heading": "প্রোফাইল",
"heading_one": "প্রোফাইল"
},
"publications": {
"form": {
"publisher": {
"label": "প্রকাশক"
}
}
},
"references": {
"form": {
"relationship": {
"label": "সম্পর্ক"
}
}
},
"section": {
"heading": "অনুচ্ছেদ"
},
"volunteer": {
"form": {
"organization": {
"label": "সংগঠন"
}
}
}
}
},
"rightSidebar": {
"sections": {
"css": {
"heading": "কাস্টম সিএসএস"
},
"export": {
"heading": "এক্সপোর্ট",
"json": {
"primary": "JSON",
"secondary": "আপনার জীবনবৃত্তান্তের একটি JSON সংস্করণ ডাউনলোড করুন যা পুনরায় প্রতিক্রিয়াশীল জীবনবৃত্তান্তে আমদানি করা যেতে পারে।"
},
"pdf": {
"loading": {
"primary": "পিডিএফ তৈরি করা হচ্ছে",
"secondary": "আপনার পিডিএফ তৈরি হওয়ার জন্য অনুগ্রহ করে অপেক্ষা করুন, এতে 15 সেকেন্ড পর্যন্ত সময় লাগতে পারে।"
},
"normal": {
"primary": "পিডিএফ",
"secondary": "আপনার জীবনবৃত্তান্তের একটি পিডিএফ ডাউনলোড করুন যা আপনি প্রিন্ট করে আপনার স্বপ্নের চাকরিতে পাঠাতে পারেন। এই ফাইলটি আরও সম্পাদনার জন্য আবার আমদানি করা যাবে না৷"
}
}
},
"layout": {
"heading": "লেআউট",
"tooltip": {
"reset-layout": "লেআউট রিসেট করুন"
}
},
"links": {
"bugs-features": {
"body": "জীবনবৃত্তান্ত তৈরি করা থেকে কিছু আপনাকে বাধা দিচ্ছে? অথবা আপনি যোগ করার জন্য একটি আশ্চর্যজনক ধারণা আছে? শুরু করতে GitHub-এ একটি সমস্যা উত্থাপন করুন।",
"button": "Github ইস্যু তালিকা",
"heading": "বাগ? বৈশিষ্ট্য অনুরোধ?"
},
"donate": {
"body": "আপনি যদি প্রতিক্রিয়াশীল জীবনবৃত্তান্ত ব্যবহার করতে পছন্দ করেন, অনুগ্রহ করে বিজ্ঞাপন ছাড়া এবং চিরতরে বিনামূল্যে অ্যাপটিকে চালু ও চালু রাখার জন্য যতটা সম্ভব দান করার কথা বিবেচনা করুন।",
"button": "আমাকে একটা কফি কিনে দাও",
"heading": "Reactive Resume -তে দান করুন"
},
"github": "সোর্স কোড",
"heading": "লিঙ্ক"
},
"settings": {
"global": {
"date": {
"primary": "তারিখ",
"secondary": "অ্যাপ জুড়ে ব্যবহার করার জন্য তারিখ বিন্যাস"
},
"heading": "বিশ্বব্যাপী",
"language": {
"primary": "ভাষা",
"secondary": "অ্যাপ জুড়ে ব্যবহার করার জন্য ভাষা প্রদর্শন করুন"
},
"theme": {
"primary": "থিম"
}
},
"heading": "সেটিংস",
"page": {
"break-line": {
"primary": "লাইন ভেঙ্গে ফেলুন",
"secondary": "একটি A4 পৃষ্ঠার উচ্চতা চিহ্নিত করতে সমস্ত পৃষ্ঠায় একটি লাইন দেখান৷"
},
"heading": "পাতা",
"orientation": {
"disabled": "শুধুমাত্র একটি পৃষ্ঠা থাকলে কোন প্রভাব নেই",
"primary": "ওরিয়েন্টেশন",
"secondary": "পৃষ্ঠাগুলি অনুভূমিকভাবে বা উল্লম্বভাবে প্রদর্শন করতে হবে কিনা"
}
},
"resume": {
"heading": "জীবনবৃত্তান্ত",
"reset": {
"primary": "সবকিছু রিসেট করুন",
"secondary": "অনেক ভুল করেছেন? সমস্ত পরিবর্তন পুনরায় সেট করতে এবং স্ক্র্যাচ থেকে শুরু করতে এখানে ক্লিক করুন। সতর্ক থাকুন, এই ক্রিয়াটি উল্টানো যাবে না।"
},
"sample": {
"primary": "নমুনা ডেটা লোড করুন",
"secondary": "কোথায় শুরু করবেন তা নিশ্চিত নন? একটি সম্পূর্ণ জীবনবৃত্তান্ত দেখতে কেমন তা দেখতে কিছু নমুনা ডেটা লোড করতে এখানে ক্লিক করুন৷"
}
}
},
"sharing": {
"heading": "শেয়ারিং",
"short-url": {
"label": "সংক্ষিপ্ত URL পছন্দ করুন"
},
"visibility": {
"subtitle": "লিঙ্ক সহ যে কাউকে আপনার জীবনবৃত্তান্ত দেখার অনুমতি দিন",
"title": "সর্বজনীন"
}
},
"templates": {
"heading": "টেমপ্লেট"
},
"theme": {
"form": {
"background": {
"label": "ব্যাকগ্রাউন্ড"
},
"primary": {
"label": "প্রাথমিক"
},
"text": {
"label": "লেখা"
}
},
"heading": "থিম"
},
"typography": {
"form": {
"font-family": {
"label": "ফন্ট পরিবার"
},
"font-size": {
"label": "অক্ষরের আকার"
}
},
"heading": "টাইপোগ্রাফি",
"widgets": {
"body": {
"label": "বডি"
},
"headings": {
"label": "শিরোনাম"
}
}
}
}
}
}

View File

@ -0,0 +1,32 @@
{
"avatar": {
"menu": {
"greeting": "হ্যালো",
"logout": "লগআউট"
}
},
"footer": {
"credit": "<1>অমরুথ পিল্লাই</1>-এর একটি প্যাশন প্রোজেক্ট",
"language": {
"missing": "আপনার ভাষা অনুপস্থিত?"
},
"license": "সম্প্রদায়ের দ্বারা, সম্প্রদায়ের জন্য।"
},
"markdown": {
"help-text": "এই বিভাগটি <1>মার্কডাউন</1> ফর্ম্যাটিং সমর্থন করে।"
},
"date": {
"present": "বর্তমান"
},
"subtitle": "একটি বিনামূল্যে এবং ওপেন সোর্স জীবনবৃত্তান্ত নির্মাতা।",
"title": "Reactive Resume",
"toast": {
"error": {
"upload-file-size": "অনুগ্রহ করে শুধুমাত্র 2 মেগাবাইটের নিচে ফাইল আপলোড করুন।",
"upload-photo-size": "অনুগ্রহ করে শুধুমাত্র 2 মেগাবাইটের নিচের ছবি আপলোড করুন, বিশেষত বর্গাকার।"
},
"success": {
"resume-link-copied": "আপনার জীবনবৃত্তান্তের একটি লিঙ্ক আপনার ক্লিপবোর্ডে অনুলিপি করা হয়েছে।"
}
}
}

View File

@ -0,0 +1,25 @@
{
"create-resume": {
"subtitle": "শুন্য থেকে শুরু করা",
"title": "নতুন জীবনবৃত্তান্ত তৈরি করুন"
},
"import-external": {
"subtitle": "LinkedIn, JSON জীবনবৃত্তান্ত, প্রতিক্রিয়াশীল জীবনবৃত্তান্ত",
"title": "বহিরাগত উত্স থেকে আমদানি"
},
"resume": {
"menu": {
"delete": "মুছুন",
"duplicate": "প্রতিলিপি",
"open": "খুলুন",
"rename": "নাম পরিবর্তন করুন",
"share-link": "লিঙ্ক শেয়ার করুন",
"tooltips": {
"delete": "আপনি কি এই জীবনবৃত্তান্ত মুছতে চান? এটি একটি অপরিবর্তনীয় ক্রিয়া।",
"share-link": "অন্যদের কাছে দৃশ্যমান করার জন্য আপনাকে আপনার জীবনবৃত্তান্তের দৃশ্যমানতা সর্বজনীনভাবে পরিবর্তন করতে হবে।"
}
},
"timestamp": "সর্বশেষ আপডেট করা হয়েছে {{timestamp}} আগে"
},
"title": "ড্যাশবোর্ড"
}

View File

@ -0,0 +1,41 @@
{
"actions": {
"app": "অ্যাপ্লিকেশন এ যান",
"login": "লগইন",
"logout": "লগআউট",
"register": "নিবন্ধন"
},
"features": {
"heading": "বৈশিষ্ট্যসমূহ",
"list": {
"ads": "বিজ্ঞাপনবিহীন",
"export": "JSON বা PDF ফরম্যাটে আপনার জীবনবৃত্তান্ত রপ্তানি করুন",
"free": "সব সময়ের জন্য ফ্রি",
"import": "LinkedIn, JSON Resume থেকে ডেটা আমদানি করুন",
"languages": "একাধিক ভাষায় অ্যাক্সেসযোগ্য",
"more": "এবং আরও অনেক উত্তেজনাপূর্ণ বৈশিষ্ট্য, <1>এটি সম্পর্কে সমস্ত এখানে পড়ুন</1>৷",
"tracking": "ব্যবাহারকারী ট্রেকারবিহীন"
}
},
"links": {
"heading": "লিঙ্ক",
"links": {
"donate": "দান করুন",
"github": "সোর্স কোড",
"privacy": "গোপনীয়তা নীতি",
"service": "সেবা পাবার শর্ত"
}
},
"screenshots": {
"heading": "স্ক্রিনশট"
},
"testimonials": {
"heading": "প্রশংসাপত্র",
"body": "ভাল বা খারাপ, আমি প্রতিক্রিয়াশীল জীবনবৃত্তান্ত সম্পর্কে আপনার মতামত এবং আপনার জন্য অভিজ্ঞতা কেমন হয়েছে তা জানতে চাই।<br/>এখানে বিশ্বজুড়ে ব্যবহারকারীদের পাঠানো কিছু বার্তা রয়েছে৷",
"contact": "আপনি <1>আমার ইমেল</1> বা <3>আমার ওয়েবসাইট</3>-এ যোগাযোগ ফর্মের মাধ্যমে আমার সাথে যোগাযোগ করতে পারেন।"
},
"summary": {
"body": "Reactive Resume হল একটি বিনামূল্যের এবং ওপেন সোর্স জীবনবৃত্তান্ত নির্মাতা যা আপনার জীবনবৃত্তান্ত তৈরি, আপডেট এবং শেয়ার করার জাগতিক কাজগুলিকে 1, 2, 3 এর মতো সহজ করে তুলতে তৈরি করা হয়েছে৷ এই অ্যাপটির মাধ্যমে, আপনি একাধিক জীবনবৃত্তান্ত তৈরি করতে পারেন, নিয়োগকারীদের বা বন্ধুদের সাথে শেয়ার করতে পারেন৷ একটি অনন্য লিঙ্কের মাধ্যমে এবং আপনার ডেটার অখণ্ডতা এবং গোপনীয়তা হারানো ছাড়াই বিনামূল্যে, কোনও বিজ্ঞাপন, কোনও ট্র্যাকিং ছাড়াই একটি পিডিএফ হিসাবে মুদ্রণ করুন।",
"heading": "সারসংক্ষেপ"
}
}

View File

@ -0,0 +1,135 @@
{
"auth": {
"forgot-password": {
"actions": {
"send-email": "পাসওয়ার্ড রিসেট ইমেল পাঠান"
},
"body": "আপনি যে অ্যাকাউন্টটি পুনরুদ্ধার করতে চান তার সাথে যুক্ত ইমেল ঠিকানাটি লিখুন।",
"form": {
"email": {
"label": "ইমেইল ঠিকানা"
}
},
"heading": "আপনি কি পাসওয়ার্ড ভুলে গেছেন?",
"help-text": "অ্যাকাউন্টটি বিদ্যমান থাকলে, আপনি আপনার পাসওয়ার্ড রিসেট করার জন্য একটি লিঙ্ক সহ একটি ইমেল পাবেন।"
},
"login": {
"actions": {
"login": "লগইন",
"google": "গুগল দিয়ে লগইন করুন"
},
"body": "লগইন এবং অ্যাক্সেস, পরিচালনা এবং আপনার জীবনবৃত্তান্ত শেয়ার করতে আপনার অ্যাকাউন্টের সাথে যুক্ত আপনার ব্যবহারকারীর নাম এবং পাসওয়ার্ড লিখুন।",
"form": {
"password": {
"label": "পাসওয়ার্ড"
},
"username": {
"help-text": "এছাড়াও আপনি আপনার ইমেল ঠিকানা লিখতে পারেন",
"label": "ইউজারনেম"
}
},
"heading": "আপনার অ্যাকাউন্টে লগ ইন করুন",
"recover-text": "আপনি যদি আপনার পাসওয়ার্ড ভুলে গিয়ে থাকেন, আপনি এখানে <1>আপনার অ্যাকাউন্ট পুনরুদ্ধার</1> করতে পারেন৷",
"register-text": "আপনার যদি একটি না থাকে, আপনি এখানে <1>একটি অ্যাকাউন্ট তৈরি করতে</1> পারেন৷"
},
"register": {
"actions": {
"register": "নিবন্ধন",
"google": "Google এর সাথে নিবন্ধন করুন"
},
"body": "একটি অ্যাকাউন্ট তৈরি করতে আপনার ব্যক্তিগত তথ্য লিখুন.",
"form": {
"confirm-password": {
"label": "পাসওয়ার্ড নিশ্চিত করুন"
},
"email": {
"label": "ইমেইল ঠিকানা"
},
"name": {
"label": "পূর্ণ নাম"
},
"password": {
"label": "পাসওয়ার্ড"
},
"username": {
"label": "ইউজারনেম"
}
},
"heading": "একটি অ্যাকাউন্ট তৈরি করুন",
"loginText": "আপনার যদি ইতিমধ্যেই একটি অ্যাকাউন্ট থাকে, আপনি <1>এখানে লগইন করতে পারেন</1>৷"
},
"reset-password": {
"actions": {
"set-password": "নতুন পাসওয়ার্ড সেট করুন"
},
"body": "আপনার অ্যাকাউন্টের জন্য একটি নতুন পাসওয়ার্ড লিখুন।",
"form": {
"confirm-password": {
"label": "পাসওয়ার্ড নিশ্চিত করুন"
},
"password": {
"label": "পাসওয়ার্ড"
}
},
"heading": "আপনার পাসওয়ার্ডটি রিসেট করুন"
}
},
"dashboard": {
"create-resume": {
"actions": {
"create-resume": "জীবনবৃত্তান্ত তৈরি করুন"
},
"body": "এটি একটি নাম দিয়ে আপনার জীবনবৃত্তান্ত নির্মাণ শুরু করুন. এটা হতে পারে আপনি যে ভূমিকার জন্য আবেদন করছেন বা আপনার প্রিয় খাবারের জন্য।",
"form": {
"name": {
"label": "নাম"
},
"public": {
"label": "সর্বজনীনভাবে অ্যাক্সেসযোগ্য?"
},
"slug": {
"label": "স্লাগ"
}
},
"heading": "একটি নতুন জীবনবৃত্তান্ত তৈরি করুন"
},
"import-external": {
"heading": "বহিরাগত উত্স থেকে আমদানি",
"json-resume": {
"actions": {
"upload-json": "JSON আপলোড করুন"
},
"body": "আপনার যদি একটি <1>প্রমাণিত JSON সারসংকলন</1> যাওয়ার জন্য প্রস্তুত থাকে, তাহলে আপনি এটি ব্যবহার করতে পারেন প্রতিক্রিয়াশীল জীবনবৃত্তান্তে আপনার বিকাশ দ্রুত-ট্র্যাক করতে। নীচের বোতামে ক্লিক করুন এবং শুরু করতে একটি বৈধ JSON ফাইল আপলোড করুন৷",
"heading": "JSON রিজিউম থেকে আমদানি করুন"
},
"linkedin": {
"actions": {
"upload-archive": "ZIP সংরক্ষণাগার আপলোড করুন"
},
"body": "আপনি LinkedIn থেকে আপনার ডেটা রপ্তানি করে এবং Reactive Resume-এ ক্ষেত্রগুলি স্বয়ংক্রিয়ভাবে পূরণ করতে ব্যবহার করে সময় বাঁচাতে পারেন। LinkedIn-এর <1>ডেটা গোপনীয়তা</1> বিভাগে যান এবং আপনার ডেটা সংরক্ষণের জন্য অনুরোধ করুন। একবার এটি উপলব্ধ হলে, নীচের জিপ ফাইলটি আপলোড করুন।",
"heading": "LinkedIn থেকে আমদানি করুন"
},
"reactive-resume": {
"actions": {
"upload-json": "JSON আপলোড করুন"
},
"body": "আপনার যদি একটি JSON থাকে যা রিঅ্যাকটিভ রিজিউমের বর্তমান সংস্করণের সাথে রপ্তানি করা হয়েছিল, আপনি আবার সম্পাদনাযোগ্য সংস্করণ পেতে এটিকে এখানে আবার আমদানি করতে পারেন। প্রতিক্রিয়াশীল জীবনবৃত্তান্তের পূর্ববর্তী সংস্করণগুলি দুর্ভাগ্যবশত এই মুহূর্তে সমর্থিত নয়৷",
"heading": "প্রতিক্রিয়াশীল জীবনবৃত্তান্ত থেকে আমদানি করুন"
}
},
"rename-resume": {
"actions": {
"rename-resume": "রিজিউমের নাম পরিবর্তন করুন"
},
"form": {
"name": {
"label": "নাম"
},
"slug": {
"label": "স্লাগ"
}
},
"heading": "আপনার জীবনবৃত্তান্তের নাম পরিবর্তন করুন"
}
}
}

View File

@ -10,13 +10,13 @@
"resume": {
"menu": {
"delete": "Löschen",
"duplicate": "Duplizieren",
"duplicate": "Duplikat",
"open": "Öffnen",
"rename": "Umbenennen",
"share-link": "Link teilen",
"share-link": "Einen Link teilen",
"tooltips": {
"delete": "Sind Sie sicher, dass Sie diesen Lebenslauf löschen möchten? Dies ist eine unumkehrbare Aktion.",
"share-link": "Du musst die Sichtbarkeit deines Lebenslaufs in die Öffentlichkeit ändern, um ihn für andere sichtbar zu machen."
"delete": "Möchten Sie diesen Lebenslauf wirklich löschen? Dies ist eine irreversible Aktion.",
"share-link": "Sie müssen die Sichtbarkeit Ihres Lebenslaufs auf öffentlich ändern, um ihn für andere sichtbar zu machen."
}
},
"timestamp": "Zuletzt vor {{timestamp}} aktualisiert"

View File

@ -2,7 +2,7 @@
"actions": {
"app": "Gehe zu App",
"login": "Anmelden",
"logout": "Abmelden",
"logout": "Ausloggen",
"register": "Registrieren"
},
"features": {
@ -18,7 +18,7 @@
}
},
"links": {
"heading": "Links",
"heading": "Verknüpfungen",
"links": {
"donate": "Spenden",
"github": "Quellcode",

View File

@ -7,7 +7,7 @@
"body": "Geben Sie einfach die E-Mail-Adresse ein, die mit dem Konto verknüpft ist, das Sie wiederherstellen möchten.",
"form": {
"email": {
"label": "E-Mail Adresse"
"label": "E-Mail-Addresse"
}
},
"heading": "Passwort vergessen?",
@ -15,7 +15,7 @@
},
"login": {
"actions": {
"login": "Anmelden",
"login": "Anmeldung",
"google": "Mit Google anmelden"
},
"body": "Bitte geben Sie Ihren Benutzernamen und Ihr Passwort ein, um sich anzumelden und zuzugreifen, Ihre Bewerbungen zu verwalten und weiterzugeben.",
@ -25,7 +25,7 @@
},
"username": {
"help-text": "Sie können auch Ihre E-Mail-Adresse eingeben",
"label": "Benutzername"
"label": "Nutzername"
}
},
"heading": "Bei Ihrem Konto anmelden",
@ -43,16 +43,16 @@
"label": "Passwort bestätigen"
},
"email": {
"label": "E-Mail Adresse"
"label": "E-Mail-Addresse"
},
"name": {
"label": "Voller Name"
"label": "Vollständiger Name"
},
"password": {
"label": "Passwort"
},
"username": {
"label": "Benutzername"
"label": "Nutzername"
}
},
"heading": "Ein Konto erstellen",
@ -94,7 +94,7 @@
"heading": "Neuen Lebenslauf erstellen"
},
"import-external": {
"heading": "Aus externen Quellen importieren",
"heading": "Import aus externen Quellen",
"json-resume": {
"actions": {
"upload-json": "JSON hochladen"

View File

@ -111,9 +111,10 @@
},
"reactive-resume": {
"actions": {
"upload-json": "Upload JSON"
"upload-json": "Upload JSON",
"upload-json-v2": "Upload JSON from v2"
},
"body": "If you have a JSON that was exported with the current version of Reactive Resume, you can import it back here to get an editable version again. Previous versions of Reactive Resume are unfortunately not supported at the moment.",
"body": "If you have a JSON that was exported with the current version of Reactive Resume, you can import it back here to get an editable version again.",
"heading": "Import From Reactive Resume"
}
},

View File

@ -16,7 +16,7 @@
"help-text": "Esta sección soporta el formato <1>markdown</1>."
},
"date": {
"present": "Regalo"
"present": "Actualidad"
},
"subtitle": "Un generador de currículum gratuito y de código abierto.",
"title": "Reactive Resume",

View File

@ -9,13 +9,13 @@
},
"resume": {
"menu": {
"delete": "Eliminar",
"delete": "Borrar",
"duplicate": "Duplicar",
"open": "Abrir",
"rename": "Renombrar",
"rename": "Rebautizar",
"share-link": "Compartir enlace",
"tooltips": {
"delete": "¿Estás seguro de que deseas eliminar este elemento? Esta acción es irreversible.",
"delete": "¿Está seguro de que desea eliminar este currículum? Esta es una acción irreversible.",
"share-link": "Debe cambiar la visibilidad de su currículum a público para que sea visible para los demás."
}
},

View File

@ -21,7 +21,7 @@
"heading": "Enlaces",
"links": {
"donate": "Donar",
"github": "Código Fuente",
"github": "Código fuente",
"privacy": "Política de Privacidad",
"service": "Términos de Servicio"
}

View File

@ -7,7 +7,7 @@
"body": "Simplemente ingrese la dirección de correo electrónico asociada con la cuenta que desea recuperar.",
"form": {
"email": {
"label": "Correo electrónico"
"label": "Dirección de correo electrónico"
}
},
"heading": "¿Olvidó su contraseña?",
@ -15,7 +15,7 @@
},
"login": {
"actions": {
"login": "Iniciar sesión",
"login": "Acceso",
"google": "Iniciar sesión con Google"
},
"body": "Por favor, introduzca su nombre de usuario y contraseña asociados a su cuenta para iniciar sesión y acceder, gestionar y compartir sus currículos.",
@ -25,7 +25,7 @@
},
"username": {
"help-text": "También puede ingresar su dirección de correo electrónico",
"label": "Nombre de usario"
"label": "Nombre de usuario"
}
},
"heading": "Ingrese en su cuenta",
@ -34,7 +34,7 @@
},
"register": {
"actions": {
"register": "Registrar",
"register": "Registrarse",
"google": "Regístrese con Google"
},
"body": "Por favor ingrese su información personal para crear una cuenta.",
@ -43,16 +43,16 @@
"label": "Confirmar Contraseña"
},
"email": {
"label": "Correo electrónico"
"label": "Dirección de correo electrónico"
},
"name": {
"label": "Nombre Completo"
"label": "Nombre completo"
},
"password": {
"label": "Contraseña"
"label": "Clave"
},
"username": {
"label": "Nombre de usario"
"label": "Nombre de usuario"
}
},
"heading": "Crear una cuenta",
@ -65,10 +65,10 @@
"body": "Introduzca una nueva contraseña para su cuenta.",
"form": {
"confirm-password": {
"label": "Confirmar Contraseña"
"label": "Confirmar contraseña"
},
"password": {
"label": "Contraseña"
"label": "Clave"
}
},
"heading": "Restablecer tu contraseña"
@ -126,7 +126,7 @@
"label": "Nombre"
},
"slug": {
"label": "Segmento"
"label": "Babosa"
}
},
"heading": "Renombrar tu currículum"

View File

@ -0,0 +1,358 @@
{
"common": {
"actions": {
"add": "Ajouter un/une {{token}}",
"delete": "Supprimer {{token}}",
"edit": "Éditer {{token}}"
},
"columns": {
"heading": "Colonnes",
"tooltip": "Modifier le nombre de colonnes"
},
"form": {
"date": {
"label": "Date"
},
"description": {
"label": "Description"
},
"email": {
"label": "Adresse email"
},
"end-date": {
"help-text": "Si actuel, laisser ce champ vide",
"label": "Date de fin"
},
"keywords": {
"label": "Mots-clés"
},
"level": {
"label": "Niveau"
},
"levelNum": {
"label": "Niveau (nombre)"
},
"name": {
"label": "Nom"
},
"phone": {
"label": "Numéro de téléphone"
},
"position": {
"label": "Poste"
},
"start-date": {
"label": "Date de début"
},
"subtitle": {
"label": "Sous-titre"
},
"summary": {
"label": "Résumé"
},
"title": {
"label": "Titre"
},
"url": {
"label": "Site web"
}
},
"glossary": {
"page": "Page"
},
"list": {
"actions": {
"delete": "Supprimer",
"duplicate": "Dédoubler",
"edit": "Éditer"
},
"empty-text": "Cette liste est vide."
},
"tooltip": {
"delete-item": "Êtes-vous sûr de vouloir supprimer cet objet ? Cette action est irréversible.",
"delete-section": "Supprimer la section",
"rename-section": "Renommer la section",
"toggle-visibility": "Rendre visible/invisible"
}
},
"controller": {
"tooltip": {
"center-artboard": "Centrer l'affichage",
"copy-link": "Copier le lien vers le CV",
"export-pdf": "Exporter en PDF",
"toggle-orientation": "Basculer l'orientation de la page",
"toggle-page-break-line": "Afficher/cacher l'indicateur de saut de page",
"toggle-sidebars": "Afficher/cacher les barres latérales",
"zoom-in": "Zoomer",
"zoom-out": "Dézoomer"
}
},
"header": {
"menu": {
"delete": "Supprimer",
"duplicate": "Dédoubler",
"rename": "Renommer",
"share-link": "Partager un lien",
"tooltips": {
"delete": "Êtes-vous sûr(e) de vouloir supprimer ce CV ? Cette action est irréversible.",
"share-link": "Vous devez changer la visibilité de votre CV en public pour le rendre visible aux autres."
}
}
},
"leftSidebar": {
"sections": {
"awards": {
"form": {
"awarder": {
"label": "Décerné par"
}
}
},
"basics": {
"actions": {
"photo-filters": "Filtres pour la photo"
},
"heading": "Informations de base",
"headline": {
"label": "Titre"
},
"name": {
"label": "Nom complet"
},
"photo-filters": {
"effects": {
"border": {
"label": "Bordure"
},
"grayscale": {
"label": "Noir et blanc"
},
"heading": "Effets"
},
"shape": {
"heading": "Forme"
},
"size": {
"heading": "Taille (en pixels)"
}
},
"photo-upload": {
"tooltip": {
"remove": "Supprimer la photo",
"upload": "Importer une photo"
}
}
},
"certifications": {
"form": {
"issuer": {
"label": "Certifié par"
}
}
},
"education": {
"form": {
"area-study": {
"label": "Domaine d'étude"
},
"courses": {
"label": "Cours"
},
"degree": {
"label": "Diplôme"
},
"grade": {
"label": "Note"
},
"institution": {
"label": "Établissement"
}
}
},
"location": {
"address": {
"label": "Adresse"
},
"city": {
"label": "Commune"
},
"country": {
"label": "Pays"
},
"heading": "Adresse",
"postal-code": {
"label": "Code postal"
},
"region": {
"label": "Région"
}
},
"profiles": {
"form": {
"network": {
"label": "Réseau social"
},
"username": {
"label": "Nom d'utilisateur"
}
},
"heading": "Profils",
"heading_one": "Profil"
},
"publications": {
"form": {
"publisher": {
"label": "Éditeur"
}
}
},
"references": {
"form": {
"relationship": {
"label": "Type de relation"
}
}
},
"section": {
"heading": "Section"
},
"volunteer": {
"form": {
"organization": {
"label": "Association"
}
}
}
}
},
"rightSidebar": {
"sections": {
"css": {
"heading": "CSS personnalisé"
},
"export": {
"heading": "Exporter",
"json": {
"primary": "JSON",
"secondary": "Télécharger une version JSON de votre CV, qui pourra être réimportée dans Reactive Resume."
},
"pdf": {
"loading": {
"primary": "Génération du PDF",
"secondary": "Veuillez patienter pendant que votre PDF est généré, cela peut prendre jusqu'à 15 secondes."
},
"normal": {
"primary": "PDF",
"secondary": "Téléchargez un PDF de votre CV que vous pourrez imprimer et envoyer à l'emploi de vos rêves. Ce fichier ne peut pas être réimporté pour être modifié ultérieurement."
}
}
},
"layout": {
"heading": "Disposition",
"tooltip": {
"reset-layout": "Réinitialiser la disposition"
}
},
"links": {
"bugs-features": {
"body": "Quelque chose vous a empêché de rédiger un CV ? Ou vous avez une idée géniale à ajouter ? Créez une issue sur GitHub pour commencer.",
"button": "Issues GitHub",
"heading": "Bogues ? Demandes de fonctionnalités ?"
},
"donate": {
"body": "Si vous avez aimé utiliser Reactive Resume, pensez à donner comme vous le pouvez pour que l'application reste disponible, gratuite et sans publicité pour toujours.",
"button": "Offrez-moi un café",
"heading": "Faire un don à Reactive Resume"
},
"github": "Code source",
"heading": "Liens"
},
"settings": {
"global": {
"date": {
"primary": "Date",
"secondary": "Format de date à utiliser dans l'application"
},
"heading": "Général",
"language": {
"primary": "Langue",
"secondary": "Langue d'affichage à utiliser dans l'application"
},
"theme": {
"primary": "Thème"
}
},
"heading": "Paramètres",
"page": {
"break-line": {
"primary": "Indicateur de changement de page",
"secondary": "Afficher une ligne sur toutes les pages pour marquer la hauteur d'une feuille A4"
},
"heading": "Page",
"orientation": {
"disabled": "N'a aucun effet lorsqu'il n'y a qu'une seule page",
"primary": "Orientation",
"secondary": "Afficher les pages horizontalement ou verticalement"
}
},
"resume": {
"heading": "CV",
"reset": {
"primary": "Tout remettre à zéro",
"secondary": "Vous avez fait trop d'erreurs ? Cliquez ici pour réinitialiser toutes les modifications et repartir de zéro. Attention, cette action ne peut pas être annulée."
},
"sample": {
"primary": "Charger les données de démo",
"secondary": "Vous ne savez pas par où commencer ? Cliquez ici pour charger quelques exemples de données afin de voir à quoi ressemble un CV complet."
}
}
},
"sharing": {
"heading": "Partage",
"short-url": {
"label": "Préférer une URL courte"
},
"visibility": {
"subtitle": "Permettre à toute personne ayant un lien de consulter votre CV",
"title": "Public"
}
},
"templates": {
"heading": "Modèles"
},
"theme": {
"form": {
"background": {
"label": "Couleur d'arrière plan"
},
"primary": {
"label": "Couleur principale"
},
"text": {
"label": "Couleur de texte"
}
},
"heading": "Thème"
},
"typography": {
"form": {
"font-family": {
"label": "Famille de police"
},
"font-size": {
"label": "Taille de la police"
}
},
"heading": "Typographie",
"widgets": {
"body": {
"label": "Contenu"
},
"headings": {
"label": "Titres"
}
}
}
}
}
}

View File

@ -0,0 +1,32 @@
{
"avatar": {
"menu": {
"greeting": "Bonjour",
"logout": "Déconnexion"
}
},
"footer": {
"credit": "Un projet passionné de <1>Amruth Pillai</1>",
"language": {
"missing": "Il manque votre langue ?"
},
"license": "Par la communauté, pour la communauté."
},
"markdown": {
"help-text": "Cette section prend en charge la mise en forme <1>markdown</1>."
},
"date": {
"present": "Présent"
},
"subtitle": "Un constructeur de CV gratuit et open source.",
"title": "Reactive Resume",
"toast": {
"error": {
"upload-file-size": "Veuillez télécharger uniquement des fichiers de moins de 2 Mo.",
"upload-photo-size": "Veuillez télécharger uniquement des photos de moins de 2 Mo, de préférence carrées."
},
"success": {
"resume-link-copied": "Un lien vers votre CV a été copié dans votre presse-papier."
}
}
}

View File

@ -0,0 +1,25 @@
{
"create-resume": {
"subtitle": "Partir de zéro",
"title": "Créer un nouveau CV"
},
"import-external": {
"subtitle": "LinkedIn, JSON Resume, Reactive Resume",
"title": "Importer de sources externes"
},
"resume": {
"menu": {
"delete": "Supprimer",
"duplicate": "Dédoubler",
"open": "Ouvrir",
"rename": "Renommer",
"share-link": "Partager un lien",
"tooltips": {
"delete": "Êtes-vous sûr(e) de vouloir supprimer ce CV ? Cette action est irréversible.",
"share-link": "Vous devez changer la visibilité de votre CV en public pour le rendre visible aux autres."
}
},
"timestamp": "Dernière mise à jour il y a {{timestamp}}"
},
"title": "Tableau de bord"
}

View File

@ -0,0 +1,41 @@
{
"actions": {
"app": "Accéder à l'app",
"login": "Connexion",
"logout": "Déconnexion",
"register": "Inscription"
},
"features": {
"heading": "Fonctionnalités",
"list": {
"ads": "Pas de publicité",
"export": "Exporter votre CV au format JSON ou PDF",
"free": "Gratuit, pour toujours",
"import": "Importer des données depuis LinkedIn, JSON Resume",
"languages": "Accessible en plusieurs langues",
"more": "Et bien plus de fonctionnalités intéressantes, <1>tout lire ici</1>",
"tracking": "Pas de pistage des utilisateurs"
}
},
"links": {
"heading": "Liens",
"links": {
"donate": "Faire un don",
"github": "Code source",
"privacy": "Politique de confidentialité",
"service": "Conditions d'utilisation"
}
},
"screenshots": {
"heading": "Captures d'écran"
},
"testimonials": {
"heading": "Témoignages",
"body": "Bonne ou mauvaiss, j'aimerais entendre votre opinion sur Reactive Resume et comment l'expérience a été pour vous.<br/>Voici quelques-uns des messages envoyés par des utilisateurs du monde entier.",
"contact": "Vous pouvez me contacter via <1>mon e-mail</1> ou via le formulaire de contact de <3>mon site web</3>."
},
"summary": {
"body": "Reactive Resume est un générateur de CV gratuit et open source, conçu pour rendre les tâches banales de création, de mise à jour et de partage de votre CV aussi faciles que de compter jusqu'à 3. Avec cette application, vous pouvez créer plusieurs CVs, les partager avec des recruteurs ou des amis par le biais d'un lien unique et les imprimer au format PDF, le tout gratuitement, sans publicité, sans pistage, et sans perdre l'intégrité et la confidentialité de vos données.",
"heading": "Résumé"
}
}

View File

@ -0,0 +1,135 @@
{
"auth": {
"forgot-password": {
"actions": {
"send-email": "Envoyer l'email de réinitialisation du mot de passe"
},
"body": "Il suffit de saisir l'adresse email associée au compte que vous souhaitez récupérer.",
"form": {
"email": {
"label": "Adresse e-mail"
}
},
"heading": "Mot de passe oublié ?",
"help-text": "Si le compte existe, vous recevrez un courriel contenant un lien pour réinitialiser votre mot de passe."
},
"login": {
"actions": {
"login": "Connexion",
"google": "Connexion avec Google"
},
"body": "Veuillez entrer votre nom d'utilisateur et votre mot de passe associés à votre compte pour vous connecter et accéder, gérer et partager vos CVs.",
"form": {
"password": {
"label": "Mot de passe"
},
"username": {
"help-text": "Vous pouvez également saisir votre adresse e-mail",
"label": "Nom d'utilisateur"
}
},
"heading": "Connexion à votre compte",
"recover-text": "Si vous avez oublié votre mot de passe, vous pouvez <1>récupérer votre compte</1> ici.",
"register-text": "Si vous n'en avez pas, vous pouvez <1>créer un compte</1> ici."
},
"register": {
"actions": {
"register": "Inscription",
"google": "Inscription avec Google"
},
"body": "Veuillez saisir vos informations personnelles pour créer un compte.",
"form": {
"confirm-password": {
"label": "Confirmez le mot de passe"
},
"email": {
"label": "Adresse e-mail"
},
"name": {
"label": "Nom complet"
},
"password": {
"label": "Mot de passe"
},
"username": {
"label": "Nom d'utilisateur"
}
},
"heading": "Créer un compte",
"loginText": "Si vous avez déjà un compte, vous pouvez <1>vous connecter ici</1>."
},
"reset-password": {
"actions": {
"set-password": "Définir un nouveau mot de passe"
},
"body": "Saisissez un nouveau mot de passe pour votre compte.",
"form": {
"confirm-password": {
"label": "Confirmez le mot de passe"
},
"password": {
"label": "Mot de passe"
}
},
"heading": "Réinitialiser votre mot de passe"
}
},
"dashboard": {
"create-resume": {
"actions": {
"create-resume": "Créer un CV"
},
"body": "Commencez à construire votre CV en lui donnant un nom. Il peut s'agir d'une référence au rôle auquel vous postulez ou simplement de votre repas préféré.",
"form": {
"name": {
"label": "Nom"
},
"public": {
"label": "Est-il accessible au public ?"
},
"slug": {
"label": "Alias"
}
},
"heading": "Créer un nouveau CV"
},
"import-external": {
"heading": "Importer de sources externes",
"json-resume": {
"actions": {
"upload-json": "Mettre en ligne un JSON"
},
"body": "Si vous avez un <1>JSON Resume valide</1> prêt à l'emploi, vous pouvez l'utiliser pour accélérer votre création sur Reactive Resume. Cliquez sur le bouton ci-dessous et mettez en ligne un fichier JSON valide pour commencer.",
"heading": "Importer depuis JSON Resume"
},
"linkedin": {
"actions": {
"upload-archive": "Mettre en ligne une archive ZIP"
},
"body": "Vous pouvez gagner du temps en exportant vos données depuis LinkedIn et en les utilisant pour remplir automatiquement les champs sur Reactive Resume. Rendez-vous dans la section <1>Confidentialité des données</1> sur LinkedIn et demandez une archive de vos données. Une fois disponible, chargez le fichier ZIP ci-dessous.",
"heading": "Import depuis LinkedIn"
},
"reactive-resume": {
"actions": {
"upload-json": "Mettre en ligne un JSON"
},
"body": "Si vous avez un JSON qui a été exporté avec la version actuelle de Reactive Resume, vous pouvez l'importer ici pour récupérer une version modifiable. Les versions précédentes de Reactive Resume ne sont malheureusement pas prises en charge pour le moment.",
"heading": "Importer depuis Reactive Resume"
}
},
"rename-resume": {
"actions": {
"rename-resume": "Renommer le CV"
},
"form": {
"name": {
"label": "Nom"
},
"slug": {
"label": "Alias"
}
},
"heading": "Renommer votre CV"
}
}
}

View File

@ -0,0 +1,358 @@
{
"common": {
"actions": {
"add": "Aggiungi {{token}}",
"delete": "Elimina {{token}}",
"edit": "Modifica {{token}}"
},
"columns": {
"heading": "Colonne",
"tooltip": "Modifica numero di colonne"
},
"form": {
"date": {
"label": "Data"
},
"description": {
"label": "Descrizione"
},
"email": {
"label": "Indirizzo e-mail"
},
"end-date": {
"help-text": "Se in corso, lascia questo campo vuoto",
"label": "Data di fine"
},
"keywords": {
"label": "Parole chiave"
},
"level": {
"label": "Livello"
},
"levelNum": {
"label": "Livello (numero)"
},
"name": {
"label": "Nome"
},
"phone": {
"label": "Numero di telefono"
},
"position": {
"label": "Posizione"
},
"start-date": {
"label": "Data di inizio"
},
"subtitle": {
"label": "Sottotitolo"
},
"summary": {
"label": "Profilo personale"
},
"title": {
"label": "Titolo"
},
"url": {
"label": "Sito web"
}
},
"glossary": {
"page": "Pagina"
},
"list": {
"actions": {
"delete": "Elimina",
"duplicate": "Duplica",
"edit": "Modifica"
},
"empty-text": "Questo elenco è vuoto."
},
"tooltip": {
"delete-item": "Vuoi davvero rimuovere questo elemento? Questa operazione è irreversibile.",
"delete-section": "Elimina sezione",
"rename-section": "Rinomina sezione",
"toggle-visibility": "Mostra/Nascondi"
}
},
"controller": {
"tooltip": {
"center-artboard": "Centra tavola da disegno",
"copy-link": "Copia link del curriculum",
"export-pdf": "Esporta in PDF",
"toggle-orientation": "Orientamento verticale/orizzontale",
"toggle-page-break-line": "Mostra/Nascondi linea interruzione pagina",
"toggle-sidebars": "Mostra/Nascondi barre laterali",
"zoom-in": "Ingrandisci",
"zoom-out": "Riduci"
}
},
"header": {
"menu": {
"delete": "Elimina",
"duplicate": "Duplica",
"rename": "Rinomina",
"share-link": "Condividi link",
"tooltips": {
"delete": "Vuoi davvero eliminare questo curriculum? Questa operazione è irreversibile.",
"share-link": "Per rendere il curriculum visibile agli altri, imposta la visibilità su \"Pubblico\"."
}
}
},
"leftSidebar": {
"sections": {
"awards": {
"form": {
"awarder": {
"label": "Conferito da"
}
}
},
"basics": {
"actions": {
"photo-filters": "Filtri per foto"
},
"heading": "Informazioni di base",
"headline": {
"label": "Intestazione"
},
"name": {
"label": "Nome e cognome"
},
"photo-filters": {
"effects": {
"border": {
"label": "Bordo"
},
"grayscale": {
"label": "Scala di grigi"
},
"heading": "Effetti"
},
"shape": {
"heading": "Forma"
},
"size": {
"heading": "Dimensioni (pixel)"
}
},
"photo-upload": {
"tooltip": {
"remove": "Rimuovi foto",
"upload": "Carica foto"
}
}
},
"certifications": {
"form": {
"issuer": {
"label": "Rilasciato da"
}
}
},
"education": {
"form": {
"area-study": {
"label": "Area di studio"
},
"courses": {
"label": "Corsi"
},
"degree": {
"label": "Laurea"
},
"grade": {
"label": "Voto"
},
"institution": {
"label": "Istituto"
}
}
},
"location": {
"address": {
"label": "Indirizzo"
},
"city": {
"label": "Città"
},
"country": {
"label": "Paese"
},
"heading": "Posizione",
"postal-code": {
"label": "CAP"
},
"region": {
"label": "Regione"
}
},
"profiles": {
"form": {
"network": {
"label": "Rete"
},
"username": {
"label": "Nome utente"
}
},
"heading": "Profili",
"heading_one": "Profilo"
},
"publications": {
"form": {
"publisher": {
"label": "Pubblicato da"
}
}
},
"references": {
"form": {
"relationship": {
"label": "Relazione"
}
}
},
"section": {
"heading": "Sezione"
},
"volunteer": {
"form": {
"organization": {
"label": "Organizzazione"
}
}
}
}
},
"rightSidebar": {
"sections": {
"css": {
"heading": "CSS personalizzato"
},
"export": {
"heading": "Esporta",
"json": {
"primary": "JSON",
"secondary": "Scarica una versione JSON del tuo curriculum che puoi importare nuovamente in Reactive Resume."
},
"pdf": {
"loading": {
"primary": "Generazione del PDF in corso...",
"secondary": "Attendi che la generazione del PDF sia completata, l'operazione potrebbe richiedere fino a 15 secondi."
},
"normal": {
"primary": "PDF",
"secondary": "Scarica un PDF del tuo curriculum che puoi stampare e usare per il lavoro dei tuoi sogni. Questo file non può essere importato nuovamente per ulteriori modifiche."
}
}
},
"layout": {
"heading": "Layout",
"tooltip": {
"reset-layout": "Ripristina layout"
}
},
"links": {
"bugs-features": {
"body": "Hai riscontrato un problema nella creazione del tuo curriculum? Hai un'idea fantastica da aggiungere? Usa gli Issue di GitHub per iniziare.",
"button": "Issue di GitHub",
"heading": "Bug? Richieste di funzionalità?"
},
"donate": {
"body": "Se Reactive Resume ti è piaciuto, ti invitiamo a contribuire con una donazione. In questo modo potremo mantenere l'applicazione attiva, gratuita e senza pubblicità.",
"button": "Offrimi un caffè",
"heading": "Donazioni a Reactive Resume"
},
"github": "Codice sorgente",
"heading": "Link"
},
"settings": {
"global": {
"date": {
"primary": "Data",
"secondary": "Formato della data da usare nell'app"
},
"heading": "Globale",
"language": {
"primary": "Lingua",
"secondary": "Lingua di visualizzazione dell'app"
},
"theme": {
"primary": "Tema"
}
},
"heading": "Impostazioni",
"page": {
"break-line": {
"primary": "Linea di interruzione",
"secondary": "Mostra su tutte le pagine una linea che indica l'altezza di un foglio A4"
},
"heading": "Pagina",
"orientation": {
"disabled": "Questa funzione non ha effetto se c'è solo una pagina",
"primary": "Orientamento",
"secondary": "Scegli se visualizzare le pagine in orizzontale o verticale"
}
},
"resume": {
"heading": "Curriculum",
"reset": {
"primary": "Ripristina tutto",
"secondary": "Hai fatto un po' troppi errori? Clicca qui per ripristinare tutte le modifiche e iniziare da zero. Questa operazione non è reversibile."
},
"sample": {
"primary": "Carica dati di esempio",
"secondary": "Non sai da dove cominciare? Clicca qui per caricare alcuni dati di esempio per vedere che aspetto ha un curriculum completo."
}
}
},
"sharing": {
"heading": "Condivisione",
"short-url": {
"label": "Preferisci URL breve"
},
"visibility": {
"subtitle": "Consenti a chiunque abbia il link di vedere il tuo curriculum",
"title": "Pubblico"
}
},
"templates": {
"heading": "Modelli"
},
"theme": {
"form": {
"background": {
"label": "Sfondo"
},
"primary": {
"label": "Principale"
},
"text": {
"label": "Testo"
}
},
"heading": "Tema"
},
"typography": {
"form": {
"font-family": {
"label": "Famiglia di caratteri"
},
"font-size": {
"label": "Dimensioni carattere"
}
},
"heading": "Tipografia",
"widgets": {
"body": {
"label": "Contenuto"
},
"headings": {
"label": "Intestazioni"
}
}
}
}
}
}

View File

@ -0,0 +1,32 @@
{
"avatar": {
"menu": {
"greeting": "Ciao",
"logout": "Esci"
}
},
"footer": {
"credit": "Un progetto nato dalla passione di <1>Amruth Pillai</1>",
"language": {
"missing": "Non trovi la tua lingua?"
},
"license": "Dalla gente, per la gente."
},
"markdown": {
"help-text": "Questa sezione supporta la formattazione <1>Markdown</1> ."
},
"date": {
"present": "In corso"
},
"subtitle": "Un'applicazione di creazione curriculum gratuita e open source.",
"title": "Reactive Resume",
"toast": {
"error": {
"upload-file-size": "Carica solo file inferiori a 2 MB.",
"upload-photo-size": "Carica solo foto inferiori a 2 MB, preferibilmente quadrate."
},
"success": {
"resume-link-copied": "Abbiamo copiato il link del tuo curriculum negli appunti."
}
}
}

View File

@ -0,0 +1,25 @@
{
"create-resume": {
"subtitle": "Inizia da zero",
"title": "Crea nuovo curriculum"
},
"import-external": {
"subtitle": "LinkedIn, JSON Resume, Reactive Resume",
"title": "Importa da fonti esterne"
},
"resume": {
"menu": {
"delete": "Elimina",
"duplicate": "Duplica",
"open": "Apri",
"rename": "Rinomina",
"share-link": "Condividi il link",
"tooltips": {
"delete": "Vuoi davvero eliminare questo curriculum? Questa operazione è irreversibile.",
"share-link": "Per rendere il curriculum visibile agli altri, imposta la visibilità su \"Pubblico\"."
}
},
"timestamp": "Ultimo aggiornamento {{timestamp}} fa"
},
"title": "Dashboard"
}

View File

@ -0,0 +1,41 @@
{
"actions": {
"app": "Vai all'app",
"login": "Accedi",
"logout": "Esci",
"register": "Registrati"
},
"features": {
"heading": "Funzioni",
"list": {
"ads": "Nessuna pubblicità",
"export": "Esporta il tuo curriculum in formato JSON o PDF",
"free": "Gratuita, per sempre",
"import": "Importa dati da LinkedIn, JSON Resume",
"languages": "Accessibile in varie lingue",
"more": "E tante altre funzioni interessanti, <1>scopri di più qui</1>",
"tracking": "Senza rilevamento delle azioni dell'utente"
}
},
"links": {
"heading": "Collegamenti",
"links": {
"donate": "Dona",
"github": "Codice sorgente",
"privacy": "Informativa sulla privacy",
"service": "Condizioni d'uso"
}
},
"screenshots": {
"heading": "Screenshot"
},
"testimonials": {
"heading": "Cosa dicono i nostri utenti",
"body": "Che la tua opinione sia positiva o negativa, mi piacerebbe sapere cosa ne pensi di Reactive Resume.<br/>Ecco alcuni dei messaggi che abbiamo ricevuto dai nostri utenti in tutto il mondo.",
"contact": "Puoi contattarmi al <1>mio indirizzo e-mail</1> oppure usando l'apposito modulo sul <3>mio sito web</3>."
},
"summary": {
"body": "Reactive Resume è un'applicazione di creazione di curriculum gratuita e open source, ideata per semplificare le attività di creazione, aggiornamento e condivisione del tuo curriculum. Con questa applicazione, è possibile creare più curriculum, condividerli con recruiter o amici attraverso un link unico e stamparli in formato PDF, il tutto gratuitamente, senza pubblicità, senza tracciamento e soprattutto preservando l'integrità e la privacy dei tuoi dati.",
"heading": "Profilo personale"
}
}

View File

@ -0,0 +1,135 @@
{
"auth": {
"forgot-password": {
"actions": {
"send-email": "Invia e-mail per reimpostare la password"
},
"body": "Inserisci l'indirizzo e-mail associato all'account da recuperare.",
"form": {
"email": {
"label": "Indirizzo e-mail"
}
},
"heading": "Hai dimenticato la password?",
"help-text": "Se l'account esiste, riceverai un'e-mail con un link per reimpostare la password."
},
"login": {
"actions": {
"login": "Accedi",
"google": "Accedi con Google"
},
"body": "Per accedere e iniziare a lavorare sui tuoi curriculum, inserisci il nome utente e la password associati al tuo account.",
"form": {
"password": {
"label": "Password"
},
"username": {
"help-text": "Puoi anche inserire il tuo indirizzo e-mail",
"label": "Nome utente"
}
},
"heading": "Accedi al tuo account",
"recover-text": "Se hai dimenticato la password, puoi recuperare il tuo account <1>qui</1>.",
"register-text": "Se non hai ancora un account, puoi crearne uno <1>qui</1>."
},
"register": {
"actions": {
"register": "Registrati",
"google": "Registrati con Google"
},
"body": "Inserisci i tuoi dati per creare un account.",
"form": {
"confirm-password": {
"label": "Conferma password"
},
"email": {
"label": "Indirizzo e-mail"
},
"name": {
"label": "Nome e cognome"
},
"password": {
"label": "Password"
},
"username": {
"label": "Nome utente"
}
},
"heading": "Crea un account",
"loginText": "Se hai già un account, puoi accedere <1>qui</1>."
},
"reset-password": {
"actions": {
"set-password": "Imposta nuova password"
},
"body": "Inserisci una nuova password per l'account.",
"form": {
"confirm-password": {
"label": "Conferma password"
},
"password": {
"label": "Password"
}
},
"heading": "Reimposta password"
}
},
"dashboard": {
"create-resume": {
"actions": {
"create-resume": "Crea curriculum"
},
"body": "Inizia a creare un curriculum inserendo un nome, in riferimento alla posizione per cui vuoi candidarti o semplicemente il tuo snack preferito.",
"form": {
"name": {
"label": "Nome"
},
"public": {
"label": "È ad accesso pubblico?"
},
"slug": {
"label": "Slug"
}
},
"heading": "Crea un nuovo curriculum"
},
"import-external": {
"heading": "Importa da fonti esterne",
"json-resume": {
"actions": {
"upload-json": "Carica JSON"
},
"body": "Se hai un <1>JSON Resume valido</1> già pronto, puoi usarlo per velocizzare il lavoro su Reactive Resume. Usa il pulsante qui sotto e carica un file JSON valido per iniziare.",
"heading": "Importa da JSON Resume"
},
"linkedin": {
"actions": {
"upload-archive": "Carica archivio ZIP"
},
"body": "Puoi velocizzare il tuo lavoro esportando i tuoi dati da LinkedIn e usandoli per riempire automaticamente i campi su Reactive Resume. Vai alla sezione <1>Privacy dei dati</1> di LinkedIn, richiedi una copia dei tuoi dati e carica il file ZIP qui sotto.",
"heading": "Importa da LinkedIn"
},
"reactive-resume": {
"actions": {
"upload-json": "Carica JSON"
},
"body": "Se hai un file JSON esportato con la versione attuale di Reactive Resume, puoi importarlo nuovamente qui e continuare ad apportare modifiche. Purtroppo le versioni precedenti di Reactive Resume non sono supportate al momento.",
"heading": "Importa da Reactive Resume"
}
},
"rename-resume": {
"actions": {
"rename-resume": "Rinomina curriculum"
},
"form": {
"name": {
"label": "Nome"
},
"slug": {
"label": "Slug"
}
},
"heading": "Rinomina il tuo curriculum"
}
}
}

View File

@ -0,0 +1,32 @@
{
"avatar": {
"menu": {
"greeting": "Xin chào",
"logout": "Đăng xuất"
}
},
"footer": {
"credit": "Một dự án làm với đam mê của <1> Amruth Pillai</1>",
"language": {
"missing": "Không có ngôn ngữ của bạn?"
},
"license": "Vì cộng đồng, cho cộng đồng."
},
"markdown": {
"help-text": "Phần này hỗ trợ định dạng <1> markdown </1>."
},
"date": {
"present": "Hiện tại"
},
"subtitle": "Một trình tạo sơ yếu lý lịch mã nguồn mở và miễn phí.",
"title": "Reactive Resume",
"toast": {
"error": {
"upload-file-size": "Vui lòng chỉ tải lên các tệp dưới 2 megabyte.",
"upload-photo-size": "Vui lòng chỉ tải lên ảnh dưới 2 megabyte, ưu tiên hình vuông."
},
"success": {
"resume-link-copied": "Một liên kết đến sơ yếu lý lịch của bạn đã được sao chép vào khay nhớ tạm của bạn."
}
}
}

View File

@ -0,0 +1,358 @@
{
"common": {
"actions": {
"add": "添加新的 {{token}}",
"delete": "删除 {{token}}",
"edit": "编辑 {{token}}"
},
"columns": {
"heading": "分栏",
"tooltip": "更改栏数"
},
"form": {
"date": {
"label": "日期"
},
"description": {
"label": "描述"
},
"email": {
"label": "Email 地址"
},
"end-date": {
"help-text": "如果仍然存在,请将此栏留空",
"label": "结束日期"
},
"keywords": {
"label": "关键字"
},
"level": {
"label": "等级"
},
"levelNum": {
"label": "等级 (数字)"
},
"name": {
"label": "名称"
},
"phone": {
"label": "电话号码"
},
"position": {
"label": "职务"
},
"start-date": {
"label": "开始日期"
},
"subtitle": {
"label": "副标题"
},
"summary": {
"label": "概要"
},
"title": {
"label": "标题"
},
"url": {
"label": "网站"
}
},
"glossary": {
"page": "页"
},
"list": {
"actions": {
"delete": "删除",
"duplicate": "创建副本",
"edit": "编辑"
},
"empty-text": "列表为空"
},
"tooltip": {
"delete-item": "您确定要删除此条数据吗? 此操作无法撤消。",
"delete-section": "删除章节",
"rename-section": "重命名章节",
"toggle-visibility": "切换显示/隐藏"
}
},
"controller": {
"tooltip": {
"center-artboard": "中心画板",
"copy-link": "复制链接地址",
"export-pdf": "输出为PDF",
"toggle-orientation": "切换页面方向",
"toggle-page-break-line": "切换分页线",
"toggle-sidebars": "显示/隐藏侧边栏",
"zoom-in": "放大",
"zoom-out": "缩小"
}
},
"header": {
"menu": {
"delete": "删除",
"duplicate": "创建副本",
"rename": "重命名",
"share-link": "分享链接",
"tooltips": {
"delete": "您确定要删除该简历吗?该操作不可撤销。",
"share-link": "您需要将简历的可见性更改为公开,以使其对其他人可见。"
}
}
},
"leftSidebar": {
"sections": {
"awards": {
"form": {
"awarder": {
"label": "获奖纪录"
}
}
},
"basics": {
"actions": {
"photo-filters": "照片滤镜"
},
"heading": "概况",
"headline": {
"label": "标题"
},
"name": {
"label": "全名"
},
"photo-filters": {
"effects": {
"border": {
"label": "边框"
},
"grayscale": {
"label": "灰度"
},
"heading": "特效"
},
"shape": {
"heading": "形状"
},
"size": {
"heading": "尺寸单位Px"
}
},
"photo-upload": {
"tooltip": {
"remove": "移除照片",
"upload": "上传照片"
}
}
},
"certifications": {
"form": {
"issuer": {
"label": "颁布方"
}
}
},
"education": {
"form": {
"area-study": {
"label": "专业"
},
"courses": {
"label": "课程"
},
"degree": {
"label": "学位"
},
"grade": {
"label": "年级"
},
"institution": {
"label": "机构"
}
}
},
"location": {
"address": {
"label": "地址"
},
"city": {
"label": "城市"
},
"country": {
"label": "国家"
},
"heading": "地点",
"postal-code": {
"label": "邮编"
},
"region": {
"label": "地区"
}
},
"profiles": {
"form": {
"network": {
"label": "网络"
},
"username": {
"label": "用户名"
}
},
"heading": "简介",
"heading_one": "简介"
},
"publications": {
"form": {
"publisher": {
"label": "发行机构"
}
}
},
"references": {
"form": {
"relationship": {
"label": "关系"
}
}
},
"section": {
"heading": "分区"
},
"volunteer": {
"form": {
"organization": {
"label": "组织"
}
}
}
}
},
"rightSidebar": {
"sections": {
"css": {
"heading": "自定义 CSS"
},
"export": {
"heading": "导出",
"json": {
"primary": "JSON",
"secondary": "下载一个JSON版本的简历可以导入到Reactive Resume中。"
},
"pdf": {
"loading": {
"primary": "正在生成 PDF",
"secondary": "请等待您的 PDF 生成,这可能需要 15 秒。"
},
"normal": {
"primary": "PDF",
"secondary": "下载您的简历 PDF您可以将其打印并发送给您期待的职位。此文件无法重新导入以进行进一步编辑。"
}
}
},
"layout": {
"heading": "布局",
"tooltip": {
"reset-layout": "重置布局"
}
},
"links": {
"bugs-features": {
"body": "是什么阻止了你制作简历?或者你有什么好主意要补充?在 GitHub 上提出问题以开始使用。",
"button": "GitHub Issues",
"heading": "发现了Bug? 想要新的功能?"
},
"donate": {
"body": "如果你喜欢使用Reactive Resume 请考虑捐款以便保持应用正常运行,没有广告和永远免费。",
"button": "送我一杯咖啡",
"heading": "捐赠给 Reactive Resume"
},
"github": "源码",
"heading": "链接"
},
"settings": {
"global": {
"date": {
"primary": "日期",
"secondary": "整个应用程序使用的日期格式"
},
"heading": "全局",
"language": {
"primary": "语言",
"secondary": "显示要在整个应用程序中使用的语言"
},
"theme": {
"primary": "主题"
}
},
"heading": "设置",
"page": {
"break-line": {
"primary": "换行",
"secondary": "在所有页面上显示一条线以标记 A4 页面的高度"
},
"heading": "页面",
"orientation": {
"disabled": "当只有一个页面时没有效果",
"primary": "方向",
"secondary": "水平或者垂直翻转图像"
}
},
"resume": {
"heading": "简历",
"reset": {
"primary": "全部重置",
"secondary": "犯了太多错误?单击此处重置所有更改并从头开始。小心,这个动作不能逆转。"
},
"sample": {
"primary": "加载样本数据",
"secondary": "不知道从哪里开始?单击此处加载一些示例数据,以查看完整简历的外观。"
}
}
},
"sharing": {
"heading": "分享",
"short-url": {
"label": "倾向于短网址"
},
"visibility": {
"subtitle": "允许任何有链接的人查看你的简历",
"title": "公开"
}
},
"templates": {
"heading": "模板"
},
"theme": {
"form": {
"background": {
"label": "背景"
},
"primary": {
"label": "主要"
},
"text": {
"label": "文本"
}
},
"heading": "主题"
},
"typography": {
"form": {
"font-family": {
"label": "字体"
},
"font-size": {
"label": "字号"
}
},
"heading": "排版",
"widgets": {
"body": {
"label": "正文内容"
},
"headings": {
"label": "标题"
}
}
}
}
}
}

View File

@ -0,0 +1,32 @@
{
"avatar": {
"menu": {
"greeting": "您好",
"logout": "退出"
}
},
"footer": {
"credit": "一个由<1>Amruth Pillai</1>开发的项目",
"language": {
"missing": "缺少您的语言?"
},
"license": "由社区驱动。"
},
"markdown": {
"help-text": "本节支持<1>markdown</1>格式化。"
},
"date": {
"present": "至今"
},
"subtitle": "一个免费开源的简历生成器。",
"title": "Reactive Resume",
"toast": {
"error": {
"upload-file-size": "请上传 2M 以下的文件。",
"upload-photo-size": "请上传 2MB 以下的照片,最好是正方形的。"
},
"success": {
"resume-link-copied": "源码的链接已复制到你的剪贴板。"
}
}
}

View File

@ -0,0 +1,25 @@
{
"create-resume": {
"subtitle": "从头创作",
"title": "创建简历"
},
"import-external": {
"subtitle": "LinkedIn, JSON 简历, Reactive Resume",
"title": "从外部导入"
},
"resume": {
"menu": {
"delete": "删除",
"duplicate": "创建副本",
"open": "打开",
"rename": "重命名",
"share-link": "分享链接",
"tooltips": {
"delete": "您确定要删除该简历吗?该操作不可撤销。",
"share-link": "您需要将简历的可见性更改为公开,以使其对其他人可见。"
}
},
"timestamp": "最近更新于 {{timestamp}}"
},
"title": "仪表盘"
}

View File

@ -0,0 +1,41 @@
{
"actions": {
"app": "前往应用",
"login": "登录",
"logout": "注销",
"register": "注册"
},
"features": {
"heading": "功能介绍",
"list": {
"ads": "无广告",
"export": "将您的简历导出为 JSON 或 PDF 格式",
"free": "永久免费",
"import": "从 LinkedIn JSON 简历导入数据",
"languages": "支持多种语言",
"more": "还有更多令人兴奋的功能,<1>在此处阅读所有相关信息</1>",
"tracking": "没有用户跟踪"
}
},
"links": {
"heading": "链接",
"links": {
"donate": "捐赠",
"github": "源码",
"privacy": "隐私政策",
"service": "服务条款"
}
},
"screenshots": {
"heading": "截屏"
},
"testimonials": {
"heading": "用户评价",
"body": "无论好坏,我很想听听您对 Reactive Resume 的看法以及您的体验如何。<br/>以下是世界各地用户发送的一些消息。",
"contact": "你可以通过<1>我的电子邮件</1>或通过<3>我的网站</3>上的联系表格与我联系。"
},
"summary": {
"body": "Reactive Resume 是一个免费和开源的简历生成器,旨在使创建、更新和共享简历的日常任务变得像 1、2、3 一样简单。使用此应用程序,您可以创建多份简历,与招聘人员或朋友分享通过一个独特的链接并将其打印为 PDF全部免费无广告无跟踪不会丢失您数据的完整性和隐私性。",
"heading": "概要"
}
}

View File

@ -0,0 +1,135 @@
{
"auth": {
"forgot-password": {
"actions": {
"send-email": "发送重置密码电子邮件"
},
"body": "只需输入与您要恢复的帐户关联的电子邮件地址。",
"form": {
"email": {
"label": "Email 地址"
}
},
"heading": "找回密码",
"help-text": "请输入您的电子邮件地址。 您将收到一封电子邮件的链接重置您的密码。"
},
"login": {
"actions": {
"login": "登录",
"google": "使用 Google 帐号登录"
},
"body": "请输入与您的帐户关联的用户名和密码,以登录和访问、管理和分享您的简历。",
"form": {
"password": {
"label": "密码"
},
"username": {
"help-text": "请输入您的电子邮箱地址",
"label": "用户名"
}
},
"heading": "登录您的账号",
"recover-text": "如果您忘记了密码,您可以<1>恢复您的帐户</1>这里。",
"register-text": "如果您没有,您可以<1>创建一个帐户</1>这里。"
},
"register": {
"actions": {
"register": "注册",
"google": "用Google注册"
},
"body": "输入您的邮箱地址并创建一个帐户",
"form": {
"confirm-password": {
"label": "确认密码"
},
"email": {
"label": "邮箱"
},
"name": {
"label": "全名"
},
"password": {
"label": "密码"
},
"username": {
"label": "用户名"
}
},
"heading": "创建一个帐户",
"loginText": "如果您已经有一个帐户,您可以<1>在此处登录</1>."
},
"reset-password": {
"actions": {
"set-password": "设置新的密码"
},
"body": "为账户设置新密码",
"form": {
"confirm-password": {
"label": "确认密码"
},
"password": {
"label": "密码"
}
},
"heading": "重置密码"
}
},
"dashboard": {
"create-resume": {
"actions": {
"create-resume": "创建简历"
},
"body": "开始建立你的简历时,要给它起个名字。它可以是指你所申请的角色,或者只是你最喜欢的零食。",
"form": {
"name": {
"label": "名称"
},
"public": {
"label": "是否可以公开访问?"
},
"slug": {
"label": "缩写"
}
},
"heading": "创建简历"
},
"import-external": {
"heading": "从外部导入",
"json-resume": {
"actions": {
"upload-json": "上传 JSON"
},
"body": "如果您有 <1> 已验证的 JSON 简历</1>准备好了,您可以使用它来快速跟踪您在 Reactive Resume 上的开发。单击下面的按钮并上传有效的 JSON 文件以开始。",
"heading": "从 JSON 简历导入"
},
"linkedin": {
"actions": {
"upload-archive": "上传ZIP压缩包"
},
"body": "您可以通过从 LinkedIn 导出数据并使用它来自动填写 Reactive Resume 上的字段来节省时间。前往<1>数据隐私</1>LinkedIn 上的部分并请求存档您的数据。一旦可用,请上传下面的 ZIP 文件。",
"heading": "从 LinkedIn 导入"
},
"reactive-resume": {
"actions": {
"upload-json": "上传 JSON"
},
"body": "如果您有一个使用当前版本的 Reactive Resume 导出的 JSON您可以在此处将其导入回来以再次获得可编辑的版本。遗憾的是目前不支持以前版本的 Reactive Resume。",
"heading": "从 Reactive Resume 导入"
}
},
"rename-resume": {
"actions": {
"rename-resume": "重命名简历"
},
"form": {
"name": {
"label": "名称"
},
"slug": {
"label": "缩写"
}
},
"heading": "重命名你的简历"
}
}
}

View File

@ -1,15 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url><loc>https://rxresu.me</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/meta/privacy</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/meta/service</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/de/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/kn/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/ta/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/hi/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/de</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/kn</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/ta</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me/hi</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-12T10:44:13.581Z</lastmod></url>
<url><loc>https://rxresu.me</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/meta/privacy</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/meta/service</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/bn/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/de/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/es/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/fr/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/hi/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/it/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/kn/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/ta/dashboard</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/bn</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/de</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/es</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/fr</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/hi</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/it</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/kn</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
<url><loc>https://rxresu.me/ta</loc><changefreq>monthly</changefreq><priority>0.7</priority><lastmod>2022-03-14T08:00:09.072Z</lastmod></url>
</urlset>

View File

@ -14,7 +14,7 @@ import {
setResumeState,
} from '../resume/resumeSlice';
const DEBOUNCE_WAIT = 2500;
const DEBOUNCE_WAIT = 1000;
const debouncedSync = debounce((resume: Resume) => updateResume(resume), DEBOUNCE_WAIT);

View File

@ -1,5 +1,5 @@
import { Email, Link, Phone } from '@mui/icons-material';
import { ListItem, Section } from '@reactive-resume/schema';
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import isEmpty from 'lodash/isEmpty';
@ -9,7 +9,7 @@ import { useAppSelector } from '@/store/hooks';
import { SectionProps } from '@/templates/sectionMap';
import DataDisplay from '@/templates/shared/DataDisplay';
import { formatDateString } from '@/utils/date';
import { parseListItemPath } from '@/utils/template';
import { addHttp, parseListItemPath } from '@/utils/template';
import Heading from './Heading';
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
headlinePath = 'headline',
keywordsPath = 'keywords',
}) => {
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
if (!section.visible) return null;
@ -86,7 +86,7 @@ const Section: React.FC<SectionProps> = ({
{summary && <Markdown>{summary}</Markdown>}
{url && (
<DataDisplay icon={<Link />} link={url}>
<DataDisplay icon={<Link />} link={addHttp(url)}>
{url}
</DataDisplay>
)}

View File

@ -1,5 +1,7 @@
import { css } from '@emotion/css';
import { alpha } from '@mui/material';
import { Theme } from '@reactive-resume/schema';
import clsx from 'clsx';
import get from 'lodash/get';
import { useMemo } from 'react';
@ -19,12 +21,16 @@ const Gengar: React.FC<PageProps> = ({ page }) => {
const theme: Theme = useAppSelector((state) => get(state.resume, 'metadata.theme', {}));
const contrast = useMemo(() => getContrastColor(theme.primary), [theme.primary]);
const backgroundColor: string = useMemo(() => alpha(theme.primary, 0.15), [theme.primary]);
const color = useMemo(() => (contrast === 'dark' ? theme.text : theme.background), [theme, contrast]);
return (
<div className={styles.page}>
<div className={styles.container}>
<div className={styles.sidebar}>
<div style={{ color: contrast === 'dark' ? theme.text : theme.background, backgroundColor: theme.primary }}>
<div
className={clsx(css(`svg { color: ${color} } --primary-color: ${color}`))}
style={{ color: contrast === 'dark' ? theme.text : theme.background, backgroundColor: theme.primary }}
>
{isFirstPage && <MastheadSidebar />}
</div>

View File

@ -1,5 +1,5 @@
import { Email, Link, Phone } from '@mui/icons-material';
import { ListItem, Section } from '@reactive-resume/schema';
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import isEmpty from 'lodash/isEmpty';
@ -9,7 +9,7 @@ import { useAppSelector } from '@/store/hooks';
import { SectionProps } from '@/templates/sectionMap';
import DataDisplay from '@/templates/shared/DataDisplay';
import { formatDateString } from '@/utils/date';
import { parseListItemPath } from '@/utils/template';
import { addHttp, parseListItemPath } from '@/utils/template';
import Heading from './Heading';
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
headlinePath = 'headline',
keywordsPath = 'keywords',
}) => {
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
@ -87,7 +87,7 @@ const Section: React.FC<SectionProps> = ({
{summary && <Markdown>{summary}</Markdown>}
{url && (
<DataDisplay icon={<Link />} link={url}>
<DataDisplay icon={<Link />} link={addHttp(url)}>
{url}
</DataDisplay>
)}

View File

@ -1,5 +1,5 @@
import { Email, Link, Phone } from '@mui/icons-material';
import { ListItem, Section } from '@reactive-resume/schema';
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import isEmpty from 'lodash/isEmpty';
@ -9,7 +9,7 @@ import { useAppSelector } from '@/store/hooks';
import { SectionProps } from '@/templates/sectionMap';
import DataDisplay from '@/templates/shared/DataDisplay';
import { formatDateString } from '@/utils/date';
import { parseListItemPath } from '@/utils/template';
import { addHttp, parseListItemPath } from '@/utils/template';
import BadgeDisplay from './BadgeDisplay';
import Heading from './Heading';
@ -21,7 +21,7 @@ const Section: React.FC<SectionProps> = ({
headlinePath = 'headline',
keywordsPath = 'keywords',
}) => {
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
@ -80,7 +80,7 @@ const Section: React.FC<SectionProps> = ({
{summary && <Markdown>{summary}</Markdown>}
{url && (
<DataDisplay icon={<Link />} link={url}>
<DataDisplay icon={<Link />} link={addHttp(url)}>
{url}
</DataDisplay>
)}

View File

@ -1,5 +1,5 @@
import { Email, Phone } from '@mui/icons-material';
import { ListItem, Section } from '@reactive-resume/schema';
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import isEmpty from 'lodash/isEmpty';
@ -8,7 +8,7 @@ import Markdown from '@/components/shared/Markdown';
import { useAppSelector } from '@/store/hooks';
import { SectionProps } from '@/templates/sectionMap';
import { formatDateString } from '@/utils/date';
import { parseListItemPath } from '@/utils/template';
import { addHttp, parseListItemPath } from '@/utils/template';
import BadgeDisplay from './BadgeDisplay';
import Heading from './Heading';
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
headlinePath = 'headline',
keywordsPath = 'keywords',
}) => {
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
@ -84,7 +84,7 @@ const Section: React.FC<SectionProps> = ({
{url && (
<div className="inline-flex justify-center">
<a href={url} target="_blank" rel="noreferrer">
<a href={addHttp(url)} target="_blank" rel="noreferrer">
{url}
</a>
</div>

View File

@ -1,5 +1,5 @@
import { Email, Link, Phone } from '@mui/icons-material';
import { ListItem, Section } from '@reactive-resume/schema';
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import isEmpty from 'lodash/isEmpty';
@ -9,7 +9,7 @@ import { useAppSelector } from '@/store/hooks';
import { SectionProps } from '@/templates/sectionMap';
import DataDisplay from '@/templates/shared/DataDisplay';
import { formatDateString } from '@/utils/date';
import { parseListItemPath } from '@/utils/template';
import { addHttp, parseListItemPath } from '@/utils/template';
import Heading from './Heading';
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
headlinePath = 'headline',
keywordsPath = 'keywords',
}) => {
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
@ -87,7 +87,7 @@ const Section: React.FC<SectionProps> = ({
{summary && <Markdown>{summary}</Markdown>}
{url && (
<DataDisplay icon={<Link />} link={url} className="text-xs">
<DataDisplay icon={<Link />} link={addHttp(url)} className="text-xs">
{url}
</DataDisplay>
)}

View File

@ -15,7 +15,7 @@ export const MastheadSidebar: React.FC = () => {
const { name, photo, email, phone, website, location, profiles } = useAppSelector((state) => state.resume.basics);
return (
<div className="col-span-2 grid justify-items-center gap-4">
<div className="col-span-2 grid justify-items-left gap-4">
{photo.visible && !isEmpty(photo.url) && (
<div className="relative aspect-square h-full w-full">
<img alt={name} src={photo.url} className={getPhotoClassNames(photo.filters)} />

View File

@ -1,5 +1,5 @@
import { Email, Link, Phone } from '@mui/icons-material';
import { ListItem, Section } from '@reactive-resume/schema';
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import isEmpty from 'lodash/isEmpty';
@ -9,7 +9,7 @@ import { useAppSelector } from '@/store/hooks';
import { SectionProps } from '@/templates/sectionMap';
import DataDisplay from '@/templates/shared/DataDisplay';
import { formatDateString } from '@/utils/date';
import { parseListItemPath } from '@/utils/template';
import { addHttp, parseListItemPath } from '@/utils/template';
import Heading from './Heading';
@ -20,7 +20,7 @@ const Section: React.FC<SectionProps> = ({
headlinePath = 'headline',
keywordsPath = 'keywords',
}) => {
const section: Section = useAppSelector((state) => get(state.resume, path, {}));
const section: SectionType = useAppSelector((state) => get(state.resume, path, {}));
const dateFormat: string = useAppSelector((state) => get(state.resume, 'metadata.date.format'));
const primaryColor: string = useAppSelector((state) => get(state.resume, 'metadata.theme.primary'));
@ -79,7 +79,7 @@ const Section: React.FC<SectionProps> = ({
{summary && <Markdown>{summary}</Markdown>}
{url && (
<DataDisplay icon={<Link />} link={url}>
<DataDisplay icon={<Link />} link={addHttp(url)}>
{url}
</DataDisplay>
)}

View File

@ -43,7 +43,7 @@ type Separator = ', ' | ' / ' | ' | ';
export const parseListItemPath = (item: ListItem, path: string | string[], separator: Separator = ', '): string => {
if (isArray(path)) {
const value = path.map((_path) => get(item, _path));
const value = path.map((_path) => get(item, _path)).filter((x) => x);
return value.join(separator);
} else {

View File

@ -10,11 +10,16 @@ const DateWrapper: React.FC = ({ children }) => {
dayjs.extend(relativeTime);
// Locales
require('dayjs/locale/es');
require('dayjs/locale/bn');
require('dayjs/locale/de');
require('dayjs/locale/en');
require('dayjs/locale/es');
require('dayjs/locale/fr');
require('dayjs/locale/hi');
require('dayjs/locale/it');
require('dayjs/locale/kn');
require('dayjs/locale/ta');
require('dayjs/locale/zh');
locale && dayjs.locale(locale);
}, [locale]);

64
docker-compose.dev.yml Normal file
View File

@ -0,0 +1,64 @@
version: '3'
services:
postgres:
image: postgres
container_name: postgres
ports:
- 5432:5432
env_file: .env
volumes:
- ./scripts/database/initialize.sql:/docker-entrypoint-initdb.d/initialize.sql
- pgdata:/var/lib/postgresql/data
traefik:
image: traefik
container_name: traefik
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
ports:
- 80:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
server:
build:
context: .
dockerfile: server/Dockerfile
container_name: server
env_file: .env
environment:
- PUBLIC_URL=http://localhost
- POSTGRES_HOST=postgres
- POSTGRES_DATABASE=reactive_resume
depends_on:
- traefik
- postgres
labels:
- traefik.enable=true
- traefik.http.routers.server.entrypoints=web
- traefik.http.routers.server.rule=Host(`localhost`) && PathPrefix(`/api/`)
- traefik.http.routers.server.middlewares=server-stripprefix
- traefik.http.middlewares.server-stripprefix.stripprefix.prefixes=/api
- traefik.http.middlewares.server-stripprefix.stripprefix.forceslash=true
client:
build:
context: .
dockerfile: client/Dockerfile
container_name: client
env_file: .env
environment:
- PUBLIC_SERVER_URL=http://localhost/api
depends_on:
- traefik
- server
labels:
- traefik.enable=true
- traefik.http.routers.client.rule=Host(`localhost`)
- traefik.http.routers.client.entrypoints=web
volumes:
pgdata:

View File

@ -8,7 +8,6 @@ services:
- 5432:5432
env_file: .env
volumes:
- ./scripts/database/initialize.sql:/docker-entrypoint-initdb.d/initialize.sql
- pgdata:/var/lib/postgresql/data
traefik:
@ -24,17 +23,10 @@ services:
- /var/run/docker.sock:/var/run/docker.sock:ro
server:
# Production
# image: amruthpillai/reactive-resume:server-latest
# Development
build:
context: .
dockerfile: server/Dockerfile
image: amruthpillai/reactive-resume:server-latest
container_name: server
env_file: .env
environment:
- PUBLIC_URL=http://localhost
- POSTGRES_HOST=postgres
depends_on:
- traefik
@ -42,29 +34,21 @@ services:
labels:
- traefik.enable=true
- traefik.http.routers.server.entrypoints=web
- traefik.http.routers.server.rule=Host(`localhost`) && PathPrefix(`/api/`)
- traefik.http.routers.server.rule=Host(`<SERVER-IP>`) && PathPrefix(`/api/`)
- traefik.http.routers.server.middlewares=server-stripprefix
- traefik.http.middlewares.server-stripprefix.stripprefix.prefixes=/api
- traefik.http.middlewares.server-stripprefix.stripprefix.forceslash=true
client:
# Production
# image: amruthpillai/reactive-resume:client-latest
# Development
build:
context: .
dockerfile: client/Dockerfile
image: amruthpillai/reactive-resume:client-latest
container_name: client
env_file: .env
environment:
- PUBLIC_SERVER_URL=http://localhost/api
depends_on:
- traefik
- server
labels:
- traefik.enable=true
- traefik.http.routers.client.rule=Host(`localhost`)
- traefik.http.routers.client.rule=Host(`<SERVER-IP>`)
- traefik.http.routers.client.entrypoints=web
volumes:

View File

@ -0,0 +1,2 @@
position: 4
label: Deployment

View File

@ -0,0 +1,71 @@
---
sidebar_position: 1
---
# Docker
You should be able to deploy a production-grade instance of this app within 5 minutes by just following this guide. For this example, I'll be creating a new DigitalOcean droplet to illustrate the steps.
1. Create a new droplet instance, preferably Ubuntu 20.04 LTS, with at least 2 GB of RAM. You can skip this step if you already have your own server. These are the settings I went with:
![Screenshot 2022-03-14 at 9 32 30 AM](https://user-images.githubusercontent.com/1134738/158134604-85ade15f-4a16-4421-ad2a-b9df3b175467.png)
2. SSH into the instance, and update/upgrade dependencies. Then, install `docker` and `docker compose`. You can follow these links for steps on [how to install Docker](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04) and [how to install Docker Compose](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-20-04) on Ubuntu 20.04 (or any other OS).
Here are all the commands you need, for quick execution:
```bash
sudo apt update && sudo apt upgrade -y
# Install Docker
sudo apt install apt-transport-https ca-certificates curl software-properties-common
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
sudo apt install -y docker-ce
# Verify that Docker is installed
sudo systemctl status docker
# Install Docker Compose
mkdir -p ~/.docker/cli-plugins/
curl -SL https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose
sudo chown $USER /var/run/docker.sock
# Verify that Docker Compose is installed
docker compose version
```
3. Create a new folder to host your app, then create a docker-compose.yml file. The contents of the file can be identical to the one found on the project root.
```bash
mkdir app && cd app
curl -L https://raw.githubusercontent.com/AmruthPillai/Reactive-Resume/main/docker-compose.yml > docker-compose.yml
curl -L https://raw.githubusercontent.com/AmruthPillai/Reactive-Resume/main/.env.example > .env
```
4. Edit the docker-compose.yml file you just pulled in and update the `<SERVER-IP>` placeholders to your server's public IP (or domain, if applicable). Also, update the `.env` file that was just created and change variables such as `PUBLIC_URL`, `PUBLIC_SERVER_URL` etc. For a clear understanding of what each of the environment variables mean, head over to [this section](/source-code/env-vars) of the docs.
To change the default port `80` to something else, say `3000`, just change the properties in docker-compose's traefik service:
```yml
traefik:
command: ...
- --entrypoints.web.address=:3000
ports:
- 3000:3000
```
5. Run the `up` command to check if everything is working as it should.
```
docker compose up
```
<img
width="853"
alt="Screenshot 2022-03-14 at 10 08 50 AM"
src="https://user-images.githubusercontent.com/1134738/158140209-f80eab18-1575-464c-b29d-ac788bd53e93.png"
/>
Now, your application should be running on http://SERVER-IP.

View File

@ -0,0 +1,9 @@
---
sidebar_position: 4
---
# Deployment
You should be able to deploy the app within minutes by following these guides.
- [Docker](/deployment/docker)

View File

@ -1,6 +1,7 @@
---
sidebar_position: 1
sidebar_label: Introduction
title: Introduction
---
import TOCInline from '@theme/TOCInline';
@ -9,6 +10,7 @@ import TOCInline from '@theme/TOCInline';
![Project Version](https://img.shields.io/github/package-json/v/AmruthPillai/Reactive-Resume?style=flat-square)
![Project License](https://img.shields.io/github/license/AmruthPillai/Reactive-Resume?style=flat-square)
[![Crowdin](https://badges.crowdin.net/reactive-resume/localized.svg)](https://translate.rxresu.me)
[![Docker Pulls](https://img.shields.io/docker/pulls/amruthpillai/reactive-resume?style=flat-square)](https://hub.docker.com/r/amruthpillai/reactive-resume)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FAmruthPillai%2FReactive-Resume.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FAmruthPillai%2FReactive-Resume?ref=badge_shield)
@ -42,11 +44,15 @@ You have complete control over what goes into your resume, how it looks, what co
## Languages
- Bengali (বাংলা)
- Chinese (中文)
- English
- French (Français)
- German (Deutsch)
- Hindi (हिन्दी)
- Kannada (ಕನ್ನಡ) (@aksh1251)
- Spanish (Español) (@seba11998)
- Italian (Italiano)
- Kannada (ಕನ್ನಡ)
- Spanish (Español)
- Tamil (தமிழ்)
Help by [translating Reactive Resume](https://translate.rxresu.me) to your language!

View File

@ -38,7 +38,7 @@ The secret key can be a unique key, a randomly generated string that is used for
**Required**: `yes`
**Description:** URL through which app is accessible
**Default Value:** `http://localhost:3000`
**Default Value:** `http://localhost`
This URL would be used in features like link sharing functionality and authentication redirection. This points only to the client app, as the server would be running on `PORT 3100` always.
@ -47,7 +47,7 @@ This URL would be used in features like link sharing functionality and authentic
**Required**: `yes`
**Description:** URL through which server is accessible
**Default Value:** `http://localhost:3100`
**Default Value:** `http://localhost/api`
This URL is used when export PDF functionality is used within the app and has to reach out to the server.

View File

@ -10,5 +10,5 @@ The source code to Reactive Resume is available on [GitHub](https://github.com/A
In this section, I'll be going through the steps on how you can build the project on your local machine (or the cloud). You can choose one of two paths from here:
- [Docker](docker)
- [Local Build](local-build)
- [Docker](/source-code/docker)
- [Local Build](/source-code/local-build)

View File

@ -33,13 +33,25 @@ pnpm dev
Now, your **frontend** client should be running on [`http://localhost:3000`](http://localhost:3000), your **backend** server on [`http://localhost:3100`](http://localhost:3100) and this **documentation** on [`http://localhost:3200`](http://localhost:3200).
5. Build the project before deploying by running the command:
5. To ensure that the app works currently, a proxy layer has to be made between the client and server. For this, I made use of a Chrome extension called [**Rabbit URL Rewriter**](https://chrome.google.com/webstore/detail/rabbit-url-rewriter/kcbmcmeblpkcndhfhkclggekfblookii?hl=en) to forward my requests made to `localhost:3000/api` to `localhost:3100`. The configuration should look something like this:
```
Website URL: http://localhost:3000
From URL: http://localhost:3000/api/(.*)
To URL: http://localhost:3100/$1
```
![Screenshot 2022-03-12 at 4 07 37 PM](https://user-images.githubusercontent.com/1134738/158023473-d415e696-f027-4bc7-af02-648c4a99b147.png)
Now, you should be able to create accounts, login etc.
6. Build the project before deploying by running the command:
```bash
pnpm build
```
6. Finally, start the production servers for all three workspaces by running:
7. Finally, start the production servers for all three workspaces by running:
```bash
pnpm start

View File

@ -12,7 +12,7 @@ const config = {
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/favicon.ico',
favicon: 'favicon.ico',
organizationName: 'AmruthPillai',
projectName: 'Reactive-Resume',
@ -45,15 +45,8 @@ const config = {
title: 'Reactive Resume',
logo: {
alt: 'Reactive Resume',
src: 'img/logo.svg',
src: 'logo.svg',
},
items: [
{
href: 'https://github.com/AmruthPillai/Reactive-Resume',
position: 'right',
label: 'GitHub',
},
],
},
footer: {
style: 'dark',

View File

@ -1,34 +1,27 @@
{
"name": "@reactive-resume/docs",
"name": "docs",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start --port 3200 --no-open",
"start": "docusaurus start --no-open --port 3200",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve --host 0.0.0.0 --port 3200",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@algolia/client-search": "^4.9.1",
"@algolia/client-search": "^4.12.2",
"@docusaurus/core": "2.0.0-beta.17",
"@docusaurus/preset-classic": "2.0.0-beta.17",
"@docusaurus/theme-classic": "^2.0.0-beta.17",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.1.1",
"prism-react-renderer": "^1.2.1",
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.0.0-beta.17",
"@docusaurus/types": "^2.0.0-beta.17",
"@tsconfig/docusaurus": "^1.0.4",
"@types/react": ">=16.8.0 <18.0.0",
"typescript": "^4.6.2"
"prism-react-renderer": "^1.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"browserslist": {
"production": [
@ -41,5 +34,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react": "17.0.40"
}
}

View File

@ -7,7 +7,7 @@
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
*/
// @ts-check

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -1,17 +0,0 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M0 10C0 4.47715 4.47715 0 10 0H502C507.523 0 512 4.47715 512 10V502C512 507.523 507.523 512 502 512H10C4.47715 512 0 507.523 0 502V10Z"
fill="white" />
<path
d="M209.701 220L197.079 201.522L183.546 220H156.74L183.546 184.736L157.521 149.472H185.367L197.859 167.299L210.872 149.472H237.027L211.002 183.825L237.938 220H209.701Z"
fill="#444444" />
<path
d="M108.927 165.797H94.8735V190H69.1088V98.9125H110.749C118.99 98.9125 126.147 100.3 132.219 103.076C138.292 105.766 142.976 109.669 146.273 114.788C149.569 119.819 151.218 125.762 151.218 132.615C151.218 139.208 149.656 144.977 146.533 149.921C143.497 154.779 139.116 158.596 133.391 161.372L153.039 190H125.453L108.927 165.797ZM125.193 132.615C125.193 128.364 123.848 125.068 121.159 122.725C118.47 120.383 114.479 119.212 109.187 119.212H94.8735V145.888H109.187C114.479 145.888 118.47 144.76 121.159 142.504C123.848 140.162 125.193 136.866 125.193 132.615Z"
fill="#444444" />
<path
d="M94.08 273.12H85.44V288H69.6V232H95.2C100.267 232 104.667 232.853 108.4 234.56C112.133 236.213 115.013 238.613 117.04 241.76C119.067 244.853 120.08 248.507 120.08 252.72C120.08 256.773 119.12 260.32 117.2 263.36C115.333 266.347 112.64 268.693 109.12 270.4L121.2 288H104.24L94.08 273.12ZM104.08 252.72C104.08 250.107 103.253 248.08 101.6 246.64C99.9467 245.2 97.4933 244.48 94.24 244.48H85.44V260.88H94.24C97.4933 260.88 99.9467 260.187 101.6 258.8C103.253 257.36 104.08 255.333 104.08 252.72ZM172.419 266.4C172.419 266.507 172.339 267.76 172.179 270.16H140.659C141.299 272.4 142.525 274.133 144.339 275.36C146.205 276.533 148.525 277.12 151.299 277.12C153.379 277.12 155.165 276.827 156.659 276.24C158.205 275.653 159.752 274.693 161.299 273.36L169.299 281.68C165.085 286.373 158.925 288.72 150.819 288.72C145.752 288.72 141.299 287.76 137.459 285.84C133.619 283.92 130.632 281.253 128.499 277.84C126.419 274.427 125.379 270.56 125.379 266.24C125.379 261.973 126.392 258.16 128.419 254.8C130.499 251.387 133.352 248.72 136.979 246.8C140.605 244.88 144.685 243.92 149.219 243.92C153.539 243.92 157.459 244.827 160.979 246.64C164.499 248.4 167.272 250.987 169.299 254.4C171.379 257.76 172.419 261.76 172.419 266.4ZM149.299 254.72C146.952 254.72 144.979 255.387 143.379 256.72C141.832 258.053 140.845 259.867 140.419 262.16H158.179C157.752 259.867 156.739 258.053 155.139 256.72C153.592 255.387 151.645 254.72 149.299 254.72ZM195.187 288.72C191.56 288.72 187.987 288.32 184.467 287.52C180.947 286.667 178.12 285.573 175.987 284.24L180.547 273.84C182.52 275.067 184.867 276.053 187.587 276.8C190.307 277.493 193 277.84 195.667 277.84C198.12 277.84 199.854 277.6 200.867 277.12C201.934 276.587 202.467 275.84 202.467 274.88C202.467 273.92 201.827 273.253 200.547 272.88C199.32 272.453 197.347 272.053 194.627 271.68C191.16 271.253 188.2 270.693 185.747 270C183.347 269.307 181.267 268.027 179.507 266.16C177.747 264.293 176.867 261.68 176.867 258.32C176.867 255.547 177.694 253.093 179.347 250.96C181 248.773 183.4 247.067 186.547 245.84C189.747 244.56 193.56 243.92 197.987 243.92C201.134 243.92 204.254 244.24 207.347 244.88C210.44 245.52 213.027 246.427 215.107 247.6L210.547 257.92C206.707 255.787 202.547 254.72 198.067 254.72C195.667 254.72 193.907 255.013 192.787 255.6C191.667 256.133 191.107 256.853 191.107 257.76C191.107 258.773 191.72 259.493 192.947 259.92C194.174 260.293 196.2 260.693 199.027 261.12C202.6 261.653 205.56 262.267 207.907 262.96C210.254 263.653 212.28 264.933 213.987 266.8C215.747 268.613 216.627 271.173 216.627 274.48C216.627 277.2 215.8 279.653 214.147 281.84C212.494 283.973 210.04 285.653 206.787 286.88C203.587 288.107 199.72 288.72 195.187 288.72ZM268.777 244.64V288H254.297V283.36C252.697 285.12 250.777 286.453 248.537 287.36C246.297 288.267 243.924 288.72 241.417 288.72C235.817 288.72 231.337 287.067 227.977 283.76C224.67 280.453 223.017 275.493 223.017 268.88V244.64H238.217V266.4C238.217 269.653 238.83 272.027 240.057 273.52C241.284 275.013 243.07 275.76 245.417 275.76C247.817 275.76 249.764 274.933 251.257 273.28C252.804 271.573 253.577 268.987 253.577 265.52V244.64H268.777ZM335.048 243.92C340.381 243.92 344.621 245.52 347.768 248.72C350.968 251.92 352.568 256.747 352.568 263.2V288H337.368V265.68C337.368 262.64 336.781 260.4 335.608 258.96C334.488 257.52 332.888 256.8 330.808 256.8C328.514 256.8 326.674 257.6 325.288 259.2C323.901 260.8 323.208 263.227 323.208 266.48V288H308.008V265.68C308.008 259.76 305.821 256.8 301.448 256.8C299.101 256.8 297.234 257.6 295.848 259.2C294.461 260.8 293.768 263.227 293.768 266.48V288H278.568V244.64H293.048V249.2C294.648 247.44 296.541 246.133 298.728 245.28C300.968 244.373 303.394 243.92 306.008 243.92C309.048 243.92 311.768 244.48 314.168 245.6C316.568 246.72 318.514 248.427 320.008 250.72C321.714 248.533 323.874 246.853 326.488 245.68C329.101 244.507 331.954 243.92 335.048 243.92ZM407.106 266.4C407.106 266.507 407.026 267.76 406.866 270.16H375.346C375.986 272.4 377.213 274.133 379.026 275.36C380.893 276.533 383.213 277.12 385.986 277.12C388.066 277.12 389.853 276.827 391.346 276.24C392.893 275.653 394.44 274.693 395.986 273.36L403.986 281.68C399.773 286.373 393.613 288.72 385.506 288.72C380.44 288.72 375.986 287.76 372.146 285.84C368.306 283.92 365.32 281.253 363.186 277.84C361.106 274.427 360.066 270.56 360.066 266.24C360.066 261.973 361.08 258.16 363.106 254.8C365.186 251.387 368.04 248.72 371.666 246.8C375.293 244.88 379.373 243.92 383.906 243.92C388.226 243.92 392.146 244.827 395.666 246.64C399.186 248.4 401.96 250.987 403.986 254.4C406.066 257.76 407.106 261.76 407.106 266.4ZM383.986 254.72C381.64 254.72 379.666 255.387 378.066 256.72C376.52 258.053 375.533 259.867 375.106 262.16H392.866C392.44 259.867 391.426 258.053 389.826 256.72C388.28 255.387 386.333 254.72 383.986 254.72Z"
fill="#444444" />
<line x1="60" y1="358" x2="360" y2="358" stroke="#DDDDDD" stroke-width="20" stroke-miterlimit="16" />
<line x1="60" y1="398" x2="300" y2="398" stroke="#BBBBBB" stroke-width="20" stroke-miterlimit="16" />
<line x1="60" y1="438" x2="240" y2="438" stroke="#DDDDDD" stroke-width="20" stroke-miterlimit="16" />
</svg>

Before

Width:  |  Height:  |  Size: 6.1 KiB

9
docs/static/logo.svg vendored Normal file
View File

@ -0,0 +1,9 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 10C0 4.47715 4.47715 0 10 0H502C507.523 0 512 4.47715 512 10V502C512 507.523 507.523 512 502 512H10C4.47715 512 0 507.523 0 502V10Z" fill="white"/>
<path d="M209.701 220L197.079 201.522L183.546 220H156.74L183.546 184.736L157.521 149.472H185.367L197.859 167.299L210.872 149.472H237.027L211.002 183.825L237.938 220H209.701Z" fill="#444444"/>
<path d="M108.927 165.797H94.8735V190H69.1088V98.9125H110.749C118.99 98.9125 126.147 100.3 132.219 103.076C138.292 105.766 142.976 109.669 146.273 114.788C149.569 119.819 151.218 125.762 151.218 132.615C151.218 139.208 149.656 144.977 146.533 149.921C143.497 154.779 139.116 158.596 133.391 161.372L153.039 190H125.453L108.927 165.797ZM125.193 132.615C125.193 128.364 123.848 125.068 121.159 122.725C118.47 120.383 114.479 119.212 109.187 119.212H94.8735V145.888H109.187C114.479 145.888 118.47 144.76 121.159 142.504C123.848 140.162 125.193 136.866 125.193 132.615Z" fill="#444444"/>
<path d="M94.08 273.12H85.44V288H69.6V232H95.2C100.267 232 104.667 232.853 108.4 234.56C112.133 236.213 115.013 238.613 117.04 241.76C119.067 244.853 120.08 248.507 120.08 252.72C120.08 256.773 119.12 260.32 117.2 263.36C115.333 266.347 112.64 268.693 109.12 270.4L121.2 288H104.24L94.08 273.12ZM104.08 252.72C104.08 250.107 103.253 248.08 101.6 246.64C99.9467 245.2 97.4933 244.48 94.24 244.48H85.44V260.88H94.24C97.4933 260.88 99.9467 260.187 101.6 258.8C103.253 257.36 104.08 255.333 104.08 252.72ZM172.419 266.4C172.419 266.507 172.339 267.76 172.179 270.16H140.659C141.299 272.4 142.525 274.133 144.339 275.36C146.205 276.533 148.525 277.12 151.299 277.12C153.379 277.12 155.165 276.827 156.659 276.24C158.205 275.653 159.752 274.693 161.299 273.36L169.299 281.68C165.085 286.373 158.925 288.72 150.819 288.72C145.752 288.72 141.299 287.76 137.459 285.84C133.619 283.92 130.632 281.253 128.499 277.84C126.419 274.427 125.379 270.56 125.379 266.24C125.379 261.973 126.392 258.16 128.419 254.8C130.499 251.387 133.352 248.72 136.979 246.8C140.605 244.88 144.685 243.92 149.219 243.92C153.539 243.92 157.459 244.827 160.979 246.64C164.499 248.4 167.272 250.987 169.299 254.4C171.379 257.76 172.419 261.76 172.419 266.4ZM149.299 254.72C146.952 254.72 144.979 255.387 143.379 256.72C141.832 258.053 140.845 259.867 140.419 262.16H158.179C157.752 259.867 156.739 258.053 155.139 256.72C153.592 255.387 151.645 254.72 149.299 254.72ZM195.187 288.72C191.56 288.72 187.987 288.32 184.467 287.52C180.947 286.667 178.12 285.573 175.987 284.24L180.547 273.84C182.52 275.067 184.867 276.053 187.587 276.8C190.307 277.493 193 277.84 195.667 277.84C198.12 277.84 199.854 277.6 200.867 277.12C201.934 276.587 202.467 275.84 202.467 274.88C202.467 273.92 201.827 273.253 200.547 272.88C199.32 272.453 197.347 272.053 194.627 271.68C191.16 271.253 188.2 270.693 185.747 270C183.347 269.307 181.267 268.027 179.507 266.16C177.747 264.293 176.867 261.68 176.867 258.32C176.867 255.547 177.694 253.093 179.347 250.96C181 248.773 183.4 247.067 186.547 245.84C189.747 244.56 193.56 243.92 197.987 243.92C201.134 243.92 204.254 244.24 207.347 244.88C210.44 245.52 213.027 246.427 215.107 247.6L210.547 257.92C206.707 255.787 202.547 254.72 198.067 254.72C195.667 254.72 193.907 255.013 192.787 255.6C191.667 256.133 191.107 256.853 191.107 257.76C191.107 258.773 191.72 259.493 192.947 259.92C194.174 260.293 196.2 260.693 199.027 261.12C202.6 261.653 205.56 262.267 207.907 262.96C210.254 263.653 212.28 264.933 213.987 266.8C215.747 268.613 216.627 271.173 216.627 274.48C216.627 277.2 215.8 279.653 214.147 281.84C212.494 283.973 210.04 285.653 206.787 286.88C203.587 288.107 199.72 288.72 195.187 288.72ZM268.777 244.64V288H254.297V283.36C252.697 285.12 250.777 286.453 248.537 287.36C246.297 288.267 243.924 288.72 241.417 288.72C235.817 288.72 231.337 287.067 227.977 283.76C224.67 280.453 223.017 275.493 223.017 268.88V244.64H238.217V266.4C238.217 269.653 238.83 272.027 240.057 273.52C241.284 275.013 243.07 275.76 245.417 275.76C247.817 275.76 249.764 274.933 251.257 273.28C252.804 271.573 253.577 268.987 253.577 265.52V244.64H268.777ZM335.048 243.92C340.381 243.92 344.621 245.52 347.768 248.72C350.968 251.92 352.568 256.747 352.568 263.2V288H337.368V265.68C337.368 262.64 336.781 260.4 335.608 258.96C334.488 257.52 332.888 256.8 330.808 256.8C328.514 256.8 326.674 257.6 325.288 259.2C323.901 260.8 323.208 263.227 323.208 266.48V288H308.008V265.68C308.008 259.76 305.821 256.8 301.448 256.8C299.101 256.8 297.234 257.6 295.848 259.2C294.461 260.8 293.768 263.227 293.768 266.48V288H278.568V244.64H293.048V249.2C294.648 247.44 296.541 246.133 298.728 245.28C300.968 244.373 303.394 243.92 306.008 243.92C309.048 243.92 311.768 244.48 314.168 245.6C316.568 246.72 318.514 248.427 320.008 250.72C321.714 248.533 323.874 246.853 326.488 245.68C329.101 244.507 331.954 243.92 335.048 243.92ZM407.106 266.4C407.106 266.507 407.026 267.76 406.866 270.16H375.346C375.986 272.4 377.213 274.133 379.026 275.36C380.893 276.533 383.213 277.12 385.986 277.12C388.066 277.12 389.853 276.827 391.346 276.24C392.893 275.653 394.44 274.693 395.986 273.36L403.986 281.68C399.773 286.373 393.613 288.72 385.506 288.72C380.44 288.72 375.986 287.76 372.146 285.84C368.306 283.92 365.32 281.253 363.186 277.84C361.106 274.427 360.066 270.56 360.066 266.24C360.066 261.973 361.08 258.16 363.106 254.8C365.186 251.387 368.04 248.72 371.666 246.8C375.293 244.88 379.373 243.92 383.906 243.92C388.226 243.92 392.146 244.827 395.666 246.64C399.186 248.4 401.96 250.987 403.986 254.4C406.066 257.76 407.106 261.76 407.106 266.4ZM383.986 254.72C381.64 254.72 379.666 255.387 378.066 256.72C376.52 258.053 375.533 259.867 375.106 262.16H392.866C392.44 259.867 391.426 258.053 389.826 256.72C388.28 255.387 386.333 254.72 383.986 254.72Z" fill="#444444"/>
<line x1="60" y1="358" x2="360" y2="358" stroke="#DDDDDD" stroke-width="20" stroke-miterlimit="16"/>
<line x1="60" y1="398" x2="300" y2="398" stroke="#BBBBBB" stroke-width="20" stroke-miterlimit="16"/>
<line x1="60" y1="438" x2="240" y2="438" stroke="#DDDDDD" stroke-width="20" stroke-miterlimit="16"/>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -1,6 +0,0 @@
{
"extends": "@tsconfig/docusaurus/tsconfig.json",
"compilerOptions": {
"baseUrl": "."
}
}

View File

@ -1,6 +1,6 @@
{
"name": "reactive-resume",
"version": "3.1.2",
"version": "3.2.3",
"private": true,
"workspaces": [
"schema",
@ -41,7 +41,7 @@
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.10.0",
"eslint": "^8.11.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^4.0.0",

542
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
"lint": "eslint --fix --ext .ts ./src"
},
"devDependencies": {
"eslint": "^8.10.0",
"eslint": "^8.11.0",
"typescript": "<4.6.0"
}
}

View File

@ -1 +1 @@
export type Integration = 'linkedin' | 'json-resume' | 'reactive-resume';
export type Integration = 'linkedin' | 'json-resume' | 'reactive-resume' | 'reactive-resume-v2';

View File

@ -23,6 +23,7 @@ export type DateConfig = {
export type Metadata = {
css: CustomCSS;
locale: string;
date: DateConfig;
layout: string[][][]; // page.column.section
template: string;

View File

@ -61,7 +61,7 @@ export type Publication = {
export type Skill = {
id?: string;
name: string;
level: string;
level?: string;
levelNum: number;
keywords?: string[];
};

View File

@ -22,7 +22,7 @@
"@nestjs/serve-static": "^2.2.2",
"@nestjs/terminus": "^8.0.4",
"@nestjs/typeorm": "^8.0.3",
"@sendgrid/mail": "^7.6.1",
"@sendgrid/mail": "^7.6.2",
"@types/passport": "^1.0.7",
"bcrypt": "^5.0.1",
"cache-manager": "^3.6.0",
@ -31,7 +31,7 @@
"cookie-parser": "^1.4.6",
"csvtojson": "^2.0.10",
"dayjs": "^1.10.8",
"googleapis": "^95.0.0",
"googleapis": "^96.0.0",
"joi": "^17.6.0",
"lodash": "^4.17.21",
"multer": "^1.4.4",
@ -58,12 +58,12 @@
"@types/express": "^4.17.13",
"@types/multer": "^1.4.7",
"@types/node": "^17.0.21",
"eslint": "^8.10.0",
"eslint": "^8.11.0",
"prettier": "^2.5.1",
"source-map-support": "^0.5.21",
"ts-loader": "^9.2.7",
"ts-loader": "^9.2.8",
"ts-node": "^10.7.0",
"tsconfig-paths": "^3.13.0",
"tsconfig-paths": "^3.14.0",
"typescript": "<4.6.0",
"webpack": "^5.70.0"
}

View File

@ -42,4 +42,15 @@ export class IntegrationsController {
return this.integrationsService.reactiveResume(userId, file.path);
}
@UseGuards(JwtAuthGuard)
@Post('reactive-resume-v2')
@UseInterceptors(FileInterceptor('file'))
reactiveResumeV2(@User('id') userId: number, @UploadedFile() file: Express.Multer.File) {
if (!file) {
throw new HttpException('You must upload a valid JSON file.', HttpStatus.BAD_REQUEST);
}
return this.integrationsService.reactiveResumeV2(userId, file.path);
}
}

View File

@ -616,4 +616,333 @@ export class IntegrationsService {
await unlink(path);
}
}
async reactiveResumeV2(userId: number, path: string): Promise<ResumeEntity> {
try {
const jsonResume = JSON.parse(await readFile(path, 'utf8'));
const resume: Partial<Resume> = cloneDeep(defaultState);
// Metadata
const timestamp = dayjs().format(FILENAME_TIMESTAMP);
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
name: `Imported from Reactive Resume V2 (${timestamp})`,
slug: `imported-from-reactive-resume-v2-${timestamp}`,
});
// Basics
try {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
basics: {
name: get(jsonResume, 'profile.firstName') + ' ' + get(jsonResume, 'profile.lastName'),
headline: get(jsonResume, 'profile.subtitle'),
photo: {
url: get(jsonResume, 'profile.photograph'),
},
email: get(jsonResume, 'profile.email'),
phone: get(jsonResume, 'profile.phone'),
website: get(jsonResume, 'profile.website'),
summary: get(jsonResume, 'objective'),
location: {
address: get(jsonResume, 'profile.address.line1'),
postalCode: get(jsonResume, 'profile.address.pincode'),
city: get(jsonResume, 'profile.address.city'),
},
},
});
} catch {
// pass through
}
// Profiles
try {
const profiles: any[] = get(jsonResume, 'social.items', []);
profiles.forEach((profile) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
basics: {
profiles: [
...resume.basics.profiles,
{
id: uuidv4(),
url: get(profile, 'url'),
network: get(profile, 'network'),
username: get(profile, 'username'),
},
],
},
});
});
} catch {
// pass through
}
// Work
try {
const work: any[] = get(jsonResume, 'work.items', []);
work.forEach((item) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
work: {
items: [
...get(resume, 'sections.work.items', []),
{
id: uuidv4(),
name: get(item, 'company'),
position: get(item, 'position'),
summary: get(item, 'summary'),
url: get(item, 'website'),
date: {
start: dayjs(get(item, 'startDate')).toISOString(),
end: dayjs(get(item, 'endDate')).toISOString(),
},
} as WorkExperience,
],
},
},
});
});
} catch {
// pass through
}
// Education
try {
const education: any[] = get(jsonResume, 'education.items', []);
education.forEach((item) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
education: {
items: [
...get(resume, 'sections.education.items', []),
{
id: uuidv4(),
institution: get(item, 'institution'),
degree: get(item, 'studyType'),
url: get(item, 'url'),
score: get(item, 'gpa'),
area: get(item, 'field'),
summary: get(item, 'summary'),
courses: get(item, 'courses', []),
date: {
start: dayjs(get(item, 'startDate')).toISOString(),
end: dayjs(get(item, 'endDate')).toISOString(),
},
} as Education,
],
},
},
});
});
} catch {
// pass through
}
// Awards
try {
const awards: any[] = get(jsonResume, 'awards.items', []);
awards.forEach((award) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
awards: {
items: [
...get(resume, 'sections.awards.items', []),
{
id: uuidv4(),
title: get(award, 'title'),
awarder: get(award, 'awarder'),
summary: get(award, 'summary'),
date: dayjs(get(award, 'date')).toISOString(),
} as Award,
],
},
},
});
});
} catch {
// pass through
}
// Certifications
try {
const certifications: any[] = get(jsonResume, 'certifications.items', []);
certifications.forEach((certificate) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
certifications: {
items: [
...get(resume, 'sections.certifications.items', []),
{
id: uuidv4(),
name: get(certificate, 'title'),
issuer: get(certificate, 'issuer'),
summary: get(certificate, 'summary'),
date: dayjs(get(certificate, 'date')).toISOString(),
} as Certificate,
],
},
},
});
});
} catch {
// pass through
}
// Skills
try {
const skills: any[] = get(jsonResume, 'skills.items', []);
skills.forEach((skill) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
skills: {
items: [
...get(resume, 'sections.skills.items', []),
{
id: uuidv4(),
name: get(skill, 'name'),
level: get(skill, 'level'),
levelNum: 5,
} as Skill,
],
},
},
});
});
} catch {
// pass through
}
// Languages
try {
const languages: any[] = get(jsonResume, 'languages.items', []);
languages.forEach((language) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
languages: {
items: [
...get(resume, 'sections.languages.items', []),
{
id: uuidv4(),
name: get(language, 'name'),
level: get(language, 'fluency'),
levelNum: 5,
} as Language,
],
},
},
});
});
} catch {
// pass through
}
// Hobbies
try {
const hobbies: any[] = get(jsonResume, 'hobbies.items', []);
hobbies.forEach((hobby) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
interests: {
items: [
...get(resume, 'sections.interests.items', []),
{
id: uuidv4(),
name: get(hobby, 'name'),
keywords: get(hobby, 'keywords', []),
} as Interest,
],
},
},
});
});
} catch {
// pass through
}
// References
try {
const references: any[] = get(jsonResume, 'references.items', []);
references.forEach((reference) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
references: {
items: [
...get(resume, 'sections.references.items', []),
{
id: uuidv4(),
name: get(reference, 'name'),
relationship: get(reference, 'position'),
phone: get(reference, 'phone'),
email: get(reference, 'email'),
summary: get(reference, 'summary'),
} as Reference,
],
},
},
});
});
} catch {
// pass through
}
// Projects
try {
const projects: any[] = get(jsonResume, 'projects.items', []);
projects.forEach((project) => {
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
sections: {
projects: {
items: [
...get(resume, 'sections.projects.items', []),
{
id: uuidv4(),
name: get(project, 'title'),
summary: get(project, 'summary'),
keywords: get(project, 'keywords'),
url: get(project, 'link'),
date: {
start: dayjs(get(project, 'date')).toISOString(),
},
} as Project,
],
},
},
});
});
} catch {
// pass through
}
// Metadata
const template = get(jsonResume, 'metadata.template');
const templateWhitelist = ['onyx', 'pikachu', 'gengar', 'castform', 'glalie'];
merge<Partial<Resume>, DeepPartial<Resume>>(resume, {
metadata: {
...get(resume, 'metadata'),
typography: {
family: {
heading: get(jsonResume, 'metadata.font'),
body: get(jsonResume, 'metadata.font'),
},
size: {
heading: get(jsonResume, 'metadata.fontSize'),
body: get(jsonResume, 'metadata.fontSize'),
},
},
theme: {
background: get(jsonResume, 'metadata.colors.background'),
primary: get(jsonResume, 'metadata.colors.primary'),
text: get(jsonResume, 'metadata.colors.text'),
},
locale: get(jsonResume, 'metadata.language'),
template: templateWhitelist.includes(template) ? template : 'kakuna',
},
});
return this.resumeService.import(resume, userId);
} catch {
throw new HttpException('You must upload a valid JSON Resume file.', HttpStatus.BAD_REQUEST);
} finally {
await unlink(path);
}
}
}

View File

@ -45,7 +45,7 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
const pdf = await PDFDocument.create();
const directory = join(__dirname, '..', 'assets/exports');
const filename = `RxResume_PDFExport_${nanoid()}.pdf`;
const publicUrl = `/api/exports/${filename}`;
const publicUrl = `/assets/exports/${filename}`;
for (let index = 0; index < resumePages.length; index++) {
await page.evaluate((page) => (document.body.innerHTML = page.innerHTML), resumePages[index]);

View File

@ -133,6 +133,7 @@ const defaultState: Partial<Resume> = {
background: '#ffffff',
primary: '#f44336',
},
locale: 'en',
date: {
format: 'MMMM DD, YYYY',
},

View File

@ -421,6 +421,7 @@ const sampleData: Partial<Resume> = {
value: '/* Enter custom CSS here */\n\n* {\n outline: 1px solid #000;\n}',
visible: false,
},
locale: 'en',
date: {
format: 'MMMM DD, YYYY',
},