Skip to content

Conversation

@jennchenn
Copy link
Contributor

Notion ticket link

[n/a]

Implementation description

Notes

Checklist

  • My PR name is descriptive, is in imperative tense and starts with one of the following: [Feature],[Improvement] or [Fix],
  • I have run the appropriate linter(s)
  • I have requested a review from the RCD team on GitHub, or specific people who are associated with this ticket

Copy link
Contributor

@ssreekar ssreekar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new backlink logic doesn't work as intended when the request page is loaded without tab / page in the URL. this can happen after doing something like creating a replacement request. If you pay close attention to the url you'll notice it says /admin?tab=undefined&page=undefined for a half second and then defaults tab to PENDING and page to an empty string, but the link itself is still incorrect.

To see this pay close attention to the URL in the following video:

Screen.Recording.2025-11-24.at.10.48.47.PM.mov

To fix this I think you should build the link from the current query with fallbacks, without relying on stale state or you could rerun when the query changes. I have an example (that I think works) that you can just drop in for the Header.tsx file if you think it looks fine:

import { ChevronDownIcon, ChevronLeftIcon } from '@chakra-ui/icons'; // Chakra UI icon
import {
  Alert,
  AlertIcon,
  Box,
  Button,
  Flex,
  HStack,
  Link,
  Menu,
  MenuButton,
  MenuItem,
  MenuList,
  Text,
  useDisclosure,
  VStack,
} from '@chakra-ui/react'; // Chakra UI
import PermitTypeBadge from '@components/admin/PermitTypeBadge';
import RequestStatusBadge from '@components/admin/RequestStatusBadge'; // Request status badge
import ShopifyBadge from '@components/admin/ShopifyBadge';
import { ApplicationStatus, ApplicationType, PermitType } from '@lib/graphql/types';
import { formatDateYYYYMMDD, formatDateYYYYMMDDLocal } from '@lib/utils/date';
import { getPermanentPermitExpiryDate } from '@lib/utils/permit-expiry';
import { CurrentApplication } from '@tools/admin/permit-holders/current-application';
import { titlecase } from '@tools/string';
import NextLink from 'next/link';
import { useRouter } from 'next/router';
import ConfirmDeleteRequestModal from './delete/ConfirmDeleteRequestModal';

type RequestHeaderProps = {
  readonly id: number;
  readonly applicationType: ApplicationType;
  readonly permitType: PermitType;
  readonly createdAt: Date;
  readonly applicationStatus?: ApplicationStatus;
  readonly paidThroughShopify?: boolean;
  readonly shopifyOrderID?: string;
  readonly shopifyOrderNumber?: string;
  readonly permitExpiry: Date | null;
  readonly temporaryPermitExpiry: Date | null;
  readonly reasonForRejection?: string;
  readonly mostRecentApplication: CurrentApplication | null;
};

/**
 * Header of View Request page
 * @param id Application id
 * @param applicationType Type of application
 * @param permitType Type of permit
 * @param createdAt Date permit was created at
 * @param applicationStatus Status of application
 * @param paidThroughShopify If the permit fee was paid through Shopify
 * @param shopifyOrderID Order ID of Shopify payment if paid through Shopify
 * @param shopifyOrderNumber Order number of Shopify payment if paid through Shopify
 * @param permitExpiry Permit expiry if application is complete
 * @param temporaryPermitExpiry Permit expiry if application is for a temporary permit
 * @param reasonForRejection Reason for rejecting application
 */
export default function RequestHeader({
  id,
  applicationType,
  permitType,
  createdAt,
  applicationStatus,
  paidThroughShopify,
  shopifyOrderID,
  shopifyOrderNumber,
  permitExpiry,
  temporaryPermitExpiry,
  reasonForRejection,
  mostRecentApplication,
}: RequestHeaderProps) {
  const displayShopifyUrl = paidThroughShopify && shopifyOrderID && shopifyOrderNumber;
  const shopifyOrderUrl = `https://${process.env.NEXT_PUBLIC_SHOPIFY_DOMAIN}/admin/orders/${shopifyOrderID}`;

  let expiryDateText: string | null;
  if (applicationStatus === 'COMPLETED' && !!permitExpiry) {
    expiryDateText = `Expiry date: ${formatDateYYYYMMDD(permitExpiry)}`;
  } else if (permitType === 'TEMPORARY' && !!temporaryPermitExpiry) {
    expiryDateText = `This permit will expire: ${formatDateYYYYMMDD(temporaryPermitExpiry)}`;
  } else if (applicationType === 'REPLACEMENT' && !!permitExpiry) {
    expiryDateText = `This permit will expire: ${formatDateYYYYMMDD(permitExpiry)}`;
  } else if (permitType === 'PERMANENT') {
    expiryDateText = `This permit will expire: ${formatDateYYYYMMDD(
      getPermanentPermitExpiryDate()
    )} (expected)`;
  } else {
    expiryDateText = null;
  }

  const router = useRouter();

  const routerStatus =
    typeof router.query.tab === 'string' ? router.query.tab : applicationStatus || 'PENDING';
  const routerPage = typeof router.query.page === 'string' ? router.query.page : '0';
  const backLink = `/admin?tab=${routerStatus}&page=${routerPage}`;

  const formatStatus = (status: string | string[] | undefined) => {
    if (typeof status != 'string') {
      return '';
    }
    if (status === 'ALL') {
      return '';
    }
    return status.toLowerCase().replace('_', ' ');
  };

  // Delete application modal state
  const {
    isOpen: isDeleteApplicationModalOpen,
    onOpen: onOpenDeleteApplicationModal,
    onClose: onCloseDeleteApplicationModal,
  } = useDisclosure();

  return (
    <Box textAlign="left">
      <NextLink href={backLink} passHref>
        <Text textStyle="button-semibold" textColor="primary" as="a">
          <ChevronLeftIcon />
          All {formatStatus(routerStatus)} requests
        </Text>
      </NextLink>

      <VStack alignItems="stretch">
        <Flex marginTop={5} alignItems="baseline" justifyContent="space-between">
          <Box>
            <Flex alignItems="center">
              <Text textStyle="display-large" as="h1" marginRight={3} textTransform="capitalize">
                {`${titlecase(applicationType)} Request`}
              </Text>
              <HStack spacing={3}>
                {applicationStatus && <RequestStatusBadge variant={applicationStatus} />}
                {paidThroughShopify && <ShopifyBadge />}
              </HStack>
            </Flex>
            <HStack spacing={3} marginTop={3}>
              <Text textStyle="caption" as="p">
                Received on {formatDateYYYYMMDDLocal(createdAt)} at{' '}
                {createdAt.toLocaleTimeString('en-CA')}
              </Text>
              <Menu>
                <MenuButton
                  as={Button}
                  rightIcon={<ChevronDownIcon />}
                  height="30px"
                  bg="background.gray"
                  _hover={{ bg: 'background.grayHover' }}
                  color="black"
                >
                  <Text textStyle="caption">More Actions</Text>
                </MenuButton>
                <MenuList>
                  {mostRecentApplication?.processing?.status == 'COMPLETED' ? null : (
                    <MenuItem
                      color="text.critical"
                      textStyle="button-regular"
                      onClick={onOpenDeleteApplicationModal}
                    >
                      {'Delete Request'}
                    </MenuItem>
                  )}
                </MenuList>
              </Menu>
            </HStack>
            {displayShopifyUrl && (
              <Text textStyle="caption" as="p">
                Paid with Shopify: Order{' '}
                <Link
                  href={shopifyOrderUrl}
                  isExternal={true}
                  textStyle="caption-bold"
                  textDecoration="underline"
                  color="primary"
                >
                  {`#${shopifyOrderNumber}`}
                </Link>
              </Text>
            )}
          </Box>
          <VStack alignItems="flex-end" spacing="0">
            <Flex alignItems="center">
              <Text textStyle="heading" as="h3" marginRight={3} textTransform="capitalize">
                Permit Type:
              </Text>
              <PermitTypeBadge variant={permitType} />
            </Flex>
            <HStack justifyContent="flex-end">
              <Text textStyle="caption" as="p" mt="12px">
                {expiryDateText}
              </Text>
            </HStack>
          </VStack>
        </Flex>
        {applicationStatus === 'REJECTED' && (
          <Alert status="error">
            <AlertIcon />
            <Text as="p" textStyle="caption">
              <b>Reason for Rejection: </b>
              {reasonForRejection || ''}
            </Text>
          </Alert>
        )}
        <ConfirmDeleteRequestModal
          isOpen={isDeleteApplicationModalOpen}
          applicationId={id}
          refetch={() => {
            /* Do not refetch, redirect to main page */
          }}
          onClose={() => {
            onCloseDeleteApplicationModal();
            router.push('/admin');
          }}
        />
      </VStack>
    </Box>
  );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants