Skip to content

Commit

Permalink
add: mass reject application
Browse files Browse the repository at this point in the history
  • Loading branch information
joonatank committed May 8, 2024
1 parent 47ea088 commit a61284f
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 8 deletions.
13 changes: 9 additions & 4 deletions apps/admin-ui/src/i18n/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ const translations: ITranslations = {
errorFetchingData: ["Virhe haettaessa tietoja"],
errorFetchingApplication: ["Virhe haettaessa hakemusta"],
errorFetchingApplications: ["Virhe haettaessa hakemuksia"],
errorRejectingApplication: ["Virhe hylätessä hakemusta"],
// TODO describe what failed if you don't know why it failed
functionFailedTitle: ["Toiminto epäonnistui"],
unexpectedError: ["Odottamaton virhe"],
Expand Down Expand Up @@ -416,10 +417,14 @@ const translations: ITranslations = {
authenticatedUser: ["Tunnistautunut käyttäjä"],
emptyFilterPageName: ["hakemusta"],
rejected: ["Hylätty"],
btnReject: ["Hylkää"],
btnRevert: ["Palauta"],
btnRejectAll: ["Hylkää kaikki"],
btnRestoreAll: ["Palauta kaikki"],
btnRestore: ["Palauta koko hakemus"],
btnReject: ["Hylkää koko hakemus"],
section: {
btnReject: ["Hylkää"],
btnRestore: ["Palauta"],
btnRejectAll: ["Hylkää kaikki"],
btnRestoreAll: ["Palauta kaikki"],
},
headings: {
id: ["id"],
customer: ["Hakija"],
Expand Down
130 changes: 126 additions & 4 deletions apps/admin-ui/src/spa/applications/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
type Maybe,
type MutationRejectAllSectionOptionsArgs,
type MutationRestoreAllSectionOptionsArgs,
type MutationRejectAllApplicationOptionsArgs,
type MutationRestoreAllApplicationOptionsArgs,
} from "common/types/gql-types";
import { formatDuration } from "common/src/common/util";
import { convertWeekday, type Day } from "common/src/conversion";
Expand All @@ -54,7 +56,9 @@ import { TimeSelector } from "../TimeSelector";
import {
APPLICATION_ADMIN_QUERY,
REJECT_ALL_SECTION_OPTIONS,
REJECT_APPLICATION,
RESTORE_ALL_SECTION_OPTIONS,
RESTORE_APPLICATION,
} from "../queries";
import { getApplicantName, getApplicationStatusColor } from "@/helpers";
// TODO move
Expand Down Expand Up @@ -166,7 +170,7 @@ const StyledAccordion = styled(Accordion).attrs({

const PreCard = styled.div`
font-size: var(--fontsize-body-s);
margin-bottom: var(--spacing-m);
margin-bottom: var(--spacing-2-xs);
`;

const EventSchedules = styled.div`
Expand Down Expand Up @@ -428,7 +432,9 @@ function RejectOptionButton({
disabled={isDisabled}
data-testid={`reject-btn-${option.pk}`}
>
{isRejected ? t("Application.btnRevert") : t("Application.btnReject")}
{isRejected
? t("Application.section.btnRestore")
: t("Application.section.btnReject")}
</Button>
);
}
Expand Down Expand Up @@ -525,8 +531,8 @@ function RejectAllOptionsButton({
isLoading={isLoading}
>
{isRejected
? t("Application.btnRestoreAll")
: t("Application.btnRejectAll")}
? t("Application.section.btnRestoreAll")
: t("Application.section.btnRejectAll")}
</Button>
);
}
Expand Down Expand Up @@ -694,6 +700,115 @@ function ApplicationSectionDetails({
);
}

function RejectApplicationButton({
application,
refetch,
}: {
application: ApplicationNode;
refetch: () => Promise<ApolloQueryResult<Query>>;
}): JSX.Element {
const { t } = useTranslation();
const { notifyError } = useNotification();

const [rejectionMutation, { loading: isRejectionLoading }] = useMutation<
Query,
MutationRejectAllApplicationOptionsArgs
>(REJECT_APPLICATION);

const [restoreMutation, { loading: isRestoreLoading }] = useMutation<
Query,
MutationRestoreAllApplicationOptionsArgs
>(RESTORE_APPLICATION);

const isLoading = isRejectionLoading || isRestoreLoading;

const updateApplication = async (
pk: Maybe<number> | undefined,
shouldReject: boolean
) => {
if (pk == null) {
return;
}
if (isLoading) {
return;
}

const mutation = shouldReject ? rejectionMutation : restoreMutation;
try {
await mutation({
variables: {
input: {
pk,
},
},
});
refetch();
} catch (err) {
const mutationErrors = getValidationErrors(err);
if (mutationErrors.length > 0) {
// TODO handle other codes also
const isInvalidState = mutationErrors.find(
(e) => e.code === "CANNOT_REJECT_APPLICATION_OPTIONS"
);
if (isInvalidState) {
notifyError(t("errors.cantRejectAlreadyAllocated"));
} else {
// TODO this should show them with cleaner formatting (multiple errors)
// TODO these should be translated
const message = mutationErrors.map((e) => e.message).join(", ");
notifyError(t("errors.formValidationError", { message }));
}
} else {
notifyError(t("errors.errorRejectingApplication"));
}
}
};

const handleRejectAll = async () => {
updateApplication(application.pk, true);
};

const handleRestoreAll = async () => {
updateApplication(application.pk, false);
};

// codegen types allow undefined so have to do this for debugging
if (application.applicationSections == null) {
// eslint-disable-next-line no-console
console.warn("application.applicationSections is null", application);
}
if (application.status == null) {
// eslint-disable-next-line no-console
console.warn("application.status is null", application);
}

const isInAllocation =
application.status === ApplicationStatusChoice.InAllocation;
const hasBeenAllocated =
application.applicationSections?.some((section) =>
section.reservationUnitOptions.some(
(option) => option.allocatedTimeSlots?.length > 0
)
) ?? false;
const canReject = isInAllocation && !hasBeenAllocated;
const isRejected =
application.applicationSections?.every((section) =>
section.reservationUnitOptions.every((option) => option.rejected)
) ?? false;

return (
<Button
size="small"
variant="secondary"
theme="black"
onClick={() => (isRejected ? handleRestoreAll() : handleRejectAll())}
disabled={!canReject}
>
{isRejected ? t("Application.btnRestore") : t("Application.btnReject")}
</Button>
);
}

function ApplicationDetails({
applicationPk,
}: {
Expand Down Expand Up @@ -789,12 +904,19 @@ function ApplicationDetails({
{t("Application.applicationReceivedTime")}{" "}
{formatDate(application.lastModifiedDate, "d.M.yyyy HH:mm")}
</PreCard>
<div style={{ marginBottom: "var(--spacing-s)" }}>
<RejectApplicationButton
application={application}
refetch={refetch}
/>
</div>
<Card
theme={{
"--background-color": "var(--color-black-5)",
"--padding-horizontal": "var(--spacing-m)",
"--padding-vertical": "var(--spacing-m)",
}}
style={{ marginBottom: "var(--spacing-m)" }}
>
<CardContentContainer>
<DefinitionList>
Expand Down
20 changes: 20 additions & 0 deletions apps/admin-ui/src/spa/applications/queries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,23 @@ export const RESTORE_ALL_SECTION_OPTIONS = gql`
}
}
`;

export const REJECT_APPLICATION = gql`
mutation rejectAllApplicationOptions(
$input: RejectAllApplicationOptionsMutationInput!
) {
rejectAllApplicationOptions(input: $input) {
pk
}
}
`;

export const RESTORE_APPLICATION = gql`
mutation restoreAllApplicationOptions(
$input: RestoreAllApplicationOptionsMutationInput!
) {
restoreAllApplicationOptions(input: $input) {
pk
}
}
`;

0 comments on commit a61284f

Please sign in to comment.