2025-11-29 09:41:38 +09:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
|
|
|
|
|
|
|
|
|
<title>{{ $title ?? config('app.name', 'Knowledge Base') }}</title>
|
|
|
|
|
|
|
|
|
|
<link rel="preconnect" href="https://fonts.bunny.net">
|
|
|
|
|
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
|
|
|
|
<link rel="stylesheet"
|
|
|
|
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github-dark.min.css">
|
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
|
|
|
|
<script>document.addEventListener('livewire:navigated', function() { hljs.highlightAll(); });</script>
|
|
|
|
|
<style>
|
|
|
|
|
pre code.hljs {
|
2025-11-29 12:00:09 +09:00
|
|
|
background: #1e1e1e !important;
|
2025-11-29 09:41:38 +09:00
|
|
|
color: #dcdcdc !important;
|
|
|
|
|
padding: 1rem;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
display: block;
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
|
|
|
|
@livewireStyles
|
|
|
|
|
@stack('styles')
|
|
|
|
|
</head>
|
|
|
|
|
<body class="font-sans antialiased">
|
|
|
|
|
<div class="min-h-screen bg-gray-50">
|
|
|
|
|
<!-- Header -->
|
|
|
|
|
<header class="bg-white border-b border-gray-200 sticky top-0 z-10">
|
|
|
|
|
<div class="max-w-full mx-auto px-4 sm:px-6 lg:px-8">
|
|
|
|
|
<div class="flex justify-between h-16">
|
2025-11-29 12:08:05 +09:00
|
|
|
<div class="flex items-center space-x-3">
|
|
|
|
|
<a href="/" class="flex items-center space-x-3">
|
|
|
|
|
<x-application-logo class="block h-8 w-auto fill-current text-gray-800" />
|
|
|
|
|
<h1 class="text-xl font-semibold text-gray-900">
|
|
|
|
|
{{ config('app.name', 'Knowledge Base') }}
|
|
|
|
|
</h1>
|
|
|
|
|
</a>
|
2025-11-29 09:41:38 +09:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="flex items-center space-x-4">
|
|
|
|
|
<!-- Quick Switcher Trigger -->
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class="inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
2025-11-29 11:08:17 +09:00
|
|
|
x-data
|
|
|
|
|
@click.prevent="$dispatch('open-quick-switcher')"
|
2025-11-29 09:41:38 +09:00
|
|
|
>
|
|
|
|
|
<svg class="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<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"></path>
|
|
|
|
|
</svg>
|
2025-11-29 12:00:09 +09:00
|
|
|
{{ __('messages.quick_switcher.title') }}
|
2025-11-29 09:41:38 +09:00
|
|
|
<kbd class="ml-2 px-2 py-1 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded">
|
|
|
|
|
Ctrl+K
|
|
|
|
|
</kbd>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
@auth
|
|
|
|
|
<!-- User Dropdown -->
|
|
|
|
|
<div x-data="{ open: false }" @click.away="open = false" class="relative">
|
|
|
|
|
<button
|
|
|
|
|
@click="open = !open"
|
|
|
|
|
class="flex items-center text-sm font-medium text-gray-700 hover:text-gray-900 focus:outline-none"
|
|
|
|
|
>
|
|
|
|
|
{{ Auth::user()->name }}
|
|
|
|
|
<svg class="ml-1 h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
|
|
|
|
|
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
x-show="open"
|
|
|
|
|
x-transition
|
|
|
|
|
class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 ring-1 ring-black ring-opacity-5"
|
|
|
|
|
>
|
|
|
|
|
<a href="{{ route('profile.edit') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
2025-11-29 12:00:09 +09:00
|
|
|
{{ __('messages.nav.profile') }}
|
2025-11-29 09:41:38 +09:00
|
|
|
</a>
|
2025-11-29 11:08:17 +09:00
|
|
|
@if(Auth::user()->isAdmin())
|
|
|
|
|
<a href="{{ route('admin.users.index') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
|
|
|
|
<span class="flex items-center">
|
|
|
|
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path>
|
|
|
|
|
</svg>
|
2025-11-29 12:00:09 +09:00
|
|
|
{{ __('messages.nav.user_management') }}
|
2025-11-29 11:08:17 +09:00
|
|
|
</span>
|
|
|
|
|
</a>
|
|
|
|
|
@endif
|
2025-11-29 09:41:38 +09:00
|
|
|
<form method="POST" action="{{ route('logout') }}">
|
|
|
|
|
@csrf
|
|
|
|
|
<button type="submit" class="w-full text-left block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
2025-11-29 12:00:09 +09:00
|
|
|
{{ __('messages.nav.logout') }}
|
2025-11-29 09:41:38 +09:00
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@else
|
|
|
|
|
<a href="{{ route('login') }}" class="text-sm text-gray-700 hover:text-gray-900">
|
2025-11-29 12:00:09 +09:00
|
|
|
{{ __('messages.nav.login') }}
|
2025-11-29 09:41:38 +09:00
|
|
|
</a>
|
|
|
|
|
@endauth
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<!-- Main Content -->
|
|
|
|
|
<div class="flex h-[calc(100vh-4rem)]">
|
|
|
|
|
<!-- Sidebar -->
|
|
|
|
|
<aside class="w-64 bg-white border-r border-gray-200 overflow-y-auto">
|
|
|
|
|
@livewire('sidebar-tree')
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
<!-- Main Panel -->
|
|
|
|
|
<main class="flex-1 overflow-y-auto">
|
|
|
|
|
{{ $slot }}
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Quick Switcher Modal -->
|
|
|
|
|
@livewire('quick-switcher')
|
|
|
|
|
|
|
|
|
|
@livewireScripts
|
|
|
|
|
|
|
|
|
|
<!-- Global Keyboard Shortcuts -->
|
|
|
|
|
<script>
|
|
|
|
|
document.addEventListener('keydown', function(e) {
|
|
|
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
window.dispatchEvent(new CustomEvent('open-quick-switcher'));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.addEventListener('alpine:init', () => {
|
|
|
|
|
Alpine.data('sidebarState', () => ({
|
|
|
|
|
expandedFolders: [],
|
|
|
|
|
|
|
|
|
|
initExpandedFolders() {
|
|
|
|
|
const stored = localStorage.getItem('kb_expanded_folders');
|
|
|
|
|
if (stored) {
|
|
|
|
|
try {
|
|
|
|
|
this.expandedFolders = JSON.parse(stored);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
this.expandedFolders = [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
toggleFolder(path) {
|
|
|
|
|
const index = this.expandedFolders.indexOf(path);
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
this.expandedFolders.splice(index, 1);
|
|
|
|
|
} else {
|
|
|
|
|
this.expandedFolders.push(path);
|
|
|
|
|
}
|
|
|
|
|
localStorage.setItem('kb_expanded_folders', JSON.stringify(this.expandedFolders));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
isFolderExpanded(path) {
|
|
|
|
|
return this.expandedFolders.includes(path);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
@stack('scripts')
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|