1
0
forked from baron/baron-sso

adminfront: React Query Optimistic Updates 적용하여 세부 권한 매트릭스 UI 0ms 즉각 반응하도록 속도 고도화 완료

This commit is contained in:
2026-06-10 17:30:12 +09:00
parent b4f80a36b0
commit 679c1656f4
2 changed files with 112 additions and 12 deletions

View File

@@ -75,24 +75,74 @@ export function TenantFineGrainedPermissionsPage() {
const addSystemRelationMutation = useMutation({
mutationFn: (payload: { userId: string; relation: string }) =>
addSystemRelation(payload.userId, payload.relation),
onMutate: async (newRelation) => {
await queryClient.cancelQueries({ queryKey: ["system-relations"] });
const previousRelations = queryClient.getQueryData<TenantRelation[]>(["system-relations"]);
queryClient.setQueryData<TenantRelation[]>(["system-relations"], (old) => {
if (!old) return [];
return old.map((user) => {
if (user.userId === newRelation.userId) {
return {
...user,
relations: user.relations.includes(newRelation.relation)
? user.relations
: [...user.relations, newRelation.relation],
};
}
return user;
});
});
return { previousRelations };
},
onError: (err: AxiosError<{ error?: string }>, _, context) => {
if (context?.previousRelations) {
queryClient.setQueryData(["system-relations"], context.previousRelations);
}
toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다."));
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["system-relations"] });
toast.success(t("msg.admin.system.relations.add_success", "시스템 메뉴 권한이 추가되었습니다."));
},
onError: (err: AxiosError<{ error?: string }>) => {
toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다."));
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["system-relations"] });
},
});
const removeSystemRelationMutation = useMutation({
mutationFn: (payload: { userId: string; relation: string }) =>
removeSystemRelation(payload.userId, payload.relation),
onMutate: async (targetRelation) => {
await queryClient.cancelQueries({ queryKey: ["system-relations"] });
const previousRelations = queryClient.getQueryData<TenantRelation[]>(["system-relations"]);
queryClient.setQueryData<TenantRelation[]>(["system-relations"], (old) => {
if (!old) return [];
return old.map((user) => {
if (user.userId === targetRelation.userId) {
return {
...user,
relations: user.relations.filter((r) => r !== targetRelation.relation),
};
}
return user;
});
});
return { previousRelations };
},
onError: (err: AxiosError<{ error?: string }>, _, context) => {
if (context?.previousRelations) {
queryClient.setQueryData(["system-relations"], context.previousRelations);
}
toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다."));
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["system-relations"] });
toast.success(t("msg.admin.system.relations.remove_success", "시스템 메뉴 권한이 회수되었습니다."));
},
onError: (err: AxiosError<{ error?: string }>) => {
toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다."));
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["system-relations"] });
},
});

View File

@@ -68,24 +68,74 @@ export function TenantFineGrainedPermissionsTab({ tenantIdProp }: TenantFineGrai
const addRelationMutation = useMutation({
mutationFn: (payload: { userId: string; relation: string }) =>
addTenantRelation(tenantId, payload.userId, payload.relation),
onMutate: async (newRelation) => {
await queryClient.cancelQueries({ queryKey: ["tenant-relations", tenantId] });
const previousRelations = queryClient.getQueryData<TenantRelation[]>(["tenant-relations", tenantId]);
queryClient.setQueryData<TenantRelation[]>(["tenant-relations", tenantId], (old) => {
if (!old) return [];
return old.map((user) => {
if (user.userId === newRelation.userId) {
return {
...user,
relations: user.relations.includes(newRelation.relation)
? user.relations
: [...user.relations, newRelation.relation],
};
}
return user;
});
});
return { previousRelations };
},
onError: (err: AxiosError<{ error?: string }>, _, context) => {
if (context?.previousRelations) {
queryClient.setQueryData(["tenant-relations", tenantId], context.previousRelations);
}
toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다."));
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["tenant-relations", tenantId] });
toast.success(t("msg.admin.tenants.relations.add_success", "세부 권한이 추가되었습니다."));
},
onError: (err: AxiosError<{ error?: string }>) => {
toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다."));
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["tenant-relations", tenantId] });
},
});
const removeRelationMutation = useMutation({
mutationFn: (payload: { userId: string; relation: string }) =>
removeTenantRelation(tenantId, payload.userId, payload.relation),
onMutate: async (targetRelation) => {
await queryClient.cancelQueries({ queryKey: ["tenant-relations", tenantId] });
const previousRelations = queryClient.getQueryData<TenantRelation[]>(["tenant-relations", tenantId]);
queryClient.setQueryData<TenantRelation[]>(["tenant-relations", tenantId], (old) => {
if (!old) return [];
return old.map((user) => {
if (user.userId === targetRelation.userId) {
return {
...user,
relations: user.relations.filter((r) => r !== targetRelation.relation),
};
}
return user;
});
});
return { previousRelations };
},
onError: (err: AxiosError<{ error?: string }>, _, context) => {
if (context?.previousRelations) {
queryClient.setQueryData(["tenant-relations", tenantId], context.previousRelations);
}
toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다."));
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["tenant-relations", tenantId] });
toast.success(t("msg.admin.tenants.relations.remove_success", "세부 권한이 회수되었습니다."));
},
onError: (err: AxiosError<{ error?: string }>) => {
toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다."));
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["tenant-relations", tenantId] });
},
});