From f235e5b5072489a9c35d14cce7f3db2627c8f0b2 Mon Sep 17 00:00:00 2001 From: Rory Doak Date: Fri, 10 Jan 2025 16:12:18 +0000 Subject: [PATCH] refine type use on sort keys --- editor.planx.uk/src/lib/featureFlags.ts | 6 +- .../src/pages/FlowEditor/lib/store/editor.ts | 29 ++++- editor.planx.uk/src/pages/Team.tsx | 7 +- .../src/ui/editor/OrderingFlowsSelect.tsx | 81 ------------ .../src/ui/editor/SortFlowsSelect.tsx | 123 ++++++++++++++++++ 5 files changed, 155 insertions(+), 91 deletions(-) delete mode 100644 editor.planx.uk/src/ui/editor/OrderingFlowsSelect.tsx create mode 100644 editor.planx.uk/src/ui/editor/SortFlowsSelect.tsx diff --git a/editor.planx.uk/src/lib/featureFlags.ts b/editor.planx.uk/src/lib/featureFlags.ts index a3b30b5a0d..7994270611 100644 --- a/editor.planx.uk/src/lib/featureFlags.ts +++ b/editor.planx.uk/src/lib/featureFlags.ts @@ -1,5 +1,9 @@ // add/edit/remove feature flags in array below -const AVAILABLE_FEATURE_FLAGS = ["FEE_BREAKDOWN", "EXCLUSIVE_OR"] as const; +const AVAILABLE_FEATURE_FLAGS = [ + "FEE_BREAKDOWN", + "EXCLUSIVE_OR", + "SORT_FLOWS", +] as const; type FeatureFlag = (typeof AVAILABLE_FEATURE_FLAGS)[number]; diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/editor.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/editor.ts index d16058ff36..16e47c4456 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/editor.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/editor.ts @@ -132,19 +132,27 @@ interface PublishFlowResponse { message: string; } +export type PublishedFlowSummary = { + publishedAt: string; + hasSendComponent: boolean; +}; + +export type FlowSummaryOperations = { + createdAt: string; + actor: { + firstName: string; + lastName: string; + }; +}; + export interface FlowSummary { id: string; name: string; slug: string; status: "online" | "offline"; updatedAt: string; - operations: { - createdAt: string; - actor: { - firstName: string; - lastName: string; - }; - }[]; + operations: FlowSummaryOperations[]; + publishedFlows: PublishedFlowSummary[]; } export interface EditorStore extends Store.Store { @@ -392,6 +400,13 @@ export const editorStore: StateCreator< lastName: last_name } } + publishedFlows: published_flows( + order_by: { created_at: desc } + limit: 1 + ) { + publishedAt: created_at + hasSendComponent: has_send_component + } } } `, diff --git a/editor.planx.uk/src/pages/Team.tsx b/editor.planx.uk/src/pages/Team.tsx index d645d67af2..2a0f7c122c 100644 --- a/editor.planx.uk/src/pages/Team.tsx +++ b/editor.planx.uk/src/pages/Team.tsx @@ -11,12 +11,13 @@ import DialogContentText from "@mui/material/DialogContentText"; import DialogTitle from "@mui/material/DialogTitle"; import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; +import { hasFeatureFlag } from "lib/featureFlags"; import React, { useCallback, useEffect, useState } from "react"; import { Link, useNavigation } from "react-navi"; import { FONT_WEIGHT_SEMI_BOLD } from "theme"; import { borderedFocusStyle } from "theme"; import { AddButton } from "ui/editor/AddButton"; -import { OrderingFlowsSelect } from "ui/editor/OrderingFlowsSelect"; +import { SortFlowsSelect } from "ui/editor/SortFlowsSelect"; import { slugify } from "utils"; import { client } from "../lib/graphql"; @@ -351,7 +352,9 @@ const Team: React.FC = () => { {showAddFlowButton && } - {flows && } + {hasFeatureFlag("SORT_FLOWS") && flows && ( + + )} {teamHasFlows && ( {flows.map((flow) => ( diff --git a/editor.planx.uk/src/ui/editor/OrderingFlowsSelect.tsx b/editor.planx.uk/src/ui/editor/OrderingFlowsSelect.tsx deleted file mode 100644 index dc56d4ea3b..0000000000 --- a/editor.planx.uk/src/ui/editor/OrderingFlowsSelect.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import TrendingDownIcon from "@mui/icons-material/TrendingDown"; -import TrendingUpIcon from "@mui/icons-material/TrendingUp"; -import Box from "@mui/material/Box"; -import IconButton from "@mui/material/IconButton"; -import MenuItem from "@mui/material/MenuItem"; -import { FlowSummary } from "pages/FlowEditor/lib/store/editor"; -import React, { useEffect, useState } from "react"; - -import SelectInput from "./SelectInput/SelectInput"; - -// Define the sort directions as their own type -type SortDirection = "asc" | "desc"; -type SortTypes = keyof FlowSummary; -interface SortObject { - displayName: string; - sortKey: SortTypes; -} - -const sortArray: SortObject[] = [ - { displayName: "Name", sortKey: "name" }, - { displayName: "Last update", sortKey: "updatedAt" }, - { displayName: "Status", sortKey: "status" }, -]; - -const sortFlows = ( - a: FlowSummary[K], - b: FlowSummary[K], - sortDirection: SortDirection, -) => { - if (a < b) { - return sortDirection === "asc" ? 1 : -1; - } - if (a > b) { - return sortDirection === "asc" ? -1 : 1; - } - return 0; -}; - -export const OrderingFlowsSelect = ({ - flows, - setFlows, -}: { - flows: FlowSummary[]; - setFlows: React.Dispatch>; -}) => { - const [sortBy, setSortBy] = useState("updatedAt"); - const [sortDirection, setSortDirection] = useState("asc"); - - useEffect(() => { - if (flows) { - const sortedFlows: FlowSummary[] = flows?.sort( - (a: FlowSummary, b: FlowSummary) => - sortFlows(a[sortBy], b[sortBy], sortDirection), - ); - sortedFlows && setFlows([...sortedFlows]); - } - }, [sortBy, sortDirection]); - - return ( - - setSortBy(e.target.value)}> - {sortArray.map(({ displayName, sortKey }) => ( - - {displayName} - - ))} - - - sortDirection === "asc" - ? setSortDirection("desc") - : setSortDirection("asc") - } - > - {sortDirection === "asc" ? : } - - - ); -}; diff --git a/editor.planx.uk/src/ui/editor/SortFlowsSelect.tsx b/editor.planx.uk/src/ui/editor/SortFlowsSelect.tsx new file mode 100644 index 0000000000..a7cc7a0093 --- /dev/null +++ b/editor.planx.uk/src/ui/editor/SortFlowsSelect.tsx @@ -0,0 +1,123 @@ +import TrendingDownIcon from "@mui/icons-material/TrendingDown"; +import TrendingUpIcon from "@mui/icons-material/TrendingUp"; +import Box from "@mui/material/Box"; +import IconButton from "@mui/material/IconButton"; +import MenuItem from "@mui/material/MenuItem"; +import { + FlowSummary, + FlowSummaryOperations, + PublishedFlowSummary, +} from "pages/FlowEditor/lib/store/editor"; +import React, { useEffect, useState } from "react"; + +import SelectInput from "./SelectInput/SelectInput"; + +type SortDirection = "asc" | "desc"; +type SortKeys = keyof FlowSummary; +type SortNestedKeys = keyof PublishedFlowSummary | keyof FlowSummaryOperations; +type SortTypes = SortKeys | SortNestedKeys; + +interface BasicSort { + displayName: string; + sortKey: Exclude; +} + +interface PublishedFlowSort { + displayName: string; + sortKey: "publishedFlows"; + nestedKey: keyof PublishedFlowSummary; +} + +type SortObject = PublishedFlowSort | BasicSort; + +const sortArray: SortObject[] = [ + { displayName: "Name", sortKey: "name" }, + { displayName: "Last updated", sortKey: "updatedAt" }, + { displayName: "Status", sortKey: "status" }, + { + displayName: "Last published", + sortKey: "publishedFlows", + nestedKey: "publishedAt", + }, +]; + +const sortFlowList = ( + a: string | boolean, + b: string | boolean, + sortDirection: SortDirection, +) => { + if (a < b) { + return sortDirection === "asc" ? 1 : -1; + } + if (a > b) { + return sortDirection === "asc" ? -1 : 1; + } + return 0; +}; + +export const SortFlowsSelect = ({ + flows, + setFlows, +}: { + flows: FlowSummary[]; + setFlows: React.Dispatch>; +}) => { + const [sortBy, setSortBy] = useState(sortArray[0]); + const [sortDirection, setSortDirection] = useState("asc"); + useEffect(() => { + const { sortKey } = sortBy; + + if (sortKey === "publishedFlows") { + const sortedFlows = flows?.sort((a: FlowSummary, b: FlowSummary) => { + const { nestedKey } = sortBy; + + // auto sort unpublished flows to bottom + if (!a[sortKey][0]) return 1; + if (!b[sortKey][0]) return -1; + + const aValue = a[sortKey][0][nestedKey]; + const bValue = b[sortKey][0][nestedKey]; + + return sortFlowList(aValue, bValue, sortDirection); + }); + sortedFlows && setFlows([...sortedFlows]); + } else { + const sortedFlows = flows?.sort((a: FlowSummary, b: FlowSummary) => + sortFlowList(a[sortKey], b[sortKey], sortDirection), + ); + sortedFlows && setFlows([...sortedFlows]); + } + }, [sortBy, sortDirection]); + + return ( + + { + const targetKey = e.target.value as SortTypes; + const newSortObject = sortArray.find( + (sortObject) => sortObject.sortKey === targetKey, + ); + newSortObject && setSortBy(newSortObject); + }} + > + {sortArray.map(({ displayName, sortKey }) => ( + + {displayName} + + ))} + + + sortDirection === "asc" + ? setSortDirection("desc") + : setSortDirection("asc") + } + > + {sortDirection === "asc" ? : } + + + ); +};