chore: ensure all changes are committed
This commit is contained in:
parent
14e14a93fd
commit
285b0f12f5
2 changed files with 384 additions and 692 deletions
|
|
@ -1,471 +1,160 @@
|
|||
"use client"
|
||||
use client
|
||||
|
||||
import { Button } from @/components/ui/button
|
||||
import { mockJobs } from @/lib/mock-data
|
||||
import Link from next/link
|
||||
import { ArrowRight, CheckCircle2 } from 'lucide-react'
|
||||
import Image from next/image
|
||||
import { motion } from framer-motion
|
||||
import { useTranslation } from @/lib/i18n
|
||||
import { Navbar } from @/components/navbar
|
||||
import { Footer } from @/components/footer
|
||||
import { HomeSearch } from @/components/home-search
|
||||
import { JobCard } from @/components/job-card
|
||||
|
||||
export default function Home() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className=min-h-screen bg-gray-50 flex flex-col font-sans>
|
||||
<Navbar />
|
||||
|
||||
<main className=flex-grow>
|
||||
{/* Hero Section */}
|
||||
<section className=relative min-h-[500px] flex items-center justify-center bg-[#1F2F40] py-16 md:py-0>
|
||||
{/* Background Image with Overlay */}
|
||||
<div className=absolute inset-0 z-0>
|
||||
<Image
|
||||
src=/10.png
|
||||
alt=Background
|
||||
fill
|
||||
className=object-cover opacity-60 contrast-125
|
||||
priority
|
||||
/>
|
||||
<div className=absolute inset-0 bg-gradient-to-r from-[#1F2F40] via-[#1F2F40]/90 to-transparent />
|
||||
</div>
|
||||
|
||||
<div className=container mx-auto px-4 relative z-10 text-center sm:text-left>
|
||||
<div className=max-w-3xl>
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className=text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6 leading-tight tracking-tight
|
||||
>
|
||||
Encontre a Vaga de TI <br className=hidden sm:block />
|
||||
dos Seus Sonhos.
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
className=text-lg md:text-xl text-gray-300 mb-8 max-w-xl leading-relaxed mx-auto sm:mx-0
|
||||
>
|
||||
Conectamos você com as melhores empresas e techs.
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<Link href=/jobs>
|
||||
<Button className=h-12 px-8 bg-orange-500 hover:bg-orange-600 text-white font-bold text-lg rounded-md shadow-lg transition-transform hover:scale-105 w-full sm:w-auto>
|
||||
Buscar Vagas
|
||||
</Button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Search Section */}
|
||||
<section className=px-4 mb-16>
|
||||
<div className=container mx-auto>
|
||||
<HomeSearch />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Latest Jobs Section */}
|
||||
<section className=py-12 bg-gray-50>
|
||||
<div className=container mx-auto px-4>
|
||||
<h2 className=text-2xl md:text-3xl font-bold text-gray-900 mb-8 text-center sm:text-left>
|
||||
Últimas Vagas Cadastradas
|
||||
</h2>
|
||||
|
||||
<div className=grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6>
|
||||
{mockJobs.slice(0, 4).map((job, index) => (
|
||||
<JobCard key={job.id} job={job} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* More Jobs Section */}
|
||||
<section className=py-12 bg-white>
|
||||
<div className=container mx-auto px-4>
|
||||
<div className=flex flex-col sm:flex-row items-center justify-between mb-8 gap-4>
|
||||
<h2 className=text-2xl md:text-3xl font-bold text-gray-900>
|
||||
Mais Vagas
|
||||
</h2>
|
||||
<Link href=/jobs className=w-full sm:w-auto>
|
||||
<Button className=bg-orange-500 hover:bg-orange-600 text-white font-bold w-full sm:w-auto>
|
||||
Ver Todas Vagas
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className=grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6>
|
||||
{mockJobs.slice(0, 8).map((job, index) => (
|
||||
<JobCard key={`more-${job.id}-${index}`} job={job} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
{/* Bottom CTA Section */}
|
||||
<section className=py-16 bg-white>
|
||||
<div className=container mx-auto px-4>
|
||||
<div className=bg-[#1F2F40] rounded-[2rem] p-8 md:p-16 relative overflow-hidden text-center md:text-left flex flex-col md:flex-row items-center justify-between min-h-[400px]>
|
||||
|
||||
{/* Content */}
|
||||
<div className=relative z-10 max-w-xl>
|
||||
<h2 className=text-3xl md:text-4xl font-bold text-white mb-4 leading-tight>
|
||||
Milhares de oportunidades <br className=hidden sm:block/> esperam você.
|
||||
</h2>
|
||||
<p className=text-base text-gray-300 mb-8>
|
||||
Conecte cargos, talentos, tomada de ações de vagas.
|
||||
</p>
|
||||
<Link href=/register/user className=w-full sm:w-auto>
|
||||
<Button className=h-12 px-8 bg-white text-gray-900 hover:bg-gray-100 font-bold text-lg rounded-md w-full sm:w-auto>
|
||||
Cadastre-se
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Image Background for CTA */}
|
||||
<div className=absolute inset-0 z-0>
|
||||
<div className=absolute right-0 top-0 h-full w-full md:w-2/3 lg:w-1/2>
|
||||
<Image
|
||||
src=/muie.jpeg
|
||||
alt=Professional
|
||||
fill
|
||||
className=object-cover object-center md:object-right opacity-40 md:opacity-100 // Opacity adjusted for mobile readability
|
||||
/>
|
||||
{/* Gradient Overlay to blend with dark background */}
|
||||
<div className=absolute inset-0 bg-gradient-to-t md:bg-gradient-to-r from-[#1F2F40] via-[#1F2F40]/30 to-transparent />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
import { Navbar } from "@/components/navbar"
|
||||
import { Footer } from "@/components/footer"
|
||||
import { JobCard } from "@/components/job-card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { mockJobs, mockTestimonials } from "@/lib/mock-data"
|
||||
import { FileText, CheckCircle, ArrowRight, Building2, Users, ChevronLeft, ChevronRight, Eye } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { motion } from "framer-motion"
|
||||
import Image from "next/image"
|
||||
import { useTranslation } from "@/lib/i18n"
|
||||
import { useConfig } from "@/contexts/ConfigContext"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import type { Job } from "@/lib/types"
|
||||
|
||||
export default function HomePage() {
|
||||
const { t } = useTranslation()
|
||||
const config = useConfig()
|
||||
const [featuredJobs, setFeaturedJobs] = useState<Job[]>(mockJobs.slice(0, 31))
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [featuredIndex, setFeaturedIndex] = useState(0)
|
||||
const [moreJobsIndex, setMoreJobsIndex] = useState(0)
|
||||
const [openFilters, setOpenFilters] = useState({
|
||||
contractType: false,
|
||||
workMode: false,
|
||||
location: false,
|
||||
salary: false
|
||||
})
|
||||
|
||||
const toggleFilter = (filterName: keyof typeof openFilters) => {
|
||||
setOpenFilters(prev => ({
|
||||
contractType: false,
|
||||
workMode: false,
|
||||
location: false,
|
||||
salary: false,
|
||||
[filterName]: !prev[filterName]
|
||||
}))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchFeaturedJobs() {
|
||||
try {
|
||||
const apiBase = config.apiUrl
|
||||
console.log("[DEBUG] API Base URL:", apiBase)
|
||||
|
||||
const mapJobs = (jobs: any[]): Job[] =>
|
||||
jobs.map((j: any) => ({
|
||||
id: String(j.id),
|
||||
title: j.title,
|
||||
company: j.companyName || t("jobs.confidential"),
|
||||
location: j.location || t("workMode.remote"),
|
||||
type: j.employmentType || "full-time",
|
||||
salary: j.salaryMin ? `R$ ${j.salaryMin}` : t("jobs.salary.negotiable"),
|
||||
description: j.description,
|
||||
requirements: j.requirements || [],
|
||||
postedAt: j.createdAt,
|
||||
isFeatured: j.isFeatured
|
||||
}))
|
||||
|
||||
console.log("[DEBUG] Fetching featured jobs from:", `${apiBase}/api/v1/jobs?featured=true&limit=31`)
|
||||
const featuredRes = await fetch(`${apiBase}/api/v1/jobs?featured=true&limit=31`)
|
||||
console.log("[DEBUG] Featured response status:", featuredRes.status)
|
||||
|
||||
if (!featuredRes.ok) throw new Error("Failed to fetch featured jobs")
|
||||
const featuredData = await featuredRes.json()
|
||||
console.log("[DEBUG] Featured data from API:", featuredData)
|
||||
|
||||
const featuredList = featuredData.data ? mapJobs(featuredData.data) : []
|
||||
console.log("[DEBUG] Mapped featured jobs:", featuredList.length, "jobs")
|
||||
|
||||
if (featuredList.length >= 24) {
|
||||
console.log("[DEBUG] Using featured/API jobs")
|
||||
setFeaturedJobs(featuredList.slice(0, 31))
|
||||
return
|
||||
}
|
||||
|
||||
console.log("[DEBUG] Fetching fallback jobs from:", `${apiBase}/api/v1/jobs?limit=31`)
|
||||
const fallbackRes = await fetch(`${apiBase}/api/v1/jobs?limit=31`)
|
||||
console.log("[DEBUG] Fallback response status:", fallbackRes.status)
|
||||
|
||||
if (!fallbackRes.ok) throw new Error("Failed to fetch fallback jobs")
|
||||
const fallbackData = await fallbackRes.json()
|
||||
console.log("[DEBUG] Fallback data from API:", fallbackData)
|
||||
|
||||
const fallbackList = fallbackData.data ? mapJobs(fallbackData.data) : []
|
||||
console.log("[DEBUG] Mapped fallback jobs:", fallbackList.length, "jobs")
|
||||
|
||||
const combined = [...featuredList, ...fallbackList].slice(0, 31)
|
||||
console.log("[DEBUG] Combined jobs:", combined.length, "jobs")
|
||||
|
||||
if (combined.length >= 24) {
|
||||
console.log("[DEBUG] Using combined jobs")
|
||||
setFeaturedJobs(combined)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[DEBUG] ❌ Error fetching featured jobs:", error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
fetchFeaturedJobs()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
<Navbar />
|
||||
|
||||
<main className="flex-1">
|
||||
{/* Hero Section */}
|
||||
<section className="bg-primary text-white relative overflow-hidden flex items-center min-h-[500px]">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image
|
||||
src="/10.png"
|
||||
alt="Background"
|
||||
fill
|
||||
className="object-cover object-center"
|
||||
quality={100}
|
||||
priority
|
||||
sizes="100vw"
|
||||
/>
|
||||
</div>
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 w-full relative z-10">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="text-left max-w-2xl py-12">
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-5xl sm:text-6xl lg:text-6xl font-bold mb-6 text-white leading-tight"
|
||||
>
|
||||
{t('home.hero.title')}<br />{t('home.hero.titleLine2')}
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
className="text-2xl mb-10 leading-relaxed text-white"
|
||||
>
|
||||
{t('home.hero.subtitle')}
|
||||
</motion.p>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="flex gap-4"
|
||||
>
|
||||
<Link href="/jobs">
|
||||
<Button size="lg" className="bg-primary hover:bg-primary/90 text-white hover:shadow-lg font-bold px-10 py-6 text-lg rounded-lg transition-all duration-200 border-0">
|
||||
{t('home.hero.cta')}
|
||||
</Button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Search Bar Section */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-8xl">
|
||||
<div className="mb-8">
|
||||
<div className="mb-6 relative">
|
||||
<svg className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={t('home.search.placeholder')}
|
||||
className="w-full h-14 pl-12 pr-4 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-base bg-white shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4 items-stretch">
|
||||
{/* Contract Type - Static Expanded */}
|
||||
<div className="bg-white border-2 border-gray-200 rounded-xl shadow-sm hover:border-primary/30 transition-colors relative h-full flex flex-col">
|
||||
<div className="flex items-center justify-between w-full p-5 pb-2">
|
||||
<span className="text-base font-bold text-gray-900">{t('home.search.contractType')}</span>
|
||||
</div>
|
||||
<div className="space-y-3 px-5 pb-5 pt-2 flex-1">
|
||||
<label className="flex items-center text-sm text-gray-700 cursor-pointer hover:text-gray-900 transition-colors">
|
||||
<input type="checkbox" className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary mr-3" />
|
||||
<span>{t('home.search.pj')}</span>
|
||||
</label>
|
||||
<label className="flex items-center text-sm text-gray-700 cursor-pointer hover:text-gray-900 transition-colors">
|
||||
<input type="checkbox" className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary mr-3" />
|
||||
<span>{t('home.search.clt')}</span>
|
||||
</label>
|
||||
<label className="flex items-center text-sm text-gray-700 cursor-pointer hover:text-gray-900 transition-colors">
|
||||
<input type="checkbox" className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary mr-3" />
|
||||
<span>{t('home.search.freelancer')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Work Mode - Static Expanded */}
|
||||
<div className="bg-white border-2 border-gray-200 rounded-xl shadow-sm hover:border-primary/30 transition-colors relative h-full flex flex-col">
|
||||
<div className="flex items-center justify-between w-full p-5 pb-2">
|
||||
<span className="text-base font-bold text-gray-900">{t('home.search.workMode')}</span>
|
||||
</div>
|
||||
<div className="space-y-3 px-5 pb-5 pt-2 flex-1">
|
||||
<label className="flex items-center text-sm text-gray-700 cursor-pointer hover:text-gray-900 transition-colors">
|
||||
<input type="checkbox" className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary mr-3" />
|
||||
<span>{t('home.search.homeOffice')}</span>
|
||||
</label>
|
||||
<label className="flex items-center text-sm text-gray-700 cursor-pointer hover:text-gray-900 transition-colors">
|
||||
<input type="checkbox" className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary mr-3" />
|
||||
<span>{t('home.search.presencial')}</span>
|
||||
</label>
|
||||
<label className="flex items-center text-sm text-gray-700 cursor-pointer hover:text-gray-900 transition-colors">
|
||||
<input type="checkbox" className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary mr-3" />
|
||||
<span>{t('home.search.hybrid')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Location - Static Expanded */}
|
||||
<div className="bg-white border-2 border-gray-200 rounded-xl shadow-sm hover:border-primary/30 transition-colors relative h-full flex flex-col">
|
||||
<div className="flex items-center justify-between w-full p-5 pb-2">
|
||||
<span className="text-base font-bold text-gray-900">{t('home.search.location')}</span>
|
||||
</div>
|
||||
<div className="px-5 pb-5 pt-2 flex-1 flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Cidade e estado"
|
||||
className="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-primary h-12"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Salary - Static Expanded */}
|
||||
<div className="bg-white border-2 border-gray-200 rounded-xl shadow-sm hover:border-primary/30 transition-colors relative h-full flex flex-col">
|
||||
<div className="flex items-center justify-between w-full p-5 pb-2">
|
||||
<span className="text-base font-bold text-gray-900">{t('home.search.salary')}</span>
|
||||
</div>
|
||||
<div className="px-5 pb-5 pt-2 flex-1 flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="R$ 0,00"
|
||||
className="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-primary h-12"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filter Button - Unified */}
|
||||
<Button className="bg-primary hover:bg-primary/90 text-white h-full min-h-[200px] lg:min-h-0 rounded-xl font-bold text-3xl shadow-md hover:shadow-lg transition-all flex flex-col gap-3 items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={3} stroke="currentColor" className="size-12">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||
</svg>
|
||||
{t('home.search.filter')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured Jobs */}
|
||||
<section className="py-12">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-8xl">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h2 className="text-3xl font-bold text-gray-900">{t('home.featuredJobs.title')}</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{(featuredJobs.length >= 4 ? featuredJobs.slice(0, 4) : mockJobs.slice(0, 4))
|
||||
.map((job, index) => {
|
||||
const dates = ['02/06', '05/06', '08/06', '11/06'];
|
||||
const randomDate = dates[index % dates.length];
|
||||
const levels = [t('home.levels.mid'), t('home.levels.junior'), t('home.levels.senior'), t('home.levels.mid')];
|
||||
const level = levels[index % levels.length];
|
||||
const statusLabels = [t('workMode.remote'), t('workMode.hybrid'), t('workMode.onsite'), t('workMode.remote')];
|
||||
const statusLabel = statusLabels[index % statusLabels.length];
|
||||
return (
|
||||
<motion.div
|
||||
key={job.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<Card className="hover:shadow-lg transition-all border border-gray-200 rounded-2xl overflow-hidden bg-white h-full group cursor-pointer flex flex-col">
|
||||
<div className="p-5 flex flex-col flex-1">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-gray-50 rounded-lg flex items-center justify-center border border-gray-100">
|
||||
<Building2 className="w-5 h-5 text-gray-700" />
|
||||
</div>
|
||||
<span className="text-sm font-bold text-gray-900 line-clamp-1">{job.company}</span>
|
||||
</div>
|
||||
<span className="text-[10px] px-2.5 py-1 bg-gray-900 text-white rounded-md font-bold uppercase tracking-wide">
|
||||
{statusLabel}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1">
|
||||
<h3 className="font-bold text-lg text-gray-900 mb-3 leading-tight line-clamp-2 pb-3 border-b border-gray-100">{job.title}</h3>
|
||||
<div className="relative w-full h-32 mb-4 flex items-center justify-center bg-gray-50/50 rounded-lg border border-gray-50">
|
||||
<Image
|
||||
src="/111.png"
|
||||
alt="Job Illustration"
|
||||
fill
|
||||
className="object-contain p-2"
|
||||
quality={100}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer Section with Separator */}
|
||||
<div className="px-5 pb-5 pt-0 mt-auto">
|
||||
<div className="border-t border-gray-200 pt-4 mb-4">
|
||||
<p className="text-sm text-gray-500 font-medium flex items-center gap-2">
|
||||
<span className="text-gray-900 font-bold">{level}</span>
|
||||
<span className="w-1 h-1 rounded-full bg-gray-300"></span>
|
||||
{job.location}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1 border-gray-200 text-gray-700 hover:bg-gray-50 hover:text-gray-900 rounded-lg font-bold h-10 text-xs uppercase tracking-wide"
|
||||
>
|
||||
{t('home.featuredJobs.viewJob')}
|
||||
</Button>
|
||||
<Link href={`/jobs/${job.id}`} className="flex-1">
|
||||
<Button className="w-full bg-gray-900 hover:bg-gray-800 text-white rounded-lg font-bold h-10 text-xs uppercase tracking-wide">
|
||||
{t('home.featuredJobs.apply')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* More Jobs Section */}
|
||||
<section className="py-12">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-8xl">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h2 className="text-3xl font-bold text-gray-900">{t('home.moreJobs.title')}</h2>
|
||||
<Link href="/jobs">
|
||||
<Button className="bg-primary hover:bg-primary/90 text-white rounded-lg px-10 py-4 font-bold text-lg min-w-[220px]">
|
||||
{t('home.moreJobs.viewAll')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{mockJobs.slice(0, 8)
|
||||
.map((job, index) => {
|
||||
const colors = [
|
||||
'bg-cyan-500', 'bg-blue-500', 'bg-indigo-500', 'bg-gray-500',
|
||||
'bg-teal-500', 'bg-sky-500', 'bg-orange-500', 'bg-purple-500'
|
||||
];
|
||||
const bgColor = colors[index % colors.length];
|
||||
const icons = ['💻', '🎨', '📊', '🚀', '⚙️', '🔧', '📱', '🎯'];
|
||||
const icon = icons[index % icons.length];
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={job.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<Card className="hover:shadow-lg transition-all border border-gray-200 bg-white rounded-xl overflow-hidden group cursor-pointer h-full">
|
||||
<CardContent className="p-5">
|
||||
{/* Cabeçalho com logo e seta */}
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-start gap-3 flex-1">
|
||||
<div className={`w-12 h-12 ${bgColor} rounded-full flex items-center justify-center text-white text-xl flex-shrink-0`}>
|
||||
{icon}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-bold text-base text-gray-900 line-clamp-1 mb-1">{job.title}</h3>
|
||||
<p className="text-sm text-gray-600 line-clamp-1">{job.company}</p>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-gray-400 group-hover:text-gray-600 transition-colors flex-shrink-0 ml-2" />
|
||||
</div>
|
||||
|
||||
{/* Rodapé com botões */}
|
||||
<div className="pt-4 mt-4 border-t border-gray-200">
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1 border-gray-200 text-gray-700 hover:bg-gray-50 hover:text-gray-900 rounded-lg font-bold h-9 text-xs uppercase tracking-wide"
|
||||
>
|
||||
{t('home.featuredJobs.viewJob')}
|
||||
</Button>
|
||||
<Link href={`/jobs/${job.id}`} className="flex-1">
|
||||
<Button className="w-full bg-gray-900 hover:bg-gray-800 text-white rounded-lg font-bold h-9 text-xs uppercase tracking-wide">
|
||||
{t('home.featuredJobs.apply')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-12">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-8xl">
|
||||
<div className="bg-gray-900 rounded-2xl overflow-hidden shadow-lg relative min-h-[500px] flex items-center">
|
||||
{/* Image Layer: Single Image with Seamless Gradient Overlay */}
|
||||
<div className="absolute inset-y-0 right-0 w-full md:w-3/4 z-0">
|
||||
<Image
|
||||
src="/muie.jpeg"
|
||||
alt="Woman with Notebook"
|
||||
fill
|
||||
className="object-contain object-right"
|
||||
quality={100}
|
||||
priority
|
||||
/>
|
||||
{/*
|
||||
Seamless Blend Gradient:
|
||||
Starts solid gray-900 (matching, container) on left.
|
||||
Fades gradually to transparent on right.
|
||||
This "dyes" the dark background of the photo to match the container.
|
||||
*/}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-gray-900 from-20% via-gray-900/80 via-50% to-transparent to-100%" />
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-2 gap-8 items-center p-8 lg:p-12 relative z-10">
|
||||
{/* Text Content */}
|
||||
<div className="text-white max-w-lg">
|
||||
<h2 className="text-3xl lg:text-4xl font-bold mb-4 leading-tight">
|
||||
{t('home.cta.title')}
|
||||
</h2>
|
||||
<p className="mb-6 text-white/90 text-lg">
|
||||
{t('home.cta.subtitle')}
|
||||
</p>
|
||||
<Button size="lg" className="bg-white text-primary hover:bg-white/90 font-bold px-12 py-7 text-xl rounded-lg">
|
||||
{t('home.cta.button')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FilterIcon() {
|
||||
return (
|
||||
<svg width="44" height="44" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="w-11 h-11">
|
||||
<rect x="4" y="7" width="16" height="3" rx="1.5" fill="white" />
|
||||
<rect x="7" y="12" width="10" height="3" rx="1.5" fill="white" />
|
||||
<rect x="10" y="17" width="4" height="3" rx="1.5" fill="white" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,223 +1,226 @@
|
|||
"use client"
|
||||
use client;
|
||||
|
||||
import { motion } from framer-motion;
|
||||
import {
|
||||
Building2,
|
||||
MapPin,
|
||||
Clock,
|
||||
Heart,
|
||||
} from lucide-react;
|
||||
import Link from next/link;
|
||||
import { useState } from react;
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
} from @/components/ui/card;
|
||||
import { Button } from @/components/ui/button;
|
||||
import { Badge } from @/components/ui/badge;
|
||||
import { Avatar, AvatarImage, AvatarFallback } from @/components/ui/avatar;
|
||||
import { formatTimeAgo } from @/lib/utils;
|
||||
import { useTranslation } from @/lib/i18n;
|
||||
import { useAuth } from @/contexts/AuthContext;
|
||||
import { useNotification } from @/contexts/notification-context;
|
||||
|
||||
interface JobCardProps {
|
||||
job: {
|
||||
id: string;
|
||||
title: string;
|
||||
company: string;
|
||||
location: string;
|
||||
type: string;
|
||||
postedAt: string | Date;
|
||||
description: string;
|
||||
salary?: string;
|
||||
requirements?: string[];
|
||||
};
|
||||
isApplied?: boolean;
|
||||
applicationStatus?: string;
|
||||
}
|
||||
|
||||
export function JobCard({ job, isApplied, applicationStatus }: JobCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const notify = useNotification();
|
||||
const [isFavorited, setIsFavorited] = useState(false);
|
||||
|
||||
const getTypeLabel = (type: string) => {
|
||||
switch (type.toLowerCase()) {
|
||||
case full-time:
|
||||
return CLT;
|
||||
case contract:
|
||||
return PJ;
|
||||
case freelance:
|
||||
return Freelancer;
|
||||
case remote:
|
||||
return Remoto;
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
const getTypeBadgeVariant = (type: string): default | secondary | outline | destructive | null => {
|
||||
switch (type.toLowerCase()) {
|
||||
case full-time:
|
||||
return secondary;
|
||||
case contract:
|
||||
return outline;
|
||||
case remote:
|
||||
return default;
|
||||
default:
|
||||
return outline;
|
||||
}
|
||||
};
|
||||
|
||||
const getCompanyInitials = (company: string) => {
|
||||
return company
|
||||
.split( )
|
||||
.map((word) => word[0])
|
||||
.join(")
|
||||
.toUpperCase()
|
||||
.slice(0, 2);
|
||||
};
|
||||
|
||||
const handleFavorite = () => {
|
||||
setIsFavorited(!isFavorited);
|
||||
if (!isFavorited) {
|
||||
notify.info(
|
||||
t('jobs.favorites.added.title'),
|
||||
t('jobs.favorites.added.desc', { title: job.title }),
|
||||
{
|
||||
actionUrl: /dashboard/favorites,
|
||||
actionLabel: t('jobs.favorites.action'),
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
whileHover={{ y: -2 }}
|
||||
transition={{ type: spring, stiffness: 300, damping: 20 }}
|
||||
>
|
||||
<Card className=relative hover:shadow-lg transition-all duration-300 border-l-4 border-l-primary/20 hover:border-l-primary h-full flex flex-col>
|
||||
<CardHeader className=pb-4>
|
||||
<div className=flex items-start justify-between>
|
||||
<div className=flex items-center gap-3>
|
||||
<Avatar className=h-12 w-12>
|
||||
<AvatarImage
|
||||
src={`https://avatar.vercel.sh/${job.company}`}
|
||||
alt={job.company}
|
||||
/>
|
||||
<AvatarFallback className=bg-primary/10 text-primary font-semibold>
|
||||
{getCompanyInitials(job.company)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<h3 className=font-semibold text-lg text-balance leading-tight hover:text-primary transition-colors>
|
||||
{job.title}
|
||||
</h3>
|
||||
<div className=flex items-center gap-2 mt-1>
|
||||
<Building2 className=h-4 w-4 text-muted-foreground />
|
||||
<span className=text-muted-foreground font-medium>
|
||||
{job.company}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleFavorite}
|
||||
className=shrink-0 p-2 hover:bg-muted rounded-full transition-colors
|
||||
>
|
||||
<Heart
|
||||
className={`h-4 w-4 transition-colors ${isFavorited
|
||||
? fill-red-500 text-red-500
|
||||
: text-muted-foreground hover:text-red-500
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className=space-y-4 flex-1>
|
||||
{/* Job Meta Information */}
|
||||
<div className=flex flex-col gap-2 text-sm>
|
||||
<div className=flex items-center gap-2 text-muted-foreground>
|
||||
<MapPin className=h-4 w-4 shrink-0 />
|
||||
<span className=truncate>{job.location}</span>
|
||||
</div>
|
||||
|
||||
<div className=flex items-center justify-between w-full flex-wrap gap-2>
|
||||
<div className=flex items-center gap-2>
|
||||
<Badge
|
||||
variant={getTypeBadgeVariant(job.type)}
|
||||
className=text-xs
|
||||
>
|
||||
{getTypeLabel(job.type)}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{job.salary && (
|
||||
<span className=font-medium text-foreground whitespace-nowrap>
|
||||
{job.salary}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Job Description Preview */}
|
||||
<div className=text-sm text-muted-foreground>
|
||||
<p className=line-clamp-2>{job.description}</p>
|
||||
</div>
|
||||
|
||||
{/* Skills/Requirements Preview */}
|
||||
{job.requirements && job.requirements.length > 0 && (
|
||||
<div className=flex flex-wrap gap-2>
|
||||
{job.requirements.slice(0, 3).map((requirement, index) => (
|
||||
<Badge key={index} variant=outline className=text-xs>
|
||||
{requirement}
|
||||
</Badge>
|
||||
))}
|
||||
{job.requirements.length > 3 && (
|
||||
<Badge
|
||||
variant=outline
|
||||
className=text-xs text-muted-foreground
|
||||
>
|
||||
{t('jobs.requirements.more', { count: job.requirements.length - 3 })}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Time Posted */}
|
||||
<div className=flex items-center gap-2 text-xs text-muted-foreground>
|
||||
<Clock className=h-3 w-3 />
|
||||
<span>{formatTimeAgo(job.postedAt)}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
<CardFooter className=pt-4 border-t>
|
||||
<div className=flex flex-col sm:flex-row gap-2 w-full>
|
||||
<Link href={`/jobs/${job.id}`} className=w-full sm:flex-1>
|
||||
<Button variant=outline className=w-full cursor-pointer>
|
||||
{t('jobs.card.viewDetails')}
|
||||
</Button>
|
||||
</Link>
|
||||
{isApplied ? (
|
||||
<Button className=w-full sm:flex-1 cursor-default bg-emerald-600 hover:bg-emerald-700 text-white variant=secondary>
|
||||
{applicationStatus === 'pending' ? t('jobs.card.applied') :
|
||||
applicationStatus === 'reviewing' ? t('jobs.card.reviewing') :
|
||||
applicationStatus === 'interview' ? t('jobs.card.interview') :
|
||||
t('jobs.card.applied')}
|
||||
</Button>
|
||||
) : (
|
||||
<Link href={`/jobs/${job.id}/apply`} className=w-full sm:flex-1>
|
||||
<Button className=w-full cursor-pointer>{t('jobs.card.apply')}</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
import type { Job } from "@/lib/types";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
MapPin,
|
||||
Briefcase,
|
||||
DollarSign,
|
||||
Clock,
|
||||
Building2,
|
||||
Heart,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import { useNotify } from "@/contexts/notification-context";
|
||||
import { useTranslation } from "@/lib/i18n";
|
||||
|
||||
interface JobCardProps {
|
||||
job: Job;
|
||||
isApplied?: boolean;
|
||||
applicationStatus?: string;
|
||||
}
|
||||
|
||||
export function JobCard({ job, isApplied, applicationStatus }: JobCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const [isFavorited, setIsFavorited] = useState(false);
|
||||
const notify = useNotify();
|
||||
|
||||
const formatTimeAgo = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffInMs = now.getTime() - date.getTime();
|
||||
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffInDays === 0) return t('jobs.posted.today');
|
||||
if (diffInDays === 1) return t('jobs.posted.yesterday');
|
||||
if (diffInDays < 7) return t('jobs.posted.daysAgo', { count: diffInDays });
|
||||
if (diffInDays < 30) return t('jobs.posted.weeksAgo', { count: Math.floor(diffInDays / 7) });
|
||||
return t('jobs.posted.monthsAgo', { count: Math.floor(diffInDays / 30) });
|
||||
};
|
||||
|
||||
const getTypeLabel = (type: string) => {
|
||||
return t(`jobs.types.${type}`) !== `jobs.types.${type}` ? t(`jobs.types.${type}`) : type;
|
||||
};
|
||||
|
||||
const getTypeBadgeVariant = (type: string) => {
|
||||
switch (type) {
|
||||
case "full-time":
|
||||
return "default";
|
||||
case "part-time":
|
||||
return "secondary";
|
||||
case "contract":
|
||||
return "outline";
|
||||
case "remote":
|
||||
return "default";
|
||||
default:
|
||||
return "outline";
|
||||
}
|
||||
};
|
||||
|
||||
const getCompanyInitials = (company: string) => {
|
||||
return company
|
||||
.split(" ")
|
||||
.map((word) => word[0])
|
||||
.join("")
|
||||
.toUpperCase()
|
||||
.slice(0, 2);
|
||||
};
|
||||
|
||||
const handleFavorite = () => {
|
||||
setIsFavorited(!isFavorited);
|
||||
if (!isFavorited) {
|
||||
notify.info(
|
||||
t('jobs.favorites.added.title'),
|
||||
t('jobs.favorites.added.desc', { title: job.title }),
|
||||
{
|
||||
actionUrl: "/dashboard/favorites",
|
||||
actionLabel: t('jobs.favorites.action'),
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
whileHover={{ y: -2 }}
|
||||
transition={{ type: "spring", stiffness: 300, damping: 20 }}
|
||||
>
|
||||
<Card className="relative hover:shadow-lg transition-all duration-300 border-l-4 border-l-primary/20 hover:border-l-primary h-full flex flex-col">
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar className="h-12 w-12">
|
||||
<AvatarImage
|
||||
src={`https://avatar.vercel.sh/${job.company}`}
|
||||
alt={job.company}
|
||||
/>
|
||||
<AvatarFallback className="bg-primary/10 text-primary font-semibold">
|
||||
{getCompanyInitials(job.company)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<h3 className="font-semibold text-lg text-balance leading-tight hover:text-primary transition-colors">
|
||||
{job.title}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<Building2 className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-muted-foreground font-medium">
|
||||
{job.company}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={handleFavorite}
|
||||
className="shrink-0"
|
||||
>
|
||||
<Heart
|
||||
className={`h-4 w-4 transition-colors ${isFavorited
|
||||
? "fill-red-500 text-red-500"
|
||||
: "text-muted-foreground hover:text-red-500"
|
||||
}`}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="space-y-4 flex-1">
|
||||
{/* Job Meta Information */}
|
||||
{/* Job Meta Information */}
|
||||
<div className="flex flex-col gap-2 text-sm">
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<MapPin className="h-4 w-4 shrink-0" />
|
||||
<span className="truncate">{job.location}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex items-center gap-2">
|
||||
<Briefcase className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
<Badge
|
||||
variant={getTypeBadgeVariant(job.type)}
|
||||
className="text-xs"
|
||||
>
|
||||
{getTypeLabel(job.type)}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{job.salary && (
|
||||
<span className="font-medium text-foreground">
|
||||
{job.salary}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Job Description Preview */}
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<p className="line-clamp-2">{job.description}</p>
|
||||
</div>
|
||||
|
||||
{/* Skills/Requirements Preview */}
|
||||
{job.requirements && job.requirements.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{job.requirements.slice(0, 3).map((requirement, index) => (
|
||||
<Badge key={index} variant="outline" className="text-xs">
|
||||
{requirement}
|
||||
</Badge>
|
||||
))}
|
||||
{job.requirements.length > 3 && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs text-muted-foreground"
|
||||
>
|
||||
{t('jobs.requirements.more', { count: job.requirements.length - 3 })}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Time Posted */}
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Clock className="h-3 w-3" />
|
||||
<span>{formatTimeAgo(job.postedAt)}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
<CardFooter className="pt-4 border-t">
|
||||
<div className="flex gap-2 w-full">
|
||||
<Link href={`/jobs/${job.id}`} className="flex-1">
|
||||
<Button variant="outline" className="w-full cursor-pointer">
|
||||
{t('jobs.card.viewDetails')}
|
||||
</Button>
|
||||
</Link>
|
||||
{isApplied ? (
|
||||
<Button className="flex-1 w-full cursor-default bg-emerald-600 hover:bg-emerald-700 text-white" variant="secondary">
|
||||
{applicationStatus === 'pending' ? t('jobs.card.applied') :
|
||||
applicationStatus === 'reviewing' ? t('jobs.card.reviewing') :
|
||||
applicationStatus === 'interview' ? t('jobs.card.interview') :
|
||||
t('jobs.card.applied')}
|
||||
</Button>
|
||||
) : (
|
||||
<Link href={`/jobs/${job.id}/apply`} className="flex-1">
|
||||
<Button className="w-full cursor-pointer">{t('jobs.card.apply')}</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue