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>
53 lines
1.7 KiB
TypeScript
53 lines
1.7 KiB
TypeScript
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;
|
|
}
|