Skip to content

Commit 6011c5b

Browse files
committed
add actions button
1 parent 6f4606a commit 6011c5b

File tree

3 files changed

+180
-15
lines changed

3 files changed

+180
-15
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { ComboboxItem, ComboboxList } from "@/shared/components/ui/combobox";
2+
import { forwardRef } from "react";
3+
4+
type ActionItem = { value: string; name: string };
5+
6+
const actionsList: Record<string, ActionItem> = {
7+
approve: {
8+
value: "approve",
9+
name: "Approve",
10+
},
11+
"cancel-approve": {
12+
value: "cancel-approve",
13+
name: "Cancel approval",
14+
},
15+
reject: {
16+
value: "reject",
17+
name: "Reject",
18+
},
19+
"cancel-reject": {
20+
value: "cancel-reject",
21+
name: "Cancel reject",
22+
},
23+
merge: {
24+
value: "merge",
25+
name: "Merge",
26+
},
27+
close: {
28+
value: "close",
29+
name: "Close",
30+
},
31+
};
32+
33+
export interface ActionComboboxListProps {
34+
onSelect: (value: string) => void;
35+
value?: string | null;
36+
}
37+
38+
export const ActionComboboxList = forwardRef<HTMLDivElement, ActionComboboxListProps>(
39+
({ value, onSelect }, ref) => {
40+
return (
41+
<ComboboxList ref={ref}>
42+
{Object.entries(actionsList).map(([, { name, value: actionValue }]) => {
43+
return (
44+
<ComboboxItem
45+
key={actionValue}
46+
value={actionValue}
47+
selectedValue={value}
48+
onSelect={() => onSelect(actionValue)}
49+
className="cursor-pointer"
50+
>
51+
<span className="truncate">{name}</span>
52+
</ComboboxItem>
53+
);
54+
})}
55+
</ComboboxList>
56+
);
57+
}
58+
);
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import graphqlClient from "@/shared/api/graphql/graphqlClientApollo";
2+
import { Button, ButtonProps } from "@/shared/components/buttons/button-primitive";
3+
import { ALERT_TYPES, Alert } from "@/shared/components/ui/alert";
4+
import { Combobox, ComboboxContent } from "@/shared/components/ui/combobox";
5+
import { PopoverTrigger } from "@/shared/components/ui/popover";
6+
import { inputStyle } from "@/shared/components/ui/style";
7+
import { classNames } from "@/shared/utils/common";
8+
import { capitalizeFirstLetter } from "@/shared/utils/string";
9+
import { Icon } from "@iconify-icon/react";
10+
import { useState } from "react";
11+
import { toast } from "react-toastify";
12+
import { useUpdateReview } from "../../domain/update-review.mutation";
13+
import { ActionComboboxList } from "./actions-combobox-list";
14+
15+
interface PcActionButtonProps extends ButtonProps {
16+
proposedChangeId: string;
17+
approvers: Array<any>;
18+
state: "closed" | "open" | "merged";
19+
}
20+
21+
export const PcActionButton = ({ proposedChangeId }: PcActionButtonProps) => {
22+
const [open, setOpen] = useState(false);
23+
const [action, setAction] = useState("approve");
24+
25+
const { mutateAsync: updateObjectMutateAsync, isPending: isObjectUpdatePending } =
26+
useUpdateReview({
27+
onSuccess: async () => {
28+
await graphqlClient.reFetchObservableQueries();
29+
toast(<Alert type={ALERT_TYPES.SUCCESS} message="Proposed change updated!" />);
30+
},
31+
onError: async () => {
32+
await graphqlClient.reFetchObservableQueries();
33+
toast(
34+
<Alert
35+
type={ALERT_TYPES.SUCCESS}
36+
message="An error occured while updating the proposed changes"
37+
/>
38+
);
39+
},
40+
});
41+
42+
const { mutateAsync: updateApprovalMutateAsync, isPending: isApprovalUpdatePending } =
43+
useUpdateReview({
44+
onSuccess: async () => {
45+
await graphqlClient.reFetchObservableQueries();
46+
toast(<Alert type={ALERT_TYPES.SUCCESS} message="Proposed change approved!" />);
47+
},
48+
onError: async () => {
49+
await graphqlClient.reFetchObservableQueries();
50+
toast(
51+
<Alert
52+
type={ALERT_TYPES.SUCCESS}
53+
message="An error occured while approving the proposed changes"
54+
/>
55+
);
56+
},
57+
});
58+
59+
const handleAction = () => {
60+
switch (action) {
61+
case "cancel-reject":
62+
case "reject":
63+
case "cancel-approve":
64+
case "approve": {
65+
return updateApprovalMutateAsync({
66+
proposedChangeId,
67+
decision: action,
68+
});
69+
}
70+
case "merge":
71+
case "close": {
72+
return updateObjectMutateAsync({
73+
proposedChangeId,
74+
decision: action,
75+
});
76+
}
77+
}
78+
};
79+
80+
const isLoading = isApprovalUpdatePending || isObjectUpdatePending;
81+
82+
return (
83+
<Combobox open={open} onOpenChange={setOpen}>
84+
<PopoverTrigger asChild>
85+
<div className={classNames(inputStyle, "flex p-0 border-0 ")}>
86+
<Button
87+
className="grow flex flex-wrap gap-2 h-full rounded-r-none border-r-white"
88+
onClick={handleAction}
89+
variant={"primary"}
90+
isLoading={isLoading}
91+
disabled={isLoading}
92+
>
93+
{capitalizeFirstLetter(action)}
94+
</Button>
95+
96+
<Button
97+
className="h-full rounded-l-none border-l-0"
98+
variant={"primary"}
99+
size={"sm"}
100+
onClick={() => {
101+
setOpen(true);
102+
}}
103+
disabled={isLoading}
104+
>
105+
<Icon icon="mdi:unfold-more-horizontal" />
106+
</Button>
107+
</div>
108+
</PopoverTrigger>
109+
<ComboboxContent fitTriggerWidth={false}>
110+
<ActionComboboxList
111+
value={action}
112+
onSelect={(action) => {
113+
setOpen(false);
114+
setAction(action);
115+
}}
116+
/>
117+
</ComboboxContent>
118+
</Combobox>
119+
);
120+
};

frontend/app/src/entities/proposed-changes/ui/proposed-change-details.tsx

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { PROPOSED_CHANGES_OBJECT, TASK_OBJECT } from "@/config/constants";
22
import { proposedChangedState } from "@/entities/proposed-changes/stores/proposedChanges.atom";
3-
import { PcApproveButton } from "@/entities/proposed-changes/ui/action-button/pc-approve-button";
4-
import { PcCloseButton } from "@/entities/proposed-changes/ui/action-button/pc-close-button";
5-
import { PcMergeButton } from "@/entities/proposed-changes/ui/action-button/pc-merge-button";
3+
import { PcActionButton } from "@/entities/proposed-changes/ui/action-button/pc-actions-button";
64
import { Conversations } from "@/entities/proposed-changes/ui/conversations";
75
import { ProposedChangeEditTrigger } from "@/entities/proposed-changes/ui/proposed-change-edit-trigger";
86
import { getProposedChangesStateBadgeType } from "@/entities/proposed-changes/ui/proposed-changes";
@@ -118,23 +116,12 @@ export const ProposedChangeDetails = ({ className, ...props }: HTMLAttributes<HT
118116
name: "Actions",
119117
value: (
120118
<div className="flex flex-wrap gap-2">
121-
<PcApproveButton
119+
<PcActionButton
122120
approvers={approvers}
123121
proposedChangeId={proposedChangeId!}
124122
state={state}
125123
disabled={!permission.update.isAllowed}
126124
/>
127-
<PcMergeButton
128-
proposedChangeId={proposedChangeId!}
129-
state={state}
130-
sourceBranch={proposedChangesDetails?.source_branch?.value}
131-
disabled={!permission.update.isAllowed || (checkData && checkData[TASK_OBJECT].count)}
132-
/>
133-
<PcCloseButton
134-
proposedChangeId={proposedChangeId!}
135-
state={state}
136-
disabled={!permission.update.isAllowed}
137-
/>
138125
</div>
139126
),
140127
},

0 commit comments

Comments
 (0)