copied the code from the working repo
This commit is contained in:
12
mtucijobsweb/.dockerignore
Normal file
12
mtucijobsweb/.dockerignore
Normal file
@@ -0,0 +1,12 @@
|
||||
**/node_modules
|
||||
*.md
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
**/.npmignore
|
||||
**/.dockerignore
|
||||
**/*.md
|
||||
**/*.log
|
||||
**/.vscode
|
||||
**/.git
|
||||
**/.eslintrc.json
|
||||
*.sh
|
||||
4
mtucijobsweb/.eslintrc.js
Normal file
4
mtucijobsweb/.eslintrc.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['next', 'next/core-web-vitals'],
|
||||
};
|
||||
35
mtucijobsweb/.gitignore
vendored
Normal file
35
mtucijobsweb/.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
18
mtucijobsweb/Dockerfile
Normal file
18
mtucijobsweb/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM node:21.5.0-alpine3.19
|
||||
|
||||
ARG NEXT_PUBLIC_APP_BASE_URL
|
||||
ARG NEXT_PUBLIC_BOT_URL
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV NEXT_PUBLIC_APP_BASE_URL=$NEXT_PUBLIC_APP_BASE_URL
|
||||
ENV NEXT_PUBLIC_BOT_URL=$NEXT_PUBLIC_BOT_URL
|
||||
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm i
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
36
mtucijobsweb/README.md
Normal file
36
mtucijobsweb/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
77
mtucijobsweb/api/api.ts
Normal file
77
mtucijobsweb/api/api.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { FormValues, JobsSearch, Request, Student } from '@/types/types';
|
||||
import { $mtuciApi } from './axiosInstance';
|
||||
|
||||
export const sendStudent = async (postData: Request) => {
|
||||
try {
|
||||
const response = await $mtuciApi.post(`/students/`, postData);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error post student:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const editStudent = async (
|
||||
postData: Omit<Student, 'StudentID'>,
|
||||
id: number
|
||||
) => {
|
||||
try {
|
||||
const response = await $mtuciApi.put(`/students/${id}`, postData);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error post student:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchStudent = async (id: number) => {
|
||||
try {
|
||||
const response = await $mtuciApi.get(`/students/${id}`);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error fetching student:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
export const deleteStudent = async (id: number) => {
|
||||
try {
|
||||
const response = await $mtuciApi.delete(`/students/${id}`);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error fetching student:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchHardSkills = async (id: number) => {
|
||||
try {
|
||||
const response = await $mtuciApi.get(`/students/hardskills/${id}`);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error fetching student:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const searchJobs = async (params: JobsSearch) => {
|
||||
try {
|
||||
const response = await $mtuciApi.get('/students/jobs-search/', { params });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error search jobs', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const fetchHardSkillsAll = async (): Promise<
|
||||
{ Hard_skillID: number; Title: string }[]
|
||||
> => {
|
||||
try {
|
||||
const response = await $mtuciApi.get(`/services/hardskills/`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching hard skills:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
10
mtucijobsweb/api/axiosInstance.ts
Normal file
10
mtucijobsweb/api/axiosInstance.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export const $mtuciApi = axios.create({
|
||||
baseURL: process.env.NEXT_PUBLIC_APP_BASE_URL, // NEXT_PUBLIC_ для переменных окружения, доступных на клиенте
|
||||
headers: {
|
||||
Accept: '*/*',
|
||||
'X-API-KEY':
|
||||
'SbRHOVoK97GKCx3Lqx6hKXLbZZJEd0GTGbeglXdpK9PhSB9kpr4eWCsuIIwnD6F2mgpTDlVHFCRbeFmuSfqBVsb12lNwF3P1tmdxiktl7zH9sDS2YK7Pyj2DecCWAZ3n',
|
||||
},
|
||||
});
|
||||
15
mtucijobsweb/api/bot.ts
Normal file
15
mtucijobsweb/api/bot.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Bot } from '@/types/types';
|
||||
import axios from 'axios';
|
||||
|
||||
export const sendDataBot = async (dataBot: Bot) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${process.env.NEXT_PUBLIC_BOT_URL}/api/resume/`,
|
||||
dataBot
|
||||
);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error post student:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
27
mtucijobsweb/app/layout.tsx
Normal file
27
mtucijobsweb/app/layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import ClientProvider from '@/fsd/app/providers/ClientProvider';
|
||||
import { TmaSDKLoader } from '@/fsd/app/providers/TmaSDKLoader';
|
||||
import type { Metadata } from 'next';
|
||||
import { Inter } from 'next/font/google';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<TmaSDKLoader>
|
||||
<ClientProvider>
|
||||
<html lang='en'>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
</ClientProvider>
|
||||
</TmaSDKLoader>
|
||||
);
|
||||
}
|
||||
20
mtucijobsweb/app/page.tsx
Normal file
20
mtucijobsweb/app/page.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import LoadingPage from '@/fsd/pages/Loading';
|
||||
import MainPage from '@/fsd/pages/MainPage';
|
||||
import { useInitData } from '@tma.js/sdk-react';
|
||||
|
||||
export default function Home() {
|
||||
const initData = useInitData(true);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{initData?.user?.id !== undefined ? (
|
||||
<MainPage id={initData?.user?.id} />
|
||||
) : (
|
||||
<LoadingPage />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
15
mtucijobsweb/app/search/page.tsx
Normal file
15
mtucijobsweb/app/search/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import LoadingPage from '@/fsd/pages/Loading';
|
||||
import SearchPage from '@/fsd/pages/SearchPage';
|
||||
import { useInitData } from '@tma.js/sdk-react';
|
||||
|
||||
const Search = () => {
|
||||
const initData = useInitData(true);
|
||||
|
||||
return (
|
||||
<>{initData?.user?.id !== undefined ? <SearchPage /> : <LoadingPage />}</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
||||
7
mtucijobsweb/build.sh
Normal file
7
mtucijobsweb/build.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
docker build . \
|
||||
--build-arg APP_BASE_URL="" \
|
||||
--no-cache \
|
||||
--rm \
|
||||
--pull \
|
||||
-t mtuci-jobs-web-image:latest
|
||||
14
mtucijobsweb/fsd/app/providers/ClientProvider.tsx
Normal file
14
mtucijobsweb/fsd/app/providers/ClientProvider.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
||||
const ClientProvider = ({ children }: PropsWithChildren) => {
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClientProvider;
|
||||
13
mtucijobsweb/fsd/app/providers/TmaSDKLoader.tsx
Normal file
13
mtucijobsweb/fsd/app/providers/TmaSDKLoader.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { SDKProvider } from '@tma.js/sdk-react';
|
||||
|
||||
|
||||
export function TmaSDKLoader({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<SDKProvider acceptCustomStyles debug>
|
||||
{children}
|
||||
</SDKProvider>
|
||||
);
|
||||
}
|
||||
109
mtucijobsweb/fsd/entities/Resume/data.ts
Normal file
109
mtucijobsweb/fsd/entities/Resume/data.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export const skillsOptions = [
|
||||
'C',
|
||||
'C++',
|
||||
'C#',
|
||||
'Java',
|
||||
'Python',
|
||||
'Ruby',
|
||||
'Ruby on Rails',
|
||||
'R',
|
||||
'Matlab',
|
||||
'Django',
|
||||
'NetBeans',
|
||||
'Scala',
|
||||
'JavaScript',
|
||||
'TypeScript',
|
||||
'Go',
|
||||
'Software Development',
|
||||
'Application Development',
|
||||
'Web Applications',
|
||||
'Object Oriented Programming',
|
||||
'Aspect Oriented Programming',
|
||||
'Concurrent Programming',
|
||||
'Mobile Development (iOS)',
|
||||
'Mobile Development (Android)',
|
||||
'Data Science',
|
||||
'Data Analytics',
|
||||
'Data Mining',
|
||||
'Data Visualization',
|
||||
'Machine Learning',
|
||||
'TensorFlow',
|
||||
'PyTorch',
|
||||
'Keras',
|
||||
'Theano',
|
||||
'Statistical Analysis',
|
||||
'Bayesian Analysis',
|
||||
'Regression Analysis',
|
||||
'Time Series Analysis',
|
||||
'Clustering',
|
||||
'K-means',
|
||||
'KNN',
|
||||
'Decision Trees',
|
||||
'Random Forest',
|
||||
'Dimensionality Reduction',
|
||||
'PCA',
|
||||
'SVD',
|
||||
'Gradient Descent',
|
||||
'Stochastic Gradient Descent',
|
||||
'Outlier Detection',
|
||||
'Frequent Itemset Mining',
|
||||
'SQL',
|
||||
'NoSQL',
|
||||
'SQL Server',
|
||||
'MS SQL Server',
|
||||
'Apache Hadoop',
|
||||
'Apache Spark',
|
||||
'Apache Airflow',
|
||||
'Apache Impala',
|
||||
'Apache Drill',
|
||||
'HTML',
|
||||
'CSS',
|
||||
'React',
|
||||
'Angular',
|
||||
'Vue.js',
|
||||
'Node.js',
|
||||
'Express.js',
|
||||
'REST',
|
||||
'SOAP',
|
||||
'Web Platforms',
|
||||
'System Architecture',
|
||||
'Distributed Computing',
|
||||
'AWS',
|
||||
'AWS Glue',
|
||||
'Azure',
|
||||
'Google Cloud Platform',
|
||||
'Docker',
|
||||
'Kubernetes',
|
||||
'UNIX',
|
||||
'Linux',
|
||||
'Windows',
|
||||
'MacOS',
|
||||
'Embedded Hardware',
|
||||
'Debugging',
|
||||
'Unit Testing',
|
||||
'Integration Testing',
|
||||
'System Testing',
|
||||
'Code Review',
|
||||
'Git',
|
||||
'SVN',
|
||||
'CI/CD',
|
||||
'Software Documentation',
|
||||
'IDE',
|
||||
'CASE Tools',
|
||||
'Computational Complexity',
|
||||
'Algorithm Design',
|
||||
'Data Structures',
|
||||
'Mathematical Modeling',
|
||||
'Statistics',
|
||||
'Technical Writing',
|
||||
'Technical Support',
|
||||
'System Design',
|
||||
'System Development',
|
||||
'Technical Guidance',
|
||||
'Client Interface',
|
||||
'Vendor Interface',
|
||||
'Emerging Technologies',
|
||||
'Jira',
|
||||
'Trello',
|
||||
'Software Architecture',
|
||||
];
|
||||
15
mtucijobsweb/fsd/pages/Loading.tsx
Normal file
15
mtucijobsweb/fsd/pages/Loading.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
'use client'
|
||||
import React from 'react';
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { Space, Spin } from 'antd';
|
||||
|
||||
const LoadingPage: React.FC = () => {
|
||||
return (
|
||||
// <Space>
|
||||
// <Spin indicator={<LoadingOutlined style={{ fontSize: 48, position:'absolute', left:'50%', top: '50%', transform:'translate(-50%, -50%)' }} spin />} />
|
||||
// </Space>
|
||||
<h3 style={{position: 'absolute', left:'50%', top:'50%', transform: 'translate(-50%, -50%)', color: 'white'}}>Загрузака...</h3>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingPage;
|
||||
17
mtucijobsweb/fsd/pages/MainPage.tsx
Normal file
17
mtucijobsweb/fsd/pages/MainPage.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
'use client';
|
||||
|
||||
import Resume from "../widgets/Resume/Resume";
|
||||
|
||||
|
||||
interface MainPageProps {
|
||||
id: number;
|
||||
}
|
||||
const MainPage: React.FC<MainPageProps> = ({ id }) => {
|
||||
return (
|
||||
<div >
|
||||
<Resume/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainPage;
|
||||
13
mtucijobsweb/fsd/pages/SearchPage.tsx
Normal file
13
mtucijobsweb/fsd/pages/SearchPage.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import SearchWidget from '../widgets/Search/SearchWidget';
|
||||
|
||||
const SearchPage: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<SearchWidget />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchPage;
|
||||
39
mtucijobsweb/fsd/widgets/Resume/Resume.module.scss
Normal file
39
mtucijobsweb/fsd/widgets/Resume/Resume.module.scss
Normal 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;
|
||||
// }
|
||||
403
mtucijobsweb/fsd/widgets/Resume/Resume.tsx
Normal file
403
mtucijobsweb/fsd/widgets/Resume/Resume.tsx
Normal 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;
|
||||
38
mtucijobsweb/fsd/widgets/Search/SearchWidget.module.scss
Normal file
38
mtucijobsweb/fsd/widgets/Search/SearchWidget.module.scss
Normal 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); /* Тень при наведении */
|
||||
}
|
||||
}
|
||||
175
mtucijobsweb/fsd/widgets/Search/SearchWidget.tsx
Normal file
175
mtucijobsweb/fsd/widgets/Search/SearchWidget.tsx
Normal 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;
|
||||
10
mtucijobsweb/next.config.js
Normal file
10
mtucijobsweb/next.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: false,
|
||||
env: {
|
||||
NEXT_PUBLIC_APP_BASE_URL: process.env.NEXT_PUBLIC_APP_BASE_URL,
|
||||
NEXT_PUBLIC_BOT_URL: process.env.NEXT_PUBLIC_BOT_URL,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
6178
mtucijobsweb/package-lock.json
generated
Normal file
6178
mtucijobsweb/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
mtucijobsweb/package.json
Normal file
35
mtucijobsweb/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "mtucijobsweb",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tma.js/launch-params": "^1.0.1",
|
||||
"@tma.js/sdk": "^2.5.1",
|
||||
"@tma.js/sdk-react": "^2.2.5",
|
||||
"antd": "^5.18.2",
|
||||
"axios": "^1.7.2",
|
||||
"dotenv": "^16.4.5",
|
||||
"next": "13.5.6",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-query": "^3.39.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"autoprefixer": "^10",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-next": "^13.5.6",
|
||||
"postcss": "^8",
|
||||
"sass": "^1.77.6",
|
||||
"tailwindcss": "^3",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
6
mtucijobsweb/postcss.config.js
Normal file
6
mtucijobsweb/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
1
mtucijobsweb/public/next.svg
Normal file
1
mtucijobsweb/public/next.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
mtucijobsweb/public/vercel.svg
Normal file
1
mtucijobsweb/public/vercel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
||||
|
After Width: | Height: | Size: 629 B |
15
mtucijobsweb/run.sh
Normal file
15
mtucijobsweb/run.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker stop mtuci-jobs-web || echo "1"
|
||||
docker rm mtuci-jobs-web || echo "2"
|
||||
|
||||
docker run -d \
|
||||
--restart always \
|
||||
-h mtuci-jobs-web \
|
||||
-p 127.0.0.1:3000:3000 \
|
||||
-e APP_BASE_URL="" \
|
||||
--name mtuci-jobs-web \
|
||||
--log-opt mode=non-blocking \
|
||||
--log-opt max-size=10m \
|
||||
--log-opt max-file=3 \
|
||||
mtuci-jobs-web-image:latest
|
||||
20
mtucijobsweb/tailwind.config.ts
Normal file
20
mtucijobsweb/tailwind.config.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { Config } from 'tailwindcss'
|
||||
|
||||
const config: Config = {
|
||||
content: [
|
||||
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
||||
'gradient-conic':
|
||||
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
export default config
|
||||
27
mtucijobsweb/tsconfig.json
Normal file
27
mtucijobsweb/tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
60
mtucijobsweb/types/types.ts
Normal file
60
mtucijobsweb/types/types.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
interface Time {
|
||||
hour: string;
|
||||
}
|
||||
export interface FormValues {
|
||||
Name: string;
|
||||
Type: string;
|
||||
Group: string;
|
||||
Faculties:string;
|
||||
Phone_number:string;
|
||||
Time: Time[];
|
||||
skills: string[];
|
||||
Soft_skills: string;
|
||||
Email: string;
|
||||
}
|
||||
|
||||
export interface Student extends Omit<FormValues, 'skills'> {
|
||||
StudentID: number;
|
||||
Link: string;
|
||||
Hardskills: string[];
|
||||
}
|
||||
|
||||
type HardSkill = {
|
||||
Title: string;
|
||||
};
|
||||
export interface Request {
|
||||
StudentID: number;
|
||||
Name: string;
|
||||
Type: string;
|
||||
Group: string;
|
||||
Faculties: string;
|
||||
Phone_number: string;
|
||||
Time: Time[]; // массив строк для времени
|
||||
Soft_skills: string;
|
||||
Link: string;
|
||||
Email: string;
|
||||
Hardskills: string[]; // массив навыков
|
||||
}
|
||||
export interface Bot {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface JobsSearch {
|
||||
year?: string;
|
||||
qualification?: boolean;
|
||||
time?: string[];
|
||||
salary?: number;
|
||||
hardskills?: string[];
|
||||
search?: string;
|
||||
}
|
||||
export interface JobData {
|
||||
JobID: number;
|
||||
Company_name: string;
|
||||
Job_name: string;
|
||||
Year: string;
|
||||
Qualification: boolean;
|
||||
Soft_skills: string;
|
||||
Salary: number;
|
||||
Email: string;
|
||||
Responsibilities: string;
|
||||
}
|
||||
Reference in New Issue
Block a user