dashboard-shell-shell 3.0.5-test.1 → 3.0.5-test.10

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 (50) hide show
  1. package/assets/styles/base/_variables.scss +3 -3
  2. package/assets/styles/global/_button.scss +7 -7
  3. package/assets/styles/global/_tooltip.scss +4 -4
  4. package/assets/styles/themes/_light.scss +3 -1
  5. package/assets/translations/zh-hans.yaml +76 -0
  6. package/components/ActionDropdown.vue +1 -1
  7. package/components/CopyToClipboard.vue +15 -0
  8. package/components/Drawer/Chrome.vue +2 -2
  9. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +22 -22
  10. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +1 -1
  11. package/components/Drawer/ResourceDetailDrawer/index.vue +2 -1
  12. package/components/ExplorerMembers.vue +18 -3
  13. package/components/PodSecurityAdmission.vue +1 -1
  14. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +1 -3
  15. package/components/Resource/Detail/Metadata/KeyValue.vue +8 -4
  16. package/components/Resource/Detail/Metadata/index.vue +3 -1
  17. package/components/Resource/Detail/TitleBar/Title.vue +4 -3
  18. package/components/Resource/Detail/TitleBar/Top.vue +2 -0
  19. package/components/Resource/Detail/TitleBar/index.vue +109 -24
  20. package/components/ResourceDetail/Masthead/legacy.vue +235 -164
  21. package/components/ResourceDetail/legacy.vue +29 -13
  22. package/components/ResourceList/Masthead.vue +22 -14
  23. package/components/SortableTable/index.vue +2 -2
  24. package/components/Tabbed/Tab.vue +1 -1
  25. package/components/Tabbed/index.vue +51 -32
  26. package/components/auth/Principal.vue +35 -11
  27. package/components/breadcrumb/index.vue +316 -0
  28. package/components/form/LabeledSelect.vue +3 -2
  29. package/components/form/NameNsDescription.vue +5 -5
  30. package/components/form/Taints.vue +2 -1
  31. package/components/form/WorkloadPorts.vue +143 -123
  32. package/components/nav/Header.vue +3 -4
  33. package/components/nav/NamespaceFilter.vue +1 -2
  34. package/edit/workload/index.vue +3 -3
  35. package/package.json +1 -1
  36. package/pages/account/index.vue +25 -79
  37. package/pages/c/_cluster/auth/roles/index.vue +38 -5
  38. package/pkg/tsconfig.json +9 -9
  39. package/pkg/vue.config.js +2 -2
  40. package/plugins/dashboard-store/resource-class.js +28 -27
  41. package/rancher-components/BadgeState/BadgeState.vue +33 -52
  42. package/rancher-components/Banner/Banner.vue +2 -2
  43. package/rancher-components/RcDropdown/RcDropdownMenu.vue +8 -7
  44. package/scripts/publish-shell.sh +1 -1
  45. package/store/i18n.js +3 -0
  46. package/store/type-map.js +1 -1
  47. package/types/shell/index.d.ts +4 -30
  48. package/utils/error.js +3 -1
  49. package/utils/errorTranslate.json +15 -0
  50. package/vue.config.js +5 -5
@@ -280,145 +280,163 @@ export default {
280
280
  'show-ipam': showIpam,
281
281
  }"
282
282
  >
283
- <div class="service-type">
284
- <LabeledSelect
285
- v-model:value="row._serviceType"
286
- :mode="mode"
287
- :label="t('workload.container.ports.createService')"
288
- :options="serviceTypes"
289
- :disabled="canNotAccessService"
290
- :tooltip="serviceTypeTooltip"
291
- @update:value="queueUpdate"
292
- />
293
- </div>
294
-
295
- <div class="portName">
296
- <LabeledInput
297
- ref="name"
298
- v-model:value="row.name"
299
- :mode="mode"
300
- :label="t('workload.container.ports.name')"
301
- @update:value="queueUpdate"
302
- />
303
- </div>
283
+
284
+ <div class="row mt-20">
285
+ <div
286
+ v-if="!row._showHost && row._serviceType !== 'LoadBalancer' && row._serviceType !== 'NodePort'"
287
+ class="add-host col"
288
+ >
289
+ <button
290
+ :disabled="mode==='view'"
291
+ type="button"
292
+ class="btn btn-sm role-tertiary"
293
+ @click="row._showHost = true"
294
+ >
295
+ {{ t('workloadPorts.addHost') }}
296
+ </button>
297
+ </div>
304
298
 
305
- <div class="port">
306
- <LabeledInput
307
- v-model:value.number="row.containerPort"
308
- :mode="mode"
309
- type="number"
310
- min="1"
311
- max="65535"
312
- placeholder="e.g. 8080"
313
- :label="t('workload.container.ports.containerPort')"
314
- :required="row._serviceType === 'LoadBalancer' "
315
- @update:value="queueUpdate"
316
- />
317
- </div>
299
+ <div
300
+ v-if="showRemove"
301
+ class="remove"
302
+ >
303
+ <button
304
+ type="button"
305
+ class="btn role-link"
306
+ @click="remove(idx)"
307
+ >
308
+ {{ t('workloadPorts.remove') }}
309
+ </button>
310
+ </div>
318
311
 
319
- <div class="protocol col">
320
- <LabeledSelect
321
- v-model:value="row.protocol"
322
- :mode="mode"
323
- :options="workloadPortOptions"
324
- :multiple="false"
325
- :label="t('workload.container.ports.protocol')"
326
- @update:value="queueUpdate"
327
- />
328
312
  </div>
329
313
 
330
- <div
331
- v-if="row._showHost"
332
- class="targetPort"
333
- >
334
- <LabeledInput
335
- ref="port"
336
- v-model:value.number="row.hostPort"
337
- :mode="mode"
338
- type="number"
339
- min="1"
340
- max="65535"
341
- placeholder="e.g. 80"
342
- :label="t('workload.container.ports.hostPort')"
343
- @update:value="queueUpdate"
344
- />
314
+ <div class="row">
315
+ <div class="service-type col span-6">
316
+ <LabeledSelect
317
+ v-model:value="row._serviceType"
318
+ :mode="mode"
319
+ :label="t('workload.container.ports.createService')"
320
+ :options="serviceTypes"
321
+ :disabled="canNotAccessService"
322
+ :tooltip="serviceTypeTooltip"
323
+ @update:value="queueUpdate"
324
+ />
325
+ </div>
326
+
327
+ <div class="portName col span-6">
328
+ <LabeledInput
329
+ ref="name"
330
+ v-model:value="row.name"
331
+ :mode="mode"
332
+ :label="t('workload.container.ports.name')"
333
+ @update:value="queueUpdate"
334
+ />
335
+ </div>
345
336
  </div>
346
337
 
347
- <div
348
- v-if="row._showHost"
349
- class="hostip"
350
- >
351
- <LabeledInput
352
- ref="port"
353
- v-model:value="row.hostIP"
354
- :mode="mode"
355
- placeholder="e.g. 1.1.1.1"
356
- :label="t('workload.container.ports.hostIP')"
357
- @update:value="queueUpdate"
358
- />
338
+ <div class="row">
339
+ <div class="port col span-6">
340
+ <LabeledInput
341
+ v-model:value.number="row.containerPort"
342
+ :mode="mode"
343
+ type="number"
344
+ min="1"
345
+ max="65535"
346
+ placeholder="e.g. 8080"
347
+ :label="t('workload.container.ports.containerPort')"
348
+ :required="row._serviceType === 'LoadBalancer' "
349
+ @update:value="queueUpdate"
350
+ />
351
+ </div>
352
+
353
+ <div class="protocol col span-6">
354
+ <LabeledSelect
355
+ v-model:value="row.protocol"
356
+ :mode="mode"
357
+ :options="workloadPortOptions"
358
+ :multiple="false"
359
+ :label="t('workload.container.ports.protocol')"
360
+ @update:value="queueUpdate"
361
+ />
362
+ </div>
359
363
  </div>
360
364
 
361
- <div
362
- v-if="!row._showHost && row._serviceType !== 'LoadBalancer' && row._serviceType !== 'NodePort'"
363
- class="add-host"
364
- >
365
- <button
366
- :disabled="mode==='view'"
367
- type="button"
368
- class="btn btn-sm role-tertiary"
369
- @click="row._showHost = true"
365
+ <div class="row">
366
+ <div
367
+ v-if="row._showHost"
368
+ class="targetPort col span-6"
370
369
  >
371
- {{ t('workloadPorts.addHost') }}
372
- </button>
373
- </div>
374
-
375
- <div v-if="row._serviceType === 'LoadBalancer' || row._serviceType === 'NodePort'">
376
- <LabeledInput
377
- ref="port"
378
- v-model:value.number="row._listeningPort"
379
- type="number"
380
- :mode="mode"
381
- :label="t('workload.container.ports.listeningPort')"
382
- :required="row._serviceType === 'LoadBalancer' "
383
- @update:value="queueUpdate"
384
- />
385
- </div>
386
-
387
- <div v-if="showIpam && row._serviceType === 'LoadBalancer' && row.protocol === 'TCP'">
388
- <div v-if="idx === ipamIndex">
389
- <LabeledSelect
390
- v-model:value="row._ipam"
370
+ <LabeledInput
371
+ ref="port"
372
+ v-model:value.number="row.hostPort"
391
373
  :mode="mode"
392
- :options="ipamOptions"
393
- :label="t('servicesPage.harvester.ipam.label')"
394
- :disabled="mode === 'edit'"
374
+ type="number"
375
+ min="1"
376
+ max="65535"
377
+ placeholder="e.g. 80"
378
+ :label="t('workload.container.ports.hostPort')"
395
379
  @update:value="queueUpdate"
396
380
  />
397
381
  </div>
398
- <div v-else>
399
- <LabeledSelect
400
- v-model:value="rows[ipamIndex]._ipam"
382
+
383
+ <div
384
+ v-if="row._showHost"
385
+ class="hostip col span-6"
386
+ >
387
+ <LabeledInput
388
+ ref="port"
389
+ v-model:value="row.hostIP"
401
390
  :mode="mode"
402
- :options="ipamOptions"
403
- :label="t('servicesPage.harvester.ipam.label')"
404
- :disabled="true"
391
+ placeholder="e.g. 1.1.1.1"
392
+ :label="t('workload.container.ports.hostIP')"
405
393
  @update:value="queueUpdate"
406
394
  />
407
395
  </div>
408
396
  </div>
409
397
 
410
- <div
411
- v-if="showRemove"
412
- class="remove"
413
- >
414
- <button
415
- type="button"
416
- class="btn role-link"
417
- @click="remove(idx)"
398
+ <div class="row">
399
+ <div
400
+ v-if="row._serviceType === 'LoadBalancer' || row._serviceType === 'NodePort'"
401
+ class="col"
418
402
  >
419
- {{ t('workloadPorts.remove') }}
420
- </button>
403
+ <LabeledInput
404
+ ref="port"
405
+ v-model:value.number="row._listeningPort"
406
+ type="number"
407
+ :mode="mode"
408
+ :label="t('workload.container.ports.listeningPort')"
409
+ :required="row._serviceType === 'LoadBalancer' "
410
+ @update:value="queueUpdate"
411
+ />
412
+ </div>
421
413
  </div>
414
+
415
+ <div class="row">
416
+ <div v-if="showIpam && row._serviceType === 'LoadBalancer' && row.protocol === 'TCP'">
417
+ <div v-if="idx === ipamIndex">
418
+ <LabeledSelect
419
+ v-model:value="row._ipam"
420
+ :mode="mode"
421
+ :options="ipamOptions"
422
+ :label="t('servicesPage.harvester.ipam.label')"
423
+ :disabled="mode === 'edit'"
424
+ @update:value="queueUpdate"
425
+ />
426
+ </div>
427
+ <div v-else>
428
+ <LabeledSelect
429
+ v-model:value="rows[ipamIndex]._ipam"
430
+ :mode="mode"
431
+ :options="ipamOptions"
432
+ :label="t('servicesPage.harvester.ipam.label')"
433
+ :disabled="true"
434
+ @update:value="queueUpdate"
435
+ />
436
+ </div>
437
+ </div>
438
+ </div>
439
+
422
440
  </div>
423
441
  <div
424
442
  v-if="showAdd"
@@ -447,11 +465,11 @@ $checkbox: 75;
447
465
  }
448
466
  }
449
467
  .ports-headers, .ports-row{
450
- display: grid;
451
- grid-template-columns: 28% 28% 15% 10% 75px 0.5fr;
452
- grid-column-gap: $column-gutter;
453
- margin-bottom: 10px;
454
- align-items: center;
468
+ // display: grid;
469
+ // grid-template-columns: 28% 28% 15% 10% 75px 0.5fr;
470
+ // grid-column-gap: $column-gutter;
471
+ // margin-bottom: 20px;
472
+ // align-items: center;
455
473
  & .port{
456
474
  display: flex;
457
475
  justify-content: space-between;
@@ -475,6 +493,7 @@ $checkbox: 75;
475
493
  }
476
494
 
477
495
  .add-host {
496
+ margin-right: 20px;
478
497
  justify-self: center;
479
498
  }
480
499
 
@@ -492,6 +511,7 @@ $checkbox: 75;
492
511
 
493
512
  .remove BUTTON {
494
513
  padding: 0px;
514
+ min-width: auto;
495
515
  }
496
516
 
497
517
  .ports-row INPUT {
@@ -80,7 +80,7 @@ export default {
80
80
  ctx: this,
81
81
  showImportModal: false,
82
82
  showSearchModal: false,
83
- userIcon: require('~shell/assets/images/logo.png'),
83
+ userIcon: require('@shell/assets/images/logo.png'),
84
84
  };
85
85
  },
86
86
 
@@ -548,7 +548,6 @@ export default {
548
548
 
549
549
  <!-- ===== 左侧 logo 区域 ===== -->
550
550
  <div class="menu-spacer">
551
-
552
551
  <!-- 如果是单产品模式且不是 RancherInHarvester,就显示 logo 路由跳转 -->
553
552
  <router-link
554
553
  v-if="isSingleProduct && !isRancherInHarvester"
@@ -556,9 +555,9 @@ export default {
556
555
  >
557
556
 
558
557
  <!-- 显示用户头像或 logo 图片 -->
559
- <!-- <img
558
+ <img
560
559
  :src="userIcon"
561
- > -->
560
+ >
562
561
  </router-link>
563
562
  </div>
564
563
 
@@ -949,7 +949,6 @@ export default {
949
949
  .ns-filter {
950
950
  width: 280px;
951
951
  display: inline-block;
952
- border-radius: var(--border-radius);
953
952
 
954
953
  .ns-glass {
955
954
  top: 0;
@@ -968,9 +967,9 @@ export default {
968
967
  }
969
968
 
970
969
  .ns-clear {
971
- padding: 0 5px;
972
970
  &:hover {
973
971
  color: var(--primary);
972
+ cursor: pointer;
974
973
  }
975
974
  }
976
975
 
@@ -108,7 +108,7 @@ export default {
108
108
  >
109
109
  <div
110
110
  v-if="isCronJob"
111
- class="col span-3"
111
+ class="col span-6"
112
112
  >
113
113
  <LabeledInput
114
114
  v-model:value="spec.schedule"
@@ -122,7 +122,7 @@ export default {
122
122
  </div>
123
123
  <div
124
124
  v-if="isReplicable"
125
- class="col span-3"
125
+ class="col span-6"
126
126
  >
127
127
  <LabeledInput
128
128
  v-model:value.number="spec.replicas"
@@ -135,7 +135,7 @@ export default {
135
135
  </div>
136
136
  <div
137
137
  v-if="isStatefulSet"
138
- class="col span-3"
138
+ class="col span-6"
139
139
  >
140
140
  <LabeledSelect
141
141
  v-model:value="spec.serviceName"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashboard-shell-shell",
3
- "version": "3.0.5-test.1",
3
+ "version": "3.0.5-test.10",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -3,8 +3,7 @@ import BackLink from '@shell/components/BackLink';
3
3
  import { MANAGEMENT, NORMAN } from '@shell/config/types';
4
4
  import { SETTING } from '@shell/config/settings';
5
5
  import Loading from '@shell/components/Loading';
6
- // import Principal from '@shell/components/auth/Principal';
7
- import Principal from './pri.vue';
6
+ import Principal from '@shell/components/auth/Principal';
8
7
  import BackRoute from '@shell/mixins/back-link';
9
8
  import { mapGetters } from 'vuex';
10
9
 
@@ -17,7 +16,7 @@ const API_ENDPOINT = '/v3';
17
16
 
18
17
  export default {
19
18
  components: {
20
- CopyToClipboardText, BackLink, Banner, PromptChangePassword, Loading, ResourceTable, Principal, TabTitle
19
+ CopyToClipboardText, BackLink, Banner, Loading, ResourceTable, Principal, TabTitle
21
20
  },
22
21
  mixins: [BackRoute],
23
22
  async fetch() {
@@ -152,37 +151,21 @@ export default {
152
151
  <template>
153
152
  <Loading v-if="$fetchState.pending" />
154
153
  <div v-else>
155
- <!-- <BackLink :link="backLink" />
154
+ <BackLink :link="backLink" />
156
155
  <h1>
157
156
  <TabTitle breadcrumb="vendor-only">
158
157
  {{ t('accountAndKeys.title') }}
159
158
  </TabTitle>
160
- </h1> -->
161
- <div class="api-key-title mb-20">
162
- {{ t('accountAndKeys.title') }}
163
- </div>
159
+ </h1>
164
160
 
165
- <!-- <h2 v-t="'accountAndKeys.account.title'" /> -->
161
+ <h2 v-t="'accountAndKeys.account.title'" />
166
162
  <div class="account">
167
- <div class="account-title">
168
- {{ t('accountAndKeys.account.title') }}
169
- </div>
170
163
  <Principal
171
164
  :value="principal.id"
172
165
  :use-muted="false"
173
166
  :show-labels="true"
174
- :isShowPass="true"
175
- >
176
- <template #edit>
177
- <span
178
- v-if="canChangePassword"
179
- class="edit-pass-txt"
180
- @click="$refs.promptChangePassword.show(true)"
181
- >修改
182
- </span>
183
- </template>
184
- </Principal>
185
- <!-- <div>
167
+ />
168
+ <div>
186
169
  <button
187
170
  v-if="canChangePassword"
188
171
  role="button"
@@ -190,25 +173,17 @@ export default {
190
173
  type="button"
191
174
  class="btn role-primary"
192
175
  data-testid="account_change_password"
193
- @click="$refs.promptChangePassword.show(true)"
176
+ @click="showChangePasswordDialog"
194
177
  >
195
178
  {{ t("accountAndKeys.account.change") }}
196
179
  </button>
197
- </div> -->
180
+ </div>
198
181
  </div>
199
- <PromptChangePassword ref="promptChangePassword" />
200
182
 
201
- <!-- <hr role="none"> -->
202
- <div
203
- style=" border: 1px solid #d7d7d7;padding: 20px 20px 0px 20px;"
204
- class="mt-20">
183
+ <hr role="none">
205
184
  <div class="keys-header">
206
185
  <div>
207
- <!-- <h2 v-t="'accountAndKeys.apiKeys.title'" /> -->
208
- <div
209
- v-t="'accountAndKeys.apiKeys.title'"
210
- class="account-title mb-20"
211
- />
186
+ <h2 v-t="'accountAndKeys.apiKeys.title'" />
212
187
  <div class="api-url">
213
188
  <span>{{ t("accountAndKeys.apiKeys.apiEndpoint") }}</span>
214
189
  <CopyToClipboardText
@@ -217,10 +192,20 @@ export default {
217
192
  />
218
193
  </div>
219
194
  </div>
195
+ <button
196
+ v-if="apiKeySchema"
197
+ role="button"
198
+ :aria-label="t('accountAndKeys.apiKeys.add.label')"
199
+ class="btn role-primary add mb-20"
200
+ data-testid="account_create_api_keys"
201
+ @click="addKey"
202
+ >
203
+ {{ t('accountAndKeys.apiKeys.add.label') }}
204
+ </button>
220
205
  </div>
221
206
  <div
222
207
  v-if="apiKeySchema"
223
- class="keys mb-20"
208
+ class="keys"
224
209
  >
225
210
  <ResourceTable
226
211
  :schema="apiKeySchema"
@@ -231,20 +216,7 @@ export default {
231
216
  :search="true"
232
217
  :row-actions="true"
233
218
  :table-actions="true"
234
- >
235
- <template #header-right>
236
- <button
237
- v-if="apiKeySchema"
238
- role="button"
239
- :aria-label="t('accountAndKeys.apiKeys.add.label')"
240
- class="btn role-primary add mb-20"
241
- data-testid="account_create_api_keys"
242
- @click="addKey"
243
- >
244
- {{ t('accountAndKeys.apiKeys.add.label') }}
245
- </button>
246
- </template>
247
- </ResourceTable>
219
+ />
248
220
  </div>
249
221
  <div v-else>
250
222
  <Banner
@@ -253,7 +225,6 @@ export default {
253
225
  />
254
226
  </div>
255
227
  </div>
256
- </div>
257
228
  </template>
258
229
 
259
230
  <style lang='scss' scoped>
@@ -262,12 +233,8 @@ export default {
262
233
  }
263
234
 
264
235
  .account {
265
- /* display: flex;
266
- justify-content: space-between */
267
- border: 1px solid #D7D7D7;
268
- padding: 20px;
269
- box-sizing: border-box;
270
-
236
+ display: flex;
237
+ justify-content: space-between
271
238
  }
272
239
 
273
240
  .keys-header {
@@ -280,10 +247,8 @@ export default {
280
247
  .keys {
281
248
  display: flex;
282
249
  flex-direction: column;
283
- margin-top: 20px;
284
250
  .add {
285
251
  align-self: flex-end;
286
- margin-left: 10px
287
252
  }
288
253
  }
289
254
 
@@ -294,23 +259,4 @@ export default {
294
259
  margin-right: 6px;
295
260
  }
296
261
  }
297
- .api-key-title{
298
- font-size: 26px;
299
- /* line-height: 20px; */
300
- font-weight: 400;
301
- }
302
-
303
- .edit-pass-txt{
304
- cursor: pointer;
305
- color: var(--primary);
306
- }
307
-
308
- .table-account{
309
- border: 1px solid #d7d7d7;
310
- }
311
- .account-title{
312
- font-size: 14px;
313
- line-height: 19px;
314
- margin-bottom: 16px;
315
- }
316
262
  </style>
@@ -162,7 +162,7 @@ export default {
162
162
  </h1>
163
163
  <div style="margin: 20px 0;">支持管理员创建与管理不同角色‌,每个角色可预设特定权限集合,可简化用户权限分配流程,提升权限管理的灵活性与安全性。</div>
164
164
  </div>
165
- <div class="actions-container actions-container-box">
165
+ <!-- <div class="actions-container actions-container-box">
166
166
  <div class="actions">
167
167
  <router-link
168
168
  v-if="canCreate"
@@ -172,7 +172,7 @@ export default {
172
172
  {{ createLabel }}
173
173
  </router-link>
174
174
  </div>
175
- </div>
175
+ </div> -->
176
176
  </header>
177
177
  <Tabbed>
178
178
  <Tab
@@ -184,7 +184,18 @@ export default {
184
184
  <ResourceTable
185
185
  :schema="tabs[GLOBAL].schema"
186
186
  :rows="globalResources"
187
- />
187
+ >
188
+ <template #header-right>
189
+ <router-link
190
+ v-if="canCreate"
191
+ :to="createLocation"
192
+ style="margin-right: 10px;"
193
+ class="btn role-primary"
194
+ >
195
+ {{ createLabel }}
196
+ </router-link>
197
+ </template>
198
+ </ResourceTable>
188
199
  </Tab>
189
200
 
190
201
  <Tab
@@ -197,7 +208,18 @@ export default {
197
208
  :schema="tabs[CLUSTER].schema"
198
209
  :headers="tabs[CLUSTER].headers"
199
210
  :rows="clusterResources"
200
- />
211
+ >
212
+ <template #header-right>
213
+ <router-link
214
+ v-if="canCreate"
215
+ :to="createLocation"
216
+ style="margin-right: 10px;"
217
+ class="btn role-primary"
218
+ >
219
+ {{ createLabel }}
220
+ </router-link>
221
+ </template>
222
+ </ResourceTable>
201
223
  </Tab>
202
224
 
203
225
  <Tab
@@ -210,7 +232,18 @@ export default {
210
232
  :schema="tabs[PROJECT].schema"
211
233
  :headers="tabs[PROJECT].headers"
212
234
  :rows="namespaceResources"
213
- />
235
+ >
236
+ <template #header-right>
237
+ <router-link
238
+ v-if="canCreate"
239
+ :to="createLocation"
240
+ style="margin-right: 10px;"
241
+ class="btn role-primary"
242
+ >
243
+ {{ createLabel }}
244
+ </router-link>
245
+ </template>
246
+ </ResourceTable>
214
247
  </Tab>
215
248
  </Tabbed>
216
249
  </div>