first task

This commit is contained in:
2025-01-01 14:21:33 +02:00
commit 46385a838d
26 changed files with 6950 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

8
README.md Normal file
View File

@@ -0,0 +1,8 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

38
eslint.config.js Normal file
View File

@@ -0,0 +1,38 @@
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
settings: { react: { version: '18.3' } },
plugins: {
react,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...js.configs.recommended.rules,
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React Course </title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

6391
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

36
package.json Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "react-course",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview",
"server": "json-server, --watch scr/jobs.json -- port 8000"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.4.0",
"react-router-dom": "^6.28.1",
"react-spinners": "^0.15.0"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"eslint": "^9.17.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"json-server": "^1.0.0-beta.3",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"vite": "^6.0.5"
}
}

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

1
public/vite.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

22
src/App.jsx Normal file
View File

@@ -0,0 +1,22 @@
import {Route, createBrowserRouter, createRoutesFromElements, RouterProvider} from 'react-router-dom'
import homepage from './pages/homepage'
import mainlayout from './layouts/mainlayout'
import jobspage from './pages/jobspage';
import notfoundpage from './pages/notfoundpage';
const router = createBrowserRouter(
createRoutesFromElements(
<Route path = '/' element = {<mainlayout />}>
<Route index element = {<homepage />} />
<Route path = '/jobs' element = {<jobspage />} />
<Route path = '*' element = {<notfoundpage />} />
</Route>
)
);
const App = () => {
return <RouterProvider router={router} />;
};
export default App

BIN
src/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

9
src/components/card.jsx Normal file
View File

@@ -0,0 +1,9 @@
import React from 'react'
const card = ({children, bg = 'bg-gray-100'}) => {
return <div className={`${bg} p-6 rounded-lg shadow-md`}>
{children}
</div>;
}
export default card

24
src/components/hero.jsx Normal file
View File

@@ -0,0 +1,24 @@
import React from 'react'
const hero = () => {
return (
<section className="bg-indigo-700 py-20 mb-4">
<div
className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col items-center"
>
<div className="text-center">
<h1
className="text-4xl font-extrabold text-white sm:text-5xl md:text-6xl"
>
Become a React Dev
</h1>
<p className="my-4 text-xl text-white">
Find the React job that fits your skills and needs
</p>
</div>
</div>
</section>
)
}
export default hero

View File

@@ -0,0 +1,40 @@
import React from 'react'
import card from './card'
import {Link} from 'react-router-dom'
const homecards = () => {
return (
<section className="py-4">
<div className="container-xl lg:container m-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 p-4 rounded-lg">
<card>
<h2 className="text-2xl font-bold">For Developers</h2>
<p className="mt-2 mb-4">
Browse our React jobs and start your career today
</p>
<Link
to="/jobs"
className="inline-block bg-black text-white rounded-lg px-4 py-2 hover:bg-gray-700"
>
Browse Jobs
</Link>
</card>
<card bg = 'bg-indigo-100'>
<h2 className="text-2xl font-bold">For Employers</h2>
<p className="mt-2 mb-4">
List your job to find the perfect developer for the role
</p>
<Link
tp="/add-job"
className="inline-block bg-indigo-500 text-white rounded-lg px-4 py-2 hover:bg-indigo-600"
>
Add Job
</Link>
</card>
</div>
</div>
</section>
)
}
export default homecards

View File

@@ -0,0 +1,51 @@
import React from 'react'
import { use } from 'react'
import { useState } from 'react'
import {FaMapMarker } from 'react-icons/fa'
import {Link} from 'react-router-dom';
const joblisting = ({job}) => {
const [showFullDescription, setShowFullDescription] = useState(false);
let description = job.description;
if (!showFullDescription)
{
description = description.substring(0,90) + '...';
}
return (
<div className="bg-white rounded-xl shadow-md relative">
<div className="p-4">
<div className="mb-6">
<div className="text-gray-600 my-2">{job.type}</div>
<h3 className="text-xl font-bold">{job.title}</h3>
</div>
<div className="mb-5">{description}
<button onClick = {() => setShowFullDescription ((prevstate) => !prevstate)} className='text-indigo-500 mb-5 hover:text-indigo-600'>
{showFullDescription ? 'Less' : 'More'}
</button>
</div>
<h3 className="text-indigo-500 mb-2">{job.salary} / Year</h3>
<div className="border border-gray-100 mb-5"></div>
<div className="flex flex-col lg:flex-row justify-between mb-4">
<div className="text-orange-700 mb-3">
<FaMapMarker className='inline-text-lg mg-1 mr-1' />
{job.location}
</div>
<Link
to={`/job/${job.id}`}
className="h-[36px] bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded-lg text-center text-sm"
>
Read More
</Link>
</div>
</div>
</div>
)
}
export default joblisting

View File

@@ -0,0 +1,24 @@
import React from 'react'
import jobs from '../jobs.json'
import joblisting from './joblistings'
const joblistings = () => {
const recentjobs = jobs.slice(0,3);
return (
<section className="bg-blue-50 px-4 py-10">
<div className="container-xl lg:container m-auto">
<h2 className="text-3xl font-bold text-indigo-500 mb-6 text-center">
Browse Jobs
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{recentjobs.map((job) => (
<joblisting key = {job.id} job ={job} />
))}
</div>
</div>
</section>
)
}
export default joblistings

55
src/components/navbar.jsx Normal file
View File

@@ -0,0 +1,55 @@
import React from 'react'
import logo from '../assets/images/logo.png';
import {NavLink} from 'react-router-dom';
const navbar = () => {
const linkclass = ({isActive}) =>
isActive ? 'bg-black text-white hover:bg-gray-900 hover:text-white rounded-md px-3 py-2' : 'text-white hover:bg-gray-900 hover:text-white rounded-md px-3 py-2'
return (
<nav className="bg-indigo-700 border-b border-indigo-500">
<div className="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
<div className="flex h-20 items-center justify-between">
<div
className="flex flex-1 items-center justify-center md:items-stretch md:justify-start"
>
{/* |<!-- Logo --> */}
<NavLink
className="flex flex-shrink-0 items-center mr-4" to="/">
<img
className="h-10 w-auto"
src= {logo}
alt="React Jobs"
/>
<span className="hidden md:block text-white text-2xl font-bold ml-2"
>React Jobs</span
>
</NavLink>
<div className="md:ml-auto">
<div className="flex space-x-2">
<NavLink
to="/"
className={linkclass}
>Home
</NavLink>
<NavLink
to="/jobs"
className={linkclass}
>Jobs
</NavLink>
<NavLink
to="/add-job"
className={linkclass}
>Add Job
</NavLink>
</div>
</div>
</div>
</div>
</div>
</nav>
)
};
export default navbar

View File

@@ -0,0 +1,17 @@
import React from 'react'
import {Link} from 'react-router-dom'
const viewalljobs = () => {
return (
<section className="m-auto max-w-lg my-10 px-6">
<Link
to="/jobs"
className="block bg-black text-white text-center py-4 px-6 rounded-xl hover:bg-gray-700"
>View All Jobs
</Link>
</section>
)
}
export default viewalljobs

3
src/index.css Normal file
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

86
src/jobs.json Normal file
View File

@@ -0,0 +1,86 @@
[
{
"id": "1",
"title": "Senior React Developer",
"type": "Full-Time",
"description": "We are seeking a talented Front-End Developer to join our team in Boston, MA. The ideal candidate will have strong skills in HTML, CSS, and JavaScript, with experience working with modern JavaScript frameworks such as React or Angular.",
"location": "Boston, MA",
"salary": "$70K - $80K",
"company": {
"name": "NewTek Solutions",
"description": "NewTek Solutions is a leading technology company specializing in web development and digital solutions. We pride ourselves on delivering high-quality products and services to our clients while fostering a collaborative and innovative work environment.",
"contactEmail": "contact@teksolutions.com",
"contactPhone": "555-555-5555"
}
},
{
"id": "2",
"title": "Front-End Engineer (React & Redux)",
"type": "Full-Time",
"location": "Miami, FL",
"description": "Join our team as a Front-End Developer in sunny Miami, FL. We are looking for a motivated individual with a passion for crafting beautiful and responsive web applications. Experience with UI/UX design principles and a strong attention to detail are highly desirable.",
"salary": "$70K - $80K",
"company": {
"name": "Veneer Solutions",
"description": "Veneer Solutions is a creative agency specializing in digital design and development. Our team is dedicated to pushing the boundaries of creativity and innovation to deliver exceptional results for our clients.",
"contactEmail": "contact@loremipsum.com",
"contactPhone": "555-555-5555"
}
},
{
"id": "3",
"title": "React.js Dev",
"type": "Full-Time",
"location": "Brooklyn, NY",
"description": "Are you passionate about front-end development? Join our team in vibrant Brooklyn, NY, and work on exciting projects that make a difference. We offer competitive compensation and a collaborative work environment where your ideas are valued.",
"salary": "$70K - $80K",
"company": {
"name": "Dolor Cloud",
"description": "Dolor Cloud is a leading technology company specializing in digital solutions for businesses of all sizes. With a focus on innovation and customer satisfaction, we are committed to delivering cutting-edge products and services.",
"contactEmail": "contact@dolorsitamet.com",
"contactPhone": "555-555-5555"
}
},
{
"id": "4",
"title": "React Front-End Developer",
"type": "Part-Time",
"description": "Join our team as a Part-Time Front-End Developer in beautiful Pheonix, AZ. We are looking for a self-motivated individual with a passion for creating engaging user experiences. This position offers flexible hours and the opportunity to work remotely.",
"location": "Pheonix, AZ",
"salary": "$60K - $70K",
"company": {
"name": "Alpha Elite",
"description": "Alpha Elite is a dynamic startup specializing in digital marketing and web development. We are committed to fostering a diverse and inclusive workplace where creativity and innovation thrive.",
"contactEmail": "contact@adipisicingelit.com",
"contactPhone": "555-555-5555"
}
},
{
"id": "5",
"title": "Full Stack React Developer",
"type": "Full-Time",
"description": "Exciting opportunity for a Full-Time Front-End Developer in bustling Atlanta, GA. We are seeking a talented individual with a passion for building elegant and scalable web applications. Join our team and make an impact!",
"location": "Atlanta, GA",
"salary": "$90K - $100K",
"company": {
"name": "Browning Technologies",
"description": "Browning Technologies is a rapidly growing technology company specializing in e-commerce solutions. We offer a dynamic and collaborative work environment where employees are encouraged to think creatively and innovate.",
"contactEmail": "contact@consecteturadipisicing.com",
"contactPhone": "555-555-5555"
}
},
{
"id": "6",
"title": "React Native Developer",
"type": "Full-Time",
"description": "Join our team as a Front-End Developer in beautiful Portland, OR. We are looking for a skilled and enthusiastic individual to help us create innovative web solutions. Competitive salary and great benefits package available.",
"location": "Portland, OR",
"salary": "$100K - $110K",
"company": {
"name": "Port Solutions INC",
"description": "Port Solutions is a leading technology company specializing in software development and digital marketing. We are committed to providing our clients with cutting-edge solutions and our employees with a supportive and rewarding work environment.",
"contactEmail": "contact@ipsumlorem.com",
"contactPhone": "555-555-5555"
}
}
]

View File

@@ -0,0 +1,15 @@
import { Outlet, replace } from "react-router-dom";
import navbar from "../components/navbar";
import React from 'react'
const mainlayout = () => {
return (
<>
<navbar />
<Outlet />
</>
);
}
export default mainlayout

10
src/main.jsx Normal file
View File

@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)

18
src/pages/homepage.jsx Normal file
View File

@@ -0,0 +1,18 @@
import React from 'react';
import hero from '../components/hero';
import homecards from '../components/homecards';
import joblistings from '../components/joblistings';
import viewalljobs from '../components/viewalljobs';
const homepage = () => {
return (
<>
<hero />
<homecards />
<joblistings />
<viewalljobs />
</>
);
}
export default homepage

11
src/pages/jobspage.jsx Normal file
View File

@@ -0,0 +1,11 @@
import React from 'react'
const jobspage = () => {
return (
<div>
</div>
)
}
export default jobspage

View File

@@ -0,0 +1,20 @@
import React from 'react'
import {Link} from 'react-router-dom';
import { FaExclamationTriangle } from 'react-icons/fa';
const notfoundpage = () => {
return (
<section className="text-center flex flex-col justify-center items-center h-96">
<FaExclamationTriangle className='text-yellow-400 text-6xl mb-4' />
<h1 className="text-6xl font-bold mb-4">404 Not Found</h1>
<p className="text-xl mb-5">This page does not exist</p>
<Link
tp="/"
className="text-white bg-indigo-700 hover:bg-indigo-900 rounded-md px-3 py-2 mt-4"
>Go Back</Link
>
</section>
)
}
export default notfoundpage

18
tailwind.config.js Normal file
View File

@@ -0,0 +1,18 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
fontFamily: {
sans: ['Roboto', 'sans-serif']
},
gridTemplateColumns: {
'70/30': '70% 28%',
},
},
},
plugins: [],
};

10
vite.config.js Normal file
View File

@@ -0,0 +1,10 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
port: 3000,
},
})