Skip to content

Commit

Permalink
Merge pull request #4115 from systeminit/jobelenus/component-actions-…
Browse files Browse the repository at this point in the history
…from-consitutient-parts

Create the component actions panel from its constituent parts of sche…
  • Loading branch information
jobelenus authored Jul 8, 2024
2 parents e5f7008 + f48ecc9 commit 7aa7fed
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 127 deletions.
45 changes: 23 additions & 22 deletions app/web/src/components/Actions/ActionWidget.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
v-if="action"
v-if="binding"
class="cursor-pointer"
:class="
clsx(
Expand All @@ -10,9 +10,9 @@
"
@click="clickHandler"
>
<Toggle :selected="isActive" :click="() => {}" />
<StatusIndicatorIcon type="action" :status="action?.name" tone="inherit" />
<div class="font-bold">{{ action?.displayName }}</div>
<Toggle :selected="!!action" :click="() => {}" />
<StatusIndicatorIcon type="action" :status="binding.kind" tone="inherit" />
<div class="font-bold">{{ binding.displayName }}</div>

<Icon
v-if="addRequestStatus.isPending || removeRequestStatus.isPending"
Expand All @@ -29,47 +29,48 @@ import clsx from "clsx";
import { PropType, computed } from "vue";
import { Icon, themeClasses } from "@si/vue-lib/design-system";
import { useActionsStore } from "@/store/actions.store";
import { ActionPrototypeId } from "@/api/sdf/dal/action";
import { ComponentId } from "@/api/sdf/dal/component";
import { Action } from "@/api/sdf/dal/func";
import StatusIndicatorIcon from "../StatusIndicatorIcon.vue";
import Toggle from "./Toggle.vue";
interface BindingWithDisplayName extends Action {
displayName: string;
}
const props = defineProps({
componentId: { type: String as PropType<ComponentId>, required: true },
actionPrototypeId: {
type: String as PropType<ActionPrototypeId>,
required: true,
},
binding: { type: Object as PropType<BindingWithDisplayName> },
});
const actionsStore = useActionsStore();
const action = computed(() => {
return _.find(
actionsStore.actionsByComponentId[props.componentId],
(a) => a.actionPrototypeId === props.actionPrototypeId,
);
const a = actionsStore.listActionsByComponentId
.get(props.componentId)
.find((a) => a.prototypeId === props.binding?.actionPrototypeId);
return a;
});
const isActive = computed(() => !!action.value?.actionInstanceId);
function clickHandler() {
if (!action.value) return;
if (action.value.actionInstanceId) {
actionsStore.CANCEL([action.value.actionInstanceId]);
} else {
actionsStore.ADD_ACTION(props.componentId, props.actionPrototypeId);
if (action.value?.id) {
actionsStore.CANCEL([action.value.id]);
} else if (props.binding?.actionPrototypeId) {
actionsStore.ADD_ACTION(
props.componentId,
props.binding?.actionPrototypeId,
);
}
}
const addRequestStatus = actionsStore.getRequestStatus(
"ADD_ACTION",
props.componentId,
props.actionPrototypeId,
props.binding?.actionPrototypeId,
);
const removeRequestStatus = actionsStore.getRequestStatus(
"CANCEL",
computed(() => action.value?.actionInstanceId),
computed(() => action.value?.id),
// ^ this won't accept [] which doesnt bode well
);
</script>
133 changes: 44 additions & 89 deletions app/web/src/components/AssetActionsDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,28 @@
@update:selectedTab="onTabSelected"
>
<TabGroupItem label="Select" slug="actions-selection">
<template v-if="actionsReqStatus.isPending">
Loading actions...</template
>
<template v-else-if="actionsReqStatus.isError">
<ErrorMessage :requestStatus="actionsReqStatus" />
</template>
<template
v-else-if="actionsReqStatus.isSuccess && selectedComponentActions"
<div
v-if="bindings.length === 0"
class="flex flex-col items-center pt-lg h-full w-full text-neutral-400"
>
<div class="w-64">
<EmptyStateIcon name="no-changes" />
</div>
<span class="text-xl">No Actions available</span>
</div>
<div v-else class="flex flex-col">
<div
v-if="selectedComponentActions.length === 0"
class="flex flex-col items-center pt-lg h-full w-full text-neutral-400"
class="text-sm text-neutral-700 dark:text-neutral-300 p-xs italic border-b dark:border-neutral-600"
>
<div class="w-64">
<EmptyStateIcon name="no-changes" />
</div>
<span class="text-xl">No Actions available</span>
</div>
<div v-else class="flex flex-col">
<div
class="text-sm text-neutral-700 dark:text-neutral-300 p-xs italic border-b dark:border-neutral-600"
>
The changes below will run when you click "Apply Changes".
</div>
<ActionWidget
v-for="action in selectedComponentActions"
:key="action.actionPrototypeId"
:componentId="componentId"
:actionPrototypeId="action.actionPrototypeId"
/>
The changes below will run when you click "Apply Changes".
</div>
</template>
<ActionWidget
v-for="action in bindings"
:key="action.actionPrototypeId || undefined"
:binding="action"
:componentId="props.componentId"
/>
</div>
</TabGroupItem>
<TabGroupItem slug="actions-history">
<template #label>
Expand All @@ -47,21 +37,6 @@
<!-- <PillCounter class="ml-2xs" :count="filteredBatches.length" /> -->
</Inline>
</template>

<div
v-if="filteredBatches.length === 0"
class="flex flex-col items-center pt-lg h-full w-full text-neutral-400"
>
<div class="w-64">
<EmptyStateIcon name="no-changes" />
</div>
<span class="text-xl">No actions history</span>
</div>
<ul v-else class="flex flex-col">
<li v-for="(actionBatch, index) in filteredBatches" :key="index">
<ApplyHistoryItem :actionBatch="actionBatch" :collapse="false" />
</li>
</ul>
</TabGroupItem>
</TabGroup>
</div>
Expand All @@ -71,58 +46,51 @@
import { computed, PropType, ref, watch } from "vue";
import * as _ from "lodash-es";
import {
ErrorMessage,
Inline,
TabGroup,
TabGroupItem,
} from "@si/vue-lib/design-system";
import { Inline, TabGroup, TabGroupItem } from "@si/vue-lib/design-system";
import { useComponentsStore } from "@/store/components.store";
import { ComponentId } from "@/api/sdf/dal/component";
import ApplyHistoryItem from "@/components/ApplyHistoryItem.vue";
import { useActionsStore } from "@/store/actions.store";
import { Action } from "@/api/sdf/dal/func";
import { useFuncStore } from "@/store/func/funcs.store";
import EmptyStateIcon from "@/components/EmptyStateIcon.vue";
import ActionWidget from "@/components/Actions/ActionWidget.vue";
import { useChangeSetsStore } from "@/store/change_sets.store";
const props = defineProps({
componentId: { type: String as PropType<ComponentId>, required: true },
});
const actionsStore = useActionsStore();
const funcStore = useFuncStore();
const componentsStore = useComponentsStore();
const changeSetsStore = useChangeSetsStore();
const actionBatches = computed(() =>
_.reverse([...actionsStore.actionBatches]),
);
const filteredBatches = computed(() =>
actionBatches.value
.map((batch) => ({
...batch,
actions: batch.actions.filter(
(action) => action.componentId === props.componentId,
),
}))
.filter((batch) => batch.actions.length),
);
const tabsRef = ref<InstanceType<typeof TabGroup>>();
function onTabSelected(newTabSlug?: string) {
componentsStore.setComponentDetailsTab(newTabSlug || null);
}
const actionsReqStatus = actionsStore.getRequestStatus(
"FETCH_COMPONENT_ACTIONS",
props.componentId,
);
interface BindingWithDisplayName extends Action {
displayName: string;
}
const selectedComponentActions = computed(
() => actionsStore.actionsByComponentId[props.componentId],
);
const bindings = computed(() => {
const _bindings = [] as BindingWithDisplayName[];
const variant =
componentsStore.schemaVariantsById[
componentsStore.selectedComponent?.schemaVariantId || ""
];
variant?.funcIds.forEach((funcId) => {
const summary = funcStore.funcsById[funcId];
const actions = funcStore.actionBindings[funcId];
if (actions && actions.length > 0) {
actions.forEach((b) => {
const a = b as BindingWithDisplayName;
a.displayName = summary?.displayName || "<Unknown>";
_bindings.push(a);
});
}
});
return _bindings;
});
watch(
() => componentsStore.selectedComponentDetailsTab,
Expand All @@ -132,17 +100,4 @@ watch(
}
},
);
watch(
[() => changeSetsStore.selectedChangeSetLastWrittenAt],
() => {
if (
componentsStore.selectedComponent &&
componentsStore.selectedComponent.changeStatus !== "deleted"
) {
actionsStore.FETCH_COMPONENT_ACTIONS(props.componentId);
}
},
{ immediate: true },
);
</script>
33 changes: 17 additions & 16 deletions app/web/src/store/actions.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,6 @@ export const useActionsStore = () => {
actionHistory: [] as ActionHistoryView[],
}),
getters: {
actionBatches() {
return [] as Array<DeprecatedActionBatch>; // is this history?
},
actionsAreInProgress(): boolean {
return (this.countActionsByState?.[ActionState.Running] || 0) > 0;
},
Expand Down Expand Up @@ -231,6 +228,23 @@ export const useActionsStore = () => {
);
},

listActionsByComponentId(
state,
): DefaultMap<ComponentId, Array<ActionProposedView>> {
const d = new DefaultMap<ComponentId, Array<ActionProposedView>>(
() => [],
);
state.actions.forEach((a) => {
const componentId = a.componentId as ComponentId | null;
if (componentId) {
const arr = d.get(componentId);
arr.push(a);
d.set(componentId, arr);
}
});
return d;
},

actionsById(state): Map<ActionId, ActionView> {
const m = new Map();
for (const a of state.actions) {
Expand All @@ -255,19 +269,6 @@ export const useActionsStore = () => {
},
});
},
async FETCH_COMPONENT_ACTIONS(componentId: ComponentId) {
return new ApiRequest<{ actions: ActionPrototype[] }>({
url: "component/get_actions",
keyRequestStatusBy: componentId,
params: {
componentId,
visibility_change_set_pk: changeSetId,
},
onSuccess: (response) => {
this.rawActionsByComponentId[componentId] = response.actions;
},
});
},
// This is proposed/queued actions
async LOAD_ACTIONS() {
return new ApiRequest<Array<ActionProposedView>>({
Expand Down

0 comments on commit 7aa7fed

Please sign in to comment.