-
Notifications
You must be signed in to change notification settings - Fork 204
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
feat(ui): enhance merchant monitoring report status component #3061
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -105,6 +105,10 @@ | |
"error": "Error occurred while creating merchant checks.", | ||
"is_example": "Please contact Ballerine at [email protected] for access to this feature." | ||
}, | ||
"business_report_status_update": { | ||
"success": "Merchant check status updated successfully.", | ||
"error": "Error occurred while updating merchant check status." | ||
}, | ||
"note_created": { | ||
"success": "Note added successfully.", | ||
"error": "Error occurred while adding note." | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,205 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
import { z } from 'zod'; | ||||||||||||||||||||||||||||||||||||||||||||||
import React, { ComponentProps } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { zodResolver } from '@hookform/resolvers/zod'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { SubmitHandler, useForm } from 'react-hook-form'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { MERCHANT_REPORT_STATUSES_MAP } from '@ballerine/common'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||
DialogDescription, | ||||||||||||||||||||||||||||||||||||||||||||||
DialogFooter, | ||||||||||||||||||||||||||||||||||||||||||||||
DialogHeader, | ||||||||||||||||||||||||||||||||||||||||||||||
DialogTitle, | ||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenu, | ||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuContent, | ||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuItem, | ||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuTrigger, | ||||||||||||||||||||||||||||||||||||||||||||||
TextArea, | ||||||||||||||||||||||||||||||||||||||||||||||
} from '@ballerine/ui'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
import { Form } from '@/common/components/organisms/Form/Form'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { Button } from '@/common/components/atoms/Button/Button'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { FormItem } from '@/common/components/organisms/Form/Form.Item'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { FormField } from '@/common/components/organisms/Form/Form.Field'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { FormLabel } from '@/common/components/organisms/Form/Form.Label'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { FormControl } from '@/common/components/organisms/Form/Form.Control'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { FormMessage } from '@/common/components/organisms/Form/Form.Message'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { useUpdateReportStatusMutation } from '@/pages/MerchantMonitoring/components/MerchantMonitoringReportStatus/hooks/useUpdateReportStatusMutation/useUpdateReportStatusMutation'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||
MerchantMonitoringStatusBadge, | ||||||||||||||||||||||||||||||||||||||||||||||
statusToData, | ||||||||||||||||||||||||||||||||||||||||||||||
} from '@/pages/MerchantMonitoring/components/MerchantMonitoringReportStatus/MerchantMonitoringStatusBadge'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { useToggle } from '@/common/hooks/useToggle/useToggle'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { useCreateNoteMutation } from '@/domains/notes/hooks/mutations/useCreateNoteMutation/useCreateNoteMutation'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { DialogDropdownItem } from '@/pages/MerchantMonitoringBusinessReport/MerchantMonitoringBusinessReport.page'; | ||||||||||||||||||||||||||||||||||||||||||||||
import { MerchantMonitoringStatusButton } from './MerchantMonitoringReportStatusButton'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const selectableStatuses = [ | ||||||||||||||||||||||||||||||||||||||||||||||
MERCHANT_REPORT_STATUSES_MAP['pending-review'], | ||||||||||||||||||||||||||||||||||||||||||||||
MERCHANT_REPORT_STATUSES_MAP['under-review'], | ||||||||||||||||||||||||||||||||||||||||||||||
MERCHANT_REPORT_STATUSES_MAP.completed, | ||||||||||||||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const MerchantMonitoringCompletedStatusFormSchema = z.object({ | ||||||||||||||||||||||||||||||||||||||||||||||
text: z.string().optional(), | ||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
export const MerchantMonitoringReportStatus = ({ | ||||||||||||||||||||||||||||||||||||||||||||||
status, | ||||||||||||||||||||||||||||||||||||||||||||||
reportId, | ||||||||||||||||||||||||||||||||||||||||||||||
businessId, | ||||||||||||||||||||||||||||||||||||||||||||||
onClick, | ||||||||||||||||||||||||||||||||||||||||||||||
}: { | ||||||||||||||||||||||||||||||||||||||||||||||
reportId?: string; | ||||||||||||||||||||||||||||||||||||||||||||||
businessId?: string; | ||||||||||||||||||||||||||||||||||||||||||||||
status?: keyof typeof statusToData; | ||||||||||||||||||||||||||||||||||||||||||||||
onClick?: ComponentProps<typeof Button>['onClick']; | ||||||||||||||||||||||||||||||||||||||||||||||
}) => { | ||||||||||||||||||||||||||||||||||||||||||||||
const { mutateAsync: mutateCreateNote } = useCreateNoteMutation({ disableToast: true }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const { mutate: mutateUpdateReportStatus, isLoading } = useUpdateReportStatusMutation(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const formDefaultValues = { | ||||||||||||||||||||||||||||||||||||||||||||||
text: '', | ||||||||||||||||||||||||||||||||||||||||||||||
} satisfies z.infer<typeof MerchantMonitoringCompletedStatusFormSchema>; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const form = useForm({ | ||||||||||||||||||||||||||||||||||||||||||||||
resolver: zodResolver(MerchantMonitoringCompletedStatusFormSchema), | ||||||||||||||||||||||||||||||||||||||||||||||
defaultValues: formDefaultValues, | ||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const [isCompleteReviewModalOpen, setIsCompleteReviewModalOpen] = useToggle(false); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const onSubmit: SubmitHandler< | ||||||||||||||||||||||||||||||||||||||||||||||
z.infer<typeof MerchantMonitoringCompletedStatusFormSchema> | ||||||||||||||||||||||||||||||||||||||||||||||
> = async ({ text }) => { | ||||||||||||||||||||||||||||||||||||||||||||||
mutateUpdateReportStatus({ reportId, status: MERCHANT_REPORT_STATUSES_MAP.completed, text }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const content = `Status changed to 'Review Completed' ${text ? ` with details: ${text}` : ''}`; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
void mutateCreateNote({ | ||||||||||||||||||||||||||||||||||||||||||||||
content, | ||||||||||||||||||||||||||||||||||||||||||||||
entityId: businessId ?? '', | ||||||||||||||||||||||||||||||||||||||||||||||
entityType: 'Business', | ||||||||||||||||||||||||||||||||||||||||||||||
noteableId: reportId ?? '', | ||||||||||||||||||||||||||||||||||||||||||||||
noteableType: 'Report', | ||||||||||||||||||||||||||||||||||||||||||||||
parentNoteId: null, | ||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+78
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for mutateCreateNote. The - void mutateCreateNote({
+ try {
+ await mutateCreateNote({
content,
entityId: businessId ?? '',
entityType: 'Business',
noteableId: reportId ?? '',
noteableType: 'Report',
parentNoteId: null,
- });
+ });
+ } catch (error) {
+ // Handle error appropriately
+ console.error('Failed to create note:', error);
+ // Consider showing an error toast or rolling back the status update
+ } 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
setIsCompleteReviewModalOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||||
form.reset(); | ||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
if (!status || !reportId) { | ||||||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenu> | ||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenuTrigger | ||||||||||||||||||||||||||||||||||||||||||||||
className={`flex items-center focus-visible:outline-none`} | ||||||||||||||||||||||||||||||||||||||||||||||
disabled={ | ||||||||||||||||||||||||||||||||||||||||||||||
isLoading || | ||||||||||||||||||||||||||||||||||||||||||||||
[ | ||||||||||||||||||||||||||||||||||||||||||||||
MERCHANT_REPORT_STATUSES_MAP['in-progress'], | ||||||||||||||||||||||||||||||||||||||||||||||
MERCHANT_REPORT_STATUSES_MAP['quality-control'], | ||||||||||||||||||||||||||||||||||||||||||||||
MERCHANT_REPORT_STATUSES_MAP['completed'], | ||||||||||||||||||||||||||||||||||||||||||||||
].includes(status) | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||
<MerchantMonitoringStatusBadge disabled={isLoading} status={status} /> | ||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenuTrigger> | ||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenuContent | ||||||||||||||||||||||||||||||||||||||||||||||
align="start" | ||||||||||||||||||||||||||||||||||||||||||||||
className={`space-y-2 p-4`} | ||||||||||||||||||||||||||||||||||||||||||||||
onEscapeKeyDown={e => { | ||||||||||||||||||||||||||||||||||||||||||||||
if (isCompleteReviewModalOpen) { | ||||||||||||||||||||||||||||||||||||||||||||||
e.preventDefault(); | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
setIsCompleteReviewModalOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||
{selectableStatuses.map(selectableStatus => | ||||||||||||||||||||||||||||||||||||||||||||||
selectableStatus === MERCHANT_REPORT_STATUSES_MAP.completed ? ( | ||||||||||||||||||||||||||||||||||||||||||||||
<DialogDropdownItem | ||||||||||||||||||||||||||||||||||||||||||||||
key={selectableStatus} | ||||||||||||||||||||||||||||||||||||||||||||||
className="flex w-full cursor-pointer items-center p-0" | ||||||||||||||||||||||||||||||||||||||||||||||
triggerChildren={ | ||||||||||||||||||||||||||||||||||||||||||||||
<MerchantMonitoringStatusButton disabled={isLoading} status={selectableStatus} /> | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
open={isCompleteReviewModalOpen} | ||||||||||||||||||||||||||||||||||||||||||||||
onOpenChange={() => { | ||||||||||||||||||||||||||||||||||||||||||||||
const activeElement = document.activeElement as HTMLElement; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
if (activeElement) { | ||||||||||||||||||||||||||||||||||||||||||||||
activeElement.blur(); | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
setIsCompleteReviewModalOpen(); | ||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||
<DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||
<DialogTitle>Confirm Review Completion</DialogTitle> | ||||||||||||||||||||||||||||||||||||||||||||||
<DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||
Please provide any relevant details or findings regarding the review. This can | ||||||||||||||||||||||||||||||||||||||||||||||
include notes or conclusions drawn from the investigation. | ||||||||||||||||||||||||||||||||||||||||||||||
</DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||
</DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
<Form {...form}> | ||||||||||||||||||||||||||||||||||||||||||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> | ||||||||||||||||||||||||||||||||||||||||||||||
<FormField | ||||||||||||||||||||||||||||||||||||||||||||||
name="text" | ||||||||||||||||||||||||||||||||||||||||||||||
control={form.control} | ||||||||||||||||||||||||||||||||||||||||||||||
render={({ field }) => ( | ||||||||||||||||||||||||||||||||||||||||||||||
<FormItem> | ||||||||||||||||||||||||||||||||||||||||||||||
<FormLabel>Additional details</FormLabel> | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
<FormControl> | ||||||||||||||||||||||||||||||||||||||||||||||
<TextArea {...field} /> | ||||||||||||||||||||||||||||||||||||||||||||||
</FormControl> | ||||||||||||||||||||||||||||||||||||||||||||||
<FormMessage /> | ||||||||||||||||||||||||||||||||||||||||||||||
</FormItem> | ||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
<DialogFooter className="mt-6 flex justify-end space-x-4"> | ||||||||||||||||||||||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||||||||||||||||||||||
type="button" | ||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||
setIsCompleteReviewModalOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||
variant="ghost" | ||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||
Cancel | ||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||
<Button type="submit" variant="destructive"> | ||||||||||||||||||||||||||||||||||||||||||||||
Complete Review | ||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||
</DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||
</form> | ||||||||||||||||||||||||||||||||||||||||||||||
</Form> | ||||||||||||||||||||||||||||||||||||||||||||||
</DialogDropdownItem> | ||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenuItem | ||||||||||||||||||||||||||||||||||||||||||||||
key={selectableStatus} | ||||||||||||||||||||||||||||||||||||||||||||||
className="flex w-full cursor-pointer items-center p-0" | ||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||
<MerchantMonitoringStatusButton | ||||||||||||||||||||||||||||||||||||||||||||||
status={selectableStatus} | ||||||||||||||||||||||||||||||||||||||||||||||
disabled={selectableStatus === status || isLoading} | ||||||||||||||||||||||||||||||||||||||||||||||
onClick={e => { | ||||||||||||||||||||||||||||||||||||||||||||||
e.preventDefault(); | ||||||||||||||||||||||||||||||||||||||||||||||
e.stopPropagation(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
mutateUpdateReportStatus({ reportId, status: selectableStatus }); | ||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenuItem> | ||||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenuContent> | ||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenu> | ||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { ctw } from '@ballerine/ui'; | ||
import React, { ComponentProps } from 'react'; | ||
|
||
import { Button } from '@/common/components/atoms/Button/Button'; | ||
import { | ||
statusToData, | ||
MerchantMonitoringStatusBadge, | ||
} from '@/pages/MerchantMonitoring/components/MerchantMonitoringReportStatus/MerchantMonitoringStatusBadge'; | ||
|
||
export const MerchantMonitoringStatusButton = ({ | ||
status, | ||
onClick, | ||
disabled = false, | ||
}: ComponentProps<typeof Button> & { disabled?: boolean; status: keyof typeof statusToData }) => ( | ||
<Button | ||
onClick={e => { | ||
if (disabled) { | ||
e.stopPropagation(); | ||
|
||
return; | ||
} | ||
|
||
onClick?.(e); | ||
}} | ||
variant={'status'} | ||
className={ctw(`flex h-16 w-80 flex-col items-start justify-center space-y-1 px-4 py-2`, { | ||
'!cursor-not-allowed': disabled, | ||
})} | ||
> | ||
<MerchantMonitoringStatusBadge status={status} disabled={disabled} /> | ||
<span className={`text-xs font-semibold leading-5 text-[#94A3B8]`}> | ||
{statusToData[status].text} | ||
</span> | ||
</Button> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Move selectableStatuses to @ballerine/common package.
The
selectableStatuses
array should be moved to the@ballerine/common
package to maintain consistency and reusability across the codebase.