Skip to content

Commit

Permalink
Merge pull request #14 from amphineko/re-web-improve
Browse files Browse the repository at this point in the history
Refactoring web frontend and migrating to SWR
  • Loading branch information
amphineko authored Apr 13, 2024
2 parents 6c462d9 + b6d6af1 commit ff65e38
Show file tree
Hide file tree
Showing 19 changed files with 993 additions and 893 deletions.
56 changes: 17 additions & 39 deletions packages/web/app/clientLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import {
AccountBox,
BugReport,
Error,
LinkOff,
Lock,
Notes,
Password,
PowerSettingsNew,
Refresh,
StopCircle,
SvgIconComponent,
Traffic,
WifiPassword,
Expand All @@ -31,14 +31,11 @@ import CssBaseline from "@mui/material/CssBaseline"
import { ThemeProvider, createTheme } from "@mui/material/styles"
import Link from "next/link"
import { usePathname, useRouter } from "next/navigation"
import { FC, JSX, PropsWithChildren, ReactNode, useCallback, useEffect, useMemo, useState } from "react"
import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from "react-query"
import { FC, JSX, PropsWithChildren, ReactNode, useEffect, useMemo, useState } from "react"

import { getStatus, reloadRadiusd, restartRadiusd } from "./actions"
import { useRadiusdStatus, useReloadRadiusd, useRestartRadiusd } from "./queries"
import { NotificationList, NotificationProvider } from "../lib/notifications"

const queryClient = new QueryClient()

const humanizer = new Intl.RelativeTimeFormat("en", { numeric: "always", style: "short" })

function humanize(seconds: number) {
Expand All @@ -54,29 +51,15 @@ function humanize(seconds: number) {
}

function RadiusdMenu(): JSX.Element {
const queryClient = useQueryClient()
const onSuccess = useCallback(async () => {
await queryClient.invalidateQueries(["index", "radiusd", "status"])
}, [queryClient])

const { mutate: mutateReload } = useMutation({
mutationFn: reloadRadiusd,
mutationKey: ["index", "radiusd", "reload"],
onSuccess,
})

const { mutate: mutateRestart } = useMutation({
mutationFn: restartRadiusd,
mutationKey: ["index", "radiusd", "restart"],
onSuccess,
})
const { trigger: reload } = useReloadRadiusd()
const { trigger: restart } = useRestartRadiusd()

return (
<Box>
<IconButton
color="inherit"
onClick={() => {
mutateReload()
void reload()
}}
>
<Tooltip title="Reload">
Expand All @@ -87,7 +70,7 @@ function RadiusdMenu(): JSX.Element {
<IconButton
color="inherit"
onClick={() => {
mutateRestart()
void restart()
}}
>
<Tooltip title="Restart">
Expand All @@ -99,16 +82,12 @@ function RadiusdMenu(): JSX.Element {
}

function StatusChip(): JSX.Element {
const { data } = useQuery({
queryFn: async () => await getStatus(),
queryKey: ["index", "radiusd", "status"],
refetchInterval: 60 * 1000,
})
const { data: status } = useRadiusdStatus()

const isRunning = data?.lastExitCode === undefined
const isRunning = status?.lastExitCode === undefined

const [now, setNow] = useState(() => Date.now())
const uptime = Math.floor(((data?.lastRestartedAt?.getTime() ?? now) - now) / 1000)
const uptime = Math.floor(((status?.lastRestartedAt?.getTime() ?? now) - now) / 1000)

useEffect(() => {
const interval = setInterval(() => {
Expand All @@ -120,16 +99,16 @@ function StatusChip(): JSX.Element {
}
}, [])

return isRunning ? (
<Tooltip title={`Radiusd is up since ${data?.lastRestartedAt?.toLocaleString()}`}>
<Chip color="success" icon={<Traffic />} label={humanize(uptime)} variant="outlined"></Chip>
return status === undefined ? (
<Tooltip title={`Cannot fetch server status`}>
<Chip color="error" icon={<LinkOff />} label="Unknown" variant="outlined" />
</Tooltip>
) : data.lastExitCode === 0 ? (
<Tooltip title={`Exited with code ${data.lastExitCode}`}>
<Chip color="warning" icon={<StopCircle />} label="Stopped" variant="outlined" />
) : isRunning ? (
<Tooltip title={`Radiusd is up since ${status.lastRestartedAt?.toLocaleString()}`}>
<Chip color="success" icon={<Traffic />} label={humanize(uptime)} variant="outlined"></Chip>
</Tooltip>
) : (
<Tooltip title={`Exited with code ${data.lastExitCode}`}>
<Tooltip title={`Exited with code ${status.lastExitCode}`}>
<Chip color="error" icon={<Error />} label="Failed" variant="outlined" />
</Tooltip>
)
Expand Down Expand Up @@ -225,7 +204,6 @@ export function RootClientLayout({ children }: { children: React.ReactNode }): J
<ProviderReducer
providers={[
({ children }) => <NotificationProvider>{children}</NotificationProvider>,
({ children }) => <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>,
({ children }) => <ThemeProvider theme={darkTheme}>{children}</ThemeProvider>,
]}
>
Expand Down
8 changes: 4 additions & 4 deletions packages/web/app/clients/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ import * as t from "io-ts"

import { deleteEndpoint, getTypedEndpoint, postTypedEndpoint } from "../../lib/actions"

export async function bulkCreateOrUpdate(clients: readonly Client[]): Promise<void> {
export async function bulkCreateOrUpdateClient(clients: readonly Client[]): Promise<void> {
await postTypedEndpoint(t.any, BulkCreateOrUpdateClientsRequestType, "api/v1/clients", clients)
}

export async function createOrUpdateByName(name: string, client: Client): Promise<void> {
export async function createOrUpdateClientByName(name: string, client: Client): Promise<void> {
await postTypedEndpoint(t.any, CreateOrUpdateClientRequestType, `api/v1/clients/${name}`, client)
}

export async function deleteByName(name: string): Promise<void> {
export async function deleteClientByName(name: string): Promise<void> {
await deleteEndpoint(`api/v1/clients/${name}`)
}

export async function getAllClients(): Promise<readonly Client[]> {
export async function listClients(): Promise<readonly Client[]> {
return await getTypedEndpoint(ListClientsResponseType, "api/v1/clients")
}
Loading

0 comments on commit ff65e38

Please sign in to comment.