admin/services/users.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

122 lines
3.7 KiB
TypeScript

import type { CompleteProfileDto, UpdateUserProfileDto, UserProfileResponseDto } from '@/api/generated/models'
import {
createServiceError,
errorResult,
handleServiceError,
type ServiceCallOptions,
type ServiceResult,
shouldBubbleErrorToParent,
successResult,
} from '@/services/errorHandler'
import { unwrapApiData } from '@/services/apiResponse'
import { ANALYTICS_EVENTS, trackAnalyticsEventOnce } from '@/lib/analytics'
import { getUsers } from '@/api/generated/users/users'
import { texts } from '@/texts'
export type UserProfile = UserProfileResponseDto
export type CompleteProfilePayload = CompleteProfileDto
export type UpdateProfilePayload = UpdateUserProfileDto
const usersApi = getUsers()
export const GET_ME = async (options?: ServiceCallOptions): Promise<ServiceResult<UserProfile>> => {
try {
const res = await usersApi.usersControllerGetMe()
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.profile.fetchProfileFailed)
}
const profile = unwrapApiData(payload)
if (!profile?.id) {
throw createServiceError(texts.common.invalidServerResponse)
}
return successResult(profile)
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) {
throw normalizedError
}
return errorResult(normalizedError)
}
}
export const PATCH_ME = async (data: UpdateProfilePayload, options?: ServiceCallOptions): Promise<ServiceResult<UserProfile>> => {
try {
const body: UpdateProfilePayload = {}
if (data.firstName !== undefined) body.firstName = data.firstName.trim()
if (data.lastName !== undefined) body.lastName = data.lastName.trim()
if (data.gender !== undefined) body.gender = data.gender
if (data.cityId !== undefined) body.cityId = data.cityId
if (data.bio !== undefined) body.bio = data.bio.trim()
if (data.avatarUrl !== undefined) body.avatarUrl = data.avatarUrl
if (data.defaultAddress !== undefined) {
body.defaultAddress = data.defaultAddress === null ? null : data.defaultAddress.trim()
}
const res = await usersApi.usersControllerUpdateMe(body)
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.profile.updateProfileFailed)
}
const profile = unwrapApiData(payload)
if (!profile?.id) {
throw createServiceError(texts.common.invalidServerResponse)
}
return successResult(profile)
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) {
throw normalizedError
}
return errorResult(normalizedError)
}
}
export const COMPLETE_PROFILE = async (data: CompleteProfilePayload, options?: ServiceCallOptions): Promise<ServiceResult<UserProfile>> => {
try {
const res = await usersApi.usersControllerCompleteProfile({
firstName: data.firstName.trim(),
lastName: data.lastName.trim(),
gender: data.gender,
dateOfBirth: data.dateOfBirth,
cityId: data.cityId,
})
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.profile.completeProfileFailed)
}
const profile = unwrapApiData(payload)
if (!profile?.id) {
throw createServiceError(texts.common.invalidServerResponse)
}
trackAnalyticsEventOnce(ANALYTICS_EVENTS.SIGN_UP, profile.id, { method: 'otp_profile_completion' })
return successResult(profile)
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) {
throw normalizedError
}
return errorResult(normalizedError)
}
}