File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
'use server';
import { revalidatePath } from 'next/cache';
import { verifySession } from '@/lib/auth/dal';
import { BackendRequestError } from '@/lib/http/backend-fetch';
import {
createAdminMember,
deleteAdminMember,
isAdminLoginIdTaken,
updateAdminMember,
} from '@/lib/data/repositories/admin-member-repository';
import { ADMIN_MENU_OPTIONS } from '@/lib/domain/admin-member';
import {
formatPhoneNumber,
validateAdminLoginId,
validateAdminMemberCreate,
validateAdminMemberUpdate,
type AdminMemberEditableValues,
type AdminMemberFormState,
} from '@/lib/domain/admin-member-form';
import { ADMIN_MEMBERS_PATH } from '@/lib/domain/admin-member-query';
const INVALID_REQUEST_MESSAGE = '요청이 올바르지 않습니다.';
const SELF_DELETE_MESSAGE = '현재 로그인한 본인 계정은 삭제할 수 없습니다.';
const DUPLICATE_LOGIN_ID_MESSAGE = '이미 사용 중인 ID입니다.';
const LOGIN_ID_CHECK_FAILED_MESSAGE =
'중복 확인에 실패했습니다. 잠시 후 다시 시도해 주세요.';
function readString(formData: FormData, key: string): string {
const value = formData.get(key);
return typeof value === 'string' ? value : '';
}
function readMenuCodes(formData: FormData): string[] {
return formData
.getAll('menuCodes')
.filter((value): value is string => typeof value === 'string')
.filter((value) =>
ADMIN_MENU_OPTIONS.some((option) => option.value === value)
);
}
function readEditableValues(formData: FormData): AdminMemberEditableValues {
const phoneNumber = formatPhoneNumber(readString(formData, 'phoneNumber'));
return {
password: readString(formData, 'password'),
phoneNumber: phoneNumber ?? '',
email: readString(formData, 'email'),
roleCode: readString(formData, 'roleCode'),
menuCodes: readMenuCodes(formData),
};
}
async function runWrite(
write: () => Promise<void>
): Promise<AdminMemberFormState | null> {
try {
await write();
return null;
} catch (error) {
if (error instanceof BackendRequestError) {
return { status: 'error', message: error.message };
}
throw error;
}
}
export async function createAdminMemberAction(
_prevState: AdminMemberFormState,
formData: FormData
): Promise<AdminMemberFormState> {
await verifySession();
const validation = validateAdminMemberCreate({
...readEditableValues(formData),
name: readString(formData, 'name'),
loginId: readString(formData, 'loginId'),
});
if (!validation.ok) {
return { status: 'error', errors: validation.errors };
}
const { name, loginId, password, phoneNumber, email, roleCode } =
validation.values;
if (await isAdminLoginIdTaken(loginId)) {
return { status: 'error', errors: { loginId: DUPLICATE_LOGIN_ID_MESSAGE } };
}
const failure = await runWrite(() =>
createAdminMember({ name, loginId, password, phoneNumber, email, roleCode })
);
if (failure) {
return failure;
}
revalidatePath(ADMIN_MEMBERS_PATH);
return { status: 'success' };
}
export async function updateAdminMemberAction(
_prevState: AdminMemberFormState,
formData: FormData
): Promise<AdminMemberFormState> {
await verifySession();
const id = readString(formData, 'id');
if (!id) {
return { status: 'error', message: INVALID_REQUEST_MESSAGE };
}
const validation = validateAdminMemberUpdate(readEditableValues(formData));
if (!validation.ok) {
return { status: 'error', errors: validation.errors };
}
const { password, phoneNumber, email, roleCode } = validation.values;
const failure = await runWrite(() =>
updateAdminMember(id, {
password,
phoneNumber,
email,
roleCode,
isLocked: readString(formData, 'isLocked') === 'true',
})
);
if (failure) {
return failure;
}
revalidatePath(ADMIN_MEMBERS_PATH);
return { status: 'success' };
}
export async function deleteAdminMemberAction(
id: string
): Promise<AdminMemberFormState> {
const admin = await verifySession();
if (!id) {
return { status: 'error', message: INVALID_REQUEST_MESSAGE };
}
if (id === admin.id) {
return { status: 'error', message: SELF_DELETE_MESSAGE };
}
await deleteAdminMember(id);
revalidatePath(ADMIN_MEMBERS_PATH);
return { status: 'success' };
}
export type LoginIdCheckResult =
| { status: 'idle' }
| { status: 'available'; loginId: string }
| { status: 'unavailable'; message: string }
| { status: 'failed'; message: string };
export async function checkAdminLoginId(
loginId: string
): Promise<LoginIdCheckResult> {
await verifySession();
const normalized = loginId.trim();
const formatError = validateAdminLoginId(normalized);
if (formatError) {
return { status: 'unavailable', message: formatError };
}
try {
if (await isAdminLoginIdTaken(normalized)) {
return { status: 'unavailable', message: DUPLICATE_LOGIN_ID_MESSAGE };
}
} catch (error) {
if (error instanceof BackendRequestError) {
return { status: 'failed', message: error.message };
}
return { status: 'failed', message: LOGIN_ID_CHECK_FAILED_MESSAGE };
}
return { status: 'available', loginId: normalized };
}