commit ed08f4aca3a581586c8f4ecee7b29729f0d42864 Author: vitaliimulika-dev Date: Thu Jan 15 12:54:49 2026 +0200 Initial commit diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..02269f0 --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +DISABLE_ESLINT_PLUGIN=true diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..8687ee0 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,62 @@ +name: Build + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to build' + required: true + default: 'main' + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout branch + uses: actions/checkout@v3 + with: + ref: ${{ gitea.event.inputs.branch }} + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 24 + + - name: Install dependencies + run: | + set -euo pipefail + npm install --no-audit --silent 2>&1 | tee install.log + env: + NODE_OPTIONS: '--max-old-space-size=4096' + + - name: Build (react-scripts build) + env: + CI: 'false' + NODE_OPTIONS: '--max-old-space-size=4096' + run: | + set -euo pipefail + npm run build 2>&1 | tee build.log + timeout-minutes: 5 + + - name: Verify build folder exists + run: test -d build || (echo "No build folder. Check build logs above."; exit 1) + + - name: Upload logs on failure + if: failure() + uses: actions/upload-artifact@v3 + with: + name: build-logs + path: | + install.log + build.log + npm-debug.log* + if-no-files-found: ignore + + - name: Build completed + if: success() + run: echo "Build completed successfully" diff --git a/package.json b/package.json new file mode 100644 index 0000000..870a5a4 --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "nestjs-jobs", + "version": "0.1.0", + "private": true, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-scripts": "^5.0.1", + "framer-motion": "^11.0.0", + "lucide-react": "^0.400.0" + }, + "devDependencies": { + "tailwindcss": "^3.4.0", + "postcss": "^8.4.0", + "autoprefixer": "^10.4.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..96bb01e --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..70c8f41 --- /dev/null +++ b/public/index.html @@ -0,0 +1,1235 @@ + + + + + + + + + + + + NestJS Jobs - Official Job Board + + + +
+ + + + + \ No newline at end of file diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..2079616 --- /dev/null +++ b/src/App.js @@ -0,0 +1,26 @@ +import React from 'react'; +import Header from './components/Header'; +import Hero from './components/Hero'; +import JobsSection from './components/JobsSection'; +import TeamAugmentation from './components/TeamAugmentation'; +import CompaniesSection from './components/CompaniesSection'; +import SupportSection from './components/SupportSection'; +import Newsletter from './components/Newsletter'; +import Footer from './components/Footer'; + +function App() { + return ( +
+
+ + + + + + +
+
+ ); +} + +export default App; \ No newline at end of file diff --git a/src/components/CompaniesSection.js b/src/components/CompaniesSection.js new file mode 100644 index 0000000..57a378a --- /dev/null +++ b/src/components/CompaniesSection.js @@ -0,0 +1,164 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +const fadeUpPreset = (delay = 0, duration = 1.2) => ({ + initial: { opacity: 0, y: 20 }, + whileInView: { opacity: 1, y: 0 }, + viewport: { once: true, amount: 0.2 }, + transition: { delay, duration, ease: "easeOut" } +}); + +const CompaniesSection = () => { + const shouldReduceMotion = useReducedMotion(); + + const companies = [ + { + name: "Sanofi", + logo: "https://www.jobs.nestjs.com/img/logos/sanofi.png", + url: "https://www.sanofi.com/" + }, + { + name: "Adidas", + logo: "https://www.jobs.nestjs.com/img/logos/adidas.svg", + url: "https://adidas.com/" + }, + { + name: "Autodesk", + logo: "https://www.jobs.nestjs.com/img/logos/autodesk.png", + url: "https://www.autodesk.com/" + }, + { + name: "Mercedes", + logo: "https://www.jobs.nestjs.com/img/logos/mercedes.png", + url: "https://www.mercedes-benz.com/" + }, + { + name: "GitLab", + logo: "https://www.jobs.nestjs.com/img/logos/gitlab.png", + url: "https://about.gitlab.com/" + }, + { + name: "Red Hat", + logo: "https://www.jobs.nestjs.com/img/logos/red-hat.svg", + url: "https://www.redhat.com/" + }, + { + name: "Société Générale", + logo: "https://www.jobs.nestjs.com/img/logos/societe-generale-logo.png", + url: "https://www.societegenerale.fr/" + }, + { + name: "Roche", + logo: "https://www.jobs.nestjs.com/img/logos/roche-logo.png", + url: "https://roche.com/" + }, + { + name: "Decathlon", + logo: "https://www.jobs.nestjs.com/img/logos/decathlon.png", + url: "https://www.decathlon.com/" + }, + { + name: "REWE", + logo: "https://www.jobs.nestjs.com/img/logos/rewe.svg", + url: "https://www.rewe-digital.com/" + }, + { + name: "TotalEnergies", + logo: "https://www.jobs.nestjs.com/img/logos/totalenergies.svg", + url: "https://totalenergies.com/" + }, + { + name: "Capgemini", + logo: "https://www.jobs.nestjs.com/img/logos/capgemini.svg", + url: "https://capgemini.com/" + }, + { + name: "BMW", + logo: "https://www.jobs.nestjs.com/img/logos/bmw.svg", + url: null + }, + { + name: "IBM", + logo: "https://www.jobs.nestjs.com/img/logos/ibm.svg", + url: "https://www.ibm.com/" + }, + { + name: "JetBrains", + logo: "https://www.jobs.nestjs.com/img/logos/jetbrains.svg", + url: "https://www.jetbrains.com/" + } + ]; + + const CompanyLogo = ({ company, index }) => { + const logoContent = ( +
+ {company.name} +
+ ); + + if (company.url) { + const wrappedContent = ( + + {logoContent} + + ); + + if (shouldReduceMotion) { + return wrappedContent; + } + + return ( + + {wrappedContent} + + ); + } + + if (shouldReduceMotion) { + return logoContent; + } + + return ( + + {logoContent} + + ); + }; + + const sectionContent = ( +
+
+
+

+ Who is using Nest? +

+

+ Nest is proudly powering a large ecosystem of enterprises and products out there. Wanna see your logo here? Find out more. +

+
+ +
+ {companies.map((company, index) => ( + + ))} +
+
+
+ ); + + if (shouldReduceMotion) { + return sectionContent; + } + + return ( + + {sectionContent} + + ); +}; + +export default CompaniesSection; \ No newline at end of file diff --git a/src/components/Footer.js b/src/components/Footer.js new file mode 100644 index 0000000..b30504d --- /dev/null +++ b/src/components/Footer.js @@ -0,0 +1,40 @@ +import React from 'react'; +import { Github, X } from 'lucide-react'; + +const Footer = () => { + return ( + + ); +}; + +export default Footer; \ No newline at end of file diff --git a/src/components/Header.js b/src/components/Header.js new file mode 100644 index 0000000..53d935e --- /dev/null +++ b/src/components/Header.js @@ -0,0 +1,82 @@ +import React, { useState } from 'react'; +import { Menu, X, ChevronDown, Github } from 'lucide-react'; + +const Header = () => { + const [isMenuOpen, setIsMenuOpen] = useState(false); + + return ( +
+
+
+ {/* Logo */} +
+ + NestJS - A progressive Node.js framework + +
+ + {/* Desktop Navigation */} + + + {/* Mobile menu button */} +
+ +
+
+ + {/* Mobile Navigation */} + {isMenuOpen && ( +
+
+ + HOME + + + JOBS + + + SIGN IN + + + RESOURCES + +
+
+ )} +
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/src/components/Hero.js b/src/components/Hero.js new file mode 100644 index 0000000..12806a5 --- /dev/null +++ b/src/components/Hero.js @@ -0,0 +1,85 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +const fadeUpPreset = (delay = 0, duration = 1.2) => ({ + initial: { opacity: 0, y: 20 }, + whileInView: { opacity: 1, y: 0 }, + viewport: { once: true, amount: 0.2 }, + transition: { delay, duration, ease: "easeOut" } +}); + +const Hero = () => { + const shouldReduceMotion = useReducedMotion(); + + if (shouldReduceMotion) { + return ( +
+
+
+
+
+ +
+
+

+ Official NestJS job board +

+

+ Discover companies looking for developers with NestJS experience and find your next role. +

+
+ + +
+
+
+
+ ); + } + + return ( + +
+
+
+
+ +
+
+ + Official NestJS job board + + + Discover companies looking for developers with NestJS experience and find your next role. + + + + + +
+
+
+ ); +}; + +export default Hero; \ No newline at end of file diff --git a/src/components/JobsSection.js b/src/components/JobsSection.js new file mode 100644 index 0000000..19d7ba1 --- /dev/null +++ b/src/components/JobsSection.js @@ -0,0 +1,250 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; +import { Search, Globe, MapPin } from 'lucide-react'; + +const fadeUpPreset = (delay = 0, duration = 1.2) => ({ + initial: { opacity: 0, y: 20 }, + whileInView: { opacity: 1, y: 0 }, + viewport: { once: true, amount: 0.2 }, + transition: { delay, duration, ease: "easeOut" } +}); + +const JobCard = ({ job, index }) => { + const shouldReduceMotion = useReducedMotion(); + + const cardContent = ( +
+
+
+ {job.company} +
+

{job.title}

+

at {job.company}

+ {job.featured && ( + ✨ FEATURED + )} +
+
+ {job.timeAgo} +
+ +
+
+ + {job.location} +
+ {job.salary && ( +
+ 💰 + {job.salary} +
+ )} + {job.flag && ( + {job.flag} + )} +
+
+ ); + + if (shouldReduceMotion) { + return cardContent; + } + + return ( + + {cardContent} + + ); +}; + +const JobsSection = () => { + const shouldReduceMotion = useReducedMotion(); + + const jobs = [ + { + id: 1, + title: "Senior Product Engineer", + company: "Pandektes", + location: "Remote", + flag: "🇩🇰 Denmark", + salary: "$120k - $150k", + timeAgo: "2 hours ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/65dc7731-a4bb-4bbe-bc20-7401691a9ec5.png", + featured: false + }, + { + id: 2, + title: "Senior Software Engineer", + company: "Trilon", + location: "Remote", + flag: "🌏 Worldwide", + timeAgo: "25 days ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/612b8b42-9610-43b1-92bd-15373110159f.png", + featured: true + } + ]; + + const expiredJobs = [ + { + id: 3, + title: "Full-Stack Developer Angular/NestJS/PHP/MySQL", + company: "Nylon Technology", + location: "Remote", + flag: "🇺🇸 United States, Continental...", + salary: "$100k - $130k", + timeAgo: "a month ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/c54da9a9-c7aa-4d9a-838d-1021b684d957.jpeg" + }, + { + id: 4, + title: "Full Stack Typescript Developer (NextJs, NestJs)", + company: "ClickTech", + location: "Remote", + flag: "🌏 Worldwide", + salary: "$40k - $50k", + timeAgo: "2 months ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/3e62a3bc-0bae-4f8b-8e5a-4b14db5ef4ab.jpeg" + }, + { + id: 5, + title: "NestJS Testing Specialist (Freelancer/Consultant)", + company: "Kapital", + location: "🌏 Worldwide", + timeAgo: "2 months ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/6e1385c3-7fd6-4444-8fc4-908d4aef24be.png" + }, + { + id: 6, + title: "Full-Stack Engineer (Mid-Senior) [NestJS, NextJS]", + company: "OASYS NOW", + location: "🇳🇱 Netherlands, Delft", + salary: "$60k - $80k", + timeAgo: "2 months ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/696a0fcf-7bdb-44e2-bb03-dbdb0fb2e18b.jpeg" + }, + { + id: 7, + title: "Senior Backend Engineer", + company: "Pandektes", + location: "Remote", + flag: "🇩🇰 Denmark, Copenhagen", + salary: "$110k - $140k", + timeAgo: "2 months ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/65dc7731-a4bb-4bbe-bc20-7401691a9ec5.png" + }, + { + id: 8, + title: "Backend NestJS Developer", + company: "ClickTech", + location: "Remote", + flag: "🌏 Worldwide", + salary: "$40k - $60k", + timeAgo: "6 months ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/3e62a3bc-0bae-4f8b-8e5a-4b14db5ef4ab.jpeg" + }, + { + id: 9, + title: "Senior Software Engineer", + company: "Pippen AI", + location: "Remote", + flag: "🇨🇦 Canada, Toronto", + salary: "$70k - $100k", + timeAgo: "6 months ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/69388f1e-2dff-444b-9fc4-9f035bbf654f.jpeg" + } + ]; + + const sectionContent = ( +
+
+
+

+ Find your next opportunity +

+

+ Browse through our list of NestJS jobs, find your perfect match and apply. Use filters for more accurate results! +

+ +
+

Get NestJS jobs right to your inbox

+

Subscribe to our newsletter to get notified.

+
+ + +
+
+
+ + {/* Search and Filters */} +
+
+
+ + +
+ + + +
+
+ + {/* Active Jobs */} +
+ {jobs.map((job, index) => ( + + ))} +
+ + {/* Expired Jobs */} +
+

Expired listings

+
+ {expiredJobs.map((job, index) => ( + + ))} +
+
+
+
+ ); + + if (shouldReduceMotion) { + return sectionContent; + } + + return ( + + {sectionContent} + + ); +}; + +export default JobsSection; \ No newline at end of file diff --git a/src/components/Newsletter.js b/src/components/Newsletter.js new file mode 100644 index 0000000..a8dcc63 --- /dev/null +++ b/src/components/Newsletter.js @@ -0,0 +1,49 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +const fadeUpPreset = (delay = 0, duration = 1.2) => ({ + initial: { opacity: 0, y: 20 }, + whileInView: { opacity: 1, y: 0 }, + viewport: { once: true, amount: 0.2 }, + transition: { delay, duration, ease: "easeOut" } +}); + +const Newsletter = () => { + const shouldReduceMotion = useReducedMotion(); + + const sectionContent = ( +
+
+

+ Join our Newsletter +

+

+ Subscribe to stay up to date with the latest Nest updates, features, and videos! +

+ +
+ + +
+
+
+ ); + + if (shouldReduceMotion) { + return sectionContent; + } + + return ( + + {sectionContent} + + ); +}; + +export default Newsletter; \ No newline at end of file diff --git a/src/components/SupportSection.js b/src/components/SupportSection.js new file mode 100644 index 0000000..03eb74c --- /dev/null +++ b/src/components/SupportSection.js @@ -0,0 +1,41 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +const fadeUpPreset = (delay = 0, duration = 1.2) => ({ + initial: { opacity: 0, y: 20 }, + whileInView: { opacity: 1, y: 0 }, + viewport: { once: true, amount: 0.2 }, + transition: { delay, duration, ease: "easeOut" } +}); + +const SupportSection = () => { + const shouldReduceMotion = useReducedMotion(); + + const sectionContent = ( +
+
+

+ Does your team need additional support? +

+

+ Nest core team members can work directly with your team on a daily basis to help take your project to the next-level. Let us partner with you and your team to develop the most ambitious projects. +

+ +
+
+ ); + + if (shouldReduceMotion) { + return sectionContent; + } + + return ( + + {sectionContent} + + ); +}; + +export default SupportSection; \ No newline at end of file diff --git a/src/components/TeamAugmentation.js b/src/components/TeamAugmentation.js new file mode 100644 index 0000000..8e1f2b3 --- /dev/null +++ b/src/components/TeamAugmentation.js @@ -0,0 +1,49 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +const fadeUpPreset = (delay = 0, duration = 1.2) => ({ + initial: { opacity: 0, y: 20 }, + whileInView: { opacity: 1, y: 0 }, + viewport: { once: true, amount: 0.2 }, + transition: { delay, duration, ease: "easeOut" } +}); + +const TeamAugmentation = () => { + const shouldReduceMotion = useReducedMotion(); + + const sectionContent = ( +
+
+
+
+
+

+ Team augmentation. By your side at every step +

+

+ Nest core team members can work directly with your team on a daily basis to help take your project to the next-level. Let us partner with you and your team to develop the most ambitious projects. +

+ +
+
+
+
+
+
+
+ ); + + if (shouldReduceMotion) { + return sectionContent; + } + + return ( + + {sectionContent} + + ); +}; + +export default TeamAugmentation; \ No newline at end of file diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..5b00510 --- /dev/null +++ b/src/index.css @@ -0,0 +1,31 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply font-sans antialiased; + } +} + +@layer components { + .btn-primary { + @apply bg-nest-red hover:bg-red-600 text-white px-6 py-3 rounded-full font-medium transition-colors duration-200; + } + + .btn-secondary { + @apply border-2 border-white text-white hover:bg-white hover:text-nest-dark px-6 py-3 rounded-full font-medium transition-all duration-200; + } + + .job-card { + @apply bg-white rounded-lg border border-gray-200 p-6 hover:shadow-lg transition-shadow duration-200; + } + + .company-logo { + @apply w-12 h-12 rounded-lg object-contain bg-gray-50 p-2; + } + + .featured-badge { + @apply bg-gradient-to-r from-purple-500 to-pink-500 text-white px-3 py-1 rounded-full text-sm font-medium; + } +} \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..0881df3 --- /dev/null +++ b/src/index.js @@ -0,0 +1,13 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App'; + +const container = document.getElementById('root'); +const root = createRoot(container); + +root.render( + + + +); \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..98f6927 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,24 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./src/**/*.{js,jsx,ts,tsx}", + ], + theme: { + extend: { + colors: { + 'nest-red': '#e53e3e', + 'nest-pink': '#e53e3e', + 'nest-dark': '#1a1a1a', + 'nest-gray': '#6b7280', + 'nest-light-gray': '#f3f4f6' + }, + fontFamily: { + 'sans': ['Inter', 'system-ui', 'sans-serif'] + }, + backgroundImage: { + 'hero-gradient': 'linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%)' + } + }, + }, + plugins: [], +} \ No newline at end of file diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..760984a --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "installCommand": "npm install", + "buildCommand": "CI=false npm run build", + "outputDirectory": "build" +} \ No newline at end of file