feat: add company sidebar filters, internal phone, mail icon fix, and update script

This commit is contained in:
2026-05-26 09:08:34 +07:00
parent 7e11d42bbb
commit fc4a283c0a
10 changed files with 250 additions and 27 deletions
+9
View File
@@ -78,11 +78,20 @@ async function initializeTables() {
email TEXT,
building TEXT,
floor TEXT,
internal_phone TEXT,
FOREIGN KEY (department_id) REFERENCES departments(id) ON DELETE SET NULL,
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE SET NULL
)
`);
// Dynamic schema migration: add internal_phone column if it does not exist
const columns = await all('PRAGMA table_info(employees)');
const hasInternalPhone = columns.some((col) => col.name === 'internal_phone');
if (!hasInternalPhone) {
console.log('Migrating database: adding internal_phone column to employees table...');
await run('ALTER TABLE employees ADD COLUMN internal_phone TEXT');
}
// Seed data if database is fresh
const deptCount = await get('SELECT COUNT(*) as count FROM departments');
if (deptCount.count === 0) {
+28 -11
View File
@@ -190,7 +190,7 @@ app.put('/api/companies/:id', authenticateToken, async (req, res) => {
// 4. Employees Endpoints (Search & List)
app.get('/api/employees', async (req, res) => {
const { search, departmentId } = req.query;
const { search, departmentId, companyId } = req.query;
let sql = `
SELECT e.*, d.name as department_name, c.name as company_name
@@ -206,6 +206,11 @@ app.get('/api/employees', async (req, res) => {
params.push(departmentId);
}
if (companyId) {
conditions.push('e.company_id = ?');
params.push(companyId);
}
if (conditions.length > 0) {
sql += ' WHERE ' + conditions.join(' AND ');
}
@@ -237,6 +242,7 @@ app.get('/api/employees', async (req, res) => {
const compMatch = e.company_name && e.company_name.toLowerCase().includes(cleanSearch);
const buildMatch = e.building && e.building.toLowerCase().includes(cleanSearch);
const floorMatch = e.floor && e.floor.toLowerCase().includes(cleanSearch);
const internalPhoneMatch = e.internal_phone && e.internal_phone.includes(cleanSearch);
let phoneMatch = false;
if (searchDigits.length > 0 && e.phone) {
@@ -246,7 +252,7 @@ app.get('/api/employees', async (req, res) => {
}
}
return nameMatch || jobMatch || emailMatch || deptMatch || compMatch || buildMatch || floorMatch || phoneMatch;
return nameMatch || jobMatch || emailMatch || deptMatch || compMatch || buildMatch || floorMatch || phoneMatch || internalPhoneMatch;
});
}
@@ -258,16 +264,20 @@ app.get('/api/employees', async (req, res) => {
// Create Employee
app.post('/api/employees', authenticateToken, async (req, res) => {
const { name, company_id, job_title, department_id, phone, email, building, floor } = req.body;
const { name, company_id, job_title, department_id, phone, email, building, floor, internal_phone } = req.body;
if (!name || !job_title) {
return res.status(400).json({ error: 'Name and Job Title are required fields.' });
}
if (internal_phone && !/^\d{3}$/.test(internal_phone.trim())) {
return res.status(400).json({ error: 'Внутренний телефон должен состоять ровно из 3 цифр.' });
}
try {
const result = await db.run(`
INSERT INTO employees (name, company_id, job_title, department_id, phone, email, building, floor)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
INSERT INTO employees (name, company_id, job_title, department_id, phone, email, building, floor, internal_phone)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [
name.trim(),
company_id || null,
@@ -276,7 +286,8 @@ app.post('/api/employees', authenticateToken, async (req, res) => {
phone ? phone.trim() : '',
email ? email.trim() : '',
building ? building.trim() : '',
floor ? floor.trim() : ''
floor ? floor.trim() : '',
internal_phone ? internal_phone.trim() : ''
]);
const newEmp = await db.get(`
@@ -296,16 +307,20 @@ app.post('/api/employees', authenticateToken, async (req, res) => {
// Update Employee
app.put('/api/employees/:id', authenticateToken, async (req, res) => {
const { id } = req.params;
const { name, company_id, job_title, department_id, phone, email, building, floor } = req.body;
const { name, company_id, job_title, department_id, phone, email, building, floor, internal_phone } = req.body;
if (!name || !job_title) {
return res.status(400).json({ error: 'Name and Job Title are required fields.' });
}
if (internal_phone && !/^\d{3}$/.test(internal_phone.trim())) {
return res.status(400).json({ error: 'Внутренний телефон должен состоять ровно из 3 цифр.' });
}
try {
await db.run(`
UPDATE employees
SET name = ?, company_id = ?, job_title = ?, department_id = ?, phone = ?, email = ?, building = ?, floor = ?
SET name = ?, company_id = ?, job_title = ?, department_id = ?, phone = ?, email = ?, building = ?, floor = ?, internal_phone = ?
WHERE id = ?
`, [
name.trim(),
@@ -316,6 +331,7 @@ app.put('/api/employees/:id', authenticateToken, async (req, res) => {
email ? email.trim() : '',
building ? building.trim() : '',
floor ? floor.trim() : '',
internal_phone ? internal_phone.trim() : '',
id
]);
@@ -393,8 +409,8 @@ app.post('/api/admin/import', authenticateToken, async (req, res) => {
const newDeptId = e.department_id ? (deptIdMapping[e.department_id] || null) : null;
const newCompId = e.company_id ? (compIdMapping[e.company_id] || null) : null;
await db.run(`
INSERT INTO employees (name, company_id, job_title, department_id, phone, email, building, floor)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
INSERT INTO employees (name, company_id, job_title, department_id, phone, email, building, floor, internal_phone)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [
e.name,
newCompId,
@@ -403,7 +419,8 @@ app.post('/api/admin/import', authenticateToken, async (req, res) => {
e.phone || '',
e.email || '',
e.building || '',
e.floor || ''
e.floor || '',
e.internal_phone || ''
]);
}