- Users page: paginated, searchable, sortable with inline roles (no N+1) - Roles page: paginated, searchable, sortable + /roles/all for dropdowns - User is_active field with migration, PATCH toggle, auth check (disabled=401) - Frontend permission checks: auth store loads permissions, sidebar/buttons conditional - Profile pictures via file storage for users and members, avatar component - Identifier images use file storage API instead of base64 - Fix TypeScript errors across admin UI - 64 API tests passing (10 new)
29 lines
1.0 KiB
TypeScript
29 lines
1.0 KiB
TypeScript
import { pgTable, uuid, varchar, timestamp, pgEnum, uniqueIndex, boolean } from 'drizzle-orm/pg-core'
|
|
import { companies } from './stores.js'
|
|
|
|
export const userRoleEnum = pgEnum('user_role', [
|
|
'admin',
|
|
'manager',
|
|
'staff',
|
|
'technician',
|
|
'instructor',
|
|
])
|
|
|
|
export const users = pgTable('user', {
|
|
id: uuid('id').primaryKey().defaultRandom(),
|
|
companyId: uuid('company_id')
|
|
.notNull()
|
|
.references(() => companies.id),
|
|
email: varchar('email', { length: 255 }).notNull().unique(),
|
|
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
|
|
firstName: varchar('first_name', { length: 100 }).notNull(),
|
|
lastName: varchar('last_name', { length: 100 }).notNull(),
|
|
role: userRoleEnum('role').notNull().default('staff'),
|
|
isActive: boolean('is_active').notNull().default(true),
|
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
|
})
|
|
|
|
export type User = typeof users.$inferSelect
|
|
export type UserInsert = typeof users.$inferInsert
|