diff --git a/src/App/about-us/AboutUs.scss b/src/App/about-us/AboutUs.scss new file mode 100644 index 0000000..0776cd7 --- /dev/null +++ b/src/App/about-us/AboutUs.scss @@ -0,0 +1,86 @@ +.about-us { + display: flex; + flex-direction: column; + + h2 { + margin-bottom: 40px; + width: fit-content; + } + + img { + align-self: center; + margin-bottom: 65px; + border-radius: 12px; + max-width: 1072px; + width: 100%; + height: 598px; + object-fit: cover; + } + + h3 { + margin-bottom: 10px; + font-size: 24px; + font-weight: 500; + line-height: 29px; + color: rgb(62, 50, 50); + } + + &__descriptions { + display: flex; + flex-direction: column; + gap: 20px; + + p { + font-size: 20px; + font-weight: 400; + line-height: 34px; + color: rgb(62, 50, 50); + } + } +} + +@media screen and (max-width: 768px) { + .about-us { + h2 { + margin-bottom: 30px; + } + + img { + margin-bottom: 30px; + height: 392px; + } + + h3 { + font-size: 20px; + line-height: 24px; + } + + &__descriptions { + gap: 16px; + + p { + font-size: 18px; + line-height: 34px; + } + } + } +} + +@media screen and (max-width: 550px) { + .about-us { + h2 { + margin-bottom: 20px; + } + + img { + height: 231px; + } + + &__descriptions { + p { + font-size: 16px; + line-height: 140%; + } + } + } +} diff --git a/src/App/about-us/assets/header.svg b/src/App/about-us/assets/header.svg new file mode 100644 index 0000000..0600736 --- /dev/null +++ b/src/App/about-us/assets/header.svg @@ -0,0 +1,12 @@ + + + Created with Pixso. + + + + + + + + + diff --git a/src/App/about-us/page.tsx b/src/App/about-us/page.tsx index 265d338..3f4925e 100644 --- a/src/App/about-us/page.tsx +++ b/src/App/about-us/page.tsx @@ -1,7 +1,63 @@ -import "./styles.scss"; +import Typography from "@/shared/ui/components/Typography/Typography"; +import "./AboutUs.scss"; +import Image from "next/image"; +import header from "./assets/header.svg"; const AboutUs = () => { - return
AboutUs
; + return ( +
+ О нас + + Header Image + +

Don’t wait. The purpose of our lives is to be happy!

+ +
+

+ 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. +

+

+ 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.{" "} +

+

+ 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. +

+

+ 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. +

+
+
+ ); }; export default AboutUs; diff --git a/src/App/about-us/styles.scss b/src/App/about-us/styles.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/App/news/News.scss b/src/App/news/News.scss new file mode 100644 index 0000000..ec20536 --- /dev/null +++ b/src/App/news/News.scss @@ -0,0 +1,43 @@ +.news { + display: flex; + flex-direction: column; + gap: 40px; + + h2 { + width: fit-content; + } + + &__list { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 30px; + } +} + +@media screen and (max-width: 1024px) { + .news { + &__list { + grid-template-columns: 1fr 1fr 1fr; + } + } +} + +@media screen and (max-width: 768px) { + .news { + gap: 30px; + + &__list { + grid-template-columns: 1fr 1fr; + } + } +} + +@media screen and (max-width: 550px) { + .news { + gap: 20px; + + &__list { + grid-template-columns: 1fr; + } + } +} diff --git a/src/App/news/[id]/NewsDetails.scss b/src/App/news/[id]/NewsDetails.scss new file mode 100644 index 0000000..f6dd156 --- /dev/null +++ b/src/App/news/[id]/NewsDetails.scss @@ -0,0 +1,104 @@ +.news-details { + display: flex; + flex-direction: column; + gap: 40px; + + &__img { + align-self: center; + #news-img { + margin-bottom: 30px; + width: 100%; + max-width: 1072px; + height: 598px; + border-radius: 12px; + object-fit: cover; + } + } + + &__date-and-reviews { + display: flex; + align-items: center; + gap: 80px; + } + + &__date, + &__reviews { + display: flex; + align-items: center; + gap: 6px; + + p { + font-size: 15px; + font-weight: 500; + line-height: 20px; + color: rgba(62, 50, 50, 0.75); + } + } + + &__text { + width: 100%; + max-width: 1072px; + align-self: center; + display: flex; + flex-direction: column; + gap: 10px; + color: rgb(62, 50, 50); + + h3 { + font-size: 24px; + font-weight: 500; + line-height: 29px; + } + + p { + font-size: 20px; + font-weight: 400; + line-height: 34px; + } + } +} + +@media screen and (max-width: 768px) { + .news-details { + gap: 30px; + + &__img { + #news-img { + margin-bottom: 25px; + height: 392px; + } + } + + &__text { + h3 { + font-size: 20px; + line-height: 24px; + } + + p { + font-size: 18px; + line-height: 34px; + } + } + } +} + +@media screen and (max-width: 550px) { + .news-details { + gap: 20px; + + &__img { + #news-img { + margin-bottom: 20px; + height: 231px; + } + } + + &__text { + p { + font-size: 16px; + line-height: 140%; + } + } + } +} diff --git a/src/App/news/[id]/icons/calendar.svg b/src/App/news/[id]/icons/calendar.svg new file mode 100644 index 0000000..e318ec7 --- /dev/null +++ b/src/App/news/[id]/icons/calendar.svg @@ -0,0 +1,17 @@ + + + Created with Pixso. + + + + + + + + + + + + + + diff --git a/src/App/news/[id]/icons/message.svg b/src/App/news/[id]/icons/message.svg new file mode 100644 index 0000000..35f5825 --- /dev/null +++ b/src/App/news/[id]/icons/message.svg @@ -0,0 +1,14 @@ + + + Created with Pixso. + + + + + + + + + + + diff --git a/src/App/news/[id]/page.tsx b/src/App/news/[id]/page.tsx new file mode 100644 index 0000000..0785d04 --- /dev/null +++ b/src/App/news/[id]/page.tsx @@ -0,0 +1,75 @@ +import Typography from "@/shared/ui/components/Typography/Typography"; +import "./NewsDetails.scss"; +import { apiInstance } from "@/shared/config/apiConfig"; +import { INews } from "@/shared/types/news-type"; +import Image from "next/image"; +import message from "./icons/message.svg"; +import calendar from "./icons/calendar.svg"; +import ReviewSection from "@/widgets/ReviewSection/ReviewSection"; + +const NewsDetails = async ({ + params, +}: { + params: { id: string }; +}) => { + const getNewsById = async () => { + const response = await apiInstance.get( + `/news/${params.id}/` + ); + + return response.data; + }; + + const data = await getNewsById(); + + const months: Record = { + "01": "Январь", + "02": "Февраль", + "03": "Март", + "04": "Апрель", + "05": "Май", + "06": "Июнь", + "07": "Июль", + "08": "Август", + "09": "Сентябрь", + "10": "Октябрь", + "11": "Ноябрь", + "12": "Декабрь", + }; + + return ( +
+ {data.title} + +
+ News Image +
+
+ Calendar Icon +

+ {months[data.created_at.slice(5, 7)]}{" "} + {data.created_at.slice(5, 7).slice(0, 1) === "0" + ? data.created_at.slice(6, 7) + : data.created_at.slice(5, 7)} + , {data.created_at.slice(0, 4)} +

+
+ +
+ Message Icon +

Комментарии: {data.count_reviews}

+
+
+
+ +
+

{data.title}

+

{data.description}

+
+ + +
+ ); +}; + +export default NewsDetails; diff --git a/src/App/news/page.tsx b/src/App/news/page.tsx new file mode 100644 index 0000000..24087b4 --- /dev/null +++ b/src/App/news/page.tsx @@ -0,0 +1,36 @@ +import Typography from "@/shared/ui/components/Typography/Typography"; +import "./News.scss"; +import { apiInstance } from "@/shared/config/apiConfig"; +import { INewsList } from "@/shared/types/news-type"; +import NewsCard from "@/entities/NewsCard/NewsCard"; + +const News = async () => { + const getNews = async () => { + const response = await apiInstance.get("/news/"); + + return response.data; + }; + + const data = await getNews(); + return ( +
+ Новости + +
    + {data.results.map((news) => ( +
  • + +
  • + ))} +
+
+ ); +}; + +export default News; diff --git a/src/App/sign-in/SignIn.scss b/src/App/sign-in/SignIn.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/App/statistics/Statistics.scss b/src/App/statistics/Statistics.scss new file mode 100644 index 0000000..108189a --- /dev/null +++ b/src/App/statistics/Statistics.scss @@ -0,0 +1,21 @@ +.statistics { + display: flex; + flex-direction: column; + gap: 40px; + + h2 { + width: fit-content; + } +} + +@media screen and (max-width: 768px) { + .statistics { + gap: 30px; + } +} + +@media screen and (max-width: 550px) { + .statistics { + gap: 20px; + } +} diff --git a/src/App/statistics/page.tsx b/src/App/statistics/page.tsx new file mode 100644 index 0000000..665625d --- /dev/null +++ b/src/App/statistics/page.tsx @@ -0,0 +1,34 @@ +import Typography from "@/shared/ui/components/Typography/Typography"; +import "./Statistics.scss"; +import { apiInstance } from "@/shared/config/apiConfig"; +import { IStatistics } from "@/shared/types/statistics-type"; +import { AxiosError } from "axios"; +import StatisticsTable from "@/widgets/StatisticsTable/StatisticsTable"; + +const Statistics = async () => { + const getStatistics = async (): Promise => { + try { + const response = await apiInstance.get( + "/report/city/stats/" + ); + + return response.data; + } catch (error: unknown) { + if (error instanceof AxiosError) { + return error.message; + } else { + return "Произошла непредвиденная ошибк"; + } + } + }; + + const data = await getStatistics(); + return ( +
+ Статистика + +
+ ); +}; + +export default Statistics; diff --git a/src/App/volunteers/Volunteers.scss b/src/App/volunteers/Volunteers.scss new file mode 100644 index 0000000..7467029 --- /dev/null +++ b/src/App/volunteers/Volunteers.scss @@ -0,0 +1,21 @@ +.volunteers { + display: flex; + flex-direction: column; + gap: 40px; + + h2 { + width: fit-content; + } +} + +@media screen and (max-width: 768px) { + .volunteers { + gap: 30px; + } +} + +@media screen and (max-width: 550px) { + .volunteers { + gap: 20px; + } +} diff --git a/src/App/volunteers/page.tsx b/src/App/volunteers/page.tsx new file mode 100644 index 0000000..fed12e3 --- /dev/null +++ b/src/App/volunteers/page.tsx @@ -0,0 +1,36 @@ +import Typography from "@/shared/ui/components/Typography/Typography"; +import "./Volunteers.scss"; +import { apiInstance } from "@/shared/config/apiConfig"; +import { AxiosError } from "axios"; +import { IUserRatings } from "@/shared/types/user-rating-type"; +import VolunteersTable from "@/widgets/VolunteersTable/VolunteersTable"; + +const Volunteers = async () => { + const getVolunteers = async (): Promise< + IUserRatings[] | string + > => { + try { + const response = await apiInstance.get( + "/report/user_ratings/" + ); + + return response.data; + } catch (error: unknown) { + if (error instanceof AxiosError) { + return error.message; + } else { + return "Произошла непредвиденная ошибк"; + } + } + }; + + const data = await getVolunteers(); + return ( +
+ Волонтеры + +
+ ); +}; + +export default Volunteers; diff --git a/src/Shared/types/fetch-type.ts b/src/Shared/types/fetch-type.ts index d484881..576ae3f 100644 --- a/src/Shared/types/fetch-type.ts +++ b/src/Shared/types/fetch-type.ts @@ -1,5 +1,5 @@ export interface IFetch { error: string; - data: any; + data?: any; isLoading: boolean; } diff --git a/src/Shared/types/news-type.ts b/src/Shared/types/news-type.ts index ffce86b..c351891 100644 --- a/src/Shared/types/news-type.ts +++ b/src/Shared/types/news-type.ts @@ -1,3 +1,5 @@ +import { IList } from "./list-type"; + export interface INews { id: number; image: string; @@ -8,3 +10,7 @@ export interface INews { updated_at: string; count_reviews: number; } + +export interface INewsList extends IList { + results: INews[]; +} diff --git a/src/Shared/types/review-type.ts b/src/Shared/types/review-type.ts new file mode 100644 index 0000000..7b76ff7 --- /dev/null +++ b/src/Shared/types/review-type.ts @@ -0,0 +1,18 @@ +import { IList } from "./list-type"; + +export interface IReview { + id: number; + author: { + id: number; + first_name: string; + last_name: string; + image: string; + govern_status: null; + }; + review: string; + created_at: string; +} + +export interface IReviewList extends IList { + results: IReview[]; +} diff --git a/src/Shared/types/statistics-type.ts b/src/Shared/types/statistics-type.ts new file mode 100644 index 0000000..ec54965 --- /dev/null +++ b/src/Shared/types/statistics-type.ts @@ -0,0 +1,9 @@ +export interface IStatistics { + 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; +} diff --git a/src/Shared/types/user-rating-type.ts b/src/Shared/types/user-rating-type.ts new file mode 100644 index 0000000..197eb82 --- /dev/null +++ b/src/Shared/types/user-rating-type.ts @@ -0,0 +1,8 @@ +export interface IUserRatings { + user_id: number; + username: string; + report_count: number; + likes_given_count: number; + likes_received_count: number; + average_rating: number; +} diff --git a/src/Shared/ui/components/Typography/Typography.scss b/src/Shared/ui/components/Typography/Typography.scss index 38d9109..afdb2f5 100644 --- a/src/Shared/ui/components/Typography/Typography.scss +++ b/src/Shared/ui/components/Typography/Typography.scss @@ -2,6 +2,7 @@ .typography-h2, .typography-h3 { + width: fit-content; text-align: center; color: $black; font-size: 42px; diff --git a/src/Widgets/ReviewSection/ReviewSection.scss b/src/Widgets/ReviewSection/ReviewSection.scss new file mode 100644 index 0000000..35c9587 --- /dev/null +++ b/src/Widgets/ReviewSection/ReviewSection.scss @@ -0,0 +1,170 @@ +.review-section { + display: flex; + flex-direction: column; + + h3 { + margin-bottom: 50px; + display: flex; + align-items: center; + gap: 6px; + font-size: 24px; + font-weight: 500; + line-height: 29px; + color: rgb(62, 50, 50); + + span { + width: 4px; + height: 10px; + border-radius: 12px; + background: rgb(57, 152, 232); + } + } + + form { + margin-bottom: 70px; + display: flex; + flex-direction: column; + gap: 16px; + textarea { + height: 258px; + width: 100%; + padding: 26px 18px; + border: 1px solid rgb(197, 198, 197); + border-radius: 12px; + } + + button { + align-self: flex-end; + padding: 10px 16px; + border-radius: 12px; + background: rgb(57, 152, 232); + color: white; + font-size: 16px; + font-weight: 500; + line-height: 20px; + } + } + + &__list { + ul { + display: flex; + flex-direction: column; + gap: 30px; + } + + .review { + padding: 20px; + display: flex; + flex-direction: column; + gap: 15px; + border: 1px solid rgb(197, 198, 197); + border-radius: 12px; + + &__author { + display: flex; + align-items: center; + gap: 10px; + + #author-img { + width: 60px; + height: 60px; + border-radius: 50%; + object-fit: cover; + } + } + &__header { + display: flex; + flex-direction: column; + gap: 8px; + } + + &__author-name { + font-size: 16px; + font-weight: 800; + line-height: 19px; + color: rgb(62, 50, 50); + } + + &__date { + display: flex; + align-items: center; + gap: 6px; + p { + font-size: 14px; + font-weight: 500; + line-height: 20px; + + color: rgba(62, 50, 50, 0.75); + } + } + + &__description { + font-size: 18px; + font-weight: 400; + line-height: 20px; + color: rgba(0, 0, 0, 0.75); + } + } + } +} + +@media screen and (max-width: 768px) { + .review-section { + h3 { + margin-bottom: 30px; + } + + form { + margin-bottom: 56px; + } + + &__list { + ul { + display: flex; + flex-direction: column; + gap: 25px; + } + + .review { + padding: 15px 20px; + + &__description { + font-size: 16px; + } + } + } + } +} + +@media screen and (max-width: 550px) { + .review-section { + h3 { + margin-bottom: 10px; + font-size: 20px; + line-height: 24px; + } + + form { + margin-bottom: 50px; + gap: 12px; + + textarea { + height: 130px; + } + } + + &__list { + ul { + gap: 13px; + } + + .review { + padding: 15px; + + &__description { + font-size: 16px; + } + } + } + } +} diff --git a/src/Widgets/ReviewSection/ReviewSection.tsx b/src/Widgets/ReviewSection/ReviewSection.tsx new file mode 100644 index 0000000..ca7cb0f --- /dev/null +++ b/src/Widgets/ReviewSection/ReviewSection.tsx @@ -0,0 +1,132 @@ +"use client"; + +import "./ReviewSection.scss"; +import { apiInstance } from "@/shared/config/apiConfig"; +import { IReviewList } from "@/shared/types/review-type"; +import { useSession } from "next-auth/react"; +import { useEffect, useState } from "react"; +import calendar from "./icons/calendar.svg"; +import Image from "next/image"; + +interface IReviewsSectionProps { + endpoint: string; + id: number; +} + +const ReviewSection: React.FC = ({ + endpoint, + id, +}: IReviewsSectionProps) => { + const [reviews, setReviews] = useState(); + const session = useSession(); + const handleSubmit: React.MouseEventHandler< + HTMLFormElement + > = async (e) => { + e.preventDefault(); + const Authorization = `Bearer ${session.data?.access_token}`; + + const formData = new FormData(e.currentTarget); + + const config = { + headers: { + Authorization, + }, + }; + + if (!formData.get("review")) { + return; + } + + formData.append("news", id.toString()); + + try { + const res = await apiInstance.post( + `/${endpoint}/${id}/reviews/`, + formData, + config + ); + getReviews(); + } catch (error) { + console.log(error); + } + }; + + const getReviews = async () => { + const response = await apiInstance.get( + `/${endpoint}/${id}/reviews/` + ); + + setReviews(response.data); + }; + + useEffect(() => { + getReviews(); + }, []); + + const months: Record = { + "01": "Январь", + "02": "Февраль", + "03": "Март", + "04": "Апрель", + "05": "Май", + "06": "Июнь", + "07": "Июль", + "08": "Август", + "09": "Сентябрь", + "10": "Октябрь", + "11": "Ноябрь", + "12": "Декабрь", + }; + return ( +
+

+ Написать комментарий +

+ +
+