Skip to content
Snippets Groups Projects
Commit e13b26d7 authored by xaralis's avatar xaralis
Browse files

feat: post can optionally be archived when rejecting it

parent 0c423ed7
Branches
No related tags found
No related merge requests found
Pipeline #1944 passed
......@@ -208,9 +208,10 @@ export const archive = createAsyncAction(
* @param {CF2021.ProposalPost} proposal
* @param {CF2021.ProposalPostState} state
*/
const updateProposalState = async (proposal, state) => {
const updateProposalState = async (proposal, state, additionalPayload) => {
const body = JSON.stringify({
state: postsStateMappingRev[state],
...(additionalPayload || {}),
});
await fetch(`/posts/${proposal.id}`, {
method: "PUT",
......@@ -277,16 +278,16 @@ export const rejectProposal = createAsyncAction(
/**
* @param {CF2021.ProposalPost} proposal
*/
(proposal) => {
return updateProposalState(proposal, "rejected");
({ proposal, archive }) => {
return updateProposalState(proposal, "rejected", { is_archived: archive });
},
{
shortCircuitHook: ({ args }) => {
if (args.type !== "procedure-proposal") {
if (args.proposal.type !== "procedure-proposal") {
return errorResult();
}
if (args.state !== "announced") {
if (args.proposal.state !== "announced") {
return errorResult();
}
......@@ -302,16 +303,18 @@ export const rejectProposalByChairman = createAsyncAction(
/**
* @param {CF2021.ProposalPost} proposal
*/
(proposal) => {
return updateProposalState(proposal, "rejected-by-chairman");
({ proposal, archive }) => {
return updateProposalState(proposal, "rejected-by-chairman", {
is_archived: archive,
});
},
{
shortCircuitHook: ({ args }) => {
if (args.type !== "procedure-proposal") {
if (args.proposal.type !== "procedure-proposal") {
return errorResult();
}
if (!["pending", "announced"].includes(args.state)) {
if (!["pending", "announced"].includes(args.proposal.state)) {
return errorResult();
}
......
import React from "react";
import Button from "components/Button";
import {
Card,
CardActions,
CardBody,
CardBodyText,
CardHeadline,
} from "components/cards";
import ErrorMessage from "components/ErrorMessage";
import Modal from "./Modal";
import ModalWithActions from "./ModalWithActions";
const ModalConfirm = ({
title,
......@@ -23,44 +15,39 @@ const ModalConfirm = ({
error,
...props
}) => {
const actions = (
<>
<Button
hoverActive
color="blue-300"
className="text-sm"
onClick={onConfirm}
loading={confirming}
>
{yesActionLabel}
</Button>
<Button
hoverActive
color="grey-125"
className="text-sm"
onClick={onCancel}
>
{cancelActionLabel}
</Button>
</>
);
return (
<Modal containerClassName="max-w-md" onRequestClose={onCancel} {...props}>
<Card>
<CardBody>
<div className="flex items-center justify-between mb-4">
<CardHeadline>{title}</CardHeadline>
<button onClick={onCancel} type="button">
<i className="ico--cross"></i>
</button>
</div>
<CardBodyText>{children}</CardBodyText>
{error && (
<ErrorMessage className="mt-2">
Při provádění akce došlo k problému: {error}
</ErrorMessage>
)}
</CardBody>
<CardActions right className="space-x-1">
<Button
hoverActive
color="blue-300"
className="text-sm"
onClick={onConfirm}
loading={confirming}
>
{yesActionLabel}
</Button>
<Button
hoverActive
color="red-600"
className="text-sm"
onClick={onCancel}
>
{cancelActionLabel}
</Button>
</CardActions>
</Card>
</Modal>
<ModalWithActions
onClose={onCancel}
title={title}
error={error}
actions={actions}
containerClassName="max-w-md"
{...props}
>
{children}
</ModalWithActions>
);
};
......
import React from "react";
import {
Card,
CardActions,
CardBody,
CardBodyText,
CardHeadline,
} from "components/cards";
import ErrorMessage from "components/ErrorMessage";
import Modal from "./Modal";
const ModalConfirm = ({
title,
children,
actions,
error,
onClose,
...props
}) => {
return (
<Modal onRequestClose={onClose} {...props}>
<Card>
<CardBody>
<div className="flex items-center justify-between mb-4">
<CardHeadline>{title}</CardHeadline>
<button onClick={onClose} type="button">
<i className="ico--cross"></i>
</button>
</div>
<CardBodyText>{children}</CardBodyText>
{error && (
<ErrorMessage className="mt-2">
Při provádění akce došlo k problému: {error}
</ErrorMessage>
)}
</CardBody>
<CardActions right className="space-x-1">
{actions}
</CardActions>
</Card>
</Modal>
);
};
export default React.memo(ModalConfirm);
import React from "react";
import Button from "components/Button";
import {
Card,
CardActions,
CardBody,
CardBodyText,
CardHeadline,
} from "components/cards";
import ErrorMessage from "components/ErrorMessage";
import Modal from "./Modal";
const RejectPostModalConfirm = ({
title,
children,
yesActionLabel = "OK",
yesAndArchiveActionLabel = "OK",
cancelActionLabel = "Zrušit",
onCancel,
onConfirm,
onConfirmAndArchive,
confirming,
error,
...props
}) => {
return (
<Modal containerClassName="max-w-md" onRequestClose={onCancel} {...props}>
<Card>
<CardBody>
<div className="flex items-center justify-between mb-4">
<CardHeadline>{title}</CardHeadline>
<button onClick={onCancel} type="button">
<i className="ico--cross"></i>
</button>
</div>
<CardBodyText>{children}</CardBodyText>
{error && (
<ErrorMessage className="mt-2">
Při provádění akce došlo k problému: {error}
</ErrorMessage>
)}
</CardBody>
<CardActions right className="space-x-1">
<Button
hoverActive
color="blue-300"
className="text-sm"
onClick={onConfirm}
loading={confirming}
>
{yesActionLabel}
</Button>
<Button
hoverActive
color="blue-300"
className="text-sm"
onClick={onConfirm}
loading={confirming}
>
{yesAndArchiveActionLabel}
</Button>
<Button
hoverActive
color="red-600"
className="text-sm"
onClick={onCancel}
>
{cancelActionLabel}
</Button>
</CardActions>
</Card>
</Modal>
);
};
export default React.memo(RejectPostModalConfirm);
......@@ -15,8 +15,10 @@ import {
rejectProposalByChairman,
} from "actions/posts";
import { ban, unban } from "actions/users";
import Button from "components/Button";
import ErrorMessage from "components/ErrorMessage";
import ModalConfirm from "components/modals/ModalConfirm";
import ModalWithActions from "components/modals/ModalWithActions";
import PostEditModal from "components/posts/PostEditModal";
import PostList from "components/posts/PostList";
import { useActionState, useItemActionConfirm } from "hooks";
......@@ -68,13 +70,19 @@ const PostsContainer = ({ className, showAddPostCta }) => {
setPostToReject,
onRejectConfirm,
onRejectCancel,
] = useItemActionConfirm(rejectProposal);
] = useItemActionConfirm(rejectProposal, (item, archive) => ({
proposal: item,
archive,
}));
const [
postToRejectByChairman,
setPostToRejectByChairman,
onRejectByChairmanConfirm,
onRejectByChairmanCancel,
] = useItemActionConfirm(rejectProposalByChairman);
] = useItemActionConfirm(rejectProposalByChairman, (item, archive) => ({
proposal: item,
archive,
}));
const { isAuthenticated, user } = AuthStore.useState();
const { window, items } = PostStore.useState((state) =>
......@@ -104,12 +112,29 @@ const PostsContainer = ({ className, showAddPostCta }) => {
);
const [rejectingProposal, rejectingProposalError] = useActionState(
rejectProposal,
postToReject
{
proposal: postToReject,
archive: false,
}
);
const [
rejectingAndArchivingProposal,
rejectingAndArchivingProposalError,
] = useActionState(rejectProposal, { proposal: postToReject, archive: true });
const [
rejectingProposalByChairman,
rejectingProposalByChairmanError,
] = useActionState(rejectProposalByChairman, postToRejectByChairman);
] = useActionState(rejectProposalByChairman, {
proposal: postToRejectByChairman,
archive: false,
});
const [
rejectingProposalByChairmanAndArchiving,
rejectingProposalByChairmanAndArchivingError,
] = useActionState(rejectProposalByChairman, {
proposal: postToRejectByChairman,
archive: true,
});
const { 2: loadResult } = loadPosts.useWatch();
......@@ -130,7 +155,7 @@ const PostsContainer = ({ className, showAddPostCta }) => {
setConfirmingEdit(false);
}
},
[postToEdit, setPostToEdit]
[postToEdit, setPostToEdit, setConfirmingEdit]
);
const cancelEdit = useCallback(() => {
......@@ -253,29 +278,88 @@ const PostsContainer = ({ className, showAddPostCta }) => {
>
Procedurální návrh bude <strong>schválen</strong>. Opravdu to chcete?
</ModalConfirm>
<ModalConfirm
<ModalWithActions
isOpen={!!postToReject}
onConfirm={onRejectConfirm}
onCancel={onRejectCancel}
confirming={rejectingProposal}
error={rejectingProposalError}
error={rejectingProposalError || rejectingAndArchivingProposalError}
title="Zamítnout procedurální návrh?"
yesActionLabel="Zamítnout návrh"
containerClassName="max-w-lg"
actions={
<>
<Button
hoverActive
color="blue-300"
className="text-sm"
onClick={() => onRejectConfirm(false)}
loading={rejectingProposal}
>
Zamítnout
</Button>
<Button
hoverActive
color="blue-300"
className="text-sm"
onClick={() => onRejectConfirm(true)}
loading={rejectingAndArchivingProposal}
>
Zamítnout a archivovat
</Button>
<Button
hoverActive
color="grey-125"
className="text-sm"
onClick={onRejectCancel}
>
Zrušit
</Button>
</>
}
>
Procedurální návrh bude <strong>zamítnut</strong>. Opravdu to chcete?
</ModalConfirm>
<ModalConfirm
</ModalWithActions>
<ModalWithActions
isOpen={!!postToRejectByChairman}
onConfirm={onRejectByChairmanConfirm}
onCancel={onRejectByChairmanCancel}
confirming={rejectingProposalByChairman}
error={rejectingProposalByChairmanError}
error={
rejectingProposalByChairmanError ||
rejectingProposalByChairmanAndArchivingError
}
title="Zamítnout procedurální návrh předsedajícícm?"
yesActionLabel="Zamítnout návrh předsedajícím"
containerClassName="max-w-lg"
actions={
<>
<Button
hoverActive
color="blue-300"
className="text-sm"
onClick={() => onRejectByChairmanConfirm(false)}
loading={rejectingProposalByChairman}
>
Zamítnout
</Button>
<Button
hoverActive
color="blue-300"
className="text-sm"
onClick={() => onRejectByChairmanConfirm(true)}
loading={rejectingProposalByChairmanAndArchiving}
>
Zamítnout a archivovat
</Button>
<Button
hoverActive
color="grey-125"
className="text-sm"
onClick={onRejectCancel}
>
Zrušit
</Button>
</>
}
>
Procedurální návrh bude <strong>zamítnut předsedajícím</strong>. Opravdu
to chcete?
</ModalConfirm>
</ModalWithActions>
{postToEdit && (
<PostEditModal
isOpen={true}
......
import { useCallback, useState } from "react";
export const useItemActionConfirm = (actionFn) => {
const baseActionParamsBuilder = (item, args) => {
return item;
};
export const useItemActionConfirm = (actionFn, actionParamsBuilder = null) => {
const [item, setItem] = useState(null);
const onActionConfirm = useCallback(async () => {
if (item) {
const result = await actionFn.run(item);
const onActionConfirm = useCallback(
async (args) => {
if (item) {
const result = await actionFn.run(
(actionParamsBuilder || baseActionParamsBuilder)(item, args)
);
if (!result.error) {
setItem(null);
if (!result.error) {
setItem(null);
}
}
}
}, [item, setItem, actionFn]);
},
[item, setItem, actionFn, actionParamsBuilder]
);
const onActionCancel = useCallback(() => {
setItem(null);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment