Add 3-dots menu to resource lists

This commit is contained in:
Emmy Leke
2023-06-07 14:55:34 +01:00
committed by EmmyMay
parent ed0ac43b75
commit 46f5455bd0
50 changed files with 1230 additions and 237 deletions
@@ -35,7 +35,9 @@
}"
class="h-100"
clickable
@search="filterList"
@row-clicked="handleRowClicked"
>
<template #header>
<b-button
@@ -61,25 +63,13 @@
v-if="canGrant"
data-test-id="button-permissions"
resource="corteza::system:apigw-route/*"
button-variant="light"
class="ml-1"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
>
<font-awesome-icon :icon="['fas', 'lock']" />
{{ $t('permissions') }}
</c-permissions-button>
<b-dropdown
v-if="false"
variant="link"
right
menu-class="shadow-sm"
:text="$t('export')"
>
<b-dropdown-item-button variant="link">
{{ $t('yaml') }}
</b-dropdown-item-button>
</b-dropdown>
<c-resource-list-status-filter
v-model="filter.deleted"
data-test-id="filter-deleted-routes"
@@ -92,23 +82,72 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: r }">
<b-dropdown
v-if="(areActionsVisible({ resource: r, conditions: ['canDeleteApigwRoute', 'canGrant'] }))"
boundary="viewport"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="r.routeID && canGrant"
>
<c-permissions-button
:title="r.endpoint || r.routeID"
:target="r.endpoint || r.routeID"
:resource="`corteza::system:apigw-route/${r.routeID}`"
button-variant="link"
class="d-print-none border-0 text-decoration-none text-dark regular-font rounded-0"
>
<font-awesome-icon :icon="['fas', 'lock']" />
{{ $t('permissions') }}
</c-permissions-button>
</b-dropdown-item>
<b-dropdown-item
v-if="r.canDeleteApigwRoute"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(r)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
v-if="!r.deletedAt"
class="p-1"
>{{ $t('delete') }}</span>
<span
v-else
class="p-1"
>{{ $t('undelete') }}</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-card>
</template>
<script>
import { mapGetters } from 'vuex'
import { mapGetters, mapActions } from 'vuex'
import listHelpers from 'corteza-webapp-admin/src/mixins/listHelpers'
import moment from 'moment'
import { components } from '@cortezaproject/corteza-vue'
@@ -163,8 +202,7 @@ export default {
},
{
key: 'actions',
label: '',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -189,9 +227,22 @@ export default {
},
methods: {
...mapActions({
incLoader: 'ui/incLoader',
decLoader: 'ui/decLoader',
}),
items () {
return this.procListResults(this.$SystemAPI.apigwRouteList(this.encodeListParams()))
},
handleDelete (route) {
this.handleItemDelete({
resource: route,
resourceName: 'apigwRoute',
locale: 'gateway',
})
},
},
}
</script>
@@ -22,6 +22,7 @@
</b-list-group>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { components } from '@cortezaproject/corteza-vue'
@@ -32,8 +32,10 @@
resourceSingle: $t('general:label.connection.single'),
resourcePlural: $t('general:label.connection.plural')
}"
clickable
hide-search
class="h-100"
@row-clicked="handleRowClicked"
>
<template #header>
<b-button
@@ -55,16 +57,49 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: c }">
<b-dropdown
v-if="c.canDeleteConnection"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="dropdown-item text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(c)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
v-if="!c.deletedAt"
class="p-1"
>{{ $t('delete') }}
</span>
<span
v-else
class="p-1"
>{{ $t('undelete') }}
</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-card>
@@ -131,8 +166,7 @@ export default {
},
{
key: 'actions',
label: '',
class: 'text-right',
class: 'actions',
},
].map(c => ({
// Generate column label translation key
@@ -143,9 +177,18 @@ export default {
},
methods: {
items () {
return this.procListResults(this.$SystemAPI.dalConnectionList(this.encodeListParams()))
},
handleDelete (connection) {
this.handleItemDelete({
resource: connection,
resourceName: 'dalConnection',
locale: 'connection',
})
},
},
}
</script>
@@ -47,6 +47,7 @@ import {
faQuestion,
faStamp,
faInfoCircle,
faEllipsisV,
} from '@fortawesome/free-solid-svg-icons'
import {
@@ -111,4 +112,5 @@ library.add(
faQuestion,
faStamp,
faInfoCircle,
faEllipsisV,
)
@@ -197,5 +197,39 @@ export default {
genericRowClass (item) {
return { 'text-secondary': item && !!item.deletedAt }
},
handleRowClicked (item) {
this.$router.push({ name: this.editRoute, params: { [this.primaryKey]: item[this.primaryKey] } })
},
handleItemDelete ({ resource, resourceName, locale, api = 'system' }) {
this.incLoader()
const { deletedAt = '' } = resource
const method = deletedAt ? `${resourceName}Undelete` : `${resourceName}Delete`
const event = deletedAt ? 'undelete' : 'delete'
const toastLocale = locale || resourceName
const API = api === 'system' ? this.$SystemAPI : this.$AutomationAPI
API[method](resource)
.then(() => {
this.toastSuccess(this.$t(`notification:${toastLocale}.${event}.success`))
this.filterList()
})
.catch(this.toastErrorHandler(this.$t(`notification:${toastLocale}.${event}.error`)))
.finally(() => {
this.decLoader()
})
},
areActionsVisible ({ resource, conditions = [] }) {
const condition = conditions.some(c => {
if (!resource[c]) {
return false
}
return true
})
return condition
},
},
}
@@ -26,10 +26,12 @@
resourceSingle: $t('general:label.session.single'),
resourcePlural: $t('general:label.session.plural')
}"
clickable
sticky-header
hide-search
hide-total
class="custom-resource-list-height-no-buttons"
@row-clicked="handleRowClicked"
>
<template #header>
<b-row>
@@ -172,10 +174,6 @@ export default {
sortable: true,
formatter: (v) => new Date(v).toLocaleString('en-EN'),
},
{
key: 'actions',
tdClass: 'text-right',
},
].map(c => ({
...c,
// Generate column label translation key
@@ -60,9 +60,11 @@
resourceSingle: $t('general:label.workflow.single'),
resourcePlural: $t('general:label.workflow.plural')
}"
clickable
sticky-header
class="custom-resource-list-height"
@search="filterList"
@row-clicked="handleRowClicked"
>
<template #header>
<c-resource-list-status-filter
@@ -75,16 +77,84 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: w }">
<b-dropdown
v-if="(areActionsVisible({ resource: w, conditions: ['canGrant', 'canDeleteWorkflow'] }) && w.workflowID)"
boundary="viewport"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="w.workflowID && canGrant"
>
<c-permissions-button
:title="w.meta.name || w.handle || w.workflowID"
:target="w.meta.name || w.handle || w.workflowID"
:resource="`corteza::automation:workflow/${w.workflowID}`"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
>
<font-awesome-icon :icon="['fas', 'lock']" />
{{ $t('permissions') }}
</c-permissions-button>
</b-dropdown-item>
<b-dropdown-item
v-if="w.canDeleteWorkflow && !w.deletedAt"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(w)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
class="p-1"
>
{{ $t('delete') }}
</span>
</c-input-confirm>
</b-dropdown-item>
<b-dropdown-item
v-if="w.canUndeleteWorkflow && w.deletedAt"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(w)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
class="p-1"
>
{{ $t('undelete') }}
</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -150,7 +220,7 @@ export default {
},
{
key: 'actions',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -178,6 +248,14 @@ export default {
items () {
return this.procListResults(this.$AutomationAPI.workflowList(this.encodeListParams()))
},
handleDelete (workflow) {
this.handleItemDelete({
resource: workflow,
resourceName: 'workflow',
api: 'automation',
})
},
},
}
</script>
@@ -40,34 +40,42 @@
prevPagination: $t('admin:general.pagination.prev'),
nextPagination: $t('admin:general.pagination.next'),
}"
clickable
sticky-header
hide-total
class="custom-resource-list-height"
@row-clicked="handleRowClicked"
@search="filterList"
>
<template #actions="{ item }">
<b-button
v-if="item.nodeID === item.sharedNodeID && (item.status || '').toLowerCase() === 'pair_requested'"
size="sm"
variant="link"
class="p-0 pr-1"
@click="openConfirmPending(item)"
<template #actions="{ item: n }">
<b-dropdown
v-if="n.nodeID === n.sharedNodeID && (item.status || '').toLowerCase() === 'pair_requested'"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'exclamation-triangle']"
class="text-danger"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<b-dropdown-item>
<b-button
size="sm"
variant="link"
@click="openConfirmPending(n)"
>
<font-awesome-icon
:icon="['fas', 'exclamation-triangle']"
class="text-danger"
/>
{{ $t('pair.confirm') }}
</b-button>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
@@ -159,6 +167,7 @@
{{ $t(pair.node.email ? 'pair.status.confirmPending.description' : 'pair.status.confirmPending.descriptionNoMail', pair.node) }}
</h2>
</div>
<c-submit-button
button-class="px-5 mt-4"
variant="outline-primary"
@@ -254,8 +263,7 @@ export default {
// },
{
key: 'actions',
label: '',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -61,9 +61,11 @@
resourceSingle: $t('general:label.application.single'),
resourcePlural: $t('general:label.application.plural'),
}"
clickable
sticky-header
class="custom-resource-list-height"
@search="filterList"
@row-clicked="handleRowClicked"
>
<template #header>
<c-resource-list-status-filter
@@ -77,16 +79,63 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: a }">
<b-dropdown
v-if="(areActionsVisible({ resource: a, conditions: ['canDeleteApplication', 'canGrant'] }) && a.applicationID)"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="a.applicationID && canGrant"
>
<c-permissions-button
:title="a.name || a.applicationID"
:target="a.name || a.applicationID"
:resource="`corteza::system:application/${a.applicationID}`"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
>
<font-awesome-icon :icon="['fas', 'lock']" />
{{ $t('permissions') }}
</c-permissions-button>
</b-dropdown-item>
<b-dropdown-item
v-if="a.canDeleteApplication"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(a)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
v-if="!a.deletedAt"
class="p-1"
>{{ $t('delete') }}</span>
<span
v-else
class="p-1"
>{{ $t('undelete') }}</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -150,7 +199,8 @@ export default {
},
{
key: 'actions',
tdClass: 'text-right',
label: '',
class: 'actions',
},
].map(c => ({
...c,
@@ -178,6 +228,17 @@ export default {
items () {
return this.procListResults(this.$SystemAPI.applicationList(this.encodeListParams()))
},
getAppInfo (item) {
return { applicationID: item[this.primaryKey], name: item.name }
},
handleDelete (application) {
this.handleItemDelete({
resource: application,
resourceName: 'application',
})
},
},
}
</script>
@@ -191,8 +191,9 @@ export default {
}
},
onDelete (clientID) {
onDelete () {
this.incLoader()
const clientID = this.authClientID
this.$SystemAPI.authClientDelete({ clientID })
.then(() => {
this.fetchAuthclient()
@@ -204,8 +205,9 @@ export default {
.finally(() => this.decLoader())
},
onUndelete (clientID) {
onUndelete () {
this.incLoader()
const clientID = this.authClientID
this.$SystemAPI.authClientUndelete({ clientID })
.then(() => {
this.fetchAuthclient()
@@ -49,10 +49,12 @@
resourceSingle: $t('general:label.auth_client.single'),
resourcePlural: $t('general:label.auth_client.plural'),
}"
clickable
sticky-header
hide-search
class="custom-resource-list-height"
@search="filterList"
@row-clicked="handleRowClicked"
>
<template #header>
<c-resource-list-status-filter
@@ -66,16 +68,68 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: a }">
<b-dropdown
v-if="(areActionsVisible({ resource: a, conditions: ['canDeleteAuthClient', 'canGrant'] }) && a.authClientID)"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="a.authClientID && canGrant"
>
<c-permissions-button
:title="a.meta.name || a.handle || a.authClientID"
:target="a.meta.name || a.handle || a.authClientID"
:resource="`corteza::system:auth-client/${a.authClientID}`"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
>
<font-awesome-icon :icon="['fas', 'lock']" />
{{ $t('permissions') }}
</c-permissions-button>
</b-dropdown-item>
<b-dropdown-item
v-if="!a.isDefault && a.canDeleteAuthClient"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(a)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
v-if="!a.deletedAt"
class="p-1"
>
{{ $t('delete') }}
</span>
<span
v-else
class="p-1"
>
{{ $t('undelete') }}
</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -139,7 +193,7 @@ export default {
},
{
key: 'actions',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -167,6 +221,15 @@ export default {
items () {
return this.procListResults(this.$SystemAPI.authClientList(this.encodeListParams()))
},
handleDelete (authclient) {
this.handleItemDelete({
resource: { clientID: authclient.authClientID },
resourceName: 'authClient',
locale: 'authclient',
})
},
},
}
</script>
@@ -172,7 +172,7 @@ export default {
.then(() => {
this.fetchQueue()
this.toastSuccess(this.$t(`notification:queue.${event}.success`))
this.$router.push({ name: 'system.queue' })
if (!deletedAt) this.$router.push({ name: 'system.queue' })
})
.catch(this.toastErrorHandler(this.$t(`notification:queue.${event}.error`)))
.finally(() => {
@@ -52,9 +52,11 @@
resourceSingle: $t('general:label.queue.single'),
resourcePlural: $t('general:label.queue.plural'),
}"
clickable
sticky-header
class="custom-resource-list-height"
@search="filterList"
@row-clicked="handleRowClicked"
>
<template #header>
<c-resource-list-status-filter
@@ -68,16 +70,46 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: q }">
<b-dropdown
v-if="q.canDeleteQueue"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(q)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span v-if="!q.deletedAt">
{{ $t('delete') }}
</span>
<span v-else>
{{ $t('undelete') }}
</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -140,7 +172,7 @@ export default {
{
key: 'actions',
label: '',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -164,6 +196,14 @@ export default {
items () {
return this.procListResults(this.$SystemAPI.queuesList(this.encodeListParams()))
},
handleDelete (queue) {
this.handleItemDelete({
resource: queue,
resourceName: 'queues',
locale: 'queue',
})
},
},
}
</script>
+68 -12
View File
@@ -70,9 +70,11 @@
resourceSingle: $t('general:label.role.single'),
resourcePlural: $t('general:label.role.plural'),
}"
clickable
sticky-header
class="custom-resource-list-height"
@search="filterList"
@row-clicked="handleRowClicked"
>
<template #header>
<c-resource-list-status-filter
@@ -96,17 +98,65 @@
/>
</template>
<template #actions="{ item }">
<b-button
v-if="item.roleID !== '1'"
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: r }">
<b-dropdown
v-if="(areActionsVisible({ resource: r, conditions: ['canDeleteRole', 'canGrant'] }) && r.roleID)"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="r.roleID && canGrant"
>
<c-permissions-button
:title="r.name || r.handle || r.roleID"
:target="r.name || r.handle || r.roleID"
:resource="`corteza::system:role/${r.roleID}`"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
>
<font-awesome-icon :icon="['fas', 'lock']" />
{{ $t('permissions') }}
</c-permissions-button>
</b-dropdown-item>
<b-dropdown-item
v-if="r.canDeleteRole"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(r)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
v-if="!r.deletedAt"
class="p-1"
>{{ $t('delete') }}</span>
<span
v-else
class="p-1"
>{{ $t('undelete') }}</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -168,8 +218,7 @@ export default {
},
{
key: 'actions',
label: '',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -208,6 +257,13 @@ export default {
rowClass (item) {
return { 'text-secondary': item && (!!item.deletedAt || !!item.archivedAt) }
},
handleDelete (role) {
this.handleItemDelete({
resource: role,
resourceName: 'role',
})
},
},
}
</script>
@@ -38,9 +38,11 @@
resourceSingle: $t('general:label.sensitivity_level.single'),
resourcePlural: $t('general:label.sensitivity_level.plural'),
}"
clickable
sticky-header
hide-search
class="custom-resource-list-height"
@row-clicked="handleRowClicked"
>
<template #header>
<c-resource-list-status-filter
@@ -53,16 +55,50 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: s }">
<b-dropdown
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(s)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
v-if="!s.deletedAt"
class="p-1"
>
{{ $t('delete') }}
</span>
<span
v-else
class="p-1"
>
{{ $t('undelete') }}
</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -121,7 +157,7 @@ export default {
},
{
key: 'actions',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -145,6 +181,14 @@ export default {
items () {
return this.procListResults(this.$SystemAPI.dalSensitivityLevelList(this.encodeListParams()))
},
handleDelete (sensitivityLevel) {
this.handleItemDelete({
resource: sensitivityLevel,
resourceName: 'dalSensitivityLevel',
locale: 'sensitivityLevel',
})
},
},
}
</script>
@@ -70,9 +70,11 @@
resourceSingle: $t('general:label.template.single'),
resourcePlural: $t('general:label.template.plural'),
}"
clickable
sticky-header
class="custom-resource-list-height"
@search="filterList"
@row-clicked="handleRowClicked"
>
<template #header>
<c-resource-list-status-filter
@@ -86,16 +88,63 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: t }">
<b-dropdown
v-if="(areActionsVisible({ resource: t, conditions: ['canDeleteTemplate', 'canGrant'] }) && t.templateID)"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="t.templateID && canGrant"
>
<c-permissions-button
:title="t.meta.short || t.handle || t.templateID"
:target="t.meta.short || t.handle || t.templateID"
:resource="`corteza::system:template/${t.templateID}`"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
>
<font-awesome-icon :icon="['fas', 'lock']" />
{{ $t('permissions') }}
</c-permissions-button>
</b-dropdown-item>
<b-dropdown-item
v-if="t.canDeleteTemplate"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(t)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
v-if="!t.deletedAt"
class="p-1"
>{{ $t('delete') }}</span>
<span
v-else
class="p-1"
>{{ $t('undelete') }}</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -157,8 +206,7 @@ export default {
},
{
key: 'actions',
label: '',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -186,6 +234,13 @@ export default {
items () {
return this.procListResults(this.$SystemAPI.templateList(this.encodeListParams()))
},
handleDelete (template) {
this.handleItemDelete({
resource: template,
resourceName: 'template',
})
},
},
}
</script>
+68 -11
View File
@@ -78,9 +78,11 @@
resourceSingle: $t('general:label.user.single'),
resourcePlural: $t('general:label.user.plural'),
}"
clickable
sticky-header
class="custom-resource-list-height"
@search="filterList"
@row-clicked="handleRowClicked"
>
<template #header>
<c-resource-list-status-filter
@@ -103,16 +105,65 @@
/>
</template>
<template #actions="{ item }">
<b-button
size="sm"
variant="link"
:to="{ name: editRoute, params: { [primaryKey]: item[primaryKey] } }"
<template #actions="{ item: u }">
<b-dropdown
v-if="(areActionsVisible({ resource: u, conditions: ['canDeleteUser', 'canGrant'] }))"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<font-awesome-icon
:icon="['fas', 'pen']"
/>
</b-button>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="canGrant"
>
<c-permissions-button
:title="u.name || u.handle || u.email || u.userID"
:target="u.name || u.handle || u.email || u.userID"
:resource="`corteza::system:user/${u.userID}`"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
>
<font-awesome-icon :icon="['fas', 'lock']" />
{{ $t('permissions') }}
</c-permissions-button>
</b-dropdown-item>
<b-dropdown-item
v-if="u.canDeleteUser"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(u)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
v-if="!u.deletedAt"
class="p-1"
>{{ $t('delete') }}</span>
<span
v-else
class="p-1"
>{{ $t('undelete') }}</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -185,8 +236,7 @@ export default {
},
{
key: 'actions',
label: '',
tdClass: 'text-right',
class: 'actions',
},
].map(c => ({
...c,
@@ -241,6 +291,13 @@ export default {
rowClass (item) {
return { 'text-secondary': item && (!!item.deletedAt || !!item.suspendedAt) }
},
handleDelete (user) {
this.handleItemDelete({
resource: user,
resourceName: 'user',
})
},
},
}
</script>
@@ -7,7 +7,7 @@
:size="size"
variant="primary"
:text="$t('related-pages')"
boundary="viewport"
:boundary="boundary"
class="related-pages-dropdown"
>
<b-dropdown-item>
@@ -84,6 +84,11 @@ export default {
type: String,
default: 'md',
},
boundary: {
type: String,
default: 'viewport',
},
},
data () {
@@ -669,7 +669,6 @@ export default {
}
.color-selector {
.vs__dropdown-menu {
min-width: 100%;
}
@@ -678,6 +677,7 @@ export default {
text-overflow: ellipsis;
overflow-x: hidden;
}
.vs__selected-options {
width: 0;
flex-wrap: nowrap;
@@ -93,17 +93,53 @@
</template>
<template #actions="{ item: c }">
<b-button-group>
<c-permissions-button
<b-dropdown
v-if="c.canGrant || c.canDeleteChart"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="c.canGrant"
:title="c.name || c.handle || c.chartID"
:target="c.name || c.handle || c.chartID"
:resource="`corteza::compose:chart/${namespace.namespaceID}/${c.chartID}`"
:tooltip="$t('permissions:resources.compose.chart.tooltip')"
button-variant="outline-light"
class="text-dark d-print-none border-0"
/>
</b-button-group>
>
<c-permissions-button
:title="c.name || c.handle || c.chartID"
:target="c.name || c.handle || c.chartID"
:resource="`corteza::compose:chart/${namespace.namespaceID}/${c.chartID}`"
:tooltip="$t('permissions:resources.compose.chart.tooltip')"
:button-label="$t('permissions:ui.label')"
button-variant="link dropdown-item text-decoration-none text-dark regular-font rounded-0"
/>
</b-dropdown-item>
<b-dropdown-item
v-if="c.canDeleteChart"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="dropdown-item text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(c)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
{{ $t('chart.delete') }}
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
<template #changedAt="{ item }">
@@ -188,7 +224,7 @@ export default {
{
key: 'actions',
label: '',
tdClass: 'text-right text-nowrap',
tdClass: 'text-right text-nowrap actions',
},
]
},
@@ -201,6 +237,7 @@ export default {
...mapActions({
createChart: 'chart/create',
deleteChart: 'chart/delete',
}),
create (subType) {
@@ -254,10 +291,17 @@ export default {
this.filterList()
this.toastSuccess(this.$t('notification:general.import.successful'))
},
handleDelete (chart) {
this.deleteChart(chart).then(() => {
this.toastSuccess(this.$t('notification:chart.deleted'))
this.filterList()
}).catch(this.toastErrorHandler(this.$t('notification:chart.deleteFailed')))
},
},
}
</script>
<style lang="scss" scoped>
<style lang="scss">
$input-height: 42px;
.chart-name-input {
@@ -112,33 +112,72 @@
:namespace="namespace"
:module="m"
size="sm"
boundary="scrollParent"
/>
<b-button-group
class="ml-2"
<b-dropdown
v-if="m.canGrant || m.canDeleteModule"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2 ml-1"
no-caret
dropleft
lazy
menu-class="m-0"
>
<b-button
data-test-id="button-all-records"
variant="outline-light"
:title="$t('allRecords.label')"
:to="{name: 'admin.modules.record.list', params: { moduleID: m.moduleID }}"
class="text-primary d-print-none border-0"
>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'columns']"
:icon="['fas', 'ellipsis-v']"
/>
</b-button>
</template>
<c-permissions-button
<b-dropdown-item>
<b-button
data-test-id="button-all-records"
variant="outline-light"
:title="$t('allRecords.label')"
:to="{name: 'admin.modules.record.list', params: { moduleID: m.moduleID }}"
class="text-primary d-print-none border-0"
>
<font-awesome-icon
:icon="['fas', 'columns']"
/>
{{ $t('allRecords.label') }}
</b-button>
</b-dropdown-item>
<b-dropdown-item
v-if="m.canGrant"
:title="m.name || m.handle || m.moduleID"
:target="m.name || m.handle || m.moduleID"
:resource="`corteza::compose:module/${m.namespaceID}/${m.moduleID}`"
:tooltip="$t('permissions:resources.compose.module.tooltip')"
button-variant="outline-light"
class="text-dark d-print-none border-0"
/>
</b-button-group>
>
<c-permissions-button
:title="m.name || m.handle || m.moduleID"
:target="m.name || m.handle || m.moduleID"
:resource="`corteza::compose:module/${m.namespaceID}/${m.moduleID}`"
:tooltip="$t('permissions:resources.compose.module.tooltip')"
:button-label="$t('permissions:ui.label')"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
/>
</b-dropdown-item>
<b-dropdown-item
v-if="m.canDeleteModule"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(m)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
{{ $t('list.delete') }}
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
<template #name="{ item: m }">
@@ -244,7 +283,7 @@ export default {
{
key: 'actions',
label: '',
tdClass: 'text-right text-nowrap',
tdClass: 'text-right text-nowrap actions',
},
]
},
@@ -257,6 +296,8 @@ export default {
methods: {
...mapActions({
createPage: 'page/create',
deletePage: 'page/delete',
deleteModule: 'module/delete',
}),
handleRowClicked ({ moduleID, canUpdateModule, canDeleteModule }) {
@@ -293,6 +334,19 @@ export default {
this.filterList()
this.toastSuccess(this.$t('notification:general.import.successful'))
},
handleDelete (module) {
this.deleteModule(module).then(() => {
const moduleRecordPage = this.pages.find(p => p.moduleID === module.moduleID)
if (moduleRecordPage) {
return this.deletePage({ ...moduleRecordPage, strategy: 'rebase' })
}
}).catch(this.toastErrorHandler(this.$t('notification:module.deleteFailed')))
.finally(() => {
this.toastSuccess(this.$t('notification:module.deleted'))
this.filterList()
})
},
},
}
</script>
@@ -322,13 +322,15 @@ import { compose, NoID } from '@cortezaproject/corteza-js'
import { url, handle } from '@cortezaproject/corteza-vue'
import EditorToolbar from 'corteza-webapp-compose/src/components/Admin/EditorToolbar'
import NamespaceTranslator from 'corteza-webapp-compose/src/components/Namespaces/NamespaceTranslator'
import { mapGetters } from 'vuex'
import { mapGetters, mapActions } from 'vuex'
export default {
i18nOptions: {
namespaces: 'namespace',
},
name: 'EditNamespace',
components: {
EditorToolbar,
NamespaceTranslator,
@@ -435,6 +437,14 @@ export default {
},
methods: {
...mapActions({
updateNamespace: 'namespace/update',
createNamespace: 'namespace/create',
findNamespace: 'namespace/findByID',
cloneNamespace: 'namespace/clone',
deleteNamespace: 'namespace/delete',
}),
async fetchNamespace () {
this.processing = true
@@ -444,7 +454,7 @@ export default {
this.isApplication = false
if (namespaceID) {
await this.$store.dispatch('namespace/findByID', { namespaceID })
await this.findNamespace({ namespaceID })
.then(ns => {
this.namespaceEnabled = ns.enabled
this.namespace = new compose.Namespace(ns)
@@ -533,7 +543,7 @@ export default {
if (this.isEdit) {
try {
await this.$store.dispatch('namespace/update', { ...payload, namespaceID }).then((ns) => {
await this.updateNamespace({ ...payload, namespaceID }).then((ns) => {
this.namespaceEnabled = ns.enabled
this.namespace = new compose.Namespace(ns)
@@ -546,7 +556,7 @@ export default {
}
} else if (this.isClone) {
try {
await this.$store.dispatch('namespace/clone', { namespaceID, name, slug, enabled, meta }).then((ns) => {
await this.cloneNamespace({ namespaceID, name, slug, enabled, meta }).then((ns) => {
this.namespace = new compose.Namespace(ns)
})
} catch (e) {
@@ -556,7 +566,7 @@ export default {
}
} else {
try {
await this.$store.dispatch('namespace/create', payload).then((ns) => {
await this.createNamespace(payload).then((ns) => {
this.namespaceEnabled = ns.enabled
this.namespace = new compose.Namespace(ns)
@@ -594,7 +604,7 @@ export default {
const { namespaceID } = this.namespace
const { applicationID } = this.application || {}
this.$store.dispatch('namespace/delete', { namespaceID })
this.deleteNamespace({ namespaceID })
.catch(this.toastErrorHandler(this.$t('notification:namespace.deleteFailed')))
.then(() => {
if (applicationID) {
@@ -44,6 +44,7 @@
/>
</b-col>
</b-row>
<transition-group
v-if="filtered && filtered.length"
name="namespace-list"
@@ -92,23 +92,61 @@
</template>
<template #actions="{ item: n }">
<b-button-group>
<c-permissions-button
v-if="n.canGrant"
:title="n.name || n.slug || n.namespaceID"
:target="n.name || n.slug || n.namespaceID"
:resource="`corteza::compose:namespace/${n.namespaceID}`"
:tooltip="$t('permissions:resources.compose.namespace.tooltip')"
button-variant="outline-light"
class="text-dark d-print-none border-0"
/>
</b-button-group>
<div>
<b-dropdown
v-if="n.canDeleteNamespace || n.canGrant"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
dropleft
lazy
menu-class="m-0"
>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="n.canGrant"
>
<c-permissions-button
:title="n.name || n.slug || n.namespaceID"
:target="n.name || n.slug || n.namespaceID"
:resource="`corteza::compose:namespace/${n.namespaceID}`"
:tooltip="$t('permissions:resources.compose.namespace.tooltip')"
:button-label="$t('permissions:ui.label')"
button-variant="link dropdown-item text-decoration-none text-dark regular-font rounded-0"
class="text-dark d-print-none border-0"
/>
</b-dropdown-item>
<b-dropdown-item
v-if="n.canDeleteNamespace"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="dropdown-item text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(n)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
{{ $t('delete') }}
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</div>
</template>
</c-resource-list>
</b-container>
</template>
<script>
import { mapGetters } from 'vuex'
import { mapGetters, mapActions } from 'vuex'
import ImporterModal from 'corteza-webapp-compose/src/components/Namespaces/Importer'
import listHelpers from 'corteza-webapp-compose/src/mixins/listHelpers'
@@ -129,6 +167,8 @@ export default {
data () {
return {
primaryKey: 'namespaceID',
application: undefined,
isApplication: false,
filter: {
query: '',
@@ -195,15 +235,20 @@ export default {
{
key: 'actions',
label: '',
tdClass: 'text-right text-nowrap',
tdClass: 'text-right text-nowrap actions',
},
]
},
},
methods: {
...mapActions({
load: 'namespace/load',
deleteNamespace: 'namespace/delete',
}),
onImported () {
this.$store.dispatch('namespace/load', { force: true })
this.load({ force: true })
.then(() => {
this.filterList()
this.toastSuccess(this.$t('notification:namespace.imported'))
@@ -225,6 +270,36 @@ export default {
namespaceList () {
return this.procListResults(this.$ComposeAPI.namespaceList(this.encodeListParams()))
},
fetchApplication (namespace) {
const { namespaceID, slug } = namespace
return this.$SystemAPI.applicationList({ name: slug || namespaceID })
.then(({ set = [] }) => {
if (set.length) {
this.application = set[0]
this.isApplication = this.application.enabled
}
})
.catch(this.toastErrorHandler(this.$t('notification:namespace.deleteFailed')))
},
async handleDelete (namespace) {
this.fetchApplication(namespace).then(() => {
const { namespaceID } = namespace
const { applicationID } = this.application || {}
this.deleteNamespace({ namespaceID })
.catch(this.toastErrorHandler(this.$t('notification:namespace.deleteFailed')))
.then(() => {
if (applicationID) {
return this.$SystemAPI.applicationDelete({ applicationID })
}
})
.then(() => {
this.toastSuccess(this.$t('notification:namespace.deleted'))
this.filterList()
})
})
},
},
}
</script>
@@ -46,6 +46,7 @@ import {
faGripVertical,
faEyeSlash,
faQuestion,
faEllipsisV,
} from '@fortawesome/free-solid-svg-icons'
import {
@@ -108,4 +109,5 @@ library.add(
faGripVertical,
faEyeSlash,
faQuestion,
faEllipsisV,
)
+1 -1
View File
@@ -64,7 +64,7 @@ export default {
}
},
async handleDelete () {
handleDelete () {
this.processing = true
return this.$SystemAPI.reportDelete(this.report)
+81 -16
View File
@@ -45,6 +45,7 @@
>
{{ $t('report.new') }}
</b-button>
<c-permissions-button
v-if="canGrant"
resource="corteza::system:report/*"
@@ -73,23 +74,73 @@
>
{{ $t('report.builder') }}
</b-button>
<b-button
v-if="r.canUpdateReport"
variant="link"
class="mr-2"
:to="{ name: 'report.edit', params: { reportID: r.reportID } }"
</template>
<template #moreActions="{ item: r }">
<b-dropdown
v-if="r.canUpdateReport || r.canGrant || r.canDeleteReport"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2"
no-caret
lazy
menu-class="m-0"
>
{{ $t('report.edit') }}
</b-button>
<c-permissions-button
v-if="r.canGrant"
:tooltip="$t('permissions:resources.system.report.tooltip')"
:title="r.meta.name || r.handle || r.reportID"
:target="r.meta.name || r.handle || r.reportID"
:resource="`corteza::system:report/${r.reportID}`"
class="btn px-2"
link
/>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="r.canUpdateReport"
>
<b-button
variant="link"
class="text-decoration-none"
:to="{ name: 'report.edit', params: { reportID: r.reportID } }"
>
<font-awesome-icon
:icon="['fa', 'pen']"
class="text-dark"
/>
<span class="p-1">{{ $t('report.edit') }}</span>
</b-button>
</b-dropdown-item>
<b-dropdown-item
v-if="r.canGrant"
>
<c-permissions-button
:tooltip="$t('permissions:resources.system.report.tooltip')"
:title="r.meta.name || r.handle || r.reportID"
:target="r.meta.name || r.handle || r.reportID"
:resource="`corteza::system:report/${r.reportID}`"
class="text-dark d-print-none border-0"
:button-label="$t('permissions:ui.label')"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
/>
</b-dropdown-item>
<b-dropdown-item
v-if="r.canDeleteReport"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(r)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
{{ $t('report.delete') }}
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -168,6 +219,11 @@ export default {
label: '',
tdClass: 'text-right text-nowrap',
},
{
key: 'moreActions',
label: '',
tdClass: 'text-right text-nowrap actions',
},
]
},
},
@@ -189,6 +245,15 @@ export default {
reportList () {
return this.procListResults(this.$SystemAPI.reportList(this.encodeListParams()))
},
handleDelete (report) {
return this.$SystemAPI.reportDelete(report)
.then(() => {
this.toastSuccess(this.$t('notification:report.delete'))
this.filterList()
})
.catch(this.toastErrorHandler(this.$t('notification:report.deleteFailed')))
},
},
}
</script>
@@ -88,6 +88,7 @@ export default {
i18nOptions: {
namespaces: 'configurator',
},
components: {
VueSelect,
},
@@ -22,6 +22,7 @@ import {
faAngleDoubleLeft,
faAngleRight,
faAngleLeft,
faEllipsisV,
} from '@fortawesome/free-solid-svg-icons'
import {
@@ -58,4 +59,5 @@ library.add(
faAngleDoubleLeft,
faAngleRight,
faAngleLeft,
faEllipsisV,
)
+93 -10
View File
@@ -14,6 +14,7 @@
:pagination="pagination"
:fields="tableFields"
:items="workflowList"
:row-class="genericRowClass"
:translations="{
searchPlaceholder: $t('general:searchPlaceholder'),
notFound: $t('general:resourceList.notFound'),
@@ -139,15 +140,80 @@
</template>
<template #actions="{ item: w }">
<c-permissions-button
v-if="w.canGrant"
:tooltip="$t('permissions:resources.automation.workflow.tooltip')"
:title="w.meta.name || w.handle || w.workflowID"
:target="w.meta.name || w.handle || w.workflowID"
:resource="`corteza::automation:workflow/${w.workflowID}`"
link
class="btn px-2"
/>
<b-dropdown
v-if="w.canGrant || w.canDeleteWorkflow"
variant="outline-light"
toggle-class="d-flex align-items-center justify-content-center text-primary border-0 py-2 ml-1"
no-caret
lazy
menu-class="m-0"
>
<template #button-content>
<font-awesome-icon
:icon="['fas', 'ellipsis-v']"
/>
</template>
<b-dropdown-item
v-if="w.canGrant"
>
<c-permissions-button
:tooltip="$t('permissions:resources.automation.workflow.tooltip')"
:title="w.meta.name || w.handle || w.workflowID"
:target="w.meta.name || w.handle || w.workflowID"
:resource="`corteza::automation:workflow/${w.workflowID}`"
button-variant="link text-decoration-none text-dark regular-font rounded-0"
:button-label="$t('permissions:ui.label')"
class="text-dark d-print-none border-0"
/>
</b-dropdown-item>
<b-dropdown-item
v-if="w.canDeleteWorkflow && !w.deletedAt"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(w)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
class="p-1"
>
{{ $t('delete') }}
</span>
</c-input-confirm>
</b-dropdown-item>
<b-dropdown-item
v-if="w.canUndeleteWorkflow && w.deletedAt"
>
<c-input-confirm
borderless
variant="link"
size="md"
button-class="text-decoration-none text-dark regular-font rounded-0"
class="w-100"
@confirmed="handleDelete(w)"
>
<font-awesome-icon
:icon="['far', 'trash-alt']"
class="text-danger"
/>
<span
class="p-1"
>
{{ $t('undelete') }}
</span>
</c-input-confirm>
</b-dropdown-item>
</b-dropdown>
</template>
</c-resource-list>
</b-container>
@@ -162,6 +228,10 @@ import { components } from '@cortezaproject/corteza-vue'
const { CResourceList } = components
export default {
i18nOptions: {
namespaces: 'list',
},
name: 'WorkflowList',
components: {
@@ -241,7 +311,7 @@ export default {
{
key: 'actions',
label: '',
tdClass: 'text-right text-nowrap',
tdClass: 'text-right text-nowrap actions',
},
]
},
@@ -307,6 +377,19 @@ export default {
handleRowClicked (workflow) {
this.$router.push({ name: 'workflow.edit', params: { workflowID: workflow.workflowID } })
},
handleDelete (workflow) {
const { deletedAt = '' } = workflow
const method = deletedAt ? 'workflowUndelete' : 'workflowDelete'
const event = deletedAt ? 'undelete' : 'delete'
const { workflowID } = workflow
this.$AutomationAPI[method]({ workflowID })
.then(() => {
this.toastSuccess(this.$t(`notification:${event}.success`))
this.filterList()
})
.catch(this.toastErrorHandler(this.$t(`notification:${event}.failed`)))
},
},
}
</script>
@@ -62,7 +62,7 @@
show-empty
no-sort-reset
no-local-sorting
:primary-key="primaryKey"
:primary-key="primaryKey"
class="mh-100 h-100 mb-0"
@sort-changed="pagination.page = 1"
@row-clicked="$emit('row-clicked', $event)"
@@ -398,3 +398,24 @@ export default {
},
}
</script>
<style lang="scss">
#resource-list {
.actions {
padding-top: 8px;
right: 0;
opacity: 0;
transition: opacity 0.25s;
width: 1%;
.regular-font {
font-family: 'Poppins-Regular' !important;
}
}
tr:hover .actions {
opacity: 1;
background-color: #F9FAFB;
}
}
</style>
@@ -32,6 +32,9 @@ list:
handle: Handle
name: Name
state: State
delete: Delete
undelete: Undelete
export: Export
rows:
filters:
@@ -23,6 +23,8 @@ list:
label: deleted routes
handle:
placeholder: Filter endpoints by path
delete: Delete
undelete: Undelete
columns:
endpoint: Endpoint
@@ -63,3 +63,5 @@ list:
permissions: Permissions
title: Applications
yaml: YAML
delete: Delete
undelete: Undelete
@@ -95,3 +95,5 @@ list:
permissions: Permissions
title: Auth Clients
yaml: YAML
delete: Delete
undelete: Undelete
@@ -24,6 +24,9 @@ list:
location: Location
ownership: Ownership
createdAt: Created
actions: ''
delete: Delete
undelete: Undelete
editor:
title:
@@ -20,12 +20,14 @@ editor:
new: Add a queue
list:
columns:
actions: Actions
actions: ''
consumer: Consumer
createdAt: Created At
state: State
queue: Queue
export: Export
delete: Delete
undelete: Undelete
rows:
filters:
deleted: Deleted
@@ -41,6 +41,10 @@ list:
handle: Handle
name: Name
state: State
delete: Delete
undelete: Undelete
archive: Archive
unarchive: Unarchive
rows:
filters:
archived: Archived
@@ -28,6 +28,8 @@ list:
level: Level
createdAt: Created
state: State
delete: Delete
undelete: Undelete
rows:
filters:
deleted: Deleted
@@ -52,6 +52,8 @@ list:
meta:
short: Short name
state: State
delete: Delete
undelete: Undelete
export: Export
rows:
filters:
@@ -81,6 +81,11 @@ list:
handle: Handle
name: Name
state: State
suspend: Suspend
unsuspend: Unsuspend
delete: Delete
undelete: Undelete
revokeActiveSession: Revoke Session
export: Export
rows:
filters:
@@ -112,6 +112,7 @@ chart:
year: YEAR
label: Chart
pick: Pick a chart
delete: Delete
preview:
chartId: Chart preview (ID {{0}})
searchPlaceholder: Type here to search all charts in this namespace
@@ -218,6 +218,7 @@ list:
name: Name
handle: Handle
changedAt: Last change
delete: Delete
recordPage:
create: Create record page
@@ -57,6 +57,7 @@ configure: Configure namespace
manage:
title: Manage namespaces
list-view: Back to namespace list
delete: Delete
disabled: Disabled
@@ -3,6 +3,7 @@ ui:
cancel: Cancel
set-for: Set permissions for {{target}}
loading: Loading permissions
label: Permissions
edit:
label: User roles
@@ -4,6 +4,7 @@ report:
edit: Edit
list: List of Reports
new: New Report
delete: Delete
searchPlaceholder: Type here to search reports
title: Reports
tooltip:
@@ -3,6 +3,7 @@ ui:
cancel: Cancel
set-for: Set permissions for {{target}}
loading: Loading permissions
label: Permissions
edit:
label: User roles
@@ -0,0 +1,2 @@
delete: Delete
undelete: Undelete
@@ -1,7 +1,5 @@
'configure-triggers': Make sure all trigger steps are properly configured
'confirm-unsaved-changes': You have unsaved changes, are you sure you want to exit?
'delete-failed': Failed to delete workflow
'deleted-workflow': Workflow deleted
error: " - ERROR: "
'event-type-not-found': Event type not found
'event-type-fetch-failed': Failed to fetch event types
@@ -37,6 +35,9 @@ import:
'trigger-paths-changed': Trigger paths changed
'updated-workflow': Workflow updated
'workflow-test-completed': Workflow test completed
delete:
success: Workflow deleted
failed: Failed to delete workflow
undelete:
success: Workflow restored
failed: Failed to restore workflow
@@ -3,6 +3,7 @@ ui:
cancel: Cancel
set-for: Set permissions for {{target}}
loading: Loading permissions
label: Permissions
edit:
label: User roles