mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
docs(admin): add dashboard feature design and mockup assets
Organize admin panel feature documentation into a dedicated directory and include an interactive HTML mockup along with a reference screenshot for the EZ-API Control Plane Dashboard.
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
# EZ-API admin-panel Dashboard 功能文档
|
||||
|
||||
## 1. 文档概述
|
||||
|
||||
| **更新日期** | 2026-01-02 |
|
||||
|
||||
---
|
||||
|
||||
## 2. API 端点总览
|
||||
|
||||
| 端点 | 方法 | 用途 | 刷新策略 |
|
||||
|------|------|------|----------|
|
||||
| `/status` | GET | 系统健康状态 | 轮询 30s |
|
||||
| `/auth/whoami` | GET | 当前用户信息 | 页面加载 |
|
||||
| `/admin/realtime` | GET | 实时 QPS/RPM | 轮询 5-10s |
|
||||
| `/admin/dashboard/summary` | GET | 概览指标聚合 | 时间范围变更时 |
|
||||
| `/admin/alerts` | GET | 告警列表 | 轮询 60s |
|
||||
| `/admin/alerts/stats` | GET | 告警统计 | 轮询 60s |
|
||||
| `/admin/logs/stats` | GET | 日志统计 (单维度) | 时间范围变更时 |
|
||||
| `/admin/logs/stats/traffic-chart` | GET | 流量图表 (时间×模型) | 时间范围变更时 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 功能模块对接详情
|
||||
|
||||
### 3.1 全局导航与用户信息
|
||||
|
||||
| 页面元素 | 数据来源 | 字段映射 | 备注 |
|
||||
|----------|----------|----------|------|
|
||||
| 用户头像 | 前端本地配置 / 默认头像 | - | 后端暂无头像字段 |
|
||||
| 用户名称 | 前端本地配置 / 默认文案 | - | 后端暂无姓名字段 |
|
||||
| 用户邮箱 | 前端本地配置 / 隐藏 | - | 后端暂无邮箱字段 |
|
||||
| 用户角色 | `GET /auth/whoami` | `type`, `role` | `type=admin|master|key`,`role=admin` |
|
||||
| Logout | 前端动作 | N/A | 清除本地 Token,重定向登录页 |
|
||||
|
||||
### 3.2 顶部栏状态与工具
|
||||
|
||||
| UI 组件 | 端点 | 字段/逻辑 |
|
||||
|---------|------|-----------|
|
||||
| System Status 胶囊 | `GET /status` | `status == "ok"` → 绿色 "NOMINAL";否则红色 |
|
||||
| Notifications 徽章 | `GET /admin/alerts/stats` | `active > 0` → 显示红点 |
|
||||
|
||||
**交互说明**:
|
||||
|
||||
| 功能点 | 用户操作 | 前端行为 | 后端请求 | 成功反馈 | 异常处理 | 风险/边界 |
|
||||
|--------|----------|----------|----------|----------|----------|-----------|
|
||||
| 系统状态 | 页面加载 / 轮询 | 显示状态胶囊 | `GET /status` | OK → NOMINAL | 请求失败 → 显示 UNKNOWN 或灰色 | 高频失败时避免闪烁 |
|
||||
| 通知入口 | 点击铃铛 | 跳转到告警页面或展开侧栏 | 无 | 页面切换 | 无 | 当前无专用“通知已读”接口 |
|
||||
|
||||
### 3.3 核心指标卡片
|
||||
|
||||
#### 实时指标
|
||||
|
||||
| 卡片名称 | 端点 | 字段 | 渲染逻辑 |
|
||||
|----------|------|------|----------|
|
||||
| Requests Per Minute | `GET /admin/realtime` | `rpm` | 格式化显示 (如 747,000) |
|
||||
| 当前 QPS | `GET /admin/realtime` | `qps` | 小数点后 1 位 |
|
||||
| 限流用户数 | `GET /admin/realtime` | `rate_limited_count` | 整数 |
|
||||
|
||||
#### 统计指标
|
||||
|
||||
| 卡片名称 | 端点 | 字段 | 渲染逻辑 |
|
||||
|----------|------|------|----------|
|
||||
| Active Provider Keys | `GET /admin/dashboard/summary` | `provider_keys.active` | 整数 (上游凭证) |
|
||||
| Total Provider Keys | `GET /admin/dashboard/summary` | `provider_keys.total` | 整数 |
|
||||
| Consumed Tokens | `GET /admin/dashboard/summary` | `tokens.total` | 格式化 (如 892k) |
|
||||
| Input Tokens | `GET /admin/dashboard/summary` | `tokens.input` | - |
|
||||
| Output Tokens | `GET /admin/dashboard/summary` | `tokens.output` | - |
|
||||
| Error Rate | `GET /admin/dashboard/summary` | `requests.error_rate` | 百分比 (如 0.02%) |
|
||||
| Total Requests | `GET /admin/dashboard/summary` | `requests.total` | 整数 |
|
||||
| Failed Requests | `GET /admin/dashboard/summary` | `requests.failed` | 整数 |
|
||||
|
||||
> **字段说明**:
|
||||
> - `provider_keys`: 上游 Provider API 密钥 (model.APIKey)
|
||||
> - `keys`: Master 生成的子 Token (model.Key),用于内部监控
|
||||
> - "Active Keys" UI 卡片应使用 `provider_keys.active`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `period` | string | 预设周期: `today`, `week`, `month`, `last7d`, `last30d`, `all` |
|
||||
| `since` | int | 自定义起始时间 (Unix 秒) |
|
||||
| `until` | int | 自定义结束时间 (Unix 秒) |
|
||||
| `include_trends` | bool | 是否包含趋势数据 (与前一周期对比) |
|
||||
|
||||
**周期类型说明**:
|
||||
|
||||
| 周期值 | 时间窗口 | 趋势对比周期 |
|
||||
|--------|----------|--------------|
|
||||
| `today` | 今日 00:00 UTC → 当前 | 昨日 |
|
||||
| `week` | 本周一 00:00 UTC → 当前 | 上一自然周 |
|
||||
| `month` | 本月 1 日 00:00 UTC → 当前 | 上一自然月 |
|
||||
| `last7d` | 当前 - 7 天 → 当前 | 14 天前 → 7 天前 |
|
||||
| `last30d` | 当前 - 30 天 → 当前 | 60 天前 → 30 天前 |
|
||||
| `all` | 无时间过滤 | 不支持趋势 |
|
||||
|
||||
**趋势数据** (当 `include_trends=true` 时返回):
|
||||
|
||||
```json
|
||||
{
|
||||
"trends": {
|
||||
"requests": { "delta": 12.5, "direction": "up" },
|
||||
"tokens": { "delta": -1.1, "direction": "down" },
|
||||
"error_rate": { "delta": 0.0, "direction": "stable" },
|
||||
"latency": { "delta": -5.2, "direction": "down" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `delta` | 与前一周期相比的百分比变化 (四舍五入到 1 位小数) |
|
||||
| `direction` | `up` (增长 >0.5%), `down` (下降 <-0.5%), `stable`, `new` (无基准数据) |
|
||||
|
||||
**交互说明**:
|
||||
|
||||
| 功能点 | 用户操作 | 前端行为 | 后端请求 | 成功反馈 | 异常处理 | 风险/边界 |
|
||||
|--------|----------|----------|----------|----------|----------|-----------|
|
||||
| 实时卡片刷新 | 页面加载/定时轮询 | 显示加载态或保留上次数据 | `GET /admin/realtime` | 数值更新 | 请求失败 → 保留上次数据并标记“已过期” | `updated_at` 可能为空 |
|
||||
| 时间范围切换 | 选择 24H/7D/30D | 触发汇总与图表更新 | `GET /admin/dashboard/summary` + `GET /admin/logs/stats/traffic-chart` | 刷新 KPI 与趋势 | 400/500 → 展示错误提示 | 7D/30D 为滚动窗口 |
|
||||
| 趋势展示 | 默认展示 | 若 `include_trends=true` 显示趋势 | `GET /admin/dashboard/summary` | 显示 ↑↓ 或 Stable | 无趋势字段 → 隐藏趋势区域 | `direction=new` 仅显示 “New” |
|
||||
|
||||
### 3.4 告警摘要
|
||||
|
||||
**端点**: `GET /admin/alerts?status=active&limit=2&offset=0`
|
||||
|
||||
| UI 元素 | 字段 | 渲染逻辑 |
|
||||
|---------|------|----------|
|
||||
| 状态文本 | `GET /admin/alerts/stats` → `active` | "X active events require attention" |
|
||||
| 事件内容 | `items[i].message` | 告警详情文本 |
|
||||
| 事件时间 | `items[i].created_at` | 转换为相对时间 "42m ago" |
|
||||
| 指示器颜色 | `items[i].severity` | `critical`-> 红色 ;`warning` → 橙色; `info` → 绿色 |
|
||||
|
||||
**告警严重级别**:
|
||||
|
||||
| Severity | 颜色 | 说明 |
|
||||
|----------|------|------|
|
||||
| `critical` | 红色 | 严重告警 |
|
||||
| `warning` | 橙色 | 警告 |
|
||||
| `info` | 绿色 | 信息 |
|
||||
|
||||
**交互说明**:
|
||||
|
||||
| 功能点 | 用户操作 | 前端行为 | 后端请求 | 成功反馈 | 异常处理 | 风险/边界 |
|
||||
|--------|----------|----------|----------|----------|----------|-----------|
|
||||
| 告警摘要展示 | 页面加载/轮询 | 拉取统计与最新告警 | `GET /admin/alerts/stats` + `GET /admin/alerts` | 更新列表与计数 | 请求失败 → 显示占位文案 | 列表为空 → 显示 "All Clear" |
|
||||
| 进入告警页 | 点击卡片/箭头 | 路由跳转 | 无 | 进入详情页 | 无 | 路由未实现时隐藏入口 |
|
||||
|
||||
### 3.5 流量分析图表 (Traffic Analysis)
|
||||
|
||||
#### 推荐端点: `GET /admin/logs/stats/traffic-chart`
|
||||
|
||||
此端点专为堆叠流量图表设计,返回"时间×模型"二维聚合数据。
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `granularity` | string | `hour` | 时间粒度: `hour` 或 `minute` |
|
||||
| `since` | int | 24h 前 | 起始时间 (Unix 秒) |
|
||||
| `until` | int | 当前 | 结束时间 (Unix 秒) |
|
||||
| `top_n` | int | 5 | Top 模型数量 (1-20),其余归入 "other" |
|
||||
|
||||
**时间粒度约束**:
|
||||
|
||||
| 粒度 | 约束 | 典型场景 |
|
||||
|------|------|----------|
|
||||
| `hour` | 无限制 | 24H / 7D / 30D 视图 |
|
||||
| `minute` | 必须提供 `since` + `until`,范围 ≤ 6 小时 | 近 1-6 小时高精度视图 |
|
||||
|
||||
**响应结构**:
|
||||
|
||||
```json
|
||||
{
|
||||
"granularity": "hour",
|
||||
"since": 1735689600,
|
||||
"until": 1735776000,
|
||||
"models": ["gpt-4-turbo", "claude-3.5-sonnet", "llama-3-70b", "other"],
|
||||
"buckets": [
|
||||
{
|
||||
"time": "2025-01-01T00:00:00Z",
|
||||
"timestamp": 1735689600,
|
||||
"breakdown": {
|
||||
"gpt-4-turbo": { "count": 1200, "tokens_in": 50000, "tokens_out": 80000 },
|
||||
"claude-3.5-sonnet": { "count": 800, "tokens_in": 30000, "tokens_out": 45000 },
|
||||
"other": { "count": 50, "tokens_in": 2000, "tokens_out": 3000 }
|
||||
},
|
||||
"total": { "count": 2050, "tokens_in": 82000, "tokens_out": 128000 }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**前端渲染建议**:
|
||||
|
||||
| 图表类型 | 数据映射 |
|
||||
|----------|----------|
|
||||
| X 轴 | `buckets[i].time` (格式化为 00:00, 04:00 等) |
|
||||
| Y 轴 | 请求数量 |
|
||||
| 堆叠系列 | `models` 数组,每个模型一个系列 |
|
||||
| 系列数据 | `buckets[i].breakdown[model].count` |
|
||||
| 总量标签 | `buckets[i].total.count` |
|
||||
|
||||
**时间范围选择器映射**:
|
||||
|
||||
| 选择器选项 | 请求参数 |
|
||||
|------------|----------|
|
||||
| 24H (默认) | `granularity=hour&since={now-24h}&until={now}` |
|
||||
| 7D | `granularity=hour&since={now-7d}&until={now}` |
|
||||
| 30D | `granularity=hour&since={now-30d}&until={now}` |
|
||||
| 最近 1H | `granularity=minute&since={now-1h}&until={now}` |
|
||||
| 最近 6H | `granularity=minute&since={now-6h}&until={now}` |
|
||||
|
||||
#### 备选端点: `GET /admin/logs/stats`
|
||||
|
||||
单维度聚合,适用于简单场景。
|
||||
|
||||
| 参数 | 返回数据 |
|
||||
|------|----------|
|
||||
| `group_by=hour` | 按小时聚合的时间序列 |
|
||||
| `group_by=minute` | 按分钟聚合的时间序列 (需 since/until,≤6h) |
|
||||
| `group_by=model` | 按模型聚合的分布数据 |
|
||||
|
||||
**交互说明**:
|
||||
|
||||
| 功能点 | 用户操作 | 前端行为 | 后端请求 | 成功反馈 | 异常处理 | 风险/边界 |
|
||||
|--------|----------|----------|----------|----------|----------|-----------|
|
||||
| 图表加载 | 页面加载 | 请求默认 24H 图表 | `GET /admin/logs/stats/traffic-chart` | 渲染堆叠柱状图 | 请求失败 → 显示错误提示 | `buckets` 为空 → 显示 No Data |
|
||||
| 时间粒度切换 | 选择分钟级范围 | 自动切换 granularity=minute | `GET /admin/logs/stats/traffic-chart` | 图表更细粒度 | 400 → 回退到 hour 并提示 | minute 范围 ≤ 6h |
|
||||
| Top 模型限制 | 修改显示数量 | 约束 top_n ≤ 20 | `GET /admin/logs/stats/traffic-chart` | 图表更新 | 400 → 自动回退到 20 | 模型过多时显示 "other" |
|
||||
|
||||
### 3.6 模型使用排行 (Top Models)
|
||||
|
||||
**端点**: `GET /admin/dashboard/summary`
|
||||
|
||||
**字段**: `top_models` 数组
|
||||
|
||||
| UI 元素 | 字段 | 渲染逻辑 |
|
||||
|---------|------|----------|
|
||||
| 模型名称 | `top_models[i].model` | 显示模型 ID |
|
||||
| 请求数量 | `top_models[i].requests` | 整数 |
|
||||
| Token 数量 | `top_models[i].tokens` | 整数 |
|
||||
| 占比百分比 | 前端计算 | `requests / sum(requests)` → 百分比 |
|
||||
| 进度条颜色 | 索引 | 固定色板: 蓝→靛→紫→青→蓝绿 |
|
||||
|
||||
**展开视图端点**: `GET /admin/logs/stats?group_by=model&since={unix}&until={unix}`
|
||||
|
||||
**展开视图字段**: `items[i].model`, `items[i].count`, `items[i].tokens_in`, `items[i].tokens_out`, `items[i].avg_latency_ms`
|
||||
|
||||
**交互说明**:
|
||||
|
||||
| 功能点 | 用户操作 | 前端行为 | 后端请求 | 成功反馈 | 异常处理 | 风险/边界 |
|
||||
|--------|----------|----------|----------|----------|----------|-----------|
|
||||
| 排行加载 | 页面加载/时间范围变更 | 拉取 Top Models | `GET /admin/dashboard/summary` | 渲染列表与进度条 | 请求失败 → 显示占位 | 为空 → 隐藏卡片 |
|
||||
| 展开查看更多 | 点击“查看更多/展开” | 打开弹窗/抽屉,展示完整模型列表 | `GET /admin/logs/stats?group_by=model` | 列表显示全量模型 | 请求失败 → 显示错误提示 | 模型过多时需滚动或搜索 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 错误处理
|
||||
|
||||
| HTTP 状态码 | 处理方式 |
|
||||
|-------------|----------|
|
||||
| 200 | 正常渲染数据 |
|
||||
| 400 | 显示参数错误提示 |
|
||||
| 401 | Token 过期,触发自动登出 |
|
||||
| 403 | 权限不足提示 |
|
||||
| 500 | 显示系统错误,建议重试 |
|
||||
|
||||
**空数据处理**:
|
||||
|
||||
| 场景 | UI 表现 |
|
||||
|------|---------|
|
||||
| 图表无数据 | 显示 "No Data" 占位符 |
|
||||
| 告警列表为空 | 显示 "All Clear" 状态 |
|
||||
| Top Models 为空 | 隐藏该卡片或显示提示 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据刷新策略
|
||||
|
||||
| 数据类型 | 刷新机制 | 频率 |
|
||||
|----------|----------|------|
|
||||
| 系统状态 | 定时轮询 | 30 秒 |
|
||||
| 实时指标 (RPM/QPS) | 定时轮询 | 5-10 秒 |
|
||||
| 告警统计 | 定时轮询 | 60 秒 |
|
||||
| 概览指标 | 时间范围变更触发 | 按需 |
|
||||
| 流量图表 | 时间范围变更触发 | 按需 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 开发与测试建议
|
||||
|
||||
| 建议项 | 说明 |
|
||||
|--------|------|
|
||||
| Mock 数据 | 前端可利用 Mock 数据先行开发 UI |
|
||||
| Swagger 验证 | 复杂聚合接口建议先在 Swagger 中验证 |
|
||||
| 性能监控 | 关注 traffic-chart 接口在大数据量下的响应时间 |
|
||||
|
||||
---
|
||||
|
||||
*注:本对接文档基于现有端点整理*
|
||||
466
docs/feat/admin-panel-dashboard-feat/code.html
Normal file
466
docs/feat/admin-panel-dashboard-feat/code.html
Normal file
@@ -0,0 +1,466 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>EZ-API Control Plane Dashboard</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#3b82f6", // Bright blue for primary actions
|
||||
"primary-dark": "#1d4ed8",
|
||||
"accent": "#6366f1", // Indigo accent
|
||||
"background-light": "#f3f4f6",
|
||||
"background-dark": "#0f172a", // Very dark slate/blue
|
||||
"surface-light": "#ffffff",
|
||||
"surface-dark": "#1e293b", // Lighter slate for cards
|
||||
"surface-darker": "#020617", // Almost black for sidebar
|
||||
},
|
||||
fontFamily: {
|
||||
display: ["Inter", "sans-serif"],
|
||||
body: ["Inter", "sans-serif"],
|
||||
},
|
||||
borderRadius: {
|
||||
DEFAULT: "0.5rem",
|
||||
'xl': '1rem',
|
||||
'2xl': '1.5rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #0f172a;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #334155;
|
||||
border-radius: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #475569;
|
||||
}
|
||||
.glass-effect {
|
||||
background: rgba(30, 41, 59, 0.7);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background-light dark:bg-background-dark text-slate-800 dark:text-slate-200 transition-colors duration-300 dark h-screen flex overflow-hidden">
|
||||
<aside class="w-64 bg-white dark:bg-surface-darker border-r border-slate-200 dark:border-slate-800 flex flex-col justify-between flex-shrink-0 z-20">
|
||||
<div>
|
||||
<div class="h-16 flex items-center px-6 border-b border-slate-200 dark:border-slate-800">
|
||||
<div class="w-8 h-8 rounded bg-primary flex items-center justify-center mr-3 shadow-lg shadow-blue-500/30">
|
||||
<span class="material-icons text-white text-sm">hub</span>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="font-bold text-sm text-slate-900 dark:text-white leading-tight">EZ-API</h1>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">Control Plane</p>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="p-4 space-y-1">
|
||||
<a class="flex items-center px-4 py-3 bg-primary/10 text-primary rounded-lg group transition-all" href="#">
|
||||
<span class="material-icons mr-3 text-xl">dashboard</span>
|
||||
<span class="font-medium text-sm">Dashboard</span>
|
||||
</a>
|
||||
<a class="flex items-center px-4 py-3 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800/50 hover:text-slate-900 dark:hover:text-white rounded-lg group transition-all" href="#">
|
||||
<span class="material-icons mr-3 text-xl group-hover:text-primary transition-colors">vpn_key</span>
|
||||
<span class="font-medium text-sm">API Keys</span>
|
||||
</a>
|
||||
<a class="flex items-center px-4 py-3 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800/50 hover:text-slate-900 dark:hover:text-white rounded-lg group transition-all" href="#">
|
||||
<span class="material-icons mr-3 text-xl group-hover:text-primary transition-colors">bar_chart</span>
|
||||
<span class="font-medium text-sm">Analytics</span>
|
||||
</a>
|
||||
<a class="flex items-center px-4 py-3 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800/50 hover:text-slate-900 dark:hover:text-white rounded-lg group transition-all" href="#">
|
||||
<span class="material-icons mr-3 text-xl group-hover:text-primary transition-colors">tune</span>
|
||||
<span class="font-medium text-sm">Gateway Settings</span>
|
||||
</a>
|
||||
<a class="flex items-center px-4 py-3 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800/50 hover:text-slate-900 dark:hover:text-white rounded-lg group transition-all" href="#">
|
||||
<span class="material-icons mr-3 text-xl group-hover:text-primary transition-colors">terminal</span>
|
||||
<span class="font-medium text-sm">Logs</span>
|
||||
</a>
|
||||
<a class="flex items-center px-4 py-3 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800/50 hover:text-slate-900 dark:hover:text-white rounded-lg group transition-all" href="#">
|
||||
<span class="material-icons mr-3 text-xl group-hover:text-primary transition-colors">credit_card</span>
|
||||
<span class="font-medium text-sm">Billing</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="p-4 border-t border-slate-200 dark:border-slate-800 space-y-1">
|
||||
<a class="flex items-center px-4 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white text-sm transition-colors" href="#">
|
||||
<span class="material-icons mr-3 text-lg">description</span>
|
||||
Documentation
|
||||
</a>
|
||||
<a class="flex items-center px-4 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white text-sm transition-colors mb-4" href="#">
|
||||
<span class="material-icons mr-3 text-lg">headset_mic</span>
|
||||
Support
|
||||
</a>
|
||||
<div class="flex items-center p-3 bg-slate-100 dark:bg-slate-800/50 rounded-xl border border-slate-200 dark:border-slate-700 mt-4">
|
||||
<div class="relative">
|
||||
<img alt="User Avatar" class="w-9 h-9 rounded-full object-cover border border-slate-300 dark:border-slate-600" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDtjVsB0qfW3oChJxs05sru_BK-FSqC1sZwvdso3DjLfnr-iB_pgcmyOz4SwpGMOX_zCgSfdvamkjiU-5FUnUTdZqAFJQIrQClxRJ_OHrKDt33pe_ugSjHOI8_8drUr84EbXuQ7rBz_U9z-qEt9QnUOJ3D8Q4E1yBFA0LAv4RUzx0CAzTiOTxwjT00DLIPQb0VPJIofgFDdMIGxD7hc9JRWilp_rJD0xzWzMdtYH6gjv8crqZGaQvuymzNEgJa7PrUwi8yolMMNXcbk"/>
|
||||
<div class="absolute bottom-0 right-0 w-2.5 h-2.5 bg-green-500 rounded-full border-2 border-white dark:border-slate-800"></div>
|
||||
</div>
|
||||
<div class="ml-3 flex-1 min-w-0">
|
||||
<p class="text-xs font-medium text-slate-900 dark:text-white truncate">Admin User</p>
|
||||
<p class="text-[10px] text-slate-500 dark:text-slate-400 truncate">admin@ez-api.io</p>
|
||||
</div>
|
||||
<button class="text-slate-400 hover:text-slate-600 dark:hover:text-white transition-colors">
|
||||
<span class="material-icons text-sm">logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="flex-1 flex flex-col min-w-0 overflow-hidden bg-background-light dark:bg-background-dark relative">
|
||||
<div class="absolute inset-0 z-0 opacity-[0.03] dark:opacity-[0.05] pointer-events-none" style="background-image: linear-gradient(#6366f1 1px, transparent 1px), linear-gradient(90deg, #6366f1 1px, transparent 1px); background-size: 40px 40px;">
|
||||
</div>
|
||||
<header class="h-16 flex items-center justify-between px-8 border-b border-slate-200 dark:border-slate-800 bg-white/80 dark:bg-background-dark/80 backdrop-blur-md z-10">
|
||||
<div class="flex items-center">
|
||||
<div class="inline-flex items-center px-3 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-600 dark:text-emerald-400 text-xs font-medium">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500 mr-2 animate-pulse"></span>
|
||||
SYSTEM STATUS: NOMINAL
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-6">
|
||||
<div class="relative w-64">
|
||||
<span class="material-icons absolute left-3 top-2.5 text-slate-400 text-sm">search</span>
|
||||
<input class="w-full bg-slate-100 dark:bg-slate-800 border-none rounded-lg py-2 pl-10 pr-4 text-sm text-slate-800 dark:text-slate-200 placeholder-slate-400 focus:ring-2 focus:ring-primary" placeholder="Search resources..." type="text"/>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<button class="relative text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-white transition-colors">
|
||||
<span class="material-icons">notifications</span>
|
||||
<span class="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full border-2 border-white dark:border-background-dark"></span>
|
||||
</button>
|
||||
<button class="text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-white transition-colors">
|
||||
<span class="material-icons">help</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="flex-1 overflow-auto p-8 z-10">
|
||||
<div class="max-w-7xl mx-auto space-y-6">
|
||||
<div class="flex items-end justify-between mb-8">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-slate-900 dark:text-white tracking-tight">System Performance</h2>
|
||||
<p class="text-slate-500 dark:text-slate-400 mt-1 text-sm">Real-time metrics from EZ-API Gateway Cluster</p>
|
||||
</div>
|
||||
<div class="flex items-center bg-white dark:bg-surface-dark border border-slate-200 dark:border-slate-700 rounded-lg p-1">
|
||||
<span class="text-xs font-medium text-slate-500 dark:text-slate-400 px-3">Time Range:</span>
|
||||
<select class="bg-transparent border-none text-sm font-semibold text-slate-900 dark:text-white focus:ring-0 py-1 pl-0 pr-8 cursor-pointer">
|
||||
<option>24H</option>
|
||||
<option>7D</option>
|
||||
<option>30D</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div class="bg-white dark:bg-surface-dark border border-slate-200 dark:border-slate-800 rounded-2xl p-5 shadow-sm hover:shadow-md transition-shadow relative overflow-hidden group">
|
||||
<div class="absolute -right-6 -top-6 w-24 h-24 bg-blue-500/10 rounded-full group-hover:scale-110 transition-transform duration-500"></div>
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div class="w-10 h-10 rounded-lg bg-blue-500/10 flex items-center justify-center text-blue-500">
|
||||
<span class="material-icons">bolt</span>
|
||||
</div>
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-emerald-500/10 text-emerald-600 dark:text-emerald-400">
|
||||
<span class="material-icons text-[10px] mr-1">trending_up</span> +12%
|
||||
</span>
|
||||
</div>
|
||||
<h3 class="text-slate-500 dark:text-slate-400 text-sm font-medium">Requests Per Minute</h3>
|
||||
<div class="mt-1 text-3xl font-bold text-slate-900 dark:text-white">747,000</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-surface-dark border border-slate-200 dark:border-slate-800 rounded-2xl p-5 shadow-sm hover:shadow-md transition-shadow relative overflow-hidden group">
|
||||
<div class="absolute -right-6 -top-6 w-24 h-24 bg-indigo-500/10 rounded-full group-hover:scale-110 transition-transform duration-500"></div>
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div class="w-10 h-10 rounded-lg bg-indigo-500/10 flex items-center justify-center text-indigo-500">
|
||||
<span class="material-icons">vpn_key</span>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-slate-500 dark:text-slate-400 text-sm font-medium">Active Keys</h3>
|
||||
<div class="mt-1 text-3xl font-bold text-slate-900 dark:text-white">2,350</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-surface-dark border border-slate-200 dark:border-slate-800 rounded-2xl p-5 shadow-sm hover:shadow-md transition-shadow relative overflow-hidden group">
|
||||
<div class="absolute -right-6 -top-6 w-24 h-24 bg-amber-500/10 rounded-full group-hover:scale-110 transition-transform duration-500"></div>
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div class="w-10 h-10 rounded-lg bg-amber-500/10 flex items-center justify-center text-amber-500">
|
||||
<span class="material-icons">token</span>
|
||||
</div>
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-rose-500/10 text-rose-600 dark:text-rose-400">
|
||||
<span class="material-icons text-[10px] mr-1">trending_down</span> -1.1%
|
||||
</span>
|
||||
</div>
|
||||
<h3 class="text-slate-500 dark:text-slate-400 text-sm font-medium">Consumed Tokens</h3>
|
||||
<div class="mt-1 text-3xl font-bold text-slate-900 dark:text-white">892k</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-surface-dark border border-slate-200 dark:border-slate-800 rounded-2xl p-5 shadow-sm hover:shadow-md transition-shadow relative overflow-hidden group">
|
||||
<div class="absolute -right-6 -top-6 w-24 h-24 bg-rose-500/10 rounded-full group-hover:scale-110 transition-transform duration-500"></div>
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div class="w-10 h-10 rounded-lg bg-rose-500/10 flex items-center justify-center text-rose-500">
|
||||
<span class="material-icons">error_outline</span>
|
||||
</div>
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-emerald-500/10 text-emerald-600 dark:text-emerald-400">
|
||||
<span class="material-icons text-[10px] mr-1">check</span> Stable
|
||||
</span>
|
||||
</div>
|
||||
<h3 class="text-slate-500 dark:text-slate-400 text-sm font-medium">Error Rate</h3>
|
||||
<div class="mt-1 text-3xl font-bold text-slate-900 dark:text-white">0.02%</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="block bg-white dark:bg-surface-dark border border-slate-200 dark:border-slate-800 rounded-2xl p-5 shadow-sm hover:shadow-md hover:border-primary/40 transition-all group" href="#">
|
||||
<div class="flex flex-col lg:flex-row items-center justify-between gap-4">
|
||||
<div class="flex items-center space-x-4 w-full lg:w-auto">
|
||||
<div class="w-10 h-10 rounded-lg bg-rose-500/10 flex items-center justify-center flex-shrink-0 text-rose-500">
|
||||
<span class="material-icons">notifications_active</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-slate-900 dark:text-white font-bold text-sm">Warnings & Alerts</h3>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">2 active events require attention</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:flex-1 lg:mx-8 grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="flex items-center space-x-3 bg-slate-50 dark:bg-slate-800/50 rounded-lg px-3 py-2 border border-slate-100 dark:border-slate-700/50">
|
||||
<div class="w-1.5 h-1.5 rounded-full bg-rose-500 flex-shrink-0 shadow-[0_0_8px_rgba(244,63,94,0.6)] animate-pulse"></div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-xs font-medium text-slate-800 dark:text-slate-200 truncate">API Key quota exceeded for <span class="text-rose-500 dark:text-rose-400 font-mono">Enterprise_Client_A</span></p>
|
||||
</div>
|
||||
<span class="text-[10px] text-slate-400 whitespace-nowrap">42m ago</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3 bg-slate-50 dark:bg-slate-800/50 rounded-lg px-3 py-2 border border-slate-100 dark:border-slate-700/50">
|
||||
<div class="w-1.5 h-1.5 rounded-full bg-emerald-500 flex-shrink-0 shadow-[0_0_8px_rgba(16,185,129,0.6)]"></div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-xs font-medium text-slate-800 dark:text-slate-200 truncate">Database backup completed successfully</p>
|
||||
</div>
|
||||
<span class="text-[10px] text-slate-400 whitespace-nowrap">2h ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden lg:flex items-center text-slate-400 group-hover:text-primary transition-colors">
|
||||
<span class="material-icons">arrow_forward</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div class="lg:col-span-2 bg-white dark:bg-surface-dark border border-slate-200 dark:border-slate-800 rounded-2xl p-6 shadow-sm">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-slate-900 dark:text-white">Traffic Volume</h3>
|
||||
<p class="text-sm text-slate-500 dark:text-slate-400">Inbound requests over the last 24 hours</p>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-2xl font-bold text-slate-900 dark:text-white">1.2M</div>
|
||||
<div class="text-xs font-medium text-emerald-500 flex items-center justify-end">
|
||||
<span class="material-icons text-[14px] mr-0.5">arrow_upward</span> 8.9%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-[350px] w-full relative">
|
||||
<canvas id="trafficChart"></canvas>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-between text-xs text-slate-400 dark:text-slate-500 px-2 font-mono">
|
||||
<span>00:00</span>
|
||||
<span>04:00</span>
|
||||
<span>08:00</span>
|
||||
<span>12:00</span>
|
||||
<span>16:00</span>
|
||||
<span>20:00</span>
|
||||
<span>23:59</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-6 flex flex-col h-full">
|
||||
<div class="bg-white dark:bg-surface-dark border border-slate-200 dark:border-slate-800 rounded-2xl p-6 shadow-sm flex-1">
|
||||
<h3 class="text-lg font-bold text-slate-900 dark:text-white mb-6">Top Models</h3>
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1.5">
|
||||
<span class="text-slate-700 dark:text-slate-300 font-medium">GPT-4-Turbo</span>
|
||||
<span class="text-slate-500 dark:text-slate-400">45%</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-100 dark:bg-slate-700/50 rounded-full h-2">
|
||||
<div class="bg-blue-500 h-2 rounded-full" style="width: 45%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1.5">
|
||||
<span class="text-slate-700 dark:text-slate-300 font-medium">Claude 3.5 Sonnet</span>
|
||||
<span class="text-slate-500 dark:text-slate-400">32%</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-100 dark:bg-slate-700/50 rounded-full h-2">
|
||||
<div class="bg-indigo-500 h-2 rounded-full" style="width: 32%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1.5">
|
||||
<span class="text-slate-700 dark:text-slate-300 font-medium">Llama 3 70B</span>
|
||||
<span class="text-slate-500 dark:text-slate-400">18%</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-100 dark:bg-slate-700/50 rounded-full h-2">
|
||||
<div class="bg-violet-500 h-2 rounded-full" style="width: 18%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1.5">
|
||||
<span class="text-slate-700 dark:text-slate-300 font-medium">Mistral Large</span>
|
||||
<span class="text-slate-500 dark:text-slate-400">5%</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-100 dark:bg-slate-700/50 rounded-full h-2">
|
||||
<div class="bg-cyan-500 h-2 rounded-full" style="width: 5%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-sm mb-1.5">
|
||||
<span class="text-slate-700 dark:text-slate-300 font-medium">Gemini Pro</span>
|
||||
<span class="text-slate-500 dark:text-slate-400">3%</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-100 dark:bg-slate-700/50 rounded-full h-2">
|
||||
<div class="bg-teal-500 h-2 rounded-full" style="width: 3%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const ctx = document.getElementById('trafficChart').getContext('2d');
|
||||
// Check for dark mode to adjust chart colors
|
||||
const isDarkMode = document.documentElement.classList.contains('dark') || true; // Defaulting true for preview
|
||||
const gridColor = isDarkMode ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.05)';
|
||||
const textColor = isDarkMode ? '#94a3b8' : '#64748b';
|
||||
// Data Generation
|
||||
const labels = ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'];
|
||||
// Generating somewhat realistic data patterns
|
||||
const dataGPT4 = [45, 42, 35, 30, 45, 65, 85, 90, 88, 75, 60, 50];
|
||||
const dataClaude = [32, 30, 25, 20, 35, 50, 60, 65, 62, 55, 45, 38];
|
||||
const dataLlama = [18, 15, 12, 10, 20, 25, 30, 35, 32, 28, 22, 19];
|
||||
const dataMistral = [5, 4, 3, 2, 6, 8, 10, 12, 11, 9, 7, 6];
|
||||
const dataGemini = [3, 2, 2, 1, 4, 5, 7, 8, 7, 6, 5, 4];
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'GPT-4-Turbo',
|
||||
data: dataGPT4,
|
||||
backgroundColor: '#3b82f6', // blue-500
|
||||
borderRadius: 4,
|
||||
barPercentage: 0.6,
|
||||
},
|
||||
{
|
||||
label: 'Claude 3.5',
|
||||
data: dataClaude,
|
||||
backgroundColor: '#6366f1', // indigo-500
|
||||
borderRadius: 4,
|
||||
barPercentage: 0.6,
|
||||
},
|
||||
{
|
||||
label: 'Llama 3 70B',
|
||||
data: dataLlama,
|
||||
backgroundColor: '#8b5cf6', // violet-500
|
||||
borderRadius: 4,
|
||||
barPercentage: 0.6,
|
||||
},
|
||||
{
|
||||
label: 'Mistral Large',
|
||||
data: dataMistral,
|
||||
backgroundColor: '#06b6d4', // cyan-500
|
||||
borderRadius: 4,
|
||||
barPercentage: 0.6,
|
||||
},
|
||||
{
|
||||
label: 'Gemini Pro',
|
||||
data: dataGemini,
|
||||
backgroundColor: '#14b8a6', // teal-500
|
||||
borderRadius: 4,
|
||||
barPercentage: 0.6,
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'bottom', // Legend at bottom
|
||||
labels: {
|
||||
color: textColor,
|
||||
usePointStyle: true,
|
||||
pointStyle: 'circle',
|
||||
padding: 20,
|
||||
font: {
|
||||
family: "'Inter', sans-serif",
|
||||
size: 11
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
backgroundColor: isDarkMode ? '#1e293b' : '#ffffff',
|
||||
titleColor: isDarkMode ? '#f8fafc' : '#0f172a',
|
||||
bodyColor: isDarkMode ? '#cbd5e1' : '#475569',
|
||||
borderColor: isDarkMode ? '#334155' : '#e2e8f0',
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
cornerRadius: 8,
|
||||
titleFont: { family: "'Inter', sans-serif", weight: '600' },
|
||||
bodyFont: { family: "'Inter', sans-serif" }
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
grid: {
|
||||
display: false,
|
||||
drawBorder: false
|
||||
},
|
||||
ticks: {
|
||||
color: textColor,
|
||||
font: {
|
||||
family: "'Inter', sans-serif",
|
||||
size: 11
|
||||
}
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
grid: {
|
||||
color: gridColor,
|
||||
borderDash: [4, 4],
|
||||
drawBorder: false
|
||||
},
|
||||
ticks: {
|
||||
color: textColor,
|
||||
font: {
|
||||
family: "'Inter', sans-serif",
|
||||
size: 11
|
||||
},
|
||||
callback: function(value) {
|
||||
return value + 'k';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body></html>
|
||||
BIN
docs/feat/admin-panel-dashboard-feat/screen.png
Normal file
BIN
docs/feat/admin-panel-dashboard-feat/screen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 277 KiB |
Reference in New Issue
Block a user