Initial commit: EatMe attendance tracker
Google-SSO PWA for bistro employee clock-in/out, admin employee management, and stats with CSV export. Express + SQLite backend, React + Zustand frontend in the light-mono-tui design language. Multi-stage Dockerfile, compose.yaml for image-based deploys, nginx reverse-proxy template, and an OKF documentation bundle in docs/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
52
backend/src/services/employees.ts
Normal file
52
backend/src/services/employees.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { db } from "../db";
|
||||
import type { Employee } from "../types";
|
||||
|
||||
export function findActiveEmployeeByEmail(email: string): Employee | undefined {
|
||||
return db
|
||||
.prepare<[string], Employee>(
|
||||
"SELECT * FROM employees WHERE email = ? AND active = 1"
|
||||
)
|
||||
.get(email.toLowerCase());
|
||||
}
|
||||
|
||||
export function findEmployeeById(id: number): Employee | undefined {
|
||||
return db.prepare<[number], Employee>("SELECT * FROM employees WHERE id = ?").get(id);
|
||||
}
|
||||
|
||||
export function listEmployees(): Employee[] {
|
||||
return db
|
||||
.prepare<[], Employee>("SELECT * FROM employees ORDER BY active DESC, email ASC")
|
||||
.all();
|
||||
}
|
||||
|
||||
/** Fills in the display name the first time we see a Google profile for this employee. */
|
||||
export function touchEmployeeName(id: number, name: string | null): void {
|
||||
if (!name) return;
|
||||
db.prepare("UPDATE employees SET name = ? WHERE id = ? AND name IS NULL").run(name, id);
|
||||
}
|
||||
|
||||
export function addEmployee(email: string, name: string | null): Employee {
|
||||
const normalized = email.trim().toLowerCase();
|
||||
const existing = db
|
||||
.prepare<[string], Employee>("SELECT * FROM employees WHERE email = ?")
|
||||
.get(normalized);
|
||||
|
||||
if (existing) {
|
||||
db.prepare("UPDATE employees SET active = 1, name = COALESCE(?, name) WHERE id = ?").run(
|
||||
name,
|
||||
existing.id
|
||||
);
|
||||
return findEmployeeById(existing.id)!;
|
||||
}
|
||||
|
||||
const result = db
|
||||
.prepare("INSERT INTO employees (email, name) VALUES (?, ?)")
|
||||
.run(normalized, name);
|
||||
return findEmployeeById(Number(result.lastInsertRowid))!;
|
||||
}
|
||||
|
||||
/** Soft delete: revokes login access but keeps attendance history for stats. */
|
||||
export function deactivateEmployee(id: number): boolean {
|
||||
const result = db.prepare("UPDATE employees SET active = 0 WHERE id = ?").run(id);
|
||||
return result.changes > 0;
|
||||
}
|
||||
Reference in New Issue
Block a user