fix(frontend): polish batch actions and sharing page guidance (#157)

* fix(frontend): polish batch actions and sharing page guidance

* fix(frontend): add top spacing above sharing section banners

* fix(frontend): allow manual and calendar expiry date input

* fix(frontend): switch batch action buttons to warning tone

* fix(frontend): switch batch action buttons to default tone
This commit is contained in:
sagit
2026-02-20 12:50:17 +08:00
committed by GitHub
parent 66de566a00
commit 5a1b72387d
7 changed files with 302 additions and 64 deletions
+1 -1
View File
@@ -1584,7 +1584,7 @@ export default function ForwardPage() {
</Button>
<Button
color={selectMode ? "warning" : "primary"}
color="default"
size="sm"
variant={selectMode ? "solid" : "flat"}
onPress={toggleSelectMode}
+2 -1
View File
@@ -645,9 +645,10 @@ export default function GroupPage() {
{(item) => <SelectItem key={item.id}>{item.name}</SelectItem>}
</Select>
<Button
className="md:self-end"
className="md:self-end md:justify-self-start whitespace-nowrap px-4"
color="primary"
isLoading={savingPermission}
size="sm"
onPress={handleAssignPermission}
>
分配权限
+1 -1
View File
@@ -1000,7 +1000,7 @@ export default function NodePage() {
<div className="flex gap-2 items-center">
<Button
color={selectMode ? "warning" : "primary"}
color="default"
size="sm"
variant={selectMode ? "solid" : "flat"}
onPress={toggleSelectMode}
+77 -12
View File
@@ -397,11 +397,30 @@ export default function PanelSharingPage() {
selectedKey={selectedTab}
onSelectionChange={(k) => setSelectedTab(k as string)}
>
<Tab key="my-shares" title="我分享的 (Provider)">
<Tab
key="my-shares"
title={
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-foreground">
Provider
</span>
<span className="text-xs text-default-500">我分享的</span>
</div>
}
>
<Card>
<CardBody>
<div className="mb-4">
<CardBody className="space-y-5 pt-4 md:pt-6">
<div className="flex flex-col gap-4 rounded-lg border border-divider bg-default-50/60 dark:bg-default-100/20 p-4 md:flex-row md:items-center md:justify-between">
<div className="space-y-1">
<h2 className="text-lg font-semibold text-foreground">
Provider 共享
</h2>
<p className="text-sm text-default-500">
将本地节点分享给其他面板,统一管理 Token、端口范围和到期策略。
</p>
</div>
<Button
className="self-start md:self-auto"
color="primary"
onPress={() => setCreateShareOpen(true)}
>
@@ -410,9 +429,25 @@ export default function PanelSharingPage() {
</div>
{loading ? (
<div className="text-center py-10 text-gray-500">加载中...</div>
<div className="text-center py-12 text-default-500">加载中...</div>
) : shares.length === 0 ? (
<div className="text-center py-10 text-gray-500">暂无分享</div>
<div className="rounded-lg border border-dashed border-divider bg-default-50/60 dark:bg-default-100/20 px-6 py-10 text-center">
<p className="text-base font-semibold text-foreground">
暂无分享
</p>
<p className="mt-2 text-sm text-default-500">
先创建一个分享,把本地节点开放给其他面板使用。
</p>
<div className="mt-5 flex justify-center">
<Button
color="primary"
variant="flat"
onPress={() => setCreateShareOpen(true)}
>
创建第一个分享
</Button>
</div>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{shares.map((share) => (
@@ -507,11 +542,30 @@ export default function PanelSharingPage() {
</CardBody>
</Card>
</Tab>
<Tab key="remote-nodes" title="远程节点 (Consumer)">
<Tab
key="remote-nodes"
title={
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-foreground">
Consumer
</span>
<span className="text-xs text-default-500">远程节点</span>
</div>
}
>
<Card>
<CardBody>
<div className="mb-4">
<CardBody className="space-y-5 pt-4 md:pt-6">
<div className="flex flex-col gap-4 rounded-lg border border-divider bg-default-50/60 dark:bg-default-100/20 p-4 md:flex-row md:items-center md:justify-between">
<div className="space-y-1">
<h2 className="text-lg font-semibold text-foreground">
Consumer 接入
</h2>
<p className="text-sm text-default-500">
导入远程节点后,可在这里查看端口占用和同步状态。
</p>
</div>
<Button
className="self-start md:self-auto"
color="secondary"
onPress={() => setImportNodeOpen(true)}
>
@@ -520,13 +574,24 @@ export default function PanelSharingPage() {
</div>
{remoteUsageLoading ? (
<div className="text-center py-10 text-gray-500">加载中...</div>
<div className="text-center py-12 text-default-500">加载中...</div>
) : remoteUsageNodes.length === 0 ? (
<div className="text-center py-10 text-gray-500">
<p>暂无远程节点占用记录。</p>
<p className="mt-2">
<div className="rounded-lg border border-dashed border-divider bg-default-50/60 dark:bg-default-100/20 px-6 py-10 text-center">
<p className="text-base font-semibold text-foreground">
暂无远程节点占用记录
</p>
<p className="mt-2 text-sm text-default-500">
导入远程节点并创建隧道后,这里会显示远端端口占用情况。
</p>
<div className="mt-5 flex justify-center">
<Button
color="secondary"
variant="flat"
onPress={() => setImportNodeOpen(true)}
>
去导入远程节点
</Button>
</div>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+1 -1
View File
@@ -678,7 +678,7 @@ export default function TunnelPage() {
<div className="flex items-center gap-2">
<Button
color={selectMode ? "warning" : "primary"}
color="default"
size="sm"
variant={selectMode ? "solid" : "flat"}
onPress={toggleSelectMode}
+16 -13
View File
@@ -943,7 +943,6 @@ export default function UserPage() {
<DatePicker
isRequired
showMonthAndYearPickers
className="cursor-pointer"
label="过期时间"
value={
userForm.expTime
@@ -1167,17 +1166,22 @@ export default function UserPage() {
)}
</div>
<Button
className="w-full sm:w-auto"
color="primary"
isDisabled={batchTunnelSelections.size === 0}
isLoading={assignLoading}
onPress={handleBatchAssignTunnel}
>
分配权限{" "}
{batchTunnelSelections.size > 0 &&
`(${batchTunnelSelections.size}个隧道)`}
</Button>
<div className="flex flex-wrap items-center gap-2">
<Button
className="w-full sm:w-auto"
color="primary"
isDisabled={batchTunnelSelections.size === 0}
isLoading={assignLoading}
onPress={handleBatchAssignTunnel}
>
分配权限
</Button>
{batchTunnelSelections.size > 0 && (
<Chip color="primary" size="sm" variant="flat">
已选 {batchTunnelSelections.size} 个隧道
</Chip>
)}
</div>
</div>
</div>
@@ -1434,7 +1438,6 @@ export default function UserPage() {
<DatePicker
isRequired
showMonthAndYearPickers
className="cursor-pointer"
label="到期时间"
value={
editTunnelForm.expTime