copied the code from the working repo

This commit is contained in:
2024-11-30 16:00:48 +03:00
parent f22b92869b
commit 15ac0cb9b8
148 changed files with 23342 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
.form {
font-size: 28px;
color: white;
}
// .inputs{
// background-color: var(--secondary_bg_color),
// }
.checkbox_button {
color:white;
}
// .checkbox_button:hover {
// border-color: #40a9ff;
// }
// .checkbox_button input {
// display: none;
// }
// .checkbox_button-checked {
// background-color: #40a9ff;
// color: white;
// border-color: #40a9ff;
// }
// .checkbox_button-checked:hover {
// background-color: #69c0ff;
// border-color: #69c0ff;
// }
// .checkbox_button-checked .ant-checkbox-inner {
// background-color: transparent;
// }
// .checkbox_button input:checked + .checkbox_button {
// background-color: #40a9ff;
// color: white;
// border-color: #40a9ff;
// }

View File

@@ -0,0 +1,403 @@
import React, { useEffect, useState } from 'react';
import { Button, Checkbox, Form, Input, Radio, Select } from 'antd';
import r from './Resume.module.scss';
import { initThemeParams, MiniApp, useInitData, useMiniApp, usePopup } from '@tma.js/sdk-react';
import {
deleteStudent,
editStudent,
fetchHardSkills,
fetchHardSkillsAll,
fetchStudent,
sendStudent,
} from '@/api/api';
import { Bot, FormValues, Request, Student } from '@/types/types';
import { useQuery } from 'react-query';
import { sendDataBot } from '@/api/bot';
type LayoutType = Parameters<typeof Form>[0]['layout'];
const { Option } = Select;
const Resume: React.FC = () => {
const [form] = Form.useForm();
const [formLayout, setFormLayout] = useState<LayoutType>('horizontal');
const miniApp = useMiniApp();
const [themeParams] = initThemeParams();
const initData = useInitData(true);
const popup = usePopup();
const { isLoading, isError, data, error } = useQuery(
['student', initData?.user?.id],
() => fetchStudent(initData?.user?.id || 0),
{
enabled: !!initData?.user?.id, // Включить запрос только когда initData.user.id доступен
refetchOnWindowFocus: false,
retry: false,
}
);
const hardSkill = useQuery(
['skills', initData?.user?.id],
() => fetchHardSkills(initData?.user?.id || 0),
{
enabled: !!initData?.user?.id, // Включить запрос только когда initData.user.id доступен
refetchOnWindowFocus: false,
retry: false,
}
);
const { data: allSkills } = useQuery(
['hardSkills'], // Обратите внимание на использование массива в качестве ключа
() => fetchHardSkillsAll(),
{
refetchOnWindowFocus: false,
retry: false,
}
);
const onFinish = async (values: FormValues) => {
const hard: string[] = values.skills;
const student: Request = {
StudentID: initData?.user?.id || 0,
Name: values.Name,
Type: values.Type,
Group: values.Group,
Faculties: values.Faculties,
Phone_number: values.Phone_number,
Time: values.Time, // список времени
Soft_skills: values.Soft_skills,
Link: 'link', // предполагается, что у вас есть ссылка
Email: values.Email,
Hardskills: values.skills, // список навыков
};
const editedStudent: Omit<Student, 'StudentID'> = {
Name: values.Name,
Type: values.Type,
Group: values.Group,
Time: values.Time,
Faculties: values.Faculties,
Phone_number: values.Phone_number,
Soft_skills: values.Soft_skills,
Link: 'link',
Email: values.Email,
Hardskills: values.skills,
};
const dataBot: Bot = {
id: initData?.user?.id || 0,
};
if (data) {
const result = await editStudent(editedStudent, initData?.user?.id || 0);
if (result.status == 200) {
popup.open({
title: '',
message: 'Изменния были внесены',
buttons: [{ id: 'my-id', type: 'default', text: 'Закрыть' }],
});
}
console.log(result.status);
} else {
const result = await sendStudent(student);
console.log(result.status);
const resultbot = await sendDataBot(dataBot);
console.log(resultbot);
miniApp.close();
}
};
useEffect(() => {
if (data) {
form.setFieldsValue({
Name: data.data.Name,
Type: data.data.Type,
Group: data.data.Group,
Time: data.data.Time,
Soft_skills: data.data.Soft_skills,
Email: data.data.Email,
skills: hardSkill.data?.data.map(
(skill: { Title: string }) => skill.Title
),
});
}
}, [data, form]);
const resetResume = async () => {
const result = await deleteStudent(initData?.user?.id || 0);
if (result.status == 204) {
popup.open({
title: '',
message: 'Ваше резюме было удалено',
buttons: [{ id: 'my-id', type: 'default', text: 'Закрыть' }],
});
}
await form.resetFields();
miniApp.close();
};
const [selectedValues, setSelectedValues] = useState<string[]>([]);
const handleCheckboxChange = (checkedValues: string[]) => {
setSelectedValues(checkedValues);
};
const onFormLayoutChange = ({ layout }: { layout: LayoutType }) => {
setFormLayout(layout);
};
// if (isLoading) {
// return <span>Загрузка...</span>;
// }
return (
<Form
layout={formLayout}
form={form}
initialValues={{ layout: formLayout }}
onValuesChange={onFormLayoutChange}
onFinish={onFinish}
style={{ maxWidth: formLayout === 'inline' ? 'none' : 600 }}
className={r.form}
>
<Form.Item
label={
<label style={{ color: themeParams.textColor, fontSize: '20px' }}>
Как вас зовут?
</label>
}
className={r.form_item}
rules={[
{ required: true, message: 'Пожалуйста, введите имя и фамилию!' },
]}
name='Name'
>
<Input placeholder='Фамилия Имя' />
</Form.Item>
<Form.Item
label={
<label style={{ color: themeParams.textColor, fontSize: '20px' }}>
Факультет
</label>
}
className={r.form_item}
rules={[{ required: true, message: 'Пожалуйста, введите факультет!' }]}
name='Faculties'
>
<Input placeholder='Фамилия Имя' />
</Form.Item>
<Form.Item
label={
<label style={{ color: themeParams.textColor, fontSize: '20px' }}>
Номер телефона
</label>
}
className={r.form_item}
rules={[
{ required: true, message: 'Пожалуйста, введите номер телефона!' },
]}
name='Phone_number'
>
<Input placeholder='Фамилия Имя' />
</Form.Item>
<Form.Item
label={
<label style={{ color: themeParams.textColor, fontSize: '20px' }}>
Что вы ищите?
</label>
}
rules={[
{ required: true, message: 'Пожалуйста, выберит тип занятости!' },
]}
name='Type'
>
<Radio.Group>
<Radio.Button value='Работа'>Работу</Radio.Button>
<Radio.Button value='Стажировка'>Стажировку</Radio.Button>
</Radio.Group>
</Form.Item>
{/* <Form.Item
label={
<label
style={{
color: themeParams.textColor,
fontSize: '20px',
marginBottom: '20px',
}}
>
Что вы ищите?
</label>
}
rules={[
{ required: true, message: 'Пожалуйста, выберите тип занятости!' },
]}
name='Type'
>
<Select
mode='multiple'
allowClear
style={{ width: '100%' }}
placeholder='Выберите тип занятости'
>
<Option value='ищу работу'>Ищу работу</Option>
<Option value='ищу стажировку у партнеров'>
Ищу стажировку у партнеров
</Option>
<Option value='ищу стажировку сторонних компаний'>
Ищу стажировку сторонних компаний
</Option>
<Option value='хочу заниматься научной работой'>
Хочу заниматься научной работой
</Option>
<Option value='хочу работать в МТУСИ'>Хочу работать в МТУСИ</Option>
</Select>
</Form.Item> */}
<Form.Item
label={
<label style={{ color: themeParams.textColor, fontSize: '20px' }}>
Ваша академическая группа:
</label>
}
name='Group'
rules={[
{
required: true,
message: 'Пожалуйста, введите номер вашей группы!',
},
{
pattern: /^[А-ЯЁ]{2,3}\d{4}$/,
message: 'Введите корректный номер группы (например, БВТ2202)!',
},
]}
>
<Input placeholder=ВТ2202' style={{ width: '85px' }} maxLength={7} />
</Form.Item>
<Form.Item
label={
<label
style={{
color: themeParams.textColor,
fontSize: '20px',
marginBottom: '20px',
}}
>
Какую занятость (часов в неделю) вы рассматриваете?
</label>
}
name='Time'
>
<Checkbox.Group onChange={handleCheckboxChange} value={selectedValues}>
<Checkbox
value='20'
className={r.checkbox_button}
style={{
color: themeParams.textColor,
}}
>
20
</Checkbox>
<Checkbox
value='30'
className={r.checkbox_button}
style={{
color: themeParams.textColor,
}}
>
30
</Checkbox>
<Checkbox
value='40'
className={r.checkbox_button}
style={{
color: themeParams.textColor,
}}
>
40
</Checkbox>
</Checkbox.Group>
</Form.Item>
<Form.Item
label={
<label
style={{
color: themeParams.textColor,
fontSize: '20px',
marginBottom: '20px',
}}
>
Какими навыками вы обладаете?
</label>
}
rules={[
{ required: true, message: 'Пожалуйста, укажите ваши hard skills!' },
]}
name='skills'
>
<Select
mode='multiple'
allowClear
style={{ width: '100%' }}
placeholder='Выберите навыки'
loading={isLoading}
>
{allSkills?.map(skill => (
<Option key={skill.Hard_skillID} value={skill.Title}>
{skill.Title}
</Option>
))}
</Select>
</Form.Item>
<Form.Item
label={
<label
style={{
color: themeParams.textColor,
fontSize: '20px',
marginBottom: '20px',
}}
>
Расскажите немного о своих soft skills:
</label>
}
rules={[
{ required: true, message: 'Пожалуйста, введите ваши soft skills!' },
]}
name='Soft_skills'
>
<Input.TextArea maxLength={150} />
</Form.Item>
<Form.Item
label={
<label style={{ color: themeParams.textColor, fontSize: '20px' }}>
Оставьте почту для работодателей:
</label>
}
className={r.form_item}
rules={[
{ required: true, message: 'Пожалуйста, введите вашу почту!' },
{ type: 'email', message: 'Введите корректный email!' },
]}
name='Email'
>
<Input placeholder='example@mtuci.ru' className={r.inputs} />
</Form.Item>
<Form.Item>
<Button type='primary' htmlType='submit'>
Отправить
</Button>
</Form.Item>
{data && (
<Form.Item>
<Button danger onClick={resetResume}>
Стереть
</Button>
</Form.Item>
)}
</Form>
);
};
export default Resume;

View File

@@ -0,0 +1,38 @@
.container {
padding: 20px;
}
.form {
margin-bottom: 20px;
color: white;
}
.error {
color: red;
}
.item{
color:white;
}
.spin{
position:absolute;
left:50%;
top:50%;
transform: translate(-50%, -50%);
}
.cardWrapper {
display: flex;
flex-direction: column;
gap: 20px; /* Расстояние между карточками */
}
.card {
border: 1px solid #d9d9d9; /* Цвет и стиль обводки */
border-radius: 4px; /* Закругление углов */
padding: 16px; /* Отступы внутри карточки */
transition: box-shadow 0.3s; /* Плавный переход для теней */
&:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Тень при наведении */
}
}

View File

@@ -0,0 +1,175 @@
'use client'
import React, { useState, useEffect } from 'react';
import { useQuery } from 'react-query';
import { searchJobs } from '@/api/api';
import { Input, Button, Spin, List, Select, Checkbox, InputNumber, Card } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
import style from './SearchWidget.module.scss';
import { JobData } from '@/types/types';
import { skillsOptions } from '@/fsd/entities/Resume/data';
import { initThemeParams } from '@tma.js/sdk-react';
const { Option } = Select;
const SearchWidget: React.FC = () => {
const [query, setQuery] = useState('');
const [year, setYear] = useState<number | undefined>();
const [qualification, setQualification] = useState<boolean | undefined>();
const [time, setTime] = useState<string[]>([]);
const [salary, setSalary] = useState<number | undefined>();
const [hardskills, setHardskills] = useState<string[]>([]);
const [searchResults, setSearchResults] = useState<JobData[]>([]);
const [themeParams] = initThemeParams();
// Функция для формирования объекта параметров
const buildQueryParams = () => {
const params: any = {}; // Или укажите более точный тип, соответствующий JobsSearch
if (year !== undefined) {
params.year = year;
}
if (qualification !== undefined) {
params.qualification = qualification;
}
if (time.length > 0) {
params.time = time; // здесь может потребоваться изменить на массив строк
}
if (salary !== undefined) {
params.salary = salary;
}
if (hardskills.length > 0) {
params.hardskills = hardskills;
}
if (query) {
params.search = query;
}
return params; // Возвращаем объект вместо строки
};
// Вызов useQuery с объектом
const { data, error, isLoading, refetch } = useQuery<JobData[]>(
['searchJobs', buildQueryParams()],
() => {
const queryParams = buildQueryParams(); // Получаем объект параметров
return searchJobs(queryParams); // Передаём объект в функцию
},
{ enabled: false, refetchOnWindowFocus: false, retry: false }
);
// Используем useEffect для обновления searchResults, когда запрос завершен
useEffect(() => {
if (data) {
setSearchResults(data);
}
}, [data]);
const handleSearch = () => {
refetch();
};
if (isLoading) return <Spin size='large' className={style.spin} />;
if (error)
return <div className={style.error}>Ошибка при поиске вакансий</div>;
return (
<div className={style.container}>
<div className={style.form}>
<Input
placeholder='Поиск по названию вакансии или компании...'
value={query}
onChange={e => setQuery(e.target.value)}
prefix={<SearchOutlined />}
style={{ marginBottom: 20 }}
/>
<Select
placeholder='Выберите Курс'
style={{ width: '100%', marginBottom: 20 }}
onChange={value => setYear(value)}
value={year}
>
<Option value={1}>1</Option>
<Option value={2}>2</Option>
<Option value={3}>3</Option>
<Option value={4}>4</Option>
{/* Добавьте другие годы по необходимости */}
</Select>
<Checkbox
checked={qualification}
onChange={e => setQualification(e.target.checked)}
style={{ marginBottom: 20, color: themeParams.textColor }}
>
Требуется квалификация
</Checkbox>
<Select
mode='multiple'
placeholder='Выберите занятость'
style={{ width: '100%', marginBottom: 20 }}
onChange={value => setTime(value)}
value={time}
>
<Option value='20'>20</Option>
<Option value='30'>30</Option>
<Option value='40'>40</Option>
</Select>
<InputNumber
placeholder='Минимальная зарплата'
value={salary}
onChange={value => setSalary(value !== null ? value : undefined)}
style={{ width: '100%', marginBottom: 20 }}
/>
<Select
mode='multiple'
placeholder='Выберите hard skills'
style={{ width: '100%', marginBottom: 20 }}
onChange={value => setHardskills(value)}
value={hardskills}
options={skillsOptions.map(skill => ({ value: skill, label: skill }))}
/>
<Button type='primary' onClick={handleSearch} style={{ width: '100%' }}>
Поиск
</Button>
</div>
<div style={{ marginTop: 20 }}>
{isLoading && <Spin size='large' />}
{searchResults.length > 0 ? (
<div className={style.cardWrapper}>
{searchResults.map((item: JobData) => (
<Card
key={item.Email} // Предположим, что Email уникален для вакансий
title={item.Job_name}
bordered={false}
style={{ marginBottom: 20 }}
className={style.card}
>
<p>Год: {item.Year}</p>
<p>Квалификация: {item.Qualification ? 'Да' : 'Нет'}</p>
<p>Зарплата: {item.Salary}</p>
<p>Soft Skills: {item.Soft_skills}</p>
<p>Обязанности: {item.Responsibilities}</p>
<p>
Email: <a href={`mailto:${item.Email}`}>{item.Email}</a>
</p>
</Card>
))}
</div>
) : (
!isLoading && (
<div style={{ color: themeParams.textColor }}>
Вакансии не найдены
</div>
)
)}
</div>
</div>
);
};
export default SearchWidget;