diff --git a/.gitignore b/.gitignore index eea34196..a9de8b3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +.env + # dependencies /node_modules -/frontend/node_modules/.cache -/frontend/node_modules/** +/frontend/node_modules/ +/frontend/node_modules/.cache/ +/backend/.secrets/ /.pnp .pnp.js diff --git a/backend/src/main/java/com/kurttekin/can/job_track/infrastructure/security/config/CorsConfig.java b/backend/src/main/java/com/kurttekin/can/job_track/infrastructure/security/config/CorsConfig.java index edad0876..d6b0c847 100644 --- a/backend/src/main/java/com/kurttekin/can/job_track/infrastructure/security/config/CorsConfig.java +++ b/backend/src/main/java/com/kurttekin/can/job_track/infrastructure/security/config/CorsConfig.java @@ -14,7 +14,6 @@ @Configuration public class CorsConfig { - // if no env var, default to localhost:3000 for react @Value("${ALLOWED_ORIGINS:http://localhost:3000}") private String allowedOrigins; diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 511ffefc..31213537 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -8,9 +8,9 @@ app.jwtExpirationInMs=86400000 # jwt signing key -gemini.api.key=${GEMINI_API_KEY} +gemini.api.key=${GEMINI_API_KEY:"GEMINI_API_KEY"} -ALLOWED_ORIGINS=${ALLOWED_ORIGINS} +#ALLOWED_ORIGINS=${ALLOWED_ORIGINS} server.port=${PORT:8080} #server.ssl.key-store=classpath:keystore.p12 #server.ssl.key-store-password=${KEY_STORE_PASS} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 09ad9502..131a7839 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,8 +12,10 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.7.7", + "i18next": "^23.16.4", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-i18next": "^15.1.0", "react-icons": "^5.3.0", "react-modal": "^3.16.1", "react-router-dom": "^6.27.0", @@ -9884,6 +9886,15 @@ "node": ">=12" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-webpack-plugin": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", @@ -10037,6 +10048,29 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "23.16.4", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.4.tgz", + "integrity": "sha512-9NIYBVy9cs4wIqzurf7nLXPyf3R78xYbxExVqHLK9od3038rjpyOEzW+XB130kZ1N4PZ9inTtJ471CRJ4Ituyg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -15915,6 +15949,28 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", "license": "MIT" }, + "node_modules/react-i18next": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.1.0.tgz", + "integrity": "sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-icons": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", @@ -18478,9 +18534,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "license": "Apache-2.0", "peer": true, "bin": { @@ -18488,7 +18544,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { @@ -18749,6 +18805,15 @@ "d3-timer": "^3.0.1" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 4e5c98ad..36c89342 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,8 +8,10 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.7.7", + "i18next": "^23.16.4", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-i18next": "^15.1.0", "react-icons": "^5.3.0", "react-modal": "^3.16.1", "react-router-dom": "^6.27.0", diff --git a/frontend/src/components/Home.js b/frontend/src/components/Home.js index c356ae5f..5618c90b 100644 --- a/frontend/src/components/Home.js +++ b/frontend/src/components/Home.js @@ -5,16 +5,17 @@ import filterImage from '../assets/filter.png'; import starsImage from '../assets/stars.png'; import goalsImage from '../assets/goal.png'; import buyMeACoffeeImage from '../assets/buy-me-a-beer.png'; -import seperator from '../assets/seperator.png'; +import separator from '../assets/seperator.png'; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../contexts/AuthContext'; import styled from "styled-components"; import screenshotImage from '../assets/screenshot1.png'; +import { useTranslation } from 'react-i18next'; +import { Trans } from 'react-i18next'; const Button = styled.button` margin-right: 0; margin-left: 0; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - &:hover { background-color: #333; } @@ -23,6 +24,7 @@ const Button = styled.button` const Home = () => { const navigate = useNavigate(); const { isLoggedIn } = useContext(AuthContext); + const { t } = useTranslation(); // Use the hook const handleRegisterClick = () => { navigate('/register'); @@ -32,14 +34,14 @@ const Home = () => {
-

Keep Track of Your Job Applications

-

Application Tracking System For Suckers

+

{t('header.title')}

+

{t('header.subtitle')}

-

Make your job hunt more organized — in one place.

+

{t('header.description')}

{!isLoggedIn && ( )}
@@ -48,19 +50,19 @@ const Home = () => {
Illustration 1 -

Add jobs from any source you want

+

{t('illustrations.job_source')}

Illustration 2 -

Comment on your applications, star your favorites

+

{t('illustrations.comment')}

Illustration 3 -

Filter and/or sort to view your applications

+

{t('illustrations.filter')}

Illustration 4 -

Land a job (hopefully)

+

{t('illustrations.goal')}

@@ -73,14 +75,12 @@ const Home = () => { />
-

Why use ATSFS instead of spreadsheets or just good old notes?

+

{t('hero.title')}

- ⚹One convenient place you can access from anywhere and any - device.
- ⚹Stay organized, never miss an opportunity.
- ⚹Generate personalized interview questions to prepare effectively.
- ⚹Analyze your application stats. -
+ ⚹ }} />
+ ⚹ }} />
+ ⚹ }} />
+ ⚹ }} />

@@ -94,37 +94,30 @@ const Home = () => { />

- This service is designed to help people in job hunt without any cost, if you want to support, - here is donation button stuff. + {t('support.text')}

- ); -}; -export default Home; + ); + }; + +export default Home; \ No newline at end of file diff --git a/frontend/src/components/LanguageSwitcher.js b/frontend/src/components/LanguageSwitcher.js index 979c270c..c75e3493 100644 --- a/frontend/src/components/LanguageSwitcher.js +++ b/frontend/src/components/LanguageSwitcher.js @@ -1,16 +1,33 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from "styled-components"; + +const Button = styled.button` + margin-right: 2px; + margin-left: 0; + padding: 4px; + background-color: transparent; + color: black; + //border: 1px solid #333; + //box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + &:hover { + background-color: #f5f5f5; + } +`; + const LanguageSwitcher = () => { - const { i18n } = useTranslation(); + const { i18n } = useTranslation(); - const changeLanguage = (language) => { - i18n.changeLanguage(language); - }; + const changeLanguage = (language) => { + i18n.changeLanguage(language); + }; - return ( -
- - -
- ); + return ( +
+ + +
+ ); }; export default LanguageSwitcher; diff --git a/frontend/src/components/Sidebar.js b/frontend/src/components/Sidebar.js index 2898cc18..7d9630f8 100644 --- a/frontend/src/components/Sidebar.js +++ b/frontend/src/components/Sidebar.js @@ -4,6 +4,7 @@ import styled from 'styled-components'; import AddJobApplication from './AddJobApplication'; import { AuthContext } from '../contexts/AuthContext'; import atsfsIcon from '../assets/atsfs.png'; +import LanguageSwitcher from "./LanguageSwitcher"; // Import the useTranslation hook const NavbarContainer = styled.div` position: fixed; @@ -164,9 +165,11 @@ const Navbar = () => { file_download Export + - )} + )} + setIsModalOpen(false)} /> {isLoggedIn &&
Logged in as {user}
} @@ -176,6 +179,7 @@ const Navbar = () => {
+ ); }; diff --git a/frontend/src/index.js b/frontend/src/index.js index d563c0fb..aadc5dce 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; +import './i18n'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index e69de29b..0bb9189c 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -0,0 +1,29 @@ +{ + "header": { + "title": "Keep Track of Your Job Applications", + "subtitle": "Application Tracking System For Suckers", + "description": "Make your job hunt more organized — in one place.", + "buttonText": "Get ATSFS free" + }, + "illustrations": { + "job_source": "Add jobs from any source you want", + "comment": "Comment on your applications, star your favorites", + "filter": "Filter and/or sort to view your applications", + "goal": "Land a job (hopefully)" + }, + "support": { + "text": "This service is designed to help people in job hunt without any cost, if you want to support, here is donation button stuff." + }, + "footer": { + "description": "Application Tracking System For Suckers(ATSFS) made with despair and boredom by cankurttekin to help me and you." + }, + "hero": { + "title": "Why use ATSFS instead of spreadsheets or just good old notes?", + "point1": "One convenient place you can access from anywhere and any device.", + "point2": "Stay organized, never miss an opportunity.", + "point3": "Generate personalized interview questions to prepare effectively.", + "point4": "Analyze your application stats." + } +} + + diff --git a/frontend/src/locales/tr.json b/frontend/src/locales/tr.json index e69de29b..2e5daca2 100644 --- a/frontend/src/locales/tr.json +++ b/frontend/src/locales/tr.json @@ -0,0 +1,27 @@ +{ + "header": { + "title": "İş Başvurularınızı Takip Edin", + "subtitle": "Application Tracking System For Suckers", + "description": "İş arama sürecinizi daha düzenli hale getirin — tek bir yerden.", + "buttonText": "Ücretsiz kaydolun" + }, + "illustrations": { + "job_source": "İstediğiniz kaynaktan işler ekleyin", + "comment": "Başvurularınıza yorum yapın, favorilerinizi yıldızlayın", + "filter": "Başvurularınızı görüntülemek için filtreleyin ve/veya sıralayın", + "goal": "İş bulun (umarım)" + }, + "support": { + "text": "Bu hizmet, iş arayan kişilere ücretsiz olarak yardımcı olmak için tasarlanmıştır, desteklemek isterseniz, işte bağış butonu." + }, + "footer": { + "description": "Application Tracking System For Suckers (ATSFS) cankurttekin tarafından umutsuzluk ve sıkıntı ile yapıldı." + }, + "hero": { + "title": "Ofis uygulamalarında tablolar veya not almak yerine neden ATSFS'yi kullanmalıyım?", + "point1": "Her yerden ve her cihazdan erişebileceğiniz tek bir yer.", + "point2": "Düzenli kalın, hiçbir fırsatı kaçırmayın.", + "point3": "Etkili hazırlanın; kişisel mülakat soruları oluşturun.", + "point4": "Başvuru istatistiklerinizi analiz edin." + } +}