copied the code from the working repo
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
.edit_btn{
|
||||
background-color: #372579;
|
||||
&:hover{
|
||||
background: #47309C !important;
|
||||
}
|
||||
}
|
||||
.spin{
|
||||
position:absolute;
|
||||
left:50%;
|
||||
top:50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.text{
|
||||
font-size:25px;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useParams } from 'next/navigation';
|
||||
import { useQuery, useMutation } from 'react-query';
|
||||
|
||||
import { Form, Input, Button, notification, Select, Spin } from 'antd';
|
||||
import { ExtendedJobData, JobData } from '@/types/types';
|
||||
import { useEffect } from 'react';
|
||||
import { fetchJobById, updateJob } from '@/api/api';
|
||||
import { Option } from 'antd/es/mentions';
|
||||
import style from './EditVacancy.module.scss'
|
||||
|
||||
|
||||
interface EditVacancyProps {
|
||||
id: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const EditVacancy: React.FC<EditVacancyProps> = ({id}) => {
|
||||
const router = useRouter();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { data, error, isLoading } = useQuery<ExtendedJobData>(
|
||||
['job', id],
|
||||
() => fetchJobById(Number(id)),
|
||||
{
|
||||
enabled: !!id,
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
form.setFieldsValue({
|
||||
Job_name: data.Job_name || '',
|
||||
Year: data.Year || '',
|
||||
Qualification: data.Qualification || false,
|
||||
Time: data.Time || [''],
|
||||
Soft_skills: data.Soft_skills || '',
|
||||
Salary: data.Salary || 0,
|
||||
Email: data.Email || '',
|
||||
Archive: data.Archive || false,
|
||||
Responsibilities: data.Responsibilities || '',
|
||||
Hardskills: ['React', 'JavaScript'],
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
const mutation = useMutation((updatedJob: JobData) => updateJob(Number(id), updatedJob), {
|
||||
onSuccess: () => {
|
||||
notification.success({
|
||||
message: 'Обновление прошло успешно',
|
||||
description: 'Вакансия была успешно обновлена.',
|
||||
});
|
||||
router.push('/view'); // Перенаправление обратно на страницу списка вакансий
|
||||
},
|
||||
onError: () => {
|
||||
notification.error({
|
||||
message: 'Не удалось выполнить обновление',
|
||||
description: 'Произошла ошибка при обновлении вакансии.',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const onFinish = (values: JobData) => {
|
||||
mutation.mutate({ ...values}); // Обновление данных вакансии
|
||||
};
|
||||
|
||||
if (isLoading) return <Spin size='large' className={style.spin} />;
|
||||
if (error) return <div>Error loading job data</div>;
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '600px', margin: 'auto', padding: '20px' }}>
|
||||
<h2 className={style.text}>Редактировать вакансию</h2>
|
||||
<Form form={form} layout='vertical' onFinish={onFinish}>
|
||||
<Form.Item
|
||||
name='Job_name'
|
||||
label='Название должности'
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='Year' label='Курс' rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name='Qualification'
|
||||
label='Квалификация'
|
||||
valuePropName='checked'
|
||||
>
|
||||
<Input type='checkbox' />
|
||||
</Form.Item>
|
||||
<Form.Item name='Time' label='Занятость' rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='Soft_skills' label='Soft Skills'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='Salary' label='Оклад' rules={[{ required: true }]}>
|
||||
<Input type='number' />
|
||||
</Form.Item>
|
||||
<Form.Item name='Email' label='Email' rules={[{ required: true }]}>
|
||||
<Input type='email' />
|
||||
</Form.Item>
|
||||
<Form.Item name='Archive' label='Archive' valuePropName='checked'>
|
||||
<Input type='checkbox' />
|
||||
</Form.Item>
|
||||
<Form.Item name='Responsibilities' label='Обязанности'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name='Hardskills'
|
||||
label='Hardskills'
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Пожалуйста, укажите свои профессиональные навыки!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
mode='tags'
|
||||
style={{ width: '100%' }}
|
||||
placeholder='Выбрать hardskills'
|
||||
>
|
||||
<Option value='JavaScript'>JavaScript</Option>
|
||||
<Option value='TypeScript'>TypeScript</Option>
|
||||
<Option value='React'>React</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type='primary' htmlType='submit' loading={mutation.isLoading} className={style.edit_btn}>
|
||||
Сохранить изменения
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditVacancy;
|
||||
@@ -0,0 +1,44 @@
|
||||
.btn{
|
||||
width:150px;
|
||||
|
||||
}
|
||||
.btn_group{
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
width:100px;
|
||||
justify-content: space-around;
|
||||
height:120px;
|
||||
gap:10px;
|
||||
margin-top:10px;
|
||||
margin-bottom:30px;
|
||||
position:relative;
|
||||
}
|
||||
.card_wrapper{
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
|
||||
}
|
||||
|
||||
.card{
|
||||
min-height: 450px;
|
||||
}
|
||||
.spin{
|
||||
position:absolute;
|
||||
left:50%;
|
||||
top:50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.empty{
|
||||
position:absolute;
|
||||
left:50%;
|
||||
top:50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size:40px;
|
||||
}
|
||||
.text{
|
||||
font-size:25px;
|
||||
margin-top:40px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
206
mtucijobsweb2/fsd/widgets/ViewVacansy/ViewVacansy.tsx
Normal file
206
mtucijobsweb2/fsd/widgets/ViewVacansy/ViewVacansy.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
'use client';
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import { deleteJob, fetchJobs, updateJob } from '../../../api/api';
|
||||
import { Card, Spin, Alert, Button, notification } from 'antd';
|
||||
import { ExtendedJobData } from '@/types/types';
|
||||
import { queryClient } from '@/fsd/app/provider/QueryClient';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import style from './ViewVacansy.module.scss';
|
||||
|
||||
const ViewVacansy = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const { data, error, isLoading } = useQuery<ExtendedJobData[]>(
|
||||
'jobs',
|
||||
fetchJobs,
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
retry: false,
|
||||
}
|
||||
);
|
||||
|
||||
const mutation = useMutation(deleteJob, {
|
||||
onSuccess: () => {
|
||||
// Обновление списка вакансий после удаления
|
||||
queryClient.invalidateQueries('jobs');
|
||||
},
|
||||
});
|
||||
|
||||
const archiveMutation = useMutation(
|
||||
(job: ExtendedJobData) =>
|
||||
updateJob(job.JobID, {
|
||||
...job,
|
||||
Archive: !job.Archive,
|
||||
Hardskills: [],
|
||||
}),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('jobs');
|
||||
},
|
||||
onError: () => {
|
||||
notification.error({
|
||||
message: 'Ошибка',
|
||||
description: 'Не удалось снять вакансию с публикации',
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const handleArchive = (job: ExtendedJobData) => {
|
||||
archiveMutation.mutate(job);
|
||||
};
|
||||
|
||||
const handleDelete = (id: number) => {
|
||||
mutation.mutate(id);
|
||||
};
|
||||
|
||||
const handleClick = (id: number) => {
|
||||
router.push(`/resume/${id}`);
|
||||
};
|
||||
|
||||
const handleEdit = (id: number) => {
|
||||
router.push(`/editvacansy/${id}`);
|
||||
};
|
||||
|
||||
if (isLoading) return <Spin size='large' className={style.spin} />;
|
||||
if (error) {
|
||||
return <div className={style.empty}>Нет доступных вакансий</div>;
|
||||
}
|
||||
|
||||
// Разделение данных на активные и архивные вакансии
|
||||
const activeJobs = data?.filter(job => !job.Archive);
|
||||
const archivedJobs = data?.filter(job => job.Archive);
|
||||
console.log(activeJobs);
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<h3 className={style.text}>Активные вакансии:</h3>
|
||||
<div
|
||||
className={style.card_wrapper}
|
||||
style={{
|
||||
display: 'grid',
|
||||
gap: '20px',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
|
||||
}}
|
||||
>
|
||||
{activeJobs?.map((job, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
title={job.Job_name}
|
||||
bordered={false}
|
||||
className={style.card}
|
||||
>
|
||||
{/* Контент вакансии */}
|
||||
<p>
|
||||
<strong>Название компании:</strong> {job.Company_name}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Курс:</strong> {job.Year}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Квалификация:</strong>{' '}
|
||||
{job.Qualification ? 'Нужна' : 'Не нужна'}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Занятость (часов в неделю):</strong>{' '}
|
||||
{job.Time.length > 1 ? job.Time.join(', ') : job.Time[0]}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Оклад:</strong> {job.Salary} ₽
|
||||
</p>
|
||||
<p>
|
||||
<strong>Email:</strong> {job.Email}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Обязанности:</strong> {job.Responsibilities}
|
||||
</p>
|
||||
<div className={style.btn_group}>
|
||||
<Button
|
||||
onClick={() => handleEdit(job.JobID)}
|
||||
className={style.btn}
|
||||
>
|
||||
Редактировать
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleClick(job.JobID)}
|
||||
className={style.btn}
|
||||
>
|
||||
Список резюме
|
||||
</Button>
|
||||
<Button onClick={() => handleArchive(job)} className={style.btn}>
|
||||
Снять с публикации
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
onClick={() => handleDelete(job.JobID)}
|
||||
className={style.btn}
|
||||
>
|
||||
Удалить
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3 className={style.text}>Архив:</h3>
|
||||
<div
|
||||
className={style.card_wrapper}
|
||||
style={{
|
||||
display: 'grid',
|
||||
gap: '20px',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
|
||||
}}
|
||||
>
|
||||
{archivedJobs?.map((job, index) => (
|
||||
<Card key={index} title={job.Job_name} bordered={false}>
|
||||
{/* Контент вакансии */}
|
||||
<p>
|
||||
<strong>Курс:</strong> {job.Year}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Квалификация:</strong> {job.Qualification ? 'Yes' : 'No'}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Занятость:</strong>{' '}
|
||||
{job.Time.length > 1 ? job.Time.join(', ') : job.Time[0]}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Оклад:</strong> ${job.Salary}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Email:</strong> {job.Email}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Archive:</strong> {job.Archive ? 'Yes' : 'No'}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Обязанности:</strong> {job.Responsibilities}
|
||||
</p>
|
||||
<div className={style.btn_group}>
|
||||
<Button
|
||||
onClick={() => handleEdit(job.JobID)}
|
||||
className={style.btn}
|
||||
>
|
||||
Редактировать
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleClick(job.JobID)}
|
||||
className={style.btn}
|
||||
>
|
||||
Список резюме
|
||||
</Button>
|
||||
<Button onClick={() => handleArchive(job)} className={style.btn}>
|
||||
Опубликовать
|
||||
</Button>
|
||||
<Button danger onClick={() => handleDelete(job.JobID)}>
|
||||
Удалить
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewVacansy;
|
||||
Reference in New Issue
Block a user