+
+
+
+
-
-
-
-
+
res.json()).then(data => {
+ this.allUsers = data;
+ });
+ },
+
get filteredAssets() {
if (!this.searchQuery) return this.assets;
const q = this.searchQuery.toLowerCase();
@@ -273,32 +489,134 @@
openRentalModal(asset) {
this.selectedAsset = asset;
+ this.rentalUserId = asset.rental_user_id || '';
this.rentalName = asset.rental_user_name || '';
+ this.rentalStartDate = asset.rental_start_date || new Date().toISOString().split('T')[0];
+ this.rentalEndDate = asset.rental_end_scheduled || '';
+ this.rentalReason = asset.rental_reason || '';
+ this.rentalRemarks = asset.remarks || '';
+ this.rentalPeripherals = asset.rental_peripherals ? asset.rental_peripherals.split(',') : [];
this.showRentalModal = true;
},
+ updateRentalName() {
+ const user = this.allUsers.find(u => u.id == this.rentalUserId);
+ if (user) {
+ this.rentalName = user.name;
+ } else {
+ this.rentalName = '';
+ }
+ },
+
submitRental() {
+ if (!this.rentalUserId) {
+ window.showAlert('임대할 직원을 선택해주세요.', '알림', 'warning');
+ return;
+ }
fetch('api.php?action=update_rental', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: this.selectedAsset.id,
- rental_user_name: this.rentalName
+ rental_user_id: this.rentalUserId,
+ rental_user_name: this.rentalName,
+ rental_start_date: this.rentalStartDate,
+ rental_end_scheduled: this.rentalEndDate,
+ rental_reason: this.rentalReason,
+ rental_peripherals: this.rentalPeripherals.join(','),
+ remarks: this.rentalRemarks
})
}).then(res => res.json()).then(data => {
if (data.success) {
+ window.showAlert('임대 설정이 완료되었습니다.', '성공', 'success');
this.showRentalModal = false;
this.fetchAssets();
+ } else {
+ window.showAlert(data.error || '임대 적용 중 오류가 발생했습니다.', '오류', 'error');
}
+ }).catch(err => {
+ window.showAlert('서버와의 통신에 실패했습니다.', '통신 오류', 'error');
});
},
+ returnRental(asset) {
+ window.showConfirm(`${asset.rental_user_name}님의 노트북 반납을 처리하시겠습니까?`, () => {
+ fetch('api.php?action=return_rental', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ id: asset.id })
+ }).then(res => res.json()).then(data => {
+ if (data.success) {
+ window.showAlert('반납 처리가 완료되었습니다.', '성공', 'success');
+ this.fetchAssets();
+ }
+ });
+ }, '반납 확인');
+ },
+
+ calculateElapsed(startDate) {
+ if (!startDate) return 0;
+ const start = new Date(startDate);
+ const now = new Date();
+ const diffTime = Math.abs(now - start);
+ const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
+ return diffDays + 1;
+ },
+
openHistoryModal(asset) {
this.selectedAsset = asset;
fetch(`api.php?action=get_asset_history&asset_id=${asset.id}`).then(res => res.json()).then(data => {
this.history = data;
this.showHistoryModal = true;
});
+ },
+
+ openRemarksModal(asset) {
+ this.selectedAsset = asset;
+ this.isEditingRemarks = false;
+ this.tempRemarks = asset.remarks || '';
+ this.showRemarksModal = true;
+ },
+
+ startEditRemarks() {
+ this.tempRemarks = this.selectedAsset.remarks || '';
+ this.isEditingRemarks = true;
+ },
+
+ saveRemarks() {
+ fetch('api.php?action=update_asset_remarks', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ id: this.selectedAsset.id,
+ remarks: this.tempRemarks
+ })
+ }).then(res => res.json()).then(data => {
+ if (data.success) {
+ this.selectedAsset.remarks = this.tempRemarks;
+ this.isEditingRemarks = false;
+ this.fetchAssets();
+ }
+ });
+ },
+
+ deleteRemarks() {
+ window.showConfirm('비고 내용을 완전히 삭제하시겠습니까?', () => {
+ fetch('api.php?action=update_asset_remarks', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ id: this.selectedAsset.id,
+ remarks: ''
+ })
+ }).then(res => res.json()).then(data => {
+ if (data.success) {
+ this.selectedAsset.remarks = '';
+ this.tempRemarks = '';
+ this.fetchAssets();
+ }
+ });
+ }, '비고 삭제 확인');
}
}
}
diff --git a/users.php b/users.php
index a6a83cf..cb4ad25 100644
--- a/users.php
+++ b/users.php
@@ -1,3 +1,4 @@
+
@@ -96,6 +97,13 @@
휴직자 표시
+
+
+
+ 분류 대상 표시
+
@@ -167,6 +175,26 @@
x-text="sortOrder === 'asc' ? '↑' : '↓'">
+
+
+ 노트북
+ (
+
+ 취득년도
+
+
+ /
+
+ 모델명
+
+
+ )
+
+
연락처 /
사내전화
+
단기임대
+
-
-
+
+
-
+
-
-
+
+
+
+
+
+ 미배정
+
@@ -226,7 +294,8 @@
@@ -248,6 +317,16 @@
class="px-3 py-1 text-xs font-bold rounded-full bg-indigo-50 text-indigo-600 border border-indigo-100">분류
+
+
+
+
+
+ -
+
+
@@ -264,6 +343,80 @@
+
+
+
+
+
+
+
+
취소
@@ -360,12 +543,15 @@
searchQuery: '',
filterRetired: false,
filterOnLeave: true,
+ filterClassification: true,
sortKey: 'name',
sortOrder: 'asc',
selectedIds: [],
bulkStatus: '',
bulkDeptId: '',
bulkPosition: '',
+ showRentalListModal: false,
+ userRentals: [],
formData: { id: '', name: '', emp_id: '', department_id: '', position: '', email: '', mobile: '', accounting_type: '일반회계', status: 'active', phone: '' },
init() {
@@ -384,22 +570,99 @@
result = result.filter(u => u.name.toLowerCase().includes(q) || u.emp_id.toLowerCase().includes(q) || (u.email && u.email.toLowerCase().includes(q)));
}
if (!this.filterRetired) result = result.filter(u => u.status !== 'retired');
- if (!this.filterOnLeave) result = result.filter(u => u.status !== 'on_leave' && u.status !== 'classification');
+ if (!this.filterOnLeave) result = result.filter(u => u.status !== 'on_leave');
+ if (!this.filterClassification) result = result.filter(u => u.status !== 'classification');
- if (this.sortKey) {
- result.sort((a, b) => {
+ // 직위 랭킹 함수
+ const getRank = (pos) => {
+ if (!pos) return 99;
+ if (pos.includes('부회장')) return 1;
+ if (pos.includes('총괄')) return 2;
+ if (pos.includes('원장')) return 3;
+ if (pos.includes('센터장')) return 4;
+ if (pos.includes('본부장')) return 5;
+ if (pos.includes('실장')) return 6;
+ if (pos.includes('부문장')) return 7;
+ if (pos.includes('팀장')) return 8;
+ return 90;
+ };
+
+ result.sort((a, b) => {
+ if (this.sortKey) {
let valA = a[this.sortKey] || '';
let valB = b[this.sortKey] || '';
if (typeof valA === 'string') valA = valA.toLowerCase();
if (typeof valB === 'string') valB = valB.toLowerCase();
- if (valA < valB) return this.sortOrder === 'asc' ? -1 : 1;
- if (valA > valB) return this.sortOrder === 'asc' ? 1 : -1;
- return 0;
- });
- }
+ if (valA !== valB) {
+ if (valA < valB) return this.sortOrder === 'asc' ? -1 : 1;
+ if (valA > valB) return this.sortOrder === 'asc' ? 1 : -1;
+ }
+ }
+
+ // 2순위: 직위 랭킹 (정렬 기준이 같을 때만 적용)
+ return getRank(a.position) - getRank(b.position);
+ });
return result;
},
+ getPositionInfo(pos) {
+ if (!pos) return { bgClass: 'bg-white', iconClass: 'bg-slate-100 text-slate-500', isVIP: false };
+
+ const configs = [
+ { key: '부회장', from: '#fbbf24', to: '#d97706', text: 'text-amber-700', icon: 'bg-amber-100 text-amber-600', tag: 'bg-amber-500 text-white' },
+ { key: '총괄', from: '#3b82f6', to: '#1d4ed8', text: 'text-blue-700', icon: 'bg-blue-100 text-blue-600', tag: 'bg-blue-600 text-white' },
+ { key: '원장', from: '#10b981', to: '#059669', text: 'text-emerald-700', icon: 'bg-emerald-100 text-emerald-600', tag: 'bg-emerald-600 text-white' },
+ { key: '센터장', from: '#06b6d4', to: '#0891b2', text: 'text-cyan-700', icon: 'bg-cyan-100 text-cyan-600', tag: 'bg-cyan-600 text-white' },
+ { key: '본부장', from: '#8b5cf6', to: '#7c3aed', text: 'text-purple-700', icon: 'bg-purple-100 text-purple-600', tag: 'bg-purple-600 text-white' },
+ { key: '실장', from: '#64748b', to: '#475569', text: 'text-slate-700', icon: 'bg-slate-100 text-slate-600', tag: 'bg-slate-600 text-white' },
+ { key: '부문장', from: '#f43f5e', to: '#e11d48', text: 'text-rose-700', icon: 'bg-rose-100 text-rose-600', tag: 'bg-rose-600 text-white' },
+ { key: '팀장', from: '#6366f1', to: '#4f46e5', text: 'text-indigo-700', icon: 'bg-indigo-100 text-indigo-600', tag: 'bg-indigo-600 text-white' }
+ ];
+
+ const config = configs.find(c => pos.includes(c.key));
+ if (config) {
+ return {
+ isVIP: true,
+ from: config.from,
+ bgClass: 'hover:bg-opacity-50 transition-all cursor-pointer',
+ iconClass: config.icon,
+ tagClass: config.tag,
+ textClass: config.text
+ };
+ }
+ return { bgClass: 'bg-white', iconClass: 'bg-slate-100 text-slate-500', isVIP: false };
+ },
+
+ getAccountingInfo(type) {
+ if (type === '특별회계') {
+ return {
+ isSpecial: true,
+ badgeClass: 'bg-amber-100 text-amber-700 border-amber-200',
+ rowGradient: 'linear-gradient(to left, rgba(251, 191, 36, 0.1) 0%, transparent 40%)'
+ };
+ }
+ return {
+ isSpecial: false,
+ badgeClass: 'bg-blue-50 text-blue-600 border-blue-100',
+ rowGradient: ''
+ };
+ },
+
+ getRowStyle(user) {
+ const vip = this.getPositionInfo(user.position);
+ const acc = this.getAccountingInfo(user.accounting_type);
+ let gradients = [];
+
+ if (vip.isVIP) {
+ gradients.push(`linear-gradient(to right, ${vip.from}08 0%, transparent 60%)`);
+ }
+ if (acc.isSpecial) {
+ gradients.push(acc.rowGradient);
+ }
+
+ return gradients.length > 0 ? `background: ${gradients.join(', ')}` : 'background: white';
+ },
+
fetchUsers() {
const scrollPos = window.scrollY;
this.loading = true;
@@ -429,6 +692,16 @@
this.selectedIds = checked ? this.filteredUsers.map(u => u.id) : [];
},
+ showUserRentals(user) {
+ this.selectedUser = user;
+ fetch(`api.php?action=get_user_rentals&user_id=${user.id}`)
+ .then(res => res.json())
+ .then(data => {
+ this.userRentals = data;
+ this.showRentalListModal = true;
+ });
+ },
+
openAddModal() {
this.resetForm();
this.showModal = true;
@@ -441,6 +714,10 @@
},
submitUser() {
+ if (!this.formData.department_id) {
+ window.showAlert('부서를 선택해주세요.', '입력 확인', 'error');
+ return;
+ }
const action = this.isEdit ? 'update_user' : 'add_user';
fetch(`api.php?action=${action}`, {
method: 'POST',
@@ -448,9 +725,15 @@
body: JSON.stringify(this.formData)
}).then(res => res.json()).then(data => {
if (data.success) {
+ window.showAlert(this.isEdit ? '정보가 수정되었습니다.' : '새 직원이 등록되었습니다.', '성공', 'success');
this.showModal = false;
this.fetchUsers();
+ } else {
+ window.showAlert(data.error || '처리 중 오류가 발생했습니다.', '오류', 'error');
}
+ }).catch(err => {
+ console.error(err);
+ window.showAlert('서버와 통신 중 문제가 발생했습니다.', '시스템 오류', 'error');
});
},
@@ -467,18 +750,21 @@
})
}).then(res => res.json()).then(data => {
if (data.success) {
+ window.showAlert(`${this.selectedIds.length}명의 직원 정보가 일괄 변경되었습니다.`, '변경 성공', 'success');
this.selectedIds = [];
this.bulkStatus = '';
this.bulkDeptId = '';
this.bulkPosition = '';
this.fetchUsers();
+ } else {
+ window.showAlert('직원 정보 일괄 변경에 실패했습니다.', '변경 실패', 'error');
}
});
},
resetForm() {
this.isEdit = false;
- this.formData = { id: '', name: '', emp_id: '', department_id: '', position: '', email: '', mobile: '', accounting_type: '일반회계', status: 'active' };
+ this.formData = { id: '', name: '', emp_id: '', department_id: '', position: '', email: '', mobile: '', accounting_type: '일반회계', status: 'active', phone: '' };
}
}
}