Законченный MVP
This commit is contained in:
@@ -1,42 +1,316 @@
|
||||
# sv
|
||||
# Flamy Trade — Frontend
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
||||
Публичный лендинг и дашборд ML-прогнозов для крипторынка. Построен на **SvelteKit 5** (Runes API), **Tailwind v4**, **TypeScript** и **lightweight-charts v5**.
|
||||
|
||||
## Creating a project
|
||||
---
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
## Стек
|
||||
|
||||
```sh
|
||||
# create a new project
|
||||
npx sv create my-app
|
||||
| Слой | Технология |
|
||||
|---|---|
|
||||
| Фреймворк | SvelteKit 5 (runes, SSR) |
|
||||
| Язык | TypeScript (strict) |
|
||||
| Стили | Tailwind CSS v4 (Vite plugin, `@theme`) |
|
||||
| Графики | lightweight-charts v5 (TradingView) |
|
||||
| Сервер | `@sveltejs/adapter-node` (Node.js) |
|
||||
| Пакетный менеджер | pnpm |
|
||||
|
||||
---
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
**Требования:** Node.js ≥ 20, pnpm ≥ 9
|
||||
|
||||
```bash
|
||||
# 1. Установить зависимости
|
||||
pnpm install
|
||||
|
||||
# 2. Запустить dev-сервер
|
||||
pnpm dev
|
||||
|
||||
# 3. Открыть в браузере
|
||||
# http://localhost:5173
|
||||
```
|
||||
|
||||
To recreate this project with the same configuration:
|
||||
Остальные команды:
|
||||
|
||||
```sh
|
||||
# recreate this project
|
||||
pnpm dlx sv@0.15.3 create --template minimal --types ts --add prettier eslint tailwindcss="plugins:none" sveltekit-adapter="adapter:node" --install pnpm Flamy-Trade_svelte
|
||||
```bash
|
||||
pnpm build # Сборка для продакшена (папка build/)
|
||||
pnpm preview # Превью продакшен-сборки
|
||||
pnpm check # Svelte + TypeScript проверка типов
|
||||
pnpm lint # ESLint + Prettier проверка
|
||||
pnpm format # Автоформатирование
|
||||
```
|
||||
|
||||
## Developing
|
||||
---
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
## Структура проекта
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
src/
|
||||
├── lib/
|
||||
│ ├── components/
|
||||
│ │ ├── Chart.svelte # lightweight-charts обёртка
|
||||
│ │ ├── home/
|
||||
│ │ │ ├── DashboardMockup.svelte # 3D-макет на главной
|
||||
│ │ │ └── DashboardStatus.svelte
|
||||
│ │ ├── layout/
|
||||
│ │ │ ├── Header.svelte # Фиксированная шапка + мобильное меню
|
||||
│ │ │ └── Footer.svelte
|
||||
│ │ └── ui/
|
||||
│ │ ├── Button.svelte # Универсальная кнопка/ссылка
|
||||
│ │ └── Logo.svelte
|
||||
│ ├── blog/
|
||||
│ │ └── posts.ts # Статьи блога (статические данные)
|
||||
│ ├── stores/
|
||||
│ │ └── chartStore.ts # Типы + генератор фейковых данных
|
||||
│ ├── utils.ts # cn() — утилита для className
|
||||
│ └── index.ts
|
||||
│
|
||||
├── routes/
|
||||
│ ├── +layout.svelte # Корневой layout: Header, Footer, шрифты
|
||||
│ ├── +page.svelte # Главная страница (секции)
|
||||
│ ├── +error.svelte # Страница ошибки
|
||||
│ ├── (sections)/ # Секции главной страницы
|
||||
│ │ ├── Hero.svelte
|
||||
│ │ ├── Ticker.svelte
|
||||
│ │ ├── About.svelte
|
||||
│ │ ├── Features.svelte
|
||||
│ │ ├── Stats.svelte
|
||||
│ │ ├── BlogPreview.svelte
|
||||
│ │ └── Cta.svelte
|
||||
│ ├── about/
|
||||
│ │ ├── +page.svelte
|
||||
│ │ └── _data.ts
|
||||
│ ├── blog/
|
||||
│ │ ├── +page.svelte # Список статей
|
||||
│ │ └── [slug]/
|
||||
│ │ ├── +page.ts # Загрузка статьи по slug
|
||||
│ │ └── +page.svelte # Статья
|
||||
│ └── dashboard/
|
||||
│ └── +page.svelte # Интерактивный дашборд с графиком
|
||||
│
|
||||
└── routes/layout.css # Tailwind @theme + глобальные стили
|
||||
```
|
||||
|
||||
## Building
|
||||
---
|
||||
|
||||
To create a production version of your app:
|
||||
## Дизайн-система
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
### Цвета (`layout.css`)
|
||||
|
||||
```css
|
||||
--color-primary: #fe4b07 /* Акцент — оранжевый */
|
||||
--color-primary-h:#c83e06 /* Hover-состояние */
|
||||
--color-bg: #09080a /* Фон страницы */
|
||||
--color-bg-e: #0f0e10 /* Elevated (шапка, карточки) */
|
||||
--color-bg-c: #141318 /* Card background */
|
||||
--color-bg-h: #1a191e /* Hover / бордеры */
|
||||
--color-title: #f0ede6 /* Основной текст */
|
||||
--color-desc: #8a887f /* Вторичный текст */
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
В Tailwind используются как `bg-primary`, `text-desc`, `border-bg-h` и т.д.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
||||
### Шрифты
|
||||
|
||||
| Переменная | Семейство | Применение |
|
||||
|---|---|---|
|
||||
| `font-display` | Unbounded | Заголовки, кнопки, тикеры |
|
||||
| `font-sans` | DM Sans | Основной текст |
|
||||
| `font-mono` | JetBrains Mono | Цены, метки, метаданные |
|
||||
|
||||
Подключаются через Google Fonts в `+layout.svelte`.
|
||||
|
||||
### Компонент `Button.svelte`
|
||||
|
||||
```svelte
|
||||
<!-- Первичная (ссылка) -->
|
||||
<Button href="/dashboard">Открыть дашборд</Button>
|
||||
|
||||
<!-- Вторичная (кнопка) -->
|
||||
<Button variant="secondary" onclick={handler}>Действие</Button>
|
||||
```
|
||||
|
||||
Props: `href?`, `target?`, `variant?: 'primary' | 'secondary'`, `class?`
|
||||
|
||||
---
|
||||
|
||||
## Дашборд и Charts
|
||||
|
||||
### Архитектура данных
|
||||
|
||||
```
|
||||
chartStore.ts → generateChartData(symbolId, timeframeId)
|
||||
└── возвращает ChartData
|
||||
├── candles: CandleBar[] // 160 свечей
|
||||
├── prediction: PredPoint[] // 19 прогнозных точек
|
||||
├── signal: Signal // direction, confidence, entry/tp/sl
|
||||
├── currentPrice: number
|
||||
└── change24h(Pct): number
|
||||
```
|
||||
|
||||
`generateChartData` — **заглушка с детерминированными фейковыми данными**. При подключении реального бекенда её нужно заменить на API-запрос, сохранив возвращаемые типы:
|
||||
|
||||
```typescript
|
||||
// chartStore.ts — заменить эту функцию на реальный запрос:
|
||||
export async function fetchChartData(symbolId: string, tfId: TimeframeId): Promise<ChartData> {
|
||||
const res = await fetch(`/api/chart/${symbolId}?tf=${tfId}`);
|
||||
return res.json();
|
||||
}
|
||||
```
|
||||
|
||||
### Компонент `Chart.svelte`
|
||||
|
||||
```svelte
|
||||
<Chart data={chartData} decimals={2} height={520} />
|
||||
```
|
||||
|
||||
| Prop | Тип | Описание |
|
||||
|---|---|---|
|
||||
| `data` | `ChartData` | Свечи + прогноз + сигнал |
|
||||
| `decimals` | `number` | Кол-во знаков после запятой для цены |
|
||||
| `height` | `number` | Высота canvas в пикселях (default: 520) |
|
||||
|
||||
Что рендерится:
|
||||
- **CandlestickSeries** — исторические зелёные/красные свечи
|
||||
- **LineSeries** — пунктирная оранжевая линия ML-прогноза (19 свечей вперёд)
|
||||
- **PriceLine** × 3 — горизонтальные линии Entry / TP / SL с подписями на шкале
|
||||
- **OHLC тултип** — кастомный HTML-оверлей при наведении (показывает O/H/L/C и ML-значение)
|
||||
- **ResizeObserver** — автоматически подстраивает ширину и высоту при изменении окна
|
||||
|
||||
### Таймфреймы и символы
|
||||
|
||||
Определены в `chartStore.ts`:
|
||||
|
||||
```typescript
|
||||
SYMBOLS // 8 монет: BTC ETH SOL BNB XRP ADA DOGE LINK
|
||||
TIMEFRAMES // 6 таймфреймов: 1m 5m 15m 1h 4h 1d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Контракт API (для бекенда)
|
||||
|
||||
Дашборд ожидает от бекенда следующую форму ответа:
|
||||
|
||||
```typescript
|
||||
type ChartData = {
|
||||
candles: Array<{
|
||||
time: number; // Unix timestamp (UTC seconds)
|
||||
open: number;
|
||||
high: number;
|
||||
low: number;
|
||||
close: number;
|
||||
}>;
|
||||
prediction: Array<{
|
||||
time: number; // Unix timestamp будущих свечей
|
||||
value: number; // Прогнозная цена (mid/close)
|
||||
}>;
|
||||
signal: {
|
||||
direction: 'long' | 'short';
|
||||
confidence: number; // 0–100
|
||||
entry: number;
|
||||
tp: number;
|
||||
sl: number;
|
||||
};
|
||||
currentPrice: number;
|
||||
change24h: number;
|
||||
change24hPct: number;
|
||||
};
|
||||
```
|
||||
|
||||
Эндпоинт (предполагаемый):
|
||||
```
|
||||
GET /api/chart/:symbol?tf=5m
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Блог
|
||||
|
||||
Статьи хранятся в `src/lib/blog/posts.ts` как статический массив `Post[]`.
|
||||
|
||||
```typescript
|
||||
type Post = {
|
||||
id: number;
|
||||
slug: string; // URL: /blog/:slug
|
||||
date: string; // ISO 8601
|
||||
title: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
content: string; // HTML-строка
|
||||
};
|
||||
```
|
||||
|
||||
Для подключения CMS — заменить `posts` на API-запрос в `+page.ts` / `+page.server.ts`.
|
||||
|
||||
---
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### SvelteKit (`svelte.config.js`)
|
||||
|
||||
- Адаптер: `adapter-node` (деплой как Node.js-сервер)
|
||||
- Runes: принудительно включены для всех файлов вне `node_modules`
|
||||
|
||||
### Tailwind v4 (`layout.css`)
|
||||
|
||||
Tailwind подключается через Vite-плагин (`@tailwindcss/vite`), конфиг CSS-первый — все кастомные токены в блоке `@theme` в `src/routes/layout.css`.
|
||||
|
||||
**Добавить новый токен:**
|
||||
```css
|
||||
/* layout.css */
|
||||
@theme {
|
||||
--color-accent: #your-color;
|
||||
}
|
||||
```
|
||||
После этого доступен как `bg-accent`, `text-accent` и т.д.
|
||||
|
||||
---
|
||||
|
||||
## Деплой
|
||||
|
||||
Сборка производит Node.js-сервер:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
node build/index.js
|
||||
```
|
||||
|
||||
Переменные окружения:
|
||||
```
|
||||
PORT=3000 # порт (default: 3000)
|
||||
HOST=0.0.0.0 # хост
|
||||
ORIGIN=https://... # обязательно при deploy за проксей
|
||||
```
|
||||
|
||||
Docker (минимальный `Dockerfile`):
|
||||
```dockerfile
|
||||
FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
COPY build/ ./build/
|
||||
COPY package.json .
|
||||
RUN npm install --omit=dev --ignore-scripts
|
||||
CMD ["node", "build/index.js"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Соглашения по коду
|
||||
|
||||
- **Svelte 5 Runes** везде: `$state`, `$derived`, `$effect`, `$props` — никакого legacy API
|
||||
- **Нет `export let`** — только деструктуризация `$props()`
|
||||
- **Tailwind-first** — inline-стили только там, где Tailwind не справляется (динамические значения, chart dimensions)
|
||||
- **cn()** из `$lib/utils` для условного объединения классов
|
||||
- **Нет комментариев** если смысл очевиден из имён; комментарий — только для неочевидного инварианта
|
||||
- **SSR-safe** — всё что требует `window`/`document` — только в `onMount`
|
||||
- **Cleanup** — каждый `onMount` возвращает функцию очистки (removeEventListener, disconnect, remove)
|
||||
|
||||
---
|
||||
|
||||
## Известные ограничения
|
||||
|
||||
- Данные в дашборде — **фейковые** (детерминированный генератор). Требует подключения реального API бекенда через замену `generateChartData` в `chartStore.ts`
|
||||
- Прогнозная линия — условная визуализация; реальная модель возвращает `prediction[]` из бекенда
|
||||
- Блог — статические данные в коде; для production рекомендуется CMS или headless API
|
||||
|
||||
Reference in New Issue
Block a user