diff --git a/src/App.css b/src/App.css index 45e4d2f..ae02eda 100644 --- a/src/App.css +++ b/src/App.css @@ -1,97 +1,75 @@ -/* @import '~antd/dist/antd.css'; */ - +/* Global styles */ body { - background-color: #f3f6f8;; + background-color: #f3f6f8; } -#usercentrics-button { - display: none; +/* Ant Design Button Customizations */ +:root { + --btn-primary-color: #7BCADE; + --btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); + --btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12); } .App { text-align: center; } -.ant-btn-primary { - color: #fff; - background-color: #7BCADE; - border-color: #7BCADE; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12); - box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); +#usercentrics-button { + display: none; } -.ant-btn:active { +.ant-btn-primary, +.ant-btn:active, +.ant-btn:focus { color: #fff; - background-color: #7BCADE; - border-color: #7BCADE; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12); - box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); + background-color: var(--btn-primary-color); + border-color: var(--btn-primary-color); + text-shadow: var(--btn-text-shadow); + box-shadow: var(--btn-shadow); } .ant-btn:hover { - border: solid 1px #7BCADE; + border: solid 1px var(--btn-primary-color); } -.ant-btn:focus { - color: #fff; - background-color: #7BCADE; - border-color: #7BCADE; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12); - box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); +/* Ant Design Slider Customizations */ +.ant-slider-handle, +.ant-slider-track, +.ant-slider-dot-active, +.ant-slider:hover .ant-slider-track, +.ant-slider:hover .ant-slider-handle, +.ant-slider:hover .ant-slider-handle:not(.ant-tooltip-open), +.ant-slider-handle-click-focused { + border-color: var(--btn-primary-color); } .ant-slider-handle { - border: solid 2px #7BCADE; -} - -.ant-slider-track { - border: solid 2px #7BCADE; -} - -.ant-slider-dot-active { - border-color: #7BCADE; + border: solid 2px var(--btn-primary-color); } -.ant-slider:hover .ant-slider-track { - background-color: #7BCADE; -} - -.ant-slider:hover .ant-slider-handle { - border-color: #7BCADE; -} - -.ant-slider:hover .ant-slider-handle:not(.ant-tooltip-open) { - border-color: #7BCADE; -} - -.ant-slider-handle-click-focused { - border-color: #7BCADE; -} +/* Other styles */ .devicon-size { font-size: 1.5rem; padding-top: 3px; } .b { - width: 30px + width: 30px; } -/* width */ +/* Scrollbar Customizations */ ::-webkit-scrollbar { width: 12px; } -/* Track */ ::-webkit-scrollbar-track { background: #f3f6f8; } -/* Handle */ ::-webkit-scrollbar-thumb { background: #888; } -/* Handle on hover */ ::-webkit-scrollbar-thumb:hover { background: #555; } diff --git a/src/App.js b/src/App.js index 4cb4c15..f72a430 100644 --- a/src/App.js +++ b/src/App.js @@ -1,75 +1,48 @@ -import React, {Component, Suspense} from 'react'; -import Routes from './routes'; -import {ThemeContext} from "./themeContext"; -import style from './Theme.module.scss' - -class RoutedApp extends Component { - render() { - return <> - - - } -} - -class Theme extends Component { - constructor(props) { - super(props); - - this.state = { - theme: localStorage.getItem('theme') ?? this.getSystemPreferredTheme(), - toggleTheme: this.toggleTheme, - }; +import React, { useState, useEffect, lazy, Suspense } from 'react'; +import Routes from './routes'; // Ensure you have this component in your project. +import { ThemeContext } from "./themeContext"; +import style from './Theme.module.scss'; +import classNames from 'classnames'; +const DARK_THEME = 'dark'; +const LIGHT_THEME = 'light'; - } - - toggleTheme = () => { - this.setState(state => { - const newTheme = state.theme === 'dark' ? 'light' : 'dark' - - localStorage.setItem('theme', newTheme); +const RoutedApp = () => { + return ; +} - return { - theme: newTheme - } - }); - } +const Theme = () => { + const storedTheme = localStorage.getItem('theme'); + const [theme, setTheme] = useState(storedTheme || getSystemPreferredTheme()); - getSystemPreferredTheme() { - const isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)"); + useEffect(() => { + localStorage.setItem('theme', theme); + }, [theme]); - if (isDarkTheme.matches) { - return 'dark'; - } + const toggleTheme = () => { + setTheme(currentTheme => currentTheme === DARK_THEME ? LIGHT_THEME : DARK_THEME); + }; - return 'light'; + const getSystemPreferredTheme = () => { + return window.matchMedia("(prefers-color-scheme: dark)").matches ? DARK_THEME : LIGHT_THEME; } - render() { + const classes = classNames(style.Theme, { + [style.Theme_dark]: theme === DARK_THEME, + [style.Theme_light]: theme !== DARK_THEME, + }); - const classes = [style.Theme]; - - if(this.state.theme === 'dark') { - classes.push(style.Theme_dark); - } else { - classes.push(style.Theme_light) - } - - return ( - -
- - - -
-
- ); - } + return ( + +
+ Loading...}> + + +
+
+ ); } - export default function App() { - return ( - - ); + return ; } diff --git a/src/Theme.module.scss b/src/Theme.module.scss index b16d32d..aed0211 100644 --- a/src/Theme.module.scss +++ b/src/Theme.module.scss @@ -1,12 +1,22 @@ +// Define theme colors and other constants +$theme-light-bg: #F3F4F6; +$theme-dark-bg: #2F3136; +$box-shadow-dark: 0 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.06), 0px 0px 1px rgba(0, 0, 0, 0.04); + .Theme { + // Smooth transition for theme changes transition: background-color .2s ease-in-out; + // Light theme styling &_light { - background-color: #F3F4F6; + background-color: $theme-light-bg; } + // Dark theme styling &_dark { - background-color: #2F3136; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.06), 0px 0px 1px rgba(0, 0, 0, 0.04); + background-color: $theme-dark-bg; + + // Shadow for added depth in dark theme + box-shadow: $box-shadow-dark; } -} \ No newline at end of file +} diff --git a/src/routes.js b/src/routes.js index d07de0c..d07e9bf 100644 --- a/src/routes.js +++ b/src/routes.js @@ -1,31 +1,34 @@ -import React from "react"; +import React, { lazy, Suspense } from "react"; import { Switch, Route } from "react-router-dom"; -import JobsContainer from "./jobs/JobsContainer/JobsContainer"; -import NotFound from "./pages/NotFound"; -import Imprint from "./pages/Imprint"; -import DataSecurity from "./pages/DataSecurity"; -import Terms from "./pages/Terms"; -import ChoosePlanContainer from "./jobs/ChoosePlanContainer/ChoosePlanContainer"; -import NewJobContainer from "./jobs/NewJobContainer/index"; -import InvoiceDetails from "./jobs/NewJobContainer/InvoiceDetails"; -import Pricing from './static/Pricing'; -import About from './static/About'; -import AboutOld from './pages/About.js'; -import Company from './static/CompanyProfile'; -import BrandsList from "./brands/BrandsList"; -import EduRoom from './EduRoom/EduRoom'; -import Tutorials from './EduRoom/Tutorials/Tutorials'; -import Meetups from './EduRoom/Meetups/Meetups'; -import Meetup from './EduRoom/Meetups/Meetup'; -import StudyMaterial from './EduRoom/StudyMaterial/StudyMaterial'; -import StudyMaterialSingle from './EduRoom/StudyMaterial/StudyMaterialSingle'; -import Blog from './Blog/Blog'; -import BlogSingle from "./Blog/BlogSingle"; -import Token from "./Token/J4IT"; -import Statistics from './statistics/Statistics'; + +const JobsContainer = lazy(() => import("./jobs/JobsContainer/JobsContainer")); +const NotFound = lazy(() => import("./pages/NotFound")); +const Imprint = lazy(() => import("./pages/Imprint")); +const DataSecurity = lazy(() => import("./pages/DataSecurity")); +const Terms = lazy(() => import("./pages/Terms")); +const ChoosePlanContainer = lazy(() => import("./jobs/ChoosePlanContainer/ChoosePlanContainer")); +const NewJobContainer = lazy(() => import("./jobs/NewJobContainer/index")); +const InvoiceDetails = lazy(() => import("./jobs/NewJobContainer/InvoiceDetails")); +const Pricing = lazy(() => import('./static/Pricing')); +const About = lazy(() => import('./static/About')); +const AboutOld = lazy(() => import('./pages/About.js')); +const Company = lazy(() => import('./static/CompanyProfile')); +const BrandsList = lazy(() => import("./brands/BrandsList")); +const EduRoom = lazy(() => import('./EduRoom/EduRoom')); +const Tutorials = lazy(() => import('./EduRoom/Tutorials/Tutorials')); +const Meetups = lazy(() => import('./EduRoom/Meetups/Meetups')); +const Meetup = lazy(() => import('./EduRoom/Meetups/Meetup')); +const StudyMaterial = lazy(() => import('./EduRoom/StudyMaterial/StudyMaterial')); +const StudyMaterialSingle = lazy(() => import('./EduRoom/StudyMaterial/StudyMaterialSingle')); +const Blog = lazy(() => import('./Blog/Blog')); +const BlogSingle = lazy(() => import("./Blog/BlogSingle")); +const Token = lazy(() => import("./Token/J4IT")); +const Statistics = lazy(() => import('./statistics/Statistics')); const Routes = () => ( + Loading...}> + @@ -33,9 +36,7 @@ const Routes = () => ( - {/**/} - @@ -51,8 +52,9 @@ const Routes = () => ( - + + ); export default Routes; diff --git a/src/sitemap.js b/src/sitemap.js index cef0034..bf8b1e5 100644 --- a/src/sitemap.js +++ b/src/sitemap.js @@ -1,189 +1,143 @@ -const { paramsApplier } = require("react-router-sitemap"); +// Dependencies and Libraries const fs = require("fs"); +const path = require('path'); const contentful = require("contentful"); const moment = require('moment'); const xmlFormatter = require('xml-formatter'); -const path = require('path'); - -const SPACE_ID = process.env.REACT_APP_SPACE_ID || "f6zwhql64w01"; -const ACCESS_TOKEN = - process.env.REACT_APP_ACCESS_TOKEN || - "00b696c26342aa70ce936b551fe48e6548745fa637b6cd0c62fa72886af5bd78"; -const MANAGER_TOKEN = - process.env.REACT_APP_MANAGER_TOKEN || - "CFPAT-1BFhL_2UiqD7Q2Z-azTipNT5RgZoqjq0U4UpQQuSTi4"; -const ENVIRONMENT = process.env.REACT_APP_ENVIRONMENT || "master"; - -const client = contentful.createClient({ - space: SPACE_ID, - accessToken: ACCESS_TOKEN, - environment: ENVIRONMENT, -}); - -const getTechnologies = () => - client.getEntries({ - content_type: "technology", - }); - -const getCities = () => - client.getEntries({ - content_type: "city", - }); - -const getAllJobs = () => client.getEntries({ - content_type: 'job', - limit: 1000, - skip: 0, - select: 'fields.position,fields.slug', - order: '-fields.displayPriority,-fields.dateDisplayed' -}); - -const getTechnologyNames = async () => { - const technologies = await getTechnologies(); - - if (technologies.items.length < 0) { - return false; - } - - return technologies.items.map((tech) => tech.fields.name.toLowerCase()); -}; - -const getCityNames = async () => { - const cities = await getCities(); - - if (cities.items.length < 0) { - return false; - } +const { paramsApplier } = require("react-router-sitemap"); - return cities.items.map((city) => city.fields.name.toLowerCase()); +// Configuration Constants +const CONTENTFUL_CONFIG = { + space: process.env.REACT_APP_SPACE_ID, + accessToken: process.env.REACT_APP_ACCESS_TOKEN, + environment: process.env.REACT_APP_ENVIRONMENT }; - -const getJobSlugs = async () => { - const jobs = await getAllJobs(1000, 0); - - if (jobs.items.length < 0) { - return false; +const client = contentful.createClient(CONTENTFUL_CONFIG); + +// Utility Functions +async function fetchContentfulEntries(contentType) { + try { + const entries = await client.getEntries({ content_type: contentType }); + return entries.items.length > 0 ? entries.items : []; + } catch (error) { + console.error(`Error fetching ${contentType} entries:`, error); + return []; } - - return jobs.items.map((job) => encodeURIComponent(job.fields.slug)); } +// Sitemap Generation Functions function generatePathsBasedOnRoute(route) { - const config = {}; - config[route.path] = [{ ...route.params }]; - + const config = { [route.path]: [{ ...route.params }] }; return paramsApplier([route.path], config); } function generateSitemap(routes) { const date = moment().format('YYYY-MM-DD'); const host = 'https://jobsforit.de'; + const xml = ` - ${routes - .map((route) => { - return `${host + route}${date}`; - }) - .join("")} - - `; + ${routes.map(route => `${host + route}${date}`).join("")} + `; return xmlFormatter(xml); } +// Main Execution async function sitemapGenerator() { - console.log('Started generating sitemap'); - const technologies = await getTechnologyNames(); - const cities = await getCityNames(); - const jobs = await getJobSlugs(); - - const routes = [ - { - path: "/filters/:tech/:city", - exact: true, - params: { - tech: technologies, - city: cities, - }, - }, - { - path: "/filters/:tech", - params: { - tech: technologies, - }, - }, - { - path: "/jobs/:id", - params: { - id: jobs - } - }, - { - path: "/choose-plan", - }, - { - path: "/token", - }, - { - path: "/pricing", - }, - { - path: "/", - }, - { - path: "/about", - }, - { - path: "/company", - }, - { - path: "/brand-room", - }, - { - path: "/imprint", - }, - { - path: "/edu-room", - }, - { - path: "/tutorials", - }, - { - path: "/meetup", - }, - { - path: "/meetups", - }, - { - path: "/study-material", - }, - { - path: "/study-material-single", - }, - { - path: "/blog", - }, - { - path: "/statistics", - }, - { - path: "/terms-and-conditions", - }, - { - path: "/privacy-policy", - }, - ]; + try { + console.log('Starting sitemap generation...'); + + const technologies = (await fetchContentfulEntries("technology")).map(tech => tech.fields.name.toLowerCase()); + const cities = (await fetchContentfulEntries("city")).map(city => city.fields.name.toLowerCase()); + const jobs = (await fetchContentfulEntries('job')).map(job => encodeURIComponent(job.fields.slug)); + + const routes = [ + { + path: "/filters/:tech/:city", + exact: true, + params: { + tech: technologies, + city: cities, + }, + }, + { + path: "/filters/:tech", + params: { + tech: technologies, + }, + }, + { + path: "/jobs/:id", + params: { + id: jobs + } + }, + { + path: "/choose-plan", + }, + { + path: "/token", + }, + { + path: "/pricing", + }, + { + path: "/", + }, + { + path: "/about", + }, + { + path: "/company", + }, + { + path: "/brand-room", + }, + { + path: "/imprint", + }, + { + path: "/edu-room", + }, + { + path: "/tutorials", + }, + { + path: "/meetup", + }, + { + path: "/meetups", + }, + { + path: "/study-material", + }, + { + path: "/study-material-single", + }, + { + path: "/blog", + }, + { + path: "/statistics", + }, + { + path: "/terms-and-conditions", + }, + { + path: "/privacy-policy", + }, + ]; - const newRoutes = []; + const newRoutes = routes.flatMap(generatePathsBasedOnRoute); - routes.forEach((route) => { - console.log(`Generating paths for: ${route.path}`); - newRoutes.push(...generatePathsBasedOnRoute(route)); - }); + console.log('Saving sitemap to public/sitemap.xml'); + fs.writeFileSync(path.join(process.cwd(), 'public', 'sitemap.xml'), generateSitemap(newRoutes)); + console.log('Sitemap generation completed.'); - console.log('Saving sitemap to public/sitemap.xml'); - fs.writeFileSync(path.join(process.cwd(), 'public', 'sitemap.xml'), generateSitemap(newRoutes)); - console.log('Sitemap successfully created'); + } catch (error) { + console.error('Error generating sitemap:', error); + } } sitemapGenerator(); diff --git a/src/themeContext.js b/src/themeContext.js index 6b57536..9a6eadd 100644 --- a/src/themeContext.js +++ b/src/themeContext.js @@ -2,15 +2,19 @@ import React from 'react'; const getSystemPreferredTheme = () => { const isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)"); + return isDarkTheme.matches ? 'dark' : 'light'; +} - if (isDarkTheme.matches) { - return 'dark'; +const getInitialTheme = () => { + try { + return localStorage.getItem('theme') ?? getSystemPreferredTheme(); + } catch (error) { + console.error("Failed to get theme from localStorage", error); + return getSystemPreferredTheme(); } - - return 'light'; } export const ThemeContext = React.createContext({ - theme: localStorage.getItem('theme') ?? 'dark', + theme: getInitialTheme(), toggleTheme: () => {} -}); \ No newline at end of file +});