function getCompMinutes(){
const el=document.getElementById('compMinutesSelect');
return el ? Number(el.value||0) : 0;
}
/* Login role is now determined by Employee Level. */
login=function(){
let em=document.getElementById('loginEmail').value.trim().toLowerCase();
let pw=document.getElementById('loginPassword').value;
if(pw!=='123456'){alert('Email or password is incorrect.');return}
let e=db.employees.find(x=>(x.email||'').toLowerCase()===em);
if(!e){
if(em==='hr@sugiblez.co.th'){
role='admin';
currentUser={...v5Roles.admin,level:'Admin'};
}else if(em==='division.manager@sugiblez.co.th'){
role='divisionManager';
currentUser={...v5Roles.divisionManager,level:'Division manager'};
}else if(em==='manager@sugiblez.co.th'){
role='manager';
currentUser={...v5Roles.manager,level:'Manager'};
}else if(em==='executive@sugiblez.co.th'){
/* Legacy demo account: keep it usable with Admin-level permissions. */
role='admin';
currentUser={...v5Roles.admin,name:'Executive',email:em,label:'Admin',division:'All Divisions',level:'Admin'};
}else{
alert('Email or password is incorrect.');return;
}
}else{
e.level=normalizeLevel(e);
role=levelRole(e.level);
currentUser={
...v5Roles.employee,
name:e.name,email:e.email,empId:e.id,division:e.division,
label:e.level,level:e.level
};
if(role==='manager'){
currentUser={...v5Roles.manager,name:e.name,email:e.email,division:e.division,level:e.level};
}else if(role==='divisionManager'){
currentUser={...v5Roles.divisionManager,name:e.name,email:e.email,division:e.division,level:e.level};
}else if(role==='admin'){
currentUser={...v5Roles.admin,name:e.name,email:e.email,level:e.level};
}
}
document.getElementById('login').classList.add('hidden');
document.getElementById('app').classList.remove('hidden');
document.getElementById('rolebox').innerHTML=`${currentUser.name}
${currentUser.label}
${currentUser.division}`;
document.getElementById('userPill').textContent=currentUser.name+' · '+currentUser.label;
buildNav();go('dashboard');
};
/* Scope is determined strictly by Level. */
v5Visible=function(){
if(role==='admin' || currentUser?.level==='Admin') return db.employees;
if(role==='manager' || currentUser?.level==='Manager' ||
role==='divisionManager' || currentUser?.level==='Division manager'){
return db.employees.filter(e=>e.division===currentUser.division);
}
let me=currentUser?.empId;
return db.employees.filter(e=>e.id===me);
};
function canManageEmployee(empId){
if(role==='admin' || currentUser?.level==='Admin') return true;
let e=v5Emp(empId);
if((role==='manager'||currentUser?.level==='Manager'||
role==='divisionManager'||currentUser?.level==='Division manager') &&
e.division===currentUser.division) return true;
return false;
}
/* Pending approval buttons follow the requested Level authority. */
v5Pending=function(list){
let allowed=(list||[]).filter(e=>canManageEmployee(e.id));
let l=db.leaves.filter(x=>allowed.some(e=>e.id===x.emp)&&x.status.includes('Pending'));
let o=db.ot.filter(x=>allowed.some(e=>e.id===x.emp)&&x.status.includes('Pending'));
return tbl(['Employee','Type','Date','Detail','Status','Action'],[
...l.map(x=>[
v5Emp(x.emp).name,'Leave',x.start,v5LeaveDurationDetail(x),v5Badge(x.status),
((role==='manager'&&x.status==='Pending Manager') ||
(role==='divisionManager'&&(x.status==='Pending Manager'||x.status==='Pending Division Manager')) ||
role==='admin')
?``:'View'
]),
...o.map(x=>[
v5Emp(x.emp).name,'OT',x.date,`Overtime · ${v5OTDurationDetail(x)}`,v5Badge(x.status),
((role==='manager'&&x.status==='Pending Manager') ||
(role==='divisionManager'&&(x.status==='Pending Manager'||x.status==='Pending Division Manager')) ||
role==='admin')
?``:'View'
])
]);
};
/* Leave: Manager acknowledgement is required before completion,
while Division Manager may give final approval immediately. */
v5Approve=function(id,type){
let x=type==='Leave'?db.leaves.find(a=>a.id===id):db.ot.find(a=>a.id===id);
if(!x){alert('Request not found.');return}
let e=v5Emp(x.emp);
if(!canManageEmployee(x.emp)){
alert('You do not have permission to approve this request.');return;
}
if(type==='Leave'){
if(role==='admin'){
x.status='Approved';
v5Log(x.emp,type,x.id,'Approved by Admin',currentUser.name,'Admin final approval');
v5Email(e.email,`${type} request approved`,`${e.name}, your leave request was approved by Admin.`);
}else if(role==='divisionManager' &&
(x.status==='Pending Manager'||x.status==='Pending Division Manager')){
const prior=x.status;
x.status='Approved';
v5Log(x.emp,type,x.id,'Approved by Division Manager',currentUser.name,
prior==='Pending Manager'?'Final approval granted without prior Manager approval':'Final approval after Manager approval');
v5Email(e.email,`${type} request approved`,`${e.name}, your leave request was fully approved by Division Manager.`);
}else if(role==='manager' && x.status==='Pending Manager'){
x.status='Pending Division Manager';
v5Log(x.emp,type,x.id,'Approved by Manager',currentUser.name,
'Manager approval recorded; Division Manager final approval required for leave completion');
v5Email(e.divisionManagerEmail,`Approval Required: ${type}`,
`${e.name}'s leave request was approved by Manager and awaits Division Manager final approval.`);
}else{
alert('This request is not awaiting your approval.');return;
}
}else{
/* OT: Manager can complete approval; Division Manager can also approve directly. */
if(role==='admin'){
x.status='Approved';
v5Log(x.emp,type,x.id,'Approved by Admin',currentUser.name,'Admin final approval');
v5Email(e.email,`${type} request approved`,`${e.name}, your OT request was approved by Admin.`);
}else if((role==='divisionManager') &&
(x.status==='Pending Manager'||x.status==='Pending Division Manager')){
x.status='Approved';
v5Log(x.emp,type,x.id,'Approved by Division Manager',currentUser.name,
'Division Manager final approval');
v5Email(e.email,`${type} request approved`,`${e.name}, your OT request was approved by Division Manager.`);
}else if(role==='manager' && x.status==='Pending Manager'){
x.status='Approved';
v5Log(x.emp,type,x.id,'Approved by Manager',currentUser.name,
'Manager approval completed');
v5Email(e.email,`${type} request approved`,`${e.name}, your OT request was approved by Manager.`);
}else{
alert('This request is not awaiting your approval.');return;
}
}
v5Save();closeModal();render(currentPage);alert('Approval recorded. Email notification queued.');
};
/* Admin may reject any request; Manager / Division Manager only within their division. */
v5Reject=function(id,type){
let x=type==='Leave'?db.leaves.find(a=>a.id===id):db.ot.find(a=>a.id===id),e=x&&v5Emp(x.emp);
if(!x||!e||!canManageEmployee(x.emp)){
alert('You do not have permission to reject this request.');return;
}
x.status='Rejected';
v5Log(x.emp,type,x.id,'Rejected',currentUser.name,'Request rejected');
v5Email(e.email,`${type} request rejected`,`${e.name}, your ${type.toLowerCase()} request was rejected.`);
v5Save();closeModal();render(currentPage);alert('Rejected. Employee notification queued.');
};
/* Leave table: Admin can review every pending request; Manager/DM only their division. */
v5LeaveTable=function(rows){
return tbl(['Employee','Type','Start','End','Days','Status','Documents','Cancellation','Action'],rows.map(x=>{
let e=v5Emp(x.emp),cancel=x.cancelStatus||'—';
if(x.status==='Approved'&&!x.cancelStatus&&role==='employee'&&x.emp===currentUser.empId)
cancel=``;
let canReview=(role==='admin'&&x.status.includes('Pending')) ||
(role==='manager'&&x.status==='Pending Manager'&&canManageEmployee(x.emp)) ||
(role==='divisionManager'&&(x.status==='Pending Manager'||x.status==='Pending Division Manager')&&canManageEmployee(x.emp));
let action=canReview?``:'View';
if(x.cancelStatus&&x.cancelStatus.includes('Pending') &&
(role==='admin'||role==='manager'||role==='divisionManager') &&
canManageEmployee(x.emp))
action=``;
return [e.name,x.type,x.start,x.end,x.days,v5Badge(x.status),fileCount(x.files),cancel,action];
}));
};
/* Cancellation: Admin can approve/reject at any point; Manager/DM remain division-scoped. */
v5ApproveCancel=function(id){
let x=db.leaves.find(a=>a.id===id),e=x&&v5Emp(x.emp);
if(!x||!e||!canManageEmployee(x.emp)){alert('You do not have permission to approve this cancellation.');return;}
if(role==='admin'){
x.cancelStatus='Cancelled';x.status='Cancelled';
v5Log(x.emp,'Leave Cancellation',x.id,'Approved by Admin',currentUser.name,'Admin final approval; leave cancelled');
v5Email(e.email,'Leave cancellation approved',`${e.name}, your leave cancellation was approved by Admin.`);
}else if(role==='divisionManager'){
x.cancelStatus='Cancelled';x.status='Cancelled';
v5Log(x.emp,'Leave Cancellation',x.id,'Approved by Division Manager',currentUser.name,'Final cancellation approval');
v5Email(e.email,'Leave cancellation approved',`${e.name}, your leave cancellation was approved by Division Manager.`);
}else if(role==='manager'&&x.cancelStatus==='Pending Manager Cancellation'){
x.cancelStatus='Pending Division Manager Cancellation';
v5Log(x.emp,'Leave Cancellation',x.id,'Approved by Manager',currentUser.name,'Forwarded to Division Manager');
v5Email(e.divisionManagerEmail,'Approval Required: Leave Cancellation',
`${e.name}'s cancellation request awaits Division Manager approval.`);
}else{
alert('This cancellation is not awaiting your approval.');return;
}
v5Save();closeModal();render(currentPage);alert('Cancellation approval recorded.');
};
v5RejectCancel=function(id){
let x=db.leaves.find(a=>a.id===id),e=x&&v5Emp(x.emp);
if(!x||!e||!canManageEmployee(x.emp)){alert('You do not have permission to reject this cancellation.');return;}
x.cancelStatus='Cancellation Rejected';
v5Log(x.emp,'Leave Cancellation',x.id,'Rejected',currentUser.name,'Cancellation rejected');
v5Email(e.email,'Leave cancellation rejected',`${e.name}, your leave cancellation request was rejected.`);
v5Save();closeModal();render(currentPage);alert('Cancellation rejected.');
};
/* Employee Master / Add Employee: Level is the role authority. */
v5EmployeeModal=function(id=''){
let e=id?v5Emp(id):{
id:'',name:'',email:'',division:'Sales and Marketing Division',
divisionManager:'Sales Division Manager',divisionManagerEmail:'division.manager@sugiblez.co.th',
manager:'Sales Manager',managerEmail:'manager@sugiblez.co.th',position:'',start:'',
status:'Active',level:'Staff',annual:10,sick:30,personal:3,password:'123456'
};
openModal(id?'Edit Employee':'Add Employee',`
`); document.getElementById('v_div').value=e.division; document.getElementById('v_status').value=e.status; document.getElementById('v_level').value=e.level; }; v5SaveEmployee=function(id){ let level=val('v_level'); let o={ id:val('v_id'),name:val('v_name'),email:val('v_email'),level, division:val('v_div'),divisionManager:val('v_dm'),divisionManagerEmail:val('v_dme'), manager:val('v_mgr'),managerEmail:val('v_mgre'),position:val('v_pos'), start:val('v_start'),status:val('v_status'),password:'123456', annual:Number(val('v_a')),sick:Number(val('v_s')),personal:Number(val('v_p')) }; if(!o.id||!o.name){alert('Employee ID and Full Name are required.');return} if(id){ let i=db.employees.findIndex(x=>x.id===id); db.employees[i]={...db.employees[i],...o}; }else{ db.employees.push(o); } v5Save();closeModal();render(currentPage);alert('Employee data saved.'); }; /* Settings > Employee Master uses the new Level field. Other settings remain unchanged. */ settingsPage=function(){ if(role!=='admin')return `
`; return `
`; }; /* Rebuild current role display when page is opened after an employee Level edit. */ v5Save(); })();