Initial commit: mail-server with WebFlux backend and Angular v22 Synthwave frontend
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { MailService } from '../../services/mail.service';
|
||||
import { Folder } from '../../models/mail.models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout',
|
||||
standalone: true,
|
||||
imports: [RouterOutlet, RouterLink, RouterLinkActive],
|
||||
template: `
|
||||
<div class="min-h-screen relative" [class.light-theme]="isLightTheme()">
|
||||
<!-- Synthwave фон -->
|
||||
<div class="synth-bg" aria-hidden="true">
|
||||
<div class="synth-sun"></div>
|
||||
<div class="synth-grid">
|
||||
<div class="synth-grid-plane"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Контент -->
|
||||
<div class="relative z-10 flex flex-col min-h-screen">
|
||||
<!-- Шапка -->
|
||||
<header class="glass-panel rounded-none border-t-0 border-x-0 p-4 sticky top-0 z-40 backdrop-blur-xl">
|
||||
<div class="max-w-7xl mx-auto flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-synth-pink to-synth-cyan flex items-center justify-center text-synth-dark font-black text-xl font-orbitron shadow-[0_0_15px_rgba(255,0,127,0.8)]">
|
||||
M
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-2xl font-orbitron font-extrabold tracking-wider neon-text-primary">
|
||||
MAIL<span class="text-synth-cyan font-normal">SERVER</span>
|
||||
</h1>
|
||||
<p class="text-xs text-synth-cyan/70 font-mono tracking-widest uppercase">Synthwave Edition</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<!-- Переключатель темы -->
|
||||
<button (click)="toggleTheme()" class="cyber-btn px-3 py-2 rounded-lg text-xs font-bold" [title]="isLightTheme() ? 'Переключить на тёмную тему' : 'Переключить на светлую тему'">
|
||||
{{ isLightTheme() ? '🌙 Dark' : '☀️ Light' }}
|
||||
</button>
|
||||
|
||||
<span class="text-sm font-mono text-slate-400">{{ email }}</span>
|
||||
<button (click)="onLogout()" class="cyber-btn px-4 py-2 rounded-lg text-xs font-bold">
|
||||
<i class="mr-1">⏻</i> Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Основная область -->
|
||||
<div class="flex flex-1 max-w-7xl mx-auto w-full p-4 gap-4">
|
||||
<!-- Боковая панель с папками -->
|
||||
<aside class="w-64 flex-shrink-0">
|
||||
<div class="glass-panel p-4">
|
||||
<h2 class="text-sm font-orbitron font-bold text-synth-cyan mb-3 flex items-center gap-2">
|
||||
<span class="text-synth-pink">📁</span> FOLDERS
|
||||
</h2>
|
||||
<nav class="space-y-1">
|
||||
@for (folder of folders(); track folder.id) {
|
||||
<a routerLink="/inbox" [queryParams]="{folderId: folder.id}"
|
||||
routerLinkActive="bg-synth-pink/20 text-synth-pink border-synth-pink"
|
||||
[queryParamsHandling]=" 'merge'"
|
||||
class="block px-3 py-2 rounded-lg text-sm font-rajdhani text-slate-300 hover:bg-synth-cyan/10 hover:text-synth-cyan border border-transparent transition-all">
|
||||
{{ folder.name }}
|
||||
@if (folder.unreadCount > 0) {
|
||||
<span class="ml-2 px-2 py-0.5 text-xs rounded-full bg-synth-pink/30 text-synth-pink font-mono">
|
||||
{{ folder.unreadCount }}
|
||||
</span>
|
||||
}
|
||||
</a>
|
||||
}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка написать -->
|
||||
<div class="glass-panel p-4 mt-4">
|
||||
<a routerLink="/compose" class="cyber-btn w-full py-3 rounded-xl text-xs font-bold block text-center">
|
||||
✉️ NEW MESSAGE
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Контент маршрутов -->
|
||||
<main class="flex-1 min-w-0">
|
||||
<router-outlet />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ``
|
||||
})
|
||||
export default class LayoutComponent {
|
||||
authService = inject(AuthService);
|
||||
mailService = inject(MailService);
|
||||
|
||||
email = this.authService.getEmail() || '';
|
||||
folders = signal<Folder[]>([]);
|
||||
isLightTheme = signal(false);
|
||||
|
||||
constructor() {
|
||||
this.loadFolders();
|
||||
// Загружаем сохранённую тему
|
||||
const saved = localStorage.getItem('theme');
|
||||
if (saved === 'light') {
|
||||
this.isLightTheme.set(true);
|
||||
document.documentElement.classList.add('light-theme');
|
||||
}
|
||||
}
|
||||
|
||||
toggleTheme(): void {
|
||||
const isLight = !this.isLightTheme();
|
||||
this.isLightTheme.set(isLight);
|
||||
if (isLight) {
|
||||
document.documentElement.classList.add('light-theme');
|
||||
localStorage.setItem('theme', 'light');
|
||||
} else {
|
||||
document.documentElement.classList.remove('light-theme');
|
||||
localStorage.setItem('theme', 'dark');
|
||||
}
|
||||
}
|
||||
|
||||
loadFolders(): void {
|
||||
this.mailService.getFolders().subscribe({
|
||||
next: (folders) => this.folders.set(folders),
|
||||
error: () => console.error('Failed to load folders')
|
||||
});
|
||||
}
|
||||
|
||||
onLogout(): void {
|
||||
this.authService.logout();
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user