Почтовый кабинет: регистрация, папки, черновики, вложения, грид @jambotron/aria-grid, интерфейс по synthwave-шаблону

Бэкенд: регистрация со входом сразу и /api/config, профиль и смена пароля, свои папки, PATCH письма
(прочитано, флажок, перенос; Спам ставит отметку), корзина и очистка, черновики, несколько адресатов и копия,
вложения черновика уходят с письмом, вложения и заголовки To/Cc входящих сохраняются, письма заблокированных —
в Спам. Исправлено: загрузка файла (415), колонка content_id (500 на любых вложениях), SPA-маршрут с точкой.
Фронтенд переписан: все таблицы — грид, оформление — шаблон synthwave-design-showcase.html.

α
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-05 16:41:29 +01:00
parent f605fbfd85
commit 29b1beabe0
70 changed files with 3468 additions and 1737 deletions
+35 -62
View File
@@ -1,5 +1,3 @@
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;800;900&family=Rajdhani:wght@400;500;600;700&family=Fira+Code:wght@400;500;600&display=swap');
@import "tailwindcss";
@theme {
@@ -11,9 +9,9 @@
--color-synth-yellow: #ffbe0b;
--color-synth-glass: rgba(15, 12, 35, 0.65);
--font-family-orbitron: 'Orbitron', sans-serif;
--font-family-rajdhani: 'Rajdhani', sans-serif;
--font-family-fira-code: 'Fira Code', monospace;
--font-orbitron: 'Orbitron', sans-serif;
--font-rajdhani: 'Rajdhani', sans-serif;
--font-mono: 'Fira Code', monospace;
}
:root {
@@ -81,12 +79,7 @@
box-shadow: 0 0 15px var(--secondary-color) !important;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@layer components {
body {
background-color: var(--bg-color);
color: #f1f5f9;
@@ -291,61 +284,41 @@ body {
box-shadow: 0 0 10px var(--secondary-color), 0 0 20px var(--secondary-color);
}
/* Light theme overrides */
.light-theme {
--bg-color: #f0f0f5;
--synth-glass: rgba(255, 255, 255, 0.7);
}
/* App pieces on top of the showcase */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
.light-theme body,
.light-theme {
background-color: #f0f0f5 !important;
color: #1a1a2e !important;
.synth-input {
width: 100%;
background: rgba(2, 6, 23, 0.8);
border: 1px solid rgba(0, 240, 255, 0.4);
border-radius: 10px;
padding: 0.55rem 0.85rem;
color: #e2e8f0;
font-family: 'Rajdhani', sans-serif;
font-size: 1rem;
transition: border-color 0.2s, box-shadow 0.2s;
}
.synth-input:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 2px rgba(255, 0, 127, 0.45); }
.synth-input::placeholder { color: #64748b; }
.synth-label { display: block; font-family: 'Fira Code', monospace; font-size: 0.72rem; color: var(--primary-color); margin-bottom: 0.3rem; letter-spacing: 0.04em; }
.light-theme .glass-panel {
background: rgba(255, 255, 255, 0.8) !important;
border-color: rgba(121, 40, 202, 0.3) !important;
color: #1a1a2e !important;
.ghost-btn {
display: inline-flex; align-items: center; gap: 0.35rem;
padding: 0.4rem 0.75rem; border-radius: 10px;
border: 1px solid rgba(0, 240, 255, 0.35); background: rgba(15, 23, 42, 0.7);
color: #cbd5e1; font-family: 'Fira Code', monospace; font-size: 0.75rem;
transition: all 0.2s;
}
.ghost-btn:hover:not(:disabled) { color: var(--secondary-color); border-color: var(--secondary-color); box-shadow: 0 0 10px var(--glow-color); }
.ghost-btn:focus-visible, .cyber-btn:focus-visible { outline: 3px solid var(--highlight-color); outline-offset: 2px; }
.ghost-btn:disabled, .cyber-btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
.ghost-btn.danger { border-color: rgba(255, 77, 109, 0.5); color: #ff8fa3; }
.ghost-btn.danger:hover:not(:disabled) { color: #fff; background: rgba(255, 77, 109, 0.25); box-shadow: 0 0 10px rgba(255, 77, 109, 0.6); }
.cyber-btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem; }
.cyber-btn .mat-icon, .ghost-btn .mat-icon { font-size: 18px; width: 18px; height: 18px; }
.light-theme .synth-stage {
display: none !important;
}
.glass-panel.static:hover { border-color: rgba(0, 240, 255, 0.25); box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5), inset 0 0 15px rgba(255, 255, 255, 0.03); }
.light-theme .neon-text-primary,
.light-theme .neon-text-secondary {
text-shadow: none !important;
color: #1a1a2e !important;
}
.light-theme input,
.light-theme .cyber-btn {
background: rgba(255, 255, 255, 0.9) !important;
color: #1a1a2e !important;
border-color: rgba(121, 40, 202, 0.4) !important;
}
.light-theme .cyber-btn:hover {
background: var(--accent-color) !important;
color: #fff !important;
}
.light-theme a,
.light-theme span,
.light-theme p,
.light-theme h1,
.light-theme h2,
.light-theme label {
color: #1a1a2e !important;
}
.light-theme .text-slate-200,
.light-theme .text-slate-300,
.light-theme .text-slate-400 {
color: #333 !important;
}
.light-theme .bg-synth-darker\/80 {
background-color: rgba(255, 255, 255, 0.9) !important;
.no-anim .grid-plane { animation: none; }
@media (prefers-reduced-motion: reduce) { .grid-plane { animation: none; } }
}