리셋
This commit is contained in:
parent
3e1cd3adab
commit
cfeea88124
7 changed files with 247 additions and 41 deletions
49
cards.php
49
cards.php
|
|
@ -27,15 +27,29 @@
|
||||||
<?php include 'nav.php'; ?>
|
<?php include 'nav.php'; ?>
|
||||||
|
|
||||||
<main class="max-w-[1600px] mx-auto p-8 pt-10">
|
<main class="max-w-[1600px] mx-auto p-8 pt-10">
|
||||||
<header class="mb-10 flex justify-between items-center">
|
<header class="mb-10 flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-3xl font-extrabold text-slate-900 tracking-tight">출입증 관리</h2>
|
<h2 class="text-3xl font-extrabold text-slate-900 tracking-tight">출입증 관리</h2>
|
||||||
<p class="text-slate-500 mt-1">보안실 연동 임시 출입증 및 NFC ID 관리</p>
|
<p class="text-slate-500 mt-1">보안실 연동 임시 출입증 및 NFC ID 관리</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="relative group">
|
||||||
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg class="h-4 w-4 text-slate-400 group-focus-within:text-emerald-500 transition-colors"
|
||||||
|
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" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input type="text" x-model="searchQuery"
|
||||||
|
class="block w-64 pl-10 pr-3 py-2.5 border border-slate-200 rounded-xl leading-5 bg-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-emerald-500 transition-all text-sm shadow-sm"
|
||||||
|
placeholder="카드번호, 종류, NFC ID 검색...">
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
@click="showModal = true; isEdit = false; formData = { id: '', card_number: '', card_type: '', nfc_id: '' }"
|
@click="showModal = true; isEdit = false; formData = { id: '', card_number: '', card_type: '', nfc_id: '' }"
|
||||||
class="bg-emerald-600 text-white px-5 py-2.5 rounded-xl font-bold shadow-lg shadow-emerald-200 hover:bg-emerald-700 transition-all">신규
|
class="bg-emerald-600 text-white px-5 py-2.5 rounded-xl font-bold shadow-lg shadow-emerald-200 hover:bg-emerald-700 transition-all shrink-0">신규
|
||||||
카드 등록</button>
|
카드 등록</button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Bulk Actions (Fixed Floating Bar) -->
|
<!-- Bulk Actions (Fixed Floating Bar) -->
|
||||||
|
|
@ -73,7 +87,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Card List Table -->
|
<!-- Card List Table -->
|
||||||
<div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden" x-show="cards.length > 0">
|
<div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden"
|
||||||
|
x-show="!loading && filteredCards.length > 0">
|
||||||
<table class="min-w-full divide-y divide-slate-100">
|
<table class="min-w-full divide-y divide-slate-100">
|
||||||
<thead class="bg-slate-50/50">
|
<thead class="bg-slate-50/50">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -92,7 +107,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-slate-50">
|
<tbody class="divide-y divide-slate-50">
|
||||||
<template x-for="card in cards" :key="card.id">
|
<template x-for="card in filteredCards" :key="card.id">
|
||||||
<tr class="hover:bg-slate-50/80 transition-colors group">
|
<tr class="hover:bg-slate-50/80 transition-colors group">
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
<input type="checkbox" :value="card.id" x-model="selectedIds"
|
<input type="checkbox" :value="card.id" x-model="selectedIds"
|
||||||
|
|
@ -119,6 +134,19 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div x-show="loading" class="p-20 flex justify-center">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-emerald-600"></div>
|
||||||
|
</div>
|
||||||
|
<div x-show="!loading && filteredCards.length === 0"
|
||||||
|
class="p-20 text-center bg-white rounded-3xl border border-dashed border-slate-300">
|
||||||
|
<template x-if="searchQuery">
|
||||||
|
<p class="text-slate-400 font-medium italic">검색 결과와 일치하는 카드가 없습니다.</p>
|
||||||
|
</template>
|
||||||
|
<template x-if="!searchQuery">
|
||||||
|
<p class="text-slate-400 font-medium italic">등록된 출입증이 없습니다.</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Card Modal -->
|
<!-- Card Modal -->
|
||||||
|
|
@ -154,17 +182,30 @@
|
||||||
showModal: false,
|
showModal: false,
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
|
loading: true,
|
||||||
|
searchQuery: '',
|
||||||
bulkStatus: '',
|
bulkStatus: '',
|
||||||
bulkCardType: '',
|
bulkCardType: '',
|
||||||
formData: { id: '', card_number: '', card_type: '', nfc_id: '' },
|
formData: { id: '', card_number: '', card_type: '', nfc_id: '' },
|
||||||
init() { this.fetchCards(); },
|
init() { this.fetchCards(); },
|
||||||
fetchCards() {
|
fetchCards() {
|
||||||
const scrollPos = window.scrollY;
|
const scrollPos = window.scrollY;
|
||||||
|
this.loading = true;
|
||||||
fetch('api.php?action=get_cards').then(res => res.json()).then(data => {
|
fetch('api.php?action=get_cards').then(res => res.json()).then(data => {
|
||||||
this.cards = data;
|
this.cards = data;
|
||||||
|
this.loading = false;
|
||||||
this.$nextTick(() => window.scrollTo(0, scrollPos));
|
this.$nextTick(() => window.scrollTo(0, scrollPos));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
get filteredCards() {
|
||||||
|
if (!this.searchQuery) return this.cards;
|
||||||
|
const q = this.searchQuery.toLowerCase();
|
||||||
|
return this.cards.filter(c =>
|
||||||
|
(c.card_number && c.card_number.toLowerCase().includes(q)) ||
|
||||||
|
(c.card_type && c.card_type.toLowerCase().includes(q)) ||
|
||||||
|
(c.nfc_id && c.nfc_id.toLowerCase().includes(q))
|
||||||
|
);
|
||||||
|
},
|
||||||
toggleSelectAll(checked) {
|
toggleSelectAll(checked) {
|
||||||
this.selectedIds = checked ? this.cards.map(c => c.id) : [];
|
this.selectedIds = checked ? this.cards.map(c => c.id) : [];
|
||||||
},
|
},
|
||||||
|
|
|
||||||
6
check_admins.php
Normal file
6
check_admins.php
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
$stmt = $db->query("SELECT * FROM administrators");
|
||||||
|
$admins = $stmt->fetchAll();
|
||||||
|
print_r($admins);
|
||||||
|
?>
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<?php
|
|
||||||
require_once 'config.php';
|
|
||||||
try {
|
|
||||||
$db->exec("ALTER TABLE laptop_assets ADD COLUMN last_confirmed_date TEXT");
|
|
||||||
echo "Column 'last_confirmed_date' added successfully to laptop_assets table.\n";
|
|
||||||
} catch (Exception $e) {
|
|
||||||
echo "Error or column already exists: " . $e->getMessage() . "\n";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
@ -44,18 +44,33 @@
|
||||||
<h2 class="text-3xl font-extrabold text-slate-900 tracking-tight">일반 물품 임대</h2>
|
<h2 class="text-3xl font-extrabold text-slate-900 tracking-tight">일반 물품 임대</h2>
|
||||||
<p class="text-slate-500 mt-1">사원별 각종 물품(어댑터, 주변기기 등) 단기 임대 및 반납 기록</p>
|
<p class="text-slate-500 mt-1">사원별 각종 물품(어댑터, 주변기기 등) 단기 임대 및 반납 기록</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="relative group">
|
||||||
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg class="h-4 w-4 text-slate-400 group-focus-within:text-blue-500 transition-colors"
|
||||||
|
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" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input type="text" x-model="searchQuery"
|
||||||
|
class="block w-64 pl-10 pr-3 py-2.5 border border-slate-200 rounded-xl leading-5 bg-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all text-sm shadow-sm"
|
||||||
|
placeholder="사원명, 품목 검색...">
|
||||||
|
</div>
|
||||||
<button @click="openAddModal()"
|
<button @click="openAddModal()"
|
||||||
class="px-6 py-3 bg-blue-600 text-white rounded-2xl font-bold shadow-lg shadow-blue-200 hover:bg-blue-700 transition-all flex items-center gap-2">
|
class="px-6 py-3 bg-blue-600 text-white rounded-2xl font-bold shadow-lg shadow-blue-200 hover:bg-blue-700 transition-all flex items-center gap-2 shrink-0">
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
임대 등록
|
임대 등록
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Rental Cards Grid -->
|
<!-- Rental Cards Grid -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"
|
||||||
<template x-for="rental in rentals" :key="rental.id">
|
x-show="!loading && filteredRentals.length > 0">
|
||||||
|
<template x-for="rental in filteredRentals" :key="rental.id">
|
||||||
<div class="bg-white rounded-[2rem] p-6 shadow-sm border border-slate-100 hover:shadow-xl transition-all relative overflow-hidden flex flex-col h-full"
|
<div class="bg-white rounded-[2rem] p-6 shadow-sm border border-slate-100 hover:shadow-xl transition-all relative overflow-hidden flex flex-col h-full"
|
||||||
:class="rental.status === 'returned' ? 'opacity-75' : ''">
|
:class="rental.status === 'returned' ? 'opacity-75' : ''">
|
||||||
|
|
||||||
|
|
@ -149,6 +164,19 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div x-show="loading" class="p-20 flex justify-center">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
||||||
|
</div>
|
||||||
|
<div x-show="!loading && filteredRentals.length === 0"
|
||||||
|
class="p-20 text-center bg-white rounded-3xl border border-dashed border-slate-300">
|
||||||
|
<template x-if="searchQuery">
|
||||||
|
<p class="text-slate-400 font-medium italic">검색 결과와 일치하는 임대 기록이 없습니다.</p>
|
||||||
|
</template>
|
||||||
|
<template x-if="!searchQuery">
|
||||||
|
<p class="text-slate-400 font-medium italic">등록된 임대 기록이 없습니다.</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Add Rental Modal -->
|
<!-- Add Rental Modal -->
|
||||||
|
|
@ -238,6 +266,8 @@
|
||||||
return {
|
return {
|
||||||
rentals: [],
|
rentals: [],
|
||||||
users: [],
|
users: [],
|
||||||
|
loading: true,
|
||||||
|
searchQuery: '',
|
||||||
showAddModal: false,
|
showAddModal: false,
|
||||||
formData: {
|
formData: {
|
||||||
user_id: '',
|
user_id: '',
|
||||||
|
|
@ -253,11 +283,24 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchRentals() {
|
fetchRentals() {
|
||||||
|
const scrollPos = window.scrollY;
|
||||||
|
this.loading = true;
|
||||||
fetch('api.php?action=get_general_rentals').then(res => res.json()).then(data => {
|
fetch('api.php?action=get_general_rentals').then(res => res.json()).then(data => {
|
||||||
this.rentals = data;
|
this.rentals = data;
|
||||||
|
this.loading = false;
|
||||||
|
this.$nextTick(() => window.scrollTo(0, scrollPos));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get filteredRentals() {
|
||||||
|
if (!this.searchQuery) return this.rentals;
|
||||||
|
const q = this.searchQuery.toLowerCase();
|
||||||
|
return this.rentals.filter(r =>
|
||||||
|
(r.user_name && r.user_name.toLowerCase().includes(q)) ||
|
||||||
|
(r.items && r.items.toLowerCase().includes(q))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
fetchUsers() {
|
fetchUsers() {
|
||||||
fetch('api.php?action=get_users').then(res => res.json()).then(data => {
|
fetch('api.php?action=get_users').then(res => res.json()).then(data => {
|
||||||
this.users = data;
|
this.users = data;
|
||||||
|
|
|
||||||
48
mfp.php
48
mfp.php
|
|
@ -27,15 +27,29 @@
|
||||||
<?php include 'nav.php'; ?>
|
<?php include 'nav.php'; ?>
|
||||||
|
|
||||||
<main class="max-w-[1600px] mx-auto p-8 pt-10">
|
<main class="max-w-[1600px] mx-auto p-8 pt-10">
|
||||||
<header class="mb-10 flex justify-between items-center">
|
<header class="mb-10 flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-3xl font-extrabold text-slate-900 tracking-tight">복합기 계정 관리</h2>
|
<h2 class="text-3xl font-extrabold text-slate-900 tracking-tight">복합기 계정 관리</h2>
|
||||||
<p class="text-slate-500 mt-1">임시/공용 복합기 계정 및 비밀번호 관리</p>
|
<p class="text-slate-500 mt-1">임시/공용 복합기 계정 및 비밀번호 관리</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="relative group">
|
||||||
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg class="h-4 w-4 text-slate-400 group-focus-within:text-amber-500 transition-colors"
|
||||||
|
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" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input type="text" x-model="searchQuery"
|
||||||
|
class="block w-64 pl-10 pr-3 py-2.5 border border-slate-200 rounded-xl leading-5 bg-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-amber-500 transition-all text-sm shadow-sm"
|
||||||
|
placeholder="계정 ID, 사용 목적 검색...">
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
@click="showModal = true; isEdit = false; formData = { id: '', account_id: '', account_pw: '', purpose: '' }"
|
@click="showModal = true; isEdit = false; formData = { id: '', account_id: '', account_pw: '', purpose: '' }"
|
||||||
class="bg-amber-600 text-white px-5 py-2.5 rounded-xl font-bold shadow-lg shadow-amber-200 hover:bg-amber-700 transition-all">신규
|
class="bg-amber-600 text-white px-5 py-2.5 rounded-xl font-bold shadow-lg shadow-amber-200 hover:bg-amber-700 transition-all shrink-0">신규
|
||||||
계정 등록</button>
|
계정 등록</button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Bulk Actions (Fixed Floating Bar) -->
|
<!-- Bulk Actions (Fixed Floating Bar) -->
|
||||||
|
|
@ -73,7 +87,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- MFP List Table -->
|
<!-- MFP List Table -->
|
||||||
<div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden" x-show="mfpList.length > 0">
|
<div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden"
|
||||||
|
x-show="!loading && filteredMfp.length > 0">
|
||||||
<table class="min-w-full divide-y divide-slate-100">
|
<table class="min-w-full divide-y divide-slate-100">
|
||||||
<thead class="bg-slate-50/50">
|
<thead class="bg-slate-50/50">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -92,7 +107,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-slate-50">
|
<tbody class="divide-y divide-slate-50">
|
||||||
<template x-for="mfp in mfpList" :key="mfp.id">
|
<template x-for="mfp in filteredMfp" :key="mfp.id">
|
||||||
<tr class="hover:bg-slate-50/80 transition-colors group">
|
<tr class="hover:bg-slate-50/80 transition-colors group">
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
<input type="checkbox" :value="mfp.id" x-model="selectedIds"
|
<input type="checkbox" :value="mfp.id" x-model="selectedIds"
|
||||||
|
|
@ -127,6 +142,19 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div x-show="loading" class="p-20 flex justify-center">
|
||||||
|
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-amber-600"></div>
|
||||||
|
</div>
|
||||||
|
<div x-show="!loading && filteredMfp.length === 0"
|
||||||
|
class="p-20 text-center bg-white rounded-3xl border border-dashed border-slate-300">
|
||||||
|
<template x-if="searchQuery">
|
||||||
|
<p class="text-slate-400 font-medium italic">검색 결과와 일치하는 계정이 없습니다.</p>
|
||||||
|
</template>
|
||||||
|
<template x-if="!searchQuery">
|
||||||
|
<p class="text-slate-400 font-medium italic">등록된 복합기 계정이 없습니다.</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- MFP Modal -->
|
<!-- MFP Modal -->
|
||||||
|
|
@ -161,17 +189,29 @@
|
||||||
showModal: false,
|
showModal: false,
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
|
loading: true,
|
||||||
|
searchQuery: '',
|
||||||
bulkStatus: '',
|
bulkStatus: '',
|
||||||
bulkPurpose: '',
|
bulkPurpose: '',
|
||||||
formData: { id: '', account_id: '', account_pw: '', purpose: '' },
|
formData: { id: '', account_id: '', account_pw: '', purpose: '' },
|
||||||
init() { this.fetchMfp(); },
|
init() { this.fetchMfp(); },
|
||||||
fetchMfp() {
|
fetchMfp() {
|
||||||
const scrollPos = window.scrollY;
|
const scrollPos = window.scrollY;
|
||||||
|
this.loading = true;
|
||||||
fetch('api.php?action=get_mfp').then(res => res.json()).then(data => {
|
fetch('api.php?action=get_mfp').then(res => res.json()).then(data => {
|
||||||
this.mfpList = data;
|
this.mfpList = data;
|
||||||
|
this.loading = false;
|
||||||
this.$nextTick(() => window.scrollTo(0, scrollPos));
|
this.$nextTick(() => window.scrollTo(0, scrollPos));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
get filteredMfp() {
|
||||||
|
if (!this.searchQuery) return this.mfpList;
|
||||||
|
const q = this.searchQuery.toLowerCase();
|
||||||
|
return this.mfpList.filter(m =>
|
||||||
|
(m.account_id && m.account_id.toLowerCase().includes(q)) ||
|
||||||
|
(m.purpose && m.purpose.toLowerCase().includes(q))
|
||||||
|
);
|
||||||
|
},
|
||||||
toggleSelectAll(checked) {
|
toggleSelectAll(checked) {
|
||||||
this.selectedIds = checked ? this.mfpList.map(m => m.id) : [];
|
this.selectedIds = checked ? this.mfpList.map(m => m.id) : [];
|
||||||
},
|
},
|
||||||
|
|
|
||||||
64
models.php
64
models.php
|
|
@ -36,14 +36,42 @@
|
||||||
<h2 class="text-3xl font-extrabold text-slate-900 tracking-tight">DB 마스터 관리</h2>
|
<h2 class="text-3xl font-extrabold text-slate-900 tracking-tight">DB 마스터 관리</h2>
|
||||||
<p class="text-slate-500 mt-1">시스템 운영을 위한 기초 자산 모델 및 조직 정보를 관리합니다.</p>
|
<p class="text-slate-500 mt-1">시스템 운영을 위한 기초 자산 모델 및 조직 정보를 관리합니다.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-3">
|
<div class="flex items-center gap-3">
|
||||||
|
<!-- Search Bar for Models -->
|
||||||
|
<div x-show="currentTab === 'models'" class="relative group">
|
||||||
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg class="h-4 w-4 text-slate-400 group-focus-within:text-blue-500 transition-colors"
|
||||||
|
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" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input type="text" x-model="searchQuery"
|
||||||
|
class="block w-64 pl-10 pr-3 py-2.5 border border-slate-200 rounded-xl leading-5 bg-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all text-sm shadow-sm"
|
||||||
|
placeholder="모델명, 제조사, CPU 검색...">
|
||||||
|
</div>
|
||||||
|
<!-- Search Bar for Departments -->
|
||||||
|
<div x-show="currentTab === 'depts'" class="relative group">
|
||||||
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||||
|
<svg class="h-4 w-4 text-slate-400 group-focus-within:text-blue-500 transition-colors"
|
||||||
|
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" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input type="text" x-model="deptSearchQuery"
|
||||||
|
class="block w-64 pl-10 pr-3 py-2.5 border border-slate-200 rounded-xl leading-5 bg-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all text-sm shadow-sm"
|
||||||
|
placeholder="부서명 검색...">
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
<button x-show="currentTab === 'models'" @click="openAddModal()"
|
<button x-show="currentTab === 'models'" @click="openAddModal()"
|
||||||
class="bg-blue-600 text-white px-5 py-2.5 rounded-xl font-bold shadow-lg shadow-blue-200 hover:bg-blue-700 transition-all">신규
|
class="bg-blue-600 text-white px-5 py-2.5 rounded-xl font-bold shadow-lg shadow-blue-200 hover:bg-blue-700 transition-all shrink-0">신규
|
||||||
모델 등록</button>
|
모델 등록</button>
|
||||||
<button x-show="currentTab === 'depts'" @click="openAddDeptModal()"
|
<button x-show="currentTab === 'depts'" @click="openAddDeptModal()"
|
||||||
class="bg-blue-600 text-white px-5 py-2.5 rounded-xl font-bold shadow-lg shadow-blue-200 hover:bg-blue-700 transition-all">신규
|
class="bg-blue-600 text-white px-5 py-2.5 rounded-xl font-bold shadow-lg shadow-blue-200 hover:bg-blue-700 transition-all shrink-0">신규
|
||||||
부서 생성</button>
|
부서 생성</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
|
|
@ -124,7 +152,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-slate-50">
|
<tbody class="divide-y divide-slate-50">
|
||||||
<template x-for="model in models" :key="model.id">
|
<template x-for="model in filteredModels" :key="model.id">
|
||||||
<tr class="hover:bg-slate-50/80 transition-colors group">
|
<tr class="hover:bg-slate-50/80 transition-colors group">
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
<input type="checkbox" :value="model.id" x-model="selectedIds"
|
<input type="checkbox" :value="model.id" x-model="selectedIds"
|
||||||
|
|
@ -172,6 +200,9 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div x-show="filteredModels.length === 0" class="p-20 text-center bg-white rounded-3xl border border-dashed border-slate-300 mt-6">
|
||||||
|
<p class="text-slate-400 font-medium italic">일치하는 모델 정보가 없습니다.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tab 2: Departments -->
|
<!-- Tab 2: Departments -->
|
||||||
|
|
@ -189,7 +220,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-slate-50">
|
<tbody class="divide-y divide-slate-50">
|
||||||
<template x-for="dept in depts" :key="dept.id">
|
<template x-for="dept in filteredDepts" :key="dept.id">
|
||||||
<tr class="hover:bg-slate-50/80 transition-colors group">
|
<tr class="hover:bg-slate-50/80 transition-colors group">
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
|
@ -219,6 +250,9 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div x-show="filteredDepts.length === 0" class="p-20 text-center bg-white rounded-3xl border border-dashed border-slate-300 mt-6">
|
||||||
|
<p class="text-slate-400 font-medium italic">일치하는 부서 정보가 없습니다.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
@ -445,6 +479,7 @@
|
||||||
showModal: false,
|
showModal: false,
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
|
searchQuery: '',
|
||||||
bulkManufacturer: '',
|
bulkManufacturer: '',
|
||||||
bulkProductName: '',
|
bulkProductName: '',
|
||||||
formData: {
|
formData: {
|
||||||
|
|
@ -455,15 +490,34 @@
|
||||||
asset_status: '', fixed_ip: '', assigned_user: '', purchase_date: '',
|
asset_status: '', fixed_ip: '', assigned_user: '', purchase_date: '',
|
||||||
power_rating: '', vendor: '', options: '', remarks: '', specs: ''
|
power_rating: '', vendor: '', options: '', remarks: '', specs: ''
|
||||||
},
|
},
|
||||||
|
get filteredModels() {
|
||||||
|
if (!this.searchQuery) return this.models;
|
||||||
|
const q = this.searchQuery.toLowerCase();
|
||||||
|
return this.models.filter(m =>
|
||||||
|
(m.model_name && m.model_name.toLowerCase().includes(q)) ||
|
||||||
|
(m.manufacturer && m.manufacturer.toLowerCase().includes(q)) ||
|
||||||
|
(m.cpu && m.cpu.toLowerCase().includes(q)) ||
|
||||||
|
(m.product_name && m.product_name.toLowerCase().includes(q))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
// Dept Data
|
// Dept Data
|
||||||
depts: [],
|
depts: [],
|
||||||
|
deptSearchQuery: '',
|
||||||
showDeptModal: false,
|
showDeptModal: false,
|
||||||
showMembersModal: false,
|
showMembersModal: false,
|
||||||
isDeptEdit: false,
|
isDeptEdit: false,
|
||||||
selectedDept: null,
|
selectedDept: null,
|
||||||
members: [],
|
members: [],
|
||||||
deptFormData: { id: '', name: '', parent_id: '', level: 1 },
|
deptFormData: { id: '', name: '', parent_id: '', level: 1 },
|
||||||
|
get filteredDepts() {
|
||||||
|
if (!this.deptSearchQuery) return this.depts;
|
||||||
|
const q = this.deptSearchQuery.toLowerCase();
|
||||||
|
return this.depts.filter(d =>
|
||||||
|
(d.name && d.name.toLowerCase().includes(q)) ||
|
||||||
|
(d.path && d.path.toLowerCase().includes(q))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.fetchModels();
|
this.fetchModels();
|
||||||
|
|
|
||||||
31
reset_admin.php
Normal file
31
reset_admin.php
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
|
||||||
|
// administrators 테이블이 없으면 생성
|
||||||
|
$db->exec("CREATE TABLE IF NOT EXISTS administrators (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
login_id TEXT UNIQUE NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
admin_name TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)");
|
||||||
|
|
||||||
|
$login_id = 'admin';
|
||||||
|
$password = 'admin1234';
|
||||||
|
$admin_name = 'Admin';
|
||||||
|
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
$stmt = $db->prepare("SELECT id FROM administrators WHERE login_id = ?");
|
||||||
|
$stmt->execute([$login_id]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$stmt = $db->prepare("UPDATE administrators SET password_hash = ?, admin_name = ? WHERE login_id = ?");
|
||||||
|
$stmt->execute([$hash, $admin_name, $login_id]);
|
||||||
|
echo "기존 admin 계정의 비밀번호를 admin1234로 초기화했습니다.<br>";
|
||||||
|
} else {
|
||||||
|
$stmt = $db->prepare("INSERT INTO administrators (login_id, password_hash, admin_name) VALUES (?, ?, ?)");
|
||||||
|
$stmt->execute([$login_id, $hash, $admin_name]);
|
||||||
|
echo "신규 admin 계정(PW: admin1234)을 생성했습니다.<br>";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<br><a href='login.php'>로그인 페이지로 이동</a>";
|
||||||
|
?>
|
||||||
Loading…
Reference in a new issue