Skip to content

Commit

Permalink
[#69247] frontend: Introduce option to specify update policy for an R…
Browse files Browse the repository at this point in the history
…DFM group
  • Loading branch information
MaciejWas authored and glatosinski committed Dec 6, 2024
1 parent 4ff6bb2 commit 0314bcd
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 6 deletions.
2 changes: 1 addition & 1 deletion frontend/src/assets/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
top: 50vh;
transform: translate(-50%, -50%);
padding: 2em;
width: 35vw;
width: max(35vw, 300px);

& > .header {
font-family: 'Inter', sans-serif;
Expand Down
1 change: 1 addition & 0 deletions frontend/src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const REGISTER_DEVICE_ENDPOINT = `${SERVER_URL}/api/v1/auth/register`;
export const GROUPS_ENDPOINT = `${SERVER_URL}/api/v2/groups`;
export const DELETE_GROUP_ENDPOINT = (id: number) => `${GROUPS_ENDPOINT}/${id}`;
export const UPDATE_GROUP_PRIORITY_ENDPOINT = (id: number) => `${GROUPS_ENDPOINT}/${id}/priority`;
export const UPDATE_GROUP_POLICY_ENDPOINT = (id: number) => `${GROUPS_ENDPOINT}/${id}/policy`;
export const PATCH_DEVICES_IN_GROUP_ENDPOINT = (id: number) => `${GROUPS_ENDPOINT}/${id}/devices`;
export const ASSIGN_PACKAGE_IN_GROUP_ENDPOINT = (id: number) => `${GROUPS_ENDPOINT}/${id}/package`;

Expand Down
106 changes: 106 additions & 0 deletions frontend/src/components/SimpleDropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<template>
<div
tabindex="1"
:style="{ border: expanded ? '1px solid var(--gray-800)' : '' }"
class="simple-dropdown"
v-on:blur="() => (expanded = false)"
v-on:click="() => (expanded = !expanded)"
>
<div style="width: 100%" v-on:click="() => (expanded = false)">
{{ selected?.display || 'No value' }}
<CaretDown v-if="!expanded" />
<CaretUp v-if="expanded" />
</div>

<div class="dropdown-options" v-if="expanded">
<div
v-on:click="
() => {
select?.(opt.id);
selected = opt;
}
"
v-for="opt in options"
:key="opt.id"
class="option"
>
{{ opt.display }}
</div>
</div>
</div>
</template>

<style>
.dropdown-options {
position: absolute;
top: 39px;
left: -1px;
right: 17px;
z-index: 14;
height: fit-content;
border: 1px solid var(--gray-400);
border-radius: 5px;
}
.simple-dropdown {
cursor: pointer;
position: relative;
color: white;
svg {
float: right;
padding: 4px;
}
}
.option {
width: 100%;
height: fit-content;
padding: 0.5em;
background: var(--gray-100);
&:hover {
background: var(--gray-500);
}
}
.simple-dropdown {
height: fit-content;
padding: 0.5em;
background: var(--gray-100);
border: 1px solid var(--gray-400);
border-radius: 4px;
}
</style>

<script lang="ts">
import CaretDown from '@/images/CaretDown.vue';
import CaretUp from '@/images/CaretUp.vue';
import { computed, defineComponent, ref, type PropType } from 'vue';
export default defineComponent({
props: {
options: {
type: Object as PropType<{ id: string; display: string }[]>,
required: true,
},
initial: {
type: Object as PropType<string | null>,
required: true,
},
select: {
type: Function as PropType<(selected: string) => void>,
required: true,
},
},
setup(props) {
const options = computed(() => props.options);
return {
expanded: ref(false),
selected: ref(options.value.find((o) => o.id == props.initial)),
options,
select: props.select,
};
},
components: { CaretDown, CaretUp },
});
</script>
142 changes: 137 additions & 5 deletions frontend/src/components/groups/GroupsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,28 @@ Component wraps functionality for displaying and working with rdfm groups.
:columns="packageDropdownColumns"
:data="packageList"
:select="selectPackage"
:toggleDropdown="() => toggleDropdown(0)"
:toggleDropdown="() => toggleDropdown(DropDownOpen.Packages)"
:dropdownOpen="packageDropdownOpen"
/>
</div>
<div v-if="groupConfiguration.policy" class="entry policy-entry">
<p>Update policy</p>
<div class="policy-type" v-if="availablePolicies.length >= 1">
<SimpleDropdown
:initial="groupConfiguration.policy"
:options="availablePolicies"
:select="
(val: string) => {
groupConfiguration.policy = val;
}
"
/>
<p v-if="unapplicablePolicyWarning" class="warning">
The selected update policy will not affect any device in this group
</p>
</div>
</div>

<div class="entry">
<p>Devices</p>
<Dropdown
Expand All @@ -102,7 +120,7 @@ Component wraps functionality for displaying and working with rdfm groups.
:columns="deviceDropdownColumns"
:data="deviceList"
:select="selectDevice"
:toggleDropdown="() => toggleDropdown(1)"
:toggleDropdown="() => toggleDropdown(DropDownOpen.Devices)"
:dropdownOpen="deviceDropdownOpen"
/>
</div>
Expand Down Expand Up @@ -224,6 +242,12 @@ Component wraps functionality for displaying and working with rdfm groups.
</template>

<style scoped>
.warning {
color: yellow;
font-size: 0.8em;
margin-top: 5px;
}

.container {
padding: 2em;

Expand Down Expand Up @@ -294,9 +318,23 @@ Component wraps functionality for displaying and working with rdfm groups.
</style>

<script lang="ts">
import { computed, onMounted, onUnmounted, ref, type Ref, reactive, type Reactive } from 'vue';

import { POLL_INTERVAL, useNotifications, type Group } from '../../common/utils';
import {
computed,
onMounted,
onUnmounted,
ref,
type Ref,
reactive,
type Reactive,
effect,
} from 'vue';
import {
POLL_INTERVAL,
useNotifications,
type Group,
type Package,
type RegisteredDevice,
} from '../../common/utils';
import {
addGroupRequest,
devicesResources,
Expand All @@ -307,10 +345,12 @@ import {
patchDevicesRequest,
removeGroupRequest,
updatePackagesRequest,
updatePolicyRequest,
updatePriorityRequest,
type GroupConfiguration,
type InitialGroupConfiguration,
type NewGroupData,
type PolicyType,
} from './groups';

import type { Column, DataEntry } from '../Dropdown.vue';
Expand All @@ -320,6 +360,7 @@ import RemovePopup from '../RemovePopup.vue';
import TitleBar from '../TitleBar.vue';
import Cross from '../icons/Cross.vue';
import Dropdown from '../Dropdown.vue';
import SimpleDropdown from '../SimpleDropdown.vue';

export enum GroupPopupOpen {
AddGroup,
Expand All @@ -341,6 +382,7 @@ export default {
TitleBar,
Cross,
Dropdown,
SimpleDropdown,
},
setup() {
const popupOpen = ref(GroupPopupOpen.None);
Expand Down Expand Up @@ -426,17 +468,76 @@ export default {
const groupConfiguration: Reactive<GroupConfiguration> = reactive({
id: null,
priority: null,
policy: null,
devices: null,
packages: null,
});

let initialGroupConfiguration: InitialGroupConfiguration | null = null;

/**
* Packages selected in the group configuration. Might be different than the group' packages if there are unsubmitted changes.
*/
const selectedPackages = computed(
() =>
(packageList.value
.filter(({ selected }) => selected)
?.map(({ id }) => packagesResources.resources.value?.find((p) => p.id == id))
.filter(Boolean) as Package[]) || [],
);

/**
* Devices selected in the group configuration. Might be different than the group's devices if there are unsubmitted changes.
*/
const selectedDevices = computed(
() =>
(deviceList.value
.filter(({ selected }) => selected)
?.map(({ id }) => devicesResources.resources.value?.find((d) => d.id == id))
.filter(Boolean) as RegisteredDevice[]) || [],
);

/**
* Warn the user if the current policy has no effect on the selected devices.
*/
const unapplicablePolicyWarning = computed(() => {
const policyArg = groupConfiguration.policy?.split(',')[1];
console.log(selectedDevices.value);

if (selectedDevices.value.length == 0) return false;
if (!policyArg) return false;

const matchingPackages = selectedPackages.value.filter(
(p) => p.metadata['rdfm.software.version'] == policyArg,
);

return selectedDevices.value.every((d) =>
matchingPackages.every(
(p) =>
p.metadata['rdfm.hardware.devtype'] != d.metadata['rdfm.hardware.devtype'],
),
);
});

const availablePolicies = computed(() => {
const availableVersions = selectedPackages.value
.map((p) => p.metadata['rdfm.software.version'])
.sort((p1, p2) => p1.localeCompare(p2));
return [
{ id: 'no_update,', display: 'No Update' },
...[...new Set(availableVersions)].map((version) => ({
id: `exact_match,${version}`,
display: `Match version ${version}`,
})),
];
});

const openConfigureGroupPopup = async (group: Group) => {
groupConfiguration.id = group.id;
groupConfiguration.priority = group.priority;
groupConfiguration.devices = [...group.devices];
groupConfiguration.packages = [...group.packages];
groupConfiguration.policy = group.policy;

packageList.value = (packagesResources.resources.value ?? []).map((v) => ({
id: v.id,
Expand All @@ -457,6 +558,7 @@ export default {
initialGroupConfiguration = {
id: group.id,
priority: group.priority,
policy: group.policy,
packages: [...group.packages],
devices: [...group.devices],
};
Expand All @@ -477,6 +579,7 @@ export default {
return !(
original.id === initial.id &&
original.priority === initial.priority &&
original.policy === initial.policy &&
JSON.stringify(original.packages) === JSON.stringify(initial.packages) &&
JSON.stringify(original.devices) === JSON.stringify(initial.devices)
);
Expand Down Expand Up @@ -561,6 +664,31 @@ export default {
}
}

if (
JSON.stringify(groupConfiguration.policy) !=
JSON.stringify(initialGroupConfiguration!.policy)
) {
if (!groupConfiguration.policy) {
notifications.notifyError({
headline: 'Error when updating group policy:',
msg: 'Policy is a required field',
});
return false;
}

const { success, message } = await updatePolicyRequest(
groupConfiguration.id!,
groupConfiguration.policy,
);
if (!success) {
notifications.notifyError({
headline: 'Error when updating group update policy',
msg: message || 'Updating group update policy failed',
});
return;
}
}

if (requestWasMade)
notifications.notifySuccess({ headline: 'Group configuration was updated' });

Expand Down Expand Up @@ -636,6 +764,8 @@ export default {
const groupsCount = computed(() => groupResources.resources.value?.length ?? 0);

return {
DropDownOpen,
openDropdown,
openRemoveGroupPopup,
closeAddGroupPopup,
addGroup,
Expand Down Expand Up @@ -666,6 +796,8 @@ export default {
deviceDropdownOpen,
closeDropdowns,
toggleDropdown,
availablePolicies,
unapplicablePolicyWarning,
};
},
};
Expand Down
Loading

0 comments on commit 0314bcd

Please sign in to comment.