feat: add Angular member and user models + API services

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Chen
2026-05-27 14:13:35 -07:00
parent 8249b3fe3e
commit d2eac52a47
4 changed files with 225 additions and 0 deletions
@@ -0,0 +1,84 @@
export type MemberStatus = 'Member' | 'Visitor' | 'Inactive' | 'Former';
export interface MemberListItemDto {
id: number;
firstName_en: string;
lastName_en: string;
nickName: string | null;
firstName_zh: string | null;
lastName_zh: string | null;
status: MemberStatus;
email: string | null;
phoneCell: string | null;
joinDate: string | null;
linkedUserId: string | null;
}
export interface MemberDto extends MemberListItemDto {
gender: string | null;
dateOfBirth: string | null;
baptismDate: string | null;
baptismChurch: string | null;
phoneHome: string | null;
address: string | null;
city: string | null;
state: string | null;
zipCode: string | null;
country: string;
photoBlobPath: string | null;
languagePreference: string;
notes: string | null;
familyUnitId: number | null;
createdAt: string;
updatedAt: string;
}
export interface CreateMemberRequest {
firstName_en: string;
lastName_en: string;
nickName: string | null;
firstName_zh: string | null;
lastName_zh: string | null;
gender: string | null;
dateOfBirth: string | null;
baptismDate: string | null;
baptismChurch: string | null;
email: string | null;
phoneCell: string | null;
phoneHome: string | null;
address: string | null;
city: string | null;
state: string | null;
zipCode: string | null;
country: string;
status: string;
languagePreference: string;
joinDate: string | null;
notes: string | null;
familyUnitId: number | null;
}
export type UpdateMemberRequest = CreateMemberRequest;
export interface PagedResult<T> {
items: T[];
totalCount: number;
page: number;
pageSize: number;
totalPages: number;
}
export interface MemberQueryParams {
page?: number;
pageSize?: number;
search?: string;
status?: string;
hasUser?: boolean;
}
/** Display name: NickName (if present) else FirstName_en, plus LastName_en */
export function memberDisplayName(
m: Pick<MemberListItemDto, 'nickName' | 'firstName_en' | 'lastName_en'>
): string {
return `${m.nickName ?? m.firstName_en} ${m.lastName_en}`;
}
@@ -0,0 +1,43 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApiConfigService } from '../../../core/services/api-config.service';
import {
MemberDto, MemberListItemDto, CreateMemberRequest,
UpdateMemberRequest, MemberQueryParams, PagedResult
} from '../models/member.model';
@Injectable({ providedIn: 'root' })
export class MemberApiService {
private readonly endpoint: string;
constructor(private http: HttpClient, apiConfig: ApiConfigService) {
this.endpoint = apiConfig.getApiUrl('members');
}
getPaged(params: MemberQueryParams = {}): Observable<PagedResult<MemberListItemDto>> {
let p = new HttpParams()
.set('page', params.page ?? 1)
.set('pageSize', params.pageSize ?? 20);
if (params.search !== undefined && params.search !== '') p = p.set('search', params.search);
if (params.status !== undefined && params.status !== '') p = p.set('status', params.status);
if (params.hasUser !== undefined) p = p.set('hasUser', params.hasUser);
return this.http.get<PagedResult<MemberListItemDto>>(this.endpoint, { params: p });
}
getById(id: number): Observable<MemberDto> {
return this.http.get<MemberDto>(`${this.endpoint}/${id}`);
}
create(request: CreateMemberRequest): Observable<{ id: number }> {
return this.http.post<{ id: number }>(this.endpoint, request);
}
update(id: number, request: UpdateMemberRequest): Observable<void> {
return this.http.put<void>(`${this.endpoint}/${id}`, request);
}
delete(id: number): Observable<void> {
return this.http.delete<void>(`${this.endpoint}/${id}`);
}
}
@@ -0,0 +1,52 @@
export interface UserListItemDto {
id: string;
email: string;
memberId: number | null;
memberDisplayName: string | null;
roles: string[];
isActive: boolean;
languagePreference: string;
lastLoginAt: string | null;
createdAt: string;
}
export type UserDto = UserListItemDto;
export interface CreateUserRequest {
memberId: number;
email: string;
roles: string[];
languagePreference: string;
}
export interface CreateUserResult {
userId: string;
tempPassword: string;
}
export interface UpdateUserRequest {
email: string;
roles: string[];
isActive: boolean;
languagePreference: string;
}
export interface UserQueryParams {
page?: number;
pageSize?: number;
search?: string;
}
export interface PagedResult<T> {
items: T[];
totalCount: number;
page: number;
pageSize: number;
totalPages: number;
}
export const ALL_ROLES = [
'super_admin','pastor','board_member','coworker_chair','ministry_leader',
'district_leader','cell_leader','coworker','finance','secretary',
'worship_leader','member','visitor'
] as const;
@@ -0,0 +1,46 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApiConfigService } from '../../../core/services/api-config.service';
import {
UserDto, UserListItemDto, CreateUserRequest, CreateUserResult,
UpdateUserRequest, UserQueryParams, PagedResult
} from '../models/user.model';
@Injectable({ providedIn: 'root' })
export class UserApiService {
private readonly endpoint: string;
constructor(private http: HttpClient, apiConfig: ApiConfigService) {
this.endpoint = apiConfig.getApiUrl('users');
}
getPaged(params: UserQueryParams = {}): Observable<PagedResult<UserListItemDto>> {
let p = new HttpParams()
.set('page', params.page ?? 1)
.set('pageSize', params.pageSize ?? 20);
if (params.search) p = p.set('search', params.search);
return this.http.get<PagedResult<UserListItemDto>>(this.endpoint, { params: p });
}
getById(id: string): Observable<UserDto> {
return this.http.get<UserDto>(`${this.endpoint}/${id}`);
}
createUser(request: CreateUserRequest): Observable<CreateUserResult> {
return this.http.post<CreateUserResult>(this.endpoint, request);
}
update(id: string, request: UpdateUserRequest): Observable<void> {
return this.http.put<void>(`${this.endpoint}/${id}`, request);
}
deactivate(id: string): Observable<void> {
return this.http.delete<void>(`${this.endpoint}/${id}`);
}
resetPassword(id: string): Observable<{ tempPassword: string }> {
return this.http.post<{ tempPassword: string }>(
`${this.endpoint}/${id}/reset-password`, {});
}
}