feat: Warm Industrial UI redesign + social footer + code deduplication

Design System (Phase A):
- New palette: warm charcoal #111110 / amber gold #c9954a / sage green #7a9a8a
- Fonts: Cabinet Grotesk (heading) + Geist (body) + JetBrains Mono (data)
- SVG grain texture overlay, collapsed sidebar with hover expand
- Bento grid dashboard with WideContinueCard + PosterCard + QuickLink
- Backward-compatible CSS aliases for glass-panel/neon-button/input-base

Social Footer (Phase B):
- AppFooter component with GitHub repo / author homepage / TG group links
- Embedded in LoginPage, Layout (global), and SettingsPage
- TG group badge added to README.md

Code Cleanup:
- Removed deprecated DownloadClientCard / NotifyChannelCard components
- Merged downloadClient/notify API clients into unified modules
- Route deduplication: removed authed-group duplicates for admin endpoints
- Removed redundant AdminPage tabs (sites/downloads/notify/scheduler)
- UI fixes: blank pages (discover/download-clients/notify-channels) nil slice defense
- Email channel support in NotifyChannelsPage
@
This commit is contained in:
ShukeBta
2026-05-17 23:13:26 +08:00
parent 9be81222cc
commit 828ca36d7f
18 changed files with 732 additions and 910 deletions
-51
View File
@@ -1,51 +0,0 @@
import { api } from './client'
import type { DownloadClient } from '../types'
export interface DownloadClientCreateParams {
name: string
type: 'qbittorrent' | 'transmission' | 'aria2'
host: string
username?: string
password?: string
is_default?: boolean
extra?: Record<string, string>
}
export interface DownloadClientUpdateParams {
name?: string
type?: 'qbittorrent' | 'transmission' | 'aria2'
host?: string
username?: string
password?: string
is_default?: boolean
enabled?: boolean
extra?: Record<string, string>
}
export const downloadClientAPI = {
list: () =>
api
.get<{ code: number; data: DownloadClient[] }>('/download-clients')
.then((r) => r.data.data),
get: (id: string) =>
api
.get<{ code: number; data: DownloadClient }>(`/download-clients/${id}`)
.then((r) => r.data.data),
create: (params: DownloadClientCreateParams) =>
api
.post<{ code: number; data: DownloadClient }>('/download-clients', params)
.then((r) => r.data.data),
update: (id: string, params: DownloadClientUpdateParams) =>
api
.put<{ code: number; data: DownloadClient }>(`/download-clients/${id}`, params)
.then((r) => r.data.data),
delete: (id: string) =>
api.delete(`/download-clients/${id}`).then((r) => r.data),
test: (id: string) =>
api.post(`/download-clients/${id}/test`).then((r) => r.data),
}
-50
View File
@@ -1,50 +0,0 @@
import { api } from './client'
import type { NotifyChannel, NotifyProviderInfo } from '../types'
export interface NotifyChannelCreateParams {
name: string
type: 'telegram' | 'wechat' | 'bark' | 'webhook' | 'email'
enabled?: boolean
config: Record<string, string>
events?: string[]
}
export interface NotifyChannelUpdateParams {
name?: string
enabled?: boolean
config?: Record<string, string>
events?: string[]
}
export const notifyAPI = {
list: () =>
api
.get<{ code: number; data: NotifyChannel[] }>('/notify-channels')
.then((r) => r.data.data),
get: (id: string) =>
api
.get<{ code: number; data: NotifyChannel }>(`/notify-channels/${id}`)
.then((r) => r.data.data),
getTypes: () =>
api
.get<{ code: number; data: NotifyProviderInfo[] }>('/notify-channels/types')
.then((r) => r.data.data),
create: (params: NotifyChannelCreateParams) =>
api
.post<{ code: number; data: NotifyChannel }>('/notify-channels', params)
.then((r) => r.data.data),
update: (id: string, params: NotifyChannelUpdateParams) =>
api
.put<{ code: number; data: NotifyChannel }>(`/notify-channels/${id}`, params)
.then((r) => r.data.data),
delete: (id: string) =>
api.delete(`/notify-channels/${id}`).then((r) => r.data),
test: (id: string) =>
api.post(`/notify-channels/${id}/test`).then((r) => r.data),
}