Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ #561 - feat: filter select options on co reviewer assignme… #569

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions backend/src/openarchiefbeheer/destruction/api/serializers.py
svenvandescheur marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,19 @@ def validate(self, attrs):
.assignees.filter(role=ListRole.co_reviewer)
.count()
)
if (
current_number_co_reviewers
+ len(attrs.get("add", []))
- len(attrs.get("remove", []))
> MAX_NUMBER_CO_REVIEWERS
):

# (New) number of co reviewers depends on whether a partial update has been mode or a full update is provided.
number_of_co_reviewers = (
(
current_number_co_reviewers
+ len(attrs.get("add", []))
- len(attrs.get("remove", []))
)
if self.partial
else len(attrs.get("add", []))
)

if number_of_co_reviewers > MAX_NUMBER_CO_REVIEWERS:
raise ValidationError(
_("The maximum number of allowed co-reviewers is %(max_co_reviewers)s.")
% {"max_co_reviewers": MAX_NUMBER_CO_REVIEWERS}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import {
Button,
FormField,
P,
SerializedFormData,
Solid,
useAlert,
useFormDialog,
validateForm,
} from "@maykin-ui/admin-ui";
import { useMemo } from "react";
import { useEffect, useMemo, useState } from "react";
import { useNavigation, useRevalidator } from "react-router-dom";

import {
Expand Down Expand Up @@ -57,11 +59,47 @@ export function DestructionListReviewer({
const assignedCoReviewers = useDestructionListCoReviewers(destructionList);
const user = useWhoAmI();

const [
assignCoReviewersFormValuesState,
setAssignCoReviewersFormValuesState,
] = useState<SerializedFormData>({});

/**
* Pre-populates `assignCoReviewersFormValuesState` with the current
* co-reviewers.
*/
useEffect(() => {
const coReviewerPks = assignedCoReviewers.map((r) => r.user.pk.toString());

setAssignCoReviewersFormValuesState({
...assignCoReviewersFormValuesState,
coReviewer: coReviewerPks,
});
}, [assignedCoReviewers.map((r) => r.user.pk).join()]);

const [assignCoReviewerModalOpenState, setAssignCoReviewerModalOpenState] =
useState(false);

/**
* Updates `assignCoReviewersFormValuesState` with `values`.
* This allows `field` to use filtered options based on it's value.
* @param values
*/
const handleValidate = (values: SerializedFormData) => {
// Ignore first run.
if (!Object.keys(values).length) {
return;
}
setAssignCoReviewersFormValuesState(values);
return validateForm(values, fields);
};

/**
* Gets called when the change is confirmed.
*/
const handleSubmit = (data: DestructionListReviewerFormType) => {
const { coReviewer, reviewer, comment } = data;
setAssignCoReviewerModalOpenState(false);

const promises: Promise<unknown>[] = [];

Expand Down Expand Up @@ -122,6 +160,10 @@ export function DestructionListReviewer({
(assignee) => assignee.role === "main_reviewer",
);

/**
* The fields to show in the form dialog, can be either for (re)assigning a
* reviewer or for (re)assigning co-reviewers.
*/
const fields = useMemo<FormField[]>(() => {
if (!user) return [];

Expand All @@ -145,29 +187,52 @@ export function DestructionListReviewer({
required: true,
};

const activeCoReviewers =
(assignCoReviewersFormValuesState.coReviewer as string[]) || [];

if (canReviewDestructionList(user, destructionList)) {
const coReviewerFields = new Array(5)
.fill({
label: "Medebeoordelaar",
name: "coReviewer",
type: "string",
options: coReviewers.map((user) => ({
label: formatUser(user),
value: user.pk,
})),
required: false,
})
.map((f, i) => ({
const coReviewerFields = new Array(5).fill(null).map((f, i) => {
return {
...f,
label: `Medebeoordelaar ${1 + i}`,
value: assignedCoReviewers[i]?.user.pk,
}));
name: "coReviewer",
required: false,
type: "string",
value: activeCoReviewers?.[i],
options: coReviewers
// Don't show the co-reviewer as option if:
// - The co-reviewer is already selected AND
// - The co-reviewer is not selected as value for the current
// field.
.filter((c) => {
const selectedIndex = activeCoReviewers.indexOf(c.pk.toString());
if (selectedIndex < 0 || selectedIndex === i) {
return true;
}
return false;
})
.map((user) => ({
label: formatUser(user),
value: user.pk,
})),
};
});

return [...coReviewerFields, comment];
}
return [reviewer, comment];
}, [user, destructionList, reviewers, assignedCoReviewers]);
}, [
user,
destructionList,
reviewers,
assignedCoReviewers,
assignCoReviewersFormValuesState,
]);

/**
* Contains the co-reviewers that are assigned to the destruction list as
* items for the AttributeTable.
*/
const coReviewerItems = useMemo(
() =>
assignedCoReviewers.reduce((acc, coReviewer, i) => {
Expand All @@ -194,6 +259,29 @@ export function DestructionListReviewer({
[assignedCoReviewers, coReviews],
);

/**
* Opens a dialog to assign a co-reviewer and updates it when `fields` change.
*/
useEffect(() => {
formDialog(
"Beoordelaar toewijzen",
null,
fields,
"Toewijzen",
"Annuleren",
handleSubmit,
undefined,
{
allowClose: true,
open: assignCoReviewerModalOpenState,
onClose: () => setAssignCoReviewerModalOpenState(false),
},
{
validate: handleValidate,
},
);
}, [assignCoReviewerModalOpenState, JSON.stringify(fields)]);

return (
<>
{reviewer && (
Expand All @@ -217,18 +305,9 @@ export function DestructionListReviewer({
}
size="xs"
variant="secondary"
onClick={(e) => {
formDialog(
"Beoordelaar toewijzen",
null,
fields,
"Toewijzen",
"Annuleren",
handleSubmit,
undefined,
{ allowClose: true },
);
}}
onClick={() =>
setAssignCoReviewerModalOpenState(true)
}
>
<Solid.PencilIcon />
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export const ReviewerCanExcludeZaak: Story = {
name: "Uitzonderen",
});
const exclude = excludes[0];
await userEvent.click(exclude);
await userEvent.click(exclude, { delay: 60 });

const reason = await canvas.findByLabelText("Reden");
const submitExclude = await canvas.findByRole("button", {
Expand Down
Loading