[REFACTOR] 實作側邊欄與儀表板多語系化,修復 UI 位移與樣式優化
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 52s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 52s
This commit is contained in:
@@ -39,7 +39,7 @@
|
||||
x-cloak></div>
|
||||
|
||||
<!-- ========== HEADER ========== -->
|
||||
<header class="sticky top-0 inset-x-0 flex flex-wrap sm:justify-start sm:flex-nowrap z-[48] w-full bg-white/80 backdrop-blur-md border-b border-slate-200/50 text-sm py-2.5 sm:py-4 lg:pl-64 dark:bg-[#0f172a]/80 dark:border-slate-800/50 shadow-sm">
|
||||
<header class="sticky top-0 inset-x-0 flex flex-wrap sm:justify-start sm:flex-nowrap z-[48] w-full bg-white/80 backdrop-blur-md border-b border-slate-200/50 text-sm py-2.5 sm:py-4 lg:pl-72 dark:bg-[#0f172a]/80 dark:border-slate-800/50 shadow-sm">
|
||||
<nav class="flex basis-full items-center w-full mx-auto px-4 sm:px-6 md:px-8" aria-label="Global">
|
||||
<div class="mr-5 lg:mr-0 lg:hidden text-center">
|
||||
<a class="flex-none text-xl font-bold dark:text-white font-display tracking-tight" href="{{ route('admin.dashboard') }}" aria-label="Brand">
|
||||
@@ -47,7 +47,8 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex items-center justify-end ml-auto sm:justify-between sm:gap-x-3 sm:order-3">
|
||||
<div class="w-full flex items-center justify-end ml-auto sm:gap-x-3 sm:order-3">
|
||||
<!-- Mobile Search Toggle -->
|
||||
<div class="sm:hidden">
|
||||
<button type="button" class="inline-flex flex-shrink-0 justify-center items-center gap-2 h-[2.375rem] w-[2.375rem] rounded-full font-medium bg-white text-gray-700 align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white transition-all text-xs dark:bg-gray-800 dark:hover:bg-slate-800 dark:text-gray-400 dark:hover:text-white dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800">
|
||||
<svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
@@ -56,20 +57,70 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="hidden sm:block">
|
||||
<!-- Desktop Search Placeholder -->
|
||||
<div class="hidden sm:block flex-1 max-w-sm">
|
||||
<!-- Search Input (Optional) -->
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center justify-end gap-2">
|
||||
<div class="flex flex-row items-center justify-end gap-x-1.5 sm:gap-x-3">
|
||||
<!-- Language Switcher -->
|
||||
<div class="relative inline-flex" x-data="{ langOpen: false }">
|
||||
<button type="button" @click="langOpen = !langOpen" @click.away="langOpen = false" class="inline-flex flex-shrink-0 justify-center items-center gap-2 h-[2.375rem] px-3 rounded-full font-medium bg-white text-gray-700 align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white transition-all text-xs dark:bg-gray-800 dark:hover:bg-slate-800 dark:text-gray-400 dark:hover:text-white dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800">
|
||||
<span class="flex items-center gap-x-2">
|
||||
@if(app()->getLocale() == 'zh_TW')
|
||||
<span class="text-base">🇹🇼</span>
|
||||
<span class="hidden md:inline font-bold">繁體中文</span>
|
||||
@elseif(app()->getLocale() == 'ja')
|
||||
<span class="text-base">🇯🇵</span>
|
||||
<span class="hidden md:inline font-bold">日本語</span>
|
||||
@else
|
||||
<span class="text-base">🇬🇧</span>
|
||||
<span class="hidden md:inline font-bold">English</span>
|
||||
@endif
|
||||
</span>
|
||||
<svg class="size-3 text-gray-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
||||
</button>
|
||||
|
||||
<div x-show="langOpen"
|
||||
x-transition:enter="transition ease-out duration-100"
|
||||
x-transition:enter-start="transform opacity-0 scale-95"
|
||||
x-transition:enter-end="transform opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="transform opacity-100 scale-100"
|
||||
x-transition:leave-end="transform opacity-0 scale-95"
|
||||
class="absolute right-0 top-full mt-2 min-w-[10rem] bg-white shadow-xl rounded-2xl p-2 dark:bg-gray-800 dark:border dark:border-gray-700 z-50 border border-slate-100"
|
||||
x-cloak>
|
||||
<a href="{{ route('lang.switch', 'zh_TW') }}" class="flex items-center gap-x-3 py-2.5 px-3 rounded-xl text-sm text-gray-800 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition-colors {{ app()->getLocale() == 'zh_TW' ? 'bg-gray-50 dark:bg-gray-900/50' : '' }}">
|
||||
<span class="text-lg">🇹🇼</span>
|
||||
<span class="font-bold">繁體中文</span>
|
||||
@if(app()->getLocale() == 'zh_TW')
|
||||
<svg class="ml-auto size-4 text-cyan-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
@endif
|
||||
</a>
|
||||
<a href="{{ route('lang.switch', 'en') }}" class="flex items-center gap-x-3 py-2.5 px-3 rounded-xl text-sm text-gray-800 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition-colors {{ app()->getLocale() == 'en' ? 'bg-gray-50 dark:bg-gray-900/50' : '' }}">
|
||||
<span class="text-lg">🇬🇧</span>
|
||||
<span class="font-bold">English</span>
|
||||
@if(app()->getLocale() == 'en')
|
||||
<svg class="ml-auto size-4 text-cyan-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
@endif
|
||||
</a>
|
||||
<a href="{{ route('lang.switch', 'ja') }}" class="flex items-center gap-x-3 py-2.5 px-3 rounded-xl text-sm text-gray-800 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition-colors {{ app()->getLocale() == 'ja' ? 'bg-gray-50 dark:bg-gray-900/50' : '' }}">
|
||||
<span class="text-lg">🇯🇵</span>
|
||||
<span class="font-bold">日本語</span>
|
||||
@if(app()->getLocale() == 'ja')
|
||||
<svg class="ml-auto size-4 text-cyan-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
@endif
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dark Mode Toggle -->
|
||||
<button type="button"
|
||||
@click="darkMode = !darkMode; localStorage.setItem('darkMode', darkMode); document.documentElement.classList.toggle('dark', darkMode)"
|
||||
class="inline-flex flex-shrink-0 justify-center items-center gap-2 h-[2.375rem] w-[2.375rem] rounded-full font-medium bg-white text-gray-700 align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white transition-all text-xs dark:bg-gray-800 dark:hover:bg-slate-800 dark:text-gray-400 dark:hover:text-white dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800">
|
||||
<!-- Moon Icon (shown in light mode) -->
|
||||
<svg x-show="!darkMode" class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278zM4.858 1.311A7.269 7.269 0 0 0 1.025 7.71c0 4.02 3.279 7.276 7.319 7.276a7.316 7.316 0 0 0 5.205-2.162c-.337.042-.68.063-1.029.063-4.61 0-8.343-3.714-8.343-8.29 0-1.167.242-2.278.681-3.286z"/>
|
||||
</svg>
|
||||
<!-- Sun Icon (shown in dark mode) -->
|
||||
<svg x-show="darkMode" class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
|
||||
</svg>
|
||||
@@ -77,7 +128,7 @@
|
||||
|
||||
<!-- Profile Dropdown -->
|
||||
<div class="relative inline-flex" x-data="{ open: false }">
|
||||
<button id="hs-dropdown-with-header" type="button" @click="open = !open" @click.away="open = false" class="inline-flex flex-shrink-0 justify-center items-center gap-2 h-[2.375rem] w-[2.375rem] rounded-full font-medium bg-white text-gray-700 align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white transition-all text-xs dark:bg-gray-800 dark:hover:bg-slate-800 dark:text-gray-400 dark:hover:text-white dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800">
|
||||
<button type="button" @click="open = !open" @click.away="open = false" class="inline-flex flex-shrink-0 justify-center items-center gap-2 h-[2.375rem] w-[2.375rem] rounded-full font-medium bg-white text-gray-700 align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-white transition-all text-xs dark:bg-gray-800 dark:hover:bg-slate-800 dark:text-gray-400 dark:hover:text-white dark:focus:ring-gray-700 dark:focus:ring-offset-gray-800">
|
||||
<img class="inline-block h-[2.375rem] w-[2.375rem] rounded-full ring-2 ring-white dark:ring-gray-800" src="https://ui-avatars.com/api/?name={{ Auth::user()->name }}&background=0D8ABC&color=fff" alt="Image Description">
|
||||
</button>
|
||||
|
||||
@@ -88,22 +139,23 @@
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="transform opacity-100 scale-100"
|
||||
x-transition:leave-end="transform opacity-0 scale-95"
|
||||
class="absolute right-0 top-full mt-2 min-w-[15rem] bg-white shadow-md rounded-lg p-2 dark:bg-gray-800 dark:border dark:border-gray-700 z-50"
|
||||
class="absolute right-0 top-full mt-2 min-w-[15rem] bg-white shadow-xl rounded-2xl p-2 dark:bg-gray-800 dark:border dark:border-gray-700 z-50 border border-slate-100"
|
||||
x-cloak>
|
||||
<div class="py-3 px-5 -m-2 bg-gray-100 rounded-t-lg dark:bg-gray-700">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Signed in as</p>
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-gray-300">{{ Auth::user()->email }}</p>
|
||||
<div class="py-3 px-5 -m-2 bg-slate-50 rounded-t-2xl dark:bg-slate-900/50 border-b border-slate-100 dark:border-slate-700">
|
||||
<p class="text-[10px] font-black uppercase tracking-widest text-slate-400 dark:text-slate-500">Signed in as</p>
|
||||
<p class="text-sm font-bold text-slate-700 dark:text-slate-200 truncate">{{ Auth::user()->email }}</p>
|
||||
</div>
|
||||
<div class="mt-2 py-2 first:pt-0 last:pb-0">
|
||||
<a class="flex items-center gap-x-3.5 py-2 px-3 rounded-md text-sm text-gray-800 hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition-colors" href="{{ route('profile.edit') }}">
|
||||
<svg class="flex-shrink-0 w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
帳戶設定
|
||||
<div class="mt-2 py-2">
|
||||
<a class="flex items-center gap-x-3.5 py-2.5 px-3 rounded-xl text-sm font-bold text-slate-700 hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 dark:text-slate-300 dark:hover:bg-gray-700 dark:hover:text-white transition-colors" href="{{ route('profile.edit') }}">
|
||||
<svg class="flex-shrink-0 size-4 text-cyan-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
{{ __('Account Settings') }}
|
||||
</a>
|
||||
<div class="my-2 border-t border-slate-100 dark:border-slate-700"></div>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="w-full flex items-center gap-x-3.5 py-2 px-3 rounded-md text-sm text-gray-800 hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300">
|
||||
<svg class="flex-shrink-0 w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>
|
||||
登出
|
||||
<button type="submit" class="w-full flex items-center gap-x-3.5 py-2.5 px-3 rounded-xl text-sm font-bold text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-500/10 transition-colors">
|
||||
<svg class="flex-shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>
|
||||
{{ __('Logout') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user