biblioteka-faylov-sozdat-pap/index.html

244 lines
8.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru" data-theme="auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Библиотека файлов</title>
<link rel="stylesheet" href="design-system/kt-ai-fonts.css">
<link rel="stylesheet" href="design-system/kt-ai-tokens.css">
<link rel="stylesheet" href="design-system/kt-ai-components.css">
<link rel="stylesheet" href="design-system/kt-ai-page.css">
<style>
.library-container { display: flex; gap: var(--kt-ai-space-6); }
.sidebar { width: 280px; flex-shrink: 0; }
.main-content { flex: 1; min-width: 0; }
.folder-item, .file-item {
display: flex; align-items: center; justify-content: space-between;
padding: var(--kt-ai-space-3) var(--kt-ai-space-4);
border-radius: var(--kt-ai-radius-md);
cursor: pointer; transition: background 0.2s;
}
.folder-item:hover, .file-item:hover { background: var(--kt-ai-bg-soft); }
.folder-item.active { background: var(--kt-ai-primary); color: white; }
.folder-icon { width: 20px; height: 20px; margin-right: var(--kt-ai-space-3); fill: currentColor; }
.file-icon { width: 20px; height: 20px; margin-right: var(--kt-ai-space-3); fill: currentColor; }
.delete-btn {
opacity: 0; background: none; border: none; cursor: pointer;
padding: var(--kt-ai-space-1); color: var(--kt-ai-fg-muted);
}
.folder-item:hover .delete-btn, .file-item:hover .delete-btn { opacity: 1; }
.delete-btn:hover { color: var(--kt-ai-danger); }
.empty-state {
text-align: center; padding: var(--kt-ai-space-12);
color: var(--kt-ai-fg-muted);
}
.file-list { display: flex; flex-direction: column; gap: var(--kt-ai-space-2); }
.file-meta { font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-muted); }
.folder-item.active .file-meta { color: rgba(255,255,255,0.7); }
.controls { display: flex; gap: var(--kt-ai-space-3); margin-bottom: var(--kt-ai-space-6); }
.search-input { flex: 1; }
.sort-select { min-width: 180px; }
</style>
</head>
<body>
<header class="kt-ai-pagehead">
<div class="kt-ai-wrap kt-ai-pagehead-in">
<strong>📁 Библиотека файлов</strong>
</div>
</header>
<section class="kt-ai-section">
<div class="kt-ai-wrap">
<div class="library-container">
<aside class="sidebar">
<div class="kt-ai-card" style="padding: var(--kt-ai-space-4)">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--kt-ai-space-4)">
<h3 style="margin:0">Папки</h3>
<button class="kt-ai-btn" data-variant="primary" data-size="sm" id="newFolderBtn">+</button>
</div>
<div id="folderList"></div>
</div>
</aside>
<main class="main-content">
<div class="kt-ai-card" style="padding: var(--kt-ai-space-6)">
<div class="controls">
<input class="kt-ai-input search-input" id="searchInput" placeholder="Поиск по названию...">
<select class="kt-ai-select sort-select" id="sortSelect">
<option value="date-desc">Сначала новые</option>
<option value="date-asc">Сначала старые</option>
<option value="name-asc">По имени (А-Я)</option>
<option value="name-desc">По имени (Я-А)</option>
</select>
<button class="kt-ai-btn" data-variant="primary" id="uploadBtn">Загрузить файл</button>
</div>
<h2 id="currentFolderTitle" style="margin-bottom: var(--kt-ai-space-4)">Все файлы</h2>
<div id="fileList" class="file-list"></div>
</div>
</main>
</div>
</div>
</section>
<footer class="kt-ai-wrap kt-ai-pagefoot">
<span>© 2026 Библиотека файлов</span>
</footer>
<script>
let folders = [];
let files = [];
let currentFolderId = null;
let searchTerm = '';
let sortMode = 'date-desc';
async function loadFolders() {
const res = await fetch('/api/folders');
folders = await res.json();
renderFolders();
}
async function loadFiles() {
const res = await fetch('/api/files');
files = await res.json();
renderFiles();
}
function renderFolders() {
const container = document.getElementById('folderList');
container.innerHTML = folders.map(f => `
<div class="folder-item ${currentFolderId === f.id ? 'active' : ''}" onclick="selectFolder(${f.id})">
<div style="display:flex;align-items:center;flex:1">
<svg class="folder-icon" viewBox="0 0 24 24"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>
<span>${f.name}</span>
</div>
<button class="delete-btn" onclick="event.stopPropagation(); deleteFolder(${f.id})">✕</button>
</div>
`).join('');
}
function renderFiles() {
const container = document.getElementById('fileList');
let filtered = files.filter(f => {
if (currentFolderId !== null && f.folderId !== currentFolderId) return false;
if (searchTerm && !f.name.toLowerCase().includes(searchTerm.toLowerCase())) return false;
return true;
});
filtered.sort((a, b) => {
const dateA = new Date(a.createdAt);
const dateB = new Date(b.createdAt);
if (sortMode === 'date-desc') return dateB - dateA;
if (sortMode === 'date-asc') return dateA - dateB;
if (sortMode === 'name-asc') return a.name.localeCompare(b.name);
if (sortMode === 'name-desc') return b.name.localeCompare(a.name);
return 0;
});
if (filtered.length === 0) {
container.innerHTML = '<div class="empty-state">Нет файлов</div>';
return;
}
container.innerHTML = filtered.map(f => {
const folder = folders.find(fl => fl.id === f.folderId);
const size = formatSize(f.size);
const date = new Date(f.createdAt).toLocaleDateString('ru-RU');
return `
<div class="file-item">
<div style="display:flex;align-items:center;flex:1">
<svg class="file-icon" viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>
<div>
<div>${f.name}</div>
<div class="file-meta">${size} • ${date}${folder ? ' • ' + folder.name : ''}</div>
</div>
</div>
<button class="delete-btn" onclick="deleteFile(${f.id})">✕</button>
</div>
`;
}).join('');
}
function formatSize(bytes) {
if (bytes < 1024) return bytes + ' Б';
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' КБ';
return (bytes / 1048576).toFixed(1) + ' МБ';
}
function selectFolder(id) {
currentFolderId = id;
const folder = folders.find(f => f.id === id);
document.getElementById('currentFolderTitle').textContent = folder ? folder.name : 'Все файлы';
renderFolders();
renderFiles();
}
async function createFolder() {
const name = prompt('Название папки:');
if (!name || !name.trim()) return;
try {
const res = await fetch('/api/folders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: name.trim() })
});
if (!res.ok) throw new Error('Ошибка: ' + res.status);
await loadFolders();
} catch (err) {
alert('Не удалось создать папку: ' + err.message);
}
}
async function deleteFolder(id) {
if (!confirm('Удалить папку и все файлы в ней?')) return;
await fetch('/api/folders/' + id, { method: 'DELETE' });
if (currentFolderId === id) {
currentFolderId = null;
document.getElementById('currentFolderTitle').textContent = 'Все файлы';
}
await loadFolders();
await loadFiles();
}
async function deleteFile(id) {
if (!confirm('Удалить файл?')) return;
await fetch('/api/files/' + id, { method: 'DELETE' });
await loadFiles();
}
document.getElementById('newFolderBtn').addEventListener('click', createFolder);
document.getElementById('searchInput').addEventListener('input', (e) => {
searchTerm = e.target.value;
renderFiles();
});
document.getElementById('sortSelect').addEventListener('change', (e) => {
sortMode = e.target.value;
renderFiles();
});
document.getElementById('uploadBtn').addEventListener('click', () => {
const folderId = currentFolderId !== null ? currentFolderId : (folders[0]?.id || null);
if (!folderId && folders.length === 0) {
alert('Сначала создайте папку');
return;
}
const name = prompt('Название файла:');
if (!name) return;
const size = parseInt(prompt('Размер (байт):', '1000')) || 0;
fetch('/api/files', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ folderId: folderId || 1, name, size })
}).then(() => loadFiles());
});
loadFolders();
loadFiles();
</script>
</body>
</html>