diff --git a/src/components/modals/ModalConfirm.jsx b/src/components/modals/ModalConfirm.jsx index eb8dcc23720be431d676700b70eab6d6a40c8978..f0cfe1e8c6e6e6b150a6011d6b707f76cb8db89d 100644 --- a/src/components/modals/ModalConfirm.jsx +++ b/src/components/modals/ModalConfirm.jsx @@ -8,6 +8,7 @@ import { CardBodyText, CardHeadline, } from "components/cards"; +import ErrorMessage from "components/ErrorMessage"; import Modal from "./Modal"; @@ -19,6 +20,7 @@ const ModalConfirm = ({ onCancel, onConfirm, confirming, + error, ...props }) => { return ( @@ -32,6 +34,11 @@ const ModalConfirm = ({ </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 diff --git a/src/containers/AddAnnouncementForm.jsx b/src/containers/AddAnnouncementForm.jsx index c3ef0e493d75a26c458e9ddb1c945ffb2e34dc7d..fdc732ecf6dae2a446eb04a0f0a766bc33507242 100644 --- a/src/containers/AddAnnouncementForm.jsx +++ b/src/containers/AddAnnouncementForm.jsx @@ -3,7 +3,8 @@ import classNames from "classnames"; import { addAnnouncement } from "actions/announcements"; import Button from "components/Button"; -import { useActionLoading } from "hooks"; +import ErrorMessage from "components/ErrorMessage"; +import { useActionState } from "hooks"; const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/; @@ -13,7 +14,7 @@ const AddAnnouncementForm = ({ className }) => { const [linkValid, setLinkValid] = useState(false); const [type, setType] = useState("announcement"); - const addingAnnouncement = useActionLoading(addAnnouncement, { + const [adding, addingError] = useActionState(addAnnouncement, { content: text, link, type, @@ -44,6 +45,11 @@ const AddAnnouncementForm = ({ className }) => { return ( <div className={className}> + {addingError && ( + <ErrorMessage> + Při přidávání oznámení došlo k problému: {addingError}. + </ErrorMessage> + )} <div className="grid grid-cols-1 gap-4"> <div className="form-field" @@ -112,10 +118,8 @@ const AddAnnouncementForm = ({ className }) => { onClick={onAdd} className="text-sm mt-4" hoverActive - loading={addingAnnouncement} - disabled={ - !text || (type === "voting" && !linkValid) || addingAnnouncement - } + loading={adding} + disabled={!text || (type === "voting" && !linkValid) || adding} > Přidat oznámení </Button> diff --git a/src/containers/AddPostForm.jsx b/src/containers/AddPostForm.jsx index 1818eb8fac7665252eb921049d883ba17b2e42c7..dc2d90b6acd0f569323e8af8b2a9ab2e38ed290c 100644 --- a/src/containers/AddPostForm.jsx +++ b/src/containers/AddPostForm.jsx @@ -2,12 +2,17 @@ import React, { useState } from "react"; import { addPost, addProposal } from "actions/posts"; import Button from "components/Button"; -import { useActionLoading } from "hooks"; +import ErrorMessage from "components/ErrorMessage"; +import { useActionState } from "hooks"; const AddPostForm = ({ className }) => { const [text, setText] = useState(""); - const addingPost = useActionLoading(addPost, { content: text }); - const addingProposal = useActionLoading(addPost, { content: text }); + const [addingPost, addingPostError] = useActionState(addPost, { + content: text, + }); + const [addingProposal, addingProposalError] = useActionState(addPost, { + content: text, + }); const onTextInput = (evt) => { setText(evt.target.value); @@ -47,6 +52,16 @@ const AddPostForm = ({ className }) => { return ( <div className={className}> + {addingPostError && ( + <ErrorMessage> + Při přidávání příspěvku došlo k problému: {addingPostError}. + </ErrorMessage> + )} + {addingProposalError && ( + <ErrorMessage> + Při přidávání příspěvku došlo k problému: {addingProposalError}. + </ErrorMessage> + )} <div className="form-field"> <div className="form-field__wrapper form-field__wrapper--shadowed"> <textarea diff --git a/src/containers/AnnoucementsContainer.jsx b/src/containers/AnnoucementsContainer.jsx index 0c052d980650f37cf9d28681fa2f6e5bd8431968..e8d6c04fe61cd8532f0e07eba521e1e6b5874bd2 100644 --- a/src/containers/AnnoucementsContainer.jsx +++ b/src/containers/AnnoucementsContainer.jsx @@ -10,7 +10,7 @@ import AnnouncementList from "components/annoucements/AnnouncementList"; import { CardBody } from "components/cards"; import ErrorMessage from "components/ErrorMessage"; import ModalConfirm from "components/modals/ModalConfirm"; -import { useActionLoading, useItemActionConfirm } from "hooks"; +import { useActionState, useItemActionConfirm } from "hooks"; import { AnnouncementStore, AuthStore } from "stores"; const AnnoucementsContainer = () => { @@ -24,7 +24,7 @@ const AnnoucementsContainer = () => { onDeleteCancel, ] = useItemActionConfirm(deleteAnnouncement); - const deletingAnnouncement = useActionLoading( + const [deletingAnnouncement, deletingAnnouncementError] = useActionState( deleteAnnouncement, itemToDelete ); @@ -79,6 +79,7 @@ const AnnoucementsContainer = () => { onConfirm={onDeleteConfirm} onCancel={onDeleteCancel} confirming={deletingAnnouncement} + error={deletingAnnouncementError} title="Opravdu smazat?" yesActionLabel="Smazat" > diff --git a/src/containers/PostsContainer.jsx b/src/containers/PostsContainer.jsx index 17c5f53b1b8ddd4e7e35b3482f8a0075dbed994c..936a3c6137aec03b21999f5682ae0c613baf9ddb 100644 --- a/src/containers/PostsContainer.jsx +++ b/src/containers/PostsContainer.jsx @@ -17,7 +17,7 @@ import ErrorMessage from "components/ErrorMessage"; import ModalConfirm from "components/modals/ModalConfirm"; import PostEditModal from "components/posts/PostEditModal"; import PostList from "components/posts/PostList"; -import { useActionLoading, useItemActionConfirm } from "hooks"; +import { useActionState, useItemActionConfirm } from "hooks"; import { AuthStore, PostStore } from "stores"; const PostsContainer = ({ className }) => { @@ -68,15 +68,24 @@ const PostsContainer = ({ className }) => { (state) => state.filters.flags === "archived" ); - const banningUser = useActionLoading(ban, userToBan); - const hidingPost = useActionLoading(hide, postToHide); - const announcingProposal = useActionLoading(announceProposal, postToAnnounce); - const acceptingProposal = useActionLoading(acceptProposal, postToAccept); - const rejectingProposal = useActionLoading(rejectProposal, postToReject); - const rejectingProposalByChairman = useActionLoading( - rejectProposalByChairman, - postToRejectByChairman + const [banningUser, banningUserError] = useActionState(ban, userToBan); + const [hidingPost, hidingPostError] = useActionState(hide, postToHide); + const [announcingProposal, announcingProposalError] = useActionState( + announceProposal, + postToAnnounce ); + const [acceptingProposal, acceptingProposalError] = useActionState( + acceptProposal, + postToAccept + ); + const [rejectingProposal, rejectingProposalError] = useActionState( + rejectProposal, + postToReject + ); + const [ + rejectingProposalByChairman, + rejectingProposalByChairmanError, + ] = useActionState(rejectProposalByChairman, postToRejectByChairman); const { 2: loadResult } = loadPosts.useWatch(); @@ -146,6 +155,7 @@ const PostsContainer = ({ className }) => { onConfirm={onBanUserConfirm} onCancel={onBanUserCancel} confirming={banningUser} + error={banningUserError} title={`Zablokovat uživatele ${userToBan ? userToBan.name : null}?`} yesActionLabel="Zablokovat" > @@ -157,6 +167,7 @@ const PostsContainer = ({ className }) => { onConfirm={onPostHideConfirm} onCancel={onPostHideCancel} confirming={hidingPost} + error={hidingPostError} title="Skrýt příspěvek?" yesActionLabel="Potvrdit" > @@ -167,6 +178,7 @@ const PostsContainer = ({ className }) => { onConfirm={onAnnounceConfirm} onCancel={onAnnounceCancel} confirming={announcingProposal} + error={announcingProposalError} title="Vyhlásit procedurální návrh?" yesActionLabel="Vyhlásit návrh" > @@ -177,6 +189,7 @@ const PostsContainer = ({ className }) => { onConfirm={onAcceptConfirm} onCancel={onAcceptCancel} confirming={acceptingProposal} + error={acceptingProposalError} title="Schválit procedurální návrh?" yesActionLabel="Schválit návrh" > @@ -187,6 +200,7 @@ const PostsContainer = ({ className }) => { onConfirm={onRejectConfirm} onCancel={onRejectCancel} confirming={rejectingProposal} + error={rejectingProposalError} title="Zamítnout procedurální návrh?" yesActionLabel="Zamítnout návrh" > @@ -197,6 +211,7 @@ const PostsContainer = ({ className }) => { onConfirm={onRejectByChairmanConfirm} onCancel={onRejectByChairmanCancel} confirming={rejectingProposalByChairman} + error={rejectingProposalByChairmanError} title="Zamítnout procedurální návrh předsedajícícm?" yesActionLabel="Zamítnout návrh předsedajícím" > diff --git a/src/hooks.js b/src/hooks.js index 1b4e5b50c1b45512e4fbb4c2acb608a289ac9e28..7573be066410e2f17db501f4950614af9c577834 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -40,7 +40,7 @@ export const useActionConfirm = (actionFn, actionArgs) => { return [showConfirm, setShowConfirm, onActionConfirm, onActionCancel]; }; -export const useActionLoading = (actionFn, actionArgs) => { - const { 0: started, 1: finished } = actionFn.useWatch(actionArgs); - return started && !finished; +export const useActionState = (actionFn, actionArgs) => { + const { 0: started, 1: finished, 2: result } = actionFn.useWatch(actionArgs); + return [started && !finished, result.error ? result.message : null]; }; diff --git a/src/pages/Program.jsx b/src/pages/Program.jsx index 3c51379781e499d4e3f59ce9fb71e840c9a78c49..fadf116e8971aeb1f29116255c924d2ea759844c 100644 --- a/src/pages/Program.jsx +++ b/src/pages/Program.jsx @@ -7,7 +7,7 @@ import { activateProgramPoint } from "actions/program"; import Button from "components/Button"; import Chip from "components/Chip"; import ModalConfirm from "components/modals/ModalConfirm"; -import { useActionLoading, useItemActionConfirm } from "hooks"; +import { useActionState, useItemActionConfirm } from "hooks"; import { AuthStore, ProgramStore } from "stores"; const Schedule = () => { @@ -20,7 +20,10 @@ const Schedule = () => { onActivateCancel, ] = useItemActionConfirm(activateProgramPoint); - const activating = useActionLoading(activateProgramPoint, entryToActivate); + const [activating, activationError] = useActionState( + activateProgramPoint, + entryToActivate + ); return ( <article className="container container--wide py-8 lg:py-24"> @@ -87,6 +90,7 @@ const Schedule = () => { onConfirm={onActivateConfirm} onCancel={onActivateCancel} confirming={activating} + error={activationError} title="Aktivovat bod programu?" yesActionLabel="Aktivovat" >