298 lines
10 KiB
TypeScript
298 lines
10 KiB
TypeScript
'use client'
|
||
|
||
import { useParams, useRouter } from 'next/navigation'
|
||
import { useVacancy } from '@/hooks/useVacancy'
|
||
import { VacancyRead } from '@/types/api'
|
||
import {
|
||
ArrowLeft,
|
||
MapPin,
|
||
Clock,
|
||
Banknote,
|
||
Building,
|
||
Users,
|
||
Calendar,
|
||
Phone,
|
||
Mail,
|
||
Globe
|
||
} from 'lucide-react'
|
||
import ResumeUploadForm from '@/components/ResumeUploadForm'
|
||
|
||
export default function VacancyPage() {
|
||
const params = useParams()
|
||
const router = useRouter()
|
||
const vacancyId = parseInt(params.id as string)
|
||
|
||
const { data: vacancy, isLoading, error } = useVacancy(vacancyId)
|
||
|
||
const formatSalary = (vacancy: VacancyRead) => {
|
||
if (!vacancy.salary_from && !vacancy.salary_to) return 'Зарплата не указана'
|
||
|
||
const currency = vacancy.salary_currency === 'RUR' ? '₽' : vacancy.salary_currency
|
||
|
||
if (vacancy.salary_from && vacancy.salary_to) {
|
||
return `${vacancy.salary_from.toLocaleString()} - ${vacancy.salary_to.toLocaleString()} ${currency}`
|
||
}
|
||
|
||
if (vacancy.salary_from) {
|
||
return `от ${vacancy.salary_from.toLocaleString()} ${currency}`
|
||
}
|
||
|
||
if (vacancy.salary_to) {
|
||
return `до ${vacancy.salary_to.toLocaleString()} ${currency}`
|
||
}
|
||
}
|
||
|
||
const getExperienceText = (experience: string) => {
|
||
const mapping = {
|
||
noExperience: 'Без опыта',
|
||
between1And3: '1-3 года',
|
||
between3And6: '3-6 лет',
|
||
moreThan6: 'Более 6 лет'
|
||
}
|
||
return mapping[experience as keyof typeof mapping] || experience
|
||
}
|
||
|
||
const getEmploymentText = (employment: string) => {
|
||
const mapping = {
|
||
full: 'Полная занятость',
|
||
part: 'Частичная занятость',
|
||
project: 'Проектная работа',
|
||
volunteer: 'Волонтерство',
|
||
probation: 'Стажировка'
|
||
}
|
||
return mapping[employment as keyof typeof mapping] || employment
|
||
}
|
||
|
||
const getScheduleText = (schedule: string) => {
|
||
const mapping = {
|
||
fullDay: 'Полный день',
|
||
shift: 'Сменный график',
|
||
flexible: 'Гибкий график',
|
||
remote: 'Удаленная работа',
|
||
flyInFlyOut: 'Вахтовый метод'
|
||
}
|
||
return mapping[schedule as keyof typeof mapping] || schedule
|
||
}
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="flex justify-center items-center min-h-[400px]">
|
||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600"></div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (error || !vacancy) {
|
||
return (
|
||
<div className="text-center py-12">
|
||
<div className="text-red-600 mb-4">
|
||
<p>Не удалось загрузить информацию о вакансии</p>
|
||
</div>
|
||
<button
|
||
onClick={() => router.back()}
|
||
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700"
|
||
>
|
||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||
Назад
|
||
</button>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="max-w-4xl mx-auto space-y-8">
|
||
{/* Header */}
|
||
<div className="flex items-center justify-between">
|
||
<button
|
||
onClick={() => router.back()}
|
||
className="inline-flex items-center text-gray-600 hover:text-gray-900"
|
||
>
|
||
<ArrowLeft className="h-5 w-5 mr-2" />
|
||
Назад к вакансиям
|
||
</button>
|
||
|
||
{vacancy.premium && (
|
||
<span className="px-3 py-1 bg-yellow-100 text-yellow-800 text-sm font-medium rounded-full">
|
||
Premium
|
||
</span>
|
||
)}
|
||
</div>
|
||
|
||
{/* Main Content */}
|
||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||
{/* Left Column - Vacancy Details */}
|
||
<div className="lg:col-span-2 space-y-6">
|
||
{/* Title and Company */}
|
||
<div className="bg-white rounded-lg shadow-md p-6">
|
||
<h1 className="text-3xl font-bold text-gray-900 mb-4">
|
||
{vacancy.title}
|
||
</h1>
|
||
|
||
<div className="flex items-center mb-6">
|
||
<Building className="h-5 w-5 text-gray-400 mr-2" />
|
||
<span className="text-lg font-medium text-gray-900">
|
||
{vacancy.company_name || 'Не указано'}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm">
|
||
<div className="flex items-center text-gray-600">
|
||
<Banknote className="h-4 w-4 mr-2" />
|
||
<span>{formatSalary(vacancy)}</span>
|
||
</div>
|
||
|
||
<div className="flex items-center text-gray-600">
|
||
<MapPin className="h-4 w-4 mr-2" />
|
||
<span>{vacancy.area_name || 'Не указано'}</span>
|
||
</div>
|
||
|
||
<div className="flex items-center text-gray-600">
|
||
<Clock className="h-4 w-4 mr-2" />
|
||
<span>{getExperienceText(vacancy.experience)}</span>
|
||
</div>
|
||
|
||
<div className="flex items-center text-gray-600">
|
||
<Users className="h-4 w-4 mr-2" />
|
||
<span>{getEmploymentText(vacancy.employment_type)}</span>
|
||
</div>
|
||
|
||
<div className="flex items-center text-gray-600">
|
||
<Calendar className="h-4 w-4 mr-2" />
|
||
<span>{getScheduleText(vacancy.schedule)}</span>
|
||
</div>
|
||
|
||
{vacancy.published_at && (
|
||
<div className="flex items-center text-gray-600">
|
||
<Clock className="h-4 w-4 mr-2" />
|
||
<span>Опубликовано {new Date(vacancy.published_at).toLocaleDateString('ru-RU')}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Description */}
|
||
<div className="bg-white rounded-lg shadow-md p-6">
|
||
<h2 className="text-xl font-semibold text-gray-900 mb-4">
|
||
Описание вакансии
|
||
</h2>
|
||
<div className="prose prose-gray max-w-none">
|
||
<p className="whitespace-pre-line text-gray-700 leading-relaxed">
|
||
{vacancy.description}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Key Skills */}
|
||
{vacancy.key_skills && (
|
||
<div className="bg-white rounded-lg shadow-md p-6">
|
||
<h2 className="text-xl font-semibold text-gray-900 mb-4">
|
||
Ключевые навыки
|
||
</h2>
|
||
<div className="prose prose-gray max-w-none">
|
||
<p className="text-gray-700">{vacancy.key_skills}</p>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Company Description */}
|
||
{vacancy.company_description && (
|
||
<div className="bg-white rounded-lg shadow-md p-6">
|
||
<h2 className="text-xl font-semibold text-gray-900 mb-4">
|
||
О компании
|
||
</h2>
|
||
<div className="prose prose-gray max-w-none">
|
||
<p className="whitespace-pre-line text-gray-700 leading-relaxed">
|
||
{vacancy.company_description}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Location Details */}
|
||
{(vacancy.address || vacancy.metro_stations) && (
|
||
<div className="bg-white rounded-lg shadow-md p-6">
|
||
<h2 className="text-xl font-semibold text-gray-900 mb-4">
|
||
Местоположение
|
||
</h2>
|
||
<div className="space-y-2 text-gray-700">
|
||
{vacancy.address && (
|
||
<div className="flex items-start">
|
||
<MapPin className="h-4 w-4 mr-2 mt-0.5 flex-shrink-0" />
|
||
<span>{vacancy.address}</span>
|
||
</div>
|
||
)}
|
||
{vacancy.metro_stations && (
|
||
<div className="flex items-start">
|
||
<span className="text-sm font-medium mr-2">Метро:</span>
|
||
<span className="text-sm">{vacancy.metro_stations}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Right Column - Application Form and Contact Info */}
|
||
<div className="space-y-6">
|
||
{/* Contact Information */}
|
||
{(vacancy.contacts_name || vacancy.contacts_email || vacancy.contacts_phone || vacancy.url) && (
|
||
<div className="bg-white rounded-lg shadow-md p-6">
|
||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||
Контактная информация
|
||
</h3>
|
||
<div className="space-y-3 text-sm">
|
||
{vacancy.contacts_name && (
|
||
<div className="flex items-center text-gray-700">
|
||
<Users className="h-4 w-4 mr-2" />
|
||
<span>{vacancy.contacts_name}</span>
|
||
</div>
|
||
)}
|
||
{vacancy.contacts_email && (
|
||
<div className="flex items-center text-gray-700">
|
||
<Mail className="h-4 w-4 mr-2" />
|
||
<a
|
||
href={`mailto:${vacancy.contacts_email}`}
|
||
className="hover:text-primary-600"
|
||
>
|
||
{vacancy.contacts_email}
|
||
</a>
|
||
</div>
|
||
)}
|
||
{vacancy.contacts_phone && (
|
||
<div className="flex items-center text-gray-700">
|
||
<Phone className="h-4 w-4 mr-2" />
|
||
<a
|
||
href={`tel:${vacancy.contacts_phone}`}
|
||
className="hover:text-primary-600"
|
||
>
|
||
{vacancy.contacts_phone}
|
||
</a>
|
||
</div>
|
||
)}
|
||
{vacancy.url && (
|
||
<div className="flex items-center text-gray-700">
|
||
<Globe className="h-4 w-4 mr-2" />
|
||
<a
|
||
href={vacancy.url}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="hover:text-primary-600"
|
||
>
|
||
Перейти к вакансии
|
||
</a>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Application Form */}
|
||
<ResumeUploadForm
|
||
vacancyId={vacancy.id}
|
||
vacancyTitle={vacancy.title}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
} |