@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,545 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import CreateEditView from '@shell/mixins/create-edit-view/impl';
|
|
3
|
+
import Loading from '@shell/components/Loading';
|
|
4
|
+
import ResourceYaml from '@shell/components/ResourceYaml';
|
|
5
|
+
import {
|
|
6
|
+
_VIEW, _EDIT, _CLONE, _IMPORT, _STAGE, _CREATE,
|
|
7
|
+
AS, _YAML, _DETAIL, _CONFIG, _GRAPH, PREVIEW, MODE,
|
|
8
|
+
} from '@shell/config/query-params';
|
|
9
|
+
import { SCHEMA } from '@shell/config/types';
|
|
10
|
+
import { createYaml } from '@shell/utils/create-yaml';
|
|
11
|
+
import Masthead from '@shell/components/ResourceDetail/Masthead';
|
|
12
|
+
import DetailTop from '@shell/components/DetailTop';
|
|
13
|
+
import { clone, diff } from '@shell/utils/object';
|
|
14
|
+
import IconMessage from '@shell/components/IconMessage';
|
|
15
|
+
import ForceDirectedTreeChart from '@shell/components/ForceDirectedTreeChart';
|
|
16
|
+
import { stringify } from '@shell/utils/error';
|
|
17
|
+
import { Banner } from '@components/Banner';
|
|
18
|
+
|
|
19
|
+
function modeFor(route) {
|
|
20
|
+
if ( route.query?.mode === _IMPORT ) {
|
|
21
|
+
return _IMPORT;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if ( route.params?.id ) {
|
|
25
|
+
return route.query.mode || _VIEW;
|
|
26
|
+
} else {
|
|
27
|
+
return _CREATE;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function getYaml(store, model) {
|
|
32
|
+
let yaml;
|
|
33
|
+
const opt = { headers: { accept: 'application/yaml' } };
|
|
34
|
+
|
|
35
|
+
if ( model.hasLink('view') ) {
|
|
36
|
+
yaml = (await model.followLink('view', opt)).data;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return model.cleanForDownload(yaml);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default {
|
|
43
|
+
emits: ['input'],
|
|
44
|
+
|
|
45
|
+
components: {
|
|
46
|
+
Loading,
|
|
47
|
+
DetailTop,
|
|
48
|
+
ForceDirectedTreeChart,
|
|
49
|
+
ResourceYaml,
|
|
50
|
+
Masthead,
|
|
51
|
+
IconMessage,
|
|
52
|
+
Banner
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
mixins: [CreateEditView],
|
|
56
|
+
|
|
57
|
+
props: {
|
|
58
|
+
storeOverride: {
|
|
59
|
+
type: String,
|
|
60
|
+
default: null,
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
resourceOverride: {
|
|
64
|
+
type: String,
|
|
65
|
+
default: null,
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
parentRouteOverride: {
|
|
69
|
+
type: String,
|
|
70
|
+
default: null,
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
flexContent: {
|
|
74
|
+
type: Boolean,
|
|
75
|
+
default: false,
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Inherited global identifier prefix for tests
|
|
80
|
+
* Define a term based on the parent component to avoid conflicts on multiple components
|
|
81
|
+
*/
|
|
82
|
+
componentTestid: {
|
|
83
|
+
type: String,
|
|
84
|
+
default: 'resource-details'
|
|
85
|
+
},
|
|
86
|
+
errorsMap: {
|
|
87
|
+
type: Object,
|
|
88
|
+
default: null
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
async fetch() {
|
|
93
|
+
const store = this.$store;
|
|
94
|
+
const route = this.$route;
|
|
95
|
+
const params = route.params;
|
|
96
|
+
let resourceType = this.resourceOverride || params.resource;
|
|
97
|
+
|
|
98
|
+
const inStore = this.storeOverride || store.getters['currentStore'](resourceType);
|
|
99
|
+
const realMode = this.realMode;
|
|
100
|
+
|
|
101
|
+
// eslint-disable-next-line prefer-const
|
|
102
|
+
let { namespace, id } = params;
|
|
103
|
+
|
|
104
|
+
// There are 6 "real" modes that can be put into the query string
|
|
105
|
+
// These are mapped down to the 3 regular page "mode"s that create-edit-view components
|
|
106
|
+
// know about: view, edit, create (stage, import and clone become "create")
|
|
107
|
+
const mode = ([_CLONE, _IMPORT, _STAGE].includes(realMode) ? _CREATE : realMode);
|
|
108
|
+
|
|
109
|
+
const getGraphConfig = store.getters['type-map/hasGraph'](resourceType);
|
|
110
|
+
const hasGraph = !!getGraphConfig;
|
|
111
|
+
const hasCustomDetail = store.getters['type-map/hasCustomDetail'](resourceType, id);
|
|
112
|
+
const hasCustomEdit = store.getters['type-map/hasCustomEdit'](resourceType, id);
|
|
113
|
+
|
|
114
|
+
const schemas = store.getters[`${ inStore }/all`](SCHEMA);
|
|
115
|
+
|
|
116
|
+
// As determines what component will be rendered
|
|
117
|
+
const requested = route.query[AS];
|
|
118
|
+
let as;
|
|
119
|
+
let notFound = false;
|
|
120
|
+
|
|
121
|
+
if ( mode === _VIEW && hasCustomDetail && (!requested || requested === _DETAIL) ) {
|
|
122
|
+
as = _DETAIL;
|
|
123
|
+
} else if ( mode === _VIEW && hasGraph && requested === _GRAPH) {
|
|
124
|
+
as = _GRAPH;
|
|
125
|
+
} else if ( hasCustomEdit && (!requested || requested === _CONFIG) ) {
|
|
126
|
+
as = _CONFIG;
|
|
127
|
+
} else {
|
|
128
|
+
as = _YAML;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.as = as;
|
|
132
|
+
|
|
133
|
+
const options = store.getters[`type-map/optionsFor`](resourceType);
|
|
134
|
+
|
|
135
|
+
this.showMasthead = [_CREATE, _EDIT].includes(mode) ? options.resourceEditMasthead : true;
|
|
136
|
+
const canViewYaml = options.canYaml;
|
|
137
|
+
|
|
138
|
+
if ( options.resource ) {
|
|
139
|
+
resourceType = options.resource;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const schema = store.getters[`${ inStore }/schemaFor`](resourceType);
|
|
143
|
+
let model, initialModel, liveModel, yaml;
|
|
144
|
+
|
|
145
|
+
if ( realMode === _CREATE || realMode === _IMPORT ) {
|
|
146
|
+
if ( !namespace ) {
|
|
147
|
+
namespace = store.getters['defaultNamespace'];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const data = { type: resourceType };
|
|
151
|
+
|
|
152
|
+
if ( schema?.attributes?.namespaced ) {
|
|
153
|
+
data.metadata = { namespace };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
liveModel = await store.dispatch(`${ inStore }/create`, data);
|
|
157
|
+
initialModel = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
|
|
158
|
+
model = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
|
|
159
|
+
|
|
160
|
+
if (model.forceYaml === true) {
|
|
161
|
+
as = _YAML;
|
|
162
|
+
this.as = as;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if ( as === _YAML ) {
|
|
166
|
+
if (schema?.fetchResourceFields) {
|
|
167
|
+
// fetch resourceFields for createYaml
|
|
168
|
+
await schema.fetchResourceFields();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
yaml = createYaml(schemas, resourceType, data);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
let fqid = id;
|
|
175
|
+
|
|
176
|
+
if ( schema.attributes?.namespaced && namespace ) {
|
|
177
|
+
fqid = `${ namespace }/${ fqid }`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
liveModel = await store.dispatch(`${ inStore }/find`, {
|
|
182
|
+
type: resourceType,
|
|
183
|
+
id: fqid,
|
|
184
|
+
opt: { watch: true }
|
|
185
|
+
});
|
|
186
|
+
} catch (e) {
|
|
187
|
+
if (e.status === 404 || e.status === 403) {
|
|
188
|
+
store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceIdNotFound', { resource: resourceType, fqid }, true)));
|
|
189
|
+
}
|
|
190
|
+
liveModel = {};
|
|
191
|
+
notFound = fqid;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
if (realMode === _VIEW) {
|
|
196
|
+
model = liveModel;
|
|
197
|
+
} else {
|
|
198
|
+
model = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
|
|
199
|
+
}
|
|
200
|
+
initialModel = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
|
|
201
|
+
|
|
202
|
+
if ( as === _YAML ) {
|
|
203
|
+
yaml = await getYaml(this.$store, liveModel);
|
|
204
|
+
}
|
|
205
|
+
} catch (e) {
|
|
206
|
+
this.errors.push(e);
|
|
207
|
+
}
|
|
208
|
+
if ( as === _YAML ) {
|
|
209
|
+
try {
|
|
210
|
+
yaml = await getYaml(this.$store, liveModel);
|
|
211
|
+
} catch (e) {
|
|
212
|
+
this.errors.push(e);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if ( as === _GRAPH ) {
|
|
217
|
+
this.chartData = liveModel;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if ( [_CLONE, _IMPORT, _STAGE].includes(realMode) ) {
|
|
221
|
+
model.cleanForNew();
|
|
222
|
+
yaml = model.cleanYaml(yaml, realMode);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Ensure common properties exists
|
|
227
|
+
try {
|
|
228
|
+
model = await store.dispatch(`${ inStore }/cleanForDetail`, model);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
this.errors.push(e);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const out = {
|
|
234
|
+
hasGraph,
|
|
235
|
+
getGraphConfig,
|
|
236
|
+
hasCustomDetail,
|
|
237
|
+
hasCustomEdit,
|
|
238
|
+
canViewYaml,
|
|
239
|
+
resourceType,
|
|
240
|
+
as,
|
|
241
|
+
yaml,
|
|
242
|
+
initialModel,
|
|
243
|
+
liveModel,
|
|
244
|
+
mode,
|
|
245
|
+
value: model,
|
|
246
|
+
notFound,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
for ( const key in out ) {
|
|
250
|
+
this[key] = out[key];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if ( this.mode === _CREATE ) {
|
|
254
|
+
this.value.applyDefaults(this, realMode);
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
data() {
|
|
258
|
+
return {
|
|
259
|
+
chartData: null,
|
|
260
|
+
resourceSubtype: null,
|
|
261
|
+
|
|
262
|
+
// Set by fetch
|
|
263
|
+
hasGraph: null,
|
|
264
|
+
hasCustomDetail: null,
|
|
265
|
+
hasCustomEdit: null,
|
|
266
|
+
resourceType: null,
|
|
267
|
+
asYaml: null,
|
|
268
|
+
yaml: null,
|
|
269
|
+
liveModel: null,
|
|
270
|
+
initialModel: null,
|
|
271
|
+
mode: null,
|
|
272
|
+
as: null,
|
|
273
|
+
value: null,
|
|
274
|
+
model: null,
|
|
275
|
+
notFound: null,
|
|
276
|
+
canViewYaml: null,
|
|
277
|
+
errors: []
|
|
278
|
+
};
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
computed: {
|
|
282
|
+
realMode() {
|
|
283
|
+
// There are 5 "real" modes that you can start in: view, edit, create, stage, clone
|
|
284
|
+
const realMode = modeFor(this.$route);
|
|
285
|
+
|
|
286
|
+
return realMode;
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
isView() {
|
|
290
|
+
return this.mode === _VIEW;
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
isYaml() {
|
|
294
|
+
return this.as === _YAML;
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
isDetail() {
|
|
298
|
+
return this.as === _DETAIL;
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
isGraph() {
|
|
302
|
+
return this.as === _GRAPH;
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
offerPreview() {
|
|
306
|
+
return this.as === _YAML && [_EDIT, _CLONE, _IMPORT, _STAGE].includes(this.mode);
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
showComponent() {
|
|
310
|
+
switch ( this.as ) {
|
|
311
|
+
case _DETAIL: return this.detailComponent;
|
|
312
|
+
case _CONFIG: return this.editComponent;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return null;
|
|
316
|
+
},
|
|
317
|
+
hasErrors() {
|
|
318
|
+
return this.errors?.length && Array.isArray(this.errors);
|
|
319
|
+
},
|
|
320
|
+
mappedErrors() {
|
|
321
|
+
return !this.errors ? {} : this.errorsMap || this.errors.reduce((acc, error) => ({
|
|
322
|
+
...acc,
|
|
323
|
+
[error]: {
|
|
324
|
+
message: error?.data?.message || error,
|
|
325
|
+
icon: null
|
|
326
|
+
}
|
|
327
|
+
}), {});
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
watch: {
|
|
332
|
+
'$route'(current, prev) {
|
|
333
|
+
if (current.name !== prev.name) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const neu = clone(current.query);
|
|
337
|
+
const old = clone(prev.query);
|
|
338
|
+
|
|
339
|
+
delete neu[PREVIEW];
|
|
340
|
+
delete old[PREVIEW];
|
|
341
|
+
|
|
342
|
+
if ( !this.isView ) {
|
|
343
|
+
delete neu[AS];
|
|
344
|
+
delete old[AS];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const queryDiff = Object.keys(diff(neu, old));
|
|
348
|
+
|
|
349
|
+
if (queryDiff.includes(MODE) || queryDiff.includes(AS)) {
|
|
350
|
+
this.$fetch();
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
// Auto refresh YAML when the model changes
|
|
355
|
+
async 'value.metadata.resourceVersion'(a, b) {
|
|
356
|
+
if ( this.mode === _VIEW && this.as === _YAML && a && b && a !== b) {
|
|
357
|
+
this.yaml = await getYaml(this.$store, this.liveModel);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
created() {
|
|
363
|
+
this.configureResource();
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
methods: {
|
|
367
|
+
stringify,
|
|
368
|
+
setSubtype(subtype) {
|
|
369
|
+
this.resourceSubtype = subtype;
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
keyAction(act) {
|
|
373
|
+
const m = this.liveModel;
|
|
374
|
+
|
|
375
|
+
if ( m?.[act] ) {
|
|
376
|
+
m[act]();
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
closeError(index) {
|
|
380
|
+
this.errors = this.errors.filter((_, i) => i !== index);
|
|
381
|
+
},
|
|
382
|
+
/**
|
|
383
|
+
* Initializes the resource components based on the provided user and
|
|
384
|
+
* resource override.
|
|
385
|
+
*
|
|
386
|
+
* Configures the detail and edit components for a resource based on the
|
|
387
|
+
* user's ID and the specified resource.
|
|
388
|
+
*
|
|
389
|
+
* @param {Object} user - The user object containing user-specific
|
|
390
|
+
* information.
|
|
391
|
+
* @param {string|null} resourceOverride - An optional resource override
|
|
392
|
+
* string. If not provided, the method will use the default resource from
|
|
393
|
+
* the route parameters or the instance's resourceOverride property.
|
|
394
|
+
*/
|
|
395
|
+
configureResource(userId = '', resourceOverride = null) {
|
|
396
|
+
const id = userId || this.$route.params.id;
|
|
397
|
+
const resource = resourceOverride || this.resourceOverride || this.$route.params.resource;
|
|
398
|
+
const options = this.$store.getters[`type-map/optionsFor`](resource);
|
|
399
|
+
|
|
400
|
+
const detailResource = options.resourceDetail || options.resource || resource;
|
|
401
|
+
const editResource = options.resourceEdit || options.resource || resource;
|
|
402
|
+
|
|
403
|
+
// FIXME: These aren't right... signature is (rawType, subType).. not (rawType, resourceId)
|
|
404
|
+
// Remove id? How does subtype get in (cluster/node)
|
|
405
|
+
this.detailComponent = this.$store.getters['type-map/importDetail'](detailResource, id);
|
|
406
|
+
this.editComponent = this.$store.getters['type-map/importEdit'](editResource, id);
|
|
407
|
+
},
|
|
408
|
+
/**
|
|
409
|
+
* Sets the mode and initializes the resource components.
|
|
410
|
+
*
|
|
411
|
+
* This method sets the mode of the component and configures the resource
|
|
412
|
+
* components based on the provided user and resource.
|
|
413
|
+
*
|
|
414
|
+
* @param {Object} payload - An object containing the mode, user, and
|
|
415
|
+
* resource properties.
|
|
416
|
+
* @param {string} payload.mode - The mode to set.
|
|
417
|
+
* @param {Object} payload.user - The user object containing user-specific
|
|
418
|
+
* information.
|
|
419
|
+
* @param {string} payload.resource - The resource string to use for
|
|
420
|
+
* initialization.
|
|
421
|
+
*/
|
|
422
|
+
setMode({ mode, userId, resource }) {
|
|
423
|
+
this.mode = mode;
|
|
424
|
+
this.value.id = userId;
|
|
425
|
+
this.configureResource(userId, resource);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
</script>
|
|
430
|
+
|
|
431
|
+
<template>
|
|
432
|
+
<Loading v-if="$fetchState.pending || notFound" />
|
|
433
|
+
<div v-else>
|
|
434
|
+
<Masthead
|
|
435
|
+
v-if="showMasthead"
|
|
436
|
+
:resource="resourceType"
|
|
437
|
+
:value="liveModel"
|
|
438
|
+
:mode="mode"
|
|
439
|
+
:real-mode="realMode"
|
|
440
|
+
:as="as"
|
|
441
|
+
:has-graph="hasGraph"
|
|
442
|
+
:has-detail="hasCustomDetail"
|
|
443
|
+
:has-edit="hasCustomEdit"
|
|
444
|
+
:can-view-yaml="canViewYaml"
|
|
445
|
+
:resource-subtype="resourceSubtype"
|
|
446
|
+
:parent-route-override="parentRouteOverride"
|
|
447
|
+
:store-override="storeOverride"
|
|
448
|
+
>
|
|
449
|
+
<DetailTop
|
|
450
|
+
v-if="isView && isDetail"
|
|
451
|
+
:value="liveModel"
|
|
452
|
+
/>
|
|
453
|
+
</Masthead>
|
|
454
|
+
<div
|
|
455
|
+
v-if="hasErrors"
|
|
456
|
+
id="cru-errors"
|
|
457
|
+
class="cru__errors"
|
|
458
|
+
>
|
|
459
|
+
<Banner
|
|
460
|
+
v-for="(err, i) in errors"
|
|
461
|
+
:key="i"
|
|
462
|
+
color="error"
|
|
463
|
+
:data-testid="`error-banner${i}`"
|
|
464
|
+
:label="stringify(mappedErrors[err].message)"
|
|
465
|
+
:icon="mappedErrors[err].icon"
|
|
466
|
+
:closable="true"
|
|
467
|
+
@close="closeError(i)"
|
|
468
|
+
/>
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
<ForceDirectedTreeChart
|
|
472
|
+
v-if="isGraph"
|
|
473
|
+
:data="chartData"
|
|
474
|
+
:fdc-config="getGraphConfig"
|
|
475
|
+
/>
|
|
476
|
+
|
|
477
|
+
<ResourceYaml
|
|
478
|
+
v-else-if="isYaml"
|
|
479
|
+
ref="resourceyaml"
|
|
480
|
+
:value="value"
|
|
481
|
+
:mode="mode"
|
|
482
|
+
:yaml="yaml"
|
|
483
|
+
:offer-preview="offerPreview"
|
|
484
|
+
:done-route="doneRoute"
|
|
485
|
+
:done-override="value ? value.doneOverride : null"
|
|
486
|
+
@update:value="$emit('input', $event)"
|
|
487
|
+
@error="e=>errors.push(e)"
|
|
488
|
+
/>
|
|
489
|
+
|
|
490
|
+
<component
|
|
491
|
+
:is="showComponent"
|
|
492
|
+
v-else
|
|
493
|
+
ref="comp"
|
|
494
|
+
v-model:value="value"
|
|
495
|
+
v-bind="$data"
|
|
496
|
+
:done-params="doneParams"
|
|
497
|
+
:done-route="doneRoute"
|
|
498
|
+
:mode="mode"
|
|
499
|
+
:initial-value="initialModel"
|
|
500
|
+
:live-value="liveModel"
|
|
501
|
+
:real-mode="realMode"
|
|
502
|
+
:class="{'flex-content': flexContent}"
|
|
503
|
+
@update:value="$emit('input', $event)"
|
|
504
|
+
@update:mode="setMode"
|
|
505
|
+
@set-subtype="setSubtype"
|
|
506
|
+
/>
|
|
507
|
+
|
|
508
|
+
<button
|
|
509
|
+
v-if="isView"
|
|
510
|
+
v-shortkey.once="['shift','d']"
|
|
511
|
+
:data-testid="componentTestid + '-detail'"
|
|
512
|
+
class="hide"
|
|
513
|
+
@shortkey="keyAction('goToDetail')"
|
|
514
|
+
/>
|
|
515
|
+
<button
|
|
516
|
+
v-if="isView"
|
|
517
|
+
v-shortkey.once="['shift','c']"
|
|
518
|
+
:data-testid="componentTestid + '-config'"
|
|
519
|
+
class="hide"
|
|
520
|
+
@shortkey="keyAction('goToViewConfig')"
|
|
521
|
+
/>
|
|
522
|
+
<button
|
|
523
|
+
v-if="isView"
|
|
524
|
+
v-shortkey.once="['shift','y']"
|
|
525
|
+
:data-testid="componentTestid + '-yaml'"
|
|
526
|
+
class="hide"
|
|
527
|
+
@shortkey="keyAction('goToViewYaml')"
|
|
528
|
+
/>
|
|
529
|
+
<button
|
|
530
|
+
v-if="isView"
|
|
531
|
+
v-shortkey.once="['shift','e']"
|
|
532
|
+
:data-testid="componentTestid + '-edit'"
|
|
533
|
+
class="hide"
|
|
534
|
+
@shortkey="keyAction('goToEdit')"
|
|
535
|
+
/>
|
|
536
|
+
</div>
|
|
537
|
+
</template>
|
|
538
|
+
|
|
539
|
+
<style lang='scss' scoped>
|
|
540
|
+
.flex-content {
|
|
541
|
+
display: flex;
|
|
542
|
+
flex-direction: column;
|
|
543
|
+
flex-grow: 1;
|
|
544
|
+
}
|
|
545
|
+
</style>
|
|
@@ -8,6 +8,9 @@ import { NAMESPACE, AGE } from '@shell/config/table-headers';
|
|
|
8
8
|
import { findBy } from '@shell/utils/array';
|
|
9
9
|
import { ExtensionPoint, TableColumnLocation } from '@shell/core/types';
|
|
10
10
|
import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
|
|
11
|
+
import { ToggleSwitch } from '@components/Form/ToggleSwitch';
|
|
12
|
+
import ResourceTableWatch from '@shell/mixins/resource-table-watch';
|
|
13
|
+
import paginationUtils from '@shell/utils/pagination-utils';
|
|
11
14
|
|
|
12
15
|
// Default group-by in the case the group stored in the preference does not apply
|
|
13
16
|
const DEFAULT_GROUP = 'namespace';
|
|
@@ -43,7 +46,13 @@ export default {
|
|
|
43
46
|
|
|
44
47
|
emits: ['clickedActionButton'],
|
|
45
48
|
|
|
46
|
-
components: {
|
|
49
|
+
components: {
|
|
50
|
+
ButtonGroup, SortableTable, ToggleSwitch
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
mixins: [
|
|
54
|
+
ResourceTableWatch
|
|
55
|
+
],
|
|
47
56
|
|
|
48
57
|
props: {
|
|
49
58
|
schema: {
|
|
@@ -222,7 +231,8 @@ export default {
|
|
|
222
231
|
* Primary purpose is to directly connect an iteration of `rows` with a sortGeneration string. This avoids
|
|
223
232
|
* reactivity issues where `rows` hasn't yet changed but something like workspaces has (stale values stored against fresh key)
|
|
224
233
|
*/
|
|
225
|
-
sortGeneration:
|
|
234
|
+
sortGeneration: undefined,
|
|
235
|
+
listAutoRefreshToggleEnabled: paginationUtils.listAutoRefreshToggleEnabled({ rootGetters: this.$store.getters }),
|
|
226
236
|
};
|
|
227
237
|
},
|
|
228
238
|
|
|
@@ -238,7 +248,8 @@ export default {
|
|
|
238
248
|
}
|
|
239
249
|
},
|
|
240
250
|
immediate: true
|
|
241
|
-
}
|
|
251
|
+
},
|
|
252
|
+
|
|
242
253
|
},
|
|
243
254
|
|
|
244
255
|
computed: {
|
|
@@ -528,7 +539,6 @@ export default {
|
|
|
528
539
|
pluralLabel: this.$store.getters['type-map/labelFor'](this.schema, 99),
|
|
529
540
|
};
|
|
530
541
|
},
|
|
531
|
-
|
|
532
542
|
},
|
|
533
543
|
|
|
534
544
|
methods: {
|
|
@@ -589,8 +599,9 @@ export default {
|
|
|
589
599
|
if (event.key === 'Enter') {
|
|
590
600
|
this.keyAction('detail');
|
|
591
601
|
}
|
|
592
|
-
}
|
|
593
|
-
|
|
602
|
+
},
|
|
603
|
+
|
|
604
|
+
}
|
|
594
605
|
};
|
|
595
606
|
</script>
|
|
596
607
|
|
|
@@ -646,7 +657,24 @@ export default {
|
|
|
646
657
|
v-if="showGrouping"
|
|
647
658
|
#header-right
|
|
648
659
|
>
|
|
649
|
-
<slot
|
|
660
|
+
<slot
|
|
661
|
+
name="header-right"
|
|
662
|
+
/>
|
|
663
|
+
</template>
|
|
664
|
+
|
|
665
|
+
<template
|
|
666
|
+
v-if="externalPaginationEnabled"
|
|
667
|
+
#watch-controls
|
|
668
|
+
>
|
|
669
|
+
<!-- See https://github.com/rancher/dashboard/issues/14359 -->
|
|
670
|
+
<ToggleSwitch
|
|
671
|
+
v-if="listAutoRefreshToggleEnabled"
|
|
672
|
+
class="auto-update"
|
|
673
|
+
:value="watching"
|
|
674
|
+
name="label-system-toggle"
|
|
675
|
+
:on-label="t('resourceTable.autoRefresh.label')"
|
|
676
|
+
@update:value="toggleWatch"
|
|
677
|
+
/>
|
|
650
678
|
</template>
|
|
651
679
|
|
|
652
680
|
<template #group-by="{group: thisGroup}">
|
|
@@ -694,3 +722,9 @@ export default {
|
|
|
694
722
|
</template>
|
|
695
723
|
</SortableTable>
|
|
696
724
|
</template>
|
|
725
|
+
|
|
726
|
+
<style lang="scss" scoped>
|
|
727
|
+
.auto-update {
|
|
728
|
+
min-width: 150px; height: 40px
|
|
729
|
+
}
|
|
730
|
+
</style>
|