KEYCLOAK-11539: Rewrite Device Activity page
This commit is contained in:
parent
9a93e5028c
commit
787386fc21
2 changed files with 20 additions and 116 deletions
|
@ -26,8 +26,6 @@ import {
|
||||||
DataListItem,
|
DataListItem,
|
||||||
DataListItemRow,
|
DataListItemRow,
|
||||||
DataListCell,
|
DataListCell,
|
||||||
DataListToggle,
|
|
||||||
DataListContent,
|
|
||||||
DataListItemCells,
|
DataListItemCells,
|
||||||
Grid,
|
Grid,
|
||||||
GridItem,
|
GridItem,
|
||||||
|
@ -44,11 +42,8 @@ import {
|
||||||
FirefoxIcon,
|
FirefoxIcon,
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
InternetExplorerIcon,
|
InternetExplorerIcon,
|
||||||
LaptopIcon,
|
|
||||||
MobileAltIcon,
|
|
||||||
OperaIcon,
|
OperaIcon,
|
||||||
SafariIcon,
|
SafariIcon,
|
||||||
TabletAltIcon,
|
|
||||||
YandexInternationalIcon,
|
YandexInternationalIcon,
|
||||||
} from '@patternfly/react-icons';
|
} from '@patternfly/react-icons';
|
||||||
|
|
||||||
|
@ -64,7 +59,6 @@ export interface DeviceActivityPageProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceActivityPageState {
|
export interface DeviceActivityPageState {
|
||||||
isRowOpen: boolean[];
|
|
||||||
devices: Device[];
|
devices: Device[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +92,6 @@ interface Client {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2019 Red Hat Inc.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
export class DeviceActivityPage extends React.Component<DeviceActivityPageProps, DeviceActivityPageState> {
|
export class DeviceActivityPage extends React.Component<DeviceActivityPageProps, DeviceActivityPageState> {
|
||||||
|
|
||||||
|
@ -106,19 +99,12 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isRowOpen: [],
|
|
||||||
devices: []
|
devices: []
|
||||||
};
|
};
|
||||||
|
|
||||||
this.fetchDevices();
|
this.fetchDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onToggle = (row: number): void => {
|
|
||||||
const newIsRowOpen: boolean[] = this.state.isRowOpen;
|
|
||||||
newIsRowOpen[row] = !newIsRowOpen[row];
|
|
||||||
this.setState({isRowOpen: newIsRowOpen});
|
|
||||||
};
|
|
||||||
|
|
||||||
private signOutAll = () => {
|
private signOutAll = () => {
|
||||||
AccountServiceClient.Instance.doDelete("/sessions")
|
AccountServiceClient.Instance.doDelete("/sessions")
|
||||||
.then( (response: AxiosResponse<Object>) => {
|
.then( (response: AxiosResponse<Object>) => {
|
||||||
|
@ -135,8 +121,6 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchDevices(): void {
|
private fetchDevices(): void {
|
||||||
const sessionsOnOpenRow: string[] = this.findSessionsOnOpenRow();
|
|
||||||
|
|
||||||
AccountServiceClient.Instance.doGet("/sessions/devices")
|
AccountServiceClient.Instance.doGet("/sessions/devices")
|
||||||
.then((response: AxiosResponse<Object>) => {
|
.then((response: AxiosResponse<Object>) => {
|
||||||
console.log({response});
|
console.log({response});
|
||||||
|
@ -145,43 +129,12 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
|
||||||
devices = this.moveCurrentToTop(devices);
|
devices = this.moveCurrentToTop(devices);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isRowOpen: this.openRows(devices, sessionsOnOpenRow),
|
|
||||||
devices: devices
|
devices: devices
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculating open rows this way assures that when a session is signed out
|
|
||||||
// or on refresh, the open rows stay open
|
|
||||||
private findSessionsOnOpenRow() : string[] {
|
|
||||||
let sessionsOnOpenRow: string[] = [];
|
|
||||||
|
|
||||||
this.state.devices.forEach( (device: Device, index: number) => {
|
|
||||||
if (this.state.isRowOpen[index]) {
|
|
||||||
device.sessions.forEach( (session: Session) => {
|
|
||||||
sessionsOnOpenRow.push(session.id);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return sessionsOnOpenRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
private openRows(devices: Device[], sessionsOnOpenRow: string[]): boolean[] {
|
|
||||||
const openRows: boolean[] = new Array<boolean>().fill(false);
|
|
||||||
|
|
||||||
devices.forEach((device: Device, deviceIndex: number) => {
|
|
||||||
device.sessions.forEach( (session: Session) => {
|
|
||||||
if (sessionsOnOpenRow.includes(session.id)) {
|
|
||||||
openRows[deviceIndex] = true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
return openRows;
|
|
||||||
}
|
|
||||||
|
|
||||||
// current device and session should display at the top of their respective lists
|
// current device and session should display at the top of their respective lists
|
||||||
private moveCurrentToTop(devices: Device[]): Device[] {
|
private moveCurrentToTop(devices: Device[]): Device[] {
|
||||||
let currentDevice: Device = devices[0];
|
let currentDevice: Device = devices[0];
|
||||||
|
@ -208,32 +161,6 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
|
||||||
return moment(time * 1000).format('LLLL');
|
return moment(time * 1000).format('LLLL');
|
||||||
}
|
}
|
||||||
|
|
||||||
private findDeviceName(device: Device): string {
|
|
||||||
let deviceName: string = device.device;
|
|
||||||
|
|
||||||
const deviceNameLower = deviceName.toLowerCase();
|
|
||||||
if (deviceNameLower.includes('other') || deviceNameLower.includes('generic') ) {
|
|
||||||
deviceName = this.findOS(device) + ' ' + this.findOSVersion(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deviceName.trim() === 'Other') {
|
|
||||||
deviceName = this.makeClientsString(device.sessions[0].clients);
|
|
||||||
}
|
|
||||||
|
|
||||||
return deviceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private findDeviceIcon(device: Device): React.ReactNode {
|
|
||||||
const deviceName = device.device.toLowerCase();
|
|
||||||
if (deviceName.includes('kindle') || deviceName.includes('ipad')) {
|
|
||||||
return (<TabletAltIcon size='lg' />);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device.mobile) return (<MobileAltIcon size='lg' />);
|
|
||||||
|
|
||||||
return (<LaptopIcon size='lg'/>);
|
|
||||||
}
|
|
||||||
|
|
||||||
private findBrowserIcon(session: Session): React.ReactNode {
|
private findBrowserIcon(session: Session): React.ReactNode {
|
||||||
const browserName: string = session.browser.toLowerCase();
|
const browserName: string = session.browser.toLowerCase();
|
||||||
if (browserName.includes("chrom")) return (<ChromeIcon size='lg'/>); // chrome or chromium
|
if (browserName.includes("chrom")) return (<ChromeIcon size='lg'/>); // chrome or chromium
|
||||||
|
@ -318,41 +245,17 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
|
||||||
/>
|
/>
|
||||||
</DataListItemRow>
|
</DataListItemRow>
|
||||||
</DataListItem>
|
</DataListItem>
|
||||||
|
|
||||||
{this.state.devices.map((device: Device, deviceIndex: number) => {
|
<DataListItem aria-labelledby='sessions'>
|
||||||
return (
|
<Grid gutter='sm'>
|
||||||
<DataListItem key={'device-' + deviceIndex} aria-labelledby="ex-item1" isExpanded={this.state.isRowOpen[deviceIndex]}>
|
<GridItem span={12}/> {/* <-- top spacing */}
|
||||||
<DataListItemRow onClick={() => this.onToggle(deviceIndex)}>
|
{this.state.devices.map((device: Device, deviceIndex: number) => {
|
||||||
<DataListToggle
|
return (
|
||||||
isExpanded={this.state.isRowOpen[deviceIndex]}
|
<React.Fragment>
|
||||||
id={'deviceToggle-' + deviceIndex}
|
|
||||||
aria-controls="ex-expand1"
|
|
||||||
/>
|
|
||||||
<DataListItemCells
|
|
||||||
dataListCells={[
|
|
||||||
<DataListCell isIcon key={"icon-" + deviceIndex} width={1}>
|
|
||||||
{this.findDeviceIcon(device)}
|
|
||||||
</DataListCell>,
|
|
||||||
<DataListCell key={"os-" + deviceIndex} width={1}>
|
|
||||||
<strong>{this.findDeviceName(device)}</strong>
|
|
||||||
</DataListCell>,
|
|
||||||
<DataListCell key={"lastAccess-" + deviceIndex} width={5}>
|
|
||||||
{device.current && <p><span className='pf-c-badge pf-m-read'><strong><Msg msgKey="currentDevice"/></strong></span></p>}
|
|
||||||
{!device.current && <p><strong><Msg msgKey="lastAccess"/>: </strong><span>{this.time(device.lastAccess)}</span></p>}
|
|
||||||
</DataListCell>
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</DataListItemRow>
|
|
||||||
<DataListContent
|
|
||||||
noPadding={false}
|
|
||||||
aria-label="Session Details"
|
|
||||||
id="ex-expand1"
|
|
||||||
isHidden={!this.state.isRowOpen[deviceIndex]}
|
|
||||||
>
|
|
||||||
<Grid gutter='sm'>
|
|
||||||
{device.sessions.map((session: Session, sessionIndex: number) => {
|
{device.sessions.map((session: Session, sessionIndex: number) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={'device-' + deviceIndex + '-session-' + sessionIndex}>
|
<React.Fragment key={'device-' + deviceIndex + '-session-' + sessionIndex}>
|
||||||
|
|
||||||
<GridItem span={3}>
|
<GridItem span={3}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<StackItem isFilled={false}>
|
<StackItem isFilled={false}>
|
||||||
|
@ -392,14 +295,15 @@ export class DeviceActivityPage extends React.Component<DeviceActivityPageProps,
|
||||||
|
|
||||||
</GridItem>
|
</GridItem>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)})
|
);
|
||||||
}
|
|
||||||
|
})}
|
||||||
</Grid>
|
</React.Fragment>
|
||||||
</DataListContent>
|
)
|
||||||
</DataListItem>
|
})}
|
||||||
)})}
|
<GridItem span={12}/> {/* <-- bottom spacing */}
|
||||||
|
</Grid>
|
||||||
|
</DataListItem>
|
||||||
</DataList>
|
</DataList>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
|
|
||||||
|
|
|
@ -949,7 +949,7 @@
|
||||||
'./icons/landmark-icon.js': '@empty',
|
'./icons/landmark-icon.js': '@empty',
|
||||||
'./icons/language-icon.js': '@empty',
|
'./icons/language-icon.js': '@empty',
|
||||||
'./icons/laptop-code-icon.js': '@empty',
|
'./icons/laptop-code-icon.js': '@empty',
|
||||||
//'./icons/laptop-icon.js': '@empty',
|
'./icons/laptop-icon.js': '@empty',
|
||||||
'./icons/laptop-medical-icon.js': '@empty',
|
'./icons/laptop-medical-icon.js': '@empty',
|
||||||
'./icons/laravel-icon.js': '@empty',
|
'./icons/laravel-icon.js': '@empty',
|
||||||
'./icons/lastfm-icon.js': '@empty',
|
'./icons/lastfm-icon.js': '@empty',
|
||||||
|
@ -1050,7 +1050,7 @@
|
||||||
'./icons/mix-icon.js': '@empty',
|
'./icons/mix-icon.js': '@empty',
|
||||||
'./icons/mixcloud-icon.js': '@empty',
|
'./icons/mixcloud-icon.js': '@empty',
|
||||||
'./icons/mizuni-icon.js': '@empty',
|
'./icons/mizuni-icon.js': '@empty',
|
||||||
//'./icons/mobile-alt-icon.js': '@empty',
|
'./icons/mobile-alt-icon.js': '@empty',
|
||||||
'./icons/mobile-icon.js': '@empty',
|
'./icons/mobile-icon.js': '@empty',
|
||||||
'./icons/modx-icon.js': '@empty',
|
'./icons/modx-icon.js': '@empty',
|
||||||
'./icons/monero-icon.js': '@empty',
|
'./icons/monero-icon.js': '@empty',
|
||||||
|
@ -1603,7 +1603,7 @@
|
||||||
'./icons/syringe-icon.js': '@empty',
|
'./icons/syringe-icon.js': '@empty',
|
||||||
'./icons/table-icon.js': '@empty',
|
'./icons/table-icon.js': '@empty',
|
||||||
'./icons/table-tennis-icon.js': '@empty',
|
'./icons/table-tennis-icon.js': '@empty',
|
||||||
//'./icons/tablet-alt-icon.js': '@empty',
|
'./icons/tablet-alt-icon.js': '@empty',
|
||||||
'./icons/tablet-icon.js': '@empty',
|
'./icons/tablet-icon.js': '@empty',
|
||||||
'./icons/tablets-icon.js': '@empty',
|
'./icons/tablets-icon.js': '@empty',
|
||||||
'./icons/tachometer-alt-icon.js': '@empty',
|
'./icons/tachometer-alt-icon.js': '@empty',
|
||||||
|
|
Loading…
Reference in a new issue