mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-28 04:01:10 -04:00
v6 update
This commit is contained in:
219
resources/scripts/admin/layouts/partials/TheSiteHeader.vue
Normal file
219
resources/scripts/admin/layouts/partials/TheSiteHeader.vue
Normal file
@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<header
|
||||
class="
|
||||
fixed
|
||||
top-0
|
||||
left-0
|
||||
z-20
|
||||
flex
|
||||
items-center
|
||||
justify-between
|
||||
w-full
|
||||
px-4
|
||||
py-3
|
||||
md:h-16 md:px-8
|
||||
bg-gradient-to-r
|
||||
from-primary-500
|
||||
to-primary-400
|
||||
"
|
||||
>
|
||||
<router-link
|
||||
to="/admin/dashboard"
|
||||
class="
|
||||
float-none
|
||||
text-lg
|
||||
not-italic
|
||||
font-black
|
||||
tracking-wider
|
||||
text-white
|
||||
brand-main
|
||||
md:float-left
|
||||
font-base
|
||||
hidden
|
||||
md:block
|
||||
"
|
||||
>
|
||||
<MainLogo class="h-6" light-color="white" dark-color="white" />
|
||||
</router-link>
|
||||
|
||||
<!-- toggle button-->
|
||||
<div
|
||||
:class="{ 'is-active': globalStore.isSidebarOpen }"
|
||||
class="
|
||||
flex
|
||||
float-left
|
||||
p-1
|
||||
overflow-visible
|
||||
text-sm
|
||||
ease-linear
|
||||
bg-white
|
||||
border-0
|
||||
rounded
|
||||
cursor-pointer
|
||||
md:hidden md:ml-0
|
||||
hover:bg-gray-100
|
||||
"
|
||||
@click.prevent="onToggle"
|
||||
>
|
||||
<BaseIcon name="MenuIcon" class="!w-6 !h-6 text-gray-500" />
|
||||
</div>
|
||||
|
||||
<ul class="flex float-right h-8 m-0 list-none md:h-9">
|
||||
<li
|
||||
v-if="hasCreateAbilities"
|
||||
class="relative hidden float-left m-0 md:block"
|
||||
>
|
||||
<BaseDropdown width-class="w-48">
|
||||
<template #activator>
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
justify-center
|
||||
w-8
|
||||
h-8
|
||||
ml-2
|
||||
text-sm text-black
|
||||
bg-white
|
||||
rounded
|
||||
md:h-9 md:w-9
|
||||
"
|
||||
>
|
||||
<BaseIcon name="PlusIcon" class="w-5 h-5 text-gray-600" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<router-link to="/admin/invoices/create">
|
||||
<BaseDropdownItem
|
||||
v-if="userStore.hasAbilities(abilities.CREATE_INVOICE)"
|
||||
>
|
||||
<BaseIcon
|
||||
name="DocumentTextIcon"
|
||||
class="w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ $t('invoices.new_invoice') }}
|
||||
</BaseDropdownItem>
|
||||
</router-link>
|
||||
<router-link to="/admin/estimates/create">
|
||||
<BaseDropdownItem
|
||||
v-if="userStore.hasAbilities(abilities.CREATE_ESTIMATE)"
|
||||
>
|
||||
<BaseIcon
|
||||
name="DocumentIcon"
|
||||
class="w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ $t('estimates.new_estimate') }}
|
||||
</BaseDropdownItem>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/admin/customers/create">
|
||||
<BaseDropdownItem
|
||||
v-if="userStore.hasAbilities(abilities.CREATE_CUSTOMER)"
|
||||
>
|
||||
<BaseIcon
|
||||
name="UserIcon"
|
||||
class="w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ $t('customers.new_customer') }}
|
||||
</BaseDropdownItem>
|
||||
</router-link>
|
||||
</BaseDropdown>
|
||||
</li>
|
||||
|
||||
<li class="ml-2">
|
||||
<GlobalSearchBar
|
||||
v-if="
|
||||
userStore.currentUser.is_owner ||
|
||||
userStore.hasAbilities(abilities.VIEW_CUSTOMER)
|
||||
"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<CompanySwitcher />
|
||||
</li>
|
||||
|
||||
<!-- User Dropdown-->
|
||||
<li class="relative block float-left ml-2">
|
||||
<BaseDropdown width-class="w-48">
|
||||
<template #activator>
|
||||
<img
|
||||
:src="previewAvatar"
|
||||
class="block w-8 h-8 rounded md:h-9 md:w-9"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<router-link to="/admin/settings/account-settings">
|
||||
<BaseDropdownItem>
|
||||
<BaseIcon
|
||||
name="CogIcon"
|
||||
class="w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ $t('navigation.settings') }}
|
||||
</BaseDropdownItem>
|
||||
</router-link>
|
||||
|
||||
<BaseDropdownItem @click="logout">
|
||||
<BaseIcon
|
||||
name="LogoutIcon"
|
||||
class="w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ $t('navigation.logout') }}
|
||||
</BaseDropdownItem>
|
||||
</BaseDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useAuthStore } from '@/scripts/admin/stores/auth'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { computed } from 'vue'
|
||||
import { useUserStore } from '@/scripts/admin/stores/user'
|
||||
import { useGlobalStore } from '@/scripts/admin/stores/global'
|
||||
|
||||
import CompanySwitcher from '@/scripts/components/CompanySwitcher.vue'
|
||||
import GlobalSearchBar from '@/scripts/components/GlobalSearchBar.vue'
|
||||
import MainLogo from '@/scripts/components/icons/MainLogo.vue'
|
||||
|
||||
import abilities from '@/scripts/admin/stub/abilities'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const userStore = useUserStore()
|
||||
const globalStore = useGlobalStore()
|
||||
const router = useRouter()
|
||||
|
||||
const previewAvatar = computed(() => {
|
||||
return userStore.currentUser && userStore.currentUser.avatar !== 0
|
||||
? userStore.currentUser.avatar
|
||||
: getDefaultAvatar()
|
||||
})
|
||||
|
||||
function getDefaultAvatar() {
|
||||
const imgUrl = new URL('/img/default-avatar.jpg', import.meta.url)
|
||||
return imgUrl
|
||||
}
|
||||
|
||||
function hasCreateAbilities() {
|
||||
return userStore.hasAbilities([
|
||||
abilities.CREATE_INVOICE,
|
||||
abilities.CREATE_ESTIMATE,
|
||||
abilities.CREATE_CUSTOMER,
|
||||
])
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
await authStore.logout()
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
function onToggle() {
|
||||
globalStore.setSidebarVisibility(true)
|
||||
}
|
||||
</script>
|
||||
179
resources/scripts/admin/layouts/partials/TheSiteSidebar.vue
Normal file
179
resources/scripts/admin/layouts/partials/TheSiteSidebar.vue
Normal file
@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<!-- MOBILE MENU -->
|
||||
<TransitionRoot as="template" :show="globalStore.isSidebarOpen">
|
||||
<Dialog
|
||||
as="div"
|
||||
class="fixed inset-0 z-40 flex md:hidden"
|
||||
@close="globalStore.setSidebarVisibility(false)"
|
||||
>
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="transition-opacity ease-linear duration-300"
|
||||
enter-from="opacity-0"
|
||||
enter-to="opacity-100"
|
||||
leave="transition-opacity ease-linear duration-300"
|
||||
leave-from="opacity-100"
|
||||
leave-to="opacity-0"
|
||||
>
|
||||
<DialogOverlay class="fixed inset-0 bg-gray-600 bg-opacity-75" />
|
||||
</TransitionChild>
|
||||
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="transition ease-in-out duration-300"
|
||||
enter-from="-translate-x-full"
|
||||
enter-to="translate-x-0"
|
||||
leave="transition ease-in-out duration-300"
|
||||
leave-from="translate-x-0"
|
||||
leave-to="-translate-x-full"
|
||||
>
|
||||
<div class="relative flex flex-col flex-1 w-full max-w-xs bg-white">
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="ease-in-out duration-300"
|
||||
enter-from="opacity-0"
|
||||
enter-to="opacity-100"
|
||||
leave="ease-in-out duration-300"
|
||||
leave-from="opacity-100"
|
||||
leave-to="opacity-0"
|
||||
>
|
||||
<div class="absolute top-0 right-0 pt-2 -mr-12">
|
||||
<button
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
justify-center
|
||||
w-10
|
||||
h-10
|
||||
ml-1
|
||||
rounded-full
|
||||
focus:outline-none
|
||||
focus:ring-2
|
||||
focus:ring-inset
|
||||
focus:ring-white
|
||||
"
|
||||
@click="globalStore.setSidebarVisibility(false)"
|
||||
>
|
||||
<span class="sr-only">Close sidebar</span>
|
||||
<BaseIcon
|
||||
name="XIcon"
|
||||
class="w-6 h-6 text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</TransitionChild>
|
||||
<div class="flex-1 h-0 pt-5 pb-4 overflow-y-auto">
|
||||
<div class="flex items-center shrink-0 px-4 mb-10">
|
||||
<MainLogo
|
||||
class="block h-auto max-w-full w-36 text-primary-400"
|
||||
alt="Crater Logo"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<nav
|
||||
v-for="menu in globalStore.menuGroups"
|
||||
:key="menu"
|
||||
class="mt-5 space-y-1"
|
||||
>
|
||||
<router-link
|
||||
v-for="item in menu"
|
||||
:key="item.name"
|
||||
:to="item.link"
|
||||
:class="[
|
||||
hasActiveUrl(item.link)
|
||||
? 'text-primary-500 border-primary-500 bg-gray-100 '
|
||||
: 'text-black',
|
||||
'cursor-pointer px-0 pl-4 py-3 border-transparent flex items-center border-l-4 border-solid text-sm not-italic font-medium',
|
||||
]"
|
||||
@click="globalStore.setSidebarVisibility(false)"
|
||||
>
|
||||
<BaseIcon
|
||||
:name="item.icon"
|
||||
:class="[
|
||||
hasActiveUrl(item.link)
|
||||
? 'text-primary-500 '
|
||||
: 'text-gray-400',
|
||||
'mr-4 shrink-0 h-5 w-5',
|
||||
]"
|
||||
@click="globalStore.setSidebarVisibility(false)"
|
||||
/>
|
||||
{{ $t(item.title) }}
|
||||
</router-link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionChild>
|
||||
<div class="shrink-0 w-14">
|
||||
<!-- Force sidebar to shrink to fit close icon -->
|
||||
</div>
|
||||
</Dialog>
|
||||
</TransitionRoot>
|
||||
|
||||
<!-- DESKTOP MENU -->
|
||||
<div
|
||||
class="
|
||||
hidden
|
||||
w-56
|
||||
h-screen h-screen-ios
|
||||
pb-32
|
||||
overflow-y-auto
|
||||
bg-white
|
||||
border-r border-gray-200 border-solid
|
||||
xl:w-64
|
||||
md:fixed md:flex md:flex-col md:inset-y-0
|
||||
pt-16
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="menu in globalStore.menuGroups"
|
||||
:key="menu"
|
||||
class="p-0 m-0 mt-6 list-none"
|
||||
>
|
||||
<router-link
|
||||
v-for="item in menu"
|
||||
:key="item"
|
||||
:to="item.link"
|
||||
:class="[
|
||||
hasActiveUrl(item.link)
|
||||
? 'text-primary-500 border-primary-500 bg-gray-100 '
|
||||
: 'text-black',
|
||||
'cursor-pointer px-0 pl-6 hover:bg-gray-50 py-3 group flex items-center border-l-4 border-solid border-transparent text-sm not-italic font-medium',
|
||||
]"
|
||||
>
|
||||
<BaseIcon
|
||||
:name="item.icon"
|
||||
:class="[
|
||||
hasActiveUrl(item.link)
|
||||
? 'text-primary-500 group-hover:text-primary-500 '
|
||||
: 'text-gray-400 group-hover:text-black',
|
||||
'mr-4 shrink-0 h-5 w-5 ',
|
||||
]"
|
||||
/>
|
||||
|
||||
{{ $t(item.title) }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MainLogo from '@/scripts/components/icons/MainLogo.vue'
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogOverlay,
|
||||
TransitionChild,
|
||||
TransitionRoot,
|
||||
} from '@headlessui/vue'
|
||||
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useGlobalStore } from '@/scripts/admin/stores/global'
|
||||
|
||||
const route = useRoute()
|
||||
const globalStore = useGlobalStore()
|
||||
|
||||
function hasActiveUrl(url) {
|
||||
return route.path.indexOf(url) > -1
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user