쿼타
This commit is contained in:
parent
9584156f9a
commit
e1372061e1
6 changed files with 219 additions and 107 deletions
81
api.php
81
api.php
|
|
@ -60,34 +60,56 @@ try {
|
||||||
|
|
||||||
case 'add_user':
|
case 'add_user':
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$status = $data['status'] ?? 'active';
|
||||||
|
$department_id = $data['department_id'];
|
||||||
|
|
||||||
|
// Auto-relocate based on status
|
||||||
|
if ($status === 'on_leave' || $status === 'retired') {
|
||||||
|
$dept_name = ($status === 'on_leave') ? '휴직' : '퇴사';
|
||||||
|
$target_dept = $db->query("SELECT id FROM departments WHERE name = '$dept_name'")->fetchColumn();
|
||||||
|
if ($target_dept)
|
||||||
|
$department_id = $target_dept;
|
||||||
|
}
|
||||||
|
|
||||||
$stmt = $db->prepare("INSERT INTO users (emp_id, name, department_id, position, email, phone, mobile, accounting_type, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
$stmt = $db->prepare("INSERT INTO users (emp_id, name, department_id, position, email, phone, mobile, accounting_type, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$data['emp_id'],
|
$data['emp_id'],
|
||||||
$data['name'],
|
$data['name'],
|
||||||
$data['department_id'],
|
$department_id,
|
||||||
$data['position'],
|
$data['position'],
|
||||||
$data['email'],
|
$data['email'],
|
||||||
$data['phone'] ?? '',
|
$data['phone'] ?? '',
|
||||||
$data['mobile'],
|
$data['mobile'],
|
||||||
$data['accounting_type'],
|
$data['accounting_type'],
|
||||||
$data['status'] ?? 'active'
|
$status
|
||||||
]);
|
]);
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'update_user':
|
case 'update_user':
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$status = $data['status'];
|
||||||
|
$department_id = $data['department_id'];
|
||||||
|
|
||||||
|
// Auto-relocate based on status
|
||||||
|
if ($status === 'on_leave' || $status === 'retired') {
|
||||||
|
$dept_name = ($status === 'on_leave') ? '휴직' : '퇴사';
|
||||||
|
$target_dept = $db->query("SELECT id FROM departments WHERE name = '$dept_name'")->fetchColumn();
|
||||||
|
if ($target_dept)
|
||||||
|
$department_id = $target_dept;
|
||||||
|
}
|
||||||
|
|
||||||
$stmt = $db->prepare("UPDATE users SET emp_id = ?, name = ?, department_id = ?, position = ?, email = ?, phone = ?, mobile = ?, accounting_type = ?, status = ? WHERE id = ?");
|
$stmt = $db->prepare("UPDATE users SET emp_id = ?, name = ?, department_id = ?, position = ?, email = ?, phone = ?, mobile = ?, accounting_type = ?, status = ? WHERE id = ?");
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$data['emp_id'],
|
$data['emp_id'],
|
||||||
$data['name'],
|
$data['name'],
|
||||||
$data['department_id'],
|
$department_id,
|
||||||
$data['position'],
|
$data['position'],
|
||||||
$data['email'],
|
$data['email'],
|
||||||
$data['phone'] ?? '',
|
$data['phone'] ?? '',
|
||||||
$data['mobile'],
|
$data['mobile'],
|
||||||
$data['accounting_type'],
|
$data['accounting_type'],
|
||||||
$data['status'],
|
$status,
|
||||||
$data['id']
|
$data['id']
|
||||||
]);
|
]);
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
|
|
@ -352,8 +374,49 @@ try {
|
||||||
|
|
||||||
case 'update_dept':
|
case 'update_dept':
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$new_name = $data['name'];
|
||||||
|
$parent_id = $data['parent_id'] ?: null;
|
||||||
|
$dept_id = $data['id'];
|
||||||
|
$merge_target_id = $data['merge_target_id'] ?? null;
|
||||||
|
|
||||||
|
if ($merge_target_id) {
|
||||||
|
// Scenario: Merge members to an existing department with the same name
|
||||||
|
$db->prepare("UPDATE users SET department_id = ? WHERE department_id = ?")->execute([$merge_target_id, $dept_id]);
|
||||||
|
// Recursively move children? For now, we delete the source dept after merging members
|
||||||
|
$db->prepare("DELETE FROM departments WHERE id = ?")->execute([$dept_id]);
|
||||||
|
echo json_encode(['success' => true, 'merged' => true]);
|
||||||
|
} else {
|
||||||
|
// Check if a department with the same name exists under the same parent (excluding itself)
|
||||||
|
$check_stmt = $db->prepare("SELECT id FROM departments WHERE name = ? AND (parent_id <=> ?) AND id != ?");
|
||||||
|
$check_stmt->execute([$new_name, $parent_id, $dept_id]);
|
||||||
|
$existing = $check_stmt->fetch();
|
||||||
|
|
||||||
|
if ($existing) {
|
||||||
|
echo json_encode(['success' => false, 'conflict' => true, 'existing_id' => $existing['id']]);
|
||||||
|
} else {
|
||||||
$stmt = $db->prepare("UPDATE departments SET name = ?, parent_id = ? WHERE id = ?");
|
$stmt = $db->prepare("UPDATE departments SET name = ?, parent_id = ? WHERE id = ?");
|
||||||
$stmt->execute([$data['name'], $data['parent_id'] ?: null, $data['id']]);
|
$stmt->execute([$new_name, $parent_id, $dept_id]);
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete_dept':
|
||||||
|
$dept_id = $_GET['id'];
|
||||||
|
// 1. Find the '미소속' department ID
|
||||||
|
$miso_id = $db->query("SELECT id FROM departments WHERE name = '미소속'")->fetchColumn();
|
||||||
|
if (!$miso_id) {
|
||||||
|
// Fallback creation if not exists
|
||||||
|
$db->exec("INSERT INTO departments (name, level) VALUES ('미소속', 0)");
|
||||||
|
$miso_id = $db->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Move members to '미소속'
|
||||||
|
$db->prepare("UPDATE users SET department_id = ? WHERE department_id = ?")->execute([$miso_id, $dept_id]);
|
||||||
|
|
||||||
|
// 3. Delete the department
|
||||||
|
$db->prepare("DELETE FROM departments WHERE id = ?")->execute([$dept_id]);
|
||||||
|
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -500,6 +563,14 @@ try {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enhanced logic: if status is changed to leave/retired, override dept
|
||||||
|
if ($newStatus === 'on_leave' || $newStatus === 'retired') {
|
||||||
|
$dept_name = ($newStatus === 'on_leave') ? '휴직' : '퇴사';
|
||||||
|
$target_dept = $db->query("SELECT id FROM departments WHERE name = '$dept_name'")->fetchColumn();
|
||||||
|
if ($target_dept)
|
||||||
|
$newDeptId = $target_dept;
|
||||||
|
}
|
||||||
|
|
||||||
$sql = "UPDATE users SET ";
|
$sql = "UPDATE users SET ";
|
||||||
$params = [];
|
$params = [];
|
||||||
$updates = [];
|
$updates = [];
|
||||||
|
|
|
||||||
32
cards.php
32
cards.php
|
|
@ -37,35 +37,39 @@
|
||||||
카드 등록</button>
|
카드 등록</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Bulk Actions -->
|
<!-- Bulk Actions (Fixed Floating Bar) -->
|
||||||
<div x-show="selectedIds.length > 0" x-transition x-cloak
|
<div x-show="selectedIds.length > 0" x-transition x-cloak
|
||||||
class="mb-6 bg-slate-900 text-white px-6 py-3 rounded-2xl flex items-center justify-between shadow-xl">
|
class="fixed bottom-10 left-10 z-[200] bg-slate-900 text-white px-6 py-4 rounded-3xl flex items-center gap-6 shadow-2xl border border-white/10 animate-in fade-in slide-in-from-left-4 duration-300">
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-emerald-400 font-black" x-text="selectedIds.length"></span>
|
<div class="w-10 h-10 bg-emerald-600 rounded-full flex items-center justify-center font-black text-sm shadow-lg shadow-emerald-500/30"
|
||||||
<span class="text-xs font-bold text-slate-400">개 선택됨</span>
|
x-text="selectedIds.length"></div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-[10px] font-black text-slate-400 uppercase tracking-tighter">Selected</span>
|
||||||
|
<span class="text-xs font-bold">출입증 선택됨</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-4 w-px bg-slate-700"></div>
|
</div>
|
||||||
|
<div class="h-8 w-px bg-white/10 mx-2"></div>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="text-xs font-bold text-slate-400 uppercase">일괄 변경:</span>
|
|
||||||
<select x-model="bulkStatus"
|
<select x-model="bulkStatus"
|
||||||
class="bg-slate-800 border-none rounded-lg text-xs font-bold px-3 py-1.5 focus:ring-1 focus:ring-emerald-500">
|
class="bg-slate-800 border-none rounded-xl text-xs font-bold px-4 py-2 focus:ring-2 focus:ring-emerald-500 appearance-none shadow-inner min-w-[150px]">
|
||||||
<option value="">상태 변경...</option>
|
<option value="">상태 변경...</option>
|
||||||
<option value="available">가용됨(available)</option>
|
<option value="available">가용됨(available)</option>
|
||||||
<option value="in_use">사용중(in_use)</option>
|
<option value="in_use">사용중(in_use)</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="text" x-model="bulkCardType" placeholder="카드 종류 일괄 입력..."
|
<input type="text" x-model="bulkCardType" placeholder="카드 종류 일괄 입력..."
|
||||||
class="bg-slate-800 border-none rounded-lg text-xs font-bold px-3 py-1.5 focus:ring-1 focus:ring-emerald-500 w-48">
|
class="bg-slate-800 border-none rounded-xl text-xs font-bold px-4 py-2 focus:ring-2 focus:ring-emerald-500 shadow-inner w-48">
|
||||||
<button @click="applyBulkUpdate"
|
<button @click="applyBulkUpdate"
|
||||||
class="bg-emerald-500 hover:bg-emerald-600 px-4 py-1.5 rounded-lg text-xs font-black transition-all">적용</button>
|
class="bg-emerald-500 hover:bg-emerald-600 px-6 py-2 rounded-xl text-xs font-black transition-all shadow-lg shadow-emerald-500/20 active:scale-95">일괄
|
||||||
</div>
|
적용</button>
|
||||||
</div>
|
<button @click="selectedIds = []"
|
||||||
<button @click="selectedIds = []" class="text-slate-400 hover:text-white transition-colors">
|
class="ml-2 w-8 h-8 flex items-center justify-center rounded-full hover:bg-white/10 text-slate-400 hover:text-white transition-all">
|
||||||
<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="M6 18L18 6M6 6l12 12" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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="cards.length > 0">
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,13 @@
|
||||||
<div x-show="showModal" x-cloak class="fixed inset-0 z-[110] flex items-center justify-center p-4">
|
<div x-show="showModal" x-cloak class="fixed inset-0 z-[110] flex items-center justify-center p-4">
|
||||||
<div class="fixed inset-0 modal-bg" @click="showModal = false"></div>
|
<div class="fixed inset-0 modal-bg" @click="showModal = false"></div>
|
||||||
<div class="bg-white rounded-3xl p-8 max-w-md w-full relative z-[111] shadow-2xl">
|
<div class="bg-white rounded-3xl p-8 max-w-md w-full relative z-[111] shadow-2xl">
|
||||||
<h3 class="text-2xl font-black mb-6" x-text="isEdit ? '부서 정보 수정' : '신규 부서 생성'"></h3>
|
<div class="flex justify-between items-center mb-6">
|
||||||
|
<h3 class="text-2xl font-black text-slate-900" x-text="isEdit ? '부서 정보 수정' : '신규 부서 생성'"></h3>
|
||||||
|
<button x-show="isEdit" type="button" @click="deleteDept()"
|
||||||
|
class="text-xs font-bold text-rose-500 hover:text-rose-700 bg-rose-50 px-3 py-1.5 rounded-lg border border-rose-100 transition-colors">
|
||||||
|
부서 제거
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<form @submit.prevent="submitDept" class="space-y-4">
|
<form @submit.prevent="submitDept" class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-bold text-slate-500 uppercase mb-2">부서명</label>
|
<label class="block text-xs font-bold text-slate-500 uppercase mb-2">부서명</label>
|
||||||
|
|
@ -205,26 +211,48 @@
|
||||||
this.showModal = true;
|
this.showModal = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
submitDept() {
|
submitDept(mergeTargetId = null) {
|
||||||
const action = this.isEdit ? 'update_dept' : 'add_dept';
|
const action = this.isEdit ? 'update_dept' : 'add_dept';
|
||||||
// Calculate level based on parent
|
if (!mergeTargetId && this.formData.parent_id) {
|
||||||
if (this.formData.parent_id) {
|
|
||||||
const parent = this.depts.find(d => d.id == this.formData.parent_id);
|
const parent = this.depts.find(d => d.id == this.formData.parent_id);
|
||||||
this.formData.level = this.getDeptLevel(parent.path) + 1;
|
this.formData.level = (parent ? this.getDeptLevel(parent.path) : 0) + 1;
|
||||||
} else {
|
} else if (!mergeTargetId) {
|
||||||
this.formData.level = 0;
|
this.formData.level = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const payload = { ...this.formData };
|
||||||
|
if (mergeTargetId) payload.merge_target_id = mergeTargetId;
|
||||||
|
|
||||||
fetch(`api.php?action=${action}`, {
|
fetch(`api.php?action=${action}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(this.formData)
|
body: JSON.stringify(payload)
|
||||||
}).then(res => res.json()).then(data => {
|
}).then(res => res.json()).then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
this.showModal = false;
|
||||||
|
this.fetchDepts();
|
||||||
|
if (data.merged) {
|
||||||
|
alert('부서원이 기존 부서로 이동되었으며, 해당 부서는 삭제되었습니다.');
|
||||||
|
}
|
||||||
|
} else if (data.conflict) {
|
||||||
|
if (confirm(`동일한 위치에 '${this.formData.name}' 부서가 이미 존재합니다.\n부서 정보를 통합하여 부서원을 이동시키겠습니까?`)) {
|
||||||
|
this.submitDept(data.existing_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteDept() {
|
||||||
|
if (confirm(`정말로 '${this.formData.name}' 부서를 제거하시겠습니까?\n이 명령을 수행하면 소속된 부서원들은 자동으로 '[한국경제인협회] - 미소속' 부서로 이동됩니다.\n이 작업은 되돌릴 수 없습니다.`)) {
|
||||||
|
fetch(`api.php?action=delete_dept&id=${this.formData.id}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.showModal = false;
|
this.showModal = false;
|
||||||
this.fetchDepts();
|
this.fetchDepts();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
viewMembers(dept) {
|
viewMembers(dept) {
|
||||||
|
|
|
||||||
35
laptops.php
35
laptops.php
|
|
@ -52,41 +52,44 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Bulk Actions -->
|
<!-- Bulk Actions (Fixed Floating Bar) -->
|
||||||
<div x-show="selectedIds.length > 0" x-transition x-cloak
|
<div x-show="selectedIds.length > 0" x-transition x-cloak
|
||||||
class="mb-6 bg-slate-900 text-white px-6 py-3 rounded-2xl flex items-center justify-between shadow-xl">
|
class="fixed bottom-10 left-10 z-[200] bg-slate-900 text-white px-6 py-4 rounded-3xl flex items-center gap-6 shadow-2xl border border-white/10 animate-in fade-in slide-in-from-left-4 duration-300">
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-blue-400 font-black" x-text="selectedIds.length"></span>
|
<div class="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center font-black text-sm shadow-lg shadow-blue-500/30"
|
||||||
<span class="text-xs font-bold text-slate-400">개 선택됨</span>
|
x-text="selectedIds.length"></div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-[10px] font-black text-slate-400 uppercase tracking-tighter">Selected</span>
|
||||||
|
<span class="text-xs font-bold">자산 선택됨</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-4 w-px bg-slate-700"></div>
|
</div>
|
||||||
|
<div class="h-8 w-px bg-white/10 mx-2"></div>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="text-xs font-bold text-slate-400 uppercase">일괄 변경:</span>
|
|
||||||
<select x-model="bulkStatus"
|
<select x-model="bulkStatus"
|
||||||
class="bg-slate-800 border-none rounded-lg text-xs font-bold px-3 py-1.5 focus:ring-1 focus:ring-blue-500">
|
class="bg-slate-800 border-none rounded-xl text-xs font-bold px-4 py-2 focus:ring-2 focus:ring-blue-500 appearance-none shadow-inner min-w-[150px]">
|
||||||
<option value="">상태 변경...</option>
|
<option value="">상태 변경...</option>
|
||||||
<option value="assigned">지급됨(assigned)</option>
|
<option value="assigned">지급됨(assigned)</option>
|
||||||
<option value="stock">재고(stock)</option>
|
<option value="stock">재고(stock)</option>
|
||||||
</select>
|
</select>
|
||||||
<select x-model="bulkModelId"
|
<select x-model="bulkModelId"
|
||||||
class="bg-slate-800 border-none rounded-lg text-xs font-bold px-3 py-1.5 focus:ring-1 focus:ring-blue-500">
|
class="bg-slate-800 border-none rounded-xl text-xs font-bold px-4 py-2 focus:ring-2 focus:ring-blue-500 appearance-none shadow-inner min-w-[200px]">
|
||||||
<option value="">모델 변경...</option>
|
<option value="">모델 변경...</option>
|
||||||
<template x-for="model in models" :key="model.id">
|
<template x-for="model in models" :key="model.id">
|
||||||
<option :value="model.id" x-text="'[' + model.manufacturer + '] ' + model.model_name">
|
<option :value="model.id" x-text="'[' + model.manufacturer + '] ' + model.model_name"></option>
|
||||||
</option>
|
|
||||||
</template>
|
</template>
|
||||||
</select>
|
</select>
|
||||||
<button @click="applyBulkUpdate"
|
<button @click="applyBulkUpdate"
|
||||||
class="bg-blue-500 hover:bg-blue-600 px-4 py-1.5 rounded-lg text-xs font-black transition-all">적용</button>
|
class="bg-blue-600 hover:bg-blue-700 px-6 py-2 rounded-xl text-xs font-black transition-all shadow-lg shadow-blue-500/20 active:scale-95">일괄
|
||||||
</div>
|
적용</button>
|
||||||
</div>
|
<button @click="selectedIds = []"
|
||||||
<button @click="selectedIds = []" class="text-slate-400 hover:text-white transition-colors">
|
class="ml-2 w-8 h-8 flex items-center justify-center rounded-full hover:bg-white/10 text-slate-400 hover:text-white transition-all">
|
||||||
<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="M6 18L18 6M6 6l12 12" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Laptop List Table -->
|
<!-- Laptop List Table -->
|
||||||
<div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden"
|
<div class="bg-white rounded-3xl shadow-sm border border-slate-200 overflow-hidden"
|
||||||
|
|
|
||||||
32
mfp.php
32
mfp.php
|
|
@ -37,35 +37,39 @@
|
||||||
계정 등록</button>
|
계정 등록</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Bulk Actions -->
|
<!-- Bulk Actions (Fixed Floating Bar) -->
|
||||||
<div x-show="selectedIds.length > 0" x-transition x-cloak
|
<div x-show="selectedIds.length > 0" x-transition x-cloak
|
||||||
class="mb-6 bg-slate-900 text-white px-6 py-3 rounded-2xl flex items-center justify-between shadow-xl">
|
class="fixed bottom-10 left-10 z-[200] bg-slate-900 text-white px-6 py-4 rounded-3xl flex items-center gap-6 shadow-2xl border border-white/10 animate-in fade-in slide-in-from-left-4 duration-300">
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-amber-400 font-black" x-text="selectedIds.length"></span>
|
<div class="w-10 h-10 bg-amber-600 rounded-full flex items-center justify-center font-black text-sm shadow-lg shadow-amber-500/30"
|
||||||
<span class="text-xs font-bold text-slate-400">개 선택됨</span>
|
x-text="selectedIds.length"></div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-[10px] font-black text-slate-400 uppercase tracking-tighter">Selected</span>
|
||||||
|
<span class="text-xs font-bold">계정 선택됨</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-4 w-px bg-slate-700"></div>
|
</div>
|
||||||
|
<div class="h-8 w-px bg-white/10 mx-2"></div>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="text-xs font-bold text-slate-400 uppercase">일괄 변경:</span>
|
|
||||||
<select x-model="bulkStatus"
|
<select x-model="bulkStatus"
|
||||||
class="bg-slate-800 border-none rounded-lg text-xs font-bold px-3 py-1.5 focus:ring-1 focus:ring-amber-500">
|
class="bg-slate-800 border-none rounded-xl text-xs font-bold px-4 py-2 focus:ring-2 focus:ring-amber-500 appearance-none shadow-inner min-w-[150px]">
|
||||||
<option value="">상태 변경...</option>
|
<option value="">상태 변경...</option>
|
||||||
<option value="active">사용중(active)</option>
|
<option value="active">사용중(active)</option>
|
||||||
<option value="inactive">비활성(inactive)</option>
|
<option value="inactive">비활성(inactive)</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="text" x-model="bulkPurpose" placeholder="사용 목적 일괄 입력..."
|
<input type="text" x-model="bulkPurpose" placeholder="사용 목적 일괄 입력..."
|
||||||
class="bg-slate-800 border-none rounded-lg text-xs font-bold px-3 py-1.5 focus:ring-1 focus:ring-amber-500 w-48">
|
class="bg-slate-800 border-none rounded-xl text-xs font-bold px-4 py-2 focus:ring-2 focus:ring-amber-500 shadow-inner w-48">
|
||||||
<button @click="applyBulkUpdate"
|
<button @click="applyBulkUpdate"
|
||||||
class="bg-amber-500 hover:bg-amber-600 px-4 py-1.5 rounded-lg text-xs font-black transition-all">적용</button>
|
class="bg-amber-500 hover:bg-amber-600 px-6 py-2 rounded-xl text-xs font-black transition-all shadow-lg shadow-amber-500/20 active:scale-95">일괄
|
||||||
</div>
|
적용</button>
|
||||||
</div>
|
<button @click="selectedIds = []"
|
||||||
<button @click="selectedIds = []" class="text-slate-400 hover:text-white transition-colors">
|
class="ml-2 w-8 h-8 flex items-center justify-center rounded-full hover:bg-white/10 text-slate-400 hover:text-white transition-all">
|
||||||
<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="M6 18L18 6M6 6l12 12" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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="mfpList.length > 0">
|
||||||
|
|
|
||||||
28
users.php
28
users.php
|
|
@ -98,36 +98,37 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bulk Command Center (appears when checked) -->
|
<!-- Bulk Command Center (Fixed Floating Bar) -->
|
||||||
<div x-show="selectedIds.length > 0" x-transition x-cloak
|
<div x-show="selectedIds.length > 0" x-transition x-cloak
|
||||||
class="bg-slate-900 text-white px-6 py-3 rounded-2xl flex items-center gap-6 shadow-xl animate-in fade-in slide-in-from-top-4 duration-300">
|
class="fixed bottom-10 left-10 z-[200] bg-slate-900 text-white px-6 py-4 rounded-3xl flex items-center gap-6 shadow-2xl border border-white/10 animate-in fade-in slide-in-from-left-4 duration-300">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-blue-400 font-black" x-text="selectedIds.length"></span>
|
<div class="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center font-black text-sm shadow-lg shadow-blue-500/30" x-text="selectedIds.length"></div>
|
||||||
<span class="text-xs font-bold text-slate-400">명 선택됨</span>
|
<div class="flex flex-col">
|
||||||
|
<span class="text-[10px] font-black text-slate-400 uppercase tracking-tighter">Selected</span>
|
||||||
|
<span class="text-xs font-bold">인원 선택됨</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-4 w-px bg-slate-700"></div>
|
</div>
|
||||||
|
<div class="h-8 w-px bg-white/10 mx-2"></div>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="text-xs font-bold text-slate-400 uppercase">일괄 변경:</span>
|
|
||||||
<select x-model="bulkStatus"
|
<select x-model="bulkStatus"
|
||||||
class="bg-slate-800 border-none rounded-lg text-xs font-bold px-3 py-1.5 focus:ring-1 focus:ring-blue-500">
|
class="bg-slate-800 border-none rounded-xl text-xs font-bold px-4 py-2 focus:ring-2 focus:ring-blue-500 appearance-none shadow-inner min-w-[120px]">
|
||||||
<option value="">상태 변경...</option>
|
<option value="">상태 변경...</option>
|
||||||
<option value="active">재직</option>
|
<option value="active">재직</option>
|
||||||
<option value="retired">퇴사</option>
|
<option value="retired">퇴사</option>
|
||||||
<option value="on_leave">휴직</option>
|
<option value="on_leave">휴직</option>
|
||||||
</select>
|
</select>
|
||||||
<select x-model="bulkDeptId"
|
<select x-model="bulkDeptId"
|
||||||
class="max-w-[150px] bg-slate-800 border-none rounded-lg text-xs font-bold px-3 py-1.5 focus:ring-1 focus:ring-blue-500">
|
class="bg-slate-800 border-none rounded-xl text-xs font-bold px-4 py-2 focus:ring-2 focus:ring-blue-500 appearance-none shadow-inner min-w-[150px]">
|
||||||
<option value="">부서 이동...</option>
|
<option value="">부서 이동...</option>
|
||||||
<template x-for="dept in depts" :key="dept.id">
|
<template x-for="dept in depts" :key="dept.id">
|
||||||
<option :value="dept.id" x-text="dept.name"></option>
|
<option :value="dept.id" x-text="dept.name"></option>
|
||||||
</template>
|
</template>
|
||||||
</select>
|
</select>
|
||||||
<button @click="applyBulkUpdate"
|
<button @click="applyBulkUpdate"
|
||||||
class="bg-blue-500 hover:bg-blue-600 px-4 py-1.5 rounded-lg text-xs font-black transition-all">적용</button>
|
class="bg-blue-600 hover:bg-blue-700 px-6 py-2 rounded-xl text-xs font-black transition-all shadow-lg shadow-blue-500/20 active:scale-95">일괄 적용</button>
|
||||||
<button @click="selectedIds = []" class="text-slate-400 hover:text-white transition-colors">
|
<button @click="selectedIds = []" class="ml-2 w-8 h-8 flex items-center justify-center rounded-full hover:bg-white/10 text-slate-400 hover:text-white transition-all">
|
||||||
<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"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
d="M6 18L18 6M6 6l12 12" />
|
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -198,7 +199,8 @@
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap cursor-pointer" @click="editUser(user)">
|
<td class="px-6 py-4 whitespace-nowrap cursor-pointer" @click="editUser(user)">
|
||||||
<div class="text-sm font-semibold text-slate-700" x-text="user.dept_name || '미소속'">
|
<div class="text-sm font-semibold text-slate-700"
|
||||||
|
x-text="user.dept_name ? user.dept_name.split(' > ').pop() : '미소속'">
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-slate-400" x-text="user.position || '-'"></div>
|
<div class="text-xs text-slate-400" x-text="user.position || '-'"></div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue