Add warning when deleting options with votes

This commit is contained in:
Luke Vella 2022-04-15 18:08:28 +01:00
parent ae9d9f1083
commit 654c300430
7 changed files with 150 additions and 20 deletions

View file

@ -0,0 +1,23 @@
import ReactDOM from "react-dom";
import Modal, { ModalProps } from "./modal/modal";
export const confirmPrompt = (props: ModalProps) => {
const div = document.createElement("div");
document.body.appendChild(div);
ReactDOM.render(
<Modal
okText="Ok"
cancelText="Cancel"
{...props}
visible={true}
onOk={() => {
props.onOk?.();
document.body.removeChild(div);
}}
onCancel={() => {
document.body.removeChild(div);
}}
/>,
div,
);
};

View file

@ -0,0 +1,60 @@
import * as React from "react";
import { useList } from "react-use";
import { useRequiredContext } from "../use-required-context";
import Modal, { ModalProps } from "./modal";
export interface ModalProviderProps {
children?: React.ReactNode;
}
const ModalContext =
React.createContext<{
render: (el: ModalProps) => void;
} | null>(null);
ModalContext.displayName = "<ModalProvider />";
export const useModalContext = () => {
return useRequiredContext(ModalContext);
};
const ModalProvider: React.VoidFunctionComponent<ModalProviderProps> = ({
children,
}) => {
const [modals, { push, removeAt, updateAt }] = useList<ModalProps>([]);
const removeModalAt = (index: number) => {
updateAt(index, { ...modals[index], visible: false });
setTimeout(() => {
removeAt(index);
}, 500);
};
return (
<ModalContext.Provider
value={{
render: (props) => {
push(props);
},
}}
>
{children}
{modals.map((props, i) => (
<Modal
key={i}
visible={true}
{...props}
onOk={() => {
props.onOk?.();
removeModalAt(i);
}}
onCancel={() => {
props.onCancel?.();
removeModalAt(i);
}}
/>
))}
</ModalContext.Provider>
);
};
export default ModalProvider;

View file

@ -13,8 +13,10 @@ import { decodeDateOption, encodeDateOption } from "utils/date-time-utils";
import Dropdown, { DropdownItem } from "../dropdown";
import { PollDetailsForm } from "../forms";
import { useModal } from "../modal";
import { useModalContext } from "../modal/modal-provider";
import { usePoll } from "../use-poll";
import { useUpdatePollMutation } from "./mutations";
import { Trans } from "next-i18next";
const PollOptionsForm = React.lazy(() => import("../forms/poll-options-form"));
@ -25,6 +27,8 @@ const ManagePoll: React.VoidFunctionComponent<{
const { t } = useTranslation("app");
const poll = usePoll();
const modalContext = useModalContext();
const { mutate: updatePollMutation, isLoading: isUpdating } =
useUpdatePollMutation();
const [
@ -68,26 +72,52 @@ const ManagePoll: React.VoidFunctionComponent<{
}}
onSubmit={(data) => {
const encodedOptions = data.options.map(encodeDateOption);
const optionsToDelete = poll.options
.filter((option) => {
return !encodedOptions.includes(option.value);
})
.map((option) => option.id);
const optionsToDelete = poll.options.filter((option) => {
return !encodedOptions.includes(option.value);
});
const optionsToAdd = encodedOptions.filter(
(encodedOption) =>
!poll.options.find((o) => o.value === encodedOption),
);
updatePollMutation(
{
timeZone: data.timeZone,
optionsToDelete,
optionsToAdd,
},
{
onSuccess: () => closeChangeOptionsModal(),
},
const onOk = () => {
updatePollMutation(
{
timeZone: data.timeZone,
optionsToDelete: optionsToDelete.map(({ id }) => id),
optionsToAdd,
},
{
onSuccess: () => closeChangeOptionsModal(),
},
);
};
const optionsToDeleteThatHaveVotes = optionsToDelete.filter(
(option) => option.votes.length > 0,
);
if (optionsToDeleteThatHaveVotes.length > 0) {
modalContext.render({
title: "Are you sure?",
description: (
<Trans
t={t}
i18nKey="deletingOptionsWarning"
components={{ b: <strong /> }}
/>
),
onOk,
okButtonProps: {
type: "danger",
},
okText: "Delete",
cancelText: "Cancel",
});
} else {
onOk();
}
}}
/>
</React.Suspense>

View file

@ -0,0 +1,14 @@
import React from "react";
export const useRequiredContext = <T extends any>(
context: React.Context<T | null>,
errorMessage?: string,
) => {
const contextValue = React.useContext(context);
if (contextValue === null) {
throw new Error(
errorMessage ?? `Missing context provider: ${context.displayName}`,
);
}
return contextValue;
};

View file

@ -1,3 +1,4 @@
import ModalProvider from "@/components/modal/modal-provider";
import { NextPage } from "next";
import { appWithTranslation } from "next-i18next";
import PlausibleProvider from "next-plausible";
@ -42,9 +43,11 @@ const MyApp: NextPage<AppProps> = ({ Component, pageProps }) => {
</Head>
<CrispChat />
<Toaster />
<UserNameContext.Provider value={sessionUserName}>
<Component {...pageProps} />
</UserNameContext.Provider>
<ModalProvider>
<UserNameContext.Provider value={sessionUserName}>
<Component {...pageProps} />
</UserNameContext.Provider>
</ModalProvider>
</QueryClientProvider>
</PlausibleProvider>
);

View file

@ -9,6 +9,7 @@ import MobilePoll from "@/components/poll/mobile-poll";
import { useUpdatePollMutation } from "@/components/poll/mutations";
import NotificationsToggle from "@/components/poll/notifications-toggle";
import PollSubheader from "@/components/poll/poll-subheader";
import TruncatedLinkify from "@/components/poll/truncated-linkify";
import { UserAvatarProvider } from "@/components/poll/user-avatar";
import Popover from "@/components/popover";
import Sharing from "@/components/sharing";
@ -30,8 +31,6 @@ import { preventWidows } from "utils/prevent-widows";
import { GetPollResponse } from "../api-client/get-poll";
import { getBrowserTimeZone } from "../utils/date-time-utils";
import Custom404 from "./404";
import Linkify from "react-linkify";
import TruncatedLinkify from "@/components/poll/truncated-linkify";
const Discussion = React.lazy(() => import("@/components/discussion"));

View file

@ -41,5 +41,6 @@
"participant": "Participant",
"participantDescription": "Partial access to vote and comment on this poll.",
"unverifiedMessage": "An email has been sent to <b>{{email}}</b> with a link to verify the email address.",
"notificationsOnDescription": "An email will be sent to <b>{{email}}</b> when there is activity on this poll."
"notificationsOnDescription": "An email will be sent to <b>{{email}}</b> when there is activity on this poll.",
"deletingOptionsWarning": "You are deleting options that participants have voted for. Their votes will be also be deleted."
}