feat(web): добавить поддержку быстрого доступа на дашборде и обновить настройки интерфейса
Docker / build (push) Failing after 19s
Docker / build (push) Failing after 19s
- Внедрён компонент QuickActionGrid для отображения часто используемых разделов на дашборде, управляемый настройкой showQuickActions. - Добавлен новый параметр showQuickActions в схему настроек для управления видимостью быстрого доступа. - Обновлён интерфейс настроек для включения переключателя отображения быстрого доступа. - Добавлен компонент SystemMonitorPopover в заголовок приложения для мониторинга системы. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -44,6 +44,7 @@ export type SettingsDto = Omit<
|
||||
| 'integrationEnabled'
|
||||
| 'customFields'
|
||||
| 'appSwitcherJson'
|
||||
| 'showQuickActions'
|
||||
> & {
|
||||
telegramBotTokenSet: boolean
|
||||
integrationTokenSet: boolean
|
||||
@@ -56,6 +57,7 @@ export type SettingsDto = Omit<
|
||||
notifyVpsDownEnabled: boolean
|
||||
webhookEnabled: boolean
|
||||
integrationEnabled: boolean
|
||||
showQuickActions: boolean
|
||||
notifyIntervalMinutes: number
|
||||
uptimeCheckIntervalMinutes: number
|
||||
customFields: unknown[]
|
||||
@@ -81,7 +83,8 @@ function toDto(row: Row | undefined): SettingsDto | undefined {
|
||||
customFields = []
|
||||
}
|
||||
}
|
||||
const { telegramBotToken, integrationToken, appSwitcherJson, ...rest } = row
|
||||
const { telegramBotToken, integrationToken, appSwitcherJson, showQuickActions: _showQa, ...rest } =
|
||||
row
|
||||
return {
|
||||
...rest,
|
||||
telegramBotTokenSet: Boolean(telegramBotToken?.trim()),
|
||||
@@ -95,6 +98,7 @@ function toDto(row: Row | undefined): SettingsDto | undefined {
|
||||
notifyVpsDownEnabled: Boolean(row.notifyVpsDownEnabled),
|
||||
webhookEnabled: Boolean(row.webhookEnabled),
|
||||
integrationEnabled: Boolean(row.integrationEnabled),
|
||||
showQuickActions: row.showQuickActions == null ? true : Boolean(row.showQuickActions),
|
||||
notifyIntervalMinutes: Number(row.notifyIntervalMinutes) || 60,
|
||||
uptimeCheckIntervalMinutes: Number(row.uptimeCheckIntervalMinutes) || 5,
|
||||
customFields: Array.isArray(customFields) ? customFields : [],
|
||||
@@ -135,6 +139,7 @@ interface SettingsInput {
|
||||
integrationEnabled?: boolean
|
||||
integrationLastSyncAt?: string
|
||||
cfdmApiUrl?: string
|
||||
showQuickActions?: boolean
|
||||
}
|
||||
|
||||
function buildValues(id: string, existing: Row | undefined, r: SettingsInput) {
|
||||
@@ -238,6 +243,16 @@ function buildValues(id: string, existing: Row | undefined, r: SettingsInput) {
|
||||
? r.integrationLastSyncAt || ''
|
||||
: existing?.integrationLastSyncAt ?? '',
|
||||
cfdmApiUrl: r.cfdmApiUrl !== undefined ? r.cfdmApiUrl || '' : existing?.cfdmApiUrl ?? '',
|
||||
showQuickActions:
|
||||
r.showQuickActions !== undefined
|
||||
? r.showQuickActions
|
||||
? 1
|
||||
: 0
|
||||
: existing?.showQuickActions == null
|
||||
? 1
|
||||
: existing.showQuickActions
|
||||
? 1
|
||||
: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ const COLUMN_MIGRATIONS: string[] = [
|
||||
`ALTER TABLE settings ADD COLUMN integrationEnabled INTEGER`,
|
||||
`ALTER TABLE settings ADD COLUMN integrationLastSyncAt TEXT`,
|
||||
`ALTER TABLE settings ADD COLUMN cfdmApiUrl TEXT`,
|
||||
`ALTER TABLE settings ADD COLUMN showQuickActions INTEGER`,
|
||||
`ALTER TABLE vps_domains ADD COLUMN targetIps TEXT`,
|
||||
]
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ export const settings = sqliteTable('settings', {
|
||||
integrationEnabled: integer('integrationEnabled'),
|
||||
integrationLastSyncAt: text('integrationLastSyncAt'),
|
||||
cfdmApiUrl: text('cfdmApiUrl'),
|
||||
showQuickActions: integer('showQuickActions'),
|
||||
})
|
||||
|
||||
export const vpsDomains = sqliteTable('vps_domains', {
|
||||
|
||||
@@ -27,6 +27,7 @@ export const settingsSchema = z.object({
|
||||
appSwitcher: appSwitcherConfigSchema.optional(),
|
||||
integrationToken: z.string().optional(),
|
||||
integrationEnabled: z.boolean().optional(),
|
||||
showQuickActions: z.boolean().optional(),
|
||||
})
|
||||
|
||||
export type Settings = z.infer<typeof settingsSchema>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
|
||||
|
||||
import { cn } from "@cfdm/ui/lib/utils"
|
||||
|
||||
function Progress({
|
||||
className,
|
||||
children,
|
||||
value,
|
||||
...props
|
||||
}: ProgressPrimitive.Root.Props) {
|
||||
return (
|
||||
<ProgressPrimitive.Root
|
||||
value={value}
|
||||
data-slot="progress"
|
||||
className={cn("flex flex-wrap gap-3", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ProgressTrack>
|
||||
<ProgressIndicator />
|
||||
</ProgressTrack>
|
||||
</ProgressPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
function ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {
|
||||
return (
|
||||
<ProgressPrimitive.Track
|
||||
className={cn(
|
||||
"relative flex h-1 w-full items-center overflow-x-hidden rounded-full bg-muted",
|
||||
className
|
||||
)}
|
||||
data-slot="progress-track"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ProgressIndicator({
|
||||
className,
|
||||
...props
|
||||
}: ProgressPrimitive.Indicator.Props) {
|
||||
return (
|
||||
<ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className={cn("h-full bg-primary transition-all", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
|
||||
return (
|
||||
<ProgressPrimitive.Label
|
||||
className={cn("text-sm font-medium", className)}
|
||||
data-slot="progress-label"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
|
||||
return (
|
||||
<ProgressPrimitive.Value
|
||||
className={cn(
|
||||
"ml-auto text-sm text-muted-foreground tabular-nums",
|
||||
className
|
||||
)}
|
||||
data-slot="progress-value"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Progress,
|
||||
ProgressTrack,
|
||||
ProgressIndicator,
|
||||
ProgressLabel,
|
||||
ProgressValue,
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
|
||||
|
||||
import { cn } from "@cfdm/ui/lib/utils"
|
||||
|
||||
function Switch({
|
||||
className,
|
||||
size = "default",
|
||||
...props
|
||||
}: SwitchPrimitive.Root.Props & {
|
||||
size?: "sm" | "default"
|
||||
}) {
|
||||
return (
|
||||
<SwitchPrimitive.Root
|
||||
data-slot="switch"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
|
||||
/>
|
||||
</SwitchPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Switch }
|
||||
Reference in New Issue
Block a user