Skip to content

Commit

Permalink
User Detail page first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
lcharette committed Oct 19, 2024
1 parent 1e1341f commit 6ffe6fd
Show file tree
Hide file tree
Showing 17 changed files with 771 additions and 15 deletions.
73 changes: 73 additions & 0 deletions app/assets/composable/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { ref, watch } from 'vue'
import axios from 'axios'
import type { UserInterface, GroupInterface } from '@userfrosting/sprinkle-account/types'
import { type AlertInterface, Severity } from '@userfrosting/sprinkle-core/types'

/**
* Create UserApi interface, based on UserInterface
*/
interface UserApi extends UserInterface {
locale_name: string
group: GroupInterface | null
}

/**
* API Composable
*/
export function useUserAdminApi(route: any) {
const loading = ref(false)
const error = ref<AlertInterface | null>()
const user = ref<UserApi>({
id: 0,
user_name: '',
first_name: '',
last_name: '',
full_name: '',
email: '',
avatar: '',
flag_enabled: false,
flag_verified: false,
group_id: null,
locale: '',
created_at: '',
updated_at: '',
deleted_at: null,
locale_name: '',
group: null
})

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

await axios
.get<UserApi>('/api/users/u/' + route.params.user_name)
.then((response) => {
user.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.user_name,
() => {
fetchApi()
},
{ immediate: true }
)

return { user, error, loading }
}

export type { UserApi }
36 changes: 36 additions & 0 deletions app/assets/views/User/UserActivities.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script setup lang="ts">
import moment from 'moment'
import { defineProps } from 'vue'
const { user_name } = defineProps<{
user_name: string
}>()
</script>

<template>
<UFCardBox title="Activities">
<UFSprunjeTable
:dataUrl="'/api/users/u/' + user_name + '/activities'"
:defaultSorts="{ occurred_at: 'desc' }">
<template #header>
<UFSprunjeHeader sort="occurred_at">Activity Time</UFSprunjeHeader>
<UFSprunjeHeader sort="description">Description</UFSprunjeHeader>
</template>

<template #body="{ item }">
<UFSprunjeColumn>
<div>{{ moment(item.occurred_at).format('dddd') }}</div>
<div>{{ moment(item.occurred_at).format('MMM Do, YYYY h:mm a') }}</div>
</UFSprunjeColumn>
<UFSprunjeColumn>
<div>
{{ item.ip_address }}
</div>
<div>
<i>{{ item.description }}</i>
</div>
</UFSprunjeColumn>
</template>
</UFSprunjeTable>
</UFCardBox>
</template>
49 changes: 49 additions & 0 deletions app/assets/views/User/UserInfo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
import { Severity } from '@userfrosting/sprinkle-core/types'
import type { UserApi } from '../../composable/user'
import { defineProps } from 'vue'
import moment from 'moment'
const { user } = defineProps<{
user: UserApi
}>()
</script>

<template>
<UFCardBox>
<div class="uk-text-center uk-margin">
<img :src="user.avatar" alt="avatar" class="uk-border-circle" />
<h3 class="uk-text-center uk-margin-remove">{{ user.full_name }}</h3>
<p class="uk-margin-remove uk-text-meta" v-if="user.user_name" data-test="meta">
({{ user.user_name }})
</p>
<slot data-test="slot"></slot>
</div>
<hr />
<!-- TODO : Find a way to slot the description list -->
<dl class="uk-description-list">
<dt><font-awesome-icon icon="envelope" /> Email</dt>
<dd class="uk-text-meta">{{ user.email }}</dd>
<dt><font-awesome-icon icon="users" /> Group</dt>
<dd class="uk-text-meta" v-if="user.group">{{ user.group.name }}</dd>
<dd class="uk-text-meta" v-else><i>None</i></dd>
<dt><font-awesome-icon icon="language" /> Locale</dt>
<dd class="uk-text-meta">{{ user.locale_name }}</dd>
<dt><font-awesome-icon icon="user-shield" /> Status</dt>
<dd class="uk-text-meta">
<UFLabel :severity="Severity.Danger" v-if="user.flag_enabled == false">
Disabled
</UFLabel>
<UFLabel :severity="Severity.Warning" v-else-if="user.flag_verified == false">
Unactivated
</UFLabel>
<UFLabel :severity="Severity.Success" v-else>Active</UFLabel>
</dd>
<dt><font-awesome-icon icon="calendar" /> Created on</dt>
<dd class="uk-text-meta">{{ moment(user.created_at).format('MMMM Do, YYYY') }}</dd>
<!-- <slot data-test="slot"></slot> -->
</dl>
<!-- TODO : Edit, Delete and other Btn -->
<!-- <slot data-test="slot"></slot> -->
</UFCardBox>
</template>
44 changes: 44 additions & 0 deletions app/assets/views/User/UserPermissions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script setup lang="ts">
import { defineProps } from 'vue'
const { user_name } = defineProps<{
user_name: string
}>()
</script>

<template>
<UFCardBox title="Permissions">
<UFSprunjeTable :dataUrl="'/api/users/u/' + user_name + '/permissions'" searchColumn="name">
<template #header>
<UFSprunjeHeader sort="name">Permission</UFSprunjeHeader>
<UFSprunjeHeader sort="properties">Slug/Condition</UFSprunjeHeader>
<UFSprunjeHeader>Has permission via roles</UFSprunjeHeader>
</template>

<template #body="{ item }">
<UFSprunjeColumn>
<strong>
<RouterLink
:to="{
name: 'admin.permission',
params: { id: item.id }
}">
{{ item.name }}
</RouterLink>
</strong>
</UFSprunjeColumn>
<UFSprunjeColumn>
<div>
<code>{{ item.slug }}</code>
</div>
<div>
↳ <code>{{ item.conditions }}</code>
</div>
<div>
<i>{{ item.description }}</i>
</div>
</UFSprunjeColumn>
</template>
</UFSprunjeTable>
</UFCardBox>
</template>
35 changes: 35 additions & 0 deletions app/assets/views/User/UserRoles.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import { defineProps } from 'vue'
const { slug } = defineProps<{
slug: string
}>()
</script>

<template>
<UFCardBox title="Roles">
<UFSprunjeTable :dataUrl="'/api/users/u/' + slug + '/roles'" searchColumn="name">
<template #header>
<UFSprunjeHeader sort="name">Role</UFSprunjeHeader>
<UFSprunjeHeader sort="description">Description</UFSprunjeHeader>
<UFSprunjeHeader sort="description">Actions</UFSprunjeHeader>
</template>

<template #body="{ item }">
<UFSprunjeColumn>
<strong>
<RouterLink
:to="{
name: 'admin.role',
params: { slug: item.slug }
}">
{{ item.name }}
</RouterLink>
</strong>
</UFSprunjeColumn>
<UFSprunjeColumn>{{ item.description }}</UFSprunjeColumn>
<UFSprunjeColumn></UFSprunjeColumn>
</template>
</UFSprunjeTable>
</UFCardBox>
</template>
38 changes: 37 additions & 1 deletion app/assets/views/UserView.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { useUserAdminApi } from '../composable/user'
import UserInfo from './User/UserInfo.vue'
import UserActivities from './User/UserActivities.vue'
import UserRoles from './User/UserRoles.vue'
import UserPermissions from './User/UserPermissions.vue'
const route = useRoute()
const { user, error } = useUserAdminApi(route)
</script>

<template>
<UFHeaderPage title="User" />
<UFHeaderPage title="User details" caption="User information page" />
<template v-if="error">
<UFCardBox>
<UFAlert :alert="error" />
</UFCardBox>
</template>
<template v-else>
<div uk-grid>
<div class="uk-width-1-3">
<UserInfo :user="user" />
</div>
<div class="uk-width-2-3">
<UserRoles :slug="user.user_name" />
</div>
</div>

<div class="uk-child-width-1-1" uk-grid>
<div>
<UserPermissions :user_name="user.user_name" />
</div>
<div>
<UserActivities :user_name="user.user_name" />
</div>
</div>
</template>
</template>
1 change: 0 additions & 1 deletion dist/UserView-DI4zS3sA.cjs

This file was deleted.

1 change: 1 addition & 0 deletions dist/UserView-DjKQWyL8.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"),i=require("./types-D9XxK5BT.cjs"),c=require("./moment-Bp7fbP4q.cjs");function _(d){const o=e.ref(!1),t=e.ref(),n=e.ref({id:0,user_name:"",first_name:"",last_name:"",full_name:"",email:"",avatar:"",flag_enabled:!1,flag_verified:!1,group_id:null,locale:"",created_at:"",updated_at:"",deleted_at:null,locale_name:"",group:null});async function a(){o.value=!0,t.value=null,await p.axios.get("/api/users/u/"+d.params.user_name).then(r=>{n.value=r.data}).catch(r=>{t.value={description:"An error as occurred",style:i.a.Danger,...r.response.data}}).finally(()=>{o.value=!1})}return e.watch(()=>d.params.user_name,()=>{a()},{immediate:!0}),{user:n,error:t,loading:o}}const N={class:"uk-text-center uk-margin"},V=["src"],f={class:"uk-text-center uk-margin-remove"},C={key:0,class:"uk-margin-remove uk-text-meta","data-test":"meta"},v={class:"uk-description-list"},x={class:"uk-text-meta"},k={key:0,class:"uk-text-meta"},g={key:1,class:"uk-text-meta"},U={class:"uk-text-meta"},S={class:"uk-text-meta"},y={class:"uk-text-meta"},E=e.defineComponent({__name:"UserInfo",props:{user:{}},setup(d){return(o,t)=>{const n=e.resolveComponent("font-awesome-icon"),a=e.resolveComponent("UFLabel"),r=e.resolveComponent("UFCardBox");return e.openBlock(),e.createBlock(r,null,{default:e.withCtx(()=>[e.createElementVNode("div",N,[e.createElementVNode("img",{src:o.user.avatar,alt:"avatar",class:"uk-border-circle"},null,8,V),e.createElementVNode("h3",f,e.toDisplayString(o.user.full_name),1),o.user.user_name?(e.openBlock(),e.createElementBlock("p",C," ("+e.toDisplayString(o.user.user_name)+") ",1)):e.createCommentVNode("",!0),e.renderSlot(o.$slots,"default",{dataTest:"slot"})]),t[9]||(t[9]=e.createElementVNode("hr",null,null,-1)),e.createElementVNode("dl",v,[e.createElementVNode("dt",null,[e.createVNode(n,{icon:"envelope"}),t[0]||(t[0]=e.createTextVNode(" Email"))]),e.createElementVNode("dd",x,e.toDisplayString(o.user.email),1),e.createElementVNode("dt",null,[e.createVNode(n,{icon:"users"}),t[1]||(t[1]=e.createTextVNode(" Group"))]),o.user.group?(e.openBlock(),e.createElementBlock("dd",k,e.toDisplayString(o.user.group.name),1)):(e.openBlock(),e.createElementBlock("dd",g,t[2]||(t[2]=[e.createElementVNode("i",null,"None",-1)]))),e.createElementVNode("dt",null,[e.createVNode(n,{icon:"language"}),t[3]||(t[3]=e.createTextVNode(" Locale"))]),e.createElementVNode("dd",U,e.toDisplayString(o.user.locale_name),1),e.createElementVNode("dt",null,[e.createVNode(n,{icon:"user-shield"}),t[4]||(t[4]=e.createTextVNode(" Status"))]),e.createElementVNode("dd",S,[o.user.flag_enabled==!1?(e.openBlock(),e.createBlock(a,{key:0,severity:e.unref(i.a).Danger},{default:e.withCtx(()=>t[5]||(t[5]=[e.createTextVNode(" Disabled ")])),_:1},8,["severity"])):o.user.flag_verified==!1?(e.openBlock(),e.createBlock(a,{key:1,severity:e.unref(i.a).Warning},{default:e.withCtx(()=>t[6]||(t[6]=[e.createTextVNode(" Unactivated ")])),_:1},8,["severity"])):(e.openBlock(),e.createBlock(a,{key:2,severity:e.unref(i.a).Success},{default:e.withCtx(()=>t[7]||(t[7]=[e.createTextVNode("Active")])),_:1},8,["severity"]))]),e.createElementVNode("dt",null,[e.createVNode(n,{icon:"calendar"}),t[8]||(t[8]=e.createTextVNode(" Created on"))]),e.createElementVNode("dd",y,e.toDisplayString(e.unref(c.hooks)(o.user.created_at).format("MMMM Do, YYYY")),1)])]),_:3})}}}),w=e.defineComponent({__name:"UserActivities",props:{user_name:{}},setup(d){return(o,t)=>{const n=e.resolveComponent("UFSprunjeHeader"),a=e.resolveComponent("UFSprunjeColumn"),r=e.resolveComponent("UFSprunjeTable"),u=e.resolveComponent("UFCardBox");return e.openBlock(),e.createBlock(u,{title:"Activities"},{default:e.withCtx(()=>[e.createVNode(r,{dataUrl:"/api/users/u/"+o.user_name+"/activities",defaultSorts:{occurred_at:"desc"}},{header:e.withCtx(()=>[e.createVNode(n,{sort:"occurred_at"},{default:e.withCtx(()=>t[0]||(t[0]=[e.createTextVNode("Activity Time")])),_:1}),e.createVNode(n,{sort:"description"},{default:e.withCtx(()=>t[1]||(t[1]=[e.createTextVNode("Description")])),_:1})]),body:e.withCtx(({item:s})=>[e.createVNode(a,null,{default:e.withCtx(()=>[e.createElementVNode("div",null,e.toDisplayString(e.unref(c.hooks)(s.occurred_at).format("dddd")),1),e.createElementVNode("div",null,e.toDisplayString(e.unref(c.hooks)(s.occurred_at).format("MMM Do, YYYY h:mm a")),1)]),_:2},1024),e.createVNode(a,null,{default:e.withCtx(()=>[e.createElementVNode("div",null,e.toDisplayString(s.ip_address),1),e.createElementVNode("div",null,[e.createElementVNode("i",null,e.toDisplayString(s.description),1)])]),_:2},1024)]),_:1},8,["dataUrl"])]),_:1})}}}),B=e.defineComponent({__name:"UserRoles",props:{slug:{}},setup(d){return(o,t)=>{const n=e.resolveComponent("UFSprunjeHeader"),a=e.resolveComponent("RouterLink"),r=e.resolveComponent("UFSprunjeColumn"),u=e.resolveComponent("UFSprunjeTable"),s=e.resolveComponent("UFCardBox");return e.openBlock(),e.createBlock(s,{title:"Roles"},{default:e.withCtx(()=>[e.createVNode(u,{dataUrl:"/api/users/u/"+o.slug+"/roles",searchColumn:"name"},{header:e.withCtx(()=>[e.createVNode(n,{sort:"name"},{default:e.withCtx(()=>t[0]||(t[0]=[e.createTextVNode("Role")])),_:1}),e.createVNode(n,{sort:"description"},{default:e.withCtx(()=>t[1]||(t[1]=[e.createTextVNode("Description")])),_:1}),e.createVNode(n,{sort:"description"},{default:e.withCtx(()=>t[2]||(t[2]=[e.createTextVNode("Actions")])),_:1})]),body:e.withCtx(({item:l})=>[e.createVNode(r,null,{default:e.withCtx(()=>[e.createElementVNode("strong",null,[e.createVNode(a,{to:{name:"admin.role",params:{slug:l.slug}}},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(l.name),1)]),_:2},1032,["to"])])]),_:2},1024),e.createVNode(r,null,{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(l.description),1)]),_:2},1024),e.createVNode(r)]),_:1},8,["dataUrl"])]),_:1})}}}),F=e.defineComponent({__name:"UserPermissions",props:{user_name:{}},setup(d){return(o,t)=>{const n=e.resolveComponent("UFSprunjeHeader"),a=e.resolveComponent("RouterLink"),r=e.resolveComponent("UFSprunjeColumn"),u=e.resolveComponent("UFSprunjeTable"),s=e.resolveComponent("UFCardBox");return e.openBlock(),e.createBlock(s,{title:"Permissions"},{default:e.withCtx(()=>[e.createVNode(u,{dataUrl:"/api/users/u/"+o.user_name+"/permissions",searchColumn:"name"},{header:e.withCtx(()=>[e.createVNode(n,{sort:"name"},{default:e.withCtx(()=>t[0]||(t[0]=[e.createTextVNode("Permission")])),_:1}),e.createVNode(n,{sort:"properties"},{default:e.withCtx(()=>t[1]||(t[1]=[e.createTextVNode("Slug/Condition")])),_:1}),e.createVNode(n,null,{default:e.withCtx(()=>t[2]||(t[2]=[e.createTextVNode("Has permission via roles")])),_:1})]),body:e.withCtx(({item:l})=>[e.createVNode(r,null,{default:e.withCtx(()=>[e.createElementVNode("strong",null,[e.createVNode(a,{to:{name:"admin.permission",params:{id:l.id}}},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(l.name),1)]),_:2},1032,["to"])])]),_:2},1024),e.createVNode(r,null,{default:e.withCtx(()=>[e.createElementVNode("div",null,[e.createElementVNode("code",null,e.toDisplayString(l.slug),1)]),e.createElementVNode("div",null,[t[3]||(t[3]=e.createTextVNode(" ↳ ")),e.createElementVNode("code",null,e.toDisplayString(l.conditions),1)]),e.createElementVNode("div",null,[e.createElementVNode("i",null,e.toDisplayString(l.description),1)])]),_:2},1024)]),_:1},8,["dataUrl"])]),_:1})}}}),h={"uk-grid":""},T={class:"uk-width-1-3"},D={class:"uk-width-2-3"},j={class:"uk-child-width-1-1","uk-grid":""},b=e.defineComponent({__name:"UserView",setup(d){const o=m.useRoute(),{user:t,error:n}=_(o);return(a,r)=>{const u=e.resolveComponent("UFHeaderPage"),s=e.resolveComponent("UFAlert"),l=e.resolveComponent("UFCardBox");return e.openBlock(),e.createElementBlock(e.Fragment,null,[e.createVNode(u,{title:"User details",caption:"User information page"}),e.unref(n)?(e.openBlock(),e.createBlock(l,{key:0},{default:e.withCtx(()=>[e.createVNode(s,{alert:e.unref(n)},null,8,["alert"])]),_:1})):(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("div",h,[e.createElementVNode("div",T,[e.createVNode(E,{user:e.unref(t)},null,8,["user"])]),e.createElementVNode("div",D,[e.createVNode(B,{slug:e.unref(t).user_name},null,8,["slug"])])]),e.createElementVNode("div",j,[e.createElementVNode("div",null,[e.createVNode(F,{user_name:e.unref(t).user_name},null,8,["user_name"])]),e.createElementVNode("div",null,[e.createVNode(w,{user_name:e.unref(t).user_name},null,8,["user_name"])])])],64))],64)}}});exports.default=b;
Loading

0 comments on commit 6ffe6fd

Please sign in to comment.