292 lines
12 KiB
Vue
292 lines
12 KiB
Vue
<script setup>
|
|
import { ref, onMounted } from 'vue';
|
|
import { Link, Head, router } from '@inertiajs/vue3';
|
|
import Toaster from '@/Shared/Toaster.vue';
|
|
import FooterLayout from '@/Layouts/FooterLayout.vue';
|
|
import ChevronIcon from '@/Shared/Icons/ChevronIcon.vue';
|
|
import DarkModeIcon from '@/Shared/Icons/DarkModeIcon.vue';
|
|
import LayoutNav from '@/Layouts/LayoutNav.vue';
|
|
import { Settings, LogOut, ScanSearch } from 'lucide-vue-next';
|
|
import { isDark, flash } from '@/methods.js';
|
|
|
|
const props = defineProps({
|
|
title: String,
|
|
insideVault: Boolean,
|
|
layoutData: Object,
|
|
});
|
|
|
|
const checked = ref(isDark());
|
|
|
|
onMounted(() => {
|
|
if (localStorage.success) {
|
|
flash(localStorage.success, 'success');
|
|
localStorage.removeItem('success');
|
|
}
|
|
});
|
|
|
|
const goToSearchPage = () => {
|
|
router.visit(props.layoutData.vault.url.search, {
|
|
data: { searchTerm: '' },
|
|
});
|
|
};
|
|
|
|
const toggleStyle = () => {
|
|
checked.value = !checked.value;
|
|
if (checked.value) {
|
|
document.documentElement.classList.add('dark');
|
|
localStorage.theme = 'dark';
|
|
} else {
|
|
document.documentElement.classList.remove('dark');
|
|
localStorage.theme = 'light';
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Head :title="title" />
|
|
|
|
<div class="min-h-full">
|
|
<div class="sm:fixed top-0 z-10 w-full">
|
|
<!-- main nav - only displayed on desktop -->
|
|
<nav
|
|
class="hidden max-w-8xl mx-auto sm:flex h-10 items-center justify-between border-b border-gray-300 bg-gray-50 px-3 dark:border-slate-600 dark:bg-gray-800 dark:text-slate-200 sm:px-6">
|
|
<div
|
|
class="dark:highlight-white/5 items-center rounded-lg border border-gray-200 bg-white px-2 py-1 text-sm dark:border-0 dark:border-gray-700 dark:bg-gray-400/20 dark:bg-gray-900 sm:flex">
|
|
<Link :href="layoutData.url.vaults" class="shrink-0 dark:text-sky-400">
|
|
{{ layoutData.user.name }}
|
|
</Link>
|
|
|
|
<!-- information about the current vault -->
|
|
<div v-if="layoutData.vault">
|
|
<span class="relative mx-1">
|
|
<ChevronIcon :type="'right'" />
|
|
</span>
|
|
{{ layoutData.vault.name }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- search box -->
|
|
<div v-if="insideVault" class="flew-grow relative">
|
|
<ScanSearch class="absolute start-2 top-2 h-4 w-4 text-gray-400" />
|
|
<input
|
|
type="text"
|
|
class="dark:highlight-white/5 block w-64 rounded-md border border-gray-300 px-2 py-1 text-center placeholder:text-gray-600 hover:cursor-pointer focus:border-indigo-500 focus:ring-indigo-500 dark:border-0 dark:border-gray-700 dark:bg-slate-900 dark:placeholder:text-gray-400 dark:hover:bg-slate-700 sm:text-sm"
|
|
:placeholder="$t('Search something')"
|
|
@focus="goToSearchPage" />
|
|
</div>
|
|
|
|
<!-- icons -->
|
|
<div class="flex items-center justify-end gap-4">
|
|
<div class="relative top-[3px] me-4 inline">
|
|
<label for="dark-mode-toggle" class="relative inline-flex cursor-pointer">
|
|
<input id="dark-mode-toggle" v-model="checked" type="checkbox" class="peer hidden" @click="toggleStyle" />
|
|
<div
|
|
class="peer me-2 h-4 w-7 rounded-full bg-gray-200 after:absolute after:left-[2px] after:right-[14px] after:top-[2px] after:h-3 after:w-3 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:bg-blue-600 peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-hidden peer-focus:ring-4 peer-focus:ring-blue-300 dark:border-gray-600 dark:bg-gray-800 dark:peer-focus:ring-blue-800" />
|
|
<DarkModeIcon :checked="checked" />
|
|
</label>
|
|
</div>
|
|
<Link :href="layoutData.url.settings" class="relative flex items-center gap-1">
|
|
<Settings
|
|
class="h-4 w-4 cursor-pointer text-gray-600 hover:text-gray-900 dark:text-gray-600 dark:hover:text-gray-100" />
|
|
|
|
<span class="text-sm dark:text-sky-400">{{ $t('Settings') }}</span>
|
|
</Link>
|
|
<Link
|
|
class="relative flex items-center gap-1 cursor-pointer"
|
|
method="post"
|
|
:href="route('logout')"
|
|
as="button">
|
|
<LogOut
|
|
class="h-4 w-4 cursor-pointer text-gray-600 hover:text-gray-900 dark:text-gray-600 dark:hover:text-gray-100" />
|
|
|
|
<span class="text-sm dark:text-sky-400">{{ $t('Logout') }}</span>
|
|
</Link>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- mobile nav -->
|
|
<div
|
|
class="sm:hidden pt-3 border-b bg-gray-50 px-3 dark:border-slate-600 dark:bg-gray-800 dark:text-slate-200 sm:px-6">
|
|
<!-- user / vault & logout -->
|
|
<div
|
|
class="flex mb-2 dark:highlight-white/5 items-center justify-between text-sm dark:border-0 dark:border-gray-700 dark:bg-gray-400/20 dark:bg-gray-900">
|
|
<div class="flex items-center border border-gray-200 rounded-lg bg-white px-2 py-1">
|
|
<Link :href="layoutData.url.vaults" class="shrink-0 dark:text-sky-400">
|
|
{{ layoutData.user.name }}
|
|
</Link>
|
|
|
|
<!-- information about the current vault -->
|
|
<div v-if="layoutData.vault">
|
|
<span class="relative mx-1">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="relative inline h-3 w-3"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
</svg>
|
|
</span>
|
|
{{ layoutData.vault.name }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center">
|
|
<!-- settings -->
|
|
<Link :href="layoutData.url.settings" class="border border-gray-200 rounded-lg bg-white px-2 py-1 mr-2">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-4 w-4"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
</svg>
|
|
</Link>
|
|
|
|
<!-- logout -->
|
|
<Link
|
|
class="border border-gray-200 rounded-lg bg-white px-2 py-1"
|
|
method="post"
|
|
:href="route('logout')"
|
|
as="button">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-4 w-4"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
|
</svg>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- search box -->
|
|
<div v-if="insideVault" class="relative mb-2">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="absolute start-2 top-2 h-4 w-4 text-gray-400"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
</svg>
|
|
<input
|
|
type="text"
|
|
class="dark:highlight-white/5 block w-full rounded-md border border-gray-300 px-2 py-1 text-center placeholder:text-gray-600 hover:cursor-pointer focus:border-indigo-500 focus:ring-indigo-500 dark:border-0 dark:border-gray-700 dark:bg-slate-900 dark:placeholder:text-gray-400 dark:hover:bg-slate-700 sm:text-sm"
|
|
:placeholder="$t('Search something')"
|
|
@focus="goToSearchPage" />
|
|
</div>
|
|
</div>
|
|
|
|
<LayoutNav
|
|
v-if="insideVault"
|
|
:links="
|
|
[
|
|
{
|
|
url: layoutData.vault.url.dashboard,
|
|
title: $t('Dashboard'),
|
|
selected: $page.component === 'Vault/Dashboard/Index',
|
|
},
|
|
{
|
|
url: layoutData.vault.url.contacts,
|
|
title: $t('Contacts'),
|
|
selected: $page.component.startsWith('Vault/Contact'),
|
|
},
|
|
layoutData.vault.visibility.show_calendar_tab
|
|
? {
|
|
url: layoutData.vault.url.calendar,
|
|
title: $t('Calendar'),
|
|
selected: $page.component.startsWith('Vault/Calendar'),
|
|
}
|
|
: null,
|
|
layoutData.vault.visibility.show_journal_tab
|
|
? {
|
|
url: layoutData.vault.url.journals,
|
|
title: $t('Journals'),
|
|
selected: $page.component.startsWith('Vault/Journal'),
|
|
}
|
|
: null,
|
|
layoutData.vault.visibility.show_group_tab
|
|
? {
|
|
url: layoutData.vault.url.groups,
|
|
title: $t('Groups'),
|
|
selected: $page.component.startsWith('Vault/Group'),
|
|
}
|
|
: null,
|
|
layoutData.vault.visibility.show_companies_tab
|
|
? {
|
|
url: layoutData.vault.url.companies,
|
|
title: $t('Companies'),
|
|
selected: $page.component.startsWith('Vault/Companies'),
|
|
}
|
|
: null,
|
|
layoutData.vault.visibility.show_tasks_tab
|
|
? {
|
|
url: layoutData.vault.url.tasks,
|
|
title: $t('Tasks'),
|
|
selected: $page.component.startsWith('Vault/Dashboard/Task'),
|
|
}
|
|
: null,
|
|
layoutData.vault.visibility.show_reports_tab
|
|
? {
|
|
url: layoutData.vault.url.reports,
|
|
title: $t('Reports'),
|
|
selected: $page.component.startsWith('Vault/Reports'),
|
|
}
|
|
: null,
|
|
layoutData.vault.visibility.show_files_tab
|
|
? {
|
|
url: layoutData.vault.url.files,
|
|
title: $t('Files'),
|
|
selected: $page.component.startsWith('Vault/Files'),
|
|
}
|
|
: null,
|
|
layoutData.vault.permission.at_least_editor
|
|
? {
|
|
url: layoutData.vault.url.settings,
|
|
title: $t('Vault settings'),
|
|
selected: $page.component.startsWith('Vault/Settings'),
|
|
}
|
|
: null,
|
|
].filter((f) => f !== null)
|
|
" />
|
|
</div>
|
|
|
|
<!-- Page Heading -->
|
|
<header
|
|
v-if="$slots.header"
|
|
class="relative mb-8 mt-10 bg-white dark:bg-gray-900 sm:border-b border-gray-200 dark:border-gray-700">
|
|
<div class="max-w-8xl mx-auto hidden px-4 py-2 sm:px-6 md:block">
|
|
<slot name="header" />
|
|
</div>
|
|
</header>
|
|
|
|
<main class="relative mb-8 mt-10">
|
|
<slot />
|
|
</main>
|
|
|
|
<FooterLayout />
|
|
</div>
|
|
|
|
<Toaster />
|
|
</template>
|