Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './updateGraduationBatchApproval';
export * from './updateGraduationApproval';
export * from './updateGraduationDisapproval';
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { patch } from '~/shared/api';
import { END_POINT } from '~/shared/constants';

export type UpdateGraduationApprovalParams = {
graduationUserId: number;
submissionId: number;
};

type UpdateGraduationApprovalResponse = {
id: number;
};

export const updateGraduationApproval = async ({
graduationUserId,
submissionId,
}: UpdateGraduationApprovalParams) => {
const response = await patch<UpdateGraduationApprovalResponse>({
request: END_POINT.ADMIN.GRADUATION_USER_APPROVE(
graduationUserId,
submissionId,
),
});

return response.data;
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { patch } from '~/shared/api';
import { END_POINT } from '~/shared/constants';

export type UpdateGraduationDisapprovalParams = {
graduationUserId: number;
submissionId: number;
};

type UpdateGraduationDisapprovalResponse = {
id: number;
};

export const updateGraduationDisapproval = async ({
graduationUserId,
submissionId,
}: UpdateGraduationDisapprovalParams) => {
const response = await patch<UpdateGraduationDisapprovalResponse>({
request: END_POINT.ADMIN.GRADUATION_USER_DISAPPROVE(
graduationUserId,
submissionId,
),
});

return response.data;
};
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './useGraduationApproval';
export * from './useGraduationBatchApproval';
export * from './useGraduationBatchApproval';
export * from './useGraduationBatchDisapproval';
Original file line number Diff line number Diff line change
@@ -1,22 +1,54 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';

import { graduationUsersKeys } from '~/shared/queries';
import { graduationUsersKeys, studentKeys } from '~/shared/queries';

import { updateGraduationBatchApproval } from '../api';
import {
type UpdateGraduationApprovalParams,
updateGraduationApproval,
} from '../api';

type Options = {
onSuccess?: () => void | Promise<void>;
};

type UpdateGraduationBatchApprovalResult = {
approvedIds: number[];
successCount: number;
failureCount: number;
};

export function useGraduationBatchApproval({ onSuccess }: Options = {}) {
const queryClient = useQueryClient();

const mutation = useMutation({
mutationFn: updateGraduationBatchApproval,
onSuccess: async () => {
const mutation = useMutation<
UpdateGraduationBatchApprovalResult,
Error,
UpdateGraduationApprovalParams[]
>({
mutationFn: async targets => {
const settled = await Promise.allSettled(
targets.map(target => updateGraduationApproval(target)),
);
const approvedIds = settled.flatMap((result, index) =>
result.status === 'fulfilled' ? [targets[index].graduationUserId] : [],
);

return {
approvedIds,
successCount: approvedIds.length,
failureCount: targets.length - approvedIds.length,
};
},
onSuccess: async result => {
if (result.successCount === 0) {
return;
}
await queryClient.invalidateQueries({
queryKey: graduationUsersKeys.all,
});
await queryClient.invalidateQueries({
queryKey: studentKeys.details(),
});
await onSuccess?.();
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';

import { graduationUsersKeys, studentKeys } from '~/shared/queries';

import {
type UpdateGraduationDisapprovalParams,
updateGraduationDisapproval,
} from '../api';

type Options = {
onSuccess?: () => void | Promise<void>;
};

type UpdateGraduationBatchDisapprovalResult = {
disapprovedIds: number[];
successCount: number;
failureCount: number;
};

export function useGraduationBatchDisapproval({ onSuccess }: Options = {}) {
const queryClient = useQueryClient();

const mutation = useMutation<
UpdateGraduationBatchDisapprovalResult,
Error,
UpdateGraduationDisapprovalParams[]
>({
mutationFn: async targets => {
const settled = await Promise.allSettled(
targets.map(target => updateGraduationDisapproval(target)),
);
const disapprovedIds = settled.flatMap((result, index) =>
result.status === 'fulfilled'
? [targets[index].graduationUserId]
: [],
);

return {
disapprovedIds,
successCount: disapprovedIds.length,
failureCount: targets.length - disapprovedIds.length,
};
},
onSuccess: async result => {
if (result.successCount === 0) {
return;
}
await queryClient.invalidateQueries({
queryKey: graduationUsersKeys.all,
});
await queryClient.invalidateQueries({
queryKey: studentKeys.details(),
});
await onSuccess?.();
},
});

return {
disapproveGraduationUsers: mutation.mutateAsync,
mutation,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useGraduationApproval';
export * from './useGraduationDisapproval';
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { createElement } from 'react';

import { useToast } from '~/shared/hooks';

import { useGraduationBatchApproval } from './useGraduationBatchApproval';

import { useGraduationBatchApproval } from '~/admin/entities/graduation-approval/model';
import {
APPROVE_ALERT,
APPROVE_CONFIRM_TITLE,
APPROVE_OK_TEXT,
APPROVE_CANCEL_TEXT,
APPROVE_CONFIRM_TITLE,
APPROVE_EMPTY,
APPROVE_FAILED,
APPROVE_NOTHING,
APPROVE_OK_TEXT,
APPROVE_REASON_ALREADY_APPROVED,
APPROVE_REASON_FAILED,
APPROVE_REASON_NOT_SUBMITTED,
Expand All @@ -19,13 +19,14 @@ import {
APPROVE_RESULT_NOT_APPROVED,
APPROVE_RESULT_TITLE,
APPROVE_SUCCESS,
} from '~/admin/shared/ui/Toolbar/toolbarTexts';
} from '~/admin/shared/constants/actionTexts';

type UseApproveGraduationUsersProps<T> = {
items: T[];
selectedIds: number[];
getId: (item: T) => number;
getLabel: (item: T) => string;
getSubmissionId: (item: T) => number | null;
status: {
isSubmitted: (item: T) => boolean;
isApproved: (item: T) => boolean;
Expand All @@ -34,7 +35,6 @@ type UseApproveGraduationUsersProps<T> = {
};

type NotApprovedDetail = {
id: number;
label: string;
reason: string;
};
Expand All @@ -44,6 +44,7 @@ export function useGraduationApproval<T>({
selectedIds,
getId,
getLabel,
getSubmissionId,
status,
onSuccess,
}: UseApproveGraduationUsersProps<T>) {
Expand Down Expand Up @@ -81,17 +82,14 @@ export function useGraduationApproval<T>({
failed: T[] = [],
) => [
...notSubmitted.map(user => ({
id: getId(user),
label: getLabel(user),
reason: APPROVE_REASON_NOT_SUBMITTED,
})),
...alreadyApproved.map(user => ({
id: getId(user),
label: getLabel(user),
reason: APPROVE_REASON_ALREADY_APPROVED,
})),
...failed.map(user => ({
id: getId(user),
label: getLabel(user),
reason: APPROVE_REASON_FAILED,
})),
Expand All @@ -107,6 +105,7 @@ export function useGraduationApproval<T>({
const notSubmitted: T[] = [];
const pending: T[] = [];
const alreadyApproved: T[] = [];
const invalidTargets: T[] = [];

selected.forEach(item => {
const submitted = status.isSubmitted(item);
Expand All @@ -120,15 +119,24 @@ export function useGraduationApproval<T>({
alreadyApproved.push(item);
return;
}
if (getSubmissionId(item) == null) {
invalidTargets.push(item);
return;
}
pending.push(item);
});

if (pending.length === 0) {
info({
title: APPROVE_RESULT_TITLE,
centered: true,
content: buildResultContent(
[],
buildNotApprovedDetails(notSubmitted, alreadyApproved),
buildNotApprovedDetails(
notSubmitted,
alreadyApproved,
invalidTargets,
),
),
});
return;
Expand All @@ -139,11 +147,22 @@ export function useGraduationApproval<T>({
content: APPROVE_ALERT,
okText: APPROVE_OK_TEXT,
cancelText: APPROVE_CANCEL_TEXT,
centered: true,
onOk: async () => {
try {
const result = await approveGraduationUsers(
pending.map(item => getId(item)),
);
const approvalTargets = pending.flatMap(item => {
const submissionId = getSubmissionId(item);
return submissionId == null
? []
: [
{
graduationUserId: getId(item),
submissionId,
},
];
});

const result = await approveGraduationUsers(approvalTargets);
const approvedIdSet = new Set(result.approvedIds);
const approved = pending.filter(item =>
approvedIdSet.has(getId(item)),
Expand All @@ -154,19 +173,27 @@ export function useGraduationApproval<T>({

info({
title: APPROVE_RESULT_TITLE,
centered: true,
content: buildResultContent(
approved,
buildNotApprovedDetails(notSubmitted, alreadyApproved, failed),
buildNotApprovedDetails(
notSubmitted,
alreadyApproved,
[...invalidTargets, ...failed],
),
),
});

if (approved.length > 0) {
if (result.successCount > 0) {
toast.success(APPROVE_SUCCESS);
} else if (result.failureCount > 0) {
toast.error(APPROVE_FAILED);
} else {
toast.warning(APPROVE_NOTHING);
}
} catch {
/* 실패 시 MutationCache 전역 토스트로 안내 */
return;
}
},
});
Expand Down
Loading
Loading