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
-
-
-
-
VPN Server Configuration
-
-
-
Public key
-
{data.pubkey}
-
-
-
-
-
-
-
-
Persistent keep alive
-
-
-
-
-
-
- );
-};
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 {