Add paginated users/roles, user status, frontend permissions, profile pictures, identifier file storage

- 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)
This commit is contained in:
Ryan Moon
2026-03-29 08:16:34 -05:00
parent 92371ff228
commit b9f78639e2
48 changed files with 1689 additions and 643 deletions

View File

@@ -14,7 +14,7 @@ function createLookupService(
systemSeeds: ReadonlyArray<{ slug: string; name: string; description: string; sortOrder: number }>,
) {
return {
async seedForCompany(db: PostgresJsDatabase, companyId: string) {
async seedForCompany(db: PostgresJsDatabase<any>, companyId: string) {
const existing = await db
.select()
.from(table)
@@ -32,7 +32,7 @@ function createLookupService(
)
},
async list(db: PostgresJsDatabase, companyId: string) {
async list(db: PostgresJsDatabase<any>, companyId: string) {
return db
.select()
.from(table)
@@ -40,7 +40,7 @@ function createLookupService(
.orderBy(table.sortOrder)
},
async getBySlug(db: PostgresJsDatabase, companyId: string, slug: string) {
async getBySlug(db: PostgresJsDatabase<any>, companyId: string, slug: string) {
const [row] = await db
.select()
.from(table)
@@ -49,7 +49,7 @@ function createLookupService(
return row ?? null
},
async create(db: PostgresJsDatabase, companyId: string, input: LookupCreateInput) {
async create(db: PostgresJsDatabase<any>, companyId: string, input: LookupCreateInput) {
const [row] = await db
.insert(table)
.values({
@@ -64,7 +64,7 @@ function createLookupService(
return row
},
async update(db: PostgresJsDatabase, companyId: string, id: string, input: LookupUpdateInput) {
async update(db: PostgresJsDatabase<any>, companyId: string, id: string, input: LookupUpdateInput) {
// Prevent modifying system rows' slug or system flag
const existing = await db
.select()
@@ -85,7 +85,7 @@ function createLookupService(
return row ?? null
},
async delete(db: PostgresJsDatabase, companyId: string, id: string) {
async delete(db: PostgresJsDatabase<any>, companyId: string, id: string) {
const existing = await db
.select()
.from(table)
@@ -104,7 +104,7 @@ function createLookupService(
return row ?? null
},
async validateSlug(db: PostgresJsDatabase, companyId: string, slug: string): Promise<boolean> {
async validateSlug(db: PostgresJsDatabase<any>, companyId: string, slug: string): Promise<boolean> {
const row = await this.getBySlug(db, companyId, slug)
return row !== null && row.isActive
},