mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 15:08:02 +08:00
fix ops dashboard mobile overflow
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<header class="glass sticky top-0 z-30 border-b border-gray-200/50 dark:border-dark-700/50">
|
||||
<div class="flex h-16 items-center justify-between px-4 md:px-6">
|
||||
<div class="flex h-16 items-center justify-between gap-2 px-2 sm:px-4 md:px-6">
|
||||
<!-- Left: Mobile Menu Toggle + Page Title -->
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex shrink-0 items-center gap-2 sm:gap-4">
|
||||
<button
|
||||
@click="toggleMobileSidebar"
|
||||
class="btn-ghost btn-icon lg:hidden"
|
||||
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Right: Announcements + Docs + Language + Subscriptions + Balance + User Dropdown -->
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex min-w-0 items-center gap-1 sm:gap-3">
|
||||
<!-- Announcement Bell -->
|
||||
<AnnouncementBell v-if="user" />
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
:href="docUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-dark-400 dark:hover:bg-dark-800 dark:hover:text-white"
|
||||
class="hidden items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-dark-400 dark:hover:bg-dark-800 dark:hover:text-white sm:flex"
|
||||
>
|
||||
<Icon name="book" size="sm" />
|
||||
<span class="hidden sm:inline">{{ t('nav.docs') }}</span>
|
||||
|
||||
@@ -173,22 +173,28 @@ function downloadChart() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col rounded-3xl bg-white p-6 shadow-sm ring-1 ring-gray-900/5 dark:bg-dark-800 dark:ring-dark-700">
|
||||
<div class="mb-4 flex shrink-0 items-center justify-between">
|
||||
<h3 class="flex items-center gap-2 text-sm font-bold text-gray-900 dark:text-white">
|
||||
<div class="flex h-full min-w-0 flex-col rounded-3xl bg-white p-6 shadow-sm ring-1 ring-gray-900/5 dark:bg-dark-800 dark:ring-dark-700">
|
||||
<div
|
||||
data-testid="throughput-chart-header"
|
||||
class="mb-4 flex shrink-0 flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"
|
||||
>
|
||||
<h3 class="flex min-w-0 items-center gap-2 text-sm font-bold text-gray-900 dark:text-white">
|
||||
<svg class="h-4 w-4 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
|
||||
</svg>
|
||||
{{ t('admin.ops.throughputTrend') }}
|
||||
<HelpTooltip v-if="!props.fullscreen" :content="t('admin.ops.tooltips.throughputTrend')" />
|
||||
</h3>
|
||||
<div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
<span class="flex items-center gap-1"><span class="h-2 w-2 rounded-full bg-blue-500"></span>QPS</span>
|
||||
<span class="flex items-center gap-1"><span class="h-2 w-2 rounded-full bg-green-500"></span>{{ t('admin.ops.tpsK') }}</span>
|
||||
<div
|
||||
data-testid="throughput-chart-toolbar"
|
||||
class="flex w-full min-w-0 flex-wrap items-center gap-2 text-xs text-gray-500 dark:text-gray-400 sm:w-auto sm:justify-end"
|
||||
>
|
||||
<span class="flex shrink-0 items-center gap-1"><span class="h-2 w-2 rounded-full bg-blue-500"></span>QPS</span>
|
||||
<span class="flex shrink-0 items-center gap-1"><span class="h-2 w-2 rounded-full bg-green-500"></span>{{ t('admin.ops.tpsK') }}</span>
|
||||
<template v-if="!props.fullscreen">
|
||||
<button
|
||||
type="button"
|
||||
class="ml-2 inline-flex items-center rounded-lg border border-gray-200 bg-white px-2 py-1 text-[11px] font-semibold text-gray-600 hover:bg-gray-50 disabled:opacity-50 dark:border-dark-700 dark:bg-dark-900 dark:text-gray-300 dark:hover:bg-dark-800"
|
||||
class="inline-flex shrink-0 items-center rounded-lg border border-gray-200 bg-white px-2 py-1 text-[11px] font-semibold text-gray-600 hover:bg-gray-50 disabled:opacity-50 dark:border-dark-700 dark:bg-dark-900 dark:text-gray-300 dark:hover:bg-dark-800"
|
||||
:disabled="state !== 'ready'"
|
||||
:title="t('admin.ops.requestDetails.title')"
|
||||
@click="emit('openDetails')"
|
||||
@@ -197,7 +203,7 @@ function downloadChart() {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="ml-2 inline-flex items-center rounded-lg border border-gray-200 bg-white px-2 py-1 text-[11px] font-semibold text-gray-600 hover:bg-gray-50 disabled:opacity-50 dark:border-dark-700 dark:bg-dark-900 dark:text-gray-300 dark:hover:bg-dark-800"
|
||||
class="inline-flex shrink-0 items-center rounded-lg border border-gray-200 bg-white px-2 py-1 text-[11px] font-semibold text-gray-600 hover:bg-gray-50 disabled:opacity-50 dark:border-dark-700 dark:bg-dark-900 dark:text-gray-300 dark:hover:bg-dark-800"
|
||||
:disabled="state !== 'ready'"
|
||||
:title="t('admin.ops.charts.resetZoomHint')"
|
||||
@click="resetZoom"
|
||||
@@ -206,7 +212,7 @@ function downloadChart() {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-2 py-1 text-[11px] font-semibold text-gray-600 hover:bg-gray-50 disabled:opacity-50 dark:border-dark-700 dark:bg-dark-900 dark:text-gray-300 dark:hover:bg-dark-800"
|
||||
class="inline-flex shrink-0 items-center rounded-lg border border-gray-200 bg-white px-2 py-1 text-[11px] font-semibold text-gray-600 hover:bg-gray-50 disabled:opacity-50 dark:border-dark-700 dark:bg-dark-900 dark:text-gray-300 dark:hover:bg-dark-800"
|
||||
:disabled="state !== 'ready'"
|
||||
:title="t('admin.ops.charts.downloadChartHint')"
|
||||
@click="downloadChart"
|
||||
@@ -244,7 +250,7 @@ function downloadChart() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="min-h-0 flex-1">
|
||||
<div class="min-h-0 min-w-0 flex-1">
|
||||
<Line v-if="state === 'ready' && chartData" ref="throughputChartRef" :data="chartData" :options="options" />
|
||||
<div v-else class="flex h-full items-center justify-center">
|
||||
<div v-if="state === 'loading'" class="animate-pulse text-sm text-gray-400">{{ t('common.loading') }}</div>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
|
||||
import OpsThroughputTrendChart from '../OpsThroughputTrendChart.vue'
|
||||
|
||||
vi.mock('vue-i18n', async () => {
|
||||
const actual = await vi.importActual<typeof import('vue-i18n')>('vue-i18n')
|
||||
return {
|
||||
...actual,
|
||||
useI18n: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('vue-chartjs', () => ({
|
||||
Line: {
|
||||
props: ['data', 'options'],
|
||||
template: '<div class="line-chart" />',
|
||||
},
|
||||
}))
|
||||
|
||||
describe('OpsThroughputTrendChart', () => {
|
||||
it('allows the header controls to wrap on narrow screens', () => {
|
||||
const wrapper = mount(OpsThroughputTrendChart, {
|
||||
props: {
|
||||
points: [],
|
||||
loading: false,
|
||||
timeRange: '1h',
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
EmptyState: true,
|
||||
HelpTooltip: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const header = wrapper.get('[data-testid="throughput-chart-header"]')
|
||||
expect(header.classes()).toEqual(expect.arrayContaining(['flex-col', 'sm:flex-row']))
|
||||
|
||||
const toolbar = wrapper.get('[data-testid="throughput-chart-toolbar"]')
|
||||
expect(toolbar.classes()).toEqual(expect.arrayContaining(['w-full', 'flex-wrap', 'sm:w-auto']))
|
||||
expect(toolbar.findAll('button')).toHaveLength(3)
|
||||
toolbar.findAll('button').forEach((button) => {
|
||||
expect(button.classes()).toContain('shrink-0')
|
||||
expect(button.classes()).not.toContain('ml-2')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user