oop
This commit is contained in:
21
components/avatar-image.tsx
Normal file
21
components/avatar-image.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
imageUrl: string;
|
||||
}
|
||||
|
||||
const AvatarImage: React.FC<Props> =({ imageUrl, className}) => {
|
||||
return(
|
||||
<Image
|
||||
src={`/images/${imageUrl}.png`}
|
||||
alt={imageUrl}
|
||||
className={`${className} relative`}
|
||||
width={67}
|
||||
height={67}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default AvatarImage;
|
37
components/breadcrumb.tsx
Normal file
37
components/breadcrumb.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
"use client";
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import React from 'react';
|
||||
|
||||
const Breadcrumbs: React.FC = () => {
|
||||
const pathname = usePathname();
|
||||
const pathSegments = pathname.split('/').filter(segment => segment);
|
||||
|
||||
const correctName = {
|
||||
events: "мероприятия сообщества",
|
||||
event: "базовая программа подготовки гештальт-терапевтов - добор",
|
||||
participants: "участники",
|
||||
id: "кириллов кирилл кириллович"
|
||||
}
|
||||
|
||||
return(
|
||||
<nav className="max-w-[1032px] mx-auto">
|
||||
<ol className="flex text-middleGrey font-[350] my-[50px]">
|
||||
<li>
|
||||
<Link href="/">главная</Link>
|
||||
</li>
|
||||
{pathSegments.map((segment, index) => {
|
||||
const href = '/' + pathSegments.slice(0, index + 1).join('/');
|
||||
return (
|
||||
<li key={href}>
|
||||
<Link href={href}><span className="mx-[2px]">/</span>{correctName[decodeURIComponent(segment) as keyof typeof correctName]}</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default Breadcrumbs;
|
34
components/button.tsx
Normal file
34
components/button.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
name: string;
|
||||
variant?: 'default' | 'secondary';
|
||||
size?: 'default';
|
||||
}
|
||||
|
||||
const Button: React.FC<Props> = ({ className, name, variant = 'default', size = 'default' }) => {
|
||||
const baseClasses = 'flex items-center justify-center whitespace-nowrap rounded-[6px] font-[400]';
|
||||
|
||||
const variantClasses = {
|
||||
default: 'bg-blue text-[16px] text-white',
|
||||
secondary: 'text-[16px] text-blue border-[1px] border-blue',
|
||||
};
|
||||
|
||||
const sizeClasses = {
|
||||
default: 'max-h-[39px] px-[25px] py-[10px]',
|
||||
};
|
||||
|
||||
const selectedVariantClasses = variantClasses[variant] || variantClasses.default;
|
||||
const selectedSizeClasses = sizeClasses[size] || sizeClasses.default;
|
||||
|
||||
return (
|
||||
<button className={`${baseClasses} ${selectedVariantClasses} ${selectedSizeClasses} ${className}`}>
|
||||
{name}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default Button;
|
23
components/event-card.tsx
Normal file
23
components/event-card.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
const EventCard: React.FC<Props> = ({title, description, image}) => {
|
||||
return (
|
||||
<Link href="events/event" className="border-[1px] border-white rounded-[6px] bg-darkGrey px-[13.5px] pt-[12px] pb-[35px] text-black">
|
||||
{image ? <Image src={image} alt='image' width={288} height={177} /> :
|
||||
<span className="flex w-[288px] h-[177px] bg-white"/>
|
||||
}
|
||||
<h5 className="max-w-[267px] font-[500] text-[16px] leading-[18px] mt-[30px] mb-[8px] line-clamp-3 text-ellipsis">{title}</h5>
|
||||
<p className="max-w-[267px] text-[15px] leading-[17px] line-clamp-3 text-ellipsis">{description}</p>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default EventCard;
|
25
components/filterCheckbox.tsx
Normal file
25
components/filterCheckbox.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const FilterCheckbox: React.FC = () => {
|
||||
const [active, setActive] = useState(false);
|
||||
|
||||
const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setActive(event.target.checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<label className="flex items-center text-[14px] text-lightGrey gap-[12px]">
|
||||
<input
|
||||
className="w-[16px] h-[16px] border-white"
|
||||
type="checkbox"
|
||||
checked={active}
|
||||
onChange={handleCheckboxChange}
|
||||
/>
|
||||
только мероприятия Донецкого сообщества
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterCheckbox;
|
28
components/footer.tsx
Normal file
28
components/footer.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
import SocialItem from "@/components/social-item";
|
||||
import Link from 'next/link';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
return (
|
||||
<footer className="container pb-8 px-8 m-auto flex justify-center mt-[80px]">
|
||||
<a href="/public">
|
||||
<Image width={150} height={48} src="/logo.svg" alt="logo"/>
|
||||
</a>
|
||||
<div className="flex gap-[96px] text-[15px] text-dark ml-[108px] mr-[188px] items-center font-[350]">
|
||||
<Link href="/events">События</Link>
|
||||
<Link href="/participants">Участники</Link>
|
||||
</div>
|
||||
<div className="flex gap-[25px]">
|
||||
<Image width={83} height={21} src="/images/oppgp.svg" alt="oop"/>
|
||||
<Image width={96} height={27} src="/images/pmg.svg" alt="pmg"/>
|
||||
</div>
|
||||
<div className="flex ml-[49px] gap-[29px]">
|
||||
<SocialItem icon="logo_tg" link=""/>
|
||||
<SocialItem icon="logo_vk" link=""/>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
export default Footer;
|
24
components/header.tsx
Normal file
24
components/header.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
import SocialItem from "@/components/social-item";
|
||||
import Link from 'next/link';
|
||||
|
||||
const Header: React.FC = () => {
|
||||
return (
|
||||
<header className="flex pt-8 px-8 justify-center items-center container m-auto">
|
||||
<div className="flex gap-[96px] text-[15px] text-dark mr-[220px] font-[350]">
|
||||
<Link href="/events">События</Link>
|
||||
<Link href="/participants">Участники</Link>
|
||||
</div>
|
||||
<Link href="/">
|
||||
<Image width={150} height={48} src="/logo.svg" alt="logo" />
|
||||
</Link>
|
||||
<div className="flex ml-[340px] gap-[19px]">
|
||||
<SocialItem icon="logo_tg" link=""/>
|
||||
<SocialItem icon="logo_vk" link=""/>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
77
components/human-card.tsx
Normal file
77
components/human-card.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
import InfoItem from "@/components/info-item";
|
||||
import Link from "next/link";
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
description?: string;
|
||||
skills: string[];
|
||||
post?: string;
|
||||
image?: string;
|
||||
variant?: 'default' | 'secondary' | 'lg';
|
||||
}
|
||||
|
||||
const HumanCard: React.FC<Props> = ({name, description, image, skills,post, variant = 'default'}) => {
|
||||
const cardStyles = {
|
||||
default: {
|
||||
main: "",
|
||||
img: {
|
||||
w: 221,
|
||||
h: 221
|
||||
},
|
||||
name: "text-[18px]",
|
||||
|
||||
},
|
||||
secondary: {
|
||||
main: "flex w-full justify-between",
|
||||
img: {
|
||||
w: 218,
|
||||
h: 232
|
||||
}
|
||||
},
|
||||
lg: {
|
||||
main: "",
|
||||
img: {
|
||||
w: 280,
|
||||
h: 280
|
||||
},
|
||||
name: "text-[20px]",
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Link href="/participants/id" className={`${cardStyles[variant].main} backdrop-blur-custom border-[1px] border-white rounded-[6px] bg-darkWhite p-[10px] text-black w-fit shadow-custom`}>
|
||||
{image ? <Image src={image} alt='image' width={cardStyles[variant].img.w} height={cardStyles[variant].img.h} /> :
|
||||
<Image src="/images/mok_human.svg" alt='image' width={cardStyles[variant].img.w} height={cardStyles[variant].img.h} className="rotate-[180deg]" />
|
||||
}
|
||||
{(variant === 'default' || variant === 'lg') &&
|
||||
<>
|
||||
<h5 className={`${cardStyles[variant].name} font-[400] leading-[20px] max-w-[190px] mt-[16px] mb-[20px]`}>{name}</h5>
|
||||
<span className="text-lightGrey">
|
||||
Работает с темами
|
||||
<p className="text-black text-[13px] leading-[15px] line-clamp-3 text-ellipsis max-w-[220px] mb-[16px]">{description}</p>
|
||||
</span>
|
||||
<div className="flex gap-[5px] max-w-[220px] flex-wrap">
|
||||
{skills.map((skill, index) => {
|
||||
return <InfoItem name={skill} key={index} />
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{variant === 'secondary' &&
|
||||
<div className="flex flex-col ml-[16px]">
|
||||
<h5 className="font-[400] text-[21px] leading-[25px] max-w-[204px] mt-[8px] mb-[18px]">{name}</h5>
|
||||
<span className="text-[15px] text-lightGrey mb-[55px]">{post}</span>
|
||||
<div className="flex gap-[7px] max-w-[220px] flex-wrap">
|
||||
{skills.map((skill, index) => {
|
||||
return <InfoItem name={skill} key={index} variable="white"/>
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default HumanCard;
|
13
components/info-block.tsx
Normal file
13
components/info-block.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
|
||||
const InfoBlock: React.FC = () => {
|
||||
return (
|
||||
<div className="flex bg-blue rounded-[25px] relative max-w-[1032px] w-full m-auto pt-[58px] pb-[61px] pl-[69px] h-[234px]">
|
||||
<p className="text-white text-[32px] max-w-[671px] font-[500]">Через собственное развитие мы развиваем и популяризируем гештальт-подход</p>
|
||||
<Image className="absolute right-[39px] top-[-20px]" src="/images/palm.svg" alt="palm" width={217} height={254} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoBlock;
|
30
components/info-item.tsx
Normal file
30
components/info-item.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
variable?: 'default' | 'lg' | 'secondary' | 'white' | 'lgFixed' | 'whiteFixed';
|
||||
pathImg?: string;
|
||||
}
|
||||
|
||||
const InfoItem: React.FC<Props> = ({name, variable = 'default', pathImg}) => {
|
||||
const variantClasses = {
|
||||
default: 'text-[9px] py-[4px] px-[8px] rounded-[3px] border-[0.5px] text-blue',
|
||||
secondary: 'text-[16px] text-lightBlack border-[1px] rounded-[6px] py-[10px] px-[28px] gap-[10px]',
|
||||
lg: 'px-[10px] py-[6.5px] rounded-[6px] border-[1px] text-[16px] text-blue',
|
||||
lgFixed: 'px-[10px] py-[6.5px] max-w-[140px] w-full rounded-[6px] border-[1px] text-[16px] text-blue',
|
||||
white: 'text-[10px] py-[9px] px-[16px] text-blue bg-white rounded-[6px]',
|
||||
whiteFixed: 'tex-[14px] rounded-[6px] max-w-[114px] w-full bg-white py-[5px] text-blue'
|
||||
};
|
||||
|
||||
const selectedVariantClasses = variantClasses[variable] || variantClasses.default;
|
||||
|
||||
return (
|
||||
<span className={`flex items-center justify-center font-[400] border-blue w-fit ${selectedVariantClasses}`}>
|
||||
{pathImg && <Image src={`/images/${pathImg}.svg`} alt={pathImg} width={18} height={18} />}
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default InfoItem
|
22
components/nav-section.tsx
Normal file
22
components/nav-section.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import InfoItem from "@/components/info-item";
|
||||
import SectionItem from "@/components/section-item";
|
||||
|
||||
const NavSection: React.FC= () => {
|
||||
const navigations : string[] = ["конференции", "акредитации", "открытые сертификации", "открытые сертификации", "общие сборы", "протоколы сборов", "новости сайта", "календарь событий", "сайт мги", "вопросы и ответы"]
|
||||
return (
|
||||
<div className="flex gap-[53px] items-end m-auto">
|
||||
<div className="flex max-w-[645px] flex-wrap gap-x-[15px] gap-y-[12px]">
|
||||
{navigations.map((nav, index) => {
|
||||
return <InfoItem name={nav} key={index} variable="lg" />
|
||||
})}
|
||||
</div>
|
||||
<div className="flex gap-[19px]">
|
||||
<SectionItem name="Книги" />
|
||||
<SectionItem name="лекции" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavSection
|
83
components/pagination.tsx
Normal file
83
components/pagination.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
|
||||
interface PaginationProps {
|
||||
totalItems: number;
|
||||
itemsPerPage: number;
|
||||
currentPage: number;
|
||||
onPageChange: (page: number) => void;
|
||||
}
|
||||
|
||||
const Pagination: React.FC<PaginationProps> = ({
|
||||
totalItems,
|
||||
itemsPerPage,
|
||||
currentPage,
|
||||
onPageChange,
|
||||
}) => {
|
||||
const totalPages = Math.ceil(totalItems / itemsPerPage);
|
||||
|
||||
const getPaginationArray = () => {
|
||||
const pages: (number | string)[] = [];
|
||||
|
||||
if (totalPages <= 5) {
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
pages.push(1);
|
||||
|
||||
if (currentPage > 3) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
for (let i = Math.max(2, currentPage - 1); i <= Math.min(currentPage + 1, totalPages - 1); i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
|
||||
if (currentPage < totalPages - 2) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
pages.push(totalPages);
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
if (page !== currentPage) {
|
||||
onPageChange(page);
|
||||
}
|
||||
};
|
||||
|
||||
const paginationArray = getPaginationArray();
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<button className="mr-[30px]" onClick={() => handlePageChange(currentPage - 1)} disabled={currentPage === 1}>
|
||||
<Image className="rotate-[90deg] relative top-[-2px]" src="/images/chevronDown.svg" alt="arrow" width={11} height={11} />
|
||||
</button>
|
||||
<div className="flex gap-[12px] items-center">
|
||||
{paginationArray.map((page, index) => (
|
||||
typeof page === 'number' ? (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handlePageChange(page)}
|
||||
className={currentPage === page ? 'text-blue font-[700] text-[17px]' : 'text-[17px]'}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
) : (
|
||||
<span key={index}>{page}</span> // Отображаем многоточие
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
<button className="flex items-center text-[15px] font-[400] ml-[26px] gap-[11px]" onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage === totalPages}>
|
||||
Следующая страница
|
||||
<Image className="rotate-[-90deg] relative top-[-2px]" src="/images/chevronDown.svg" alt="arrow" width={11} height={11} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pagination;
|
17
components/section-item.tsx
Normal file
17
components/section-item.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
}
|
||||
|
||||
const SectionItem: React.FC<Props> = ({name}) => {
|
||||
return (
|
||||
<div className="flex border-[4px] border-white rounded-[6px] items-center w-[160px] h-[120px] justify-center relative">
|
||||
<Image className="absolute top-[-18px]" src="/images/chevronDown.svg" alt="arrow" width={11} height={11} />
|
||||
<span className="text-[26px] text-black uppercase">{name}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SectionItem
|
26
components/select.tsx
Normal file
26
components/select.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import Image from "next/image";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
name: string;
|
||||
variant?: 'default' | 'secondary';
|
||||
}
|
||||
|
||||
const Select: React.FC<Props> = ({className, name, variant = 'default'}) => {
|
||||
const variantClasses = {
|
||||
default: 'px-[25px] py-[10px]',
|
||||
secondary: 'px-[36px] py-[7px] bg-white',
|
||||
};
|
||||
|
||||
return(
|
||||
<div className={`${className} ${variantClasses[variant]} w-fit cursor-pointer border-blue border-[1px] flex gap-[10px] rounded-[10px]`}>
|
||||
<p className="text-blue font-[400] text-[16px]">{name}</p>
|
||||
<Image src="/images/chevronDown.svg" alt="chevronDown" width={11} height={8} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Select
|
16
components/social-item.tsx
Normal file
16
components/social-item.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import Image from "next/image";
|
||||
|
||||
interface Props {
|
||||
icon: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
const SocialItem: React.FC<Props> = ({icon, link}) => {
|
||||
return (
|
||||
<a className="flex justify-center items-center w-[41px] h-[41px] bg-blue rounded-full" href="">
|
||||
<Image src={`/images/${icon}.svg`} alt={icon} className="w-auto h-auto" width={1} height={1} /> </a>
|
||||
);
|
||||
}
|
||||
|
||||
export default SocialItem
|
Reference in New Issue
Block a user