diff --git a/app/frontend/src/components/shared/VacancyCard/index.ts b/app/frontend/src/components/shared/VacancyCard/index.ts new file mode 100644 index 00000000..d092a135 --- /dev/null +++ b/app/frontend/src/components/shared/VacancyCard/index.ts @@ -0,0 +1 @@ +export { VacancyCard } from './ui/VacancyCard' \ No newline at end of file diff --git a/app/frontend/src/components/shared/VacancyCard/ui/VacancyCard.tsx b/app/frontend/src/components/shared/VacancyCard/ui/VacancyCard.tsx new file mode 100644 index 00000000..96bca65a --- /dev/null +++ b/app/frontend/src/components/shared/VacancyCard/ui/VacancyCard.tsx @@ -0,0 +1,176 @@ +import React from "react"; +import type { VacancyCardProps } from "../../../../types"; +import { Card, Group, Text, Badge, Button, Stack, Box } from '@mantine/core'; +import { Building2, MapPin, ChevronDown, ChevronUp } from "lucide-react"; +import { useState } from "react"; +import { Link } from "@inertiajs/react"; + +interface VacancyCardPropsWrapper { + props: VacancyCardProps; +} + +export const VacancyCard: React.FC = ({ props }) => { + + const { id, title, url, salary, employment, company, city, skills } = props; + + const [skillsExpanded, setSkillsExpanded] = useState(false); + + const skillsCutDesktop = skills.slice(0,12); + const remainingSkillsCount = skills.length - 3; + const hasMoreSkills = skills.length > 3; + + const displayedSkills = skillsExpanded ? skills : skills.slice(0, 3); + + return ( + + + {/* Десктопная версия */} + + {/* Левая часть */} + + + {/* Название вакансии */} + {title} + + {/* Информация о компании */} + + {company ? + + + {company.name} + : + Название компании не указано + } + {city ? + + + {city.name} + : + Город не указан + } + {employment} + + + {/* Навыки */} + + {skills && skills.length > 0 ? ( + skillsCutDesktop.map((skill) => ( + + {skill} + + )) + ) : ( + Необходимые навыки не указаны + )} + + + + + {/* Правая часть */} + + + {salary ? + {salary} : + Зарплата не указана + } + + + + + {/* Мобильная версия */} + + {/* Название вакансии */} + {title} + + {/* Компания и город */} + + {company ? + + + {company.name} + : + Название компании не указано + } + {city ? + + + {city.name} + : + Город не указан + } + + + {/* Формат работы */} + {employment} + + {/* Зарплата */} + {salary ? + {salary} : + Зарплата не указана + } + + {/* Навыки */} + + + {skills && skills.length > 0 ? ( + <> + {displayedSkills.map((skill) => ( + + {skill} + + ))} + + {/* Кнопка для показа/скрытия навыков */} + {hasMoreSkills && ( + + )} + + ) : ( + Необходимые навыки не указаны + )} + + + + + + + + ); +}; + diff --git a/app/frontend/src/main.tsx b/app/frontend/src/main.tsx index 9ef434e0..003d4f14 100644 --- a/app/frontend/src/main.tsx +++ b/app/frontend/src/main.tsx @@ -1,6 +1,8 @@ import { createRoot } from "react-dom/client"; import { createInertiaApp } from "@inertiajs/react"; import { InertiaProgress } from '@inertiajs/progress'; +import { MantineProvider } from "@mantine/core"; +import '@mantine/core/styles.css'; import axios from 'axios'; import React from 'react'; @@ -15,7 +17,7 @@ document.addEventListener('DOMContentLoaded', () => { resolve: (name) => import(`./components/pages/${name}.tsx`), setup: ({ el, App, props }) => { const root = createRoot(el); - root.render(React.createElement(App, props)); + root.render(React.createElement(MantineProvider, {}, React.createElement(App, props))); }, }); }); diff --git a/app/frontend/src/types/index.ts b/app/frontend/src/types/index.ts index 63f4bcb9..87a14921 100644 --- a/app/frontend/src/types/index.ts +++ b/app/frontend/src/types/index.ts @@ -4,3 +4,21 @@ export interface User { avatarUrl: string; email: string; } + +export interface VacancyCardProps { + id: number; + title: string; + url?: string; + salary: string; + experience?: string; + employment?: string; + company?: { + id: number; + name: string; + }; + city?: { + id: number; + name: string; + }; + skills: string[]; +}