Skip to content

Commit

Permalink
add order to constraint items in plans
Browse files Browse the repository at this point in the history
reconnect constraint run results to UI
  • Loading branch information
duranb committed Mar 3, 2025
1 parent d5f9d70 commit b5d4e11
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 95 deletions.
22 changes: 9 additions & 13 deletions src/components/constraints/ConstraintForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,10 @@
tagsToUpdate,
},
} = event;
const constraintMetadataTagsToUpdate: ConstraintMetadataTagsInsertInput[] = tagsToUpdate.map(
({ id: tag_id }) => ({
constraint_id: initialConstraintId as number,
tag_id,
}),
);
const constraintMetadataTagsToUpdate: ConstraintMetadataTagsInsertInput[] = tagsToUpdate.map(({ id }) => ({
constraint_id: initialConstraintId as number,
tag_id: id,
}));
await effects.updateConstraintMetadata(
initialConstraintId,
Expand Down Expand Up @@ -220,13 +218,11 @@
detail: { tagIdsToDelete, tagsToUpdate },
} = event;
// Associate new tags with constraint definition version
const constraintDefinitionTagsToUpdate: ConstraintDefinitionTagsInsertInput[] = tagsToUpdate.map(
({ id: tag_id }) => ({
constraint_id: initialConstraintId as number,
constraint_revision: initialConstraintRevision as number,
tag_id,
}),
);
const constraintDefinitionTagsToUpdate: ConstraintDefinitionTagsInsertInput[] = tagsToUpdate.map(({ id }) => ({
constraint_id: initialConstraintId as number,
constraint_revision: initialConstraintRevision as number,
tag_id: id,
}));
await effects.updateConstraintDefinitionTags(
initialConstraintId,
initialConstraintRevision,
Expand Down
213 changes: 179 additions & 34 deletions src/components/constraints/ConstraintListItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<script lang="ts">
import { base } from '$app/paths';
import CheckmarkIcon from '@nasa-jpl/stellar/icons/check.svg?component';
import CaretDownFillIcon from 'bootstrap-icons/icons/caret-down-fill.svg?component';
import CaretUpFillIcon from 'bootstrap-icons/icons/caret-up-fill.svg?component';
import FilterIcon from '@nasa-jpl/stellar/icons/filter.svg?component';
import VisibleHideIcon from '@nasa-jpl/stellar/icons/visible_hide.svg?component';
import VisibleShowIcon from '@nasa-jpl/stellar/icons/visible_show.svg?component';
Expand Down Expand Up @@ -48,15 +50,22 @@
}>();
let formParameters: FormParameter[] = [];
let order: number;
let orderInput: HTMLInputElement;
let revisions: number[] = [];
let upButtonHidden: boolean = false;
let version: Pick<ConstraintDefinition, 'type' | 'revision' | 'parameter_schema'> | undefined = undefined;
$: revisions = constraint.versions.map(({ revision }) => revision);
$: violationCount = constraintResponse?.results?.violations?.length;
$: success = constraintResponse?.success;
$: {
order = constraintPlanSpec.order;
upButtonHidden = order <= 0;
}
$: {
if (constraintPlanSpec.constraint_revision !== null) {
version = constraint.versions.find(version => version.revision === constraintPlanSpec.constraint_revision);
version = constraint.versions.find(v => v.revision === constraintPlanSpec.constraint_revision);
} else {
// if the `constraint_revision` is null, that means to use the latest version of the definition
// the query for this constraint returns the versions in descending order, so the first entry in the array should correspond to the latest version
Expand All @@ -78,6 +87,55 @@
}
}
function focusInput() {
if (document.activeElement !== orderInput) {
orderInput?.focus();
}
return true;
}
function updateOrder(orderUpdate: number) {
dispatch('updateConstraintPlanSpec', {
...constraintPlanSpec,
order: orderUpdate,
});
}
function onKeyDown(e: KeyboardEvent) {
if (['ArrowUp', 'ArrowDown'].includes(e.key)) {
e.preventDefault();
e.stopPropagation();
if (e.key === 'ArrowUp') {
if (order > 0) {
updateOrder(order - 1);
}
} else {
updateOrder(order + 1);
}
}
}
function onDecreaseOrder() {
if (order !== undefined) {
focusInput();
updateOrder(order + 1);
}
}
function onIncreaseOrder() {
if (order !== undefined) {
focusInput();
updateOrder(order - 1);
}
}
function onUpdateOrder() {
if (order !== undefined) {
updateOrder(order);
}
}
function onDuplicateConstraintInvocation() {
dispatch('duplicateConstraintInvocation', {
...constraintPlanSpec,
Expand Down Expand Up @@ -115,7 +173,11 @@
</script>

<div class="constraint-list-item">
<Collapse title={constraint.name} tooltipContent={constraint.name} defaultExpanded={false}>
<Collapse
title={`${constraint.name}${constraintPlanSpec.order}`}
tooltipContent={constraint.name}
defaultExpanded={false}
>
<svelte:fragment slot="left">
<div class="left-content">
<input
Expand All @@ -136,7 +198,7 @@
</div>
</svelte:fragment>
<svelte:fragment slot="right">
<div class="right-content">
<div class="right-content" role="none" on:click|stopPropagation>
{#if violationCount}
<div
class="st-badge violation-badge"
Expand All @@ -161,38 +223,73 @@
<StatusBadge status={Status.Unchecked} />
</span>
{/if}
{constraintPlanSpec.invocation_id}
<button
use:tooltip={{ content: visible ? 'Hide' : 'Show', placement: 'top' }}
class="st-button icon"
on:click|stopPropagation={() =>
dispatch('toggleVisibility', {
constraintId: constraintPlanSpec.constraint_id,
invocationId: constraintPlanSpec.invocation_id,
visible: !visible,
})}
>
{#if visible}
<VisibleShowIcon />
{:else}
<VisibleHideIcon />
<div class="order-container">
<input
bind:this={orderInput}
bind:value={order}
class="st-input"
min="0"
style:width="68px"
type="number"
on:change={onUpdateOrder}
on:keydown={onKeyDown}
use:permissionHandler={{
hasPermission: hasEditPermission,
permissionError: editPermissionError,
}}
/>
{#if hasEditPermission}
<div class="order-buttons">
<button
use:tooltip={{ content: 'Increase order', placement: 'top' }}
class="st-button tertiary up-button"
class:hidden={upButtonHidden}
tabindex={upButtonHidden ? -1 : 0}
on:click={onIncreaseOrder}
>
<CaretUpFillIcon />
</button>
<button
use:tooltip={{ content: 'Decrease order', placement: 'top' }}
class="st-button tertiary down-button"
on:click={onDecreaseOrder}
>
<CaretDownFillIcon />
</button>
</div>
{/if}
</button>
<select
class="st-select"
value={constraintPlanSpec.constraint_revision}
on:change={onUpdateRevision}
on:click|stopPropagation
use:permissionHandler={{
hasPermission: hasEditPermission,
permissionError: editPermissionError,
}}
>
<option value={null}>Always use latest</option>
{#each revisions as revision, index}
<option value={revision}>{revision}{index === 0 ? ' (Latest)' : ''}</option>
{/each}
</select>
<button
use:tooltip={{ content: visible ? 'Hide' : 'Show', placement: 'top' }}
class="st-button icon"
on:click|stopPropagation={() =>
dispatch('toggleVisibility', {
constraintId: constraintPlanSpec.constraint_id,
invocationId: constraintPlanSpec.invocation_id,
visible: !visible,
})}
>
{#if visible}
<VisibleShowIcon />
{:else}
<VisibleHideIcon />
{/if}
</button>
<select
class="st-select"
value={constraintPlanSpec.constraint_revision}
on:change={onUpdateRevision}
on:click|stopPropagation
use:permissionHandler={{
hasPermission: hasEditPermission,
permissionError: editPermissionError,
}}
>
<option value={null}>Always use latest</option>
{#each revisions as revision, index}
<option value={revision}>{revision}{index === 0 ? ' (Latest)' : ''}</option>
{/each}
</select>
</div>
</div>
</svelte:fragment>

Expand Down Expand Up @@ -340,4 +437,52 @@
justify-content: center;
width: 20px;
}
.order-container {
display: flex;
}
/* Hide number input "spinners" (up and down arrows) in WebKit browsers ... */
.order-container input::-webkit-outer-spin-button,
.order-container input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* ... and Firefox */
.order-container input[type='number'] {
-moz-appearance: textfield;
appearance: textfield;
padding-right: 32px;
}
.order-buttons {
align-items: center;
display: flex;
margin-left: -36px;
}
.order-buttons :global(button) {
align-items: center;
color: var(--st-gray-40);
cursor: pointer;
display: flex;
min-width: 0;
padding: 0;
pointer-events: painted;
}
.order-buttons :global(button):hover {
background-color: transparent !important;
color: var(--st-gray-60);
}
.down-button {
margin-left: -3px;
margin-right: 2px;
}
.hidden {
opacity: 0;
pointer-events: none;
}
</style>
27 changes: 14 additions & 13 deletions src/components/constraints/ConstraintsPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
constraintsMap,
constraintsStatus,
initialConstraintPlanSpecsLoading,
initialConstraintRunsLoading,
initialConstraintsLoading,
setAllConstraintsVisible,
setConstraintVisibility,
Expand Down Expand Up @@ -137,19 +136,19 @@
$: totalViolationCount = getViolationCount($constraintResponses);
$: filteredViolationCount = getViolationCount(
filteredConstraintPlanSpecifications.map(({ constraint_id, invocation_id }) => {
return constraintToConstraintResponseMap[constraint_id]?.[invocation_id];
filteredConstraintPlanSpecifications.map(({ constraint_id: constraintId, invocation_id: invocationId }) => {
return constraintToConstraintResponseMap[constraintId]?.[invocationId];
}),
);
function filterConstraints(
planSpecs: ConstraintPlanSpecification[],
constraintToConstraintResponseMap: ConstraintInvocationMap<ConstraintResponse>,
filterText: string,
showConstraintsWithNoViolations: boolean,
constraintInvocationToConstraintResponseMap: ConstraintInvocationMap<ConstraintResponse>,
filter: string,
shouldShowConstraintsWithNoViolations: boolean,
) {
return planSpecs.filter(constraintPlanSpec => {
const filterTextLowerCase = filterText.toLowerCase();
const filterTextLowerCase = filter.toLowerCase();
const includesName = constraintPlanSpec.constraint_metadata?.name
.toLocaleLowerCase()
.includes(filterTextLowerCase);
Expand All @@ -158,20 +157,22 @@
}
const constraintResponse =
constraintToConstraintResponseMap[constraintPlanSpec.constraint_id]?.[constraintPlanSpec.invocation_id];
constraintInvocationToConstraintResponseMap[constraintPlanSpec.constraint_id]?.[
constraintPlanSpec.invocation_id
];
// Always show constraints with no violations
if (!constraintResponse?.results.violations?.length) {
return showConstraintsWithNoViolations;
return shouldShowConstraintsWithNoViolations;
}
return true;
});
}
function getViolationCount(constraintResponse: ConstraintResponse[]) {
return constraintResponse.reduce((count, constraintResponse) => {
return constraintResponse?.results.violations
? constraintResponse.results.violations.filter(violation => violation.windows.length > 0).length + count
return constraintResponse.reduce((count, response) => {
return response?.results.violations
? response.results.violations.filter(violation => violation.windows.length > 0).length + count
: count;
}, 0);
}
Expand Down Expand Up @@ -378,7 +379,7 @@
</CollapsibleListControls>

<div class="pt-2">
{#if $initialConstraintsLoading || $initialConstraintPlanSpecsLoading || $initialConstraintRunsLoading}
{#if $initialConstraintsLoading || $initialConstraintPlanSpecsLoading}
<div class="p-1">
<Loading />
</div>
Expand Down
12 changes: 6 additions & 6 deletions src/components/menus/Menu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
export function hideAllMenus(type?: MenuType) {
if (type) {
hideFns[type].forEach(hide => {
hide();
hideFns[type].forEach(hideFn => {
hideFn();
});
} else {
hideFns.dropdown.forEach(hide => {
hide();
hideFns.dropdown.forEach(hideFn => {
hideFn();
});
hideFns.input.forEach(hide => {
hide();
hideFns.input.forEach(hideFn => {
hideFn();
});
}
}
Expand Down
Loading

0 comments on commit b5d4e11

Please sign in to comment.