From 90eab86cf4f31f34ea2badfebb3c2e7d65c19436 Mon Sep 17 00:00:00 2001 From: Ong Guan Hong Malcolm <65110268+m0nggh@users.noreply.github.com> Date: Fri, 4 Oct 2024 16:30:21 +0800 Subject: [PATCH] templates: add more changes before release (#761) Details - Refactor empty flows page to use the same tile component from templates page - Modify schedule reminder template details - Add new badge to templates sidebar item - Change whats new date --------- Co-authored-by: Ian Chen --- .../src/db/storage/schedule-reminders.ts | 2 +- .../components/EmptyFlows/FlowTemplate.tsx | 52 -------- .../src/components/EmptyFlows/index.tsx | 107 ---------------- .../components/Layout/NavigationSidebar.tsx | 6 +- .../frontend/src/components/Layout/index.tsx | 1 + .../src/components/NewsDrawer/NewsItemList.ts | 2 +- packages/frontend/src/exports/components.ts | 1 - .../components/ApproveTransfersInfobox.tsx | 23 ++-- .../src/pages/Flows/components/EmptyFlows.tsx | 69 +++++++++++ packages/frontend/src/pages/Flows/index.tsx | 114 ++++++++---------- .../Templates/components/TemplateTile.tsx | 43 +++++++ .../frontend/src/pages/Templates/index.tsx | 47 ++------ 12 files changed, 191 insertions(+), 276 deletions(-) delete mode 100644 packages/frontend/src/components/EmptyFlows/FlowTemplate.tsx delete mode 100644 packages/frontend/src/components/EmptyFlows/index.tsx create mode 100644 packages/frontend/src/pages/Flows/components/EmptyFlows.tsx create mode 100644 packages/frontend/src/pages/Templates/components/TemplateTile.tsx diff --git a/packages/backend/src/db/storage/schedule-reminders.ts b/packages/backend/src/db/storage/schedule-reminders.ts index 21282b93f..c91444eba 100644 --- a/packages/backend/src/db/storage/schedule-reminders.ts +++ b/packages/backend/src/db/storage/schedule-reminders.ts @@ -8,7 +8,7 @@ export const SCHEDULE_REMINDERS_TEMPLATE: ITemplate = { id: SCHEDULE_REMINDERS_ID, name: 'Schedule reminders', description: - 'Schedule a recurring reminder to yourself to complete a task everyday', + 'Schedule a recurring reminder to yourself to complete a task weekly', iconName: 'BiCalendar', tags: ['empty'], // Steps: scheduler --> postman diff --git a/packages/frontend/src/components/EmptyFlows/FlowTemplate.tsx b/packages/frontend/src/components/EmptyFlows/FlowTemplate.tsx deleted file mode 100644 index e61dfa155..000000000 --- a/packages/frontend/src/components/EmptyFlows/FlowTemplate.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { ITemplate } from '@plumber/types' - -import { BiRightArrowAlt } from 'react-icons/bi' -import { useNavigate } from 'react-router-dom' -import { Box, Card, CardBody, CardFooter, Flex, Text } from '@chakra-ui/react' -import { Badge, Button } from '@opengovsg/design-system-react' - -import * as URLS from '@/config/urls' -import { TemplateIcon } from '@/helpers/flow-templates' - -export interface FlowTemplateProps { - template: ITemplate -} - -export default function FlowTemplate(props: FlowTemplateProps) { - const { template } = props - const { id, name, description, iconName, tags } = template - const navigate = useNavigate() - const isDemoTemplate = tags && tags.some((tag) => tag === 'demo') - - return ( - - - - - - - {isDemoTemplate && ( - - Demo - - )} - - - - {name} - {description} - - - - - - - - ) -} diff --git a/packages/frontend/src/components/EmptyFlows/index.tsx b/packages/frontend/src/components/EmptyFlows/index.tsx deleted file mode 100644 index 872e7c84e..000000000 --- a/packages/frontend/src/components/EmptyFlows/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import type { ITemplate } from '@plumber/types' - -import { useQuery } from '@apollo/client' -import { - AbsoluteCenter, - Box, - Center, - Divider, - Flex, - Grid, - Hide, - Text, - useDisclosure, -} from '@chakra-ui/react' -import { Button } from '@opengovsg/design-system-react' - -import NavigationDrawer from '@/components/Layout/NavigationDrawer' -import { GET_TEMPLATES } from '@/graphql/queries/get-templates' -import ApproveTransfersInfobox from '@/pages/Flows/components/ApproveTransfersInfobox' -import CreateFlowModal from '@/pages/Flows/components/CreateFlowModal' - -import PrimarySpinner from '../PrimarySpinner' - -import FlowTemplate from './FlowTemplate' - -interface EmptyFlowsProps { - count?: number -} - -export default function EmptyFlows(props: EmptyFlowsProps) { - const { count } = props - - const { data, loading } = useQuery(GET_TEMPLATES, { - variables: { - tag: 'empty', - }, - }) - const emptyFlowsTemplates: ITemplate[] = data?.getTemplates - - // for creation of flows - const { - isOpen: isCreateFlowModalOpen, - onOpen: onCreateFlowModalOpen, - onClose: onCreateFlowModalClose, - } = useDisclosure() - - return ( - <> - - {count === undefined || count === 0 ? ( - <> - ) : ( - - )} - - - - - - - - - Start creating a pipe from one of our templates below - - - - {loading ? ( -
- -
- ) : ( - - {emptyFlowsTemplates.map((template) => ( - - ))} - - )} - - - - - - OR - - - - - -
- - {isCreateFlowModalOpen && ( - - )} - - ) -} diff --git a/packages/frontend/src/components/Layout/NavigationSidebar.tsx b/packages/frontend/src/components/Layout/NavigationSidebar.tsx index 4b5c20337..128ef0c80 100644 --- a/packages/frontend/src/components/Layout/NavigationSidebar.tsx +++ b/packages/frontend/src/components/Layout/NavigationSidebar.tsx @@ -47,7 +47,11 @@ function NavigationSidebarItem({ {text} {link.badge && ( - + {link.badge} )} diff --git a/packages/frontend/src/components/Layout/index.tsx b/packages/frontend/src/components/Layout/index.tsx index 49d6cb60b..35bc4ef55 100644 --- a/packages/frontend/src/components/Layout/index.tsx +++ b/packages/frontend/src/components/Layout/index.tsx @@ -53,6 +53,7 @@ const drawerLinks = [ text: 'Templates', to: URLS.TEMPLATES, isBottom: true, + badge: 'New', }, ] diff --git a/packages/frontend/src/components/NewsDrawer/NewsItemList.ts b/packages/frontend/src/components/NewsDrawer/NewsItemList.ts index 8118af96c..2c8676167 100644 --- a/packages/frontend/src/components/NewsDrawer/NewsItemList.ts +++ b/packages/frontend/src/components/NewsDrawer/NewsItemList.ts @@ -11,7 +11,7 @@ const IF_THEN_EXTERNAL_LINK = export const NEWS_ITEM_LIST: NewsItemProps[] = [ { - date: '2024-10-03', + date: '2024-10-04', tag: NEW_FEATURE_TAG, title: 'Save time building workflows with Templates!', details: `Each template comes with pre-selected steps and content set up for you. Use them as starting points or to get inspired by what others are creating. We are continuously working on adding more relevant templates to speed up workflow building. If you need a template, put in a request [here](${URLS.TEMPLATES_FORM_LINK})!`, diff --git a/packages/frontend/src/exports/components.ts b/packages/frontend/src/exports/components.ts index 2d3ea88bc..8bce44350 100644 --- a/packages/frontend/src/exports/components.ts +++ b/packages/frontend/src/exports/components.ts @@ -18,7 +18,6 @@ export { default as EditableTypography } from '../components/EditableTypography' export { default as Editor } from '../components/Editor' export { default as EditorLayout } from '../components/EditorLayout' export { default as EditorSettings } from '../components/EditorSettings' -export { default as EmptyFlows } from '../components/EmptyFlows' export { default as ErrorResult } from '../components/ErrorResult' export { default as ExecutionHeader } from '../components/ExecutionHeader' export { default as ExecutionRow } from '../components/ExecutionRow' diff --git a/packages/frontend/src/pages/Flows/components/ApproveTransfersInfobox.tsx b/packages/frontend/src/pages/Flows/components/ApproveTransfersInfobox.tsx index 108a0df80..9c146994c 100644 --- a/packages/frontend/src/pages/Flows/components/ApproveTransfersInfobox.tsx +++ b/packages/frontend/src/pages/Flows/components/ApproveTransfersInfobox.tsx @@ -1,19 +1,24 @@ +import type { IFlowTransfer } from '@plumber/types' + import { BiLoader } from 'react-icons/bi' import { useNavigate } from 'react-router-dom' +import { useQuery } from '@apollo/client' import { Flex, Icon, Text } from '@chakra-ui/react' import { Button, Infobox } from '@opengovsg/design-system-react' -import * as URLS from '../../../config/urls' - -interface ApproveTransfersInfoboxProps { - count: number -} +import * as URLS from '@/config/urls' +import { GET_PENDING_FLOW_TRANSFERS } from '@/graphql/queries/get-pending-flow-transfers' -export default function ApproveTransfersInfobox( - props: ApproveTransfersInfoboxProps, -) { - const { count } = props +export default function ApproveTransfersInfobox() { + const { data, loading } = useQuery(GET_PENDING_FLOW_TRANSFERS) + const flowTransfers: IFlowTransfer[] = data?.getPendingFlowTransfers + const count = flowTransfers?.length const navigate = useNavigate() + + if (loading || count === 0) { + return <> + } + return ( } diff --git a/packages/frontend/src/pages/Flows/components/EmptyFlows.tsx b/packages/frontend/src/pages/Flows/components/EmptyFlows.tsx new file mode 100644 index 000000000..43b258ff0 --- /dev/null +++ b/packages/frontend/src/pages/Flows/components/EmptyFlows.tsx @@ -0,0 +1,69 @@ +import { useNavigate } from 'react-router-dom' +import { Box, Flex, Hide, Text } from '@chakra-ui/react' +import { Tile } from '@opengovsg/design-system-react' + +import NavigationDrawer from '@/components/Layout/NavigationDrawer' +import * as URLS from '@/config/urls' +import { TemplateIcon } from '@/helpers/flow-templates' + +export default function EmptyFlows({ onCreate }: { onCreate: () => void }) { + const navigate = useNavigate() + + return ( + + + + + + + + How do you want to create your pipe? + + + + ( + + + + )} + display="flex" + flex="1" + onClick={() => navigate(URLS.TEMPLATES)} + > + + Use a template + + Select from pre-built workflows that you can use as-is or + customize further for your own use case + + + + + ( + + + + )} + display="flex" + flex="1" + onClick={onCreate} + > + + Start from scratch + + Use our workflow builder to create your own workflow + + + + + + ) +} diff --git a/packages/frontend/src/pages/Flows/index.tsx b/packages/frontend/src/pages/Flows/index.tsx index d84bf5aef..d22f6cd89 100644 --- a/packages/frontend/src/pages/Flows/index.tsx +++ b/packages/frontend/src/pages/Flows/index.tsx @@ -1,4 +1,4 @@ -import type { IFlow, IFlowTransfer } from '@plumber/types' +import type { IFlow } from '@plumber/types' import { useCallback, useMemo } from 'react' import { BiPlus } from 'react-icons/bi' @@ -10,17 +10,16 @@ import debounce from 'lodash/debounce' import ConditionalIconButton from '@/components/ConditionalIconButton' import Container from '@/components/Container' -import EmptyFlowsTemplate from '@/components/EmptyFlows' import FlowRow from '@/components/FlowRow' import NoResultFound from '@/components/NoResultFound' import PageTitle from '@/components/PageTitle' import PrimarySpinner from '@/components/PrimarySpinner' import SearchInput from '@/components/SearchInput' import { GET_FLOWS } from '@/graphql/queries/get-flows' -import { GET_PENDING_FLOW_TRANSFERS } from '@/graphql/queries/get-pending-flow-transfers' import ApproveTransfersInfobox from './components/ApproveTransfersInfobox' import CreateFlowModal from './components/CreateFlowModal' +import EmptyFlows from './components/EmptyFlows' const FLOW_PER_PAGE = 10 const FLOWS_TITLE = 'Pipes' @@ -43,13 +42,6 @@ export default function Flows(): React.ReactElement { // modal for creation of flows const { isOpen, onOpen, onClose } = useDisclosure() - // for flow transfers - const { data: flowTransfersData, loading: flowTransfersLoading } = useQuery( - GET_PENDING_FLOW_TRANSFERS, - ) - const flowTransfers: IFlowTransfer[] = - flowTransfersData?.getPendingFlowTransfers - // format search params for empty string input and first page const formatSearchParams = useCallback( (params: Partial) => { @@ -101,86 +93,78 @@ export default function Flows(): React.ReactElement { const { pageInfo, edges } = data?.getFlows || {} const flows: IFlow[] = edges?.map(({ node }: { node: IFlow }) => node) - const hasFlows = flows?.length + const hasFlows = flows?.length > 0 + const isSearching = flowName !== '' || page !== 1 - // TODO (mal): think of a way to make this less complicated - if (!loading && !hasFlows && flowName === '' && page === 1) { - return ( - - ) - } + const isEmptyState = !hasFlows && !isSearching + const isEmptySearchResults = !loading && !hasFlows && isSearching + const hasPagination = pageInfo && pageInfo.totalCount > FLOW_PER_PAGE return ( <> - - - - - - - - - - - } - data-test="create-flow-button" - onClick={onOpen} - > - Create Pipe - - - - - {flowTransfersLoading ? ( -
- -
- ) : flowTransfers.length === 0 ? ( - <> - ) : ( - + md: `"title search button"`, + }} + gridTemplateColumns={{ base: '1fr auto', md: '2fr 1fr auto' }} + columnGap={3} + rowGap={5} + alignItems="center" + pl={{ base: '0', md: '2rem' }} + mb={6} + > + + + + + + + + + + } + data-test="create-flow-button" + onClick={onOpen} + > + Create Pipe + + + )} + + {loading && (
)} + {!loading && isEmptyState && } + {!loading && flows?.map((flow) => )} - {!loading && !hasFlows && ( + {isEmptySearchResults && ( )} - {!loading && pageInfo && pageInfo.totalCount > FLOW_PER_PAGE && ( + {hasPagination && ( tag === 'demo') + + return ( + ( + + + + )} + badge={ + isDemoTemplate ? ( + + Demo included + + ) : undefined + } + display="flex" + onClick={() => navigate(URLS.TEMPLATE(id))} + > + + {name} + {description} + + + ) +} diff --git a/packages/frontend/src/pages/Templates/index.tsx b/packages/frontend/src/pages/Templates/index.tsx index 05030e65d..beddce7b9 100644 --- a/packages/frontend/src/pages/Templates/index.tsx +++ b/packages/frontend/src/pages/Templates/index.tsx @@ -1,23 +1,23 @@ import { ITemplate } from '@plumber/types' -import { useNavigate, useParams } from 'react-router-dom' +import { useParams } from 'react-router-dom' import { useQuery } from '@apollo/client' -import { Box, Center, Flex, Grid, Text } from '@chakra-ui/react' -import { Badge, Link, Tile } from '@opengovsg/design-system-react' +import { Center, Flex, Grid, Text } from '@chakra-ui/react' +import { Link } from '@opengovsg/design-system-react' import Container from '@/components/Container' import PageTitle from '@/components/PageTitle' import PrimarySpinner from '@/components/PrimarySpinner' import * as URLS from '@/config/urls' import { GET_TEMPLATES } from '@/graphql/queries/get-templates' -import { TemplateIcon } from '@/helpers/flow-templates' import TemplateModal from '../Template' +import TemplateTile from './components/TemplateTile' + const TEMPLATES_TITLE = 'Templates' export default function Templates(): JSX.Element { - const navigate = useNavigate() const { data, loading } = useQuery(GET_TEMPLATES) const templates: ITemplate[] = data?.getTemplates @@ -53,40 +53,9 @@ export default function Templates(): JSX.Element { rowGap={6} mb={8} > - {templates?.map((template, index) => { - const isDemoTemplate = template.tags?.some( - (tag) => tag === 'demo', - ) - return ( - ( - - - - )} - badge={ - isDemoTemplate ? ( - - Demo included - - ) : undefined - } - display="flex" - onClick={() => navigate(URLS.TEMPLATE(template.id))} - > - - - {template.name} - - {template.description} - - - ) - })} + {templates?.map((template, index) => ( + + ))} )}