All files / libs/lti/views/participant-enrollment/src/lib/confirm-enrollment-button LtiParticipantConfirmEnrollmentButton.tsx

0% Statements 0/141
0% Branches 0/1
0% Functions 0/1
0% Lines 0/141

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142                                                                                                                                                                                                                                                                                           
import { memo, useCallback, useMemo } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { match } from 'ts-pattern';

import { Button, Modal, Tooltip } from '@allshares/studio-design-system';
import { routes } from '@amalia/core/routes';
import { useBoolState } from '@amalia/ext/react/hooks';
import { useNavigate } from '@amalia/ext/react-router-dom';
import { assert } from '@amalia/ext/typescript';
import { objectToQs } from '@amalia/ext/web';
import { useAuthenticatedContext } from '@amalia/kernel/auth/state';
import { useConfirmEnrollment, useListGrantDocuments } from '@amalia/lti/state';
import { isGrantOfferExpired, type LtiGrant } from '@amalia/lti/types';

interface LtiParticipantConfirmEnrollmentButtonProps {
  readonly grant: LtiGrant;
  readonly isPreview?: boolean;
}

export const LtiParticipantConfirmEnrollmentButton = memo(function LtiParticipantConfirmEnrollmentButton({
  grant,
  isPreview,
}: LtiParticipantConfirmEnrollmentButtonProps) {
  const { formatMessage } = useIntl();
  const { authenticatedContext } = useAuthenticatedContext();

  const isAdminImpersonating = !!authenticatedContext.impersonation?.impersonator;

  const navigate = useNavigate();

  const { data: documents = [], isLoading: isDocumentsLoading } = useListGrantDocuments(grant.planId, grant.id);

  const { isConfirmModalOpen, setConfirmModalOpenTrue, setConfirmModalOpenFalse } = useBoolState(
    false,
    'confirmModalOpen',
  );

  const { mutate: confirmEnrollment, isPending } = useConfirmEnrollment();

  const hasAllRequiredSignatures = useMemo(
    () =>
      !isDocumentsLoading &&
      documents.filter((document) => document.metadata.isSignatureRequired).every((document) => !!document.signedAt),
    [documents, isDocumentsLoading],
  );

  const handleConfirm = useCallback(() => {
    assert(!isPreview, formatMessage({ defaultMessage: 'Enrollment confirmation is not available in preview mode.' }));

    confirmEnrollment(
      { grantId: grant.id, planId: grant.planId },
      {
        onSuccess: () => {
          setConfirmModalOpenFalse();
          navigate({
            pathname: routes.LTI_PLANS_PARTICIPANT,
            search: objectToQs({
              enqueueSnackbar: formatMessage({ defaultMessage: 'Enrollment confirmed successfully.' }),
            }),
          });
        },
      },
    );
  }, [confirmEnrollment, formatMessage, grant.id, grant.planId, navigate, setConfirmModalOpenFalse, isPreview]);

  const isConfirmButtonDisabled =
    !!grant.acceptedAt ||
    isGrantOfferExpired(grant) ||
    !hasAllRequiredSignatures ||
    isPending ||
    isAdminImpersonating ||
    isPreview;

  const confirmButtonTooltipContent = useMemo(
    () =>
      match({
        grantAlreadyAccepted: !!grant.acceptedAt,
        grantExpired: isGrantOfferExpired(grant),
        isAdminImpersonating,
        isPreview,
        missingSignatures: !hasAllRequiredSignatures,
      })
        .with({ grantAlreadyAccepted: true }, () =>
          formatMessage({ defaultMessage: 'This enrollment has already been confirmed.' }),
        )
        .with({ grantExpired: true }, () => formatMessage({ defaultMessage: 'This enrollment offer has expired.' }))
        .with({ isAdminImpersonating: true }, () =>
          formatMessage({ defaultMessage: 'You cannot confirm enrollment while impersonating a user.' }),
        )
        .with({ missingSignatures: true }, () =>
          formatMessage({ defaultMessage: 'You must sign all required documents before confirming enrollment.' }),
        )
        .with({ isPreview: true }, () =>
          formatMessage({ defaultMessage: 'Enrollment confirmation is not available in preview mode.' }),
        )
        .otherwise(() => undefined),
    [formatMessage, grant, isAdminImpersonating, hasAllRequiredSignatures, isPreview],
  );

  return (
    <div>
      <Tooltip content={confirmButtonTooltipContent}>
        <Button
          disabled={isConfirmButtonDisabled}
          size="large"
          variant="primary"
          onClick={setConfirmModalOpenTrue}
        >
          <FormattedMessage defaultMessage="Confirm enrollment" />
        </Button>
      </Tooltip>

      <Modal
        isOpen={isConfirmModalOpen}
        onClose={setConfirmModalOpenFalse}
      >
        <Modal.Content>
          <Modal.Header>
            <Modal.Title>
              <FormattedMessage defaultMessage="Confirm enrollment" />
            </Modal.Title>
          </Modal.Header>

          <Modal.Body>
            <FormattedMessage defaultMessage="Are you sure you want to confirm your enrollment in this plan? This action cannot be undone." />
          </Modal.Body>
        </Modal.Content>

        <Modal.Actions>
          <Modal.CancelAction />
          <Modal.MainAction
            isLoading={isPending}
            onClick={handleConfirm}
          >
            <FormattedMessage defaultMessage="Confirm" />
          </Modal.MainAction>
        </Modal.Actions>
      </Modal>
    </div>
  );
});