Skip to content

Commit

Permalink
Allow unsetting property endpoints
Browse files Browse the repository at this point in the history
Added a button to allow users to unset properties
endpoints.
Implemented a new modal specifically for unsetting
properties

closes #450

Signed-off-by: Safeta Hajdarevic <safeta.hajdarevic@secomind.com>
  • Loading branch information
safeta87 committed Aug 8, 2024
1 parent 0249f39 commit be817fd
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Compute permissions from the JWT and disable unavailable UI sections, ([#416](https://github.com/astarte-platform/astarte-dashboard/issues/416)).
- For server-owned interfaces, display a form to send data to the device, ([#417](https://github.com/astarte-platform/astarte-dashboard/issues/417)).
- Add a "keep me logged in" during login, ([#451](https://github.com/astarte-platform/astarte-dashboard/issues/451)).
- Allow unsetting property endpoints, ([#450](https://github.com/astarte-platform/astarte-dashboard/issues/450)).

### Changed
- Adapt Device Stats and PieChart to exclude interfaces with 0 exchanged messages, ([#428](https://github.com/astarte-platform/astarte-dashboard/issues/428)).
Expand Down
126 changes: 123 additions & 3 deletions src/DeviceInterfaceValues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,83 @@ const SendInterfaceDataModal = ({
);
};

interface UnsetDataModalProps {
showUnsetModal: boolean;
interfaceDefinition: AstarteInterface;
unsettingData: boolean;
handleShowUnsetModal: () => void;
unsetInterfaceData: (endpoint: string) => void;
}

const UnsetDataModal = ({
showUnsetModal,
interfaceDefinition,
unsettingData,
handleShowUnsetModal,
unsetInterfaceData,
}: UnsetDataModalProps) => {
const [selectedEndpoint, setSelectedEndpoint] = useState<string>('');
const [errors, setErrors] = useState<string>('');

const handleEndpointChange = (e: ChangeEvent<HTMLSelectElement>) => {
setSelectedEndpoint(e.target.value);
setErrors('');
};

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!selectedEndpoint) {
setErrors('Please select an endpoint.');
return;
}
unsetInterfaceData(selectedEndpoint);
};

return (
<Modal size="lg" centered show={showUnsetModal} onHide={handleShowUnsetModal}>
<Form onSubmit={handleSubmit}>
<Modal.Header closeButton>
<Modal.Title>Unset Data</Modal.Title>
</Modal.Header>

<Modal.Body>
<Form.Group as={Col} controlId="formEndpoint">
<Form.Label>Select endpoint to unset</Form.Label>
<Form.Select
value={selectedEndpoint}
onChange={handleEndpointChange}
isInvalid={!!errors}
>
<option value="">Select an endpoint</option>
{interfaceDefinition?.mappings
.filter((mapping) => mapping.allowUnset)
.map((mapping, index) => (
<option key={index} value={mapping.endpoint}>
{mapping.endpoint}
</option>
))}
</Form.Select>
<Form.Control.Feedback type="invalid">{errors}</Form.Control.Feedback>
</Form.Group>
</Modal.Body>

<Modal.Footer>
<Button variant="secondary" onClick={handleShowUnsetModal}>
Cancel
</Button>
<Button variant="primary" type="submit" disabled={unsettingData}>
{unsettingData ? (
<Spinner className="me-2" size="sm" animation="border" role="status" />
) : (
'Unset Data'
)}
</Button>
</Modal.Footer>
</Form>
</Modal>
);
};

export default (): React.ReactElement => {

Check warning on line 597 in src/DeviceInterfaceValues.tsx

View workflow job for this annotation

GitHub Actions / Run code quality and funcionality tests

Assign arrow function to a variable before exporting as module default
const { deviceId = '', interfaceName = '' } = useParams();
const astarte = useAstarte();
Expand All @@ -527,14 +604,20 @@ export default (): React.ReactElement => {
}),
);
const [showModal, setShowModal] = useState(false);
const [showUnsetModal, setShowUnsetModal] = useState(false);
const [sendingData, setSendingData] = useState(false);
const [unsettingData, setUnsettingData] = useState(false);
const [formAlerts, formAlertsController] = useAlerts();
const iface = deviceDataFetcher.value?.interface as AstarteInterface;

const handleShowModal = () => {
setShowModal(!showModal);
};

const handleShowUnsetModal = () => {
setShowUnsetModal(!showUnsetModal);
};

const sendInterfaceData = (data: { endpoint: string; value: AstarteDataValue }) => {
setSendingData(true);
astarte.client
Expand All @@ -554,6 +637,25 @@ export default (): React.ReactElement => {
});
};

const unsetInterfaceData = (endpoint: string) => {
setUnsettingData(true);
astarte.client
.unsetProperty({ deviceId, interfaceName, path: endpoint })
.then(() => {
handleShowUnsetModal();
deviceDataFetcher.refresh();
})
.catch((err) => {
formAlertsController.showError(
`Could not unset data from interface: ${err.response.data.errors.detail}`,
);
handleShowUnsetModal();
})
.finally(() => {
setUnsettingData(false);
});
};

return (
<Container fluid className="p-3">
<div className="d-flex justify-content-between">
Expand All @@ -567,9 +669,18 @@ export default (): React.ReactElement => {
`devices/${deviceId}/interfaces/${interfaceName}`,
) &&
iface?.ownership === 'server' && (
<Button onClick={handleShowModal} className="m-2">
Publish Data
</Button>
<>
<div className="button-container">
<Button onClick={handleShowModal} className="m-2">
Publish Data
</Button>
{iface.mappings.some((mapping) => mapping.allowUnset) && (
<Button onClick={handleShowUnsetModal} className="m-2">
Unset Data
</Button>
)}
</div>
</>
)}
</div>
<AlertsBanner alerts={formAlerts} />
Expand Down Expand Up @@ -603,6 +714,15 @@ export default (): React.ReactElement => {
sendInterfaceData={sendInterfaceData}
/>
)}
{showUnsetModal && (
<UnsetDataModal
showUnsetModal={showUnsetModal}
interfaceDefinition={iface}
unsettingData={unsettingData}
handleShowUnsetModal={handleShowUnsetModal}
unsetInterfaceData={unsetInterfaceData}
/>
)}
</Container>
);
};
11 changes: 11 additions & 0 deletions src/astarte-client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,17 @@ astarteAPIurl`${config.realmManagementApiUrl}v1/${'realm'}/interfaces/${'interfa
data,
);
}
async unsetProperty(params: {
deviceId: string;
interfaceName: string;
path: string;
}): Promise<void> {
const { deviceId, interfaceName, path } = params;

await this.$delete(
this.apiConfig.sendInterfaceData({ ...this.config, deviceId, interfaceName, path }),
);
}
}

export default AstarteClient;

0 comments on commit be817fd

Please sign in to comment.