@rancher/shell 3.0.9-rc.4 → 3.0.9-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/brand/suse/metadata.json +2 -1
- package/assets/translations/en-us.yaml +91 -3
- package/components/ActionMenuShell.vue +1 -1
- package/components/Inactivity.vue +2 -2
- package/components/Resource/Detail/Card/ExtrasCard.vue +49 -15
- package/components/Resource/Detail/Card/__tests__/ExtrasCard.test.ts +111 -0
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +0 -17
- package/components/Resource/Detail/Masthead/index.vue +11 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +3 -1
- package/components/Resource/Detail/Metadata/index.vue +1 -1
- package/components/Resource/Detail/ResourceRow.vue +1 -1
- package/components/ResourceDetail/Masthead/latest.vue +12 -2
- package/components/ResourceList/index.vue +9 -0
- package/components/ResourceTable.vue +38 -4
- package/components/Tabbed/Tab.vue +4 -0
- package/components/Tabbed/index.vue +4 -1
- package/components/__tests__/ProjectRow.test.ts +60 -0
- package/components/form/ChangePassword.vue +41 -35
- package/components/form/ResourceQuota/Project.vue +42 -1
- package/components/form/ResourceQuota/ProjectRow.vue +71 -4
- package/components/form/ResourceQuota/__tests__/Project.test.ts +63 -0
- package/components/form/SelectOrCreateAuthSecret.vue +6 -1
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +35 -0
- package/components/formatter/MachineSummaryGraph.vue +10 -2
- package/components/nav/TopLevelMenu.helper.ts +50 -2
- package/components/nav/TopLevelMenu.vue +14 -0
- package/components/nav/Type.vue +5 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +3 -3
- package/components/nav/__tests__/Type.test.ts +6 -4
- package/config/product/explorer.js +4 -3
- package/config/product/manager.js +18 -1
- package/config/router/navigation-guards/authentication.js +8 -9
- package/config/types.js +10 -2
- package/detail/auditlog.cattle.io.auditpolicy.vue +19 -0
- package/detail/management.cattle.io.user.vue +1 -2
- package/detail/node.vue +0 -1
- package/detail/provisioning.cattle.io.cluster.vue +2 -1
- package/dialog/ChangePasswordDialog.vue +8 -0
- package/dialog/GenericPrompt.vue +20 -3
- package/dialog/ScaleMachineDownDialog.vue +65 -15
- package/dialog/SearchDialog.vue +10 -2
- package/dialog/__tests__/ScaleMachineDownDialog.test.ts +184 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +89 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -1
- package/edit/auditlog.cattle.io.auditpolicy/AdditionalRedactions.vue +114 -0
- package/edit/auditlog.cattle.io.auditpolicy/Filters.vue +119 -0
- package/edit/auditlog.cattle.io.auditpolicy/General.vue +180 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/AdditionalRedactions.test.ts +327 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/Filters.test.ts +449 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/General.test.ts +472 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/AdditionalRedactions.test.ts.snap +27 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/Filters.test.ts.snap +39 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +174 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +29 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/index.test.ts +215 -0
- package/edit/auditlog.cattle.io.auditpolicy/index.vue +104 -0
- package/edit/auditlog.cattle.io.auditpolicy/types.ts +28 -0
- package/edit/fleet.cattle.io.gitrepo.vue +16 -1
- package/edit/management.cattle.io.project.vue +8 -2
- package/edit/management.cattle.io.user.vue +29 -34
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +178 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +21 -2
- package/edit/provisioning.cattle.io.cluster/shared.ts +4 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +1 -1
- package/list/auditlog.cattle.io.auditpolicy.vue +63 -0
- package/list/group.principal.vue +11 -15
- package/list/management.cattle.io.user.vue +11 -21
- package/machine-config/azure.vue +14 -0
- package/mixins/browser-tab-visibility.js +5 -4
- package/mixins/fetch.client.js +6 -0
- package/models/__tests__/auditlog.cattle.io.auditpolicy.test.ts +117 -0
- package/models/__tests__/workload.test.ts +49 -6
- package/models/auditlog.cattle.io.auditpolicy.js +46 -0
- package/models/cluster.x-k8s.io.machine.js +1 -1
- package/models/cluster.x-k8s.io.machinedeployment.js +5 -5
- package/models/event.js +5 -0
- package/models/ext.cattle.io.groupmembershiprefreshrequest.js +15 -0
- package/models/ext.cattle.io.passwordchangerequest.js +15 -0
- package/models/ext.cattle.io.selfuser.js +15 -0
- package/models/fleet-application.js +17 -7
- package/models/management.cattle.io.user.js +28 -31
- package/models/schema.js +18 -0
- package/models/secret.js +27 -24
- package/models/steve-schema.ts +39 -2
- package/models/workload.js +3 -2
- package/package.json +1 -1
- package/pages/account/index.vue +23 -16
- package/pages/auth/login.vue +15 -8
- package/pages/auth/setup.vue +52 -15
- package/pages/home.vue +9 -3
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -3
- package/plugins/dashboard-store/actions.js +7 -0
- package/plugins/dashboard-store/getters.js +23 -1
- package/plugins/dashboard-store/index.js +3 -2
- package/plugins/dashboard-store/mutations.js +4 -0
- package/plugins/dashboard-store/resource-class.js +12 -5
- package/plugins/steve/__tests__/steve-class.test.ts +167 -0
- package/plugins/steve/schema.d.ts +5 -0
- package/plugins/steve/steve-class.js +19 -0
- package/plugins/steve/steve-pagination-utils.ts +2 -1
- package/store/auth.js +57 -19
- package/store/notifications.ts +1 -1
- package/store/type-map.js +12 -1
- package/types/shell/index.d.ts +10 -14
- package/types/store/dashboard-store.types.ts +7 -0
- package/utils/pagination-wrapper.ts +11 -3
- package/vue.config.js +26 -13
- package/edit/provisioning.cattle.io.cluster/defaults.ts +0 -1
|
@@ -163,6 +163,9 @@ generic:
|
|
|
163
163
|
externalIps: External IPs
|
|
164
164
|
internalIps: Internal IPs
|
|
165
165
|
opensInNewTab: Opens in a new tab
|
|
166
|
+
autogeneratedCreated:
|
|
167
|
+
title: "{resource} created"
|
|
168
|
+
message: "{id} has been created."
|
|
166
169
|
|
|
167
170
|
tabs:
|
|
168
171
|
addItem: Add a new tab item
|
|
@@ -307,6 +310,7 @@ nav:
|
|
|
307
310
|
clusterNotFound: Cluster { clusterId } not found
|
|
308
311
|
productNotFound: Product { productNotFound } not found
|
|
309
312
|
resourceNotFound: Resource type { resource } not found
|
|
313
|
+
resourceListNotListable: Resource type { resource } cannot be listed
|
|
310
314
|
resourceListNotFound: Resource type { resource } not found, unable to display list
|
|
311
315
|
resourceIdNotFound: Resource { resource } with id { fqid } not found, unable to display resource details
|
|
312
316
|
reload: Reload
|
|
@@ -1447,6 +1451,8 @@ changePassword:
|
|
|
1447
1451
|
failedToChange: Failed to change password
|
|
1448
1452
|
failedDeleteKey: Failed to delete key
|
|
1449
1453
|
failedDeleteKeys: Failed to delete keys
|
|
1454
|
+
cannotChange: This user does not have permissions to change their password
|
|
1455
|
+
cannotFetchSelf: This user does not have permissions to get their user information
|
|
1450
1456
|
|
|
1451
1457
|
chartHeading:
|
|
1452
1458
|
overview: Overview
|
|
@@ -1859,7 +1865,7 @@ cluster:
|
|
|
1859
1865
|
acceleratedNetworking:
|
|
1860
1866
|
label: Accelerated Networking
|
|
1861
1867
|
availabilitySet:
|
|
1862
|
-
label: Availability Set
|
|
1868
|
+
label: Availability Set
|
|
1863
1869
|
description: Availability sets are used to protect applications from hardware failures within an Azure data center.
|
|
1864
1870
|
availabilityZone:
|
|
1865
1871
|
label: Availability Zone
|
|
@@ -1884,6 +1890,7 @@ cluster:
|
|
|
1884
1890
|
label: Location
|
|
1885
1891
|
managedDisks:
|
|
1886
1892
|
label: Use Managed Disks
|
|
1893
|
+
deprecationWarning: 'Azure is retiring unmanaged disk support on March 31, 2026. After this date, VMs using unmanaged disks will be stopped and deallocated. Enable "Use Managed Disks" to avoid service disruption.'
|
|
1887
1894
|
managedDisksSize:
|
|
1888
1895
|
label: Managed Disk Size
|
|
1889
1896
|
nsg:
|
|
@@ -3112,6 +3119,7 @@ fleet:
|
|
|
3112
3119
|
label: Authentication
|
|
3113
3120
|
git: Git Authentication
|
|
3114
3121
|
helm: Helm Authentication
|
|
3122
|
+
githubdotcomPasswordBanner: 'GitHub no longer supports password authentication for repositories. Please enter a personal access token with the required permissions instead of your GitHub password.'
|
|
3115
3123
|
caBundle:
|
|
3116
3124
|
label: Certificates
|
|
3117
3125
|
placeholder: "Paste in one or more certificates, starting with -----BEGIN CERTIFICATE----"
|
|
@@ -3636,6 +3644,71 @@ import:
|
|
|
3636
3644
|
other {# Resources}
|
|
3637
3645
|
}
|
|
3638
3646
|
|
|
3647
|
+
auditPolicy:
|
|
3648
|
+
active: Active
|
|
3649
|
+
inactive: Inactive
|
|
3650
|
+
reasons:
|
|
3651
|
+
PolicyNotYetActivated: Not yet activated
|
|
3652
|
+
PolicyIsActive: Active
|
|
3653
|
+
PolicyIsInvalid: Invalid
|
|
3654
|
+
PolicyWasDisabled: Disabled
|
|
3655
|
+
general:
|
|
3656
|
+
title: General
|
|
3657
|
+
enabled:
|
|
3658
|
+
label: Enabled
|
|
3659
|
+
title: Enabled
|
|
3660
|
+
checkbox: Enables this audit policy
|
|
3661
|
+
verbosity:
|
|
3662
|
+
label: Log Verbosity
|
|
3663
|
+
title: Log Verbosity
|
|
3664
|
+
banner: Audit Log Verbosity and install time Audit Log settings are additive.
|
|
3665
|
+
level:
|
|
3666
|
+
0: 0 - Log request and response metadata
|
|
3667
|
+
1: 1 - Log request and response headers
|
|
3668
|
+
2: 2 - Log request body
|
|
3669
|
+
3: 3 - Log response body
|
|
3670
|
+
title: Log Levels
|
|
3671
|
+
label: Level
|
|
3672
|
+
tooltip: Each log level is cumulative, higher log levels include the data from lower levels. Each log entry contains both request and response information.
|
|
3673
|
+
requestResponse:
|
|
3674
|
+
tooltip: Override the Log Level and explicitly include Headers / Body
|
|
3675
|
+
request:
|
|
3676
|
+
title: Request
|
|
3677
|
+
requestHeaders: Request Headers
|
|
3678
|
+
requestBody: Request Body
|
|
3679
|
+
response:
|
|
3680
|
+
title: Response
|
|
3681
|
+
responseHeaders: Response Headers
|
|
3682
|
+
responseBody: Response Body
|
|
3683
|
+
filters:
|
|
3684
|
+
add: Add Filter
|
|
3685
|
+
title: Filters
|
|
3686
|
+
action:
|
|
3687
|
+
title: Action
|
|
3688
|
+
label: Action
|
|
3689
|
+
allow: Allow
|
|
3690
|
+
deny: Deny
|
|
3691
|
+
placeholder: Allow/Deny
|
|
3692
|
+
requestURI:
|
|
3693
|
+
title: Request URI
|
|
3694
|
+
label: Request URI
|
|
3695
|
+
placeholder: e.g. /foo/.*
|
|
3696
|
+
additionalRedactions:
|
|
3697
|
+
title: Additional Redactions
|
|
3698
|
+
headers:
|
|
3699
|
+
title: Headers
|
|
3700
|
+
label: Headers
|
|
3701
|
+
placeholder: e.g. Cache.*
|
|
3702
|
+
add: Add Header
|
|
3703
|
+
paths:
|
|
3704
|
+
title: Paths
|
|
3705
|
+
label: Paths
|
|
3706
|
+
tooltip: Paths redacts information from request and response bodies based on json path expressions
|
|
3707
|
+
placeholder: e.g. $.gitCommit
|
|
3708
|
+
add: Add Path
|
|
3709
|
+
error:
|
|
3710
|
+
enableOrDisable: "{flag, select, enable {Error when enabling - {id}} disable {Error when disabling - {id}} other {Error - {id}}}"
|
|
3711
|
+
|
|
3639
3712
|
ingress:
|
|
3640
3713
|
description: Ingresses route incoming traffic from the internet to Services within the cluster based on the hostname and path specified in the request. You can expose multiple Services on the same external IP address and port.
|
|
3641
3714
|
certificates:
|
|
@@ -5714,7 +5787,11 @@ promptScaleMachineDown:
|
|
|
5714
5787
|
attemptingToRemove: "You are attempting to delete {count} {type}"
|
|
5715
5788
|
retainedMachine1: At least one Machine must exist for roles Control Plane and Etcd.
|
|
5716
5789
|
retainedMachine2: <b>{ name }</b> will remain
|
|
5717
|
-
|
|
5790
|
+
scaling: |-
|
|
5791
|
+
{count, plural,
|
|
5792
|
+
=1 {This machine pool is still reconciling. A different node may be deleted instead of the one you selected. It’s best to wait until reconciliation finishes.<br> Do you still want to mark this node for deletion?}
|
|
5793
|
+
other {At least one of these machine pools is still reconciling. Different nodes may be deleted instead of the ones you selected. It’s best to wait until reconciliation finishes.<br> Do you still want to mark these nodes for deletion?}
|
|
5794
|
+
}
|
|
5718
5795
|
promptSlo:
|
|
5719
5796
|
title: "Log out"
|
|
5720
5797
|
text: "Log out of only Rancher, or all {name} applications."
|
|
@@ -5932,6 +6009,9 @@ rbac:
|
|
|
5932
6009
|
label: Login Access
|
|
5933
6010
|
clustertemplaterevisions-create:
|
|
5934
6011
|
label: Create RKE Template Revisions
|
|
6012
|
+
proxy-endpoints-manage:
|
|
6013
|
+
label: Manage Rancher Proxy
|
|
6014
|
+
description: Allows the user to manage settings for proxying HTTP requests via Rancher
|
|
5935
6015
|
errors:
|
|
5936
6016
|
escalation: You cannot assign Global Permissions that are higher than your own. Please verify the permissions you are attempting to assign.
|
|
5937
6017
|
|
|
@@ -6178,6 +6258,7 @@ selectOrCreateAuthSecret:
|
|
|
6178
6258
|
basic:
|
|
6179
6259
|
username: Username
|
|
6180
6260
|
password: Password
|
|
6261
|
+
passwordPersonalAccessToken: Password/Personal Access Token
|
|
6181
6262
|
rke:
|
|
6182
6263
|
info: "An RKE Auth Config secret contains the username and password concatenated and base64 encoded into the secret's 'auth' key"
|
|
6183
6264
|
namespaceGroup: "Namespace: {name}"
|
|
@@ -6332,6 +6413,7 @@ setup:
|
|
|
6332
6413
|
useRandom: Use a randomly generated password
|
|
6333
6414
|
copyRandom: Copy random password to clipboard
|
|
6334
6415
|
welcome: Welcome to {vendor}!
|
|
6416
|
+
setup: Setup
|
|
6335
6417
|
|
|
6336
6418
|
sortableTable:
|
|
6337
6419
|
ariaLabel:
|
|
@@ -7881,6 +7963,7 @@ typeDescription:
|
|
|
7881
7963
|
logging.banzaicloud.io.output: An output defines which logging providers that logs can be sent to. The output needs to be in the same namespace as the flow that is using it.
|
|
7882
7964
|
group.principal: Assigning global roles to a group only works with external auth providers that support groups. Local authorization does not support groups.
|
|
7883
7965
|
management.cattle.io.oidcclient: Here you can add applications to Rancher's single sign-on identity provider
|
|
7966
|
+
auditlog.cattle.io.auditpolicy: Define rules that determine which Rancher API events are logged and the level of detail they contain.
|
|
7884
7967
|
|
|
7885
7968
|
typeLabel:
|
|
7886
7969
|
management.cattle.io.oidcclient: |-
|
|
@@ -8033,6 +8116,11 @@ typeLabel:
|
|
|
8033
8116
|
one { Resource Quota }
|
|
8034
8117
|
other { Resource Quotas }
|
|
8035
8118
|
}
|
|
8119
|
+
auditlog.cattle.io.auditpolicy: |-
|
|
8120
|
+
{count, plural,
|
|
8121
|
+
one { Audit Log Policy }
|
|
8122
|
+
other { Audit Log Policies }
|
|
8123
|
+
}
|
|
8036
8124
|
# pruh-mee-thee-eyes https://www.prometheus.io/docs/introduction/faq/#what-is-the-plural-of-prometheus
|
|
8037
8125
|
monitoring.coreos.com.prometheus: |-
|
|
8038
8126
|
{count, plural,
|
|
@@ -9182,7 +9270,7 @@ component:
|
|
|
9182
9270
|
events: Events
|
|
9183
9271
|
extrasCard:
|
|
9184
9272
|
title: Extras
|
|
9185
|
-
message: 'Consider installing additional <
|
|
9273
|
+
message: 'Consider installing additional <extensionsLink>extensions</extensionsLink> and / or <clusterToolsLink>cluster tools</clusterToolsLink> to enrich your Rancher experience.'
|
|
9186
9274
|
scaler:
|
|
9187
9275
|
ariaLabel:
|
|
9188
9276
|
increase: Increase {resourceName}
|
|
@@ -97,7 +97,7 @@ const menuOptions = () => {
|
|
|
97
97
|
<template>
|
|
98
98
|
<rc-dropdown-menu
|
|
99
99
|
:button-variant="buttonVariant || 'link'"
|
|
100
|
-
:button-size="buttonSize || '
|
|
100
|
+
:button-size="buttonSize || 'medium'"
|
|
101
101
|
:button-aria-label="buttonAriaLabel"
|
|
102
102
|
:dropdown-aria-label="dropdownAriaLabel"
|
|
103
103
|
:options="menuOptions()"
|
|
@@ -120,10 +120,10 @@ export default {
|
|
|
120
120
|
|
|
121
121
|
methods: {
|
|
122
122
|
async initializeInactivityData() {
|
|
123
|
-
const
|
|
123
|
+
const canGetUserAct = this.$store.getters[`management/canGet`](EXT.USER_ACTIVITY);
|
|
124
124
|
const canListTokens = this.$store.getters[`rancher/canList`](NORMAN.TOKEN);
|
|
125
125
|
|
|
126
|
-
if (
|
|
126
|
+
if (canGetUserAct && canListTokens) {
|
|
127
127
|
const tokens = await this.$store.dispatch('rancher/findAll', { type: NORMAN.TOKEN, opt: { watch: false } });
|
|
128
128
|
|
|
129
129
|
this.tokens = tokens;
|
|
@@ -3,37 +3,71 @@ import Card from '@shell/components/Resource/Detail/Card/index.vue';
|
|
|
3
3
|
import { useI18n } from '@shell/composables/useI18n';
|
|
4
4
|
import { useStore } from 'vuex';
|
|
5
5
|
import { BLANK_CLUSTER } from '@shell/store/store-types';
|
|
6
|
+
import { isAdminUser } from '@shell/store/type-map';
|
|
7
|
+
import { DOCS_BASE } from '@shell/config/private-label';
|
|
6
8
|
</script>
|
|
7
9
|
<script setup lang="ts">
|
|
8
|
-
import
|
|
10
|
+
import RichTranslation from '@shell/components/RichTranslation.vue';
|
|
11
|
+
import { computed } from 'vue';
|
|
9
12
|
|
|
10
13
|
const store = useStore();
|
|
11
|
-
const router = useRouter();
|
|
12
14
|
const i18n = useI18n(store);
|
|
15
|
+
const isAdmin = computed(() => isAdminUser(store.getters));
|
|
13
16
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
params: { cluster: BLANK_CLUSTER }
|
|
17
|
-
}).href;
|
|
17
|
+
const extensionsRoute = { name: 'c-cluster-uiplugins', params: { cluster: BLANK_CLUSTER } };
|
|
18
|
+
const extensionsDocsUrl = `${ DOCS_BASE }/integrations-in-rancher/rancher-extensions`;
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
params: { cluster: BLANK_CLUSTER }
|
|
22
|
-
}).href;
|
|
20
|
+
const clusterToolsRoute = { name: 'c-cluster-explorer-tools' };
|
|
21
|
+
const clusterToolsDocsUrl = `${ DOCS_BASE }/reference-guides/rancher-cluster-tools`;
|
|
23
22
|
</script>
|
|
24
23
|
|
|
25
24
|
<template>
|
|
26
25
|
<Card :title="i18n.t('component.resource.detail.card.extrasCard.title')">
|
|
27
|
-
<p
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
<p class="message text-deemphasized">
|
|
27
|
+
<RichTranslation k="component.resource.detail.card.extrasCard.message">
|
|
28
|
+
<template #extensionsLink="{ content }">
|
|
29
|
+
<router-link
|
|
30
|
+
v-if="isAdmin"
|
|
31
|
+
class="secondary text-deemphasized"
|
|
32
|
+
:to="extensionsRoute"
|
|
33
|
+
>
|
|
34
|
+
{{ content }}
|
|
35
|
+
</router-link>
|
|
36
|
+
<a
|
|
37
|
+
v-else
|
|
38
|
+
class="secondary text-deemphasized"
|
|
39
|
+
:href="extensionsDocsUrl"
|
|
40
|
+
target="_blank"
|
|
41
|
+
>
|
|
42
|
+
{{ content }}
|
|
43
|
+
</a>
|
|
44
|
+
</template>
|
|
45
|
+
<template #clusterToolsLink="{ content }">
|
|
46
|
+
<router-link
|
|
47
|
+
v-if="isAdmin"
|
|
48
|
+
class="secondary-text-link"
|
|
49
|
+
:to="clusterToolsRoute"
|
|
50
|
+
>
|
|
51
|
+
{{ content }}
|
|
52
|
+
</router-link>
|
|
53
|
+
<a
|
|
54
|
+
v-else
|
|
55
|
+
class="secondary-text-link"
|
|
56
|
+
:href="clusterToolsDocsUrl"
|
|
57
|
+
target="_blank"
|
|
58
|
+
>
|
|
59
|
+
{{ content }}
|
|
60
|
+
</a>
|
|
61
|
+
</template>
|
|
62
|
+
</RichTranslation>
|
|
63
|
+
</p>
|
|
31
64
|
</Card>
|
|
32
65
|
</template>
|
|
33
66
|
|
|
34
67
|
<style lang="scss" scoped>
|
|
35
68
|
.message {
|
|
36
69
|
margin: 0;
|
|
37
|
-
|
|
70
|
+
margin-top: -2px;
|
|
71
|
+
line-height: 20px;
|
|
38
72
|
}
|
|
39
73
|
</style>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { createStore } from 'vuex';
|
|
3
|
+
import ExtrasCard from '@shell/components/Resource/Detail/Card/ExtrasCard.vue';
|
|
4
|
+
import Card from '@shell/components/Resource/Detail/Card/index.vue';
|
|
5
|
+
import RichTranslation from '@shell/components/RichTranslation.vue';
|
|
6
|
+
|
|
7
|
+
const TRANSLATION_KEY = 'component.resource.detail.card.extrasCard.message';
|
|
8
|
+
const TRANSLATION_VALUE = 'Consider installing additional <extensionsLink>extensions</extensionsLink> and / or <clusterToolsLink>cluster tools</clusterToolsLink> to enrich your Rancher experience.';
|
|
9
|
+
|
|
10
|
+
jest.mock('@shell/store/type-map', () => ({ isAdminUser: jest.fn() }));
|
|
11
|
+
jest.mock('@shell/config/private-label', () => ({ DOCS_BASE: 'https://docs.example.com' }));
|
|
12
|
+
|
|
13
|
+
const { isAdminUser } = require('@shell/store/type-map');
|
|
14
|
+
|
|
15
|
+
function createMockStore() {
|
|
16
|
+
return createStore({
|
|
17
|
+
getters: {
|
|
18
|
+
'i18n/t': () => (key: string) => {
|
|
19
|
+
if (key === TRANSLATION_KEY) {
|
|
20
|
+
return TRANSLATION_VALUE;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return key;
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function mountExtrasCard({ admin = false } = {}) {
|
|
30
|
+
isAdminUser.mockReturnValue(admin);
|
|
31
|
+
|
|
32
|
+
const store = createMockStore();
|
|
33
|
+
|
|
34
|
+
return mount(ExtrasCard, {
|
|
35
|
+
global: {
|
|
36
|
+
plugins: [store],
|
|
37
|
+
stubs: { 'router-link': { template: '<a :href="JSON.stringify(to)" :class="$attrs.class"><slot /></a>', props: ['to'] } },
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('component: ExtrasCard', () => {
|
|
43
|
+
it('should render the Card component with the correct title', () => {
|
|
44
|
+
const wrapper = mountExtrasCard();
|
|
45
|
+
const card = wrapper.findComponent(Card);
|
|
46
|
+
|
|
47
|
+
expect(card.exists()).toBe(true);
|
|
48
|
+
expect(card.props('title')).toStrictEqual('component.resource.detail.card.extrasCard.title');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should render the RichTranslation component with the correct key', () => {
|
|
52
|
+
const wrapper = mountExtrasCard();
|
|
53
|
+
const richTranslation = wrapper.findComponent(RichTranslation);
|
|
54
|
+
|
|
55
|
+
expect(richTranslation.exists()).toBe(true);
|
|
56
|
+
expect(richTranslation.props('k')).toStrictEqual(TRANSLATION_KEY);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('when user is admin', () => {
|
|
60
|
+
it('should render router-link for extensions', () => {
|
|
61
|
+
const wrapper = mountExtrasCard({ admin: true });
|
|
62
|
+
const links = wrapper.findAll('a');
|
|
63
|
+
const extensionsLink = links.find((l) => l.text() === 'extensions');
|
|
64
|
+
|
|
65
|
+
expect(extensionsLink).toBeDefined();
|
|
66
|
+
expect(extensionsLink!.classes()).toContain('secondary');
|
|
67
|
+
expect(extensionsLink!.classes()).toContain('text-deemphasized');
|
|
68
|
+
expect(extensionsLink!.attributes('href')).toContain('c-cluster-uiplugins');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should render router-link for cluster tools', () => {
|
|
72
|
+
const wrapper = mountExtrasCard({ admin: true });
|
|
73
|
+
const links = wrapper.findAll('a');
|
|
74
|
+
const clusterToolsLink = links.find((l) => l.text() === 'cluster tools');
|
|
75
|
+
|
|
76
|
+
expect(clusterToolsLink).toBeDefined();
|
|
77
|
+
expect(clusterToolsLink!.classes()).toContain('secondary-text-link');
|
|
78
|
+
expect(clusterToolsLink!.attributes('href')).toContain('c-cluster-explorer-tools');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should not render external anchor tags', () => {
|
|
82
|
+
const wrapper = mountExtrasCard({ admin: true });
|
|
83
|
+
const anchors = wrapper.findAll('a[target="_blank"]');
|
|
84
|
+
|
|
85
|
+
expect(anchors).toHaveLength(0);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('when user is not admin', () => {
|
|
90
|
+
it('should render an external link for extensions', () => {
|
|
91
|
+
const wrapper = mountExtrasCard({ admin: false });
|
|
92
|
+
const links = wrapper.findAll('a[target="_blank"]');
|
|
93
|
+
const extensionsLink = links.find((l) => l.text() === 'extensions');
|
|
94
|
+
|
|
95
|
+
expect(extensionsLink).toBeDefined();
|
|
96
|
+
expect(extensionsLink!.attributes('href')).toStrictEqual('https://docs.example.com/integrations-in-rancher/rancher-extensions');
|
|
97
|
+
expect(extensionsLink!.classes()).toContain('secondary');
|
|
98
|
+
expect(extensionsLink!.classes()).toContain('text-deemphasized');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should render an external link for cluster tools', () => {
|
|
102
|
+
const wrapper = mountExtrasCard({ admin: false });
|
|
103
|
+
const links = wrapper.findAll('a[target="_blank"]');
|
|
104
|
+
const clusterToolsLink = links.find((l) => l.text() === 'cluster tools');
|
|
105
|
+
|
|
106
|
+
expect(clusterToolsLink).toBeDefined();
|
|
107
|
+
expect(clusterToolsLink!.attributes('href')).toStrictEqual('https://docs.example.com/reference-guides/rancher-cluster-tools');
|
|
108
|
+
expect(clusterToolsLink!.classes()).toContain('secondary-text-link');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -50,21 +50,4 @@ describe('component: Masthead/index', () => {
|
|
|
50
50
|
|
|
51
51
|
expect(cardsComponent.props('resource')).toStrictEqual(mockResource);
|
|
52
52
|
});
|
|
53
|
-
|
|
54
|
-
it('should render Cards with mb-20 class', () => {
|
|
55
|
-
const wrapper = mount(Masthead, {
|
|
56
|
-
props: defaultProps,
|
|
57
|
-
global: {
|
|
58
|
-
stubs: {
|
|
59
|
-
TitleBar: true,
|
|
60
|
-
Metadata: true,
|
|
61
|
-
Cards: true
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const cardsComponent = wrapper.findComponent(Cards);
|
|
67
|
-
|
|
68
|
-
expect(cardsComponent.classes()).toContain('mb-20');
|
|
69
|
-
});
|
|
70
53
|
});
|
|
@@ -24,10 +24,11 @@ const props = defineProps<MastheadProps>();
|
|
|
24
24
|
</template>
|
|
25
25
|
</TitleBar>
|
|
26
26
|
<Metadata
|
|
27
|
+
class="metadata-section"
|
|
27
28
|
v-bind="props.metadataProps"
|
|
28
29
|
/>
|
|
29
30
|
<Cards
|
|
30
|
-
class="
|
|
31
|
+
class="cards-section"
|
|
31
32
|
:resource="props.titleBarProps.resource"
|
|
32
33
|
/>
|
|
33
34
|
</div>
|
|
@@ -35,8 +36,14 @@ const props = defineProps<MastheadProps>();
|
|
|
35
36
|
|
|
36
37
|
<style lang='scss' scoped>
|
|
37
38
|
.masthead {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
:deep() .metadata-section {
|
|
40
|
+
margin-top: 16px;
|
|
41
|
+
margin-bottom: 24px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.cards-section {
|
|
45
|
+
margin: 0;
|
|
46
|
+
margin-bottom: 24px;
|
|
47
|
+
}
|
|
41
48
|
}
|
|
42
49
|
</style>
|
|
@@ -64,11 +64,11 @@ const store = useStore();
|
|
|
64
64
|
/>
|
|
65
65
|
<Metadata
|
|
66
66
|
v-bind="metadataProps"
|
|
67
|
-
class="
|
|
67
|
+
class="metadata-section"
|
|
68
68
|
/>
|
|
69
69
|
<Cards
|
|
70
70
|
v-if="props.isCustomDetailOrEdit"
|
|
71
|
-
class="
|
|
71
|
+
class="cards-section"
|
|
72
72
|
:resource="props.value"
|
|
73
73
|
/>
|
|
74
74
|
</div>
|
|
@@ -79,4 +79,14 @@ const store = useStore();
|
|
|
79
79
|
margin: 0;
|
|
80
80
|
margin-top: 16px;
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
:deep() .metadata-section {
|
|
84
|
+
margin-top: 16px;
|
|
85
|
+
margin-bottom: 24px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.cards-section {
|
|
89
|
+
margin: 0;
|
|
90
|
+
margin-bottom: 24px;
|
|
91
|
+
}
|
|
82
92
|
</style>
|
|
@@ -86,6 +86,15 @@ export default {
|
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
88
|
|
|
89
|
+
beforeMount() {
|
|
90
|
+
const inStore = this.$store.getters['currentStore'](this.resource);
|
|
91
|
+
const canList = this.$store.getters[`${ inStore }/canList`](this.resource);
|
|
92
|
+
|
|
93
|
+
if (!canList) {
|
|
94
|
+
this.$store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceListNotListable', { resource: this.schema.id }, true)));
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
|
|
89
98
|
data() {
|
|
90
99
|
const getters = this.$store.getters;
|
|
91
100
|
const params = { ...this.$route.params };
|
|
@@ -5,6 +5,7 @@ import { mapPref, GROUP_RESOURCES } from '@shell/store/prefs';
|
|
|
5
5
|
import ButtonGroup from '@shell/components/ButtonGroup';
|
|
6
6
|
import SortableTable from '@shell/components/SortableTable';
|
|
7
7
|
import { NAMESPACE, AGE } from '@shell/config/table-headers';
|
|
8
|
+
import { COUNT } from '@shell/config/types';
|
|
8
9
|
import { findBy } from '@shell/utils/array';
|
|
9
10
|
import { ExtensionPoint, TableColumnLocation, TableLocation } from '@shell/core/types';
|
|
10
11
|
import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
|
|
@@ -238,6 +239,7 @@ export default {
|
|
|
238
239
|
*/
|
|
239
240
|
sortGeneration: undefined,
|
|
240
241
|
listAutoRefreshToggleEnabled: paginationUtils.listAutoRefreshToggleEnabled({ rootGetters: this.$store.getters }),
|
|
242
|
+
hasSearchFilter: false,
|
|
241
243
|
};
|
|
242
244
|
},
|
|
243
245
|
|
|
@@ -600,6 +602,26 @@ export default {
|
|
|
600
602
|
pluralLabel: this.$store.getters['type-map/labelFor'](this.schema, 99),
|
|
601
603
|
};
|
|
602
604
|
},
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Get the counts data by namespace for the current resource type
|
|
608
|
+
*/
|
|
609
|
+
namespaceCounts() {
|
|
610
|
+
if (!this.inStore || !this.schema?.id) {
|
|
611
|
+
return {};
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const counts = this.$store.getters[`${ this.inStore }/all`](COUNT)?.[0]?.counts || {};
|
|
615
|
+
|
|
616
|
+
return counts[this.schema.id]?.namespaces || {};
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Whether we should show namespace counts in group tabs
|
|
621
|
+
*/
|
|
622
|
+
showNamespaceCounts() {
|
|
623
|
+
return (this.group === 'namespace' || this.group === 'metadata.namespace') && this.isNamespaced && !this.hasSearchFilter;
|
|
624
|
+
},
|
|
603
625
|
},
|
|
604
626
|
|
|
605
627
|
methods: {
|
|
@@ -671,6 +693,8 @@ export default {
|
|
|
671
693
|
}
|
|
672
694
|
});
|
|
673
695
|
}
|
|
696
|
+
|
|
697
|
+
this.hasSearchFilter = !!arg?.filtering?.searchQuery;
|
|
674
698
|
}
|
|
675
699
|
}
|
|
676
700
|
};
|
|
@@ -750,10 +774,15 @@ export default {
|
|
|
750
774
|
</template>
|
|
751
775
|
|
|
752
776
|
<template #group-by="{group: thisGroup}">
|
|
753
|
-
<div
|
|
754
|
-
v-clean-html="thisGroup.ref"
|
|
755
|
-
|
|
756
|
-
|
|
777
|
+
<div class="group-tab">
|
|
778
|
+
<span v-clean-html="thisGroup.ref" />
|
|
779
|
+
<span
|
|
780
|
+
v-if="showNamespaceCounts && Number.isInteger(namespaceCounts[thisGroup.rows?.[0]?.metadata?.namespace]?.count)"
|
|
781
|
+
class="count"
|
|
782
|
+
>
|
|
783
|
+
({{ namespaceCounts[thisGroup.rows?.[0]?.metadata?.namespace]?.count }})
|
|
784
|
+
</span>
|
|
785
|
+
</div>
|
|
757
786
|
</template>
|
|
758
787
|
|
|
759
788
|
<!-- Pass down templates provided by the caller -->
|
|
@@ -799,4 +828,9 @@ export default {
|
|
|
799
828
|
.auto-update {
|
|
800
829
|
min-width: 150px; height: 40px
|
|
801
830
|
}
|
|
831
|
+
|
|
832
|
+
.group-tab .count {
|
|
833
|
+
opacity: 0.7;
|
|
834
|
+
margin-left: 2px;
|
|
835
|
+
}
|
|
802
836
|
</style>
|
|
@@ -359,7 +359,7 @@ export default {
|
|
|
359
359
|
>{{ tab.badge }}</span>
|
|
360
360
|
<i
|
|
361
361
|
v-if="hasErrorIcon(tab)"
|
|
362
|
-
v-clean-tooltip="t('validation.tab')"
|
|
362
|
+
v-clean-tooltip="tab.errorIconTooltip || t('validation.tab')"
|
|
363
363
|
class="conditions-alert-icon icon-error"
|
|
364
364
|
/>
|
|
365
365
|
</a>
|
|
@@ -417,11 +417,13 @@ export default {
|
|
|
417
417
|
:name="tab.name"
|
|
418
418
|
:label="tab.label"
|
|
419
419
|
:label-key="tab.labelKey"
|
|
420
|
+
:label-icon="tab.labelIcon"
|
|
420
421
|
:weight="tab.weight"
|
|
421
422
|
:tooltip="tab.tooltip"
|
|
422
423
|
:show-header="tab.showHeader"
|
|
423
424
|
:display-alert-icon="tab.displayAlertIcon"
|
|
424
425
|
:error="tab.error"
|
|
426
|
+
:error-icon-tooltip="tab.errorIconTooltip"
|
|
425
427
|
:badge="tab.badge"
|
|
426
428
|
>
|
|
427
429
|
<component
|
|
@@ -504,6 +506,7 @@ export default {
|
|
|
504
506
|
.conditions-alert-icon {
|
|
505
507
|
color: var(--error);
|
|
506
508
|
padding-left: 4px;
|
|
509
|
+
margin-left: auto;
|
|
507
510
|
}
|
|
508
511
|
|
|
509
512
|
&:last-child {
|