mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
feat(web): move version display to the config page header (#2273)
- remove version details from the sidebar footer - show the current app version as a badge in the config page header - add a reusable Badge UI component for the new version label
This commit is contained in:
@@ -11,12 +11,10 @@ import {
|
||||
IconSparkles,
|
||||
IconTools,
|
||||
} from "@tabler/icons-react"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { Link, useRouterState } from "@tanstack/react-router"
|
||||
import * as React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { getSystemVersionInfo } from "@/api/system"
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
@@ -25,7 +23,6 @@ import {
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarGroupLabel,
|
||||
@@ -84,13 +81,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
language: (i18n.resolvedLanguage ?? i18n.language ?? "").toLowerCase(),
|
||||
t,
|
||||
})
|
||||
const { data: versionInfo } = useQuery({
|
||||
queryKey: ["system", "version"],
|
||||
queryFn: getSystemVersionInfo,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
|
||||
const versionText = versionInfo?.version ?? t("footer.version_unknown")
|
||||
const handleNavItemClick = React.useCallback(() => {
|
||||
if (isMobile) {
|
||||
setOpenMobile(false)
|
||||
@@ -263,26 +254,6 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
</Collapsible>
|
||||
))}
|
||||
</SidebarContent>
|
||||
<SidebarFooter className="border-t-border/30 border-t px-3 py-2 group-data-[collapsible=icon]:hidden">
|
||||
<div className="text-muted-foreground flex flex-col gap-0.5 text-[11px] leading-4">
|
||||
<div className="truncate" title={versionText}>
|
||||
<span className="text-foreground/80">{t("footer.version")}:</span>{" "}
|
||||
{versionText}
|
||||
</div>
|
||||
{versionInfo?.git_commit && (
|
||||
<div className="truncate" title={versionInfo.git_commit}>
|
||||
<span className="text-foreground/80">{t("footer.commit")}:</span>{" "}
|
||||
{versionInfo.git_commit}
|
||||
</div>
|
||||
)}
|
||||
{versionInfo?.build_time && (
|
||||
<div className="truncate" title={versionInfo.build_time}>
|
||||
<span className="text-foreground/80">{t("footer.build")}:</span>{" "}
|
||||
{versionInfo.build_time}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SidebarFooter>
|
||||
<SidebarRail />
|
||||
</Sidebar>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IconCode, IconDeviceFloppy } from "@tabler/icons-react"
|
||||
import { IconCode, IconDeviceFloppy, IconTag } from "@tabler/icons-react"
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import { Link } from "@tanstack/react-router"
|
||||
import { useEffect, useState } from "react"
|
||||
@@ -10,6 +10,7 @@ import { launcherFetch } from "@/api/http"
|
||||
import {
|
||||
getAutoStartStatus,
|
||||
getLauncherConfig,
|
||||
getSystemVersionInfo,
|
||||
setAutoStartEnabled as updateAutoStartEnabled,
|
||||
setLauncherConfig as updateLauncherConfig,
|
||||
} from "@/api/system"
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
parseMultilineList,
|
||||
} from "@/components/config/form-model"
|
||||
import { PageHeader } from "@/components/page-header"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { refreshGatewayState } from "@/store/gateway"
|
||||
|
||||
@@ -64,6 +66,12 @@ export function ConfigPage() {
|
||||
queryFn: getLauncherConfig,
|
||||
})
|
||||
|
||||
const { data: versionInfo } = useQuery({
|
||||
queryKey: ["system", "version"],
|
||||
queryFn: getSystemVersionInfo,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
|
||||
const {
|
||||
data: autoStartStatus,
|
||||
isLoading: isAutoStartLoading,
|
||||
@@ -297,6 +305,17 @@ export function ConfigPage() {
|
||||
<div className="flex h-full flex-col">
|
||||
<PageHeader
|
||||
title={t("navigation.config")}
|
||||
titleExtra={
|
||||
versionInfo && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="gap-1 font-mono text-[11px] font-normal opacity-80"
|
||||
>
|
||||
<IconTag className="size-3 opacity-70" />
|
||||
{versionInfo.version}
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
children={
|
||||
<Button variant="outline" asChild>
|
||||
<Link to="/config/raw">
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { Slot } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
||||
destructive:
|
||||
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
||||
outline:
|
||||
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
||||
ghost:
|
||||
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Badge({
|
||||
className,
|
||||
variant = "default",
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"span"> &
|
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||
const Comp = asChild ? Slot.Root : "span"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="badge"
|
||||
data-variant={variant}
|
||||
className={cn(badgeVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
Reference in New Issue
Block a user