CN provider accounts (Kimi/Zhipu/DeepSeek) and the monitor quota view each
hand-rolled their own usage bar, so the same account column rendered three
different label styles, bar widths and threshold palettes. Both now reuse
UsageProgressBar, the component the other five platforms already share.
Also move the default utilization thresholds from >=100 red / >=80 amber to
>=90 / >=75. Turning red only once the quota is exhausted is an after-the-fact
notice rather than a warning. The remainingCapacity mode keeps its 20/50 pair,
which is already a warning semantic.
- UsageProgressBar: add labelWidth 'fixed' | 'auto' so the monitor view can
render composite labels ("Pro/7d") without breaking column alignment; the
fixed branch is byte-identical, so the existing platforms are untouched
- drop the superseded utilizationColor/utilizationTextColor/formatReset copies
- shorten CN window labels to 5h / 7d and remove the now-dead resetSoon keys
Common Components
This directory contains reusable Vue 3 components built with Composition API, TypeScript, and TailwindCSS.
Components
DataTable.vue
A generic data table component with sorting, loading states, and custom cell rendering.
Props:
columns: Column[]- Array of column definitions with key, label, sortable, and formatterdata: any[]- Array of data objects to displayloading?: boolean- Show loading skeletondefaultSortKey?: string- Default sort key (only used if no persisted sort state)defaultSortOrder?: 'asc' | 'desc'- Default sort order (default:asc)sortStorageKey?: string- Persist sort state (key + order) to localStoragerowKey?: string | (row: any) => string | number- Row key field or resolver (defaults torow.id, falls back to index)
Slots:
empty- Custom empty state contentcell-{key}- Custom cell renderer for specific column (receivesrowandvalue)
Usage:
<DataTable
:columns="[
{ key: 'name', label: 'Name', sortable: true },
{ key: 'email', label: 'Email' },
{ key: 'status', label: 'Status', formatter: (val) => val.toUpperCase() }
]"
:data="users"
:loading="isLoading"
>
<template #cell-actions="{ row }">
<button @click="editUser(row)">Edit</button>
</template>
</DataTable>
Pagination.vue
Pagination component with page numbers, navigation, and page size selector.
Props:
total: number- Total number of itemspage: number- Current page (1-indexed)pageSize: number- Items per pagepageSizeOptions?: number[]- Available page size options (default: [10, 20, 50, 100])
Events:
update:page- Emitted when page changesupdate:pageSize- Emitted when page size changes
Usage:
<Pagination
:total="totalUsers"
:page="currentPage"
:pageSize="pageSize"
@update:page="currentPage = $event"
@update:pageSize="pageSize = $event"
/>
Modal.vue
Modal dialog with customizable size and close behavior.
Props:
show: boolean- Control modal visibilitytitle: string- Modal titlesize?: 'sm' | 'md' | 'lg' | 'xl' | 'full'- Modal size (default: 'md')closeOnEscape?: boolean- Close on Escape key (default: true)closeOnClickOutside?: boolean- Close on backdrop click (default: true)
Events:
close- Emitted when modal should close
Slots:
default- Modal body contentfooter- Modal footer content
Usage:
<Modal :show="showModal" title="Edit User" size="lg" @close="showModal = false">
<form @submit.prevent="saveUser">
<!-- Form content -->
</form>
<template #footer>
<button @click="showModal = false">Cancel</button>
<button @click="saveUser">Save</button>
</template>
</Modal>
ConfirmDialog.vue
Confirmation dialog built on top of Modal component.
Props:
show: boolean- Control dialog visibilitytitle: string- Dialog titlemessage: string- Confirmation messageconfirmText?: string- Confirm button text (default: 'Confirm')cancelText?: string- Cancel button text (default: 'Cancel')danger?: boolean- Use danger/red styling (default: false)
Events:
confirm- Emitted when user confirmscancel- Emitted when user cancels
Usage:
<ConfirmDialog
:show="showDeleteConfirm"
title="Delete User"
message="Are you sure you want to delete this user? This action cannot be undone."
confirm-text="Delete"
cancel-text="Cancel"
danger
@confirm="deleteUser"
@cancel="showDeleteConfirm = false"
/>
StatCard.vue
Statistics card component for displaying metrics with optional change indicators.
Props:
title: string- Card titlevalue: number | string- Main value to displayicon?: Component- Icon componentchange?: number- Percentage change valuechangeType?: 'up' | 'down' | 'neutral'- Change direction (default: 'neutral')formatValue?: (value) => string- Custom value formatter
Usage:
<StatCard title="Total Users" :value="1234" :icon="UserIcon" :change="12.5" change-type="up" />
Toast.vue
Toast notification component that automatically displays toasts from the app store.
Usage:
<!-- Add once in App.vue or layout -->
<Toast />
// Trigger toasts from anywhere using the app store
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
appStore.addToast({
type: 'success',
title: 'Success!',
message: 'User created successfully',
duration: 3000
})
appStore.addToast({
type: 'error',
message: 'Failed to delete user'
})
LoadingSpinner.vue
Simple animated loading spinner.
Props:
size?: 'sm' | 'md' | 'lg' | 'xl'- Spinner size (default: 'md')color?: 'primary' | 'secondary' | 'white' | 'gray'- Spinner color (default: 'primary')
Usage:
<LoadingSpinner size="lg" color="primary" />
EmptyState.vue
Empty state placeholder with icon, message, and optional action button.
Props:
icon?: Component- Icon componenttitle: string- Empty state titledescription: string- Empty state descriptionactionText?: string- Action button textactionTo?: string | object- Router link destinationactionIcon?: boolean- Show plus icon in button (default: true)
Slots:
icon- Custom icon contentaction- Custom action button/link
Usage:
<EmptyState
title="No users found"
description="Get started by creating your first user account."
action-text="Add User"
:action-to="{ name: 'users-create' }"
/>
Import
You can import components individually:
import { DataTable, Pagination, Modal } from '@/components/common'
Or import specific components:
import DataTable from '@/components/common/DataTable.vue'
Features
All components include:
- TypeScript support with proper type definitions
- Accessibility with ARIA attributes and keyboard navigation
- Responsive design with mobile-friendly layouts
- TailwindCSS styling for consistent design
- Vue 3 Composition API with
<script setup> - Slot support for customization