diff --git a/new-ui/src/pages/full/LocationDetailsPage/LocationDetailsPage.tsx b/new-ui/src/pages/full/LocationDetailsPage/LocationDetailsPage.tsx new file mode 100644 index 00000000..e798604d --- /dev/null +++ b/new-ui/src/pages/full/LocationDetailsPage/LocationDetailsPage.tsx @@ -0,0 +1,70 @@ +import './style.scss'; + +import { useQuery } from '@tanstack/react-query'; +import { useNavigate, useSearch } from '@tanstack/react-router'; +import { Button } from '../../../shared/components/Button/Button'; +import { ButtonVariant } from '../../../shared/components/Button/types'; +import { Controls } from '../../../shared/components/Controls/Controls'; +import { DetailsFold } from '../../../shared/components/DetailsFold/DetailsFold'; +import { FullPageTitle } from '../../../shared/components/FullPageTitle/FullPageTitle'; +import { FullPage } from '../../../shared/layouts/FullPage/FullPage'; +import { getLocationDetailsQueryOptions } from '../../../shared/rust-api/query'; +import { ThemeSpacing } from '../../../shared/types'; + +const fallback = '–'; + +export const LocationDetailsPage = () => { + const { locationId, locationName, connectionType } = useSearch({ + from: '/full/_default/location-details', + }); + const navigate = useNavigate(); + + const { data } = useQuery( + getLocationDetailsQueryOptions({ locationId, connectionType }), + ); + + return ( + + + + + - )} */} - -
- - - {selection?.kind === 'instance' && ( - - )} -
diff --git a/new-ui/src/pages/full/OverviewPage/components/DetailsFold/DetailsFold.tsx b/new-ui/src/pages/full/OverviewPage/components/DetailsFold/DetailsFold.tsx deleted file mode 100644 index 1499300f..00000000 --- a/new-ui/src/pages/full/OverviewPage/components/DetailsFold/DetailsFold.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import './style.scss'; -import { Divider } from '../../../../../shared/components/Divider/Divider'; -import type { InstanceInfo } from '../../../../../shared/rust-api/types'; -import { ThemeSpacing } from '../../../../../shared/types'; - -interface Props { - data: InstanceInfo; -} - -export const DetailsFold = ({ data }: Props) => { - return ( -
-
-

Device configuration

-
-
-

Public key

-
- -
-

Addresses

-
- -
-

Listen port

-
-
-
-
-

VPN Server Configuration

-
-
-

Public key

-

{data.pubkey}

-
- -
-

Allowed IPs

-
- -
-

DNS servers

-
- -
-

Persistent keep alive

-
- -
-

Latest Handshake

-
-
-
-
- ); -}; diff --git a/new-ui/src/pages/full/UpdatePage/assets/banner_update_available.png b/new-ui/src/pages/full/UpdatePage/assets/banner_update_available.png index ce2031f2..ba0230f2 100644 Binary files a/new-ui/src/pages/full/UpdatePage/assets/banner_update_available.png and b/new-ui/src/pages/full/UpdatePage/assets/banner_update_available.png differ diff --git a/new-ui/src/routeTree.gen.ts b/new-ui/src/routeTree.gen.ts index 7bbcb7b1..60a60453 100644 --- a/new-ui/src/routeTree.gen.ts +++ b/new-ui/src/routeTree.gen.ts @@ -24,6 +24,7 @@ import { Route as FullDefaultSupportRouteImport } from './routes/full/_default/s import { Route as FullDefaultSettingsRouteImport } from './routes/full/_default/settings' import { Route as FullDefaultOverviewRouteImport } from './routes/full/_default/overview' import { Route as FullDefaultLogRouteImport } from './routes/full/_default/log' +import { Route as FullDefaultLocationDetailsRouteImport } from './routes/full/_default/location-details' import { Route as FullDefaultAddIndexRouteImport } from './routes/full/_default/add/index' import { Route as FullDefaultAddTunnelRouteImport } from './routes/full/_default/add/tunnel' import { Route as FullDefaultAddInstanceRouteImport } from './routes/full/_default/add/instance' @@ -102,6 +103,12 @@ const FullDefaultLogRoute = FullDefaultLogRouteImport.update({ path: '/log', getParentRoute: () => FullDefaultRoute, } as any) +const FullDefaultLocationDetailsRoute = + FullDefaultLocationDetailsRouteImport.update({ + id: '/location-details', + path: '/location-details', + getParentRoute: () => FullDefaultRoute, + } as any) const FullDefaultAddIndexRoute = FullDefaultAddIndexRouteImport.update({ id: '/add/', path: '/add/', @@ -128,6 +135,7 @@ export interface FileRoutesByFullPath { '/compact/': typeof CompactIndexRoute '/full/': typeof FullIndexRoute '/playground/': typeof PlaygroundIndexRoute + '/full/location-details': typeof FullDefaultLocationDetailsRoute '/full/log': typeof FullDefaultLogRoute '/full/overview': typeof FullDefaultOverviewRoute '/full/settings': typeof FullDefaultSettingsRoute @@ -146,6 +154,7 @@ export interface FileRoutesByTo { '/full/tunnel-wizard': typeof FullTunnelWizardRoute '/compact': typeof CompactIndexRoute '/playground': typeof PlaygroundIndexRoute + '/full/location-details': typeof FullDefaultLocationDetailsRoute '/full/log': typeof FullDefaultLogRoute '/full/overview': typeof FullDefaultOverviewRoute '/full/settings': typeof FullDefaultSettingsRoute @@ -167,6 +176,7 @@ export interface FileRoutesById { '/compact/': typeof CompactIndexRoute '/full/': typeof FullIndexRoute '/playground/': typeof PlaygroundIndexRoute + '/full/_default/location-details': typeof FullDefaultLocationDetailsRoute '/full/_default/log': typeof FullDefaultLogRoute '/full/_default/overview': typeof FullDefaultOverviewRoute '/full/_default/settings': typeof FullDefaultSettingsRoute @@ -188,6 +198,7 @@ export interface FileRouteTypes { | '/compact/' | '/full/' | '/playground/' + | '/full/location-details' | '/full/log' | '/full/overview' | '/full/settings' @@ -206,6 +217,7 @@ export interface FileRouteTypes { | '/full/tunnel-wizard' | '/compact' | '/playground' + | '/full/location-details' | '/full/log' | '/full/overview' | '/full/settings' @@ -226,6 +238,7 @@ export interface FileRouteTypes { | '/compact/' | '/full/' | '/playground/' + | '/full/_default/location-details' | '/full/_default/log' | '/full/_default/overview' | '/full/_default/settings' @@ -351,6 +364,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof FullDefaultLogRouteImport parentRoute: typeof FullDefaultRoute } + '/full/_default/location-details': { + id: '/full/_default/location-details' + path: '/location-details' + fullPath: '/full/location-details' + preLoaderRoute: typeof FullDefaultLocationDetailsRouteImport + parentRoute: typeof FullDefaultRoute + } '/full/_default/add/': { id: '/full/_default/add/' path: '/add' @@ -376,6 +396,7 @@ declare module '@tanstack/react-router' { } interface FullDefaultRouteChildren { + FullDefaultLocationDetailsRoute: typeof FullDefaultLocationDetailsRoute FullDefaultLogRoute: typeof FullDefaultLogRoute FullDefaultOverviewRoute: typeof FullDefaultOverviewRoute FullDefaultSettingsRoute: typeof FullDefaultSettingsRoute @@ -387,6 +408,7 @@ interface FullDefaultRouteChildren { } const FullDefaultRouteChildren: FullDefaultRouteChildren = { + FullDefaultLocationDetailsRoute: FullDefaultLocationDetailsRoute, FullDefaultLogRoute: FullDefaultLogRoute, FullDefaultOverviewRoute: FullDefaultOverviewRoute, FullDefaultSettingsRoute: FullDefaultSettingsRoute, diff --git a/new-ui/src/routes/full/_default/location-details.tsx b/new-ui/src/routes/full/_default/location-details.tsx new file mode 100644 index 00000000..fa53ae72 --- /dev/null +++ b/new-ui/src/routes/full/_default/location-details.tsx @@ -0,0 +1,14 @@ +import { createFileRoute } from '@tanstack/react-router'; +import { z } from 'zod'; +import { LocationDetailsPage } from '../../../pages/full/LocationDetailsPage/LocationDetailsPage'; + +const searchSchema = z.object({ + locationId: z.number(), + locationName: z.string(), + connectionType: z.enum(['Location', 'Tunnel']), +}); + +export const Route = createFileRoute('/full/_default/location-details')({ + validateSearch: searchSchema, + component: LocationDetailsPage, +}); diff --git a/new-ui/src/shared/components/DetailsFold/DetailsFold.tsx b/new-ui/src/shared/components/DetailsFold/DetailsFold.tsx new file mode 100644 index 00000000..6ac9b07c --- /dev/null +++ b/new-ui/src/shared/components/DetailsFold/DetailsFold.tsx @@ -0,0 +1,43 @@ +import './style.scss'; + +import clsx from 'clsx'; +import { Fragment, type ReactNode } from 'react'; +import { Divider } from '../Divider/Divider'; + +export type DetailsFoldRow = { + label: string; + value: ReactNode; +}; + +export type DetailsFoldSection = { + title: string; + rows: DetailsFoldRow[]; + compact?: boolean; +}; + +type Props = { + sections: DetailsFoldSection[]; +}; + +export const DetailsFold = ({ sections }: Props) => { + return ( +
+ {sections.map((section) => ( +
+

{section.title}

+
+ {section.rows.map((row, index) => ( + + {index > 0 && } +
+

{row.label}

+

{row.value}

+
+
+ ))} +
+
+ ))} +
+ ); +}; diff --git a/new-ui/src/pages/full/OverviewPage/components/DetailsFold/style.scss b/new-ui/src/shared/components/DetailsFold/style.scss similarity index 68% rename from new-ui/src/pages/full/OverviewPage/components/DetailsFold/style.scss rename to new-ui/src/shared/components/DetailsFold/style.scss index 66066a76..4580799d 100644 --- a/new-ui/src/pages/full/OverviewPage/components/DetailsFold/style.scss +++ b/new-ui/src/shared/components/DetailsFold/style.scss @@ -8,16 +8,29 @@ flex-flow: column; row-gap: var(--spacing-sm); + > p { + font: var(--t-body-sm-500); + color: var(--fg-white-70); + user-select: none; + } + > .card { box-sizing: border-box; + display: flex; + flex-flow: column; + row-gap: var(--spacing-lg); border-radius: 12px; border: 1px solid var(--border-disabled); padding: var(--spacing-md); - > .row { + &.compact { + row-gap: var(--spacing-md); + } + + .row { display: flex; flex-flow: row nowrap; - align-items: center; + align-items: flex-start; justify-content: space-between; column-gap: var(--spacing-2xl); @@ -26,6 +39,7 @@ font: var(--t-body-xs-400); color: var(--fg-white-80); text-align: left; + flex-shrink: 0; } :nth-child(2) { @@ -33,6 +47,8 @@ color: var(--fg-white-100); text-align: right; max-width: 365px; + min-width: 0; + overflow-wrap: anywhere; } } } diff --git a/new-ui/src/shared/components/LocationCard/components/LocationCardHeaderInfo/LocationCardHeaderInfo.tsx b/new-ui/src/shared/components/LocationCard/components/LocationCardHeaderInfo/LocationCardHeaderInfo.tsx index e60e0cc6..2b6cc9b5 100644 --- a/new-ui/src/shared/components/LocationCard/components/LocationCardHeaderInfo/LocationCardHeaderInfo.tsx +++ b/new-ui/src/shared/components/LocationCard/components/LocationCardHeaderInfo/LocationCardHeaderInfo.tsx @@ -1,12 +1,15 @@ import './style.scss'; import { ConnectionType, type LocationInfo } from '../../../../rust-api/types'; +import { ThemeVariable } from '../../../../types'; +import { Icon, IconKind } from '../../../Icon'; import { LocationCardIcon } from '../LocationCardIcon'; interface Props { location: LocationInfo; + onInfoClick?: () => void; } -export const LocationCardHeaderInfo = ({ location }: Props) => ( +export const LocationCardHeaderInfo = ({ location, onInfoClick }: Props) => (
@@ -15,6 +18,20 @@ export const LocationCardHeaderInfo = ({ location }: Props) => (

{location.name}

+ {onInfoClick && ( + + )} {location.active && (

Online

diff --git a/new-ui/src/shared/components/LocationCard/components/LocationCardHeaderInfo/style.scss b/new-ui/src/shared/components/LocationCard/components/LocationCardHeaderInfo/style.scss index 77d2601e..3bb5f3fb 100644 --- a/new-ui/src/shared/components/LocationCard/components/LocationCardHeaderInfo/style.scss +++ b/new-ui/src/shared/components/LocationCard/components/LocationCardHeaderInfo/style.scss @@ -24,7 +24,18 @@ flex-flow: row nowrap; align-items: center; justify-content: flex-start; - column-gap: var(--spacing-sm); + column-gap: var(--spacing-xs); + } + + .info-btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + margin: 0; + border: 0; + background: transparent; + cursor: pointer; } .online-badge { diff --git a/new-ui/src/shared/components/OverviewLocationCard/OverviewLocationCard.tsx b/new-ui/src/shared/components/OverviewLocationCard/OverviewLocationCard.tsx index 32c2a595..5e00ecdb 100644 --- a/new-ui/src/shared/components/OverviewLocationCard/OverviewLocationCard.tsx +++ b/new-ui/src/shared/components/OverviewLocationCard/OverviewLocationCard.tsx @@ -1,6 +1,7 @@ import './style.scss'; import { useMutation } from '@tanstack/react-query'; +import { useNavigate } from '@tanstack/react-router'; import clsx from 'clsx'; import { useMemo } from 'react'; import { ConnectModalView } from '../../../pages/full/OverviewPage/components/ConnectModal/hooks/types'; @@ -24,6 +25,7 @@ interface Props { } export const OverviewLocationCard = ({ location, instance }: Props) => { + const navigate = useNavigate(); const { mutate: updateRouting } = useMutation({ mutationFn: api.updateLocationRouting, meta: { @@ -103,7 +105,19 @@ export const OverviewLocationCard = ({ location, instance }: Props) => { return (
- + + navigate({ + to: '/full/location-details', + search: { + locationId: location.id, + locationName: location.name, + connectionType: location.connection_type, + }, + }) + } + />
diff --git a/new-ui/src/shared/components/OverviewLocationCard/style.scss b/new-ui/src/shared/components/OverviewLocationCard/style.scss index 226f4048..5bef355e 100644 --- a/new-ui/src/shared/components/OverviewLocationCard/style.scss +++ b/new-ui/src/shared/components/OverviewLocationCard/style.scss @@ -18,6 +18,9 @@ > .right { margin-left: auto; + display: flex; + align-items: center; + column-gap: var(--spacing-xs); } button {