Initial commit — Умный Байт landing

This commit is contained in:
2026-05-13 09:32:45 +03:00
commit 9e21350def
37 changed files with 1950 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
const API_BASE = import.meta.env.VITE_API_URL || '';
export interface Product {
id: number;
slug: string;
title: string;
subtitle: string | null;
description: string;
status: 'production' | 'development' | 'planned';
audience: 'B2B' | 'B2C' | 'global' | null;
icon_key: string | null;
tags: string[];
sort_order: number;
}
export interface ApproachItem {
id: number;
title: string;
description: string;
icon_key: string | null;
sort_order: number;
}
export interface LandingContent {
sections: Record<string, Record<string, unknown>>;
products: Product[];
approach: ApproachItem[];
settings: Record<string, string>;
}
async function request<T>(
path: string,
options: RequestInit = {}
): Promise<T> {
const res = await fetch(`${API_BASE}${path}`, {
credentials: 'include',
headers: {
'Content-Type': 'application/json',
...(options.headers || {}),
},
...options,
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.error || `request_failed_${res.status}`);
}
return res.json();
}
export const api = {
getContent: () => request<LandingContent>('/api/content'),
login: (login: string, password: string) =>
request<{ ok: true; login: string }>('/api/admin/login', {
method: 'POST',
body: JSON.stringify({ login, password }),
}),
logout: () =>
request<{ ok: true }>('/api/admin/logout', { method: 'POST' }),
me: () => request<{ admin: { id: number; login: string } }>('/api/admin/me'),
listProducts: () =>
request<{ products: Product[] }>('/api/admin/products'),
updateProduct: (id: number, data: Partial<Product>) =>
request<{ product: Product }>(`/api/admin/products/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
}),
createProduct: (data: Omit<Product, 'id'>) =>
request<{ product: Product }>('/api/admin/products', {
method: 'POST',
body: JSON.stringify(data),
}),
deleteProduct: (id: number) =>
request<{ ok: true }>(`/api/admin/products/${id}`, {
method: 'DELETE',
}),
listSections: () => request<{ sections: unknown[] }>('/api/admin/sections'),
updateSection: (key: string, data: { content_json?: unknown; title?: string }) =>
request<{ section: unknown }>(`/api/admin/sections/${key}`, {
method: 'PUT',
body: JSON.stringify(data),
}),
listApproach: () => request<{ items: ApproachItem[] }>('/api/admin/approach'),
updateApproach: (id: number, data: Partial<ApproachItem>) =>
request<{ item: ApproachItem }>(`/api/admin/approach/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
}),
listSettings: () =>
request<{ settings: Record<string, string> }>('/api/admin/settings'),
updateSetting: (key: string, value: string) =>
request<{ ok: true }>(`/api/admin/settings/${key}`, {
method: 'PUT',
body: JSON.stringify({ value }),
}),
};