-
Notifications
You must be signed in to change notification settings - Fork 2
[improvement] Add tab name to backlink button text #400
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
base: staging
Are you sure you want to change the base?
Conversation
79c369e to
4346ca9
Compare
ssreekar
left a comment
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.
LGTM
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.
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>
);
}
Notion ticket link
[n/a]
Implementation description
Notes
Checklist
[Feature],[Improvement]or[Fix],