commit e4640a588461dd8e5dbe1e50327599f14aa02331 Author: vitaliimulika-dev Date: Thu Jan 15 14:05:51 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..c355b23 --- /dev/null +++ b/src/components/CompaniesSection.js @@ -0,0 +1,171 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +function CompaniesSection() { + const shouldReduce = useReducedMotion(); + + 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 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: "Red Hat", + logo: "https://www.jobs.nestjs.com/img/logos/red-hat.svg", + url: "https://www.redhat.com/" + }, + { + name: "Roche", + logo: "https://www.jobs.nestjs.com/img/logos/roche-logo.png", + url: "https://roche.com/" + }, + { + name: "IBM", + logo: "https://www.jobs.nestjs.com/img/logos/ibm.svg", + url: "https://www.ibm.com/" + }, + { + name: "Decathlon", + logo: "https://www.jobs.nestjs.com/img/logos/decathlon.png", + url: "https://www.decathlon.com/" + }, + { + name: "BMW", + logo: "https://www.jobs.nestjs.com/img/logos/bmw.svg", + url: "" + }, + { + name: "SociΓ©tΓ© GΓ©nΓ©rale", + logo: "https://www.jobs.nestjs.com/img/logos/societe-generale-logo.png", + url: "https://www.societegenerale.fr/" + }, + { + name: "TotalEnergies", + logo: "https://www.jobs.nestjs.com/img/logos/totalenergies.svg", + url: "https://totalenergies.com/" + }, + { + name: "REWE", + logo: "https://www.jobs.nestjs.com/img/logos/rewe.svg", + url: "https://www.rewe-digital.com/" + }, + { + name: "Capgemini", + logo: "https://www.jobs.nestjs.com/img/logos/capgemini.svg", + url: "https://capgemini.com/" + }, + { + name: "JetBrains", + logo: "https://www.jobs.nestjs.com/img/logos/jetbrains.svg", + url: "https://www.jetbrains.com/" + }, + { + name: "GitLab", + logo: "https://www.jobs.nestjs.com/img/logos/gitlab.png", + url: "https://about.gitlab.com/" + } + ]; + + const CompanyLogo = ({ company, index }) => { + const LogoComponent = shouldReduce ? 'div' : motion.div; + const logoProps = shouldReduce ? {} : fadeUpPreset(index * 0.05, 0.6); + + const logoElement = ( + {company.name} + ); + + return ( + + {company.url ? ( + + {logoElement} + + ) : ( + logoElement + )} + + ); + }; + + if (shouldReduce) { + return ( +
+
+
+

+ 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) => ( + + ))} +
+
+
+ ); + } + + return ( + +
+ +

+ 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) => ( + + ))} +
+
+
+ ); +} + +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..3c25e21 --- /dev/null +++ b/src/components/Footer.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { Github } from 'lucide-react'; + +function 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..54c36c5 --- /dev/null +++ b/src/components/Header.js @@ -0,0 +1,73 @@ +import React, { useState } from 'react'; +import { Menu, X, ChevronDown, Github } from 'lucide-react'; + +function Header() { + const [isMenuOpen, setIsMenuOpen] = useState(false); + const [isResourcesOpen, setIsResourcesOpen] = 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..72d4f21 --- /dev/null +++ b/src/components/Hero.js @@ -0,0 +1,89 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +function Hero() { + const shouldReduce = useReducedMotion(); + + 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" } + }); + + if (shouldReduce) { + 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..f6a0c9a --- /dev/null +++ b/src/components/JobsSection.js @@ -0,0 +1,309 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; +import { Search, Globe, MapPin, Clock } from 'lucide-react'; + +function JobsSection() { + const shouldReduce = useReducedMotion(); + + 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 jobs = [ + { + id: 1, + title: "Senior Product Engineer", + company: "Pandektes", + location: "Remote", + country: "πŸ‡©πŸ‡° Denmark", + salary: "$120k - $150k", + time: "4 hours ago", + featured: false, + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/65dc7731-a4bb-4bbe-bc20-7401691a9ec5.png" + }, + { + id: 2, + title: "Senior Software Engineer", + company: "Trilon", + location: "Remote", + country: "🌏 Worldwide", + salary: "", + time: "25 days ago", + featured: true, + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/612b8b42-9610-43b1-92bd-15373110159f.png" + } + ]; + + const expiredJobs = [ + { + id: 3, + title: "Full-Stack Developer Angular/NestJS/PHP/MySQL", + company: "Nylon Technology", + location: "Remote", + country: "πŸ‡ΊπŸ‡Έ United States, Continental...", + salary: "$100k - $130k", + time: "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", + country: "🌏 Worldwide", + salary: "$40k - $50k", + time: "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", + country: "", + salary: "", + time: "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", + country: "", + salary: "$60k - $80k", + time: "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", + country: "πŸ‡©πŸ‡° Denmark, Copenhagen", + salary: "$110k - $140k", + time: "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", + country: "🌏 Worldwide", + salary: "$40k - $60k", + time: "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", + country: "πŸ‡¨πŸ‡¦ Canada, Toronto", + salary: "$70k - $100k", + time: "6 months ago", + logo: "https://nestjs-jobs-bucket-localhost.s3.amazonaws.com/69388f1e-2dff-444b-9fc4-9f035bbf654f.jpeg" + } + ]; + + const JobCard = ({ job, index, isExpired = false }) => { + const CardComponent = shouldReduce ? 'div' : motion.div; + const cardProps = shouldReduce ? {} : fadeUpPreset(index * 0.08, 0.6); + + return ( + +
+
+
+ {job.company} +
+
+
+

{job.title}

+ {job.featured && ( + ✨ FEATURED + )} +
+

at {job.company}

+
+ + + {job.location} + + {job.country && ( + + + {job.country} + + )} + {job.salary && ( + + πŸ’° {job.salary} + + )} +
+
+
+
+ + {job.time} +
+
+
+ ); + }; + + if (shouldReduce) { + return ( +
+
+
+

+ 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 +
+
+ + Location +
+
+ πŸ₯ Remote +
+
+ πŸ“ˆ Salary +
+
+
+ +
+ {jobs.map((job, index) => ( + + ))} +
+ +
+

Expired listings

+
+ {expiredJobs.map((job, index) => ( + + ))} +
+
+
+
+ ); + } + + return ( + +
+ +

+ 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 +
+
+ + Location +
+
+ πŸ₯ Remote +
+
+ πŸ“ˆ Salary +
+
+
+ +
+ {jobs.map((job, index) => ( + + ))} +
+ + +

Expired listings

+
+ {expiredJobs.map((job, index) => ( + + ))} +
+
+
+
+ ); +} + +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..235e9b4 --- /dev/null +++ b/src/components/Newsletter.js @@ -0,0 +1,85 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; +import { Send } from 'lucide-react'; + +function Newsletter() { + const shouldReduce = useReducedMotion(); + + 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" } + }); + + if (shouldReduce) { + return ( +
+
+
+

+ Join our Newsletter +

+

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

+
+ +
+
+ + +
+
+
+
+ ); + } + + return ( + +
+ +

+ Join our Newsletter +

+

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

+
+ + +
+ + +
+
+
+
+ ); +} + +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..26e7a06 --- /dev/null +++ b/src/components/SupportSection.js @@ -0,0 +1,61 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +function SupportSection() { + const shouldReduce = useReducedMotion(); + + 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" } + }); + + if (shouldReduce) { + return ( +
+
+

+ 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. +

+ +
+
+ ); + } + + return ( + +
+ + 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. + + + Contact us + +
+
+ ); +} + +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..4e0a5f5 --- /dev/null +++ b/src/components/TeamAugmentation.js @@ -0,0 +1,77 @@ +import React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +function TeamAugmentation() { + const shouldReduce = useReducedMotion(); + + 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" } + }); + + if (shouldReduce) { + return ( +
+
+
+
+ +
+
+
+

+ 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. +

+ +
+
+
+
+
+
+
+ ); + } + + return ( + +
+
+
+ +
+
+ +

+ 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. +

+ +
+ +
+
+
+
+
+ ); +} + +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..35caf17 --- /dev/null +++ b/src/index.css @@ -0,0 +1,31 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply font-sans bg-white text-gray-900; + } +} + +@layer components { + .btn-primary { + @apply bg-nest-pink hover:bg-red-600 text-white px-6 py-3 rounded-full font-medium transition-colors duration-200; + } + + .btn-secondary { + @apply border border-white text-white hover:bg-white hover:text-black px-6 py-3 rounded-full font-medium transition-colors duration-200; + } + + .job-card { + @apply bg-white border border-gray-200 rounded-lg p-6 hover:shadow-lg transition-shadow duration-200; + } + + .featured-job { + @apply border-nest-pink bg-gradient-to-r from-pink-50 to-red-50; + } + + .company-logo { + @apply w-12 h-12 object-contain grayscale hover:grayscale-0 transition-all duration-200; + } +} \ 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..50726c9 --- /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': '#e91e63', + 'nest-dark': '#1a1a1a', + 'nest-gray': '#6b7280', + 'nest-light-gray': '#f3f4f6' + }, + fontFamily: { + 'sans': ['Inter', 'system-ui', 'sans-serif'] + }, + backgroundImage: { + 'hero-pattern': '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