Skip to content

Commit

Permalink
Add permission page
Browse files Browse the repository at this point in the history
  • Loading branch information
lcharette committed Oct 20, 2024
1 parent cdc483a commit 105be84
Show file tree
Hide file tree
Showing 14 changed files with 418 additions and 16 deletions.
62 changes: 62 additions & 0 deletions app/assets/composable/permission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ref, watch } from 'vue'
import axios from 'axios'
import type { PermissionInterface } from '@userfrosting/sprinkle-account/types'
import { type AlertInterface, Severity } from '@userfrosting/sprinkle-core/types'

/**
* Create PermissionApi interface, based on PermissionInterface
*/
interface PermissionApi extends PermissionInterface {}

/**
* API Composable
*/
export function usePermissionApi(route: any) {
const loading = ref(false)
const error = ref<AlertInterface | null>()
const permission = ref<PermissionApi>({
id: 0,
slug: '',
name: '',
conditions: '',
description: '',
created_at: '',
updated_at: '',
deleted_at: null
})

async function fetchApi() {
loading.value = true
error.value = null

await axios
.get<PermissionApi>('/api/permissions/p/' + route.params.slug)
.then((response) => {
permission.value = response.data
})
.catch((err) => {
error.value = {
...{
description: 'An error as occurred',
style: Severity.Danger
},
...err.response.data
}
})
.finally(() => {
loading.value = false
})
}

watch(
() => route.params.slug,
() => {
fetchApi()
},
{ immediate: true }
)

return { permission, error, loading }
}

export type { PermissionApi }
33 changes: 33 additions & 0 deletions app/assets/views/Permission/PermissionInfo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script setup lang="ts">
import type { PermissionApi } from '../../composable/permission'
import { defineProps } from 'vue'
const { permission } = defineProps<{
permission: PermissionApi
}>()
</script>

<template>
<UFCardBox>
<div class="uk-text-center">
<font-awesome-icon icon="key" class="fa-5x" />
</div>
<h3 class="uk-text-center uk-margin-remove">{{ permission.name }}</h3>
<p class="uk-text-meta">
{{ permission.description }}
</p>
<hr />
<!-- TODO : Find a way to slot the description list -->
<dl class="uk-description-list">
<dt>Slug</dt>
<dd>
<pre><code>{{ permission.slug }}</code></pre>
</dd>
<dt>Conditions</dt>
<dd>
<pre style="text-wrap: wrap"><code>{{ permission.conditions }}</code></pre>
</dd>
</dl>
<slot data-test="slot"></slot>
</UFCardBox>
</template>
44 changes: 44 additions & 0 deletions app/assets/views/Permission/PermissionUsers.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script setup lang="ts">
import { defineProps } from 'vue'
const { slug } = defineProps<{
slug: string | number
}>()
</script>

<template>
<UFCardBox title="Users with this permission">
<UFSprunjeTable
:dataUrl="'/api/permissions/p/' + slug + '/users'"
searchColumn="name"
hideFilters>
<template #header>
<UFSprunjeHeader sort="name">User</UFSprunjeHeader>
<UFSprunjeHeader>Has permission via roles</UFSprunjeHeader>
</template>

<template #body="{ item }">
<UFSprunjeColumn>
<strong>
<RouterLink
:to="{
name: 'admin.user',
params: { user_name: item.user_name }
}">
{{ item.full_name }} ({{ item.user_name }})
</RouterLink>
</strong>
<div class="uk-text-meta">{{ item.email }}</div>
</UFSprunjeColumn>
<UFSprunjeColumn>
<RouterLink
v-for="role in item.roles_via"
:key="role.id"
:to="{ name: 'admin.role', params: { slug: role.slug } }">
<UFLabel>{{ role.name }}</UFLabel>
</RouterLink>
</UFSprunjeColumn>
</template>
</UFSprunjeTable>
</UFCardBox>
</template>
27 changes: 26 additions & 1 deletion app/assets/views/PermissionView.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { usePermissionApi } from '../composable/permission'
import PermissionInfo from './Permission/PermissionInfo.vue'
import PermissionUsers from './Permission/PermissionUsers.vue'
const route = useRoute()
const { permission, error } = usePermissionApi(route)
</script>

<template>
<UFHeaderPage title="Permission" />
<UFHeaderPage title="Permission details" caption="Permission information page" />
<template v-if="error">
<UFCardBox>
<UFAlert :alert="error" />
</UFCardBox>
</template>
<template v-else>
<div uk-grid>
<div class="uk-width-1-3">
<PermissionInfo :permission="permission" />
</div>
<div class="uk-width-2-3">
<PermissionUsers :slug="$route.params.slug.toString()" />
</div>
</div>
</template>
</template>
1 change: 0 additions & 1 deletion app/locale/en_US/messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@
'ID' => 'Permission ID',
'INFO_PAGE' => "Permission information page for '{{name}}'",
'MANAGE' => 'Manage permissions',
'NOTE_READ_ONLY' => '<strong>Please note:</strong> permissions are considered "part of the code" and cannot be modified through the interface. To add, remove, or modify permissions, the site maintainers will need to use a <a href="https://learn.userfrosting.com/database/extending-the-database" target="about:_blank">database migration.</a>',
'NOT_FOUND' => 'Permission not found',
'PAGE_DESCRIPTION' => 'A listing of the permissions for your site. Provides management tools for editing and deleting permissions.',
'SUMMARY' => 'Permission Summary',
Expand Down
1 change: 1 addition & 0 deletions dist/PermissionView-CIL26irJ.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),m=require("vue-router"),p=require("./axios-tuVKNgv9.cjs"),_=require("./types-D9XxK5BT.cjs");function f(l){const o=e.ref(!1),t=e.ref(),n=e.ref({id:0,slug:"",name:"",conditions:"",description:"",created_at:"",updated_at:"",deleted_at:null});async function r(){o.value=!0,t.value=null,await p.axios.get("/api/permissions/p/"+l.params.slug).then(s=>{n.value=s.data}).catch(s=>{t.value={description:"An error as occurred",style:_.a.Danger,...s.response.data}}).finally(()=>{o.value=!1})}return e.watch(()=>l.params.slug,()=>{r()},{immediate:!0}),{permission:n,error:t,loading:o}}const C={class:"uk-text-center"},g={class:"uk-text-center uk-margin-remove"},V={class:"uk-text-meta"},N={class:"uk-description-list"},k={style:{"text-wrap":"wrap"}},h=e.defineComponent({__name:"PermissionInfo",props:{permission:{}},setup(l){return(o,t)=>{const n=e.resolveComponent("font-awesome-icon"),r=e.resolveComponent("UFCardBox");return e.openBlock(),e.createBlock(r,null,{default:e.withCtx(()=>[e.createElementVNode("div",C,[e.createVNode(n,{icon:"key",class:"fa-5x"})]),e.createElementVNode("h3",g,e.toDisplayString(o.permission.name),1),e.createElementVNode("p",V,e.toDisplayString(o.permission.description),1),t[2]||(t[2]=e.createElementVNode("hr",null,null,-1)),e.createElementVNode("dl",N,[t[0]||(t[0]=e.createElementVNode("dt",null,"Slug",-1)),e.createElementVNode("dd",null,[e.createElementVNode("pre",null,[e.createElementVNode("code",null,e.toDisplayString(o.permission.slug),1)])]),t[1]||(t[1]=e.createElementVNode("dt",null,"Conditions",-1)),e.createElementVNode("dd",null,[e.createElementVNode("pre",k,[e.createElementVNode("code",null,e.toDisplayString(o.permission.conditions),1)])])]),e.renderSlot(o.$slots,"default",{dataTest:"slot"})]),_:3})}}}),v={class:"uk-text-meta"},x=e.defineComponent({__name:"PermissionUsers",props:{slug:{}},setup(l){return(o,t)=>{const n=e.resolveComponent("UFSprunjeHeader"),r=e.resolveComponent("RouterLink"),s=e.resolveComponent("UFSprunjeColumn"),i=e.resolveComponent("UFLabel"),c=e.resolveComponent("UFSprunjeTable"),d=e.resolveComponent("UFCardBox");return e.openBlock(),e.createBlock(d,{title:"Users with this permission"},{default:e.withCtx(()=>[e.createVNode(c,{dataUrl:"/api/permissions/p/"+o.slug+"/users",searchColumn:"name",hideFilters:""},{header:e.withCtx(()=>[e.createVNode(n,{sort:"name"},{default:e.withCtx(()=>t[0]||(t[0]=[e.createTextVNode("User")])),_:1}),e.createVNode(n,null,{default:e.withCtx(()=>t[1]||(t[1]=[e.createTextVNode("Has permission via roles")])),_:1})]),body:e.withCtx(({item:a})=>[e.createVNode(s,null,{default:e.withCtx(()=>[e.createElementVNode("strong",null,[e.createVNode(r,{to:{name:"admin.user",params:{user_name:a.user_name}}},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(a.full_name)+" ("+e.toDisplayString(a.user_name)+") ",1)]),_:2},1032,["to"])]),e.createElementVNode("div",v,e.toDisplayString(a.email),1)]),_:2},1024),e.createVNode(s,null,{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(a.roles_via,u=>(e.openBlock(),e.createBlock(r,{key:u.id,to:{name:"admin.role",params:{slug:u.slug}}},{default:e.withCtx(()=>[e.createVNode(i,null,{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(u.name),1)]),_:2},1024)]),_:2},1032,["to"]))),128))]),_:2},1024)]),_:1},8,["dataUrl"])]),_:1})}}}),U={key:1,"uk-grid":""},w={class:"uk-width-1-3"},F={class:"uk-width-2-3"},y=e.defineComponent({__name:"PermissionView",setup(l){const o=m.useRoute(),{permission:t,error:n}=f(o);return(r,s)=>{const i=e.resolveComponent("UFHeaderPage"),c=e.resolveComponent("UFAlert"),d=e.resolveComponent("UFCardBox");return e.openBlock(),e.createElementBlock(e.Fragment,null,[e.createVNode(i,{title:"Permission details",caption:"Permission information page"}),e.unref(n)?(e.openBlock(),e.createBlock(d,{key:0},{default:e.withCtx(()=>[e.createVNode(c,{alert:e.unref(n)},null,8,["alert"])]),_:1})):(e.openBlock(),e.createElementBlock("div",U,[e.createElementVNode("div",w,[e.createVNode(h,{permission:e.unref(t)},null,8,["permission"])]),e.createElementVNode("div",F,[e.createVNode(x,{slug:r.$route.params.slug.toString()},null,8,["slug"])])]))],64)}}});exports.default=y;
1 change: 0 additions & 1 deletion dist/PermissionView-CZvtwzCa.cjs

This file was deleted.

11 changes: 0 additions & 11 deletions dist/PermissionView-CrT77lW9.js

This file was deleted.

187 changes: 187 additions & 0 deletions dist/PermissionView-DpVnMGym.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { ref as k, watch as w, defineComponent as C, resolveComponent as r, openBlock as d, createBlock as c, withCtx as t, createElementVNode as n, createVNode as o, toDisplayString as i, renderSlot as y, createTextVNode as _, createElementBlock as v, Fragment as x, renderList as S, unref as h } from "vue";
import { useRoute as B } from "vue-router";
import { a as P } from "./axios-CXDYiOMX.js";
import { a as $ } from "./types-Daou0lcF.js";
function j(m) {
const s = k(!1), e = k(), a = k({
id: 0,
slug: "",
name: "",
conditions: "",
description: "",
created_at: "",
updated_at: "",
deleted_at: null
});
async function l() {
s.value = !0, e.value = null, await P.get("/api/permissions/p/" + m.params.slug).then((u) => {
a.value = u.data;
}).catch((u) => {
e.value = {
description: "An error as occurred",
style: $.Danger,
...u.response.data
};
}).finally(() => {
s.value = !1;
});
}
return w(
() => m.params.slug,
() => {
l();
},
{ immediate: !0 }
), { permission: a, error: e, loading: s };
}
const b = { class: "uk-text-center" }, A = { class: "uk-text-center uk-margin-remove" }, H = { class: "uk-text-meta" }, L = { class: "uk-description-list" }, T = { style: { "text-wrap": "wrap" } }, V = /* @__PURE__ */ C({
__name: "PermissionInfo",
props: {
permission: {}
},
setup(m) {
return (s, e) => {
const a = r("font-awesome-icon"), l = r("UFCardBox");
return d(), c(l, null, {
default: t(() => [
n("div", b, [
o(a, {
icon: "key",
class: "fa-5x"
})
]),
n("h3", A, i(s.permission.name), 1),
n("p", H, i(s.permission.description), 1),
e[2] || (e[2] = n("hr", null, null, -1)),
n("dl", L, [
e[0] || (e[0] = n("dt", null, "Slug", -1)),
n("dd", null, [
n("pre", null, [
n("code", null, i(s.permission.slug), 1)
])
]),
e[1] || (e[1] = n("dt", null, "Conditions", -1)),
n("dd", null, [
n("pre", T, [
n("code", null, i(s.permission.conditions), 1)
])
])
]),
y(s.$slots, "default", { dataTest: "slot" })
]),
_: 3
});
};
}
}), N = { class: "uk-text-meta" }, R = /* @__PURE__ */ C({
__name: "PermissionUsers",
props: {
slug: {}
},
setup(m) {
return (s, e) => {
const a = r("UFSprunjeHeader"), l = r("RouterLink"), u = r("UFSprunjeColumn"), f = r("UFLabel"), g = r("UFSprunjeTable"), U = r("UFCardBox");
return d(), c(U, { title: "Users with this permission" }, {
default: t(() => [
o(g, {
dataUrl: "/api/permissions/p/" + s.slug + "/users",
searchColumn: "name",
hideFilters: ""
}, {
header: t(() => [
o(a, { sort: "name" }, {
default: t(() => e[0] || (e[0] = [
_("User")
])),
_: 1
}),
o(a, null, {
default: t(() => e[1] || (e[1] = [
_("Has permission via roles")
])),
_: 1
})
]),
body: t(({ item: p }) => [
o(u, null, {
default: t(() => [
n("strong", null, [
o(l, {
to: {
name: "admin.user",
params: { user_name: p.user_name }
}
}, {
default: t(() => [
_(i(p.full_name) + " (" + i(p.user_name) + ") ", 1)
]),
_: 2
}, 1032, ["to"])
]),
n("div", N, i(p.email), 1)
]),
_: 2
}, 1024),
o(u, null, {
default: t(() => [
(d(!0), v(x, null, S(p.roles_via, (F) => (d(), c(l, {
key: F.id,
to: { name: "admin.role", params: { slug: F.slug } }
}, {
default: t(() => [
o(f, null, {
default: t(() => [
_(i(F.name), 1)
]),
_: 2
}, 1024)
]),
_: 2
}, 1032, ["to"]))), 128))
]),
_: 2
}, 1024)
]),
_: 1
}, 8, ["dataUrl"])
]),
_: 1
});
};
}
}), D = {
key: 1,
"uk-grid": ""
}, E = { class: "uk-width-1-3" }, I = { class: "uk-width-2-3" }, K = /* @__PURE__ */ C({
__name: "PermissionView",
setup(m) {
const s = B(), { permission: e, error: a } = j(s);
return (l, u) => {
const f = r("UFHeaderPage"), g = r("UFAlert"), U = r("UFCardBox");
return d(), v(x, null, [
o(f, {
title: "Permission details",
caption: "Permission information page"
}),
h(a) ? (d(), c(U, { key: 0 }, {
default: t(() => [
o(g, { alert: h(a) }, null, 8, ["alert"])
]),
_: 1
})) : (d(), v("div", D, [
n("div", E, [
o(V, { permission: h(e) }, null, 8, ["permission"])
]),
n("div", I, [
o(R, {
slug: l.$route.params.slug.toString()
}, null, 8, ["slug"])
])
]))
], 64);
};
}
});
export {
K as default
};
Loading

0 comments on commit 105be84

Please sign in to comment.