mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-10-30 21:21:09 -04:00 
			
		
		
		
	v5.0.0 update
This commit is contained in:
		
							
								
								
									
										170
									
								
								resources/scripts/layouts/partials/TheSiteHeader.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								resources/scripts/layouts/partials/TheSiteHeader.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,170 @@ | ||||
| <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" | ||||
|     > | ||||
|       <img | ||||
|         id="logo-white" | ||||
|         src="/img/logo-white.png" | ||||
|         alt="Crater Logo" | ||||
|         class="h-6" | ||||
|       /> | ||||
|     </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" | ||||
|             /> | ||||
|             Logout | ||||
|           </BaseDropdownItem> | ||||
|         </BaseDropdown> | ||||
|       </li> | ||||
|     </ul> | ||||
|   </header> | ||||
| </template> | ||||
|  | ||||
| <script setup> | ||||
| import { useAuthStore } from '@/scripts/stores/auth' | ||||
| import { useRouter } from 'vue-router' | ||||
| import { computed } from 'vue' | ||||
| import { useUserStore } from '@/scripts/stores/user' | ||||
| import { useGlobalStore } from '@/scripts/stores/global' | ||||
| import CompanySwitcher from '@/scripts/components/CompanySwitcher.vue' | ||||
| import GlobalSearchBar from '@/scripts/components/GlobalSearchBar.vue' | ||||
| import abilities from '@/scripts/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> | ||||
							
								
								
									
										178
									
								
								resources/scripts/layouts/partials/TheSiteSidebar.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								resources/scripts/layouts/partials/TheSiteSidebar.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,178 @@ | ||||
| <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 transform" | ||||
|         enter-from="-translate-x-full" | ||||
|         enter-to="translate-x-0" | ||||
|         leave="transition ease-in-out duration-300 transform" | ||||
|         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 flex-shrink-0 px-4 mb-10"> | ||||
|               <img | ||||
|                 src="/img/crater-logo.png" | ||||
|                 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 flex-shrink-0 h-5 w-5', | ||||
|                   ]" | ||||
|                   @click="globalStore.setSidebarVisibility(false)" | ||||
|                 /> | ||||
|                 {{ $t(item.title) }} | ||||
|               </router-link> | ||||
|             </nav> | ||||
|           </div> | ||||
|         </div> | ||||
|       </TransitionChild> | ||||
|       <div class="flex-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 flex-shrink-0 h-5 w-5 ', | ||||
|           ]" | ||||
|         /> | ||||
|  | ||||
|         {{ $t(item.title) }} | ||||
|       </router-link> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup> | ||||
| import { | ||||
|   Dialog, | ||||
|   DialogOverlay, | ||||
|   TransitionChild, | ||||
|   TransitionRoot, | ||||
| } from '@headlessui/vue' | ||||
|  | ||||
| import { useRoute } from 'vue-router' | ||||
| import { useGlobalStore } from '@/scripts/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