forked from Transparency/kgroad-frontend2
made about-us, news list, volunteers page and statistics page
This commit is contained in:
parent
744dd7224c
commit
083ef42f9e
3
app/news/[id]/page.tsx
Normal file
3
app/news/[id]/page.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import NewsDetailsPage from "@/Pages/NewsDetailsPage/NewsDetailsPage";
|
||||||
|
|
||||||
|
export default NewsDetailsPage;
|
@ -1,4 +1,13 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {};
|
const nextConfig = {
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: "https",
|
||||||
|
hostname: "**",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
49
src/Entities/NewsCard/NewsCard.scss
Normal file
49
src/Entities/NewsCard/NewsCard.scss
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
@import "../../Shared/variables.scss";
|
||||||
|
|
||||||
|
.news-card {
|
||||||
|
min-height: 420px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 160px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__date {
|
||||||
|
width: fit-content;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: $light-blue;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: white;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
gap: 20px;
|
||||||
|
h4 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 150%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__more-btn {
|
||||||
|
width: fit-content;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 2px solid #3b3b3b;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #3b3b3b;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
57
src/Entities/NewsCard/NewsCard.tsx
Normal file
57
src/Entities/NewsCard/NewsCard.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import Image, { StaticImageData } from "next/image";
|
||||||
|
import "./NewsCard.scss";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
interface INewsCard {
|
||||||
|
id: number;
|
||||||
|
image: StaticImageData;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NewsCard: React.FC<INewsCard> = ({
|
||||||
|
id,
|
||||||
|
image,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
date,
|
||||||
|
}: INewsCard) => {
|
||||||
|
const sliceDate = (date: string) => {
|
||||||
|
return `${date.slice(8, 10)}/${date.slice(5, 7)}/${date.slice(
|
||||||
|
0,
|
||||||
|
4
|
||||||
|
)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sliceDescription = (description: string) => {
|
||||||
|
if (description.length > 65) {
|
||||||
|
return `${description.slice(0, 65)}...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return description;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="news-card">
|
||||||
|
<Image
|
||||||
|
src={image}
|
||||||
|
alt="Card Image"
|
||||||
|
width={250}
|
||||||
|
height={160}
|
||||||
|
blurDataURL="blur"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="news-card__text">
|
||||||
|
<h5 className="news-card__date">{sliceDate(date)}</h5>
|
||||||
|
<h4>{title}</h4>
|
||||||
|
<p>{sliceDescription(description)}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link href={`/news/${id}`} className="news-card__more-btn">
|
||||||
|
Подробнее
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewsCard;
|
@ -4,13 +4,15 @@ import search_icon from "./icons/search-icon.svg";
|
|||||||
|
|
||||||
interface ISearchBar {
|
interface ISearchBar {
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
style?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchBar: React.FC<ISearchBar> = ({
|
const SearchBar: React.FC<ISearchBar> = ({
|
||||||
placeholder,
|
placeholder,
|
||||||
|
style,
|
||||||
}: ISearchBar) => {
|
}: ISearchBar) => {
|
||||||
return (
|
return (
|
||||||
<div className="search-bar">
|
<div style={style} className="search-bar">
|
||||||
<div className="search-bar__input">
|
<div className="search-bar__input">
|
||||||
<Image src={search_icon} alt="Search Icon" />
|
<Image src={search_icon} alt="Search Icon" />
|
||||||
<input type="text" placeholder={placeholder} />
|
<input type="text" placeholder={placeholder} />
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
.about-us-page {
|
||||||
|
padding: 118px 90px 0px 90px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 40px;
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 600px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: #3e3232;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 30px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #3e3232;
|
||||||
|
font-feature-settings: "clig" off, "liga" off;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 34px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1024px) {
|
||||||
|
.about-us-page {
|
||||||
|
padding: 112px 30px 0px 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.about-us-page {
|
||||||
|
padding: 112px 30px 0px 30px;
|
||||||
|
gap: 30px;
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
margin-bottom: 0;
|
||||||
|
img {
|
||||||
|
height: 392px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
gap: 20px;
|
||||||
|
p {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
.about-us-page {
|
||||||
|
padding: 112px 16px 0px 16px;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
img {
|
||||||
|
height: 230px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
div {
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,66 @@
|
|||||||
|
import HeaderText from "@/Shared/UI/HeaderText/HeaderText";
|
||||||
import "./AboutUsPage.scss";
|
import "./AboutUsPage.scss";
|
||||||
|
import Image from "next/image";
|
||||||
|
import image from "./assets/image.png";
|
||||||
|
|
||||||
const AboutUsPage = () => {
|
const AboutUsPage = () => {
|
||||||
return <div>AboutUsPage</div>;
|
return (
|
||||||
|
<div className="about-us-page">
|
||||||
|
<HeaderText>О нас</HeaderText>
|
||||||
|
<div className="about-us-page__image">
|
||||||
|
<Image src={image} alt="About Us Image" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="about-us-page__container">
|
||||||
|
<h3>Don’t wait. The purpose of our lives is to be happy!</h3>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Upon arrival, your senses will be rewarded with the
|
||||||
|
pleasant scent of lemongrass oil used to clean the natural
|
||||||
|
wood found throughout the room, creating a relaxing
|
||||||
|
atmosphere within the space. A wonderful serenity has
|
||||||
|
taken possession of my entire soul, like these sweet
|
||||||
|
mornings of spring which I enjoy with my whole heart. I am
|
||||||
|
alone, and feel the charm of existence in this spot, which
|
||||||
|
was created for the bliss of souls like mine. I am so
|
||||||
|
happy, my dear friend, so absorbed in the exquisite.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
When you are ready to indulge your sense of excitement,
|
||||||
|
check out the range of water- sports opportunities at the
|
||||||
|
resort’s on-site water-sports center. Want to leave your
|
||||||
|
stress on the water? The resort has kayaks, paddleboards,
|
||||||
|
or the low-key pedal boats. Snorkeling equipment is
|
||||||
|
available as well, so you can experience the ever-changing
|
||||||
|
undersea environment. Not only do visitors to a bed and
|
||||||
|
breakfast get a unique perspective on the place they are
|
||||||
|
visiting, they have options for special packages not
|
||||||
|
available in other hotel settings.{" "}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
bed and breakfasts can partner easily with local
|
||||||
|
businesses for a smoothly organized and highly
|
||||||
|
personalized vacation experience. The Fife and Drum Inn
|
||||||
|
offers options such as the Historic Triangle Package that
|
||||||
|
includes three nights at the Inn, breakfasts, and
|
||||||
|
admissions to historic Williamsburg, Jamestown, and
|
||||||
|
Yorktown. Bed and breakfasts also lend themselves to
|
||||||
|
romance.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Part of the charm of a bed and breakfast is the
|
||||||
|
uniqueness; art, décor, and food are integrated to create
|
||||||
|
a complete experience. For example, the Fife and Drum
|
||||||
|
retains the colonial feel of the area in all its guest
|
||||||
|
rooms. Special features include antique furnishings,
|
||||||
|
elegant four poster beds in some guest rooms, as well folk
|
||||||
|
art and artifacts from the restoration period of the
|
||||||
|
historic area available for guests to enjoy.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AboutUsPage;
|
export default AboutUsPage;
|
||||||
|
BIN
src/Pages/AboutUsPage/assets/image.png
Normal file
BIN
src/Pages/AboutUsPage/assets/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
0
src/Pages/NewsDetailsPage/NewsDetailsPage.scss
Normal file
0
src/Pages/NewsDetailsPage/NewsDetailsPage.scss
Normal file
7
src/Pages/NewsDetailsPage/NewsDetailsPage.tsx
Normal file
7
src/Pages/NewsDetailsPage/NewsDetailsPage.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import "./NewsDetailsPage.scss";
|
||||||
|
|
||||||
|
const NewsDetailsPage = () => {
|
||||||
|
return <div>NewsDetailsPage</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewsDetailsPage;
|
@ -0,0 +1,24 @@
|
|||||||
|
.news-page {
|
||||||
|
padding: 118px 90px 0px 90px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1024px) {
|
||||||
|
.news-page {
|
||||||
|
padding: 118px 30px 0px 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.news-page {
|
||||||
|
padding: 112px 30px 0px 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
.news-page {
|
||||||
|
padding: 112px 16px 0px 16px;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,14 @@
|
|||||||
|
import HeaderText from "@/Shared/UI/HeaderText/HeaderText";
|
||||||
import "./NewsPage.scss";
|
import "./NewsPage.scss";
|
||||||
|
import NewsList from "@/Widgets/NewsList/NewsList";
|
||||||
|
|
||||||
const NewsPage = () => {
|
const NewsPage = () => {
|
||||||
return <div>NewsPage</div>;
|
return (
|
||||||
|
<div className="news-page">
|
||||||
|
<HeaderText>Новости</HeaderText>
|
||||||
|
<NewsList />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NewsPage;
|
export default NewsPage;
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
.statistics-page {
|
||||||
|
padding: 118px 90px 0 90px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1024px) {
|
||||||
|
.statistics-page {
|
||||||
|
padding: 118px 30px 0 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.statistics-page {
|
||||||
|
padding: 112px 30px 0 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1024px) {
|
||||||
|
.statistics-page {
|
||||||
|
padding: 112px 16px 0 16px;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,19 @@
|
|||||||
|
import HeaderText from "@/Shared/UI/HeaderText/HeaderText";
|
||||||
import "./StatisticsPage.scss";
|
import "./StatisticsPage.scss";
|
||||||
|
import SearchBar from "@/Features/SearchBar/SearchBar";
|
||||||
|
import StatisticsTable from "@/Widgets/StatisticsTable/StatisticsTable";
|
||||||
|
|
||||||
const StatisticsPage = () => {
|
const StatisticsPage = () => {
|
||||||
return <div>StatisticsPage</div>;
|
return (
|
||||||
|
<div className="statistics-page">
|
||||||
|
<HeaderText>Статистика</HeaderText>
|
||||||
|
<SearchBar
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
placeholder="Введите населенный пункт"
|
||||||
|
/>
|
||||||
|
<StatisticsTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StatisticsPage;
|
export default StatisticsPage;
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
.volunteers-page {
|
||||||
|
padding: 118px 90px 0 90px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1024px) {
|
||||||
|
.volunteers-page {
|
||||||
|
padding: 118px 30px 0 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.volunteers-page {
|
||||||
|
padding: 112px 30px 0 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
.volunteers-page {
|
||||||
|
padding: 112px 16px 0 16px;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,14 @@
|
|||||||
|
import HeaderText from "@/Shared/UI/HeaderText/HeaderText";
|
||||||
import "./VolunteersPage.scss";
|
import "./VolunteersPage.scss";
|
||||||
|
import VolunteersTable from "@/Widgets/VolunteersTable/VolunteersTable";
|
||||||
|
|
||||||
const VolunteersPage = () => {
|
const VolunteersPage = () => {
|
||||||
return <div>VolunteersPage</div>;
|
return (
|
||||||
|
<div className="volunteers-page">
|
||||||
|
<HeaderText>Волонтеры</HeaderText>
|
||||||
|
<VolunteersTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default VolunteersPage;
|
export default VolunteersPage;
|
||||||
|
19
src/Shared/UI/HeaderText/HeaderText.scss
Normal file
19
src/Shared/UI/HeaderText/HeaderText.scss
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
@import "../../variables.scss";
|
||||||
|
|
||||||
|
.header-text {
|
||||||
|
color: $gray-700;
|
||||||
|
font-size: 42px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.header-text {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
.header-text {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
13
src/Shared/UI/HeaderText/HeaderText.tsx
Normal file
13
src/Shared/UI/HeaderText/HeaderText.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import "./HeaderText.scss";
|
||||||
|
|
||||||
|
interface IHeaderText {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HeaderText: React.FC<IHeaderText> = ({
|
||||||
|
children,
|
||||||
|
}: IHeaderText) => {
|
||||||
|
return <h2 className="header-text">{children}</h2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeaderText;
|
@ -4,3 +4,9 @@ export interface IFetch {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IList {
|
||||||
|
count: number;
|
||||||
|
next: number | null;
|
||||||
|
previous: number | null;
|
||||||
|
}
|
||||||
|
17
src/Widgets/NewsList/NewsList.scss
Normal file
17
src/Widgets/NewsList/NewsList.scss
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
.news-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1024px) {
|
||||||
|
.news-list {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
.news-list {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
31
src/Widgets/NewsList/NewsList.tsx
Normal file
31
src/Widgets/NewsList/NewsList.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import "./NewsList.scss";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useNews } from "./news.store";
|
||||||
|
import NewsCard from "@/Entities/NewsCard/NewsCard";
|
||||||
|
|
||||||
|
const NewsList = () => {
|
||||||
|
const { data, getNews } = useNews();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getNews();
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<ul className="news-list">
|
||||||
|
{data.results.map((card) => (
|
||||||
|
<li key={card.id}>
|
||||||
|
<NewsCard
|
||||||
|
id={card.id}
|
||||||
|
image={card.image}
|
||||||
|
title={card.title}
|
||||||
|
description={card.description}
|
||||||
|
date={card.created_at}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewsList;
|
45
src/Widgets/NewsList/news.store.ts
Normal file
45
src/Widgets/NewsList/news.store.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { baseAPI } from "@/Shared/API/baseAPI";
|
||||||
|
import { IFetch, IList } from "@/Shared/types";
|
||||||
|
import axios from "axios";
|
||||||
|
import { StaticImageData } from "next/image";
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface INews extends IList {
|
||||||
|
results: IResult[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IResult {
|
||||||
|
id: number;
|
||||||
|
image: StaticImageData;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface INewsStore extends IFetch {
|
||||||
|
data: INews;
|
||||||
|
getNews: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useNews = create<INewsStore>((set) => ({
|
||||||
|
data: {
|
||||||
|
count: 0,
|
||||||
|
next: null,
|
||||||
|
previous: null,
|
||||||
|
results: [],
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
error: "",
|
||||||
|
getNews: async () => {
|
||||||
|
try {
|
||||||
|
set({ loading: true });
|
||||||
|
const response = await axios.get<INews>(`${baseAPI}/news/`);
|
||||||
|
|
||||||
|
set({ data: response.data });
|
||||||
|
} catch (error: any) {
|
||||||
|
set({ error: error.message });
|
||||||
|
} finally {
|
||||||
|
set({ loading: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
103
src/Widgets/StatisticsTable/StatisticsTable.scss
Normal file
103
src/Widgets/StatisticsTable/StatisticsTable.scss
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
@import "../../Shared/variables.scss";
|
||||||
|
|
||||||
|
.statistics-table {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #d5d5d5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__location {
|
||||||
|
position: absolute;
|
||||||
|
top: 30%;
|
||||||
|
left: 1%;
|
||||||
|
border-radius: 8px;
|
||||||
|
z-index: 15;
|
||||||
|
|
||||||
|
li {
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px;
|
||||||
|
color: #101828;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_active {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
thead {
|
||||||
|
height: 76px;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
|
||||||
|
th {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
color: $gray-500;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: start;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.statistics-table__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th:first-child {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
height: 76px;
|
||||||
|
border-bottom: 1px solid #f1f4f9;
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 0 20px;
|
||||||
|
color: $gray-500;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
padding-left: 20px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:last-child {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
189
src/Widgets/StatisticsTable/StatisticsTable.tsx
Normal file
189
src/Widgets/StatisticsTable/StatisticsTable.tsx
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import "./StatisticsTable.scss";
|
||||||
|
import Image from "next/image";
|
||||||
|
import arrow_down_icon from "./icons/arrow-down-icon.svg";
|
||||||
|
import arrow_up_icon from "./icons/arrow-up-icon.svg";
|
||||||
|
import Link from "next/link";
|
||||||
|
import chevron_down from "./icons/chevron-down.svg";
|
||||||
|
import { useStatistics } from "./statistics.store";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const StatisticsTable = () => {
|
||||||
|
const [location, setLocation] = useState("city");
|
||||||
|
const [locationMenu, setLocationMenu] = useState(false);
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
getStatisticsForCity,
|
||||||
|
getStatisticsForState,
|
||||||
|
getStatisticsForVillage,
|
||||||
|
} = useStatistics();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getStatisticsForState();
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<div className="statistics-table">
|
||||||
|
{locationMenu && (
|
||||||
|
<ul
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
className="statistics-table__location"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
className={
|
||||||
|
location === "state"
|
||||||
|
? "statistics-table__location_active"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setLocation("state");
|
||||||
|
getStatisticsForState();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Регион
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={
|
||||||
|
location === "city"
|
||||||
|
? "statistics-table__location_active"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setLocation("city");
|
||||||
|
getStatisticsForCity();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Город
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={
|
||||||
|
location === "village"
|
||||||
|
? "statistics-table__location_active"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setLocation("village");
|
||||||
|
getStatisticsForVillage();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Деревня
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
<div className="statistics-table__wrapper">
|
||||||
|
<table cellSpacing={0}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
tabIndex={1}
|
||||||
|
onClick={() => setLocationMenu((prev) => !prev)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Город
|
||||||
|
<Image src={chevron_down} alt="Chevron Icon" />
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th tabIndex={1}>
|
||||||
|
<div className="statistics-table__header">
|
||||||
|
Добавлено дорог
|
||||||
|
<div>
|
||||||
|
<Image
|
||||||
|
src={arrow_down_icon}
|
||||||
|
alt="Arrow Down Icon"
|
||||||
|
/>
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th tabIndex={1}>
|
||||||
|
<div className="statistics-table__header">
|
||||||
|
Локальных дефектов
|
||||||
|
<div>
|
||||||
|
<Image
|
||||||
|
src={arrow_down_icon}
|
||||||
|
alt="Arrow Down Icon"
|
||||||
|
/>
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th tabIndex={1}>
|
||||||
|
<div className="statistics-table__header">
|
||||||
|
Очагов аварийности
|
||||||
|
<div>
|
||||||
|
<Image
|
||||||
|
src={arrow_down_icon}
|
||||||
|
alt="Arrow Down Icon"
|
||||||
|
/>
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th tabIndex={1}>
|
||||||
|
<div className="statistics-table__header">
|
||||||
|
Локальных дефектов исправлено
|
||||||
|
<div>
|
||||||
|
<Image
|
||||||
|
src={arrow_down_icon}
|
||||||
|
alt="Arrow Down Icon"
|
||||||
|
/>
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th tabIndex={1}>
|
||||||
|
<div className="statistics-table__header">
|
||||||
|
В планах ремонта
|
||||||
|
<div>
|
||||||
|
<Image
|
||||||
|
src={arrow_down_icon}
|
||||||
|
alt="Arrow Down Icon"
|
||||||
|
/>
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th tabIndex={1}>
|
||||||
|
<div className="statistics-table__header">
|
||||||
|
Отремонтировано
|
||||||
|
<div>
|
||||||
|
<Image
|
||||||
|
src={arrow_down_icon}
|
||||||
|
alt="Arrow Down Icon"
|
||||||
|
/>
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{data.map((statistic) => (
|
||||||
|
<tr>
|
||||||
|
<td>{statistic.name}</td>
|
||||||
|
<td>{statistic.broken_road_1}</td>
|
||||||
|
<td>{statistic.local_defect_3}</td>
|
||||||
|
<td>{statistic.hotbed_of_accidents_2}</td>
|
||||||
|
<td>{statistic.local_defect_fixed_6}</td>
|
||||||
|
<td>{statistic.repair_plans_4}</td>
|
||||||
|
<td>{statistic.repaired_5}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StatisticsTable;
|
5
src/Widgets/StatisticsTable/icons/arrow-down-icon.svg
Normal file
5
src/Widgets/StatisticsTable/icons/arrow-down-icon.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="arrow-down">
|
||||||
|
<path id="Icon" d="M8.00016 3.33301V12.6663M8.00016 12.6663L12.6668 7.99967M8.00016 12.6663L3.3335 7.99967" stroke="#667085" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 325 B |
5
src/Widgets/StatisticsTable/icons/arrow-up-icon.svg
Normal file
5
src/Widgets/StatisticsTable/icons/arrow-up-icon.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="arrow-down">
|
||||||
|
<path id="Icon" d="M7.99984 12.667L7.99984 3.33366M7.99984 3.33366L3.33317 8.00033M7.99984 3.33366L12.6665 8.00033" stroke="#667085" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 333 B |
5
src/Widgets/StatisticsTable/icons/chevron-down.svg
Normal file
5
src/Widgets/StatisticsTable/icons/chevron-down.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="18" height="12" viewBox="0 0 18 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="arrow/chevron-down">
|
||||||
|
<path id="Vector" d="M12.7503 4H5.25034C5.1077 3.99993 4.96799 4.04055 4.84761 4.11708C4.72723 4.19362 4.63118 4.30289 4.57071 4.43209C4.51025 4.56129 4.48788 4.70505 4.50624 4.84651C4.52459 4.98797 4.58291 5.12126 4.67434 5.23075L8.42435 9.73075C8.49473 9.81516 8.58281 9.88306 8.68235 9.92966C8.78188 9.97626 8.89044 10.0004 9.00035 10.0004C9.11025 10.0004 9.21881 9.97626 9.31834 9.92966C9.41788 9.88306 9.50596 9.81516 9.57634 9.73075L13.3263 5.23075C13.4178 5.12126 13.4761 4.98797 13.4945 4.84651C13.5128 4.70505 13.4904 4.56129 13.43 4.43209C13.3695 4.30289 13.2735 4.19362 13.1531 4.11708C13.0327 4.04055 12.893 3.99993 12.7503 4ZM9.00035 8.0785L6.8516 5.5H11.1491L9.00035 8.0785Z" fill="#667085"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 843 B |
72
src/Widgets/StatisticsTable/statistics.store.ts
Normal file
72
src/Widgets/StatisticsTable/statistics.store.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { baseAPI } from "@/Shared/API/baseAPI";
|
||||||
|
import { IFetch } from "@/Shared/types";
|
||||||
|
import axios from "axios";
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface IStatistic {
|
||||||
|
name: string;
|
||||||
|
broken_road_1: number;
|
||||||
|
hotbed_of_accidents_2: number;
|
||||||
|
local_defect_3: number;
|
||||||
|
repair_plans_4: number;
|
||||||
|
repaired_5: number;
|
||||||
|
local_defect_fixed_6: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IStatisticStore extends IFetch {
|
||||||
|
data: IStatistic[];
|
||||||
|
getStatisticsForCity: () => Promise<void>;
|
||||||
|
getStatisticsForState: () => Promise<void>;
|
||||||
|
getStatisticsForVillage: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useStatistics = create<IStatisticStore>((set) => ({
|
||||||
|
data: [],
|
||||||
|
loading: false,
|
||||||
|
error: "",
|
||||||
|
getStatisticsForCity: async () => {
|
||||||
|
try {
|
||||||
|
set({ loading: true });
|
||||||
|
|
||||||
|
const response = await axios.get<IStatistic[]>(
|
||||||
|
`${baseAPI}/report/city/stats/`
|
||||||
|
);
|
||||||
|
|
||||||
|
set({ data: response.data });
|
||||||
|
} catch (error: any) {
|
||||||
|
set({ error: error.message });
|
||||||
|
} finally {
|
||||||
|
set({ loading: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatisticsForState: async () => {
|
||||||
|
try {
|
||||||
|
set({ loading: true });
|
||||||
|
|
||||||
|
const response = await axios.get<IStatistic[]>(
|
||||||
|
`${baseAPI}/report/state/stats/`
|
||||||
|
);
|
||||||
|
|
||||||
|
set({ data: response.data });
|
||||||
|
} catch (error: any) {
|
||||||
|
set({ error: error.message });
|
||||||
|
} finally {
|
||||||
|
set({ loading: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatisticsForVillage: async () => {
|
||||||
|
try {
|
||||||
|
set({ loading: true });
|
||||||
|
|
||||||
|
const response = await axios.get<IStatistic[]>(
|
||||||
|
`${baseAPI}/report/village/stats/`
|
||||||
|
);
|
||||||
|
|
||||||
|
set({ data: response.data });
|
||||||
|
} catch (error: any) {
|
||||||
|
set({ error: error.message });
|
||||||
|
} finally {
|
||||||
|
set({ loading: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
140
src/Widgets/VolunteersTable/VolunteersTable.scss
Normal file
140
src/Widgets/VolunteersTable/VolunteersTable.scss
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
@import "../../Shared/variables.scss";
|
||||||
|
|
||||||
|
.volunteers-table {
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--Grey-for-stroke, #d5d5d5);
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
thead {
|
||||||
|
height: 76px;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
|
||||||
|
th {
|
||||||
|
border-style: none;
|
||||||
|
|
||||||
|
color: $gray-500;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: start;
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th:first-child {
|
||||||
|
padding-left: 20px;
|
||||||
|
min-width: 113px;
|
||||||
|
}
|
||||||
|
th:nth-child(2) {
|
||||||
|
min-width: 206px;
|
||||||
|
}
|
||||||
|
th:nth-child(3) {
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 190px;
|
||||||
|
}
|
||||||
|
th:nth-child(4) {
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 213px;
|
||||||
|
}
|
||||||
|
th:nth-child(5) {
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 222px;
|
||||||
|
}
|
||||||
|
th:last-child {
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 92px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
height: 76px;
|
||||||
|
border-bottom: 1px solid #f1f4f9;
|
||||||
|
|
||||||
|
td {
|
||||||
|
color: $gray-500;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
padding-left: 20px;
|
||||||
|
min-width: 113px;
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
}
|
||||||
|
td:nth-child(2) {
|
||||||
|
min-width: 206px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $light-blue;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td:nth-child(3) {
|
||||||
|
min-width: 190px;
|
||||||
|
}
|
||||||
|
td:nth-child(4) {
|
||||||
|
min-width: 213;
|
||||||
|
}
|
||||||
|
td:nth-child(5) {
|
||||||
|
min-width: 222px;
|
||||||
|
}
|
||||||
|
td:last-child {
|
||||||
|
min-width: 92px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:last-child {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
.volunteers-table {
|
||||||
|
table {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td:first-child {
|
||||||
|
padding-left: 20px;
|
||||||
|
min-width: 113px;
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
}
|
||||||
|
td:nth-child(2) {
|
||||||
|
min-width: 206px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $light-blue;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td:nth-child(3) {
|
||||||
|
min-width: 190px;
|
||||||
|
}
|
||||||
|
td:nth-child(4) {
|
||||||
|
min-width: 213;
|
||||||
|
}
|
||||||
|
td:nth-child(5) {
|
||||||
|
min-width: 222px;
|
||||||
|
}
|
||||||
|
td:last-child {
|
||||||
|
min-width: 92px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
src/Widgets/VolunteersTable/VolunteersTable.tsx
Normal file
75
src/Widgets/VolunteersTable/VolunteersTable.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
import "./VolunteersTable.scss";
|
||||||
|
import arrow_down_icon from "./icons/arrow-down-icon.svg";
|
||||||
|
import arrow_up_icon from "./icons/arrow-up-icon.svg";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useVolunteers } from "./volunteer.store";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
const VolunteersTable = () => {
|
||||||
|
const { getVolunteers, data } = useVolunteers();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getVolunteers();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="volunteers-table">
|
||||||
|
<table cellSpacing={0}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>№</th>
|
||||||
|
<th>Активист</th>
|
||||||
|
<th>
|
||||||
|
<div>
|
||||||
|
Добавлено дорог
|
||||||
|
<Image src={arrow_down_icon} alt="Arrow Down Icon" />
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<div>
|
||||||
|
Получено голосов{" "}
|
||||||
|
<Image src={arrow_down_icon} alt="Arrow Down Icon" />
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<div>
|
||||||
|
Оставлено голосов{" "}
|
||||||
|
<Image src={arrow_down_icon} alt="Arrow Down Icon" />
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<div>
|
||||||
|
Рейтинг{" "}
|
||||||
|
<Image src={arrow_down_icon} alt="Arrow Down Icon" />
|
||||||
|
<Image src={arrow_up_icon} alt="Arrow Up Icon" />
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{data.map((volunteer) => (
|
||||||
|
<tr key={volunteer.user_id}>
|
||||||
|
<td>1</td>
|
||||||
|
<td>
|
||||||
|
<Link href={"#"}>{volunteer.username}</Link>
|
||||||
|
</td>
|
||||||
|
<td>{volunteer.report_count}</td>
|
||||||
|
<td>{volunteer.likes_received_count}</td>
|
||||||
|
<td>{volunteer.likes_given_count}</td>
|
||||||
|
<td>{volunteer.average_rating}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VolunteersTable;
|
5
src/Widgets/VolunteersTable/icons/arrow-down-icon.svg
Normal file
5
src/Widgets/VolunteersTable/icons/arrow-down-icon.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="arrow-down">
|
||||||
|
<path id="Icon" d="M8.00016 3.33301V12.6663M8.00016 12.6663L12.6668 7.99967M8.00016 12.6663L3.3335 7.99967" stroke="#667085" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 325 B |
5
src/Widgets/VolunteersTable/icons/arrow-up-icon.svg
Normal file
5
src/Widgets/VolunteersTable/icons/arrow-up-icon.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="arrow-down">
|
||||||
|
<path id="Icon" d="M7.99984 12.667L7.99984 3.33366M7.99984 3.33366L3.33317 8.00033M7.99984 3.33366L12.6665 8.00033" stroke="#667085" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 333 B |
39
src/Widgets/VolunteersTable/volunteer.store.ts
Normal file
39
src/Widgets/VolunteersTable/volunteer.store.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { baseAPI } from "@/Shared/API/baseAPI";
|
||||||
|
import { IFetch, IList } from "@/Shared/types";
|
||||||
|
import axios from "axios";
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface IVolunteer {
|
||||||
|
user_id: number;
|
||||||
|
username: string;
|
||||||
|
report_count: number;
|
||||||
|
likes_given_count: number;
|
||||||
|
likes_received_count: number;
|
||||||
|
average_rating: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IVolunteerStore extends IFetch {
|
||||||
|
data: IVolunteer[];
|
||||||
|
getVolunteers: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useVolunteers = create<IVolunteerStore>((set) => ({
|
||||||
|
data: [],
|
||||||
|
loading: false,
|
||||||
|
error: "",
|
||||||
|
getVolunteers: async () => {
|
||||||
|
try {
|
||||||
|
set({ loading: true });
|
||||||
|
|
||||||
|
const response = await axios.get<IVolunteer[]>(
|
||||||
|
`${baseAPI}/report/user_ratings/`
|
||||||
|
);
|
||||||
|
|
||||||
|
set({ data: response.data });
|
||||||
|
} catch (error: any) {
|
||||||
|
set({ error: error.message });
|
||||||
|
} finally {
|
||||||
|
set({ loading: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
@ -14,52 +14,6 @@
|
|||||||
gap: 30px;
|
gap: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.news-card {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 24px;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 160px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__date {
|
|
||||||
width: fit-content;
|
|
||||||
padding: 8px 12px;
|
|
||||||
background-color: $light-blue;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: white;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 20px;
|
|
||||||
h4 {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 150%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__more-btn {
|
|
||||||
width: fit-content;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border: 2px solid #3b3b3b;
|
|
||||||
border-radius: 3px;
|
|
||||||
color: #3b3b3b;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__read-more-btn {
|
&__read-more-btn {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,32 +1,36 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import "./NewsSection.scss";
|
import "./NewsSection.scss";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import SectionHeader from "@/Shared/UI/SectionHeader/SectionHeader";
|
import SectionHeader from "@/Shared/UI/SectionHeader/SectionHeader";
|
||||||
import image from "./assets/image.jpg";
|
import image from "./assets/image.jpg";
|
||||||
import arrow_icon from "./icons/arrow-right-icon.svg";
|
import arrow_icon from "./icons/arrow-right-icon.svg";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useNewsHome } from "./news-home.store";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import NewsCard from "@/Entities/NewsCard/NewsCard";
|
||||||
|
|
||||||
const NewsSection = () => {
|
const NewsSection = () => {
|
||||||
|
const { data, loading, error, getNews } = useNewsHome();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getNews();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="news-section">
|
<div className="news-section">
|
||||||
<SectionHeader>Новости</SectionHeader>
|
<SectionHeader>Новости</SectionHeader>
|
||||||
|
|
||||||
<ul className="news-section__list">
|
<ul className="news-section__list">
|
||||||
{[0, 1, 2, 3].map((card) => (
|
{data.map((card) => (
|
||||||
<li key={card} className="news-card">
|
<li key={card.id} className="news-card">
|
||||||
<Image src={image} alt="Card Image" />
|
<NewsCard
|
||||||
|
id={card.id}
|
||||||
<div className="news-card__text">
|
title={card.title}
|
||||||
<h5 className="news-card__date">23/09/2023</h5>
|
image={card.image}
|
||||||
<h4>
|
description={card.description}
|
||||||
Short title of the card describing the main content
|
date={card.created_at}
|
||||||
</h4>
|
/>
|
||||||
<p>
|
|
||||||
Short paragraph description of the article, outlining
|
|
||||||
main story and focus.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button className="news-card__more-btn">Подробнее</button>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
40
src/Widgets/home/NewsSection/news-home.store.ts
Normal file
40
src/Widgets/home/NewsSection/news-home.store.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { baseAPI } from "@/Shared/API/baseAPI";
|
||||||
|
import { IFetch, IList } from "@/Shared/types";
|
||||||
|
import axios from "axios";
|
||||||
|
import { StaticImageData } from "next/image";
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface INews {
|
||||||
|
results: IResult[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IResult {
|
||||||
|
id: number;
|
||||||
|
image: StaticImageData;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface INewsHomeStore extends IFetch {
|
||||||
|
data: IResult[];
|
||||||
|
getNews: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useNewsHome = create<INewsHomeStore>((set) => ({
|
||||||
|
data: [],
|
||||||
|
loading: false,
|
||||||
|
error: "",
|
||||||
|
getNews: async () => {
|
||||||
|
try {
|
||||||
|
set({ loading: true });
|
||||||
|
const response = await axios.get<INews>(`${baseAPI}/news/`);
|
||||||
|
|
||||||
|
set({ data: response.data.results.slice(0, 4) });
|
||||||
|
} catch (error: any) {
|
||||||
|
set({ error: error.message });
|
||||||
|
} finally {
|
||||||
|
set({ loading: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
Loading…
Reference in New Issue
Block a user