@rancher/shell 3.0.5-rc.3 → 3.0.5-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/images/icons/document.svg +3 -0
- package/assets/images/vendor/cognito.svg +1 -0
- package/assets/styles/app.scss +1 -0
- package/assets/styles/base/_basic.scss +10 -0
- package/assets/styles/base/_spacing.scss +29 -0
- package/assets/styles/global/_layout.scss +1 -1
- package/assets/styles/themes/_dark.scss +25 -0
- package/assets/styles/themes/_light.scss +65 -0
- package/assets/translations/en-us.yaml +322 -24
- package/assets/translations/zh-hans.yaml +8 -5
- package/components/Certificates.vue +5 -0
- package/components/FilterPanel.vue +156 -0
- package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
- package/components/IconOrSvg.vue +14 -35
- package/components/PromptRemove.vue +5 -1
- package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
- package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
- package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
- package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
- package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
- package/components/Resource/Detail/Card/Scaler.vue +89 -0
- package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
- package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
- package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
- package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
- package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
- package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
- package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
- package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
- package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
- package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
- package/components/Resource/Detail/Card/index.vue +56 -0
- package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
- package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
- package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
- package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
- package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
- package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
- package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
- package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
- package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
- package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
- package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
- package/components/Resource/Detail/Metadata/composables.ts +29 -0
- package/components/Resource/Detail/Metadata/index.vue +66 -0
- package/components/Resource/Detail/Page.vue +22 -0
- package/components/Resource/Detail/PercentageBar.vue +40 -0
- package/components/Resource/Detail/ResourceRow.vue +119 -0
- package/components/Resource/Detail/SpacedRow.vue +14 -0
- package/components/Resource/Detail/StatusBar.vue +59 -0
- package/components/Resource/Detail/StatusRow.vue +61 -0
- package/components/Resource/Detail/TitleBar/Title.vue +13 -0
- package/components/Resource/Detail/TitleBar/Top.vue +14 -0
- package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
- package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
- package/components/Resource/Detail/TitleBar/composable.ts +31 -0
- package/components/Resource/Detail/TitleBar/index.vue +124 -0
- package/components/Resource/Detail/Top/index.vue +34 -0
- package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
- package/components/ResourceDetail/__tests__/index.test.ts +114 -0
- package/components/ResourceDetail/index.vue +64 -562
- package/components/ResourceDetail/legacy.vue +545 -0
- package/components/ResourceTable.vue +41 -7
- package/components/SlideInPanelManager.vue +76 -8
- package/components/SortableTable/index.vue +13 -2
- package/components/SortableTable/selection.js +21 -8
- package/components/StatusBadge.vue +6 -4
- package/components/SubtleLink.vue +25 -0
- package/components/Wizard.vue +12 -1
- package/components/YamlEditor.vue +1 -1
- package/components/__tests__/FilterPanel.test.ts +81 -0
- package/components/auth/AuthBanner.vue +2 -3
- package/components/auth/RoleDetailEdit.vue +45 -3
- package/components/auth/login/oidc.vue +6 -1
- package/components/fleet/FleetApplications.vue +181 -0
- package/components/fleet/FleetHelmOps.vue +115 -0
- package/components/fleet/FleetIntro.vue +58 -28
- package/components/fleet/FleetNoWorkspaces.vue +5 -1
- package/components/fleet/FleetOCIStorageSecret.vue +171 -0
- package/components/fleet/FleetRepos.vue +38 -76
- package/components/fleet/FleetResources.vue +50 -22
- package/components/fleet/FleetSummary.vue +26 -51
- package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
- package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
- package/components/fleet/dashboard/Empty.vue +73 -0
- package/components/fleet/dashboard/ResourceCard.vue +183 -0
- package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
- package/components/fleet/dashboard/ResourceDetails.vue +196 -0
- package/components/fleet/dashboard/ResourcePanel.vue +376 -0
- package/components/form/ArrayList.vue +6 -0
- package/components/form/SimpleSecretSelector.vue +8 -2
- package/components/form/ValueFromResource.vue +31 -19
- package/components/formatter/FleetApplicationClustersReady.vue +77 -0
- package/components/formatter/FleetApplicationSource.vue +71 -0
- package/components/formatter/FleetSummaryGraph.vue +7 -0
- package/components/nav/Header.vue +8 -7
- package/components/nav/TopLevelMenu.helper.ts +55 -34
- package/components/nav/TopLevelMenu.vue +11 -0
- package/components/nav/Type.vue +4 -1
- package/composables/useI18n.ts +12 -11
- package/config/labels-annotations.js +14 -11
- package/config/product/auth.js +1 -0
- package/config/product/fleet.js +70 -17
- package/config/query-params.js +3 -1
- package/config/roles.ts +1 -0
- package/config/router/routes.js +20 -2
- package/config/secret.ts +15 -0
- package/config/settings.ts +3 -2
- package/config/table-headers.js +52 -22
- package/config/types.js +2 -0
- package/core/plugin-helpers.ts +3 -2
- package/detail/fleet.cattle.io.cluster.vue +28 -15
- package/detail/fleet.cattle.io.gitrepo.vue +10 -1
- package/detail/fleet.cattle.io.helmop.vue +157 -0
- package/dialog/HelmOpForceUpdateDialog.vue +132 -0
- package/dialog/RedeployWorkloadDialog.vue +164 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
- package/edit/auth/oidc.vue +159 -93
- package/edit/fleet.cattle.io.gitrepo.vue +26 -33
- package/edit/fleet.cattle.io.helmop.vue +997 -0
- package/edit/management.cattle.io.fleetworkspace.vue +43 -10
- package/list/fleet.cattle.io.gitrepo.vue +1 -1
- package/list/fleet.cattle.io.helmop.vue +108 -0
- package/list/namespace.vue +5 -2
- package/mixins/auth-config.js +8 -1
- package/mixins/preset.js +100 -0
- package/mixins/resource-fetch-api-pagination.js +2 -0
- package/mixins/resource-fetch.js +1 -1
- package/mixins/resource-table-watch.js +45 -0
- package/models/__tests__/chart.test.ts +273 -0
- package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
- package/models/chart.js +144 -2
- package/models/fleet-application.js +385 -0
- package/models/fleet.cattle.io.bundle.js +9 -8
- package/models/fleet.cattle.io.gitrepo.js +41 -365
- package/models/fleet.cattle.io.helmop.js +228 -0
- package/models/management.cattle.io.authconfig.js +1 -0
- package/models/management.cattle.io.fleetworkspace.js +12 -0
- package/models/workload.js +14 -18
- package/package.json +2 -1
- package/pages/auth/verify.vue +13 -1
- package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
- package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
- package/pages/c/_cluster/apps/charts/index.vue +302 -484
- package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
- package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
- package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
- package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
- package/pages/c/_cluster/fleet/application/create.vue +340 -0
- package/pages/c/_cluster/fleet/application/index.vue +139 -0
- package/pages/c/_cluster/fleet/graph/config.js +277 -0
- package/pages/c/_cluster/fleet/index.vue +772 -330
- package/pages/explorer/resource/detail/configmap.vue +19 -0
- package/plugins/dashboard-store/actions.js +31 -9
- package/plugins/dashboard-store/getters.js +34 -21
- package/plugins/dashboard-store/mutations.js +51 -7
- package/plugins/dashboard-store/resource-class.js +14 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
- package/plugins/steve/actions.js +3 -0
- package/plugins/steve/steve-pagination-utils.ts +14 -13
- package/plugins/steve/subscribe.js +229 -42
- package/rancher-components/BadgeState/BadgeState.vue +3 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
- package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
- package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
- package/rancher-components/RcItemCard/index.ts +2 -0
- package/store/auth.js +1 -0
- package/store/catalog.js +62 -24
- package/store/index.js +33 -14
- package/store/slideInPanel.ts +6 -0
- package/store/type-map.js +1 -0
- package/types/fleet.d.ts +35 -0
- package/types/resources/settings.d.ts +19 -1
- package/types/shell/index.d.ts +339 -272
- package/types/store/dashboard-store.types.ts +17 -3
- package/types/store/pagination.types.ts +6 -1
- package/types/store/subscribe.types.ts +50 -0
- package/utils/auth.js +32 -3
- package/utils/fleet-types.ts +0 -0
- package/utils/fleet.ts +200 -1
- package/utils/pagination-utils.ts +26 -1
- package/utils/pagination-wrapper.ts +132 -50
- package/utils/settings.ts +4 -1
- package/utils/style.ts +39 -0
- package/utils/validators/formRules/__tests__/index.test.ts +36 -3
- package/utils/validators/formRules/index.ts +10 -3
- package/utils/window.js +11 -7
- package/components/__tests__/ApplicationCard.test.ts +0 -27
- package/components/cards/ApplicationCard.vue +0 -145
- package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
- package/config/secret.js +0 -14
- package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
- /package/{components/form/SSHKnownHosts → dialog}/__tests__/KnownHostsEditDialog.test.ts +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { DefineComponent } from 'vue';
|
|
3
|
+
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generic type for the passed components as props
|
|
7
|
+
*/
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
type ComponentType = DefineComponent<any, any, any>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A single filter option for a group (e.g., a single checkbox).
|
|
13
|
+
*/
|
|
14
|
+
type FilterOption = {
|
|
15
|
+
/** Value to apply when selected */
|
|
16
|
+
value?: string;
|
|
17
|
+
/** Optional custom component to render for this option (e.g., link or button) */
|
|
18
|
+
component?: ComponentType;
|
|
19
|
+
/** Props passed to the custom component */
|
|
20
|
+
componentProps?: Record<string, unknown>;
|
|
21
|
+
/** Label to show next to the checkbox, or a custom component next to the checkbox */
|
|
22
|
+
label?: string | { component: ComponentType; componentProps: Record<string, unknown>; };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A group of filter options (e.g., Repositories, Categories).
|
|
27
|
+
*/
|
|
28
|
+
type FilterGroup = {
|
|
29
|
+
/** Key that maps to the filters object (e.g., 'repos', 'categories') */
|
|
30
|
+
key: string;
|
|
31
|
+
/** Title for the filter group */
|
|
32
|
+
title: string;
|
|
33
|
+
/** The available options within this group */
|
|
34
|
+
options: FilterOption[];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Props accepted by the filter panel.
|
|
39
|
+
*/
|
|
40
|
+
const props = defineProps<{
|
|
41
|
+
/** Two-way bound filters */
|
|
42
|
+
modelValue: Record<string, string[]>;
|
|
43
|
+
/** The list of filter groups to display */
|
|
44
|
+
filters: FilterGroup[];
|
|
45
|
+
}>();
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Event emitted by the component.
|
|
49
|
+
*/
|
|
50
|
+
const emit = defineEmits<{(e: 'update:modelValue', val: Record<string, string[]>): void;}>();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Handles updating the selected filters for a given filter group key.
|
|
54
|
+
*
|
|
55
|
+
* @param key - The key of the filter group being updated (e.g., 'tags').
|
|
56
|
+
* @param value - The updated list of selected values (e.g. ['monitoring', 'networking']).
|
|
57
|
+
*/
|
|
58
|
+
const updateFilter = (key: string, value: string[]) => {
|
|
59
|
+
const newValue = { ...props.modelValue, [key]: value };
|
|
60
|
+
|
|
61
|
+
emit('update:modelValue', newValue);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<div class="filter-panel">
|
|
68
|
+
<div
|
|
69
|
+
v-for="filter in filters"
|
|
70
|
+
:key="filter.key"
|
|
71
|
+
class="filter-panel-filter-group"
|
|
72
|
+
data-testid="filter-panel-filter-group"
|
|
73
|
+
>
|
|
74
|
+
<h4 class="filter-panel-filter-group-title">
|
|
75
|
+
{{ filter.title }}
|
|
76
|
+
</h4>
|
|
77
|
+
<div
|
|
78
|
+
v-for="(option, i) in filter.options"
|
|
79
|
+
:key="`${filter.key}-${i}`"
|
|
80
|
+
class="filter-panel-filter-option"
|
|
81
|
+
>
|
|
82
|
+
<template v-if="option.component">
|
|
83
|
+
<component
|
|
84
|
+
:is="option.component"
|
|
85
|
+
v-bind="option.componentProps"
|
|
86
|
+
data-testid="filter-panel-custom-component"
|
|
87
|
+
/>
|
|
88
|
+
</template>
|
|
89
|
+
<template v-else-if="option.label">
|
|
90
|
+
<Checkbox
|
|
91
|
+
:key="i"
|
|
92
|
+
class="filter-panel-filter-checkbox"
|
|
93
|
+
:label="typeof option.label === 'string' ? option.label : undefined"
|
|
94
|
+
:value="modelValue[filter.key]"
|
|
95
|
+
:value-when-true="option.value"
|
|
96
|
+
data-testid="filter-panel-filter-checkbox"
|
|
97
|
+
@update:value="updateFilter(filter.key, $event)"
|
|
98
|
+
>
|
|
99
|
+
<template #label>
|
|
100
|
+
<span v-if="typeof option.label === 'string'">{{ option.label }}</span>
|
|
101
|
+
<component
|
|
102
|
+
:is="option.label.component"
|
|
103
|
+
v-else
|
|
104
|
+
v-bind="option.label.componentProps"
|
|
105
|
+
/>
|
|
106
|
+
</template>
|
|
107
|
+
</Checkbox>
|
|
108
|
+
</template>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</template>
|
|
113
|
+
|
|
114
|
+
<style lang='scss' scoped>
|
|
115
|
+
.filter-panel {
|
|
116
|
+
display: flex;
|
|
117
|
+
min-width: 250px;
|
|
118
|
+
height: max-content;
|
|
119
|
+
padding: 16px;
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
align-items: flex-start;
|
|
122
|
+
gap: 24px;
|
|
123
|
+
border-radius: 6px;
|
|
124
|
+
background: var(--sortable-table-header-bg);
|
|
125
|
+
|
|
126
|
+
&-filter-group {
|
|
127
|
+
display: flex;
|
|
128
|
+
flex-direction: column;
|
|
129
|
+
gap: 6px;
|
|
130
|
+
width: 100%;
|
|
131
|
+
|
|
132
|
+
&-title {
|
|
133
|
+
margin-bottom: 6px;
|
|
134
|
+
font-size: 16px;
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
line-height: 23px;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
&-filter-option {
|
|
141
|
+
width: 100%;
|
|
142
|
+
|
|
143
|
+
.filter-panel-filter-checkbox {
|
|
144
|
+
|
|
145
|
+
.checkbox-label span {
|
|
146
|
+
max-width: 200px;
|
|
147
|
+
text-overflow: ellipsis;
|
|
148
|
+
white-space: nowrap;
|
|
149
|
+
overflow: hidden;
|
|
150
|
+
color: var(--link-text-secondary);
|
|
151
|
+
margin-left: 4px;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import * as d3 from 'd3';
|
|
3
3
|
import { STATES } from '@shell/plugins/dashboard-store/resource-class';
|
|
4
4
|
import { BadgeState } from '@components/BadgeState';
|
|
5
|
-
import { getChartIcon } from './chartIcons.js';
|
|
6
5
|
|
|
7
6
|
export default {
|
|
8
7
|
name: 'ForceDirectedTreeChart',
|
|
@@ -17,8 +16,24 @@ export default {
|
|
|
17
16
|
required: true
|
|
18
17
|
}
|
|
19
18
|
},
|
|
19
|
+
|
|
20
|
+
async fetch() {
|
|
21
|
+
this.canViewChart = await this.fdcConfig.checkSchemaPermissions(this.$store);
|
|
22
|
+
|
|
23
|
+
if (this.canViewChart) {
|
|
24
|
+
// set watcher for the chart data
|
|
25
|
+
this.dataWatcher = this.$watch(this.fdcConfig.watcherProp, (newValue) => {
|
|
26
|
+
this.watcherFunction(newValue);
|
|
27
|
+
}, {
|
|
28
|
+
deep: true,
|
|
29
|
+
immediate: true
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
20
34
|
data() {
|
|
21
35
|
return {
|
|
36
|
+
canViewChart: null,
|
|
22
37
|
dataWatcher: undefined,
|
|
23
38
|
parsedInfo: undefined,
|
|
24
39
|
root: undefined,
|
|
@@ -37,7 +52,7 @@ export default {
|
|
|
37
52
|
},
|
|
38
53
|
methods: {
|
|
39
54
|
watcherFunction(newValue) {
|
|
40
|
-
if (newValue
|
|
55
|
+
if (newValue?.length) {
|
|
41
56
|
if (!this.isChartFirstRendered) {
|
|
42
57
|
this.parsedInfo = this.fdcConfig.parseData(this.data);
|
|
43
58
|
|
|
@@ -158,16 +173,11 @@ export default {
|
|
|
158
173
|
.attr('r', this.setNodeRadius);
|
|
159
174
|
|
|
160
175
|
nodeEnter.append('circle')
|
|
161
|
-
.attr('r', (d) =>
|
|
162
|
-
return this.setNodeRadius(d) - 5;
|
|
163
|
-
})
|
|
176
|
+
.attr('r', (d) => this.setNodeRadius(d) - 5)
|
|
164
177
|
.attr('class', 'node-hover-layer');
|
|
165
178
|
|
|
166
|
-
nodeEnter.append('svg')
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return getChartIcon(icon);
|
|
170
|
-
})
|
|
179
|
+
nodeEnter.append('svg')
|
|
180
|
+
.html((d) => this.fdcConfig.fetchNodeIcon(d))
|
|
171
181
|
.attr('x', this.nodeImagePosition)
|
|
172
182
|
.attr('y', this.nodeImagePosition)
|
|
173
183
|
.attr('height', this.nodeImageSize)
|
|
@@ -177,9 +187,7 @@ export default {
|
|
|
177
187
|
|
|
178
188
|
this.simulation.nodes(this.allNodesData);
|
|
179
189
|
this.simulation.force('link', d3.forceLink()
|
|
180
|
-
.id((d) =>
|
|
181
|
-
return d.id;
|
|
182
|
-
})
|
|
190
|
+
.id((d) => d.id)
|
|
183
191
|
.distance(100)
|
|
184
192
|
.links(this.allLinks)
|
|
185
193
|
);
|
|
@@ -188,10 +196,10 @@ export default {
|
|
|
188
196
|
const lowerCaseStatus = d.data?.state ? d.data.state.toLowerCase() : 'unkown_status';
|
|
189
197
|
const defaultClassArray = ['node'];
|
|
190
198
|
|
|
191
|
-
if (
|
|
192
|
-
defaultClassArray.push(`node-${ STATES[lowerCaseStatus].color }`);
|
|
193
|
-
} else {
|
|
199
|
+
if (d?.data?.muteStatus) {
|
|
194
200
|
defaultClassArray.push(`node-default-fill`);
|
|
201
|
+
} else if (STATES[lowerCaseStatus] && STATES[lowerCaseStatus].color) {
|
|
202
|
+
defaultClassArray.push(`node-${ STATES[lowerCaseStatus].color }`);
|
|
195
203
|
}
|
|
196
204
|
|
|
197
205
|
// node active (clicked)
|
|
@@ -334,14 +342,6 @@ export default {
|
|
|
334
342
|
this.svg = d3.select('#tree').append('svg')
|
|
335
343
|
.attr('viewBox', `0 0 ${ this.fdcConfig.chartWidth } ${ this.fdcConfig.chartHeight }`)
|
|
336
344
|
.attr('preserveAspectRatio', 'none');
|
|
337
|
-
|
|
338
|
-
// set watcher for the chart data
|
|
339
|
-
this.dataWatcher = this.$watch(this.fdcConfig.watcherProp, function(newValue) {
|
|
340
|
-
this.watcherFunction(newValue);
|
|
341
|
-
}, {
|
|
342
|
-
deep: true,
|
|
343
|
-
immediate: true
|
|
344
|
-
});
|
|
345
345
|
},
|
|
346
346
|
unmounted() {
|
|
347
347
|
this.dataWatcher();
|
|
@@ -353,20 +353,26 @@ export default {
|
|
|
353
353
|
<div>
|
|
354
354
|
<div
|
|
355
355
|
class="chart-container"
|
|
356
|
-
data-testid="
|
|
356
|
+
data-testid="resource-graph"
|
|
357
357
|
>
|
|
358
358
|
<!-- loading status container -->
|
|
359
359
|
<div
|
|
360
360
|
v-if="!isChartFirstRenderAnimationFinished"
|
|
361
361
|
class="loading-container"
|
|
362
362
|
>
|
|
363
|
-
<p v-
|
|
364
|
-
{{ t('
|
|
363
|
+
<p v-if="canViewChart === false">
|
|
364
|
+
{{ t('graph.noPermissions') }}
|
|
365
|
+
</p>
|
|
366
|
+
<p v-else-if="!isChartFirstRendered">
|
|
367
|
+
{{ t('graph.loading') }}
|
|
365
368
|
</p>
|
|
366
|
-
<p v-
|
|
367
|
-
{{ t('
|
|
369
|
+
<p v-else-if="!isChartFirstRenderAnimationFinished">
|
|
370
|
+
{{ t('graph.rendering') }}
|
|
368
371
|
</p>
|
|
369
|
-
<i
|
|
372
|
+
<i
|
|
373
|
+
v-if="canViewChart !== false"
|
|
374
|
+
class="mt-10 icon-spinner icon-spin"
|
|
375
|
+
/>
|
|
370
376
|
</div>
|
|
371
377
|
<!-- main div for svg container -->
|
|
372
378
|
<div id="tree" />
|
|
@@ -416,6 +422,9 @@ export default {
|
|
|
416
422
|
>
|
|
417
423
|
<p>{{ item.value }}</p>
|
|
418
424
|
</td>
|
|
425
|
+
<td v-else-if="item.type === 'resource-type'">
|
|
426
|
+
{{ t(`typeLabel."${ item.valueKey }"`, { count: 1 }) }}
|
|
427
|
+
</td>
|
|
419
428
|
<!-- default template -->
|
|
420
429
|
<td v-else>
|
|
421
430
|
{{ item.value }}
|
|
@@ -478,32 +487,29 @@ export default {
|
|
|
478
487
|
}
|
|
479
488
|
}
|
|
480
489
|
|
|
481
|
-
&.repo.active > circle {
|
|
482
|
-
transform: scale(1.2);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
490
|
&.bundle.active > circle {
|
|
486
491
|
transform: scale(1.35);
|
|
487
492
|
}
|
|
488
493
|
|
|
489
|
-
&.
|
|
494
|
+
&.bundledeployment.active > circle {
|
|
490
495
|
transform: scale(1.6);
|
|
491
496
|
}
|
|
492
497
|
|
|
493
|
-
&.node-default-fill > circle
|
|
494
|
-
|
|
498
|
+
&.node-default-fill > circle {
|
|
499
|
+
transform: scale(1.2);
|
|
495
500
|
fill: var(--muted);
|
|
496
501
|
}
|
|
497
|
-
|
|
502
|
+
|
|
503
|
+
&.node-success > circle {
|
|
498
504
|
fill: var(--success);
|
|
499
505
|
}
|
|
500
|
-
|
|
506
|
+
&.node-info > circle {
|
|
501
507
|
fill: var(--info);
|
|
502
508
|
}
|
|
503
|
-
|
|
509
|
+
&.node-warning > circle {
|
|
504
510
|
fill: var(--warning);
|
|
505
511
|
}
|
|
506
|
-
|
|
512
|
+
&.node-error > circle {
|
|
507
513
|
fill: var(--error);
|
|
508
514
|
}
|
|
509
515
|
|
package/components/IconOrSvg.vue
CHANGED
|
@@ -61,41 +61,16 @@ export default {
|
|
|
61
61
|
},
|
|
62
62
|
|
|
63
63
|
methods: {
|
|
64
|
+
getComputedStyleFor(cssVar) {
|
|
65
|
+
return normalizeHex(mapStandardColors((window.getComputedStyle(document.body).getPropertyValue(cssVar)).trim()));
|
|
66
|
+
},
|
|
67
|
+
|
|
64
68
|
setColor() {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
for (let i = 0; i < Object.keys(document.styleSheets).length; i++) {
|
|
71
|
-
let found = false;
|
|
72
|
-
const stylesheet = document.styleSheets[i];
|
|
73
|
-
|
|
74
|
-
if (stylesheet && stylesheet.cssRules) {
|
|
75
|
-
for (let x = 0; x < Object.keys(stylesheet.cssRules).length; x++) {
|
|
76
|
-
const cssRules = stylesheet.cssRules[x];
|
|
77
|
-
|
|
78
|
-
if (cssRules.selectorText && ((currTheme === 'light' && (cssRules.selectorText.includes('body') || cssRules.selectorText.includes('BODY')) &&
|
|
79
|
-
cssRules.selectorText.includes('.theme-light') && cssRules.style.cssText.includes('--link:')) ||
|
|
80
|
-
(currTheme === 'dark' && cssRules.selectorText.includes('.theme-dark')))) {
|
|
81
|
-
// grab the colors to be used on the icon from the css rules
|
|
82
|
-
uiColor = mapStandardColors(cssRules.style.getPropertyValue(colors[this.color].color).trim());
|
|
83
|
-
hoverColor = mapStandardColors(cssRules.style.getPropertyValue(colors[this.color].hover).trim());
|
|
84
|
-
|
|
85
|
-
// normalize hex colors (#xxx to #xxxxxx)
|
|
86
|
-
uiColor = normalizeHex(uiColor);
|
|
87
|
-
hoverColor = normalizeHex(hoverColor);
|
|
88
|
-
|
|
89
|
-
found = true;
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (found) {
|
|
95
|
-
break;
|
|
96
|
-
} else {
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
69
|
+
const uiColor = this.getComputedStyleFor(colors[this.color].color);
|
|
70
|
+
const hoverColor = this.getComputedStyleFor(colors[this.color].hover);
|
|
71
|
+
|
|
72
|
+
if (!uiColor || !hoverColor) {
|
|
73
|
+
return;
|
|
99
74
|
}
|
|
100
75
|
|
|
101
76
|
const uiColorRGB = colorToRgb(uiColor);
|
|
@@ -142,7 +117,11 @@ export default {
|
|
|
142
117
|
}
|
|
143
118
|
a.option:hover > img.${ className } {
|
|
144
119
|
${ hoverFilter };
|
|
145
|
-
}
|
|
120
|
+
}
|
|
121
|
+
a.option.active-menu-link > img.${ className } {
|
|
122
|
+
${ hoverFilter };
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
146
125
|
|
|
147
126
|
const styleSheet = document.createElement('style');
|
|
148
127
|
|
|
@@ -43,6 +43,7 @@ export default {
|
|
|
43
43
|
chartsToRemoveIsApp: false,
|
|
44
44
|
chartsDeleteCrd: false,
|
|
45
45
|
showModal: false,
|
|
46
|
+
cachedDoneLocation: null
|
|
46
47
|
};
|
|
47
48
|
},
|
|
48
49
|
computed: {
|
|
@@ -220,6 +221,7 @@ export default {
|
|
|
220
221
|
this.error = '';
|
|
221
222
|
this.chartsDeleteCrd = false;
|
|
222
223
|
this.chartsToRemoveIsApp = false;
|
|
224
|
+
this.cachedDoneLocation = null;
|
|
223
225
|
this.$store.commit('action-menu/togglePromptRemove');
|
|
224
226
|
},
|
|
225
227
|
|
|
@@ -227,6 +229,8 @@ export default {
|
|
|
227
229
|
if (this.doneLocation) {
|
|
228
230
|
// doneLocation will recompute to undefined when delete request completes
|
|
229
231
|
this.cachedDoneLocation = { ...this.doneLocation };
|
|
232
|
+
} else {
|
|
233
|
+
this.cachedDoneLocation = null;
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
if (this.hasCustomRemove && this.$refs?.customPrompt?.remove) {
|
|
@@ -294,7 +298,7 @@ export default {
|
|
|
294
298
|
}
|
|
295
299
|
},
|
|
296
300
|
done() {
|
|
297
|
-
if (
|
|
301
|
+
if (!isEmpty(this.cachedDoneLocation) && this.currentRouter) {
|
|
298
302
|
this.currentRouter.push(this.cachedDoneLocation);
|
|
299
303
|
}
|
|
300
304
|
this.close();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useI18n } from '@shell/composables/useI18n';
|
|
2
|
+
import { toValue } from 'vue';
|
|
3
|
+
import { useStore } from 'vuex';
|
|
4
|
+
|
|
5
|
+
export type ScaleFn = () => Promise<void>;
|
|
6
|
+
export const useScaling = (workloadName: string, scaleUpFn: ScaleFn, scaleDownFn: ScaleFn) => {
|
|
7
|
+
const store = useStore();
|
|
8
|
+
const i18n = useI18n(store);
|
|
9
|
+
|
|
10
|
+
const scale = (scaleFn: () => Promise<void>, workloadName: string, direction: 'up' | 'down') => {
|
|
11
|
+
return async() => {
|
|
12
|
+
try {
|
|
13
|
+
await scaleFn();
|
|
14
|
+
} catch (err) {
|
|
15
|
+
store.dispatch('growl/fromError', {
|
|
16
|
+
title: i18n.t('workload.list.errorCannotScale', { workloadName, direction }),
|
|
17
|
+
err
|
|
18
|
+
},
|
|
19
|
+
{ root: true });
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const workloadNameValue = toValue(workloadName);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
scaleUp: scale(() => scaleUpFn(), workloadNameValue, 'up'),
|
|
28
|
+
scaleDown: scale(() => scaleDownFn(), workloadNameValue, 'down')
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Card from '@shell/components/Resource/Detail/Card/index.vue';
|
|
3
|
+
import Scaler from '@shell/components/Resource/Detail/Card/Scaler.vue';
|
|
4
|
+
import VerticalGap from '@shell/components/Resource/Detail/Card/VerticalGap.vue';
|
|
5
|
+
import StatusBar from '@shell/components/Resource/Detail/StatusBar.vue';
|
|
6
|
+
import StatusRow from '@shell/components/Resource/Detail/StatusRow.vue';
|
|
7
|
+
import { useI18n } from '@shell/composables/useI18n';
|
|
8
|
+
import { StateColor } from '@shell/utils/style';
|
|
9
|
+
import { computed } from 'vue';
|
|
10
|
+
import { useStore } from 'vuex';
|
|
11
|
+
|
|
12
|
+
export interface Props {
|
|
13
|
+
pods?: any[];
|
|
14
|
+
showScaling?: boolean;
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
const store = useStore();
|
|
20
|
+
const i18n = useI18n(store);
|
|
21
|
+
|
|
22
|
+
const props = withDefaults(defineProps<Props>(), { pods: undefined, showScaling: false });
|
|
23
|
+
const emit = defineEmits(['decrease', 'increase']);
|
|
24
|
+
|
|
25
|
+
const segmentAccumulator = computed(() => {
|
|
26
|
+
interface Value {
|
|
27
|
+
count: number;
|
|
28
|
+
}
|
|
29
|
+
const accumulator: {[key in StateColor]?: Value} = {};
|
|
30
|
+
|
|
31
|
+
props.pods?.forEach((pod: any) => {
|
|
32
|
+
const color: StateColor = pod.stateSimpleColor;
|
|
33
|
+
|
|
34
|
+
accumulator[color] = accumulator[color] || { count: 0 };
|
|
35
|
+
accumulator[color].count++;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return accumulator;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const rowAccumulator = computed(() => {
|
|
42
|
+
interface Value {
|
|
43
|
+
count: number;
|
|
44
|
+
color: StateColor;
|
|
45
|
+
}
|
|
46
|
+
const accumulator: {[key in string]: Value} = {};
|
|
47
|
+
|
|
48
|
+
props.pods?.forEach((pod: any) => {
|
|
49
|
+
accumulator[pod.stateDisplay] = accumulator[pod.stateDisplay] || { count: 0 };
|
|
50
|
+
accumulator[pod.stateDisplay].count++;
|
|
51
|
+
accumulator[pod.stateDisplay].color = pod.stateSimpleColor.replace('text-', '') as StateColor;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return accumulator;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const percent = (count: number, total: number) => {
|
|
58
|
+
return count / total * 100;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const count = computed(() => props.pods?.length || 0);
|
|
62
|
+
|
|
63
|
+
const segmentColors = computed(() => Object.keys(segmentAccumulator.value) as StateColor[]);
|
|
64
|
+
const segments = computed(() => segmentColors.value.map((color: StateColor) => ({
|
|
65
|
+
color,
|
|
66
|
+
percent: percent(segmentAccumulator.value[color]?.count || 0, count.value)
|
|
67
|
+
})));
|
|
68
|
+
|
|
69
|
+
const rowStates = computed(() => {
|
|
70
|
+
return Object.keys(rowAccumulator.value);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const rows = computed(() => {
|
|
74
|
+
return rowStates.value.map((state) => ({
|
|
75
|
+
color: rowAccumulator.value[state].color,
|
|
76
|
+
label: state,
|
|
77
|
+
count: rowAccumulator.value[state].count,
|
|
78
|
+
percent: percent(rowAccumulator.value[state].count, count.value)
|
|
79
|
+
}));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<template>
|
|
85
|
+
<Card :title="i18n.t('component.resource.detail.card.podsCard.title')">
|
|
86
|
+
<template
|
|
87
|
+
v-if="props.showScaling"
|
|
88
|
+
#heading-action
|
|
89
|
+
>
|
|
90
|
+
<Scaler
|
|
91
|
+
:ariaResourceName="i18n.t('component.resource.detail.card.podsCard.ariaResourceName')"
|
|
92
|
+
:value="count"
|
|
93
|
+
:min="0"
|
|
94
|
+
@increase="(newValue) => emit('increase', newValue)"
|
|
95
|
+
@decrease="(newValue) => emit('decrease', newValue)"
|
|
96
|
+
/>
|
|
97
|
+
</template>
|
|
98
|
+
<StatusBar :segments="segments" />
|
|
99
|
+
<VerticalGap />
|
|
100
|
+
<div class="pod-distribution">
|
|
101
|
+
<StatusRow
|
|
102
|
+
v-for="(row, i) in rows"
|
|
103
|
+
:key="i"
|
|
104
|
+
:color="row.color"
|
|
105
|
+
:label="row.label"
|
|
106
|
+
:count="row.count"
|
|
107
|
+
:percent="row.percent"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
</Card>
|
|
111
|
+
</template>
|
|
112
|
+
|
|
113
|
+
<style lang="scss" scoped>
|
|
114
|
+
.pod-distribution {
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-direction: column;
|
|
117
|
+
}
|
|
118
|
+
</style>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Props } from '@shell/components/Resource/Detail/Card/ResourceUsageCard/index.vue';
|
|
2
|
+
import { useI18n } from '@shell/composables/useI18n';
|
|
3
|
+
import { computed } from 'vue';
|
|
4
|
+
import { useStore } from 'vuex';
|
|
5
|
+
|
|
6
|
+
export const useCpuUsageCardDefaultProps = (resource: any): Props => {
|
|
7
|
+
const store = useStore();
|
|
8
|
+
const i18n = useI18n(store);
|
|
9
|
+
|
|
10
|
+
// const resourceValue = toValue(resource);
|
|
11
|
+
|
|
12
|
+
return computed(() => {
|
|
13
|
+
return {
|
|
14
|
+
title: i18n.t('component.resource.detail.card.resourceUsage.cpu'),
|
|
15
|
+
used: 1.20,
|
|
16
|
+
available: 1.93
|
|
17
|
+
};
|
|
18
|
+
}).value;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const useMemoryUsageCardDefaultProps = (resource: any): Props => {
|
|
22
|
+
const store = useStore();
|
|
23
|
+
const i18n = useI18n(store);
|
|
24
|
+
|
|
25
|
+
// const resourceValue = toValue(resource);
|
|
26
|
+
|
|
27
|
+
return computed(() => {
|
|
28
|
+
return {
|
|
29
|
+
title: i18n.t('component.resource.detail.card.resourceUsage.memory'),
|
|
30
|
+
used: 870,
|
|
31
|
+
available: 3290,
|
|
32
|
+
availableFormatter: (n: number) => (n / 1000).toFixed(2),
|
|
33
|
+
unit: 'GiB',
|
|
34
|
+
};
|
|
35
|
+
}).value;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const usePodUsageCardDefaultProps = (resource: any): Props => {
|
|
39
|
+
const store = useStore();
|
|
40
|
+
const i18n = useI18n(store);
|
|
41
|
+
|
|
42
|
+
// const resourceValue = toValue(resource);
|
|
43
|
+
|
|
44
|
+
return computed(() => {
|
|
45
|
+
return {
|
|
46
|
+
title: i18n.t('component.resource.detail.card.resourceUsage.pods'),
|
|
47
|
+
used: 9,
|
|
48
|
+
available: 17
|
|
49
|
+
};
|
|
50
|
+
}).value;
|
|
51
|
+
};
|