Skip to content

Commit

Permalink
users can manage approval requirements for views!~
Browse files Browse the repository at this point in the history
Co-authored-by: Nick Gerace <nick@systeminit.com>
Co-authored-by: Jacob Helwig <jacob@systeminit.com>
  • Loading branch information
3 people committed Jan 28, 2025
1 parent 4f6eaeb commit 94417b4
Show file tree
Hide file tree
Showing 28 changed files with 1,080 additions and 95 deletions.
10 changes: 10 additions & 0 deletions app/web/src/api/sdf/dal/property_editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export interface PropertyEditorPropWidgetKindArray {
kind: "array";
}

export interface PropertyEditorpropWidgetKindRequirement {
kind: "requirement";
}

export interface PropertyEditorPropWidgetKindUsers {
kind: "users";
}

export interface PropertyEditorPropWidgetKindCheckBox {
kind: "checkbox";
}
Expand Down Expand Up @@ -85,6 +93,8 @@ export type PropertyEditorPropWidgetKind =
| PropertyEditorPropWidgetKindInteger
| PropertyEditorPropWidgetKindHeader
| PropertyEditorPropWidgetKindArray
| PropertyEditorpropWidgetKindRequirement
| PropertyEditorPropWidgetKindUsers
| PropertyEditorPropWidgetKindCodeEditor
| PropertyEditorPropWidgetKindComboBox
| PropertyEditorPropWidgetKindSelect
Expand Down
11 changes: 11 additions & 0 deletions app/web/src/api/sdf/dal/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
DiagramViewData,
SocketLocationInfo,
} from "@/components/ModelingDiagram/diagram_types";
import { UserId } from "@/store/auth.store";
import { ComponentType } from "./schema";

export type ViewId = string;
Expand Down Expand Up @@ -39,3 +40,13 @@ export interface StringGeometry {
width: string;
height: string;
}

// Approval Requirement Definition types
export type ApprovalRequirementDefinitionId = string;
export interface ViewApprovalRequirementDefinition {
id: ApprovalRequirementDefinitionId;
entityId: ViewId;
requiredCount: number;
approverGroups: Record<string, UserId[]>;
approverIndividuals: UserId[];
}
2 changes: 1 addition & 1 deletion app/web/src/components/Actions/ActionsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</div>
</div>
<div class="flex-none font-bold">
{{ displayActions.length }} Actions
{{ displayActions.length }} Action(s)
</div>
<!-- TODO(Wendy) - maybe a PillCounter makes more sense here? -->
<!-- <PillCounter
Expand Down
139 changes: 122 additions & 17 deletions app/web/src/components/AttributesPanel/TreeFormItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,28 @@
)
"
>
<template v-if="treeDef.children.length === 0">(empty)</template>
<template v-else-if="treeDef.children.length === 1"
>(1 item)</template
>
<template v-else>({{ treeDef.children.length }} items)</template>
<template v-if="widgetKind === 'users'">
<template v-if="treeDef.children.length === 0"
>(empty)</template
>
<template v-else-if="treeDef.children.length === 1"
>(1 user)</template
>
<template v-else
>({{ treeDef.children.length }} users)</template
>
</template>
<template v-else>
<template v-if="treeDef.children.length === 0"
>(empty)</template
>
<template v-else-if="treeDef.children.length === 1"
>(1 item)</template
>
<template v-else
>({{ treeDef.children.length }} items)</template
>
</template>
</div>
</div>
<SourceIconWithTooltip
Expand Down Expand Up @@ -205,6 +222,21 @@
v-show="isOpen && headerHasContent"
class="attributes-panel-item__children"
>
<div
v-if="widgetKind === 'users' && isArray && propManual"
:style="{ marginLeft: indentPx }"
class="flex flex-col grow gap-xs relative pt-2xs px-xs"
>
<div class="text-xs">Add an approver user for this requirement -</div>
<UserSelectMenu
ref="userSelectMenuRef"
class="flex-none"
noUsersLabel="All users are approvers!"
:usersToFilterOut="usersToFilterOut"
@select="addUser"
/>
</div>

<TreeFormItem
v-for="childProp in treeDef.children"
:key="`${propName}/${childProp.propDef?.name}`"
Expand All @@ -229,8 +261,27 @@
</div>
</div>

<template v-if="(isArray || isMap) && propManual">
<template
v-if="
(isArray || isMap || widgetKind === 'requirement') && propManual
"
>
<div
v-if="widgetKind === 'requirement'"
:style="{ marginLeft: indentPx }"
class="flex flex-row grow relative overflow-hidden items-center justify-center pt-xs"
>
<VButton
label="Delete Requirement"
tone="destructive"
variant="ghost"
icon="trash"
size="sm"
@click="deleteRequirement"
/>
</div>
<div
v-else-if="widgetKind !== 'users'"
:style="{ marginLeft: indentPx }"
class="h-[34px] flex flex-row grow gap-xs relative overflow-hidden items-center pt-2xs"
>
Expand Down Expand Up @@ -455,6 +506,7 @@
</div>
<!-- Actual input, to the right -->
<div
v-if="widgetKind !== 'users'"
:class="
clsx(
'attributes-panel-item__input-wrap group/input',
Expand Down Expand Up @@ -726,6 +778,21 @@
@click="openNonEditableModal"
/>
</div>
<!-- users widget is just a delete button -->
<IconButton
v-else
icon="trash"
iconTone="destructive"
iconIdleTone="shade"
:tooltip="
treeDef.propDef.isReadonly
? 'Can\'t Remove Only Approver!'
: 'Remove Approver'
"
size="xs"
:disabled="treeDef.propDef.isReadonly"
@click="() => removeUser(treeDef.propId)"
/>
</div>

<!-- VALIDATION DETAILS -->
Expand Down Expand Up @@ -938,6 +1005,7 @@ import SecretsModal from "../SecretsModal.vue";
import SourceIconWithTooltip from "./SourceIconWithTooltip.vue";
import CodeViewer from "../CodeViewer.vue";
import { TreeFormContext } from "./TreeForm.vue";
import UserSelectMenu from "../UserSelectMenu.vue";
export type TreeFormProp = {
id: string;
Expand Down Expand Up @@ -1052,7 +1120,13 @@ const viewsStore = useViewsStore();
const componentId = viewsStore.selectedComponentId!;
const changeSetsStore = useChangeSetsStore();
const attributesStore = useComponentAttributesStore(componentId);
const attributesStore = computed(() => {
if (props.attributesPanel) {
return useComponentAttributesStore(componentId);
} else {
return undefined;
}
});
const secretsStore = useSecretsStore();
const fullPropDef = computed(() => props.treeDef.propDef);
Expand Down Expand Up @@ -1239,10 +1313,12 @@ const propSource = computed<AttributeValueSource>(() => {
});
const setSource = (source: AttributeValueSource) => {
if (!attributesStore.value) return;
if (source === AttributeValueSource.Manual) {
const value = props.treeDef.value?.value ?? null;
attributesStore.UPDATE_PROPERTY_VALUE({
attributesStore.value.UPDATE_PROPERTY_VALUE({
update: {
attributeValueId: props.treeDef.valueId,
parentAttributeValueId: props.treeDef.parentValueId,
Expand All @@ -1253,7 +1329,7 @@ const setSource = (source: AttributeValueSource) => {
},
});
} else {
attributesStore.RESET_PROPERTY_VALUE({
attributesStore.value.RESET_PROPERTY_VALUE({
attributeValueId: props.treeDef.valueId,
});
}
Expand Down Expand Up @@ -1334,8 +1410,8 @@ const newMapChildKeyIsValid = computed(() => {
function removeChildHandler() {
if (!isChildOfArray.value && !isChildOfMap.value) return;
if (props.attributesPanel) {
attributesStore.REMOVE_PROPERTY_VALUE({
if (props.attributesPanel && attributesStore.value) {
attributesStore.value.REMOVE_PROPERTY_VALUE({
attributeValueId: props.treeDef.valueId,
propId: props.treeDef.propId,
componentId,
Expand Down Expand Up @@ -1371,8 +1447,8 @@ function addChildHandler() {
return;
}
if (props.attributesPanel) {
attributesStore.UPDATE_PROPERTY_VALUE({
if (props.attributesPanel && attributesStore.value) {
attributesStore.value.UPDATE_PROPERTY_VALUE({
insert: {
parentAttributeValueId: props.treeDef.valueId,
propId: props.treeDef.propId,
Expand All @@ -1391,8 +1467,8 @@ function unsetHandler(value?: string) {
newValueBoolean.value = false;
newValueString.value = "";
if (props.attributesPanel) {
attributesStore.RESET_PROPERTY_VALUE({
if (props.attributesPanel && attributesStore.value) {
attributesStore.value.RESET_PROPERTY_VALUE({
attributeValueId: props.treeDef.valueId,
});
} else {
Expand Down Expand Up @@ -1445,8 +1521,8 @@ function updateValue(maybeNewVal?: unknown) {
isForSecret = true;
}
if (props.attributesPanel) {
attributesStore.UPDATE_PROPERTY_VALUE({
if (props.attributesPanel && attributesStore.value) {
attributesStore.value.UPDATE_PROPERTY_VALUE({
update: {
attributeValueId: props.treeDef.valueId,
parentAttributeValueId: props.treeDef.parentValueId,
Expand Down Expand Up @@ -1703,6 +1779,35 @@ const socketSearchFilters = computed(() => {
return filters;
});
// APPROVAL REQUIREMENTS STUFF
const usersToFilterOut = computed(() => {
if (props.treeDef.propDef.widgetKind.kind === "users") {
const users = props.treeDef.children.map((user) => user.propId);
return users;
} else return undefined;
});
const userSelectMenuRef = ref<InstanceType<typeof UserSelectMenu>>();
const addUser = async (userId: string) => {
const requirementId = props.treeDef.parentValueId;
await viewsStore.ADD_INDIVIDUAL_APPROVER_TO_REQUIREMENT(
requirementId,
userId,
);
userSelectMenuRef.value?.clearSelection();
};
const removeUser = (userId: string) => {
const requirementId = props.treeDef.parentValueId;
viewsStore.REMOVE_INDIVIDUAL_APPROVER_FROM_REQUIREMENT(requirementId, userId);
};
const deleteRequirement = () => {
const requirementId = props.treeDef.propId;
viewsStore.REMOVE_VIEW_APPROVAL_REQUIREMENT(requirementId);
};
</script>

<style lang="less">
Expand Down
18 changes: 9 additions & 9 deletions app/web/src/components/ChangesPanelProposed.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div>
<div class="h-full flex flex-col overflow-hidden">
<ConfirmHoldModal ref="confirmRef" :ok="finishHold" />
<div v-if="actionsStore.proposedActions.length > 0">
<div v-if="actionsStore.proposedActions.length > 0 || true">
<!-- TODO(Wendy)- SEARCH BAR SHOULD GO HERE -->
<div class="flex flex-row place-content-center">
<VButton
Expand Down Expand Up @@ -34,7 +34,7 @@
:open="!!singleSelectedAction"
:selectedTab="selectedTab"
/>
<div v-if="changeSetStore.headSelected">
<template v-if="changeSetStore.headSelected">
<ActionsList
v-if="actionsStore.proposedActions.length > 0"
:clickAction="clickAction"
Expand All @@ -47,8 +47,8 @@
primaryText="All Actions on HEAD have been run"
secondaryText="You can see those actions in the history tab."
/>
</div>
<div v-else>
</template>
<template v-else>
<TreeNode
enableDefaultHoverClasses
enableGroupToggle
Expand All @@ -57,7 +57,7 @@
leftBorderSize="none"
defaultOpen
internalScrolling
class="min-h-[50vh]"
class="min-h-[32px]"
primaryIconClasses=""
label="Proposed Actions In This Change Set"
>
Expand Down Expand Up @@ -99,11 +99,11 @@
<EmptyStateCard
v-else
iconName="actions"
primaryText="All Actions on HEAD have been run"
secondaryText="You can see those actions in the history tab."
primaryText="All Actions on HEAD have run"
secondaryText="See past actions in the history tab."
/>
</TreeNode>
</div>
</template>
</div>
</template>

Expand Down
2 changes: 1 addition & 1 deletion app/web/src/components/ComponentCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
:class="
clsx(
'p-xs border-l-4 border relative',
titleCard ? 'mb-xs' : 'rounded-md',
!titleCard && 'rounded-md',
'toDelete' in component.def && component.def.toDelete && 'opacity-70',
'fromBaseChangeSet' in component.def &&
component.def.fromBaseChangeSet &&
Expand Down
2 changes: 1 addition & 1 deletion app/web/src/components/ComponentDetails.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<ScrollArea>
<template #top>
<ComponentCard :component="props.component" titleCard>
<ComponentCard :component="props.component" titleCard class="mb-xs">
<DetailsPanelMenuIcon
:selected="props.menuSelected"
@click="
Expand Down
Loading

0 comments on commit 94417b4

Please sign in to comment.