upd
This commit is contained in:
+3
-4
@@ -13,8 +13,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.5.0",
|
||||
"@tailwindcss/postcss": "^4.0.0",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"@tanstack/react-query": "^5.62.7",
|
||||
"@tanstack/react-query-devtools": "^5.64.2",
|
||||
"@tinymce/tinymce-react": "^5.1.1",
|
||||
@@ -30,7 +28,6 @@
|
||||
"libphonenumber-js": "^1.11.7",
|
||||
"next": "14.2.5",
|
||||
"next-intl": "^4.0.2",
|
||||
"postcss": "^8.5.1",
|
||||
"react": "^18",
|
||||
"react-circular-progressbar": "^2.1.0",
|
||||
"react-dom": "^18",
|
||||
@@ -46,7 +43,6 @@
|
||||
"react-transition-group": "^4.4.5",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"sharp": "^0.33.5",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"tinymce": "^7.4.1",
|
||||
"usehooks-ts": "^3.1.0",
|
||||
"zustand": "^4.5.4"
|
||||
@@ -61,8 +57,11 @@
|
||||
"@types/react-phone-number-input": "^3.1.37",
|
||||
"@types/react-rangeslider": "^2.2.7",
|
||||
"@types/react-transition-group": "^4.4.11",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.5",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -2,7 +2,9 @@ import { api } from "@/api";
|
||||
import { IArticle } from "@/types/IArticle";
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
import { Metadata } from "next";
|
||||
import NotFound from "@/app/not-found";
|
||||
import Link from "next/link";
|
||||
import CloseIcon from "@/components/icons/CloseIcon";
|
||||
import { RelevantArticlesPreview } from "@/components/pages/ArticlePage/RelevantArticlesPreview";
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -42,17 +44,19 @@ export default async function Layout({
|
||||
const { slug } = await params;
|
||||
|
||||
return (
|
||||
// <section className="fixed inset-0 bg-[#0F101199] [backdrop-filter:blur(16px)] z-[14]">
|
||||
// <Link href={'/blog'} className="inset-0 fixed"></Link>
|
||||
// <RelevantArticlesPreview slug={slug} />
|
||||
// {children}
|
||||
// <Link
|
||||
// href={'/blog'}
|
||||
// className="fixed lg:right-[1.389vw] lg:top-[1.389vw] right-5 top-5 lg:rounded-[1.111vw] rounded-2xl bg-[#37393B99] lg:p-[1.111vw] p-4 cursor-pointer"
|
||||
// >
|
||||
// <CloseIcon className="lg:w-[1.111vw] lg:h-[1.111vw] w-4 h-4 text-white" />
|
||||
// </Link>
|
||||
// </section>
|
||||
<NotFound />
|
||||
<section className="fixed inset-0 bg-[#0F101199] [backdrop-filter:blur(16px)] z-[14]">
|
||||
<Link href={"/blog"} className="inset-0 fixed"></Link>
|
||||
<RelevantArticlesPreview slug={slug} />
|
||||
{children}
|
||||
<Link
|
||||
href={"/blog"}
|
||||
className="fixed lg:right-[1.389vw] lg:top-[1.389vw] right-5 top-5 lg:rounded-[1.111vw] rounded-2xl bg-[#37393B99] lg:p-[1.111vw] p-4 cursor-pointer"
|
||||
>
|
||||
<div className="lg:w-[1.111vw] lg:h-[1.111vw] w-4 h-4 text-white">
|
||||
<CloseIcon />
|
||||
</div>
|
||||
</Link>
|
||||
</section>
|
||||
// <NotFound />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { api } from '@/api';
|
||||
import { ArticleSyncPage } from '@/components/pages/ArticlePage/ArticleSyncPage';
|
||||
import { IArticle } from '@/types/IArticle';
|
||||
import { api } from "@/api";
|
||||
import { ArticleSyncPage } from "@/components/pages/ArticlePage/ArticleSyncPage";
|
||||
import { IArticle } from "@/types/IArticle";
|
||||
import {
|
||||
dehydrate,
|
||||
HydrationBoundary,
|
||||
QueryClient,
|
||||
queryOptions,
|
||||
} from '@tanstack/react-query';
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
export default async function ArticlePage({
|
||||
params,
|
||||
@@ -19,7 +19,7 @@ export default async function ArticlePage({
|
||||
|
||||
await queryClient.prefetchQuery(
|
||||
queryOptions({
|
||||
queryKey: ['articles', slug],
|
||||
queryKey: ["articles", slug],
|
||||
queryFn: () => api.get(`articles/${slug}`).json<IArticle>(),
|
||||
})
|
||||
);
|
||||
@@ -35,8 +35,11 @@ export async function generateStaticParams() {
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const articles = await queryClient.fetchQuery<IArticle[]>({
|
||||
queryKey: ['articles'],
|
||||
queryFn: () => api.get('articles').json<IArticle[]>(),
|
||||
queryKey: ["articles"],
|
||||
queryFn: () =>
|
||||
api
|
||||
.get("articles", { searchParams: { locale: "en" } })
|
||||
.json<IArticle[]>(),
|
||||
});
|
||||
|
||||
return articles
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { NotFoundPage } from "@/components/pages/NotFoundPage";
|
||||
import { StoriesButton } from "@/ui/StoriesButton";
|
||||
import { Title } from "@/ui/Title";
|
||||
import { Metadata, ResolvingMetadata } from "next";
|
||||
import { InProcess } from "@/components/pages/InProcess";
|
||||
|
||||
export async function generateMetadata(
|
||||
{},
|
||||
@@ -19,17 +17,19 @@ export default async function BlogLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
// <section className="space-y-12">
|
||||
// <div className="flex flex-col gap-6 items-center">
|
||||
// <Title className="text-center" headerLevel={2}>
|
||||
// All about company's work:
|
||||
// <br />
|
||||
// <span className="text-[#7A7A7A]">articles, videos and publications</span>
|
||||
// </Title>
|
||||
// <StoriesButton />
|
||||
// </div>
|
||||
// <div className="grid grid-cols-6 lg:gap-x-[0.833vw]">{children}</div>
|
||||
// </section>
|
||||
<InProcess />
|
||||
<section className="space-y-12">
|
||||
<div className="flex flex-col gap-6 items-center">
|
||||
<Title className="text-center" headerLevel={2}>
|
||||
All about company's work:
|
||||
<br />
|
||||
<span className="text-[#7A7A7A]">
|
||||
articles, videos and publications
|
||||
</span>
|
||||
</Title>
|
||||
<StoriesButton />
|
||||
</div>
|
||||
<div className="grid grid-cols-6 lg:gap-x-[0.833vw]">{children}</div>
|
||||
</section>
|
||||
// <InProcess />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ export default async function BlogPage() {
|
||||
|
||||
await queryClient.prefetchQuery({
|
||||
queryKey: ["articles"],
|
||||
queryFn: () => api.get("articles").json<IArticle[]>(),
|
||||
queryFn: () =>
|
||||
api
|
||||
.get("articles", { searchParams: { locale: "en" } })
|
||||
.json<IArticle[]>(),
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
+54
-49
@@ -1,5 +1,8 @@
|
||||
@import url('/fonts/TTHovesProAll/stylesheet.css');
|
||||
@import 'tailwindcss';
|
||||
@import url("/fonts/TTHovesProAll/stylesheet.css");
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@theme {
|
||||
--gradient: linear-gradient(87deg, #798fff 15%, #d375ff 100%);
|
||||
@@ -10,7 +13,7 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'TTHovesPro';
|
||||
font-family: "TTHovesPro";
|
||||
color: #fff;
|
||||
background-color: #0f1011;
|
||||
}
|
||||
@@ -21,7 +24,7 @@ html {
|
||||
}
|
||||
|
||||
.bg-gradient-card {
|
||||
content: '';
|
||||
content: "";
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
@@ -34,7 +37,7 @@ html {
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
@@ -46,62 +49,64 @@ html {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
@utility line1 {
|
||||
@apply 2xl:text-[128px] lg:max-2xl:text-[clamp(96px,96px+(100vw-1440px)/96*32,128px)] md:max-lg:text-[clamp(56px,56px+(100vw-768px)/672*40,96px)] xs:max-md:text-[clamp(40px,40px+(100vw-360px)/408*16,56px)] text-[40px] leading-[85%];
|
||||
}
|
||||
@layer utilities {
|
||||
.line1 {
|
||||
@apply 2xl:text-[128px] lg:max-2xl:text-[clamp(96px,96px+(100vw-1440px)/96*32,128px)] md:max-lg:text-[clamp(56px,56px+(100vw-768px)/672*40,96px)] xs:max-md:text-[clamp(40px,40px+(100vw-360px)/408*16,56px)] text-[40px] leading-[85%];
|
||||
}
|
||||
|
||||
@utility line2 {
|
||||
@apply lg:text-[clamp(64px,4.444vw,88px)] md:max-lg:text-[clamp(40px,40px+(100vw-768px)/672*24,64px)] xs:max-md:text-[clamp(32px,32px+(100vw-360px)/408*8,40px)] max-xs:text-[32px] leading-[95%];
|
||||
}
|
||||
.line2 {
|
||||
@apply lg:text-[clamp(64px,4.444vw,88px)] md:max-lg:text-[clamp(40px,40px+(100vw-768px)/672*24,64px)] xs:max-md:text-[clamp(32px,32px+(100vw-360px)/408*8,40px)] max-xs:text-[32px] leading-[95%];
|
||||
}
|
||||
|
||||
@utility heading1 {
|
||||
@apply lg:text-[clamp(28px,1.944vw,42px)] md:max-lg:text-[clamp(24px,24px+(100vw-768px)/672*4,28px)] text-2xl leading-[1.167];
|
||||
}
|
||||
.heading1 {
|
||||
@apply lg:text-[clamp(28px,1.944vw,42px)] md:max-lg:text-[clamp(24px,24px+(100vw-768px)/672*4,28px)] text-2xl leading-[1.167];
|
||||
}
|
||||
|
||||
@utility heading2 {
|
||||
@apply lg:text-[clamp(24px,1.667vw,36px)] md:max-lg:text-[clamp(20px,20px+(100vw-768px)/672*4,24px)] xs:max-md:text-[clamp(16px,16px+(100vw-360px)/408*4,20px)] text-base lg:leading-[1.2] leading-[1.125];
|
||||
}
|
||||
.heading2 {
|
||||
@apply lg:text-[clamp(24px,1.667vw,36px)] md:max-lg:text-[clamp(20px,20px+(100vw-768px)/672*4,24px)] xs:max-md:text-[clamp(16px,16px+(100vw-360px)/408*4,20px)] text-base lg:leading-[1.2] leading-[1.125];
|
||||
}
|
||||
|
||||
@utility accent {
|
||||
@apply lg:text-[clamp(32px,2.222vw,56px)] md:max-lg:text-[clamp(24px,24px+(100vw-768px)/672*8,32px)] text-2xl lg:leading-[1.1] leading-none;
|
||||
}
|
||||
.accent {
|
||||
@apply lg:text-[clamp(32px,2.222vw,56px)] md:max-lg:text-[clamp(24px,24px+(100vw-768px)/672*8,32px)] text-2xl lg:leading-[1.1] leading-none;
|
||||
}
|
||||
|
||||
@utility text1 {
|
||||
@apply lg:text-[clamp(18px,1.25vw,24px)] md:max-lg:text-[clamp(14px,14px+(100vw-768px)/672*4,18px)] text-sm leading-[1.35];
|
||||
}
|
||||
.text1 {
|
||||
@apply lg:text-[clamp(18px,1.25vw,24px)] md:max-lg:text-[clamp(14px,14px+(100vw-768px)/672*4,18px)] text-sm leading-[1.35];
|
||||
}
|
||||
|
||||
@utility text2 {
|
||||
@apply lg:text-[clamp(12px,0.972vw,20px)] md:max-lg:text-[clamp(12px,12px+(100vw-768px)/672*2,14px)] text-xs leading-[1.35];
|
||||
}
|
||||
.text2 {
|
||||
@apply lg:text-[clamp(12px,0.972vw,20px)] md:max-lg:text-[clamp(12px,12px+(100vw-768px)/672*2,14px)] text-xs leading-[1.35];
|
||||
}
|
||||
|
||||
@utility btnl {
|
||||
@apply lg:text-[clamp(18px,1.25vw,28px)] md:max-lg:text-[clamp(16px,16+(100vw-768px)/672*2,18px)] text-base leading-none;
|
||||
}
|
||||
.btnl {
|
||||
@apply lg:text-[clamp(18px,1.25vw,28px)] md:max-lg:text-[clamp(16px,16+(100vw-768px)/672*2,18px)] text-base leading-none;
|
||||
}
|
||||
|
||||
@utility btnm {
|
||||
@apply lg:text-[clamp(16px,1.111vw,24px)] md:max-lg:text-[clamp(14px,14px+(100vw-768px)/672*2,16px)] text-sm leading-none;
|
||||
}
|
||||
.btnm {
|
||||
@apply lg:text-[clamp(16px,1.111vw,24px)] md:max-lg:text-[clamp(14px,14px+(100vw-768px)/672*2,16px)] text-sm leading-none;
|
||||
}
|
||||
|
||||
@utility btns {
|
||||
@apply lg:text-[clamp(14px,0.972vw,20px)] md:max-lg:text-[clamp(12px,12px+(100vw-768px)/672*2,14px)] text-xs leading-none;
|
||||
}
|
||||
.btns {
|
||||
@apply lg:text-[clamp(14px,0.972vw,20px)] md:max-lg:text-[clamp(12px,12px+(100vw-768px)/672*2,14px)] text-xs leading-none;
|
||||
}
|
||||
|
||||
@utility caption {
|
||||
@apply text-[clamp(14px,14px+(100vw-360px)/1240*2,16px)] leading-none;
|
||||
}
|
||||
.caption {
|
||||
@apply text-[clamp(14px,14px+(100vw-360px)/1240*2,16px)] leading-none;
|
||||
}
|
||||
|
||||
@utility text-gradient {
|
||||
/* -webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
@apply bg-gradient-to-r from-[#798FFF] to-[#D375FF] bg-clip-text; */
|
||||
background: linear-gradient(87deg, #798fff 15%, #d375ff 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.text-gradient {
|
||||
/* -webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
@apply bg-gradient-to-r from-[#798FFF] to-[#D375FF] bg-clip-text; */
|
||||
background: linear-gradient(87deg, #798fff 15%, #d375ff 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
@utility bg-gradient {
|
||||
background: linear-gradient(87deg, #798fff 15%, #d375ff 100%);
|
||||
.bg-gradient {
|
||||
background: linear-gradient(87deg, #798fff 15%, #d375ff 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@theme {
|
||||
|
||||
@@ -84,6 +84,7 @@ export function ArticleContentFormModal({
|
||||
drafted,
|
||||
posterImage,
|
||||
id,
|
||||
locale: "en",
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useArticleMutation } from '@/hooks/useArticleMutation';
|
||||
import { useModalStore } from '@/stores/useModalStore';
|
||||
import { IArticle } from '@/types/IArticle';
|
||||
import { CheckboxesGroup } from '@/ui/CheckboxesGroup';
|
||||
import { TextInput } from '@/ui/TextInput';
|
||||
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
||||
import { ImageUploader } from '../ImageUploader';
|
||||
import { ArticleContentFormModal } from './ArticleContentFormModal';
|
||||
import { ArticleFormActions } from './ArticleFormActions';
|
||||
import { FormModalHeader } from './FormModalHeader';
|
||||
import ReactLenis, { LenisRef } from 'lenis/react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useLenis } from '@/hooks/useLenis';
|
||||
import { useArticleMutation } from "@/hooks/useArticleMutation";
|
||||
import { useModalStore } from "@/stores/useModalStore";
|
||||
import { IArticle } from "@/types/IArticle";
|
||||
import { CheckboxesGroup } from "@/ui/CheckboxesGroup";
|
||||
import { TextInput } from "@/ui/TextInput";
|
||||
import { FormProvider, useForm, useWatch } from "react-hook-form";
|
||||
import { ImageUploader } from "../ImageUploader";
|
||||
import { ArticleContentFormModal } from "./ArticleContentFormModal";
|
||||
import { ArticleFormActions } from "./ArticleFormActions";
|
||||
import { FormModalHeader } from "./FormModalHeader";
|
||||
|
||||
interface IArticleFormModalProps<TAction extends 'create' | 'edit'> {
|
||||
interface IArticleFormModalProps<TAction extends "create" | "edit"> {
|
||||
action: TAction;
|
||||
defaultValues?: TAction extends 'edit' ? IArticle : undefined;
|
||||
defaultValues?: TAction extends "edit" ? IArticle : undefined;
|
||||
}
|
||||
|
||||
export interface IArticleInput extends Omit<IArticle, 'id'> {}
|
||||
export interface IArticleInput extends Omit<IArticle, "id"> {}
|
||||
|
||||
export function ArticleFormModal<TAction extends 'create' | 'edit'>({
|
||||
export function ArticleFormModal<TAction extends "create" | "edit">({
|
||||
action,
|
||||
defaultValues,
|
||||
}: IArticleFormModalProps<TAction>) {
|
||||
@@ -34,7 +31,7 @@ export function ArticleFormModal<TAction extends 'create' | 'edit'>({
|
||||
tags: [],
|
||||
drafted: true,
|
||||
},
|
||||
mode: 'onChange',
|
||||
mode: "onChange",
|
||||
});
|
||||
|
||||
async function onSubmit(data: IArticleInput) {
|
||||
@@ -53,7 +50,7 @@ export function ArticleFormModal<TAction extends 'create' | 'edit'>({
|
||||
await mutateAsync({
|
||||
...getValues(),
|
||||
blocks: JSON.stringify(
|
||||
defaultValues ? defaultValues.blocks : getValues('blocks') ?? []
|
||||
defaultValues ? defaultValues.blocks : getValues("blocks") ?? []
|
||||
),
|
||||
drafted,
|
||||
});
|
||||
@@ -61,7 +58,7 @@ export function ArticleFormModal<TAction extends 'create' | 'edit'>({
|
||||
}
|
||||
|
||||
const { mutateAsync } = useArticleMutation(
|
||||
action === 'create'
|
||||
action === "create"
|
||||
? { action, id: undefined }
|
||||
: { action, id: defaultValues!.id }
|
||||
);
|
||||
@@ -101,7 +98,7 @@ export function ArticleFormModal<TAction extends 'create' | 'edit'>({
|
||||
</label>
|
||||
<CheckboxesGroup
|
||||
name="tags"
|
||||
options={['Недвижимость', 'Награды', 'Выставки']}
|
||||
options={["Недвижимость", "Награды", "Выставки"]}
|
||||
/>
|
||||
</div>
|
||||
<ImageUploader
|
||||
|
||||
@@ -7,7 +7,6 @@ import { createRef, RefObject, useEffect, useRef, useState } from "react";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import CloseIcon from "@/components/icons/CloseIcon";
|
||||
import { ItemActions } from "../ItemActions";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
|
||||
const { setModal } = useModalStore();
|
||||
@@ -79,8 +78,6 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
|
||||
onSwipedRight: () => setCurrentIndex((prev) => Math.max(0, prev - 1)),
|
||||
});
|
||||
|
||||
const t = useTranslations("stories");
|
||||
|
||||
if (!stories || !stories.length) return null;
|
||||
|
||||
return (
|
||||
@@ -102,7 +99,7 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
|
||||
className={`flex lg:gap-[1.667vw] md:max-lg:gap-6 items-center h-full relative`}
|
||||
>
|
||||
{!!videoRefs.length &&
|
||||
stories?.map(({ id, video, preview, text, createdAt }, index) => (
|
||||
stories.map(({ id, video, preview, text, createdAt }, index) => (
|
||||
<div
|
||||
style={{
|
||||
transform: isLg
|
||||
@@ -113,10 +110,10 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
|
||||
left: isLg || isMd ? `calc(${1 - currentIndex}*1.667vw)` : 0,
|
||||
}}
|
||||
key={id}
|
||||
className={`select-none relative flex items-end group overflow-hidden lg:rounded-[0.833vw] md:max-lg:rounded-xl cursor-pointer transition-transform lg:p-[1.111vw] p-4 ${
|
||||
className={`select-none relative flex items-end group overflow-hidden lg:rounded-[0.833vw] md:max-lg:rounded-xl cursor-pointer w-1/2 transition-transform lg:p-[1.111vw] p-4 ${
|
||||
index === currentIndex
|
||||
? "lg:min-w-[28.125vw] lg:h-[76.433vh] md:max-lg:min-w-[52.734vw] md:max-lg:h-[67.669vh] max-md:min-w-screen max-md:h-dvh"
|
||||
: "lg:min-w-[26.25vw] lg:h-[71.338vh] md:max-lg:min-w-[49.219vw] md:max-lg:h-[63.158vh] max-md:min-w-screen max-md:h-dvh"
|
||||
? "lg:w-[28.125vw] lg:h-[76.433vh] md:max-lg:min-w-[52.734vw] md:max-lg:h-[67.669vh] max-md:min-w-screen max-md:h-dvh"
|
||||
: "lg:w-[26.25vw] lg:h-[71.338vh] md:max-lg:min-w-[49.219vw] md:max-lg:h-[63.158vh] max-md:min-w-screen max-md:h-dvh"
|
||||
}`}
|
||||
onClick={() => setCurrentIndex(index)}
|
||||
>
|
||||
@@ -136,7 +133,10 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
|
||||
/>
|
||||
{currentIndex === index && (
|
||||
<div className="space-y-5 z-1 max-md:hidden">
|
||||
<p className="heading1 font-medium">{t(id)}</p>
|
||||
<p className="heading1 font-medium">
|
||||
Interactive sales tool GRAFF.estate for the Upside Towers
|
||||
residential complex
|
||||
</p>
|
||||
<div className="bg-white/30 w-full h-1 rounded-[34px]">
|
||||
<div
|
||||
className="h-1 bg-white transition-[width] rounded-[34px]"
|
||||
@@ -152,7 +152,8 @@ export function StoriesModal({ startIndex = 0 }: { startIndex?: number }) {
|
||||
{stories && stories.length > 0 && (
|
||||
<div className="md:hidden absolute space-y-6 left-2.5 right-2.5 bottom-4 z-1 w-full">
|
||||
<p className="heading1 font-medium">
|
||||
{stories[currentIndex]?.text}
|
||||
Interactive sales tool GRAFF.estate for the Upside Towers
|
||||
residential complex
|
||||
</p>
|
||||
<div className="flex gap-1">
|
||||
{stories.map(({ id }, index) => (
|
||||
|
||||
@@ -15,6 +15,7 @@ import ArrowMoreIcon from "@/components/icons/ArrowMoreIcon";
|
||||
import EditIcon from "@/components/icons/EditIcon";
|
||||
import { ReactLenis } from "lenis/react";
|
||||
import { useLenis } from "@/hooks/useLenis";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export function ArticleSyncPage({ slug }: { slug: string }) {
|
||||
const { data: article } = useGetArticleById(slug);
|
||||
@@ -25,6 +26,8 @@ export function ArticleSyncPage({ slug }: { slug: string }) {
|
||||
|
||||
const lenis = useLenis();
|
||||
|
||||
const t = useTranslations("feedback");
|
||||
|
||||
if (!article) return null;
|
||||
|
||||
return (
|
||||
@@ -68,9 +71,9 @@ export function ArticleSyncPage({ slug }: { slug: string }) {
|
||||
{article.tags.map((tag) => (
|
||||
<div
|
||||
key={tag}
|
||||
className="bg-[#37393B99] lg:rounded-[1.181vw] rounded-[17px] lg:px-[0.833vw] lg:py-[0.556vw] px-3 py-[7px] btns font-medium backdrop-blur-2xl"
|
||||
className="bg-[#37393B99] text-nowrap lg:rounded-[1.181vw] rounded-[17px] lg:px-[0.833vw] lg:py-[0.556vw] px-3 py-[7px] btns font-medium backdrop-blur-2xl"
|
||||
>
|
||||
{tag}
|
||||
{t(tag)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -80,7 +83,7 @@ export function ArticleSyncPage({ slug }: { slug: string }) {
|
||||
{article.blocks.map((block, index) => (
|
||||
<Fragment key={index}>
|
||||
{block.type === "Content" ? (
|
||||
<div className="lg:max-w-2/3 [&_p_*]:!text1 [&_h1_*]:!heading1 [&_h2_*]:!heading2">
|
||||
<div className="[&_p_*]:!text1 [&_h1_*]:!heading1 [&_h2_*]:!heading2">
|
||||
{parse(block.content)}
|
||||
</div>
|
||||
) : block.type === "ButtonLink" ? (
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { ItemActions } from '@/components/ItemActions';
|
||||
import { IArticle } from '@/types/IArticle';
|
||||
import { PostTag } from '@/ui/PostTag';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { ItemActions } from "@/components/ItemActions";
|
||||
import { IArticle } from "@/types/IArticle";
|
||||
import { PostTag } from "@/ui/PostTag";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
|
||||
export function ArticleCard({
|
||||
id,
|
||||
@@ -22,11 +23,13 @@ export function ArticleCard({
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
const t = useTranslations("feedback");
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => !drafted && push('/blog/' + slug)}
|
||||
onClick={() => !drafted && push("/blog/" + slug)}
|
||||
className={`${
|
||||
className ? className + ' ' : ''
|
||||
className ? className + " " : ""
|
||||
}hover:backdrop-blur-[500px] cursor-pointer h-full group flex flex-col justify-between gap-3 overflow-hidden transition-[background-size] lg:bg-[length:100%_0%] bg-bottom hover:bg-[length:100%_100%] bg-no-repeat lg:rounded-[1.111vw] rounded-2xl lg:p-[0.833vw] p-3 relative`}
|
||||
>
|
||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom,#7A7A7A50,transparent)] max-lg:opacity-100 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
@@ -42,9 +45,9 @@ export function ArticleCard({
|
||||
<div className="flex flex-wrap lg:gap-[0.278vw] gap-1">
|
||||
{tags.map((tag) => (
|
||||
<PostTag
|
||||
text={tag}
|
||||
text={t(tag)}
|
||||
key={tag}
|
||||
active={params.getAll('tags').includes(tag)}
|
||||
active={params.getAll("tags").includes(tag)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -58,6 +61,7 @@ export function ArticleCard({
|
||||
drafted,
|
||||
posterImage,
|
||||
createdAt,
|
||||
locale: "en",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@ export function ArticlesList() {
|
||||
className="lg:w-[21.435vw] lg:aspect-[308.67/352] md:max-lg:w-[47.135vw] max-md:w-full"
|
||||
/>
|
||||
))}
|
||||
<div className="bg-[#37393B99] nth-[3n+1]:m-auto nth-[3n]:w-[21.25vw] max-lg:hidden lg:rounded-[1.111vw] rounded-2xl p-[1.111vw] w-[43.611vw] h-[24.444vw] bg-[url(/img/pages/blog/tg/into_lapenko.jpg)] bg-no-repeat bg-right-bottom bg-[length:calc(582.85/628*100%)] flex flex-col justify-between">
|
||||
{/* <div className="bg-[#37393B99] nth-[3n+1]:m-auto nth-[3n]:w-[21.25vw] max-lg:hidden lg:rounded-[1.111vw] rounded-2xl p-[1.111vw] w-[43.611vw] h-[24.444vw] bg-[url(/img/pages/blog/tg/into_lapenko.jpg)] bg-no-repeat bg-right-bottom bg-[length:calc(582.85/628*100%)] flex flex-col justify-between">
|
||||
<div className="text1 font-medium lg:max-w-[25.069vw]">
|
||||
We haven't moved all publications here yet, but you can find
|
||||
the actual content in our Telegram channel{" "}
|
||||
@@ -52,7 +52,7 @@ export function ArticlesList() {
|
||||
<TelegramIcon />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,6 +50,7 @@ export function Clients() {
|
||||
"Центр-Инвест",
|
||||
"DNS",
|
||||
"ПСК Дом девелопмент",
|
||||
"ПИК",
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { enMapProject } from '@/consts/mockEnMapProject';
|
||||
import { useLongPress } from '@/hooks/useLongPress';
|
||||
import { useMediaQueries } from '@/hooks/useMediaQueries';
|
||||
import { useGetProjectsCountQuery } from '@/queries/getProjectsCount';
|
||||
import { useCityPointStore } from '@/stores/useCityPointStore';
|
||||
import { ICityProjects } from '@/types/ICityProjects';
|
||||
import { IMapProject } from '@/types/IMapProject';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
|
||||
import { useHover } from 'usehooks-ts';
|
||||
import { enMapProjects } from "@/consts/mockEnMapProject";
|
||||
import { useLongPress } from "@/hooks/useLongPress";
|
||||
import { useMediaQueries } from "@/hooks/useMediaQueries";
|
||||
import { useGetProjectsCountQuery } from "@/queries/getProjectsCount";
|
||||
import { useCityPointStore } from "@/stores/useCityPointStore";
|
||||
import { ICityProjects } from "@/types/ICityProjects";
|
||||
import { IMapProject } from "@/types/IMapProject";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
|
||||
import { useHover } from "usehooks-ts";
|
||||
|
||||
export function CityPoint({
|
||||
x,
|
||||
@@ -25,7 +25,7 @@ export function CityPoint({
|
||||
setCurrentHovered: Dispatch<SetStateAction<number | undefined>>;
|
||||
projects?: IMapProject[];
|
||||
}) {
|
||||
const count = enMapProject[title as keyof typeof enMapProject]?.length || 0;
|
||||
const count = enMapProjects.length || 0;
|
||||
|
||||
const { setCityPoint } = useCityPointStore();
|
||||
|
||||
@@ -55,7 +55,7 @@ export function CityPoint({
|
||||
|
||||
const [animationCompleted, setAnimationCompleted] = useState(false);
|
||||
|
||||
const t = useTranslations('cities');
|
||||
const t = useTranslations("cities");
|
||||
|
||||
useEffect(() => {
|
||||
if (animationCompleted && circleRef.current)
|
||||
@@ -87,18 +87,18 @@ export function CityPoint({
|
||||
ref={pointRef}
|
||||
style={{ left: `${x}%`, top: `${y}%` }}
|
||||
className={`absolute outline-none lg:px-[1.111vw] lg:py-[0.625vw] flex lg:gap-[0.278vw] items-center transition-colors cursor-pointer select-none font-medium ${
|
||||
active ? 'text-white' : 'text-[#7A7A7A]'
|
||||
active ? "text-white" : "text-[#7A7A7A]"
|
||||
}`}
|
||||
>
|
||||
<p
|
||||
className={`heading2 font-medium${active ? ' z-2' : ''}`}
|
||||
className={`heading2 font-medium${active ? " z-2" : ""}`}
|
||||
ref={refTitle}
|
||||
>
|
||||
{localeKey ? t(localeKey) : title}
|
||||
</p>
|
||||
<p
|
||||
className={`btns lg:h-[2.083vw] font-medium h-[5.114vw]${
|
||||
active ? ' z-2' : ''
|
||||
active ? " z-2" : ""
|
||||
}`}
|
||||
ref={refCount}
|
||||
>
|
||||
@@ -121,7 +121,7 @@ export function CityPoint({
|
||||
},
|
||||
}}
|
||||
onAnimationComplete={() => setAnimationCompleted(true)}
|
||||
className='absolute rounded-full aspect-square z-1 [backdrop-filter:blur(3.21px)] bg-[radial-gradient(#37393B00,#37393B99)] -translate-x-1/2 left-1/2'
|
||||
className="absolute rounded-full aspect-square z-1 [backdrop-filter:blur(3.21px)] bg-[radial-gradient(#37393B00,#37393B99)] -translate-x-1/2 left-1/2"
|
||||
/>
|
||||
{!!projects?.length &&
|
||||
circleRef.current &&
|
||||
@@ -189,8 +189,8 @@ export function LogoItem({
|
||||
top: -circleRadius,
|
||||
}}
|
||||
exit={{ opacity: 0, transition: { delay: index * 0.1 } }}
|
||||
transition={{ delay: index * 0.1 + 0.3, bounce: 'none' }}
|
||||
className='max-w-[6.264vw] w-full aspect-square absolute rounded-[1.567vw] -left-[9.049vw]'
|
||||
transition={{ delay: index * 0.1 + 0.3, bounce: "none" }}
|
||||
className="max-w-[6.264vw] w-full aspect-square absolute rounded-[1.567vw] -left-[9.049vw]"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { cities, mobileCities } from '@/consts/cities';
|
||||
import { useGetMapPointByCity } from '@/queries/getMapPointByCity';
|
||||
import { useCityPointStore } from '@/stores/useCityPointStore';
|
||||
import { useState } from 'react';
|
||||
import { CityPoint } from './CityPoint';
|
||||
import { Slider } from './Slider';
|
||||
import { useMediaQueries } from '@/hooks/useMediaQueries';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { cities, mobileCities } from "@/consts/cities";
|
||||
import { useGetMapPointByCity } from "@/queries/getMapPointByCity";
|
||||
import { useCityPointStore } from "@/stores/useCityPointStore";
|
||||
import { useState } from "react";
|
||||
import { CityPoint } from "./CityPoint";
|
||||
import { Slider } from "./Slider";
|
||||
import { useMediaQueries } from "@/hooks/useMediaQueries";
|
||||
import { Icon } from "@/ui/Icon";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export function Map() {
|
||||
const { cityPoint } = useCityPointStore();
|
||||
@@ -19,12 +19,12 @@ export function Map() {
|
||||
|
||||
const { isLg } = useMediaQueries();
|
||||
|
||||
const t = useTranslations('map');
|
||||
const t = useTranslations("map");
|
||||
|
||||
return (
|
||||
<div className='relative'>
|
||||
<div className='max-lg:overflow-x-auto overflow-y-visible h-full scrollbar-hide max-lg:aspect-[340/620] md:max-lg:-mx-4 max-md:-mx-2.5 mt-16 relative'>
|
||||
<div className='lg:bg-[url(/img/pages/home/stats/map2.0.png)] bg-[url(/img/pages/home/stats/map_mobile.png)] -mt-19 bg-no-repeat bg-contain lg:aspect-[1432.4/731.98] h-full aspect-[1068.86/586.76] relative'>
|
||||
<div className="relative">
|
||||
<div className="max-lg:overflow-x-auto overflow-y-visible h-full scrollbar-hide max-lg:aspect-[340/620] md:max-lg:-mx-4 max-md:-mx-2.5 mt-16 relative">
|
||||
<div className="lg:bg-[url(/img/pages/home/stats/map2.0.png)] bg-[url(/img/pages/home/stats/map_mobile.png)] -mt-19 bg-no-repeat bg-contain lg:aspect-[1432.4/731.98] h-full aspect-[1068.86/586.76] relative">
|
||||
{(isLg ? cities : mobileCities).map((point, index) => (
|
||||
<CityPoint
|
||||
key={point.title}
|
||||
@@ -37,21 +37,21 @@ export function Map() {
|
||||
}
|
||||
index={index}
|
||||
setCurrentHovered={setCurrentHovered}
|
||||
projects={point.title === cityPoint?.title ? mapPoint : undefined}
|
||||
// projects={point.title === cityPoint?.title ? mapPoint : undefined}
|
||||
/>
|
||||
))}
|
||||
<Slider mapPoint={mapPoint} />
|
||||
<Slider />
|
||||
</div>
|
||||
</div>
|
||||
<div className='lg:hidden absolute left-[50vw] bottom-[4.444vw] translate-y-1/2 -translate-x-1/2 w-[68.611vw] aspect-[247/56] rounded-[4.444vw] bg-[#37393B99] px-[4.167vw] py-[4.444vw] flex gap-[2.222vw] justify-between items-center'>
|
||||
<div className='w-[6.667vw] h-[6.667vw]'>
|
||||
<div className="lg:hidden absolute left-[50vw] bottom-[4.444vw] translate-y-1/2 -translate-x-1/2 w-[68.611vw] aspect-[247/56] rounded-[4.444vw] bg-[#37393B99] px-[4.167vw] py-[4.444vw] flex gap-[2.222vw] justify-between items-center">
|
||||
<div className="w-[6.667vw] h-[6.667vw]">
|
||||
<Icon
|
||||
name='finger_print'
|
||||
svgProp={{ className: 'w-[6.667vw] h-[6.667vw]' }}
|
||||
name="finger_print"
|
||||
svgProp={{ className: "w-[6.667vw] h-[6.667vw]" }}
|
||||
/>
|
||||
</div>
|
||||
<p className='caption leading-[120%] font-medium select-none'>
|
||||
{t('instruction')}
|
||||
<p className="caption leading-[120%] font-medium select-none">
|
||||
{t("instruction")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+12
-13
@@ -4,7 +4,7 @@ import { queryOptions, useQuery } from '@tanstack/react-query';
|
||||
|
||||
export const queryArticlesOptions = {
|
||||
queryKey: ['articles'],
|
||||
queryFn: () => api.get('articles').json<IArticle[]>(),
|
||||
queryFn: () => api.get('articles', { searchParams: { locale: "en" } }).json<IArticle[]>(),
|
||||
};
|
||||
|
||||
export function useGetArticlesQuery(tags?: string | string[]) {
|
||||
@@ -12,18 +12,17 @@ export function useGetArticlesQuery(tags?: string | string[]) {
|
||||
queryOptions(
|
||||
tags && tags.length > 0
|
||||
? {
|
||||
queryKey: ['articles', tags],
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(
|
||||
`articles?${
|
||||
Array.isArray(tags)
|
||||
? tags.map((tag) => `tags=${tag}`).join('&')
|
||||
: 'tags=' + tags
|
||||
}`
|
||||
)
|
||||
.json<IArticle[]>(),
|
||||
}
|
||||
queryKey: ['articles', tags],
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(
|
||||
`articles?${Array.isArray(tags)
|
||||
? tags.map((tag) => `tags=${tag}`).join('&')
|
||||
: 'tags=' + tags
|
||||
}&locale=en`
|
||||
)
|
||||
.json<IArticle[]>(),
|
||||
}
|
||||
: queryArticlesOptions
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { api } from '@/api';
|
||||
import { enMapProject } from '@/consts/mockEnMapProject';
|
||||
import { enMapProjects } from '@/consts/mockEnMapProject';
|
||||
import { useCityPointStore } from '@/stores/useCityPointStore';
|
||||
import { IMapProject } from '@/types/IMapProject';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
@@ -15,6 +15,6 @@ export function useGetMapPointByCity() {
|
||||
// });
|
||||
|
||||
return {
|
||||
data: enMapProject[cityPoint?.title as keyof typeof enMapProject],
|
||||
data: enMapProjects,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,5 +6,6 @@ export function useGetStories() {
|
||||
return useQuery({
|
||||
queryKey: ['stories'],
|
||||
queryFn: () => api.get('stories').json<IStory[]>(),
|
||||
select: (data) => data.slice(2, 3),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,4 +44,5 @@ export interface IArticle {
|
||||
drafted: boolean;
|
||||
slug?: string;
|
||||
blocks: Block[];
|
||||
locale: 'ru' | 'en';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
|
||||
theme: {
|
||||
screens: {
|
||||
xs: "360px",
|
||||
sm: "640px",
|
||||
md: "768px",
|
||||
lg: "1440px",
|
||||
"2xl": "1536px",
|
||||
},
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
Reference in New Issue
Block a user