Skip to content

Commit

Permalink
Add Deletion in progress status for every device
Browse files Browse the repository at this point in the history
Add possibility that user can see Deletion in progress status for every device
Add new checkbox filter for Deletion in progress status
The status changes to `Yes` whenever deleted device

closes astarte-platform#398

Signed-off-by: Armin Ahmetovic <armin.ahmetovic@secomind.com>
  • Loading branch information
arahmarchak committed Jan 15, 2024
1 parent ab4fedf commit fd93349
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/DeviceStatusPage/AliasesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const AliasesCard = ({
<p>Device has no aliases</p>
)}
<div className="mt-auto">
<Button variant="primary" onClick={onNewAliasClick}>
<Button variant="primary" onClick={onNewAliasClick} disabled={device.deletionInProgress}>
Add alias
</Button>
</div>
Expand Down
6 changes: 5 additions & 1 deletion src/DeviceStatusPage/AttributesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ const AttributesCard = ({
<p>Device has no attribute</p>
)}
<div className="mt-auto">
<Button variant="primary" onClick={onNewAttributeClick}>
<Button
variant="primary"
onClick={onNewAttributeClick}
disabled={device.deletionInProgress}
>
Add attribute
</Button>
</div>
Expand Down
40 changes: 32 additions & 8 deletions src/DeviceStatusPage/DeviceInfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import type { AstarteDevice } from 'astarte-client';
import FullHeightCard from '../components/FullHeightCard';
import Icon from '../components/Icon';

interface ConnectionStatusProps {
status: AstarteDevice['connectionStatus'];
interface DeviceStatusProps {
status: AstarteDevice['deviceStatus'];
}

const ConnectionStatus = ({ status }: ConnectionStatusProps): React.ReactElement => {
const DeviceStatus = ({ status }: DeviceStatusProps): React.ReactElement => {
let statusString;
let icon;

Expand All @@ -42,6 +42,11 @@ const ConnectionStatus = ({ status }: ConnectionStatusProps): React.ReactElement
icon = 'statusDisconnected' as const;
break;

case 'in_deletion':
statusString = 'In deletion';
icon = 'statusInDeletion' as const;
break;

case 'never_connected':
default:
statusString = 'Never connected';
Expand Down Expand Up @@ -81,24 +86,43 @@ const DeviceInfoCard = ({
<p>{device.hasNameAlias ? device.name : 'No name alias set'}</p>
<h6>Status</h6>
<p>
<ConnectionStatus status={device.connectionStatus} />
<DeviceStatus status={device.deviceStatus} />
</p>
<h6>Credentials inhibited</h6>
<p>{device.hasCredentialsInhibited ? 'True' : 'False'}</p>
<div className="mt-auto">
{device.hasCredentialsInhibited ? (
<Button variant="success text-white" className="mr-1" onClick={onEnableCredentialsClick}>
<Button
variant="success text-white"
className="mr-1"
onClick={onEnableCredentialsClick}
disabled={device.deletionInProgress}
>
Enable credentials request
</Button>
) : (
<Button variant="danger" className="mr-1" onClick={onInhibitCredentialsClick}>
<Button
variant="danger"
className="mr-1"
onClick={onInhibitCredentialsClick}
disabled={device.deletionInProgress}
>
Inhibit credentials
</Button>
)}
<Button variant="danger" onClick={onWipeCredentialsClick}>
<Button
variant="danger"
onClick={onWipeCredentialsClick}
disabled={device.deletionInProgress}
>
Wipe credential secret
</Button>
<Button variant="danger" className="ml-1" onClick={onDeleteDeviceClick}>
<Button
variant="danger"
className="ml-1"
onClick={onDeleteDeviceClick}
disabled={device.deletionInProgress}
>
Delete device
</Button>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/DeviceStatusPage/DeviceStatusEventsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const DeviceStatusEventsCard = ({ device }: DeviceStatusEventsCardProps): React.
{ label: 'First registration', value: device.firstRegistration?.toLocaleString() },
{ label: 'Last connection', value: device.lastConnection?.toLocaleString() },
{ label: 'Last disconnection', value: device.lastDisconnection?.toLocaleString() },
{ label: 'Deletion in progress', value: device.deletionInProgress },
].filter(({ value }) => value !== undefined);

return (
Expand Down
29 changes: 28 additions & 1 deletion src/DevicesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ interface DeviceFilters {
attributeKey?: string;
attributeValue?: string;
activeSinceDate?: Date;
showDeletionInProgress?: boolean;
}

const DEVICES_PER_PAGE = 20;
Expand Down Expand Up @@ -100,25 +101,36 @@ const DeviceRow = ({ device, filters }: DeviceRowProps): React.ReactElement => {
let lastEvent;
let icon;
let iconTooltip;
let statusLabel;

if (device.isConnected) {
icon = 'statusConnected' as const;
iconTooltip = 'Connected';
statusLabel = 'Connected';
lastEvent = `Connected on ${(device.lastConnection as Date).toLocaleString()}`;
} else if (device.lastConnection) {
icon = 'statusDisconnected' as const;
iconTooltip = 'Disconnected';
statusLabel = 'Disconnected';
lastEvent = `Disconnected on ${(device.lastDisconnection as Date).toLocaleString()}`;
} else {
icon = 'statusNeverConnected' as const;
iconTooltip = 'Never connected';
statusLabel = 'Never connected';
lastEvent = 'Never connected';
}

if (device.deletionInProgress) {
icon = 'statusInDeletion' as const;
iconTooltip = 'In deletion';
statusLabel = 'In deletion';
}

return (
<tr>
<td>
<Icon icon={icon} tooltip={iconTooltip} tooltipPlacement="right" />
<Icon className="mr-2" icon={icon} tooltip={iconTooltip} tooltipPlacement="right" />
<span>{statusLabel}</span>
</td>
<td className={device.hasNameAlias ? '' : 'text-monospace'}>
<Link to={`/devices/${device.id}/edit`}>{device.name}</Link>
Expand Down Expand Up @@ -159,6 +171,7 @@ const matchFilters = (device: AstarteDevice, filters: DeviceFilters) => {
showConnected = true,
showDisconnected = true,
showNeverConnected = true,
showDeletionInProgress = true,
activeSinceDate,
} = filters;

Expand All @@ -180,6 +193,10 @@ const matchFilters = (device: AstarteDevice, filters: DeviceFilters) => {
return false;
}

if (!showDeletionInProgress && device.deletionInProgress) {
return false;
}

if (
(attributeKey !== '' || attributeValue !== '') &&
!Array.from(device.attributes).some(([key, value]) =>
Expand Down Expand Up @@ -279,6 +296,7 @@ const FilterForm = ({ filters, onUpdateFilters }: FilterFormProps): React.ReactE
showConnected = true,
showDisconnected = true,
showNeverConnected = true,
showDeletionInProgress = true,
attributeKey = '',
attributeValue = '',
activeSinceDate,
Expand Down Expand Up @@ -329,6 +347,15 @@ const FilterForm = ({ filters, onUpdateFilters }: FilterFormProps): React.ReactE
onUpdateFilters({ ...filters, showNeverConnected: e.target.checked })
}
/>
<Form.Check
type="checkbox"
id="checkbox-deletion-in-progress"
label="Deletion in progress"
checked={showDeletionInProgress}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onUpdateFilters({ ...filters, showDeletionInProgress: e.target.checked })
}
/>
</Form.Group>
<Form.Group controlId="filterActiveSince" className="mb-4">
<Form.Label>
Expand Down
13 changes: 12 additions & 1 deletion src/GroupDevicesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,36 @@ const deviceTableRow = (
let icon;
let iconTooltip;
let lastEvent;
let statusLabel;

if (device.isConnected) {
icon = 'statusConnected' as const;
iconTooltip = 'Connected';
statusLabel = 'Connected';
lastEvent = `Connected on ${(device.lastConnection as Date).toLocaleString()}`;
} else if (device.lastConnection) {
icon = 'statusDisconnected' as const;
iconTooltip = 'Disconnected';
statusLabel = 'Disconnected';
lastEvent = `Disconnected on ${(device.lastDisconnection as Date).toLocaleString()}`;
} else {
icon = 'statusNeverConnected' as const;
iconTooltip = 'Never connected';
statusLabel = 'Never connected';
lastEvent = 'Never connected';
}

if (device.deletionInProgress) {
icon = 'statusInDeletion' as const;
iconTooltip = 'In deletion';
statusLabel = 'In deletion';
}

return (
<tr key={index}>
<td>
<Icon icon={icon} tooltip={iconTooltip} tooltipPlacement="right" />
<Icon className="mr-2" icon={icon} tooltip={iconTooltip} tooltipPlacement="right" />
<span>{statusLabel}</span>
</td>
<td className={device.hasNameAlias ? '' : 'text-monospace'}>
<Link to={`/devices/${device.id}/edit`}>{device.name}</Link>
Expand Down
13 changes: 11 additions & 2 deletions src/astarte-client/models/Device/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export interface AstarteDeviceObject {
lastSeenIp?: string;

lastCredentialsRequestIp?: string;

deletionInProgress: boolean;
}

const generateMapValidation =
Expand Down Expand Up @@ -119,10 +121,11 @@ const astarteDeviceObjectSchema: yup.ObjectSchema<AstarteDeviceObject> = yup
lastConnection: yup.date().notRequired(),
lastSeenIp: yup.string().notRequired(),
lastCredentialsRequestIp: yup.string().notRequired(),
deletionInProgress: yup.boolean().required(),
})
.required();

type AstarteDeviceConnectionStatus = 'never_connected' | 'connected' | 'disconnected';
type AstarteDeviceStatus = 'never_connected' | 'connected' | 'disconnected' | 'in_deletion';

export class AstarteDevice {
id: string;
Expand Down Expand Up @@ -157,6 +160,8 @@ export class AstarteDevice {

lastCredentialsRequestIp?: string;

deletionInProgress: boolean;

constructor(obj: AstarteDeviceObject) {
const validatedObj = astarteDeviceObjectSchema.validateSync(obj);
this.id = validatedObj.id;
Expand All @@ -175,6 +180,7 @@ export class AstarteDevice {
this.lastConnection = validatedObj.lastConnection;
this.lastSeenIp = validatedObj.lastSeenIp;
this.lastCredentialsRequestIp = validatedObj.lastCredentialsRequestIp;
this.deletionInProgress = validatedObj.deletionInProgress;
}

get hasNameAlias(): boolean {
Expand All @@ -188,7 +194,10 @@ export class AstarteDevice {
return this.id;
}

get connectionStatus(): AstarteDeviceConnectionStatus {
get deviceStatus(): AstarteDeviceStatus {
if (this.deletionInProgress) {
return 'in_deletion';
}
if (this.lastConnection == null) {
return 'never_connected';
}
Expand Down
2 changes: 2 additions & 0 deletions src/astarte-client/transforms/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const fromAstarteDeviceDTO = (dto: AstarteDeviceDTO): AstarteDevice =>
lastSeenIp: dto.last_seen_ip != null ? dto.last_seen_ip : undefined,
lastCredentialsRequestIp:
dto.last_credentials_request_ip != null ? dto.last_credentials_request_ip : undefined,
deletionInProgress: !!dto.deletion_in_progress,
});

export const toAstarteDeviceDTO = (obj: AstarteDevice): AstarteDeviceDTO => ({
Expand All @@ -89,4 +90,5 @@ export const toAstarteDeviceDTO = (obj: AstarteDevice): AstarteDeviceDTO => ({
last_seen_ip: obj.lastSeenIp != null ? obj.lastSeenIp : undefined,
last_credentials_request_ip:
obj.lastCredentialsRequestIp != null ? obj.lastCredentialsRequestIp : undefined,
deletion_in_progress: !!obj.deletionInProgress,
});
1 change: 1 addition & 0 deletions src/astarte-client/types/dto/device.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ export interface AstarteDeviceDTO {
exchanged_msgs?: number;
exchanged_bytes?: number;
}>;
deletion_in_progress?: boolean;
}
1 change: 1 addition & 0 deletions src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const iconToClassName = {
statusDisconnected: 'fas fa-circle color-red',
statusKO: 'fas fa-times-circle color-red',
statusNeverConnected: 'fas fa-circle color-grey',
statusInDeletion: 'fas fa-circle color-grey',
triggers: 'fas fa-bolt',
policy: 'fas fa-file-invoice',
};
Expand Down

0 comments on commit fd93349

Please sign in to comment.