@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.
Files changed (200) hide show
  1. package/assets/images/icons/document.svg +3 -0
  2. package/assets/images/vendor/cognito.svg +1 -0
  3. package/assets/styles/app.scss +1 -0
  4. package/assets/styles/base/_basic.scss +10 -0
  5. package/assets/styles/base/_spacing.scss +29 -0
  6. package/assets/styles/global/_layout.scss +1 -1
  7. package/assets/styles/themes/_dark.scss +25 -0
  8. package/assets/styles/themes/_light.scss +65 -0
  9. package/assets/translations/en-us.yaml +322 -24
  10. package/assets/translations/zh-hans.yaml +8 -5
  11. package/components/Certificates.vue +5 -0
  12. package/components/FilterPanel.vue +156 -0
  13. package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
  14. package/components/IconOrSvg.vue +14 -35
  15. package/components/PromptRemove.vue +5 -1
  16. package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
  17. package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
  18. package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
  19. package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
  20. package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
  21. package/components/Resource/Detail/Card/Scaler.vue +89 -0
  22. package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
  23. package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
  24. package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
  25. package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
  26. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
  27. package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
  28. package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
  29. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
  30. package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
  31. package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
  32. package/components/Resource/Detail/Card/index.vue +56 -0
  33. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
  34. package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
  35. package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
  36. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
  37. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
  38. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
  39. package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
  40. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
  41. package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
  42. package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
  43. package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
  44. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
  45. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
  46. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
  47. package/components/Resource/Detail/Metadata/composables.ts +29 -0
  48. package/components/Resource/Detail/Metadata/index.vue +66 -0
  49. package/components/Resource/Detail/Page.vue +22 -0
  50. package/components/Resource/Detail/PercentageBar.vue +40 -0
  51. package/components/Resource/Detail/ResourceRow.vue +119 -0
  52. package/components/Resource/Detail/SpacedRow.vue +14 -0
  53. package/components/Resource/Detail/StatusBar.vue +59 -0
  54. package/components/Resource/Detail/StatusRow.vue +61 -0
  55. package/components/Resource/Detail/TitleBar/Title.vue +13 -0
  56. package/components/Resource/Detail/TitleBar/Top.vue +14 -0
  57. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
  58. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
  59. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
  60. package/components/Resource/Detail/TitleBar/composable.ts +31 -0
  61. package/components/Resource/Detail/TitleBar/index.vue +124 -0
  62. package/components/Resource/Detail/Top/index.vue +34 -0
  63. package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
  64. package/components/ResourceDetail/__tests__/index.test.ts +114 -0
  65. package/components/ResourceDetail/index.vue +64 -562
  66. package/components/ResourceDetail/legacy.vue +545 -0
  67. package/components/ResourceTable.vue +41 -7
  68. package/components/SlideInPanelManager.vue +76 -8
  69. package/components/SortableTable/index.vue +13 -2
  70. package/components/SortableTable/selection.js +21 -8
  71. package/components/StatusBadge.vue +6 -4
  72. package/components/SubtleLink.vue +25 -0
  73. package/components/Wizard.vue +12 -1
  74. package/components/YamlEditor.vue +1 -1
  75. package/components/__tests__/FilterPanel.test.ts +81 -0
  76. package/components/auth/AuthBanner.vue +2 -3
  77. package/components/auth/RoleDetailEdit.vue +45 -3
  78. package/components/auth/login/oidc.vue +6 -1
  79. package/components/fleet/FleetApplications.vue +181 -0
  80. package/components/fleet/FleetHelmOps.vue +115 -0
  81. package/components/fleet/FleetIntro.vue +58 -28
  82. package/components/fleet/FleetNoWorkspaces.vue +5 -1
  83. package/components/fleet/FleetOCIStorageSecret.vue +171 -0
  84. package/components/fleet/FleetRepos.vue +38 -76
  85. package/components/fleet/FleetResources.vue +50 -22
  86. package/components/fleet/FleetSummary.vue +26 -51
  87. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
  88. package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
  89. package/components/fleet/dashboard/Empty.vue +73 -0
  90. package/components/fleet/dashboard/ResourceCard.vue +183 -0
  91. package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
  92. package/components/fleet/dashboard/ResourceDetails.vue +196 -0
  93. package/components/fleet/dashboard/ResourcePanel.vue +376 -0
  94. package/components/form/ArrayList.vue +6 -0
  95. package/components/form/SimpleSecretSelector.vue +8 -2
  96. package/components/form/ValueFromResource.vue +31 -19
  97. package/components/formatter/FleetApplicationClustersReady.vue +77 -0
  98. package/components/formatter/FleetApplicationSource.vue +71 -0
  99. package/components/formatter/FleetSummaryGraph.vue +7 -0
  100. package/components/nav/Header.vue +8 -7
  101. package/components/nav/TopLevelMenu.helper.ts +55 -34
  102. package/components/nav/TopLevelMenu.vue +11 -0
  103. package/components/nav/Type.vue +4 -1
  104. package/composables/useI18n.ts +12 -11
  105. package/config/labels-annotations.js +14 -11
  106. package/config/product/auth.js +1 -0
  107. package/config/product/fleet.js +70 -17
  108. package/config/query-params.js +3 -1
  109. package/config/roles.ts +1 -0
  110. package/config/router/routes.js +20 -2
  111. package/config/secret.ts +15 -0
  112. package/config/settings.ts +3 -2
  113. package/config/table-headers.js +52 -22
  114. package/config/types.js +2 -0
  115. package/core/plugin-helpers.ts +3 -2
  116. package/detail/fleet.cattle.io.cluster.vue +28 -15
  117. package/detail/fleet.cattle.io.gitrepo.vue +10 -1
  118. package/detail/fleet.cattle.io.helmop.vue +157 -0
  119. package/dialog/HelmOpForceUpdateDialog.vue +132 -0
  120. package/dialog/RedeployWorkloadDialog.vue +164 -0
  121. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
  122. package/edit/auth/oidc.vue +159 -93
  123. package/edit/fleet.cattle.io.gitrepo.vue +26 -33
  124. package/edit/fleet.cattle.io.helmop.vue +997 -0
  125. package/edit/management.cattle.io.fleetworkspace.vue +43 -10
  126. package/list/fleet.cattle.io.gitrepo.vue +1 -1
  127. package/list/fleet.cattle.io.helmop.vue +108 -0
  128. package/list/namespace.vue +5 -2
  129. package/mixins/auth-config.js +8 -1
  130. package/mixins/preset.js +100 -0
  131. package/mixins/resource-fetch-api-pagination.js +2 -0
  132. package/mixins/resource-fetch.js +1 -1
  133. package/mixins/resource-table-watch.js +45 -0
  134. package/models/__tests__/chart.test.ts +273 -0
  135. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  136. package/models/chart.js +144 -2
  137. package/models/fleet-application.js +385 -0
  138. package/models/fleet.cattle.io.bundle.js +9 -8
  139. package/models/fleet.cattle.io.gitrepo.js +41 -365
  140. package/models/fleet.cattle.io.helmop.js +228 -0
  141. package/models/management.cattle.io.authconfig.js +1 -0
  142. package/models/management.cattle.io.fleetworkspace.js +12 -0
  143. package/models/workload.js +14 -18
  144. package/package.json +2 -1
  145. package/pages/auth/verify.vue +13 -1
  146. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
  147. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
  148. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
  149. package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
  150. package/pages/c/_cluster/apps/charts/index.vue +302 -484
  151. package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
  152. package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
  153. package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
  154. package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
  155. package/pages/c/_cluster/fleet/application/create.vue +340 -0
  156. package/pages/c/_cluster/fleet/application/index.vue +139 -0
  157. package/pages/c/_cluster/fleet/graph/config.js +277 -0
  158. package/pages/c/_cluster/fleet/index.vue +772 -330
  159. package/pages/explorer/resource/detail/configmap.vue +19 -0
  160. package/plugins/dashboard-store/actions.js +31 -9
  161. package/plugins/dashboard-store/getters.js +34 -21
  162. package/plugins/dashboard-store/mutations.js +51 -7
  163. package/plugins/dashboard-store/resource-class.js +14 -2
  164. package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
  165. package/plugins/steve/actions.js +3 -0
  166. package/plugins/steve/steve-pagination-utils.ts +14 -13
  167. package/plugins/steve/subscribe.js +229 -42
  168. package/rancher-components/BadgeState/BadgeState.vue +3 -1
  169. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
  170. package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
  171. package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
  172. package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
  173. package/rancher-components/RcItemCard/index.ts +2 -0
  174. package/store/auth.js +1 -0
  175. package/store/catalog.js +62 -24
  176. package/store/index.js +33 -14
  177. package/store/slideInPanel.ts +6 -0
  178. package/store/type-map.js +1 -0
  179. package/types/fleet.d.ts +35 -0
  180. package/types/resources/settings.d.ts +19 -1
  181. package/types/shell/index.d.ts +339 -272
  182. package/types/store/dashboard-store.types.ts +17 -3
  183. package/types/store/pagination.types.ts +6 -1
  184. package/types/store/subscribe.types.ts +50 -0
  185. package/utils/auth.js +32 -3
  186. package/utils/fleet-types.ts +0 -0
  187. package/utils/fleet.ts +200 -1
  188. package/utils/pagination-utils.ts +26 -1
  189. package/utils/pagination-wrapper.ts +132 -50
  190. package/utils/settings.ts +4 -1
  191. package/utils/style.ts +39 -0
  192. package/utils/validators/formRules/__tests__/index.test.ts +36 -3
  193. package/utils/validators/formRules/index.ts +10 -3
  194. package/utils/window.js +11 -7
  195. package/components/__tests__/ApplicationCard.test.ts +0 -27
  196. package/components/cards/ApplicationCard.vue +0 -145
  197. package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
  198. package/config/secret.js +0 -14
  199. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
  200. /package/{components/form/SSHKnownHosts → dialog}/__tests__/KnownHostsEditDialog.test.ts +0 -0
@@ -0,0 +1,36 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Card from '@shell/components/Resource/Detail/Card/index.vue';
3
+
4
+ describe('component: Card/index', () => {
5
+ it('should render title and default slot', async() => {
6
+ const title = 'title';
7
+ const content = 'content';
8
+ const wrapper = mount(Card, { props: { title }, slots: { default: content } });
9
+
10
+ expect(wrapper.find('.title').element.innerHTML).toStrictEqual(title);
11
+ });
12
+
13
+ it('should allow you to override the heading with slot', async() => {
14
+ const title = 'title';
15
+ const content = 'content';
16
+ const wrapper = mount(Card, { props: { title }, slots: { heading: content } });
17
+
18
+ expect(wrapper.find('.title').exists()).toBeFalsy();
19
+ expect(wrapper.find('.heading').element.innerHTML).toStrictEqual(content);
20
+ });
21
+
22
+ it('should allow you to override the title with slot', async() => {
23
+ const title = 'title';
24
+ const content = 'content';
25
+ const wrapper = mount(Card, { props: { title }, slots: { title: content } });
26
+
27
+ expect(wrapper.find('.title').element.innerHTML).toStrictEqual(content);
28
+ });
29
+
30
+ it('should allow you to insert heading-action with slot', async() => {
31
+ const content = '<div id="test">content</div>';
32
+ const wrapper = mount(Card, { slots: { 'heading-action': content } });
33
+
34
+ expect(wrapper.find('.heading #test').exists()).toBeTruthy();
35
+ });
36
+ });
@@ -0,0 +1,56 @@
1
+ <script setup lang="ts">
2
+ import VerticalGap from '@shell/components/Resource/Detail/Card/VerticalGap.vue';
3
+
4
+ export interface CardProps {
5
+ title?: string;
6
+ }
7
+
8
+ const { title } = defineProps<CardProps>();
9
+
10
+ </script>
11
+
12
+ <template>
13
+ <div class="detail-card">
14
+ <div class="heading">
15
+ <slot name="heading">
16
+ <div class="title">
17
+ <slot name="title">
18
+ {{ title }}
19
+ </slot>
20
+ </div>
21
+ </slot>
22
+ <slot name="heading-action" />
23
+ </div>
24
+ <VerticalGap />
25
+ <div class="body">
26
+ <slot name="default" />
27
+ </div>
28
+ </div>
29
+ </template>
30
+
31
+ <style lang="scss" scoped>
32
+ .detail-card {
33
+ padding: 16px;
34
+ border-radius: var(--border-radius-md);
35
+ border: 1px solid var(--border);
36
+
37
+ .heading {
38
+ display: flex;
39
+ justify-content: space-between;
40
+
41
+ height: 32px;
42
+
43
+ .title {
44
+ font-size: 18px;
45
+ font-weight: 600;
46
+ line-height: 21px;
47
+ }
48
+ }
49
+
50
+ .body {
51
+ display: flex;
52
+ flex-direction: column;
53
+ justify-content: flex-start;
54
+ }
55
+ }
56
+ </style>
@@ -0,0 +1,19 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Annotations from '@shell/components/Resource/Detail/Metadata/Annotations/index.vue';
3
+ import { createStore } from 'vuex';
4
+
5
+ describe('component: Metadata/Annotations', () => {
6
+ it('shoulder render KeyValue with the appropriate props', async() => {
7
+ const annotations = [{ key: 'key', value: 'value' }];
8
+ const wrapper = mount(Annotations, {
9
+ props: { annotations },
10
+ global: { provide: { store: createStore({}) }, stubs: { KeyValue: true } }
11
+ });
12
+
13
+ const keyValue = wrapper.getComponent<any>('key-value-stub');
14
+
15
+ expect(keyValue.props('propertyName')).toStrictEqual('component.resource.detail.metadata.annotations.title');
16
+ expect(keyValue.props('rows')).toStrictEqual(annotations);
17
+ expect(keyValue.props('outline')).toStrictEqual(true);
18
+ });
19
+ });
@@ -0,0 +1,12 @@
1
+ import { Annotation } from '@shell/components/Resource/Detail/Metadata/Annotations/index.vue';
2
+ import { computed, Ref, toValue } from 'vue';
3
+
4
+ export const useDefaultAnnotations = (resource: any): Ref<Annotation[]> => {
5
+ const resourceValue = toValue(resource);
6
+
7
+ return computed(() => {
8
+ const keyValuePairs = Object.entries<string>(resourceValue.annotations || {});
9
+
10
+ return keyValuePairs.map(([key, value]) => ({ key, value }));
11
+ });
12
+ };
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import KeyValue, { Row } from '@shell/components/Resource/Detail/Metadata/KeyValue.vue';
3
+ import { useI18n } from '@shell/composables/useI18n';
4
+ import { useStore } from 'vuex';
5
+
6
+ export type Annotation = Row;
7
+
8
+ export interface AnnotationsProps {
9
+ annotations: Annotation[];
10
+ }
11
+
12
+ </script>
13
+
14
+ <script setup lang="ts">
15
+ const { annotations } = defineProps<AnnotationsProps>();
16
+ const store = useStore();
17
+ const i18n = useI18n(store);
18
+ </script>
19
+
20
+ <template>
21
+ <KeyValue
22
+ :propertyName="i18n.t('component.resource.detail.metadata.annotations.title')"
23
+ :rows="annotations"
24
+ :outline="true"
25
+ />
26
+ </template>
@@ -0,0 +1,103 @@
1
+ import { mount, RouterLinkStub } from '@vue/test-utils';
2
+ import IdentifyingInformation from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue';
3
+ import Rectangle from '@shell/components/Resource/Detail/Metadata/Rectangle.vue';
4
+ import { markRaw } from 'vue';
5
+
6
+ describe('component: Metadata/IdentifyingInformation', () => {
7
+ const label = 'LABEL';
8
+ const value = 'VALUE';
9
+ const status = 'error';
10
+ const to = 'http://google.com';
11
+
12
+ it('should render container with identifying information', async() => {
13
+ const wrapper = mount(IdentifyingInformation, {
14
+ props: { rows: [] },
15
+ global: { stubs: { 'router-link': RouterLinkStub } }
16
+ });
17
+
18
+ expect(wrapper.find('.identifying-information').exists()).toBeTruthy();
19
+ });
20
+
21
+ it('should render basic label and value', async() => {
22
+ const wrapper = mount(IdentifyingInformation, {
23
+ props: {
24
+ rows: [
25
+ {
26
+ label,
27
+ value
28
+ }
29
+ ]
30
+ },
31
+ global: { stubs: { 'router-link': RouterLinkStub } }
32
+ });
33
+
34
+ expect(wrapper.find('.label').element.innerHTML.trim()).toStrictEqual(label);
35
+ expect(wrapper.find('.value span').element.innerHTML.trim()).toStrictEqual(value);
36
+ });
37
+
38
+ it('should render a link value', async() => {
39
+ const wrapper = mount(IdentifyingInformation, {
40
+ props: {
41
+ rows: [
42
+ {
43
+ label,
44
+ value,
45
+ to
46
+ }
47
+ ]
48
+ },
49
+ global: { stubs: { 'router-link': RouterLinkStub } }
50
+ });
51
+
52
+ expect(wrapper.find('.label').element.innerHTML.trim()).toStrictEqual(label);
53
+
54
+ const routerLinkComponent = wrapper.find('.value').getComponent(RouterLinkStub);
55
+
56
+ expect(routerLinkComponent.props('to')).toStrictEqual(to);
57
+ expect(routerLinkComponent.element.innerHTML).toStrictEqual(value);
58
+ });
59
+
60
+ it('should render a status value', async() => {
61
+ const wrapper = mount(IdentifyingInformation, {
62
+ props: {
63
+ rows: [
64
+ {
65
+ label,
66
+ value,
67
+ status
68
+ }
69
+ ]
70
+ },
71
+ global: { stubs: { 'router-link': RouterLinkStub } }
72
+ });
73
+
74
+ expect(wrapper.find('.label').element.innerHTML.trim()).toStrictEqual(label);
75
+ expect(wrapper.find('.value span').element.innerHTML.trim()).toStrictEqual(value);
76
+ expect(wrapper.find(`.value .status.${ status }`).exists()).toBeTruthy();
77
+ });
78
+
79
+ it('should render a valueOverride', async() => {
80
+ const valueOverride = {
81
+ component: markRaw(Rectangle),
82
+ props: { outline: false }
83
+ };
84
+ const wrapper = mount(IdentifyingInformation, {
85
+ props: {
86
+ rows: [
87
+ {
88
+ label,
89
+ value,
90
+ valueOverride
91
+ }
92
+ ]
93
+ },
94
+ global: { stubs: { 'router-link': RouterLinkStub } }
95
+ });
96
+
97
+ expect(wrapper.find('.label').element.innerHTML.trim()).toStrictEqual(label);
98
+
99
+ const testComponent = wrapper.find('.value').getComponent(Rectangle);
100
+
101
+ expect(testComponent.props('outline')).toStrictEqual(valueOverride.props.outline);
102
+ });
103
+ });
@@ -0,0 +1,281 @@
1
+ import { useI18n } from '@shell/composables/useI18n';
2
+ import { computed, ComputedRef, markRaw, toValue } from 'vue';
3
+ import LiveDate from '@shell/components/formatter/LiveDate.vue';
4
+ import LinkName from '@shell/components/formatter/LinkName.vue';
5
+ import Additional from '@shell/components/Resource/Detail/Additional.vue';
6
+ import { useStore } from 'vuex';
7
+ import CopyToClipboardText from '@shell/components/CopyToClipboardText.vue';
8
+ import IconText from '@shell/components/formatter/IconText.vue';
9
+ import { NODE } from '@shell/config/types';
10
+ import day from 'dayjs';
11
+ import { Row } from '@shell/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue';
12
+
13
+ export const useContainerRuntime = (resource: any): ComputedRef<Row> => {
14
+ const store = useStore();
15
+ const i18n = useI18n(store);
16
+ const resourceValue = toValue(resource);
17
+
18
+ return computed(() => ({
19
+ label: i18n.t('node.detail.detailTop.containerRuntime'),
20
+ valueOverride: {
21
+ component: markRaw(IconText),
22
+ props: { value: resourceValue.containerRuntimeVersion, iconClass: resourceValue.containerRuntimeIcon }
23
+ },
24
+ value: resourceValue.containerRuntimeVersion
25
+ }));
26
+ };
27
+
28
+ export const useExternalIp = (resource: any): ComputedRef<Row> => {
29
+ const store = useStore();
30
+ const i18n = useI18n(store);
31
+ const resourceValue = toValue(resource);
32
+
33
+ return computed(() => ({
34
+ label: i18n.t('node.detail.detailTop.externalIP'),
35
+ valueOverride: {
36
+ component: markRaw(CopyToClipboardText),
37
+ props: { text: resourceValue.externalIp }
38
+ },
39
+ value: resourceValue.externalIp
40
+ }));
41
+ };
42
+
43
+ export const usePodIp = (resource: any): ComputedRef<Row> => {
44
+ const store = useStore();
45
+ const i18n = useI18n(store);
46
+ const resourceValue = toValue(resource);
47
+
48
+ return computed(() => ({
49
+ label: i18n.t('workload.detailTop.podIP'),
50
+ value: resourceValue.status.podIP
51
+ }));
52
+ };
53
+
54
+ export const useWorkload = (resource: any): ComputedRef<Row> => {
55
+ const store = useStore();
56
+ const i18n = useI18n(store);
57
+ const resourceValue = toValue(resource);
58
+
59
+ return computed(() => ({
60
+ label: i18n.t('workload.detailTop.workload'),
61
+ valueOverride: {
62
+ component: markRaw(LinkName),
63
+ props: {
64
+ type: resourceValue.workloadRef.type,
65
+ value: resourceValue.workloadRef.name,
66
+ namespace: resourceValue.workloadRef.namespace
67
+ }
68
+ },
69
+ value: resourceValue.workloadRef.name
70
+ }));
71
+ };
72
+
73
+ export const useNode = (resource: any): ComputedRef<Row> => {
74
+ const store = useStore();
75
+ const i18n = useI18n(store);
76
+ const resourceValue = toValue(resource);
77
+
78
+ return computed(() => ({
79
+ label: i18n.t('workload.detailTop.node'),
80
+ valueOverride: {
81
+ component: markRaw(LinkName),
82
+ props: { type: NODE, value: resourceValue.spec.nodeName }
83
+ },
84
+ value: resourceValue.spec.nodeName
85
+ }));
86
+ };
87
+
88
+ export const useStarted = (resource: any): ComputedRef<Row> => {
89
+ const store = useStore();
90
+ const i18n = useI18n(store);
91
+ const resourceValue = toValue(resource);
92
+
93
+ return computed(() => ({
94
+ label: i18n.t('workload.detailTop.started'),
95
+ valueOverride: {
96
+ component: markRaw(LiveDate),
97
+ props: { addSuffix: true, value: resourceValue.status.startTime }
98
+ },
99
+ value: resourceValue.spec.nodeName
100
+ }));
101
+ };
102
+
103
+ export const useDuration = (resource: any): ComputedRef<Row> => {
104
+ const store = useStore();
105
+ const i18n = useI18n(store);
106
+ const resourceValue = toValue(resource);
107
+
108
+ const FACTORS = [60, 60, 24];
109
+ const LABELS = ['sec', 'min', 'hour', 'day'];
110
+ const value = computed(() => {
111
+ const { completionTime, startTime } = resourceValue.status;
112
+ const end = day(completionTime);
113
+ const start = day(startTime);
114
+ let diff = end.diff(start) / 1000;
115
+
116
+ let label: any;
117
+
118
+ let i = 0;
119
+
120
+ while (diff >= FACTORS[i] && i < FACTORS.length) {
121
+ diff /= FACTORS[i];
122
+ i++;
123
+ }
124
+
125
+ if (diff < 5) {
126
+ label = Math.floor(diff * 10) / 10;
127
+ } else {
128
+ label = Math.floor(diff);
129
+ }
130
+
131
+ label += ` ${ i18n.t(`unit.${ LABELS[i] }`, { count: label }) } `;
132
+ label = label.trim();
133
+
134
+ return label;
135
+ });
136
+
137
+ return computed(() => ({
138
+ label: i18n.t('workload.detailTop.duration'),
139
+ valueOverride: {
140
+ component: markRaw(LiveDate),
141
+ props: { value: value.value }
142
+ },
143
+ value: value.value
144
+ }));
145
+ };
146
+
147
+ export const useImage = (resource: any): ComputedRef<Row> => {
148
+ const store = useStore();
149
+ const i18n = useI18n(store);
150
+ const resourceValue = toValue(resource);
151
+
152
+ return computed(() => ({
153
+ label: i18n.t('component.resource.detail.metadata.identifyingInformation.image'),
154
+ value: resourceValue.imageNames,
155
+ valueOverride: {
156
+ component: markRaw(Additional),
157
+ props: { items: resourceValue.imageNames }
158
+ },
159
+ }));
160
+ };
161
+
162
+ export const useReady = (resource: any): ComputedRef<Row> => {
163
+ const store = useStore();
164
+ const i18n = useI18n(store);
165
+ const resourceValue = toValue(resource);
166
+
167
+ return computed(() => ({
168
+ label: i18n.t('component.resource.detail.metadata.identifyingInformation.ready'),
169
+ value: resourceValue.ready,
170
+ }));
171
+ };
172
+
173
+ export const useRestarts = (resource: any): ComputedRef<Row> => {
174
+ const store = useStore();
175
+ const i18n = useI18n(store);
176
+ const resourceValue = toValue(resource);
177
+
178
+ return computed(() => ({
179
+ label: i18n.t('workload.detailTop.podRestarts'),
180
+ value: resourceValue.restartCount
181
+ }));
182
+ };
183
+
184
+ export const useInternalIp = (resource: any): ComputedRef<Row> => {
185
+ const store = useStore();
186
+ const i18n = useI18n(store);
187
+ const resourceValue = toValue(resource);
188
+
189
+ return computed(() => ({
190
+ label: i18n.t('node.detail.detailTop.internalIP'),
191
+ valueOverride: {
192
+ component: markRaw(CopyToClipboardText),
193
+ props: { text: resourceValue.internalIp }
194
+ },
195
+ value: resourceValue.internalIp
196
+ }));
197
+ };
198
+
199
+ export const useVersion = (resource: any): ComputedRef<Row> => {
200
+ const store = useStore();
201
+ const i18n = useI18n(store);
202
+ const resourceValue = toValue(resource);
203
+
204
+ return computed(() => ({
205
+ label: i18n.t('node.detail.detailTop.version'),
206
+ value: resourceValue.version
207
+ }));
208
+ };
209
+
210
+ export const useOs = (resource: any): ComputedRef<Row> => {
211
+ const store = useStore();
212
+ const i18n = useI18n(store);
213
+ const resourceValue = toValue(resource);
214
+
215
+ return computed(() => ({
216
+ label: i18n.t('node.detail.detailTop.os'),
217
+ value: resourceValue.status.nodeInfo.osImage
218
+ }));
219
+ };
220
+
221
+ export const useLiveDate = (resource: any): ComputedRef<Row> => {
222
+ const store = useStore();
223
+ const i18n = useI18n(store);
224
+ const resourceValue = toValue(resource);
225
+
226
+ return computed(() => ({
227
+ label: i18n.t('component.resource.detail.metadata.identifyingInformation.age'),
228
+ valueOverride: {
229
+ component: markRaw(LiveDate),
230
+ props: { value: resourceValue.creationTimestamp }
231
+ },
232
+ value: resourceValue.age,
233
+ }));
234
+ };
235
+
236
+ export const useProject = (resource: any): ComputedRef<Row> => {
237
+ const store = useStore();
238
+ const i18n = useI18n(store);
239
+ const resourceValue = toValue(resource);
240
+
241
+ return computed(() => ({
242
+ label: i18n.t('component.resource.detail.metadata.identifyingInformation.project'),
243
+ value: resourceValue.project.nameDisplay,
244
+ to: '#'
245
+ }));
246
+ };
247
+
248
+ export const useDefaultIdentifyingInformation = (resource: any): ComputedRef<Row[]> => {
249
+ const store = useStore();
250
+ const i18n = useI18n(store);
251
+ const resourceValue = toValue(resource);
252
+ const liveDate = useLiveDate(resource);
253
+
254
+ return computed(() => [
255
+ {
256
+ label: i18n.t('component.resource.detail.metadata.identifyingInformation.namespace'),
257
+ value: resourceValue.namespace,
258
+ },
259
+ liveDate.value
260
+ ]);
261
+ };
262
+
263
+ export const useDefaultWorkloadIdentifyingInformation = (resource: any): ComputedRef<Row[]> => {
264
+ const store = useStore();
265
+ const i18n = useI18n(store);
266
+
267
+ const resourceValue = toValue(resource);
268
+
269
+ return computed(() => [
270
+ useImage(resource).value,
271
+ useReady(resource).value,
272
+ {
273
+ label: i18n.t('component.resource.detail.metadata.identifyingInformation.up-to-date'),
274
+ value: resourceValue.upToDate,
275
+ },
276
+ {
277
+ label: i18n.t('component.resource.detail.metadata.identifyingInformation.available'),
278
+ value: resourceValue.available,
279
+ },
280
+ ]);
281
+ };
@@ -0,0 +1,111 @@
1
+ <script lang="ts">
2
+ import { RouteLocationRaw } from 'vue-router';
3
+
4
+ export interface Row {
5
+ label: string;
6
+ value?: string;
7
+ valueOverride?: {
8
+ component: any,
9
+ props?: Object
10
+ },
11
+ to?: RouteLocationRaw;
12
+ status?: 'success' | 'warning' | 'info' | 'error',
13
+ }
14
+
15
+ export interface MetadataProps {
16
+ rows: Row[];
17
+ }
18
+ </script>
19
+
20
+ <script setup lang="ts">
21
+ const { rows } = defineProps<MetadataProps>();
22
+ </script>
23
+
24
+ <template>
25
+ <div class="identifying-information">
26
+ <div
27
+ v-for="row in rows"
28
+ :key="`${row.label}:${row.value}`"
29
+ class="row"
30
+ >
31
+ <div class="label text-muted">
32
+ {{ row.label }}
33
+ </div>
34
+ <div
35
+ v-if="row.valueOverride?.component"
36
+ class="value"
37
+ >
38
+ <component
39
+ :is="row.valueOverride?.component"
40
+ v-if="row.valueOverride?.component"
41
+ v-bind="row.valueOverride?.props"
42
+ />
43
+ </div>
44
+ <div
45
+ v-else
46
+ class="value"
47
+ >
48
+ <div
49
+ v-if="row.status"
50
+ :class="['status', row.status]"
51
+ />
52
+ <router-link
53
+ v-if="row.to"
54
+ :to="row.to"
55
+ >
56
+ {{ row.value }}
57
+ </router-link>
58
+ <span v-else>{{ row.value }}</span>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </template>
63
+
64
+ <style lang="scss" scoped>
65
+ .identifying-information {
66
+ display: flex;
67
+ flex-direction: column;
68
+
69
+ .row {
70
+ margin-bottom: 8px;
71
+
72
+ .value {
73
+ display: flex;
74
+ flex-direction: row;
75
+ align-items: center;
76
+ }
77
+
78
+ .label {
79
+ width: 30%;
80
+ min-width: 120px;
81
+ }
82
+
83
+ .status {
84
+ display: inline-block;
85
+ $size: 8px;
86
+ border-radius: 50%;
87
+ width: $size;
88
+ height: $size;
89
+ margin-right: 12px;
90
+
91
+ &.success {
92
+ background-color: var(--success);
93
+ }
94
+
95
+ &.warning {
96
+ background-color: var(--warning);
97
+ }
98
+
99
+ &.error {
100
+ background-color: var(--error);
101
+ }
102
+
103
+ &.info {
104
+ background-color: var(--info);
105
+ }
106
+ }
107
+
108
+ }
109
+
110
+ }
111
+ </style>