diff --git a/.env b/.env index df13fd97..e80df4f2 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -REVIEW_FORM_API=https://graff.estate/api \ No newline at end of file +NEXT_PUBLIC_API=https://graff.estate/api \ No newline at end of file diff --git a/package.json b/package.json index 0138bead..26693a5a 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "date-fns": "^3.6.0", "framer-motion": "^11.3.9", "ky": "^1.4.0", "next": "14.2.5", diff --git a/src/api/contactsFormApi.ts b/src/api/contactsFormApi.ts deleted file mode 100644 index 24b22905..00000000 --- a/src/api/contactsFormApi.ts +++ /dev/null @@ -1,5 +0,0 @@ -import ky from 'ky'; - -export const contactsFormApi = ky.extend({ - prefixUrl: process.env.REVIEW_FORM_API, -}); diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 00000000..d80cdaf8 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,5 @@ +import ky from 'ky'; + +export const api = ky.extend({ + prefixUrl: process.env.NEXT_PUBLIC_API, +}); diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index 17bf84b9..3d4e1c58 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -1,29 +1,32 @@ import PostCard from '@/components/Blog/PostCard'; import { ArrowDownIcon } from '@/components/icons/ArrowDown'; +import { CategoryFilterItem } from '@/components/CategoryFilterItem'; +import { YearFilterItem } from '@/components/YearFilterItem'; import { Posts } from '@/consts/Posts'; +import { Title } from '@/components/Title'; export default function BlogPage() { return ( -
-
-

Блог

+
+
+ Блог
-
- - - - +
+ + + +
- - - - - - - + + + + + + +
- +
Все время @@ -31,7 +34,7 @@ export default function BlogPage() {
-
+
{Posts.map(post => ( ))} @@ -39,37 +42,3 @@ export default function BlogPage() {
); } - -function Category({ - text, - count, - chosen = false, -}: { - text: string; - count: string; - chosen?: boolean; -}) { - return ( - - ); -} - -function YearItem({ text }: { text: string }) { - return ( - - ); -} diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index a834532c..ef1fdb85 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -1,3 +1,43 @@ +import { CategoryFilterItem } from '@/components/CategoryFilterItem'; +import { ProjectsList } from '@/components/Projects/ProjectsList'; +import { Title } from '@/components/Title'; +import { YearFilterItem } from '@/components/YearFilterItem'; +import { Vertical } from '@/ui/Vertical'; + export default function ProjectsPage() { - return
Проекты
; + return ( +
+
+ Проекты +
+
+ + + + + +
+
+ + + + + + + + + + + + + + + + +
+
+
+ +
+ ); } diff --git a/src/components/Blog/PostCard.tsx b/src/components/Blog/PostCard.tsx index f1c3a938..74149e36 100644 --- a/src/components/Blog/PostCard.tsx +++ b/src/components/Blog/PostCard.tsx @@ -12,12 +12,20 @@ export default function PostCard({ id, }: IBlogPost) { const [date, month, year] = createdAt.split(' '); + return ( -
+ {title} +

{date}

{month} {year} @@ -26,28 +34,28 @@ export default function PostCard({ {title} {title} -

-
+
+
{tags.map(tag => ( ))}
-

{title}

+

{title}

{desc}

-
+

{date} {month} {year}

diff --git a/src/components/Blog/PostsFilters.tsx b/src/components/Blog/PostsFilters.tsx new file mode 100644 index 00000000..2fc42939 --- /dev/null +++ b/src/components/Blog/PostsFilters.tsx @@ -0,0 +1,3 @@ +export function PostsFilters() { + return
PostsFilters
; +} diff --git a/src/components/Blog/PostsList.tsx b/src/components/Blog/PostsList.tsx new file mode 100644 index 00000000..138bd96e --- /dev/null +++ b/src/components/Blog/PostsList.tsx @@ -0,0 +1,9 @@ +import { Posts } from '@/consts/Posts'; +import { useEffect, useState } from 'react'; + +export function PostsList() { + const [posts, setPosts] = useState(Posts); + useEffect(() => {}, []); + + return
PostsList
; +} diff --git a/src/components/CategoryFilterItem.tsx b/src/components/CategoryFilterItem.tsx new file mode 100644 index 00000000..2d663481 --- /dev/null +++ b/src/components/CategoryFilterItem.tsx @@ -0,0 +1,30 @@ +export function CategoryFilterItem({ + text, + count, + chosen = false, +}: { + text: string; + count?: string; + chosen?: boolean; +}) { + return ( + + ); +} diff --git a/src/components/Layout/ContactsForm.tsx b/src/components/Layout/ContactsForm.tsx index 6566298a..badf8a21 100644 --- a/src/components/Layout/ContactsForm.tsx +++ b/src/components/Layout/ContactsForm.tsx @@ -2,7 +2,7 @@ import InputMask from 'react-input-mask'; import { ChangeEvent, FormEvent, useState } from 'react'; -import { contactsFormApi } from '@/api/contactsFormApi'; +import { api } from '@/api'; import { AsteriskIcon } from '../icons/AsteriskIcon'; import { Button } from '@/ui/Button'; import { LoaderIcon } from '../icons/LoaderIcon'; @@ -27,7 +27,7 @@ export function ContactsForm({ inModal = false }: { inModal?: boolean }) { setIsLoading(true); try { - await contactsFormApi + await api .post('mail', { json: { fullname: name, diff --git a/src/components/Layout/Feedback.tsx b/src/components/Layout/Feedback.tsx index 1b92e678..e089bf91 100644 --- a/src/components/Layout/Feedback.tsx +++ b/src/components/Layout/Feedback.tsx @@ -16,7 +16,8 @@ export function Feedback() {
с нами

- Хотите использовать интерактивные тренажеры в обучении?
+ Хотите увеличить конверсию? +
Давайте обсудим детали!

diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index 17507148..95403f2a 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -36,7 +36,7 @@ export function Header() {
-
+
Продукты
diff --git a/src/components/Main/Motivation.tsx b/src/components/Main/Motivation.tsx index 2bdb47f6..81f52632 100644 --- a/src/components/Main/Motivation.tsx +++ b/src/components/Main/Motivation.tsx @@ -1,15 +1,16 @@ -import SkolkovoIcon from '../icons/SkolkovoIcon'; +import { SkolkovoIcon } from '../icons/SkolkovoIcon'; +import { Title } from '../Title'; export function Motivation() { return (
-

+ Интерактивный инструмент <br /> продаж <span className="text-gradient"> для застройщиков</span> - </h1> +

Помогаем девелоперам эффективно демонстрировать свой объект.
diff --git a/src/components/Main/Video.tsx b/src/components/Main/Video.tsx deleted file mode 100644 index 7c60ff17..00000000 --- a/src/components/Main/Video.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export function Video() { - return
Video
; -} diff --git a/src/components/Projects/ProjectsFilters.tsx b/src/components/Projects/ProjectsFilters.tsx new file mode 100644 index 00000000..d7a9b751 --- /dev/null +++ b/src/components/Projects/ProjectsFilters.tsx @@ -0,0 +1,38 @@ +import { Vertical } from '@/ui/Vertical'; +import { YearFilterItem } from '../YearFilterItem'; +import { CategoryFilterItem } from '../CategoryFilterItem'; +import { useEffect } from 'react'; + +export function ProjectsFilters() { + useEffect(() => {}, []); + + return ( +
+
+ + + + + +
+
+ + + + + + + + + + + + + + + + +
+
+ ); +} diff --git a/src/components/Projects/ProjectsList.tsx b/src/components/Projects/ProjectsList.tsx new file mode 100644 index 00000000..5b733a49 --- /dev/null +++ b/src/components/Projects/ProjectsList.tsx @@ -0,0 +1,44 @@ +'use client'; + +import { api } from '@/api'; +import { IProject } from '@/types/IProject'; +import { getSortedProjects } from '@/utils/getSortedProjects'; +import { getTime } from 'date-fns'; +import { useEffect, useState } from 'react'; + +export function ProjectsList() { + const [projects, setProjects] = useState([]); + const [projectLabels, setProjectLabels] = useState<(string | number)[]>([]); + const [sortedProjects, setSortedProjects] = + useState>(); + + async function getProjects() { + try { + const _projects: IProject[] = await api.get('projects').json(); + const _sortedProjects = getSortedProjects(_projects); + const _projectLabels = Array.from(_sortedProjects.keys()).sort( + (first, second) => getTime(second) - getTime(first), + ); + + setProjects(_projects); + setProjectLabels(_projectLabels); + setSortedProjects(_sortedProjects); + } catch (error) { + if (error instanceof Error) { + alert(`Error: ${error.message}`); + } + } + } + + useEffect(() => { + getProjects(); + }, []); + + useEffect(() => { + console.log('projects: ', projects); + console.log('sortedProjects: ', sortedProjects); + console.log('projectLabels: ', projectLabels); + }, [sortedProjects, projectLabels, projects]); + + return
ProjectsList
; +} diff --git a/src/components/Title.tsx b/src/components/Title.tsx new file mode 100644 index 00000000..54915c1b --- /dev/null +++ b/src/components/Title.tsx @@ -0,0 +1,8 @@ +import { PropsWithChildren } from 'react'; + +export function Title({ + className = '', + children, +}: PropsWithChildren<{ className?: string }>) { + return

{children}

; +} diff --git a/src/components/YearFilterItem.tsx b/src/components/YearFilterItem.tsx new file mode 100644 index 00000000..e3af053d --- /dev/null +++ b/src/components/YearFilterItem.tsx @@ -0,0 +1,5 @@ +export function YearFilterItem({ text }: { text: string }) { + return ( + + ); +} diff --git a/src/components/icons/SkolkovoIcon.tsx b/src/components/icons/SkolkovoIcon.tsx index e62063c0..0cc70b2d 100644 --- a/src/components/icons/SkolkovoIcon.tsx +++ b/src/components/icons/SkolkovoIcon.tsx @@ -1,4 +1,4 @@ -export default function Skolkovo({ +export function SkolkovoIcon({ className = '', type = 'standart', }: { diff --git a/src/stores/usePostsTagsStore.ts b/src/stores/usePostsTagsStore.ts new file mode 100644 index 00000000..353d6131 --- /dev/null +++ b/src/stores/usePostsTagsStore.ts @@ -0,0 +1,11 @@ +import { create } from 'zustand'; + +interface IPostsTagsState { + tags: string[]; + setTags: (tags: string[]) => void; +} + +export const usePostsTagsStore = create()(set => ({ + tags: [], + setTags: tags => set({ tags }), +})); diff --git a/src/stores/usePostsYearsStore.ts b/src/stores/usePostsYearsStore.ts new file mode 100644 index 00000000..b477855c --- /dev/null +++ b/src/stores/usePostsYearsStore.ts @@ -0,0 +1,11 @@ +import { create } from 'zustand'; + +interface IPostsYearsState { + years: string[]; + setYears: (years: string[]) => void; +} + +export const usePostsYearsStore = create()(set => ({ + years: [], + setYears: years => set({ years }), +})); diff --git a/src/stores/useProjectsFiltersStore.ts b/src/stores/useProjectsFiltersStore.ts new file mode 100644 index 00000000..2f165e97 --- /dev/null +++ b/src/stores/useProjectsFiltersStore.ts @@ -0,0 +1,15 @@ +import { create } from 'zustand'; + +interface IProjectsFilters { + devices: string[]; + years: string[]; + setDevices: (devices: string[]) => void; + setYears: (years: string[]) => void; +} + +export const useProjectsFiltersStore = create()(set => ({ + devices: ['Все'], + years: ['Все время'], + setDevices: devices => set({ devices }), + setYears: years => set({ years }), +})); diff --git a/src/types/IProject.ts b/src/types/IProject.ts new file mode 100644 index 00000000..30ae88d6 --- /dev/null +++ b/src/types/IProject.ts @@ -0,0 +1,12 @@ +type Device = 'stream' | 'touch' | 'mobile' | 'vr'; + +export interface IProject { + id?: string; + name: string; + company: string; + city: string; + image: string; + stage?: number; + releaseDate: string; + devices: Device[]; +} diff --git a/src/ui/Vertical.tsx b/src/ui/Vertical.tsx new file mode 100644 index 00000000..de613bed --- /dev/null +++ b/src/ui/Vertical.tsx @@ -0,0 +1,3 @@ +export function Vertical() { + return
; +} diff --git a/src/utils/getDevicesProjects.ts b/src/utils/getDevicesProjects.ts new file mode 100644 index 00000000..cac848a9 --- /dev/null +++ b/src/utils/getDevicesProjects.ts @@ -0,0 +1,6 @@ +// import { IProject } from '@/types/IProject'; + +// export function getProjectsDevices(projects: IProject[]) { +// const projectsDevices: string[] = []; + +// } diff --git a/src/utils/getPostsTags.ts b/src/utils/getPostsTags.ts new file mode 100644 index 00000000..a64eb78d --- /dev/null +++ b/src/utils/getPostsTags.ts @@ -0,0 +1,13 @@ +import { IBlogPost } from '@/types/IBlogPost'; + +export function getPostsTags(posts: IBlogPost[]) { + const tags: string[] = []; + + posts.forEach(post => { + post.tags.forEach(tag => { + if (!tags.includes(tag)) { + tags.push(tag); + } + }); + }); +} diff --git a/src/utils/getSortedProjects.ts b/src/utils/getSortedProjects.ts new file mode 100644 index 00000000..80b44550 --- /dev/null +++ b/src/utils/getSortedProjects.ts @@ -0,0 +1,16 @@ +import { getTime, getYear } from 'date-fns'; +import { IProject } from '../types/IProject'; + +export function getSortedProjects(projects: IProject[]) { + const sortedProjects = new Map(); + const sorted = projects.toSorted( + (first, second) => getTime(second.releaseDate) - getTime(first.releaseDate), + ); + + for (const project of sorted) { + const key = project.stage !== 6 ? 'В работе' : getYear(project.releaseDate); + sortedProjects.set(key, [...(sortedProjects.get(key) ?? []), project]); + } + + return sortedProjects; +} diff --git a/yarn.lock b/yarn.lock index 95635949..27cdb7f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -686,6 +686,11 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +date-fns@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" + integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"