This commit is contained in:
2024-09-01 18:54:23 +05:00
parent 76d18365a5
commit 061f09eca1
1597 changed files with 109451 additions and 1 deletions

View File

@@ -0,0 +1,48 @@
<template>
<nav
v-if="hasItems"
class="text-gray-500 font-semibold"
aria-label="breadcrumb"
dusk="breadcrumbs"
>
<ol>
<li
v-for="(item, index) in breadcrumbs"
class="inline-block"
v-bind="{
'aria-current': index === breadcrumbs.length - 1 ? 'page' : null,
}"
>
<div class="flex items-center">
<Link
:href="$url(item.path)"
v-if="item.path !== null && index < breadcrumbs.length - 1"
class="link-default"
>
{{ item.name }}
</Link>
<span v-else>{{ item.name }}</span>
<Icon
type="chevron-right"
v-if="index < breadcrumbs.length - 1"
class="w-4 h-4 mx-2 text-gray-300 dark:text-gray-700"
/>
</div>
</li>
</ol>
</nav>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['breadcrumbs']),
hasItems() {
return this.breadcrumbs.length > 0
},
},
}
</script>

View File

@@ -0,0 +1,31 @@
<template>
<div
v-if="hasItems"
class="sidebar-menu space-y-6"
dusk="sidebar-menu"
role="navigation"
>
<component
:key="item.key"
:is="item.component"
v-for="(item, index) in mainMenu"
:item="item"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'MainMenu',
computed: {
...mapGetters(['mainMenu']),
hasItems() {
return this.mainMenu.length > 0
},
},
}
</script>

View File

@@ -0,0 +1,73 @@
<template>
<div v-if="item.items.length > 0">
<h4
@click.prevent="handleClick"
class="flex items-center px-1 py-1 rounded text-left text-gray-500"
:class="{
'cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-800':
displayAsButton,
'font-bold text-primary-500 dark:text-primary-500': item.active,
}"
>
<span class="inline-block shrink-0 w-6 h-6" />
<span
class="flex-1 flex items-center w-full tracking-wide uppercase font-bold text-left text-xs px-3 py-1"
>
{{ item.name }}
</span>
<span
v-if="item.collapsable"
class="inline-flex items-center justify-center shrink-0 w-6 h-6"
>
<CollapseButton :collapsed="collapsed" :to="item.path" />
</span>
</h4>
<div v-if="!collapsed">
<component
:key="item.name"
v-for="item in item.items"
:is="item.component"
:item="item"
/>
</div>
</div>
</template>
<script>
import { Collapsable } from '@/mixins'
export default {
mixins: [Collapsable],
props: ['item'],
methods: {
handleClick() {
if (this.item.collapsable) {
this.toggleCollapse()
}
},
},
computed: {
component() {
if (this.item.items.length > 0) {
return 'div'
}
return 'h3'
},
displayAsButton() {
return this.item.items.length > 0 && this.item.collapsable
},
collapsedByDefault() {
return this.item?.collapsedByDefault ?? false
},
},
}
</script>

View File

@@ -0,0 +1,89 @@
<template>
<div>
<component
:is="component"
v-bind="linkAttributes"
class="w-full flex min-h-8 px-1 py-1 rounded text-left text-gray-500 dark:text-gray-500 focus:outline-none focus:ring focus:ring-primary-200 dark:focus:ring-gray-600 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-800"
:data-active-link="item.active"
:class="{
'font-bold text-primary-500 dark:text-primary-500': item.active,
}"
@click="handleClick"
>
<span class="inline-block shrink-0 w-6 h-6" />
<span class="flex-1 flex items-center w-full px-3 text-sm">
{{ item.name }}
</span>
<span class="inline-block h-6 shrink-0">
<Badge v-if="item.badge" :extra-classes="item.badge.typeClass">
{{ item.badge.value }}
</Badge>
</span>
</component>
</div>
</template>
<script>
import identity from 'lodash/identity'
import isNull from 'lodash/isNull'
import omitBy from 'lodash/omitBy'
import pickBy from 'lodash/pickBy'
import { mapGetters, mapMutations } from 'vuex'
export default {
props: {
item: {
type: Object,
required: true,
},
},
methods: {
...mapMutations(['toggleMainMenu']),
handleClick() {
if (this.mainMenuShown) {
this.toggleMainMenu()
}
},
},
computed: {
...mapGetters(['mainMenuShown']),
requestMethod() {
return this.item.method || 'GET'
},
component() {
if (this.requestMethod !== 'GET') {
return 'FormButton'
} else if (this.item.external !== true) {
return 'Link'
}
return 'a'
},
linkAttributes() {
let method = this.requestMethod
return pickBy(
omitBy(
{
href: this.item.path,
method: method !== 'GET' ? method : null,
headers: this.item.headers || null,
data: this.item.data || null,
rel: this.component === 'a' ? 'noreferrer noopener' : null,
target: this.item.target || null,
},
isNull
),
identity
)
},
},
}
</script>

View File

@@ -0,0 +1,11 @@
<template>
<div class="sidebar-list">
<menu-item :key="item.key" v-for="item in item.items" :item="item" />
</div>
</template>
<script>
export default {
props: ['item'],
}
</script>

View File

@@ -0,0 +1,99 @@
<template>
<div class="relative" v-if="item.path || item.items.length > 0">
<component
:is="component"
:href="item.path ?? null"
@click.prevent="handleClick"
:tabindex="displayAsButton ? 0 : null"
class="w-full flex items-start px-1 py-1 rounded text-left text-gray-500 dark:text-gray-500 focus:outline-none focus:ring focus:ring-primary-200 dark:focus:ring-gray-600"
:class="{
'cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-800':
displayAsButton,
'font-bold text-primary-500 dark:text-primary-500': item.active,
}"
>
<span class="inline-block shrink-0 w-6 h-6">
<component
:is="`heroicons-outline-${item.icon}`"
height="24"
width="24"
/>
</span>
<span class="flex-1 flex items-center w-full px-3 text-base">
{{ item.name }}
</span>
<span class="inline-block h-6 shrink-0">
<Badge v-if="item.badge" :extra-classes="item.badge.typeClass">
{{ item.badge.value }}
</Badge>
</span>
<span
v-if="item.collapsable"
class="inline-flex items-center justify-center shrink-0 w-6 h-6"
>
<CollapseButton :collapsed="collapsed" :to="item.path" />
</span>
</component>
<div v-if="item.items.length > 0 && !collapsed" class="mt-1 flex flex-col">
<component
:is="item.component"
v-for="item in item.items"
:key="item.name"
:item="item"
/>
</div>
</div>
</template>
<script>
import { Collapsable } from '@/mixins'
import { mapGetters, mapMutations } from 'vuex'
export default {
mixins: [Collapsable],
props: ['item'],
methods: {
...mapMutations(['toggleMainMenu']),
handleClick() {
if (this.item.collapsable) {
this.toggleCollapse()
}
if (this.mainMenuShown && this.component !== 'button') {
this.toggleMainMenu()
}
},
},
computed: {
...mapGetters(['mainMenuShown']),
component() {
if (this.item.path) {
return 'Link'
}
if (this.item.items.length > 0 && this.item.collapsable) {
return 'button'
}
return 'h3'
},
displayAsButton() {
return ['Link', 'button'].includes(this.component)
},
collapsedByDefault() {
return this.item?.collapsedByDefault ?? false
},
},
}
</script>