LendingTelecom/landing/src/components/ScreenThree/index.tsx

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;