-
Notifications
You must be signed in to change notification settings - Fork 424
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
TAA-136: MVP of the Approval Request and Approval Request List page functionality #576
Changes from 16 commits
c979b49
548391f
e1e3c55
a378675
aa08970
bd686d0
c44cbc5
e0b910c
6e2bf42
0c77487
350abd6
b828012
3968b2c
e574128
9ae1976
f9fd58b
a2fb5d7
7d1ddab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,15 +21,18 @@ | |
"@zendeskgarden/container-grid": "^3.0.14", | ||
"@zendeskgarden/container-utilities": "^2.0.2", | ||
"@zendeskgarden/react-accordions": "8.76.9", | ||
"@zendeskgarden/react-avatars": "8.76.9", | ||
"@zendeskgarden/react-breadcrumbs": "8.76.9", | ||
"@zendeskgarden/react-buttons": "8.76.9", | ||
"@zendeskgarden/react-datepickers": "8.76.9", | ||
"@zendeskgarden/react-dropdowns.next": "8.76.9", | ||
"@zendeskgarden/react-grid": "8.76.9", | ||
"@zendeskgarden/react-forms": "8.76.9", | ||
"@zendeskgarden/react-grid": "^8.76.9", | ||
"@zendeskgarden/react-loaders": "8.76.9", | ||
"@zendeskgarden/react-modals": "8.76.9", | ||
"@zendeskgarden/react-notifications": "8.76.9", | ||
"@zendeskgarden/react-pagination": "8.76.9", | ||
"@zendeskgarden/react-tables": "8.76.9", | ||
"@zendeskgarden/react-tags": "8.76.9", | ||
"@zendeskgarden/react-theming": "8.76.9", | ||
"@zendeskgarden/react-tooltips": "8.76.9", | ||
|
@@ -66,7 +69,8 @@ | |
"@svgr/rollup": "^8.1.0", | ||
"@testing-library/dom": "^9.3.1", | ||
"@testing-library/jest-dom": "^5.16.5", | ||
"@testing-library/user-event": "^14.4.3", | ||
"@testing-library/react": "^12.1.5", | ||
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. Only using |
||
"@testing-library/user-event": "^14.6.1", | ||
"@types/lodash.debounce": "^4.0.9", | ||
"@types/react": "^17.0.62", | ||
"@types/react-dom": "^17.0.20", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { memo, useState, useMemo } from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
import styled from "styled-components"; | ||
import { MD, XXL } from "@zendeskgarden/react-typography"; | ||
import { getColorV8 } from "@zendeskgarden/react-theming"; | ||
import { Spinner } from "@zendeskgarden/react-loaders"; | ||
import { useSearchApprovalRequests } from "./hooks/useSearchApprovalRequests"; | ||
import ApprovalRequestListFilters from "./components/approval-request-list/ApprovalRequestListFilters"; | ||
import ApprovalRequestListTable from "./components/approval-request-list/ApprovalRequestListTable"; | ||
|
||
const Container = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
gap: ${(props) => props.theme.space.lg}; | ||
margin-top: ${(props) => props.theme.space.xl}; /* 40px */ | ||
`; | ||
|
||
const LoadingContainer = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
`; | ||
|
||
const NoApprovalRequestsText = styled(MD)` | ||
color: ${(props) => getColorV8("grey", 600, props.theme)}; | ||
`; | ||
|
||
export interface ApprovalRequestListPageProps { | ||
baseLocale: string; | ||
helpCenterPath: string; | ||
} | ||
|
||
type SortDirection = "asc" | "desc" | undefined; | ||
|
||
function ApprovalRequestListPage({ | ||
baseLocale, | ||
helpCenterPath, | ||
}: ApprovalRequestListPageProps) { | ||
const { t } = useTranslation(); | ||
const [searchTerm, setSearchTerm] = useState(""); | ||
const [sortDirection, setSortDirection] = useState<SortDirection>(undefined); | ||
const { | ||
approvalRequests, | ||
errorFetchingApprovalRequests: error, | ||
approvalRequestStatus, | ||
setApprovalRequestStatus, | ||
isLoading, | ||
} = useSearchApprovalRequests(); | ||
|
||
const sortedAndFilteredApprovalRequests = useMemo(() => { | ||
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. Shouldn't filtering and sorting be handled via the api? 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. Yes, ideally the filtering and sorting is both handled API side but that has not been implemented yet and we agreed to do this client side for the EAP. The results are currently unpaginated as well and we do have plans to sort/filter on the API long term |
||
let results = [...approvalRequests]; | ||
|
||
// Apply search filter | ||
if (searchTerm) { | ||
const term = searchTerm.toLowerCase(); | ||
results = results.filter((request) => | ||
request.subject.toLowerCase().includes(term) | ||
); | ||
} | ||
|
||
// Apply sorting | ||
if (sortDirection) { | ||
results.sort((a, b) => { | ||
const dateA = new Date(a.created_at).getTime(); | ||
const dateB = new Date(b.created_at).getTime(); | ||
return sortDirection === "asc" ? dateA - dateB : dateB - dateA; | ||
}); | ||
} | ||
|
||
return results; | ||
}, [approvalRequests, searchTerm, sortDirection]); | ||
|
||
if (error) { | ||
throw error; | ||
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. nit: i know this is still MVP but we could note to handle errors gracefully in the future like a growl or something with better UX 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. maybe similar to using notify below? 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. Good callout, I followed the pattern used in the Service Catalog: https://github.com/zendesk/copenhagen_theme/blob/beta/src/modules/service-catalog/ServiceCatalogItemPage.tsx#L49-L51 This relies on the |
||
} | ||
|
||
if (isLoading) { | ||
return ( | ||
<LoadingContainer> | ||
<Spinner size="64" /> | ||
</LoadingContainer> | ||
); | ||
} | ||
|
||
return ( | ||
<Container> | ||
<XXL isBold> | ||
{t("approval-requests.list.header", "Approval requests")} | ||
</XXL> | ||
<ApprovalRequestListFilters | ||
approvalRequestStatus={approvalRequestStatus} | ||
setApprovalRequestStatus={setApprovalRequestStatus} | ||
setSearchTerm={setSearchTerm} | ||
/> | ||
{approvalRequests.length === 0 ? ( | ||
<NoApprovalRequestsText> | ||
{t( | ||
"approval-requests.list.no-requests", | ||
"No approval requests found." | ||
)} | ||
</NoApprovalRequestsText> | ||
) : ( | ||
<ApprovalRequestListTable | ||
approvalRequests={sortedAndFilteredApprovalRequests} | ||
baseLocale={baseLocale} | ||
helpCenterPath={helpCenterPath} | ||
sortDirection={sortDirection} | ||
onSortChange={setSortDirection} | ||
/> | ||
)} | ||
</Container> | ||
); | ||
} | ||
|
||
export default memo(ApprovalRequestListPage); | ||
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. Just curious why do we need to memoize here? 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. This is a habit/pre-optimization. In |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { memo } from "react"; | ||
import styled from "styled-components"; | ||
import { MD, XXL } from "@zendeskgarden/react-typography"; | ||
import { Spinner } from "@zendeskgarden/react-loaders"; | ||
import ApprovalRequestDetails from "./components/approval-request/ApprovalRequestDetails"; | ||
import ApprovalTicketDetails from "./components/approval-request/ApprovalTicketDetails"; | ||
import ApproverActions from "./components/approval-request/ApproverActions"; | ||
import { useApprovalRequest } from "./hooks/useApprovalRequest"; | ||
import type { Organization } from "../ticket-fields"; | ||
import ApprovalRequestBreadcrumbs from "./components/approval-request/ApprovalRequestBreadcrumbs"; | ||
|
||
const Container = styled.div` | ||
display: flex; | ||
flex-direction: row; | ||
margin-top: ${(props) => props.theme.space.xl}; /* 40px */ | ||
margin-bottom: ${(props) => props.theme.space.lg}; /* 32px */ | ||
|
||
@media (max-width: ${(props) => props.theme.breakpoints.md}) { | ||
flex-direction: column; | ||
margin-bottom: ${(props) => props.theme.space.xl}; /* 40px */ | ||
} | ||
`; | ||
|
||
const LoadingContainer = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
`; | ||
|
||
const LeftColumn = styled.div` | ||
flex: 2; | ||
|
||
& > *:first-child { | ||
margin-bottom: ${(props) => props.theme.space.base * 4}px; /* 16px */ | ||
} | ||
|
||
& > *:not(:first-child) { | ||
margin-bottom: ${(props) => props.theme.space.lg}; /* 32px */ | ||
} | ||
|
||
& > *:last-child { | ||
margin-bottom: 0; | ||
} | ||
|
||
@media (max-width: ${(props) => props.theme.breakpoints.md}) { | ||
flex: 1; | ||
margin-right: 0; | ||
margin-bottom: ${(props) => props.theme.space.lg}; | ||
} | ||
`; | ||
|
||
const RightColumn = styled.div` | ||
flex: 1; | ||
margin-left: ${(props) => props.theme.space.base * 6}px; /* 24px */ | ||
|
||
@media (max-width: ${(props) => props.theme.breakpoints.md}) { | ||
margin-left: 0; | ||
} | ||
`; | ||
|
||
export interface ApprovalRequestPageProps { | ||
approvalWorkflowInstanceId: string; | ||
approvalRequestId: string; | ||
baseLocale: string; | ||
helpCenterPath: string; | ||
organizations: Array<Organization>; | ||
userId: number; | ||
} | ||
|
||
function ApprovalRequestPage({ | ||
approvalWorkflowInstanceId, | ||
approvalRequestId, | ||
baseLocale, | ||
helpCenterPath, | ||
organizations, | ||
userId, | ||
}: ApprovalRequestPageProps) { | ||
const { | ||
approvalRequest, | ||
setApprovalRequest, | ||
errorFetchingApprovalRequest: error, | ||
isLoading, | ||
} = useApprovalRequest(approvalWorkflowInstanceId, approvalRequestId); | ||
|
||
if (error) { | ||
throw error; | ||
} | ||
|
||
if (isLoading) { | ||
return ( | ||
<LoadingContainer> | ||
<Spinner size="64" /> | ||
</LoadingContainer> | ||
); | ||
} | ||
|
||
const showApproverActions = | ||
userId === approvalRequest?.assignee_user?.id && | ||
approvalRequest?.status === "active"; | ||
|
||
return ( | ||
<> | ||
<ApprovalRequestBreadcrumbs | ||
helpCenterPath={helpCenterPath} | ||
organizations={organizations} | ||
/> | ||
<Container> | ||
<LeftColumn> | ||
<XXL isBold>{approvalRequest?.subject}</XXL> | ||
<MD>{approvalRequest?.message}</MD> | ||
{approvalRequest?.ticket_details && ( | ||
<ApprovalTicketDetails ticket={approvalRequest.ticket_details} /> | ||
)} | ||
</LeftColumn> | ||
<RightColumn> | ||
{approvalRequest && ( | ||
<ApprovalRequestDetails | ||
approvalRequest={approvalRequest} | ||
baseLocale={baseLocale} | ||
/> | ||
)} | ||
</RightColumn> | ||
</Container> | ||
{showApproverActions && ( | ||
<ApproverActions | ||
approvalWorkflowInstanceId={approvalWorkflowInstanceId} | ||
approvalRequestId={approvalRequestId} | ||
setApprovalRequest={setApprovalRequest} | ||
assigneeUser={approvalRequest?.assignee_user} | ||
/> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
export default memo(ApprovalRequestPage); |
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.
@luis-almeida I ended up adding a
__tests__
directory within the approval-requests module and this was throwing an eslint error on those files. Let me know if you want to handle this differently.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.
Would it be ok to colocate the tests? e.g.: https://github.com/zendesk/copenhagen_theme/blob/master/src/modules/new-request-form/getVisibleFields.test.tsx
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.
Updated to colocate the tests in 7d1ddab which removed the need for the eslint updates