Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/network-devices-ui' into network…
Browse files Browse the repository at this point in the history
…-devices
  • Loading branch information
t-aleksander committed Dec 20, 2024
2 parents 1d56ae3 + c2a4278 commit 301114f
Show file tree
Hide file tree
Showing 17 changed files with 377 additions and 28,866 deletions.
11 changes: 11 additions & 0 deletions web/src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ const en: BaseTranslation = {
},
key: 'Key',
name: 'Name',
noData: 'No data',
unavailable: 'Unavailable',
notSet: 'Not set',
},
messages: {
error: 'Error has occurred.',
Expand All @@ -51,6 +54,10 @@ const en: BaseTranslation = {
},
},
addStandaloneDevice: {
toasts: {
deviceCreated: 'Device added',
creationFailed: 'Device could not be added.',
},
infoBox: {
setup:
'Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now.',
Expand Down Expand Up @@ -1646,6 +1653,10 @@ const en: BaseTranslation = {
out: 'Out:',
gatewayDisconnected: 'Gateway disconnected',
},
cardsLabels: {
users: 'Connected Users',
devices: 'Connected Network Devices',
},
},
connectedUsersOverview: {
pageTitle: 'Connected users',
Expand Down
64 changes: 64 additions & 0 deletions web/src/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ type RootTranslation = {
* N​a​m​e
*/
name: string
/**
* N​o​ ​d​a​t​a
*/
noData: string
/**
* U​n​a​v​a​i​l​a​b​l​e
*/
unavailable: string
/**
* N​o​t​ ​s​e​t
*/
notSet: string
}
messages: {
/**
Expand Down Expand Up @@ -164,6 +176,16 @@ type RootTranslation = {
}
}
addStandaloneDevice: {
toasts: {
/**
* D​e​v​i​c​e​ ​a​d​d​e​d
*/
deviceCreated: string
/**
* D​e​v​i​c​e​ ​c​o​u​l​d​ ​n​o​t​ ​b​e​ ​a​d​d​e​d​.
*/
creationFailed: string
}
infoBox: {
/**
* H​e​r​e​ ​y​o​u​ ​c​a​n​ ​a​d​d​ ​d​e​f​i​n​i​t​i​o​n​s​ ​o​r​ ​g​e​n​e​r​a​t​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​s​ ​f​o​r​ ​d​e​v​i​c​e​s​ ​t​h​a​t​ ​c​a​n​ ​c​o​n​n​e​c​t​ ​t​o​ ​y​o​u​r​ ​V​P​N​.​ ​O​n​l​y​ ​l​o​c​a​t​i​o​n​s​ ​w​i​t​h​o​u​t​ ​M​u​l​t​i​-​F​a​c​t​o​r​ ​A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​a​r​e​ ​a​v​a​i​l​a​b​l​e​ ​h​e​r​e​,​ ​a​s​ ​M​F​A​ ​i​s​ ​o​n​l​y​ ​s​u​p​p​o​r​t​e​d​ ​i​n​ ​D​e​f​g​u​a​r​d​ ​D​e​s​k​t​o​p​ ​C​l​i​e​n​t​ ​f​o​r​ ​n​o​w​.
Expand Down Expand Up @@ -3918,6 +3940,16 @@ type RootTranslation = {
*/
gatewayDisconnected: string
}
cardsLabels: {
/**
* C​o​n​n​e​c​t​e​d​ ​U​s​e​r​s
*/
users: string
/**
* C​o​n​n​e​c​t​e​d​ ​N​e​t​w​o​r​k​ ​D​e​v​i​c​e​s
*/
devices: string
}
}
connectedUsersOverview: {
/**
Expand Down Expand Up @@ -4858,6 +4890,18 @@ export type TranslationFunctions = {
* Name
*/
name: () => LocalizedString
/**
* No data
*/
noData: () => LocalizedString
/**
* Unavailable
*/
unavailable: () => LocalizedString
/**
* Not set
*/
notSet: () => LocalizedString
}
messages: {
/**
Expand Down Expand Up @@ -4913,6 +4957,16 @@ export type TranslationFunctions = {
}
}
addStandaloneDevice: {
toasts: {
/**
* Device added
*/
deviceCreated: () => LocalizedString
/**
* Device could not be added.
*/
creationFailed: () => LocalizedString
}
infoBox: {
/**
* Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now.
Expand Down Expand Up @@ -8633,6 +8687,16 @@ export type TranslationFunctions = {
*/
gatewayDisconnected: () => LocalizedString
}
cardsLabels: {
/**
* Connected Users
*/
users: () => LocalizedString
/**
* Connected Network Devices
*/
devices: () => LocalizedString
}
}
connectedUsersOverview: {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ interface Props {
}

export const OverviewConnectedUsers = ({ stats }: Props) => {
if (!stats || stats.length === 0) return null;
return (
<div className="connection-cards">
{stats && stats.length > 0 && (
<div className="connected-users grid">
{stats.map((userStats) => (
<UserConnectionCard key={userStats.user.username} data={userStats} />
))}
</div>
)}
<div className="connected-users grid">
{stats.map((userStats) => (
<UserConnectionCard key={userStats.user.username} data={userStats} />
))}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import './style.scss';

import classNames from 'classnames';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import { motion } from 'framer-motion';
import { sumBy } from 'lodash-es';
import { useCallback, useEffect, useMemo, useState } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import { timer } from 'rxjs';
Expand All @@ -20,13 +20,31 @@ import { NetworkSpeed } from '../../../../shared/defguard-ui/components/Layout/N
import { NetworkDirection } from '../../../../shared/defguard-ui/components/Layout/NetworkSpeed/types';
import { UserInitials } from '../../../../shared/defguard-ui/components/Layout/UserInitials/UserInitials';
import { getUserFullName } from '../../../../shared/helpers/getUserFullName';
import { NetworkDeviceStats, NetworkUserStats } from '../../../../shared/types';
import {
NetworkDeviceStats,
NetworkUserStats,
StandaloneDeviceStats,
} from '../../../../shared/types';
import { titleCase } from '../../../../shared/utils/titleCase';
import { summarizeDeviceStats, summarizeUsersNetworkStats } from '../../helpers/stats';
import {
summarizeDevicesStats,
summarizeDeviceStats,
summarizeUsersNetworkStats,
} from '../../helpers/stats';
import { NetworkUsageChart } from '../shared/components/NetworkUsageChart/NetworkUsageChart';
import { formatConnectionTime } from './formatConnectionTime';

dayjs.extend(utc);
type DeviceConnectionCardProps = {
data: StandaloneDeviceStats;
};

export const StandaloneDeviceConnectionCard = ({ data }: DeviceConnectionCardProps) => {
return (
<div className="connected-user-card">
<DeviceCardContent data={data} />
</div>
);
};

interface Props {
data: NetworkUserStats;
Expand Down Expand Up @@ -59,6 +77,58 @@ export const UserConnectionCard = ({ data }: Props) => {
);
};

const DeviceCardContent = (props: { data: StandaloneDeviceStats }) => {
const { data } = props;

const getSummarizedStats = useMemo(() => summarizeDeviceStats(data.stats), [data]);

const totalUpload = useMemo(
() => sumBy(getSummarizedStats, (s) => s.upload),
[getSummarizedStats],
);
const totalDownload = useMemo(
() => sumBy(getSummarizedStats, (s) => s.download),
[getSummarizedStats],
);

return (
<div className="user-info">
<div className="upper">
<DeviceAvatar deviceId={data.id} />
<NameBox
name={data.name}
publicIp={data.public_ip}
wireguardIp={data.wireguard_ip}
/>
</div>
<div className="lower device">
<ConnectionTime connectedAt={data.connected_at} />
<div className="network-usage-summary">
<div className="network-usage-stats">
<NetworkSpeed
speedValue={totalDownload}
direction={NetworkDirection.DOWNLOAD}
data-testid="download"
/>
<NetworkSpeed
speedValue={totalUpload}
direction={NetworkDirection.UPLOAD}
data-testid="upload"
/>
</div>
<div className="chart">
<AutoSizer>
{({ height, width }) => (
<NetworkUsageChart height={height} width={width} data={data.stats} />
)}
</AutoSizer>
</div>
</div>
</div>
</div>
);
};

interface MainCardContentProps {
data: NetworkUserStats;
}
Expand All @@ -74,7 +144,7 @@ const MainCardContent = ({ data }: MainCardContentProps) => {
}, [data]);

const getSummarizedStats = useMemo(
() => summarizeDeviceStats(data.devices),
() => summarizeDevicesStats(data.devices),
[data.devices],
);

Expand Down Expand Up @@ -131,24 +201,30 @@ const MainCardContent = ({ data }: MainCardContentProps) => {

interface NameBoxProps {
name: string;
publicIp: string;
wireguardIp: string;
publicIp?: string;
wireguardIp?: string;
}

const NameBox = ({ name, publicIp, wireguardIp }: NameBoxProps) => {
return (
<div className="name-box">
<span className="name">{name}</span>
<div className="lower">
<Badge styleVariant={BadgeStyleVariant.STANDARD} text={publicIp} />
<Badge styleVariant={BadgeStyleVariant.STANDARD} text={wireguardIp} />
</div>
{(publicIp || wireguardIp) && (
<div className="lower">
{publicIp !== undefined && publicIp.length > 0 && (
<Badge styleVariant={BadgeStyleVariant.STANDARD} text={publicIp} />
)}
{wireguardIp !== undefined && wireguardIp.length > 0 && (
<Badge styleVariant={BadgeStyleVariant.STANDARD} text={wireguardIp} />
)}
</div>
)}
</div>
);
};

interface ConnectionTimeProps {
connectedAt: string;
connectedAt?: string;
}

const ConnectionTime = ({ connectedAt }: ConnectionTimeProps) => {
Expand All @@ -157,8 +233,11 @@ const ConnectionTime = ({ connectedAt }: ConnectionTimeProps) => {
const [displayedTime, setDisplayedTime] = useState<string | undefined>();

const updateConnectionTime = useCallback(() => {
setDisplayedTime(formatConnectionTime(connectedAt));
}, [connectedAt]);
if (connectedAt) {
setDisplayedTime(formatConnectionTime(connectedAt));
}
return LL.common.noData();
}, [connectedAt, LL.common]);

useEffect(() => {
const interval = 60 * 1000;
Expand Down Expand Up @@ -236,7 +315,7 @@ interface ExpandedDeviceCardProps {
}

const ExpandedDeviceCard = ({ data }: ExpandedDeviceCardProps) => {
const getSummarizedStats = useMemo(() => summarizeDeviceStats([data]), [data]);
const getSummarizedStats = useMemo(() => summarizeDevicesStats([data]), [data]);
const downloadSummary = getSummarizedStats.reduce((sum, e) => {
return sum + e.download;
}, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@
grid-row: 1;
grid-column: 2 / 3;
}

.avatar-icon {
min-width: 40px;
min-height: 40px;

svg {
width: 30px;
height: 30px;
}
}
}

& > .lower {
Expand All @@ -125,20 +135,37 @@
grid-template-rows: 1fr 1fr;
grid-template-columns: 1fr 1fr;
row-gap: 10px;
grid-template-areas:
'time devices'
'usage usage';
column-gap: 10px;
align-items: center;
justify-items: start;
justify-content: space-evenly;

@media (min-width: 400px) {
grid-template-rows: 1fr;
grid-template-columns: 100px 100px 1fr;
grid-template-areas: 'time devices usage';
row-gap: 0;
column-gap: 10px;
&:not(.device) {
grid-template-areas:
'time devices'
'usage usage';

@media (min-width: 400px) {
grid-template-rows: 1fr;
grid-template-columns: 100px 100px 1fr;
grid-template-areas: 'time devices usage';
row-gap: 0;
column-gap: 10px;
}
}

&.device {
grid-template-areas:
'time'
'usage';

@media (min-width: 400px) {
grid-template-rows: 1fr;
grid-template-columns: 100px 1fr;
grid-template-areas: 'time usage';
row-gap: 0;
column-gap: 10px;
}
}

.connection-time {
Expand Down
Loading

0 comments on commit 301114f

Please sign in to comment.