Skip to content

Commit

Permalink
Merge pull request #1884 from threefoldtech/development_improve_node_…
Browse files Browse the repository at this point in the history
…selector_details

Improve node selector details
  • Loading branch information
xmonader authored Jan 10, 2024
2 parents f88a06b + 612e9db commit aac2542
Show file tree
Hide file tree
Showing 5 changed files with 409 additions and 104 deletions.
2 changes: 2 additions & 0 deletions packages/grid_client/src/primitives/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ interface NodeInfo {
publicConfig: PublicConfig;
status: string;
certificationType: string;
dedicated: boolean;
hasGPU: boolean;
extraFee: number;
rentedByTwinId: number;
rentContractId: number;
serialNumber?: string;
}
interface PublicConfig {
domain: string;
Expand Down
208 changes: 134 additions & 74 deletions packages/playground/src/components/node_selector/TfAutoNodeSelector.vue
Original file line number Diff line number Diff line change
@@ -1,93 +1,127 @@
<template>
<section>
<VFadeTransition>
<VAlert type="warning" class="mb-4 mt-1" v-if="filtersUpdated && validFilters">
Please press on <strong>Load Nodes</strong> button to list nodes matching your new requirements.
</VAlert>
</VFadeTransition>

<input-tooltip tooltip="Select a node ID to deploy on.">
<div class="d-flex w-100">
<VAutocomplete
ref="nodeInput"
label="Node"
placeholder="Select node"
:items="loadedNodes"
item-title="nodeId"
return-object
:model-value="nodeInputValidateTask.initialized ? $props.modelValue : undefined"
@update:model-value="bindModelValueAndValidate($event)"
<VBtn
variant="tonal"
color="secondary"
class="mb-4"
size="x-large"
block
@click="resetPageAndReloadNodes()"
:loading="pageCountTask.loading || nodesTask.loading"
:disabled="nodeInputValidateTask.loading || !validFilters"
>
Load Nodes
</VBtn>

<input-tooltip tooltip="Select a node ID to deploy on." align-center>
<div class="w-100" :style="{ position: 'relative' }">
<VProgressLinear
v-if="loadedNodes.length > 0 && (pageCountTask.loading || nodesTask.loading)"
indeterminate
:style="{
position: 'absolute',
top: 0,
left: 0,
zIndex: 9,
transform: 'none',
width: 'calc(100% - 16px)',
}"
height="2px"
color="primary"
/>

<VCard
flat
class="mb-4 border"
:disabled="!validFilters || filtersUpdated"
:loading="nodeInputValidateTask.loading"
required
:hint="
!validFilters
? 'Please provide valid data.'
: nodeInputValidateTask.loading
? `Checking if the disks will fit in the node's storage pools...`
: undefined
"
:persistent-hint="!validFilters || nodeInputValidateTask.loading"
clearable
@click:clear="
bindModelValueAndValidate();
($refs.nodeInput as VInput)?.$el.querySelector('input').blur();
nodeInputValidateTask.reset();
touched = false;
"
:error="!!nodeInputValidateTask.error && !filtersUpdated"
:error-messages="!filtersUpdated ? nodeInputValidateTask.error || undefined : undefined"
@blur="
touched
? undefined
: (() => {
touched = true;
!nodeInputValidateTask.initialized && nodeInputValidateTask.run($props.modelValue);
})()
"
:rules="[() => true]"
:style="{
opacity: !validFilters || filtersUpdated ? 0.5 : 1,
borderWidth: '2px !important',
borderColor: nodeInputValidateTask.error ? 'rgba(var(--v-theme-error), 0.4) !important' : undefined,
}"
>
<template #item="{ item, props }">
<VListItem v-bind="props">
<template #title> {{ item.value.nodeId }} </template>
<template #append>
<VChip class="mr-2" v-if="item.value.certificationType.toLowerCase() === 'certified'" color="primary">
Certified
</VChip>
<VChip :color="item.value.rentedByTwinId === 0 ? 'secondary' : 'success'">
{{ item.value.rentedByTwinId === 0 ? "Shared" : "Dedicated" }}
</VChip>
</template>
</VListItem>
</template>
<VContainer v-if="loadedNodes.length === 0 && (pageCountTask.loading || nodesTask.loading)">
<VRow align="center" justify="center" class="pa-4">
<VProgressCircular color="primary" indeterminate class="mr-2" /> Loading Nodes...
</VRow>
</VContainer>

<VContainer v-if="loadedNodes.length === 0 && !(pageCountTask.loading || nodesTask.loading)">
<VAlert type="error" text="No Nodes were found!" />
</VContainer>

<template #append-item v-if="pagination.page !== -1">
<VContainer>
<div
ref="nodesContainer"
:style="{ maxHeight: '450px', paddingBottom: '100px', backgroundColor: 'rgb(var(--v-theme-background))' }"
class="overflow-auto px-4"
v-if="loadedNodes.length"
>
<template v-for="node in loadedNodes" :key="node.id">
<div class="my-4">
<TfNodeDetailsCard
:node="node"
:selected="!validFilters || filtersUpdated ? false : $props.modelValue === node"
selectable
@node:select="bindModelValueAndValidate"
:status="
$props.modelValue === node
? nodeInputValidateTask.loading
? 'Pending'
: nodeInputValidateTask.data
? 'Valid'
: 'Invalid'
: 'Init'
"
/>
</div>
<!-- <div class="border-b" :style="{ borderBottomWidth: '2px !important' }" /> -->
</template>

<VContainer v-if="loadedNodes.length > 0 && pagination.page !== -1">
<VBtn
@click="reloadNodes()"
block
color="secondary"
variant="tonal"
size="large"
:loading="nodesTask.loading"
prepend-icon="mdi-reload"
:disabled="nodeInputValidateTask.loading"
>
Load More Nodes
</VBtn>
</VContainer>
</template>
</VAutocomplete>

<VBtn
variant="outlined"
color="secondary"
class="mt-2 ml-2"
@click="resetPageAndReloadNodes()"
:loading="pageCountTask.loading || nodesTask.loading"
:disabled="nodeInputValidateTask.loading || !validFilters"
</div>
</VCard>

<VAlert
:type="!validFilters ? 'error' : 'warning'"
variant="elevated"
:style="{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', zIndex: 9 }"
v-if="!validFilters || (filtersUpdated && validFilters)"
>
Load Nodes
</VBtn>
<span v-if="!validFilters" v-text="'Please provide valid data.'" />
<template v-else>
Please press on <strong>Load Nodes</strong> button to list nodes matching your new requirements.
</template>
</VAlert>

<VAlert
type="info"
variant="elevated"
:style="{ position: 'absolute', bottom: '31px', right: '31px', zIndex: 9 }"
v-else-if="nodeInputValidateTask.loading"
text="Checking if the deployment will fit in the node's disks..."
/>

<VAlert
type="error"
variant="elevated"
v-if="!filtersUpdated && nodeInputValidateTask.error"
:style="{ position: 'absolute', bottom: '31px', right: '31px', zIndex: 9 }"
:text="nodeInputValidateTask.error"
closable
/>
</div>
</input-tooltip>
</section>
Expand All @@ -99,7 +133,7 @@ import equals from "lodash/fp/equals.js";
import sample from "lodash/fp/sample.js";
import { computed, nextTick, onMounted, onUnmounted, type PropType, ref } from "vue";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { VInput } from "vuetify/components/VInput";
import type { VCard } from "vuetify/components/VCard";
import { useAsync, usePagination, useWatchDeep } from "../../hooks";
import { ValidatorStatus } from "../../hooks/form_validator";
Expand All @@ -112,9 +146,11 @@ import {
normalizeNodeFilters,
normalizeNodeOptions,
} from "../../utils/nodeSelector";
import TfNodeDetailsCard from "./TfNodeDetailsCard.vue";
export default {
name: "TfAutoNodeSelector",
components: { TfNodeDetailsCard },
props: {
modelValue: Object as PropType<NodeInfo>,
validFilters: { type: Boolean, required: true },
Expand Down Expand Up @@ -213,7 +249,21 @@ export default {
tries: 1,
shouldRun: () => props.validFilters,
onBeforeTask: () => bindStatus(ValidatorStatus.Pending),
onAfterTask: ({ data }) => bindStatus(data ? ValidatorStatus.Valid : ValidatorStatus.Invalid),
onAfterTask({ data }) {
bindStatus(data ? ValidatorStatus.Valid : ValidatorStatus.Invalid);
const container = nodesContainer.value as HTMLDivElement;
if (container) {
const card = container.querySelector(".selected-node") as HTMLDivElement;
if (card && container.getAttribute("data-scrolled") !== "scrolled") {
container.setAttribute("data-scrolled", "scrolled");
container.scroll({
behavior: "smooth",
top: card.offsetTop - 100,
});
}
}
},
onReset: bindStatus,
},
);
Expand All @@ -238,6 +288,8 @@ export default {
ctx.emit("update:status", status || ValidatorStatus.Init);
}
const nodesContainer = ref<HTMLDivElement>();
return {
pageCountTask,
nodesTask,
Expand All @@ -252,7 +304,15 @@ export default {
touched,
bindModelValueAndValidate,
bindStatus,
nodesContainer,
};
},
};
</script>

<style>
.node-selector .v-select__selection {
width: 100%;
}
</style>
Loading

0 comments on commit aac2542

Please sign in to comment.