SUGI BLEZ TH

HR Management System

Internal HR portal · Functional V4
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 `
HR-only settings. Employee master data, leave rules, holidays, working time and OT conversion rules can only be changed by HR / Admin.
`; return `

Employee Master Data

${tbl(['ID','Name','Level','Division','Division Manager Name','Manager Name','Email','Position','Action'], db.employees.map(e=>[ e.id,e.name,normalizeLevel(e),e.division,e.divisionManager,e.manager,e.email,e.position, `` ]) )}

Leave Rules

Company Holidays & Compensatory Workdays

${tbl(['Date','Holiday','Type','Compensatory Workday','Action'],db.holidays.map((h,i)=>[h.date,h.name,h.type,h.comp||'—',``]))}

Working Time & OT Conversion

OT cash payment is disabled. Approved OT is converted into compensatory time in 15-minute units and rounded down.
`; }; /* Rebuild current role display when page is opened after an employee Level edit. */ v5Save(); })();