create dashboard layout

This commit is contained in:
Geriano
2022-07-16 04:29:30 +07:00
parent 2ca55419f7
commit cff8ab1b72
11 changed files with 276 additions and 16 deletions

View File

@@ -0,0 +1,30 @@
<script setup>
import { getCurrentInstance, ref } from 'vue'
import axios from 'axios'
import { usePage } from '@inertiajs/inertia-vue3'
import Builder from './Sidebar/Builder.vue'
import Icon from '@/Components/Icon.vue'
const self = getCurrentInstance()
const menus = ref([])
const { user } = usePage().props.value
const fetch = async () => {
try {
const response = await axios.get(route('api.v1.user.menu', user.id))
return menus.value = response.data
} catch (e) {
setTimeout(fetch, 1000)
}
}
fetch()
</script>
<template>
<div class="flex flex-col w-full h-full bg-inherit">
<Builder v-if="menus.length" :menus="menus" />
</div>
<div class="hidden pl-8"></div>
</template>

View File

@@ -0,0 +1,30 @@
<script>
import { defineComponent, h } from "vue"
import Link from "./Link.vue"
import Links from "./Links.vue"
export default defineComponent({
props: {
menus: Array,
},
setup(props, { attrs }) {
return props => {
const { menus } = props
const generate = (menu, attrs = {}) => {
if (menu.childs?.length > 0) {
return h(Links, {
...attrs,
menu,
childs: menu.childs,
}, menu.childs.map(child => generate(child, { class: 'pl-8' })))
}
return h(Link, { ...attrs, menu })
}
return h('div', { class: 'flex flex-col' }, menus.map(menu => generate(menu)))
}
},
})
</script>

View File

@@ -0,0 +1,23 @@
<script setup>
import { getCurrentInstance } from 'vue'
import { Link } from '@inertiajs/inertia-vue3'
import Icon from '@/Components/Icon.vue'
const { menu } = defineProps({
menu: Object,
})
const active = route().current(menu.route_or_url)
if (route().has(menu.route_or_url))
menu.route_or_url = route(menu.route_or_url)
</script>
<template>
<Link :href="menu.route_or_url" class="w-full px-4 py-3" :class="`${themes().get('sidebar', 'bg-slate-700 text-gray-200')} ${active && 'bg-slate-800'}`">
<div class="flex items-center space-x-2">
<Icon :name="menu.icon" />
<p class="uppercase font-semibold">{{ menu.name }}</p>
</div>
</Link>
</template>

View File

@@ -0,0 +1,41 @@
<script setup>
import { getCurrentInstance, ref } from 'vue'
import Icon from '@/Components/Icon.vue'
const { menu, childs } = defineProps({
menu: Object,
childs: Array,
})
const trace = menu => {
if (menu.childs?.length) {
for (const child of menu.child) {
if (trace(child)) {
return true
}
}
}
return route().current(menu.route_or_url)
}
const active = childs.find(trace)
const self = getCurrentInstance()
const open = ref(active ? true : false)
</script>
<template>
<div class="w-full flex flex-col">
<button @click.prevent="open = ! open" class="w-full p-4" :class="`${themes().get('sidebar', 'bg-slate-700 text-gray-200')} ${open && 'dark:bg-gray-800'}`">
<div class="flex items-center space-x-2">
<Icon :name="menu.icon" />
<p class="uppercase font-semibold w-full text-left">{{ menu.name }}</p>
<Icon name="caret-left" class="transition-all ease-in-out duration-150" :class="open && '-rotate-90'" />
</div>
</button>
<div v-if="open" class="flex flex-col">
<slot />
</div>
</div>
</template>

View File

@@ -0,0 +1,11 @@
<script setup>
import Icon from '../Icon.vue';
</script>
<template>
<div class="flex-none w-14 h-14 p-3">
<button @click.prevent="$emit('toggle')" type="button" class="w-full h-full rounded-md border dark:border-gray-600 text-white dark:text-gray-700 transition-all hover:scale-105 p-1">
<Icon name="bars" />
</button>
</div>
</template>

View File

@@ -0,0 +1,55 @@
<script setup>
import { getCurrentInstance, ref } from 'vue'
import { usePage, Link } from '@inertiajs/inertia-vue3'
import { Inertia } from '@inertiajs/inertia'
import Icon from '../Icon.vue'
const open = ref(false)
const { user } = usePage().props.value
const logout = () => Inertia.post(route('logout'))
</script>
<style scoped>
.slide-enter-active, .slide-leave-active {
transition: all 500ms ease-in-out;
}
.slide-enter-from, .slide-leave-to {
right: -15rem;
}
</style>
<template>
<div ref="container" class="flex-none flex items-center justify-between space-x-2 w-full max-w-xs h-14 px-3">
<img :src="user.profile_photo_url" :alt="user.name" class="flex-none rounded-full w-10 h-10">
<p class="font-semibold lowercase first-letter:capitalize truncate w-full">{{ user.name }}</p>
<div class="flex-none w-14 h-14 p-3">
<button @click.prevent="open = ! open" class="rounded-md border dark:border-gray-600 w-full h-full text-white dark:text-gray-700 transition-all ease-in-out duration-150 hover:border-gray-700 hover:text-gray-900">
<Icon name="caret-left" class="transition-all ease-linear duration-500" :class="open && '-rotate-90'" />
</button>
</div>
</div>
<transition name="slide">
<div v-if="open" class="fixed sm:right-4 top-12 w-full max-w-xl sm:w-48 bg-white dark:bg-gray-700 rounded-md shadow-xl">
<Link :href="route('profile.show')" as="button" class="w-full border-l-8 border-transparent dark:hover:border-gray-600 px-4 py-2 rounded-t-md transition-all ease-linear duration-150 hover:bg-gray-800">
<div class="flex items-center space-x-2 dark:text-white font-semibold">
<Icon name="user" />
<p class="uppercase">profile</p>
</div>
</Link>
<button @click.prevent="logout" class="w-full border-l-8 border-transparent dark:hover:border-gray-600 px-4 py-2 rounded-b-md transition-all ease-linear duration-150 hover:bg-gray-800">
<div class="flex items-center space-x-2 dark:text-white font-semibold">
<Icon name="door-open" />
<p class="uppercase">logout</p>
</div>
</button>
</div>
</transition>
</template>