chore: format code
This commit is contained in:
@@ -17,7 +17,7 @@ import {
|
||||
TextField,
|
||||
} from "@heroui/react";
|
||||
import { ArrowLeft, Pencil, PlugConnection } from "@gravity-ui/icons";
|
||||
import { api, type ChargePointDetail, type PaginatedTransactions } from "@/lib/api";
|
||||
import { api } from "@/lib/api";
|
||||
import { useSession } from "@/lib/auth-client";
|
||||
|
||||
// ── Status maps ────────────────────────────────────────────────────────────
|
||||
@@ -116,8 +116,7 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id
|
||||
|
||||
const txQuery = useQuery({
|
||||
queryKey: ["chargePointTransactions", id, txPage],
|
||||
queryFn: () =>
|
||||
api.transactions.list({ page: txPage, limit: TX_LIMIT, chargePointId: id }),
|
||||
queryFn: () => api.transactions.list({ page: txPage, limit: TX_LIMIT, chargePointId: id }),
|
||||
refetchInterval: 3_000,
|
||||
});
|
||||
|
||||
@@ -240,104 +239,104 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{/* Device info — admin only */}
|
||||
{isAdmin && (
|
||||
<div className="rounded-xl border border-border bg-surface p-4">
|
||||
<h2 className="mb-3 text-sm font-semibold text-foreground">设备信息</h2>
|
||||
<dl className="divide-y divide-border">
|
||||
{[
|
||||
{ label: "品牌", value: cp.chargePointVendor },
|
||||
{ label: "型号", value: cp.chargePointModel },
|
||||
{ label: "序列号", value: cp.chargePointSerialNumber },
|
||||
{ label: "固件版本", value: cp.firmwareVersion },
|
||||
{ label: "电表型号", value: cp.meterType },
|
||||
{ label: "电表序列号", value: cp.meterSerialNumber },
|
||||
{ label: "ICCID", value: cp.iccid },
|
||||
{ label: "IMSI", value: cp.imsi },
|
||||
].map(({ label, value }) => (
|
||||
<div key={label} className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">{label}</dt>
|
||||
<dd className="truncate text-sm text-foreground">
|
||||
{value ?? <span className="text-muted">—</span>}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
<div className="rounded-xl border border-border bg-surface p-4">
|
||||
<h2 className="mb-3 text-sm font-semibold text-foreground">设备信息</h2>
|
||||
<dl className="divide-y divide-border">
|
||||
{[
|
||||
{ label: "品牌", value: cp.chargePointVendor },
|
||||
{ label: "型号", value: cp.chargePointModel },
|
||||
{ label: "序列号", value: cp.chargePointSerialNumber },
|
||||
{ label: "固件版本", value: cp.firmwareVersion },
|
||||
{ label: "电表型号", value: cp.meterType },
|
||||
{ label: "电表序列号", value: cp.meterSerialNumber },
|
||||
{ label: "ICCID", value: cp.iccid },
|
||||
{ label: "IMSI", value: cp.imsi },
|
||||
].map(({ label, value }) => (
|
||||
<div key={label} className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">{label}</dt>
|
||||
<dd className="truncate text-sm text-foreground">
|
||||
{value ?? <span className="text-muted">—</span>}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Operation info — admin only */}
|
||||
{isAdmin && (
|
||||
<div className="rounded-xl border border-border bg-surface p-4">
|
||||
<h2 className="mb-3 text-sm font-semibold text-foreground">运行配置</h2>
|
||||
<dl className="divide-y divide-border">
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">注册状态</dt>
|
||||
<dd>
|
||||
<Chip
|
||||
color={registrationColorMap[cp.registrationStatus] ?? "warning"}
|
||||
size="sm"
|
||||
variant="soft"
|
||||
>
|
||||
{cp.registrationStatus}
|
||||
</Chip>
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">电价</dt>
|
||||
<dd className="text-sm text-foreground">
|
||||
{cp.feePerKwh > 0 ? (
|
||||
<span>
|
||||
{cp.feePerKwh} 分/kWh
|
||||
<span className="ml-1 text-xs text-muted">
|
||||
(¥{(cp.feePerKwh / 100).toFixed(2)}/kWh)
|
||||
<div className="rounded-xl border border-border bg-surface p-4">
|
||||
<h2 className="mb-3 text-sm font-semibold text-foreground">运行配置</h2>
|
||||
<dl className="divide-y divide-border">
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">注册状态</dt>
|
||||
<dd>
|
||||
<Chip
|
||||
color={registrationColorMap[cp.registrationStatus] ?? "warning"}
|
||||
size="sm"
|
||||
variant="soft"
|
||||
>
|
||||
{cp.registrationStatus}
|
||||
</Chip>
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">电价</dt>
|
||||
<dd className="text-sm text-foreground">
|
||||
{cp.feePerKwh > 0 ? (
|
||||
<span>
|
||||
{cp.feePerKwh} 分/kWh
|
||||
<span className="ml-1 text-xs text-muted">
|
||||
(¥{(cp.feePerKwh / 100).toFixed(2)}/kWh)
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
"免费"
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">心跳间隔</dt>
|
||||
<dd className="text-sm text-foreground">
|
||||
{cp.heartbeatInterval != null ? (
|
||||
`${cp.heartbeatInterval} 秒`
|
||||
) : (
|
||||
<span className="text-muted">—</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">最后心跳</dt>
|
||||
<dd className="text-right text-sm text-foreground">
|
||||
{cp.lastHeartbeatAt ? (
|
||||
<span title={new Date(cp.lastHeartbeatAt).toLocaleString("zh-CN")}>
|
||||
{relativeTime(cp.lastHeartbeatAt)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted">—</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">最后启动通知</dt>
|
||||
<dd className="text-right text-sm text-foreground">
|
||||
{cp.lastBootNotificationAt ? (
|
||||
<span title={new Date(cp.lastBootNotificationAt).toLocaleString("zh-CN")}>
|
||||
{relativeTime(cp.lastBootNotificationAt)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted">—</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">注册时间</dt>
|
||||
<dd className="text-sm text-foreground">
|
||||
{new Date(cp.createdAt).toLocaleDateString("zh-CN")}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
) : (
|
||||
"免费"
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">心跳间隔</dt>
|
||||
<dd className="text-sm text-foreground">
|
||||
{cp.heartbeatInterval != null ? (
|
||||
`${cp.heartbeatInterval} 秒`
|
||||
) : (
|
||||
<span className="text-muted">—</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">最后心跳</dt>
|
||||
<dd className="text-right text-sm text-foreground">
|
||||
{cp.lastHeartbeatAt ? (
|
||||
<span title={new Date(cp.lastHeartbeatAt).toLocaleString("zh-CN")}>
|
||||
{relativeTime(cp.lastHeartbeatAt)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted">—</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">最后启动通知</dt>
|
||||
<dd className="text-right text-sm text-foreground">
|
||||
{cp.lastBootNotificationAt ? (
|
||||
<span title={new Date(cp.lastBootNotificationAt).toLocaleString("zh-CN")}>
|
||||
{relativeTime(cp.lastBootNotificationAt)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted">—</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<dt className="shrink-0 text-sm text-muted">注册时间</dt>
|
||||
<dd className="text-sm text-foreground">
|
||||
{new Date(cp.createdAt).toLocaleDateString("zh-CN")}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Fee info — user only */}
|
||||
@@ -362,7 +361,9 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id
|
||||
<dt className="shrink-0 text-sm text-muted">充电桥状态</dt>
|
||||
<dd>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className={`size-2 rounded-full ${isOnline ? "bg-success animate-pulse" : "bg-muted"}`} />
|
||||
<span
|
||||
className={`size-2 rounded-full ${isOnline ? "bg-success animate-pulse" : "bg-muted"}`}
|
||||
/>
|
||||
<span className="text-sm text-foreground">{isOnline ? "在线" : "离线"}</span>
|
||||
</div>
|
||||
</dd>
|
||||
|
||||
@@ -337,7 +337,12 @@ export default function IdTagsPage() {
|
||||
const [deletingTag, setDeletingTag] = useState<string | null>(null);
|
||||
const [claiming, setClaiming] = useState(false);
|
||||
|
||||
const { data: idTagsData, isPending: loading, isFetching: refreshing, refetch } = useQuery({
|
||||
const {
|
||||
data: idTagsData,
|
||||
isPending: loading,
|
||||
isFetching: refreshing,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["idTags"],
|
||||
queryFn: async () => {
|
||||
const [tagList, userList] = await Promise.all([
|
||||
@@ -435,38 +440,38 @@ export default function IdTagsPage() {
|
||||
<ArrowRotateRight className={`size-4 ${refreshing ? "animate-spin" : ""}`} />
|
||||
</Button>
|
||||
<Modal>
|
||||
<Button size="sm" variant="secondary" onPress={openCreate}>
|
||||
<Plus className="size-4" />
|
||||
新增储值卡
|
||||
</Button>
|
||||
<Modal.Backdrop>
|
||||
<Modal.Container scroll="outside">
|
||||
<Modal.Dialog className="sm:max-w-105">
|
||||
<Modal.CloseTrigger />
|
||||
<Modal.Header>
|
||||
<Modal.Heading>新增储值卡</Modal.Heading>
|
||||
</Modal.Header>
|
||||
<Modal.Body className="space-y-3">
|
||||
<TagFormBody
|
||||
form={form}
|
||||
setForm={setForm}
|
||||
isEdit={false}
|
||||
users={users}
|
||||
tags={tags}
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer className="flex justify-end gap-2">
|
||||
<Button slot="close" variant="ghost">
|
||||
取消
|
||||
</Button>
|
||||
<Button isDisabled={saving} slot="close" onPress={handleSave}>
|
||||
{saving ? <Spinner size="sm" /> : "创建"}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal.Dialog>
|
||||
</Modal.Container>
|
||||
</Modal.Backdrop>
|
||||
</Modal>
|
||||
<Button size="sm" variant="secondary" onPress={openCreate}>
|
||||
<Plus className="size-4" />
|
||||
新增储值卡
|
||||
</Button>
|
||||
<Modal.Backdrop>
|
||||
<Modal.Container scroll="outside">
|
||||
<Modal.Dialog className="sm:max-w-105">
|
||||
<Modal.CloseTrigger />
|
||||
<Modal.Header>
|
||||
<Modal.Heading>新增储值卡</Modal.Heading>
|
||||
</Modal.Header>
|
||||
<Modal.Body className="space-y-3">
|
||||
<TagFormBody
|
||||
form={form}
|
||||
setForm={setForm}
|
||||
isEdit={false}
|
||||
users={users}
|
||||
tags={tags}
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer className="flex justify-end gap-2">
|
||||
<Button slot="close" variant="ghost">
|
||||
取消
|
||||
</Button>
|
||||
<Button isDisabled={saving} slot="close" onPress={handleSave}>
|
||||
{saving ? <Spinner size="sm" /> : "创建"}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal.Dialog>
|
||||
</Modal.Container>
|
||||
</Modal.Backdrop>
|
||||
</Modal>
|
||||
</div>
|
||||
) : (
|
||||
<Button size="sm" variant="secondary" isDisabled={claiming} onPress={handleClaim}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReactNode } from 'react'
|
||||
import Sidebar from '@/components/sidebar'
|
||||
import { ReactQueryProvider } from '@/components/query-provider'
|
||||
import { ReactNode } from "react";
|
||||
import Sidebar from "@/components/sidebar";
|
||||
import { ReactQueryProvider } from "@/components/query-provider";
|
||||
|
||||
export default function DashboardLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
@@ -11,12 +11,10 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<main className="flex-1 overflow-y-auto pt-14 lg:pt-0">
|
||||
<div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
|
||||
<ReactQueryProvider>
|
||||
{children}
|
||||
</ReactQueryProvider>
|
||||
<ReactQueryProvider>{children}</ReactQueryProvider>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,9 +118,7 @@ function RecentTransactions({ txns }: { txns: Transaction[] }) {
|
||||
<span
|
||||
className={`flex size-7 shrink-0 items-center justify-center rounded-full ${active ? "bg-warning-soft" : "bg-success/10"}`}
|
||||
>
|
||||
<Thunderbolt
|
||||
className={`size-3.5 ${active ? "text-warning" : "text-success"}`}
|
||||
/>
|
||||
<Thunderbolt className={`size-3.5 ${active ? "text-warning" : "text-success"}`} />
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium text-foreground">
|
||||
@@ -174,9 +172,7 @@ function ChargePointStatus({ cps }: { cps: ChargePoint[] }) {
|
||||
{chargingCount > 0 && (
|
||||
<p className="text-xs font-medium text-warning">{chargingCount} 充电中</p>
|
||||
)}
|
||||
<p className="text-xs text-muted">
|
||||
{online ? `${availableCount} 可用` : "离线"}
|
||||
</p>
|
||||
<p className="text-xs text-muted">{online ? `${availableCount} 可用` : "离线"}</p>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
@@ -283,9 +279,7 @@ export default function DashboardPage() {
|
||||
footer={
|
||||
<>
|
||||
<StatusDot color="success" />
|
||||
<span className="font-medium text-success">
|
||||
{s?.onlineChargePoints ?? 0} 在线
|
||||
</span>
|
||||
<span className="font-medium text-success">{s?.onlineChargePoints ?? 0} 在线</span>
|
||||
<span className="text-border">·</span>
|
||||
<span>{offlineCount} 离线</span>
|
||||
</>
|
||||
@@ -383,4 +377,3 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -223,27 +223,35 @@ export default function SettingsPage() {
|
||||
placeholder="对外显示的名称"
|
||||
value={profileName}
|
||||
onChange={(e) => setProfileName(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === "Enter") void handleSaveProfile(); }}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") void handleSaveProfile();
|
||||
}}
|
||||
/>
|
||||
</TextField>
|
||||
{profileError && (
|
||||
<Alert status="danger">
|
||||
<Alert.Indicator />
|
||||
<Alert.Content><Alert.Description>{profileError}</Alert.Description></Alert.Content>
|
||||
<Alert.Content>
|
||||
<Alert.Description>{profileError}</Alert.Description>
|
||||
</Alert.Content>
|
||||
<CloseButton onPress={() => setProfileError("")} />
|
||||
</Alert>
|
||||
)}
|
||||
{profileSuccess && (
|
||||
<Alert status="success">
|
||||
<Alert.Indicator />
|
||||
<Alert.Content><Alert.Description>{profileSuccess}</Alert.Description></Alert.Content>
|
||||
<Alert.Content>
|
||||
<Alert.Description>{profileSuccess}</Alert.Description>
|
||||
</Alert.Content>
|
||||
<CloseButton onPress={() => setProfileSuccess("")} />
|
||||
</Alert>
|
||||
)}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
size="sm"
|
||||
isDisabled={savingProfile || !profileName.trim() || profileName === session?.user.name}
|
||||
isDisabled={
|
||||
savingProfile || !profileName.trim() || profileName === session?.user.name
|
||||
}
|
||||
onPress={handleSaveProfile}
|
||||
>
|
||||
{savingProfile ? <Spinner size="sm" color="current" /> : "保存"}
|
||||
@@ -292,20 +300,26 @@ export default function SettingsPage() {
|
||||
placeholder="再次输入新密码"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === "Enter") void handleChangePassword(); }}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") void handleChangePassword();
|
||||
}}
|
||||
/>
|
||||
</TextField>
|
||||
{pwError && (
|
||||
<Alert status="danger">
|
||||
<Alert.Indicator />
|
||||
<Alert.Content><Alert.Description>{pwError}</Alert.Description></Alert.Content>
|
||||
<Alert.Content>
|
||||
<Alert.Description>{pwError}</Alert.Description>
|
||||
</Alert.Content>
|
||||
<CloseButton onPress={() => setPwError("")} />
|
||||
</Alert>
|
||||
)}
|
||||
{pwSuccess && (
|
||||
<Alert status="success">
|
||||
<Alert.Indicator />
|
||||
<Alert.Content><Alert.Description>{pwSuccess}</Alert.Description></Alert.Content>
|
||||
<Alert.Content>
|
||||
<Alert.Description>{pwSuccess}</Alert.Description>
|
||||
</Alert.Content>
|
||||
<CloseButton onPress={() => setPwSuccess("")} />
|
||||
</Alert>
|
||||
)}
|
||||
@@ -369,7 +383,9 @@ export default function SettingsPage() {
|
||||
<div className="border-b border-border px-5 py-3">
|
||||
<Alert status="danger">
|
||||
<Alert.Indicator />
|
||||
<Alert.Content><Alert.Description>{error}</Alert.Description></Alert.Content>
|
||||
<Alert.Content>
|
||||
<Alert.Description>{error}</Alert.Description>
|
||||
</Alert.Content>
|
||||
<CloseButton onPress={() => setError("")} />
|
||||
</Alert>
|
||||
</div>
|
||||
@@ -378,7 +394,9 @@ export default function SettingsPage() {
|
||||
<div className="border-b border-border px-5 py-3">
|
||||
<Alert status="success">
|
||||
<Alert.Indicator />
|
||||
<Alert.Content><Alert.Description>{success}</Alert.Description></Alert.Content>
|
||||
<Alert.Content>
|
||||
<Alert.Description>{success}</Alert.Description>
|
||||
</Alert.Content>
|
||||
<CloseButton onPress={() => setSuccess("")} />
|
||||
</Alert>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Button, Chip, Modal, Pagination, Spinner, Table } from "@heroui/react";
|
||||
import { TrashBin } from "@gravity-ui/icons";
|
||||
import { api, type PaginatedTransactions } from "@/lib/api";
|
||||
import { api } from "@/lib/api";
|
||||
import { useSession } from "@/lib/auth-client";
|
||||
|
||||
const LIMIT = 15;
|
||||
@@ -27,7 +27,11 @@ export default function TransactionsPage() {
|
||||
const [stoppingId, setStoppingId] = useState<number | null>(null);
|
||||
const [deletingId, setDeletingId] = useState<number | null>(null);
|
||||
|
||||
const { data, isPending: loading, refetch } = useQuery({
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["transactions", page, status],
|
||||
queryFn: () =>
|
||||
api.transactions.list({
|
||||
@@ -197,53 +201,54 @@ export default function TransactionsPage() {
|
||||
</Modal>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<Modal>
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
variant="tertiary"
|
||||
isDisabled={deletingId === tx.id}
|
||||
>
|
||||
{deletingId === tx.id ? (
|
||||
<Spinner size="sm" />
|
||||
) : (
|
||||
<TrashBin className="size-4" />
|
||||
)}
|
||||
</Button>
|
||||
<Modal.Backdrop>
|
||||
<Modal.Container scroll="outside">
|
||||
<Modal.Dialog className="sm:max-w-96">
|
||||
<Modal.CloseTrigger />
|
||||
<Modal.Header>
|
||||
<Modal.Heading>确认删除记录</Modal.Heading>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<p className="text-sm text-muted">
|
||||
将永久删除充电记录{" "}
|
||||
<span className="font-mono font-medium text-foreground">
|
||||
#{tx.id}
|
||||
</span>
|
||||
(储值卡:<span className="font-mono">{tx.idTag}</span>)。
|
||||
{!tx.stopTimestamp && "该记录仍进行中,删除同时将重置接口状态。"}
|
||||
</p>
|
||||
</Modal.Body>
|
||||
<Modal.Footer className="flex justify-end gap-2">
|
||||
<Button slot="close" variant="ghost">
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
slot="close"
|
||||
variant="danger"
|
||||
isDisabled={deletingId === tx.id}
|
||||
onPress={() => handleDelete(tx.id)}
|
||||
>
|
||||
{deletingId === tx.id ? <Spinner size="sm" /> : "确认删除"}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal.Dialog>
|
||||
</Modal.Container>
|
||||
</Modal.Backdrop>
|
||||
</Modal>
|
||||
<Modal>
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
variant="tertiary"
|
||||
isDisabled={deletingId === tx.id}
|
||||
>
|
||||
{deletingId === tx.id ? (
|
||||
<Spinner size="sm" />
|
||||
) : (
|
||||
<TrashBin className="size-4" />
|
||||
)}
|
||||
</Button>
|
||||
<Modal.Backdrop>
|
||||
<Modal.Container scroll="outside">
|
||||
<Modal.Dialog className="sm:max-w-96">
|
||||
<Modal.CloseTrigger />
|
||||
<Modal.Header>
|
||||
<Modal.Heading>确认删除记录</Modal.Heading>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<p className="text-sm text-muted">
|
||||
将永久删除充电记录{" "}
|
||||
<span className="font-mono font-medium text-foreground">
|
||||
#{tx.id}
|
||||
</span>
|
||||
(储值卡:<span className="font-mono">{tx.idTag}</span>)。
|
||||
{!tx.stopTimestamp &&
|
||||
"该记录仍进行中,删除同时将重置接口状态。"}
|
||||
</p>
|
||||
</Modal.Body>
|
||||
<Modal.Footer className="flex justify-end gap-2">
|
||||
<Button slot="close" variant="ghost">
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
slot="close"
|
||||
variant="danger"
|
||||
isDisabled={deletingId === tx.id}
|
||||
onPress={() => handleDelete(tx.id)}
|
||||
>
|
||||
{deletingId === tx.id ? <Spinner size="sm" /> : "确认删除"}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal.Dialog>
|
||||
</Modal.Container>
|
||||
</Modal.Backdrop>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
</Table.Cell>
|
||||
|
||||
@@ -5,12 +5,12 @@ import "./globals.css";
|
||||
const fontSaira = Saira({
|
||||
variable: "--font-saira",
|
||||
subsets: ["latin"],
|
||||
})
|
||||
});
|
||||
|
||||
const fontNotoSans = Noto_Sans({
|
||||
variable: "--font-noto-sans",
|
||||
subsets: ["latin"],
|
||||
})
|
||||
});
|
||||
|
||||
const fontMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
@@ -19,7 +19,8 @@ const fontMono = Geist_Mono({
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Helios EVCS",
|
||||
description: "A modern EV charging station management system built with Next.js, Drizzle ORM, and OCPP.",
|
||||
description:
|
||||
"A modern EV charging station management system built with Next.js, Drizzle ORM, and OCPP.",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
||||
@@ -141,7 +141,7 @@ export default function LoginPage() {
|
||||
</Card.Content>
|
||||
</Card>
|
||||
|
||||
<p className="mt-6 text-xs text-muted/60">OCPP 1.6-J Protocol • v0.1.0</p>
|
||||
<p className="mt-6 text-xs text-muted/60">Helios EVCS</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { redirect } from 'next/navigation'
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Home() {
|
||||
redirect('/dashboard')
|
||||
redirect("/dashboard");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user