feat(rules): tab style for rules page #163 (#187)

Co-authored-by: nb5p <nb5p@users.noreply.github.com>
Co-authored-by: kunish <kunish.butt@gmail.com>
This commit is contained in:
nb5p 2023-09-09 17:16:18 +08:00 committed by GitHub
parent 8fa51e5639
commit a9aa8e540e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 3908 additions and 1731 deletions

File diff suppressed because it is too large Load Diff

3866
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@ export default {
autoSwitchTheme: 'Automatically switch theme', autoSwitchTheme: 'Automatically switch theme',
favDayTheme: 'Favorite light theme', favDayTheme: 'Favorite light theme',
favNightTheme: 'Favorite dark theme', favNightTheme: 'Favorite dark theme',
renderInTwoColumns: 'Render proxies in two columns', renderInTwoColumns: 'Render in two columns',
updateGEODatabases: 'Update GEO Databases', updateGEODatabases: 'Update GEO Databases',
restartCore: 'Restart Core', restartCore: 'Restart Core',
upgradeCore: 'Upgrade Core', upgradeCore: 'Upgrade Core',
@ -60,8 +60,6 @@ export default {
ms: 'ms', ms: 'ms',
updated: 'Updated', updated: 'Updated',
renderProxiesInSamePage: 'Render proxies and proxy provider in same page', renderProxiesInSamePage: 'Render proxies and proxy provider in same page',
renderRulesAndProviderInTwoColumns:
'Render rules and rule provider in two columns',
tableSize: 'Table size', tableSize: 'Table size',
xs: 'Extra small size', xs: 'Extra small size',
sm: 'Small size', sm: 'Small size',

View File

@ -47,7 +47,7 @@ export default {
autoSwitchTheme: '自动切换主题', autoSwitchTheme: '自动切换主题',
favDayTheme: '浅色主题偏好', favDayTheme: '浅色主题偏好',
favNightTheme: '深色主题偏好', favNightTheme: '深色主题偏好',
renderInTwoColumns: '节点双列渲染', renderInTwoColumns: '双列渲染',
updateGEODatabases: '更新 GEO 数据库文件', updateGEODatabases: '更新 GEO 数据库文件',
restartCore: '重启核心', restartCore: '重启核心',
upgradeCore: '更新核心', upgradeCore: '更新核心',
@ -60,7 +60,6 @@ export default {
ms: '毫秒', ms: '毫秒',
updated: '更新于', updated: '更新于',
renderProxiesInSamePage: '将代理和代理提供者显示在同一页', renderProxiesInSamePage: '将代理和代理提供者显示在同一页',
renderRulesAndProviderInTwoColumns: '规则和规则提供者双列渲染',
tableSize: '表格大小', tableSize: '表格大小',
xs: '超小尺寸', xs: '超小尺寸',
sm: '小尺寸', sm: '小尺寸',

View File

@ -30,7 +30,6 @@ import {
proxiesOrderingType, proxiesOrderingType,
proxiesPreviewType, proxiesPreviewType,
renderInTwoColumns, renderInTwoColumns,
renderRulesAndProviderInTwoColumns,
setAutoCloseConns, setAutoCloseConns,
setAutoSwitchTheme, setAutoSwitchTheme,
setFavDayTheme, setFavDayTheme,
@ -39,7 +38,6 @@ import {
setProxiesOrderingType, setProxiesOrderingType,
setProxiesPreviewType, setProxiesPreviewType,
setRenderInTwoColumns, setRenderInTwoColumns,
setRenderRulesAndProviderInTwoColumns,
setSelectedEndpoint, setSelectedEndpoint,
setTableSize, setTableSize,
setTwemoji, setTwemoji,
@ -319,11 +317,6 @@ const ConfigForXd = () => {
value: renderInTwoColumns, value: renderInTwoColumns,
onChange: setRenderInTwoColumns, onChange: setRenderInTwoColumns,
}, },
{
label: 'renderRulesAndProviderInTwoColumns',
value: renderRulesAndProviderInTwoColumns,
onChange: setRenderRulesAndProviderInTwoColumns,
},
{ {
label: 'autoSwitchTheme', label: 'autoSwitchTheme',
value: autoSwitchTheme, value: autoSwitchTheme,

View File

@ -3,15 +3,20 @@ import { IconReload } from '@tabler/icons-solidjs'
import InfiniteScroll from 'solid-infinite-scroll' import InfiniteScroll from 'solid-infinite-scroll'
import { For, Show, createMemo, createSignal, onMount } from 'solid-js' import { For, Show, createMemo, createSignal, onMount } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { Button } from '~/components' import { Button, ForTwoColumns } from '~/components'
import { formatTimeFromNow, useStringBooleanMap } from '~/helpers' import { formatTimeFromNow, useStringBooleanMap } from '~/helpers'
import { renderRulesAndProviderInTwoColumns, useRules } from '~/signals' import { useRules } from '~/signals'
enum ActiveTab {
ruleProviders = 'ruleProviders',
rules = 'rules',
}
export default () => { export default () => {
const [t] = useI18n() const [t] = useI18n()
const { const {
rules, rules,
rulesProviders, ruleProviders,
updateRules, updateRules,
updateAllRuleProvider, updateAllRuleProvider,
updateRuleProviderByName, updateRuleProviderByName,
@ -39,43 +44,45 @@ export default () => {
setAllProviderIsUpdating(false) setAllProviderIsUpdating(false)
} }
const [activeTab, setActiveTab] = createSignal(ActiveTab.rules)
const tabs = () => [
{
type: ActiveTab.rules,
name: t('rules'),
count: rules().length,
},
{
type: ActiveTab.ruleProviders,
name: t('ruleProviders'),
count: ruleProviders().length,
},
]
return ( return (
<div <div class="flex h-full flex-col gap-2">
class={twMerge( <Show when={ruleProviders().length > 0}>
'flex w-full flex-col gap-4',
renderRulesAndProviderInTwoColumns() && 'flex-row',
)}
>
<div class="flex-1">
<h1 class="pb-4 text-lg font-semibold">{t('rules')}</h1>
<InfiniteScroll
each={renderRules()}
hasMore={renderRules().length < rules().length}
next={() => setMaxRuleRender(maxRuleRender() + 100)}
>
{(rule) => (
<div class="card card-bordered card-compact mb-2 bg-base-200 p-4">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="break-all">{rule.payload}</span> <div class="tabs-boxed tabs gap-2">
<Show when={typeof rule.size === 'number' && rule.size !== -1}> <For each={tabs()}>
<div class="badge badge-sm">{rule.size}</div> {(tab) => (
</Show> <button
</div> class={twMerge(
<div class="text-xs text-slate-500"> activeTab() === tab.type && 'tab-active',
{rule.type} :: {rule.proxy} 'tab gap-2 px-2',
</div>
</div>
)} )}
</InfiniteScroll> onClick={() => setActiveTab(tab.type)}
>
<span>{tab.name}</span>
<div class="badge badge-sm">{tab.count}</div>
</button>
)}
</For>
</div> </div>
<Show when={rulesProviders().length > 0}> <Show when={activeTab() === ActiveTab.ruleProviders}>
<div class="flex-1">
<h1 class="flex items-center gap-2 pb-4 text-lg font-semibold">
{t('ruleProviders')}
<Button <Button
class="btn-circle btn-sm" class="btn btn-circle btn-sm"
disabled={allProviderIsUpdating()} disabled={allProviderIsUpdating()}
onClick={(e) => onUpdateAllProviderClick(e)} onClick={(e) => onUpdateAllProviderClick(e)}
> >
@ -85,38 +92,66 @@ export default () => {
)} )}
/> />
</Button> </Button>
</h1> </Show>
</div>
</Show>
<For each={rulesProviders()}> <div class="flex-1 overflow-y-auto">
{(rulesProvider) => ( <Show when={activeTab() === ActiveTab.ruleProviders}>
<ForTwoColumns
subChild={ruleProviders().map((ruleProvider) => {
return (
<div class="card card-bordered card-compact mb-2 bg-base-200 p-4"> <div class="card card-bordered card-compact mb-2 bg-base-200 p-4">
<div class="flex items-center gap-2 pr-8"> <div class="flex items-center gap-2 pr-8">
<span class="break-all">{rulesProvider.name}</span> <span class="break-all">{ruleProvider.name}</span>
<div class="badge badge-sm">{rulesProvider.ruleCount}</div> <div class="badge badge-sm">{ruleProvider.ruleCount}</div>
</div> </div>
<div class="text-xs text-slate-500"> <div class="text-xs text-slate-500">
{rulesProvider.vehicleType} / {rulesProvider.behavior} / {ruleProvider.vehicleType} / {ruleProvider.behavior} /
{t('updated')} {formatTimeFromNow(rulesProvider.updatedAt)} {t('updated')} {formatTimeFromNow(ruleProvider.updatedAt)}
</div> </div>
<Button <Button
class="btn-circle btn-sm absolute right-2 top-2 mr-2 h-4" class="btn-circle btn-sm absolute right-2 top-2 mr-2 h-4"
disabled={updatingMap()[rulesProvider.name]} disabled={updatingMap()[ruleProvider.name]}
onClick={(e) => onUpdateProviderClick(e, rulesProvider.name)} onClick={(e) => onUpdateProviderClick(e, ruleProvider.name)}
> >
<IconReload <IconReload
class={twMerge( class={twMerge(
updatingMap()[rulesProvider.name] && updatingMap()[ruleProvider.name] &&
'animate-spin text-success', 'animate-spin text-success',
)} )}
/> />
</Button> </Button>
</div> </div>
)} )
</For> })}
</div> />
</Show> </Show>
<Show when={activeTab() === ActiveTab.rules}>
<InfiniteScroll
each={renderRules()}
hasMore={renderRules().length < rules().length}
next={() => setMaxRuleRender(maxRuleRender() + 100)}
>
{(rule) => (
<div class="card card-bordered card-compact mb-2 bg-base-200 p-4">
<div class="flex items-center gap-2">
<span class="break-all">{rule.payload}</span>
<Show
when={typeof rule.size === 'number' && rule.size !== -1}
>
<div class="badge badge-sm">{rule.size}</div>
</Show>
</div>
<div class="text-xs text-slate-500">
{rule.type} :: {rule.proxy}
</div>
</div>
)}
</InfiniteScroll>
</Show>
</div>
</div> </div>
) )
} }

View File

@ -45,13 +45,6 @@ export const [renderInTwoColumns, setRenderInTwoColumns] = makePersisted(
createSignal(true), createSignal(true),
{ name: 'renderInTwoColumn', storage: localStorage }, { name: 'renderInTwoColumn', storage: localStorage },
) )
export const [
renderRulesAndProviderInTwoColumns,
setRenderRulesAndProviderInTwoColumns,
] = makePersisted(createSignal(true), {
name: 'renderRulesAndProviderInTwoColumns',
storage: localStorage,
})
export const [tableSize, setTableSize] = makePersisted( export const [tableSize, setTableSize] = makePersisted(
createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS), createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS),
{ name: 'tableSize', storage: localStorage }, { name: 'tableSize', storage: localStorage },

View File

@ -5,7 +5,7 @@ import type { Rule, RuleProvider } from '~/types'
export const useRules = () => { export const useRules = () => {
const request = useRequest() const request = useRequest()
const [rules, setRules] = createSignal<Rule[]>([]) const [rules, setRules] = createSignal<Rule[]>([])
const [rulesProviders, setRulesProviders] = createSignal<RuleProvider[]>([]) const [ruleProviders, setRuleProviders] = createSignal<RuleProvider[]>([])
const updateRules = async () => { const updateRules = async () => {
const [{ rules }, { providers }] = await Promise.all([ const [{ rules }, { providers }] = await Promise.all([
@ -16,12 +16,12 @@ export const useRules = () => {
]) ])
setRules(Object.values(rules)) setRules(Object.values(rules))
setRulesProviders(Object.values(providers)) setRuleProviders(Object.values(providers))
} }
const updateAllRuleProvider = async () => { const updateAllRuleProvider = async () => {
await Promise.all( await Promise.all(
rulesProviders().map((provider) => { ruleProviders().map((provider) => {
return request.put(`providers/rules/${provider.name}`) return request.put(`providers/rules/${provider.name}`)
}), }),
) )
@ -35,7 +35,7 @@ export const useRules = () => {
return { return {
rules, rules,
rulesProviders, ruleProviders,
updateRules, updateRules,
updateAllRuleProvider, updateAllRuleProvider,
updateRuleProviderByName, updateRuleProviderByName,