feat: 콘텐츠 승인 로직 작업

This commit is contained in:
2025-04-18 18:19:36 +09:00
parent fa183f9d92
commit 8927fb88b4
5 changed files with 64 additions and 31 deletions
@@ -76,6 +76,15 @@ export class ContentsApiService implements ContentsService {
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}/${contsSn}/${contsVer}/approveRequest`,
);
}
public async approveContents(
contsSn: number,
contsVer: number,
): Promise<void> {
await client.put(
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}/${contsSn}/${contsVer}/approve`,
);
}
}
export const ContentsApi = new ContentsApiService();
@@ -96,4 +96,5 @@ export interface ContentsService {
): Promise<void>;
deleteContents(contsSn: number, contsVer: number): Promise<void>;
requestApprove(contsSn: number, contsVer: number): Promise<void>;
approveContents(contsSn: number, contsVer: number): Promise<void>;
}
@@ -49,6 +49,7 @@ const Editor = ({ ...props }: IAllProps) => {
}}
init={{
...baseConfig.init,
disabled: props.disabled,
images_upload_handler: imagesUploadHandler,
file_picker_callback: filePickerCallback,
}}
@@ -27,7 +27,8 @@ const Editor = dynamic(
interface ContentsFormProps {
form: UseFormReturn<ContentsFormData>;
contsSn?: number;
disabledFields?: Partial<Record<keyof ContentsFormData, boolean>>;
isEditable?: boolean;
isContsTtlDisabled?: boolean;
handleSave: (data: ContentsFormData) => Promise<void>;
handleDelete?: () => Promise<void>;
}
@@ -35,7 +36,8 @@ interface ContentsFormProps {
function ContentsForm({
form,
contsSn,
disabledFields,
isEditable,
isContsTtlDisabled,
...props
}: ContentsFormProps) {
const { data: cclCode } = useQuery({
@@ -71,7 +73,7 @@ function ContentsForm({
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="콘텐츠 제목을 입력하십시오."
disabled={disabledFields?.contsTtl}
disabled={!isEditable || isContsTtlDisabled}
/>
</FormControl>
</FormItem>
@@ -91,6 +93,7 @@ function ContentsForm({
<Editor
value={field.value ?? ""}
onEditorChange={field.onChange}
disabled={!isEditable}
/>
</FormControl>
</FormItem>
@@ -112,6 +115,7 @@ function ContentsForm({
name="cclCd"
value={field.value ?? ""}
onValueChange={field.onChange}
disabled={!isEditable}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="" id="ccl-none" />
@@ -162,6 +166,7 @@ function ContentsForm({
name="koglCd"
value={field.value ?? ""}
onValueChange={field.onChange}
disabled={!isEditable}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="" id="koglNone" />
@@ -214,6 +219,7 @@ function ContentsForm({
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="태그를 입력하십시오."
disabled={!isEditable}
/>
</FormControl>
</FormItem>
@@ -235,6 +241,7 @@ function ContentsForm({
name="useYn"
value={field.value ?? ""}
onValueChange={field.onChange}
disabled={!isEditable}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="Y" id="useY" />
+43 -28
View File
@@ -24,16 +24,12 @@ import {
} from "@common/components/ui";
function ModifyPage({ sequential }: { sequential: string }) {
const contsSn = Number(sequential);
const { confirm } = useConfirm();
const queryClient = useQueryClient();
const { setMessage: alert } = useAlertStore((state) => state);
const [contentsVersion, setContentsVersion] = useState(0);
const [disabledFields, setDisabledFields] = useState<
Partial<Record<keyof ContentsFormData, boolean>>
>({});
const contsSn = Number(sequential);
const [contsVer, setContsVer] = useState(0);
const { data: contentsVersionList } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn],
@@ -46,14 +42,15 @@ function ModifyPage({ sequential }: { sequential: string }) {
});
const { data: contentsDetail } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contentsVersion],
queryFn: () => ContentsApi.getLatestContentsInfo(contsSn, contentsVersion),
enabled: !!contentsVersion,
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsVer],
queryFn: () => ContentsApi.getLatestContentsInfo(contsSn, contsVer),
enabled: !!contsVer,
});
const { mutateAsync: createContents } = useMutation({
mutationFn: (data: ContentsFormData) => ContentsApi.createContents(data),
onSuccess: () => {
setContsVer(0);
alert("수정되었습니다.");
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn],
@@ -63,15 +60,16 @@ function ModifyPage({ sequential }: { sequential: string }) {
const { mutateAsync: updateContents } = useMutation({
mutationFn: (data: ContentsFormData) =>
ContentsApi.updateContents(contsSn, contentsVersion, data),
ContentsApi.updateContents(contsSn, contsVer, data),
onSuccess: () => {
alert("수정되었습니다.");
},
});
const { mutateAsync: deleteContents } = useMutation({
mutationFn: () => ContentsApi.deleteContents(contsSn, contentsVersion),
mutationFn: () => ContentsApi.deleteContents(contsSn, contsVer),
onSuccess: () => {
setContsVer(0);
alert("삭제되었습니다.");
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn],
@@ -80,11 +78,21 @@ function ModifyPage({ sequential }: { sequential: string }) {
});
const { mutateAsync: requestApprove } = useMutation({
mutationFn: () => ContentsApi.requestApprove(contsSn, contentsVersion),
mutationFn: () => ContentsApi.requestApprove(contsSn, contsVer),
onSuccess: () => {
alert("승인 요청이 완료되었습니다.");
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contentsVersion],
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS],
});
},
});
const { mutateAsync: approveContents } = useMutation({
mutationFn: () => ContentsApi.approveContents(contsSn, contsVer),
onSuccess: () => {
alert("승인되었습니다.");
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS],
});
},
});
@@ -94,12 +102,9 @@ function ModifyPage({ sequential }: { sequential: string }) {
values: contentsDetail,
});
const isDraftContents =
contentsDetail?.aprvCd === CONTENTS_APPROVE_STATUS.DRAFT;
const isRequestedContents =
contentsDetail?.aprvCd === CONTENTS_APPROVE_STATUS.REQUESTED;
const isApprovedContents =
contentsDetail?.aprvCd === CONTENTS_APPROVE_STATUS.APPROVED;
const approveStatus = contentsDetail?.aprvCd;
const isContsTtlDisabled =
(contentsVersionList?.content?.[0]?.contsVer ?? 0) > 1;
const onRequestApprove = async () => {
const message =
@@ -107,14 +112,19 @@ function ModifyPage({ sequential }: { sequential: string }) {
if (await confirm(message)) requestApprove();
};
const onApprove = async () => {
const message = "승인하시겠습니까?";
if (await confirm(message)) approveContents();
};
useEffect(() => {
if (contsVer) return;
if (!contentsVersionList?.content?.length) return;
const latestContent = contentsVersionList?.content[0];
if (!latestContent) return;
if (latestContent?.contsVer > 1) setDisabledFields({ contsTtl: true });
setContentsVersion(latestContent?.contsVer ?? 0);
setContsVer(latestContent?.contsVer ?? 0);
}, [contentsVersionList?.content]);
return (
@@ -123,9 +133,9 @@ function ModifyPage({ sequential }: { sequential: string }) {
<h3 className="text-2xl font-medium text-label">템플릿 수정</h3>
<div className="flex items-center gap-2">
<Select
value={contentsVersion.toString()}
value={contsVer.toString()}
onValueChange={(value) => {
setContentsVersion(Number(value));
setContsVer(Number(value));
}}
>
<SelectTrigger className="w-[28rem]">
@@ -142,12 +152,12 @@ function ModifyPage({ sequential }: { sequential: string }) {
))}
</SelectContent>
</Select>
{isDraftContents && (
{approveStatus === CONTENTS_APPROVE_STATUS.DRAFT && (
<Button onClick={onRequestApprove}>승인요청</Button>
)}
{isRequestedContents && (
{approveStatus === CONTENTS_APPROVE_STATUS.REQUESTED && (
<>
<Button>승인</Button>
<Button onClick={onApprove}>승인</Button>
<Button color="gray">반려</Button>
</>
)}
@@ -156,8 +166,13 @@ function ModifyPage({ sequential }: { sequential: string }) {
<ContentsForm
form={form}
contsSn={contsSn}
disabledFields={disabledFields}
handleSave={isApprovedContents ? createContents : updateContents}
isEditable={approveStatus !== CONTENTS_APPROVE_STATUS.REQUESTED}
isContsTtlDisabled={isContsTtlDisabled}
handleSave={
approveStatus === CONTENTS_APPROVE_STATUS.APPROVED
? createContents
: updateContents
}
handleDelete={deleteContents}
/>
</div>