jasan/nav.php
2026-03-14 22:09:17 +09:00

204 lines
No EOL
11 KiB
PHP

<?php
/**
* nav.php
* 공통 네비게이션 컴포넌트 (최상단 고정형 리본 메뉴)
*/
require_once 'config.php';
$current_page = basename($_SERVER['PHP_SELF']);
// 시스템 설정 로드 (config.php에서 테이블 생성을 보장함)
$settings = [];
try {
$settings = $db->query("SELECT key_name, value FROM system_settings")->fetchAll(PDO::FETCH_KEY_PAIR);
} catch (Exception $e) {
$settings = ['menu_cards_visible' => '1', 'menu_mfp_visible' => '1'];
}
?>
<nav class="fixed top-0 left-0 right-0 bg-[#0f172a] text-white z-[100] shadow-2xl border-b border-blue-500/20 px-6">
<div class="max-w-[1600px] mx-auto h-20 flex items-center justify-between">
<!-- Logo -->
<div class="flex items-center space-x-3 mr-10">
<a href="index.php" class="flex items-center">
<div
class="w-10 h-10 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg mr-3">
<span class="text-xl font-black italic">F</span>
</div>
<div class="hidden sm:block">
<h1 class="text-xl font-black tracking-tighter">FKI <span class="text-blue-400">JASAN</span></h1>
<p class="text-[9px] text-slate-400 font-bold uppercase tracking-widest leading-none">V2.0 Architect
</p>
</div>
</a>
</div>
<!-- Horizontal Menu -->
<div class="flex-1 flex items-center justify-center space-x-1 lg:space-x-4 overflow-x-auto no-scrollbar py-2">
<?php
$menu_items = [
['url' => 'rental.php', 'name' => '노트북 임대', 'icon' => 'M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4'],
['url' => 'general_rental.php', 'name' => '임대', 'icon' => 'M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4'],
['url' => 'users.php', 'name' => '직원 관리', 'icon' => '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'],
['url' => 'laptops.php', 'name' => '노트북 자산', 'icon' => 'M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z'],
];
if (($settings['menu_cards_visible'] ?? '1') === '1') {
$menu_items[] = ['url' => 'cards.php', 'name' => '출입증 현황', 'icon' => 'M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z'];
}
if (($settings['menu_mfp_visible'] ?? '1') === '1') {
$menu_items[] = ['url' => 'mfp.php', 'name' => '복합기 계정', 'icon' => 'M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z'];
}
$menu_items[] = ['url' => 'models.php', 'name' => '관리자페이지', 'icon' => 'M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4'];
$menu_items[] = ['url' => 'admins.php', 'name' => 'ADMIN', 'icon' => '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 M15 12a3 3 0 11-6 0 3 3 0 016 0z'];
foreach ($menu_items as $item):
$is_active = ($current_page == $item['url']);
?>
<a href="<?php echo $item['url']; ?>" class="flex items-center px-4 py-2.5 rounded-xl text-sm font-bold transition-all duration-300 transform whitespace-nowrap
<?php echo $is_active
? 'bg-blue-600 text-white shadow-[0_0_20px_rgba(37,99,235,0.4)] scale-105'
: 'text-slate-400 hover:text-white hover:bg-slate-800'; ?>">
<svg class="w-5 h-5 mr-2 hidden md:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="<?php echo $item['icon']; ?>" />
</svg>
<?php echo $item['name']; ?>
</a>
<?php endforeach; ?>
</div>
<!-- User Info & Logout -->
<div class="hidden lg:flex items-center ml-10 space-x-4 border-l border-slate-700 pl-10" x-data="{
logout() {
window.showConfirm('로그아웃 하시겠습니까?', () => {
fetch('api.php?action=logout').then(() => location.href = 'login.php');
});
}
}">
<div class="text-right">
<p class="text-xs font-black text-white"><?php echo $_SESSION['admin_name'] ?? 'ADMIN'; ?></p>
<button @click="logout()"
class="text-[10px] text-blue-400 font-bold uppercase tracking-tight hover:text-white transition-colors flex items-center gap-1">
Sign Out
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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>
</button>
</div>
<div
class="w-10 h-10 bg-slate-800 rounded-full flex items-center justify-center border border-slate-700 shadow-inner">
<span
class="text-xs font-black text-slate-300"><?php echo strtoupper(substr($_SESSION['admin_login_id'] ?? 'AD', 0, 2)); ?></span>
</div>
</div>
<!-- Mobile Menu Placeholder -->
<div class="lg:hidden ml-4">
<div class="w-8 h-8 flex items-center justify-center">
<span class="w-1.5 h-1.5 bg-emerald-500 rounded-full animate-ping"></span>
</div>
</div>
</div>
</nav>
<!-- Global Styles for Scrollbar -->
<style>
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
body {
padding-top: 80px;
}
</style>
<!-- Global Notification System (Antigravity Standard) -->
<div x-data="{
show: false,
type: 'alert',
title: '',
message: '',
onConfirm: null,
init() {
window.showAlert = (msg, title = '알림', type = 'alert') => {
this.type = type;
this.title = title;
this.message = msg;
this.onConfirm = null;
this.show = true;
};
window.showConfirm = (msg, callback, title = '확인 요청') => {
this.type = 'confirm';
this.title = title;
this.message = msg;
this.onConfirm = callback;
this.show = true;
};
},
confirmAction() {
if (this.onConfirm) this.onConfirm();
this.show = false;
}
}" x-show="show" x-cloak class="fixed inset-0 z-[1000] flex items-center justify-center p-4">
<div class="fixed inset-0 bg-slate-900/60 backdrop-blur-sm" @click="show = false"></div>
<div
class="bg-white rounded-[2.5rem] p-8 max-w-sm w-full relative z-[1001] shadow-[0_32px_64px_-12px_rgba(0,0,0,0.2)] border border-white/20 transform transition-all animate-in zoom-in-95 duration-200">
<div class="mb-6 text-center">
<div class="w-20 h-20 rounded-3xl mb-6 flex items-center justify-center mx-auto transition-transform hover:scale-110 duration-300"
:class="{
'bg-amber-50 text-amber-500': type === 'confirm',
'bg-rose-50 text-rose-500': type === 'error',
'bg-emerald-50 text-emerald-500': type === 'success',
'bg-blue-50 text-blue-500': type === 'alert'
}">
<template x-if="type === 'confirm'">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</template>
<template x-if="type === 'error'">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</template>
<template x-if="type === 'success'">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</template>
<template x-if="type === 'alert'">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</template>
</div>
<h4 class="text-2xl font-black text-slate-900 mb-3 tracking-tight" x-text="title"></h4>
<p class="text-sm font-bold text-slate-500 leading-relaxed whitespace-pre-line px-2" x-text="message"></p>
</div>
<div class="flex gap-3 mt-8">
<template x-if="type === 'confirm'">
<button @click="show = false"
class="flex-1 py-4 bg-slate-100 rounded-2xl font-black text-sm text-slate-600 hover:bg-slate-200 transition-all active:scale-95">취소</button>
</template>
<button @click="confirmAction"
class="flex-1 py-4 rounded-2xl font-black text-sm text-white shadow-2xl transition-all active:scale-95"
:class="{
'bg-amber-500 shadow-amber-200 hover:bg-amber-600': type === 'confirm',
'bg-rose-500 shadow-rose-200 hover:bg-rose-600': type === 'error',
'bg-emerald-500 shadow-emerald-200 hover:bg-emerald-600': type === 'success',
'bg-blue-600 shadow-blue-200 hover:bg-blue-700': type === 'alert'
}" x-text="type === 'confirm' ? '확인 및 실행' : '확인'"></button>
</div>
</div>
</div>