admin/services/contactLinks.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

128 lines
4.5 KiB
TypeScript

import type { CreateUserContactLinkDto, UpdateUserContactLinkDto, UserContactLinkResponseDto } from '@/api/generated/models'
import { getUsers } from '@/api/generated/users/users'
import {
createServiceError,
errorResult,
handleServiceError,
type ServiceCallOptions,
type ServiceResult,
shouldBubbleErrorToParent,
successResult,
} from '@/services/errorHandler'
import { unwrapApiData } from '@/services/apiResponse'
import { texts } from '@/texts'
export type ContactLink = UserContactLinkResponseDto
export type CreateContactLinkPayload = CreateUserContactLinkDto
export type UpdateContactLinkPayload = UpdateUserContactLinkDto
export const CONTACT_CHANNEL_OPTIONS = [
{ selectKey: 'instagram', name: texts.common.instagram, placeholder: texts.profile.usernamePlaceholder },
{ selectKey: 'telegram', name: texts.common.telegram, placeholder: texts.profile.usernamePlaceholder },
{ selectKey: 'twitter', name: texts.profile.channelTwitter, placeholder: texts.profile.usernamePlaceholder },
{ selectKey: 'linkedin', name: texts.profile.channelLinkedin, placeholder: texts.profile.channelLinkedinPlaceholder },
{ selectKey: 'youtube', name: texts.profile.channelYoutube, placeholder: texts.profile.channelYoutubePlaceholder },
{ selectKey: 'website', name: texts.profile.channelWebsite, placeholder: texts.profile.channelWebsitePlaceholder },
{ selectKey: 'support_phone', name: texts.profile.channelSupportPhone, placeholder: texts.profile.channelSupportPhonePlaceholder },
] as const
const usersApi = getUsers()
export const LIST_CONTACT_LINKS = async (options?: ServiceCallOptions): Promise<ServiceResult<ContactLink[]>> => {
try {
const res = await usersApi.userContactLinksControllerList()
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.profile.fetchContactLinksFailed)
}
return successResult(unwrapApiData(payload) ?? [])
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) throw normalizedError
return errorResult(normalizedError)
}
}
export const CREATE_CONTACT_LINK = async (
data: CreateContactLinkPayload,
options?: ServiceCallOptions
): Promise<ServiceResult<ContactLink>> => {
try {
const res = await usersApi.userContactLinksControllerCreate({
channel: data.channel,
value: data.value.trim(),
isPublic: data.isPublic ?? true,
displayOrder: data.displayOrder,
})
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.profile.addContactLinkFailed)
}
const link = unwrapApiData(payload)
if (!link?.id) throw createServiceError(texts.common.invalidServerResponse)
return successResult(link)
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) throw normalizedError
return errorResult(normalizedError)
}
}
export const UPDATE_CONTACT_LINK = async (
id: string,
data: UpdateContactLinkPayload,
options?: ServiceCallOptions
): Promise<ServiceResult<ContactLink>> => {
try {
const res = await usersApi.userContactLinksControllerUpdate(id, {
...(data.value !== undefined ? { value: data.value.trim() } : {}),
...(data.isPublic !== undefined ? { isPublic: data.isPublic } : {}),
...(data.displayOrder !== undefined ? { displayOrder: data.displayOrder } : {}),
})
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.profile.updateContactLinkFailed)
}
const link = unwrapApiData(payload)
if (!link?.id) throw createServiceError(texts.common.invalidServerResponse)
return successResult(link)
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) throw normalizedError
return errorResult(normalizedError)
}
}
export const DELETE_CONTACT_LINK = async (id: string, options?: ServiceCallOptions): Promise<ServiceResult<{ success: true }>> => {
try {
const res = await usersApi.userContactLinksControllerRemove(id)
const payload = res.data
if (payload && !payload.success) {
throw createServiceError(payload.message || texts.profile.deleteContactLinkFailed)
}
return successResult({ success: true })
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) throw normalizedError
return errorResult(normalizedError)
}
}