284 lines
6.8 KiB
TypeScript
284 lines
6.8 KiB
TypeScript
// src/components/ScreenThree/index.tsx
|
|
import styled from "styled-components";
|
|
import {
|
|
s_f_Text18_400 as Text18_400,
|
|
Text24_700 as Text24_700,
|
|
s_Text40_700 as Text40_700,
|
|
s_Text14_400 as Text14_400,
|
|
} from "../Typography";
|
|
import TradingViewChart from "./TradingViewChart";
|
|
import { useSingleton } from "cms/factory";
|
|
import { AsyncReveal } from "../AsyncReveal";
|
|
|
|
/* ===================== TYPES ===================== */
|
|
type StatCard = {
|
|
id: string;
|
|
value: string;
|
|
label: string;
|
|
color?: string | null;
|
|
order: number;
|
|
};
|
|
|
|
type IndexItem = {
|
|
id: string;
|
|
text: string;
|
|
order: number;
|
|
};
|
|
|
|
type ScreenThree = {
|
|
id: string;
|
|
title?: string | null;
|
|
description?: string | null;
|
|
footnote?: string | null;
|
|
statCards: StatCard[];
|
|
indexes: IndexItem[];
|
|
};
|
|
|
|
/* ===================== GQL SELECTION ===================== */
|
|
const SELECTION = `
|
|
id
|
|
title
|
|
description
|
|
footnote
|
|
statCards(orderBy: { order: asc }) { id value label color order }
|
|
indexes(orderBy: { order: asc }) { id text order }
|
|
`;
|
|
|
|
/* ===================== UTILS ===================== */
|
|
function chunkInto<T>(arr: T[], size: number): T[][] {
|
|
const out: T[][] = [];
|
|
for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));
|
|
return out;
|
|
}
|
|
|
|
/* ===================== STYLES ===================== */
|
|
const ScreenSecondBlock = styled.div`
|
|
width: 100%;
|
|
max-width: 1380px;
|
|
padding: 64px 10px;
|
|
margin: 0 auto;
|
|
display: flex;
|
|
justify-content: center;
|
|
@media (max-width: 768px) {
|
|
padding: 48px 10px;
|
|
}
|
|
`;
|
|
|
|
const ContentCard = styled.div`
|
|
width: 90%;
|
|
max-width: 1200px;
|
|
background: #fff;
|
|
border-radius: 16px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
@media (max-width: 768px) {
|
|
width: 100%;
|
|
}
|
|
`;
|
|
|
|
const HeaderRow = styled.div`
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 48px;
|
|
|
|
@media (max-width: 768px) {
|
|
flex-direction: column-reverse;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
margin-bottom: 32px;
|
|
}
|
|
`;
|
|
|
|
const TitleBlock = styled.div`
|
|
max-width: 720px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
`;
|
|
|
|
const ChartAndStats = styled.div`
|
|
display: flex;
|
|
gap: 32px;
|
|
align-items: stretch;
|
|
|
|
@media (max-width: 768px) {
|
|
flex-direction: column;
|
|
display: block;
|
|
}
|
|
`;
|
|
|
|
const ChartWrapper = styled.div`
|
|
flex: 1;
|
|
border: 1px solid #F5F8FB;
|
|
border-radius: 8px;
|
|
@media (max-width: 768px) {
|
|
width: 100%;
|
|
height: 436px; /* фиксированная высота вместо min-height */
|
|
margin-bottom:32px;
|
|
}
|
|
`;
|
|
|
|
|
|
const StatsWrapper = styled.div`
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24px;
|
|
align-items: stretch;
|
|
|
|
@media (max-width: 768px) {
|
|
width: 100%;
|
|
gap: 16px;
|
|
}
|
|
`;
|
|
|
|
const StatCardRow = styled.div`
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: 24px;
|
|
align-items: stretch;
|
|
|
|
@media (max-width: 768px) {
|
|
width: 100%;
|
|
flex-direction: column;
|
|
}
|
|
`;
|
|
|
|
const StatCardBox = styled.div`
|
|
background: #F5F8FB;
|
|
border-radius: 12px;
|
|
padding: 33px 32px;
|
|
text-align: left;
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: min-content;
|
|
min-height: 188px;
|
|
|
|
@media (max-width: 768px) {
|
|
min-height: auto;
|
|
width: 100%;
|
|
}
|
|
`;
|
|
|
|
const IndexesBlock = styled.div`
|
|
border-radius: 12px;
|
|
|
|
ul {
|
|
margin-top: 12px;
|
|
padding-left: 20px;
|
|
list-style-type: disc;
|
|
list-style-position: outside;
|
|
}
|
|
|
|
li {
|
|
margin: 6px 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
margin-top: 0;
|
|
}
|
|
`;
|
|
|
|
/* ===================== COMPONENT ===================== */
|
|
function ScreenThree() {
|
|
const { data: s3, loading, error } = useSingleton<ScreenThree>(
|
|
"ScreenThreeSection",
|
|
SELECTION
|
|
);
|
|
|
|
const cardGroups = chunkInto<StatCard>(s3?.statCards ?? [], 2);
|
|
const indexes: IndexItem[] = s3?.indexes ?? [];
|
|
|
|
return (
|
|
<ScreenSecondBlock id="business_areas">
|
|
<ContentCard>
|
|
<AsyncReveal loading={loading} error={error} stagger={0.08} from={12}>
|
|
{/* Заголовок + описание */}
|
|
<HeaderRow>
|
|
<TitleBlock>
|
|
{s3?.title ? (
|
|
<div style={{ marginBottom: 24 }}>
|
|
<Text40_700>{s3.title}</Text40_700>
|
|
</div>
|
|
) : null}
|
|
|
|
{s3?.description ? (
|
|
<Text18_400
|
|
style={{textAlign:'left'}}
|
|
dangerouslySetInnerHTML={{
|
|
__html: s3.description.replace(/\n/g, "<br />"),
|
|
}}
|
|
/>
|
|
) : null}
|
|
</TitleBlock>
|
|
</HeaderRow>
|
|
|
|
<ChartAndStats>
|
|
{/* График — оставляем как есть */}
|
|
<ChartWrapper>
|
|
<TradingViewChart />
|
|
</ChartWrapper>
|
|
|
|
{/* Карточки + индексы из CMS */}
|
|
<StatsWrapper>
|
|
{/* Карточки — группами по 2 */}
|
|
{cardGroups.map((row: StatCard[], ri: number) => (
|
|
<StatCardRow key={`row-${ri}`}>
|
|
{row.map((card: StatCard) => (
|
|
<StatCardBox key={card.id}>
|
|
{card.value ? (
|
|
<Text24_700
|
|
style={{
|
|
color: card.color || "#16a34a",
|
|
whiteSpace: "nowrap",
|
|
marginBottom: 24,
|
|
textAlign: "left",
|
|
}}
|
|
>
|
|
{card.value}
|
|
</Text24_700>
|
|
) : null}
|
|
{card.label ? (
|
|
<Text18_400 style={{ textAlign: "left" }}>
|
|
{card.label}
|
|
</Text18_400>
|
|
) : null}
|
|
</StatCardBox>
|
|
))}
|
|
</StatCardRow>
|
|
))}
|
|
|
|
{/* Индексы */}
|
|
{indexes.length > 0 ? (
|
|
<IndexesBlock>
|
|
<Text24_700 style={{ textAlign: "left" }}>
|
|
FRHC is a part of indexes:
|
|
</Text24_700>
|
|
<ul>
|
|
{indexes.map((it: IndexItem) => (
|
|
<li key={it.id}>
|
|
<Text18_400 style={{ textAlign: "left" }}>
|
|
{it.text}
|
|
</Text18_400>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</IndexesBlock>
|
|
) : null}
|
|
|
|
{/* Сноска */}
|
|
{s3?.footnote ? (
|
|
<Text14_400 style={{ textAlign: "left" }}>
|
|
{s3.footnote}
|
|
</Text14_400>
|
|
) : null}
|
|
</StatsWrapper>
|
|
</ChartAndStats>
|
|
</AsyncReveal>
|
|
</ContentCard>
|
|
</ScreenSecondBlock>
|
|
);
|
|
}
|
|
|
|
export default ScreenThree;
|