feat(d34): import employee master + invited/pending-login flag
- staff_user gains must_change_password (SQLite migrate + PG migration 011): the invited-employee indicator, cleared on the user's first self password-change. - createEmployee takes phone/title/mustChangePassword; Employee carries mustChangePassword. - scripts/import-employees.ts (tsx, Postgres- or SQLite-aware) seeds the 11 from empmaster.xlsx: username = first-name slug, default password <username>@123, role staff, active per ACTIVE_STAT, must-change flagged. Idempotent. Ran on live DB — 11 added. - Employees list: 'Invited · must change pw' badge + a notice explaining the default password pattern so the owner can share logins. Verified: adithya/adithya@123 → 200, inactive raveena blocked. typecheck + employee tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>feat/client-detail-redesign
parent
6e3a79adb8
commit
e8e9db33ea
@ -0,0 +1,58 @@
|
||||
// apps/hq/scripts/import-employees.ts — one-off: seed the employee master (D34).
|
||||
// Adds each employee with username = first-name slug, a default password `<username>@123`,
|
||||
// and must_change_password=1 (they change it on first login). Active per ACTIVE_STAT.
|
||||
// Idempotent: skips a username/email that already exists.
|
||||
// DATABASE_URL=postgres://… npx tsx apps/hq/scripts/import-employees.ts
|
||||
import { openDb } from '../src/db'
|
||||
import { openPgDb } from '../src/db-pg'
|
||||
import { createEmployee } from '../src/repos-employees'
|
||||
|
||||
// From empmaster.xlsx (EMPNAME + ACTIVE_STAT + DESIGNATION code). Emails/phones were blank.
|
||||
const EMPLOYEES: { name: string; active: boolean; title?: string }[] = [
|
||||
{ name: 'ADITHYA', active: true, title: '702' },
|
||||
{ name: 'RAVEENA', active: false, title: '702' },
|
||||
{ name: 'ADITHYAN', active: true },
|
||||
{ name: 'CYRIAC', active: true, title: '702' },
|
||||
{ name: 'ANJU', active: true, title: '702' },
|
||||
{ name: 'RENJITH', active: true },
|
||||
{ name: 'AJMAL', active: true },
|
||||
{ name: 'ELIAS MATHEW', active: false, title: '703' },
|
||||
{ name: 'THOMAS', active: true, title: '700' },
|
||||
{ name: 'DEVIKA', active: true, title: '702' },
|
||||
{ name: 'TIBIN', active: true, title: '703' },
|
||||
]
|
||||
|
||||
/** First name, lowercased, non-alphanumerics stripped — the login username + password base. */
|
||||
function slug(name: string): string {
|
||||
return name.trim().toLowerCase().split(/\s+/)[0]!.replace(/[^a-z0-9]/g, '')
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Same engine selection as the server: DATABASE_URL → Postgres, else SQLite at HQ_DATA_DIR.
|
||||
const pgUrl = process.env['DATABASE_URL'] ?? ''
|
||||
const db = pgUrl !== '' ? await openPgDb(pgUrl) : openDb(process.env['HQ_DATA_DIR'])
|
||||
let added = 0, skipped = 0
|
||||
const summary: string[] = []
|
||||
for (const e of EMPLOYEES) {
|
||||
const username = slug(e.name)
|
||||
const password = `${username}@123`
|
||||
const email = `${username}@sims.local` // placeholder (no real emails in the master)
|
||||
try {
|
||||
const emp = await createEmployee(db, 'system', {
|
||||
email, username, displayName: e.name, role: 'staff', password,
|
||||
mustChangePassword: true, ...(e.title !== undefined ? { title: e.title } : {}),
|
||||
})
|
||||
// Set inactive employees inactive (createEmployee makes them active by default).
|
||||
if (!e.active) await db.run(`UPDATE staff_user SET active=0 WHERE id=?`, emp.id)
|
||||
added++
|
||||
summary.push(` + ${e.name} → username: ${username} password: ${password} ${e.active ? '' : '(inactive)'}`)
|
||||
} catch (err) {
|
||||
skipped++
|
||||
summary.push(` - ${e.name} skipped: ${err instanceof Error ? err.message : String(err)}`)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Employee import: ${added} added, ${skipped} skipped.\n${summary.join('\n')}`)
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error(e); process.exit(1) })
|
||||
Loading…
Reference in New Issue