From ad816b927645e9033fcb6ea269b3cf41cc3d1efa Mon Sep 17 00:00:00 2001 From: Ian Jones <51156018+ianjon3s@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:13:44 +0100 Subject: [PATCH] feat: Updated editor teams and team selection pages (#3303) --- .../ui-driven/src/create-flow/helpers.ts | 2 +- e2e/tests/ui-driven/src/login.spec.ts | 6 +- editor.planx.uk/src/pages/Team.tsx | 179 +++++++++--------- editor.planx.uk/src/pages/Teams.tsx | 118 ++++++++---- editor.planx.uk/src/routes/authenticated.tsx | 6 +- editor.planx.uk/src/ui/editor/Dashboard.tsx | 22 +++ 6 files changed, 203 insertions(+), 130 deletions(-) create mode 100644 editor.planx.uk/src/ui/editor/Dashboard.tsx diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index edaea7b683..842cf6e430 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -29,6 +29,6 @@ export async function getTeamPage({ teamName: string; }): Promise { const page = await getAdminPage({ browser, userId }); - await page.locator("h2", { hasText: teamName }).click(); + await page.locator("h3", { hasText: teamName }).click(); return page; } diff --git a/e2e/tests/ui-driven/src/login.spec.ts b/e2e/tests/ui-driven/src/login.spec.ts index ea7df20615..62481fc98c 100644 --- a/e2e/tests/ui-driven/src/login.spec.ts +++ b/e2e/tests/ui-driven/src/login.spec.ts @@ -37,7 +37,7 @@ test.describe("Login", () => { return response.url().includes("/graphql"); }); - const team = page.locator("h2", { hasText: context.team.name }); + const team = page.locator("h3", { hasText: context.team.name }); await expect(team).toBeVisible(); }); @@ -50,7 +50,7 @@ test.describe("Login", () => { }); await page.goto("/"); - const teamLink = page.locator("h2").filter({ hasText: context.team.name }); + const teamLink = page.locator("h3").filter({ hasText: context.team.name }); await teamLink.waitFor(); // wait for this to be visible // drop graphql requests @@ -67,7 +67,7 @@ test.describe("Login", () => { route.continue(); }); await expect( - page.locator("h1").filter({ hasText: "My services" }), + page.locator("h1").filter({ hasText: "Services" }), ).toBeVisible(); await expect(page.getByText(toastText)).toBeHidden(); }); diff --git a/editor.planx.uk/src/pages/Team.tsx b/editor.planx.uk/src/pages/Team.tsx index d530c235f8..fc6ee62a4c 100644 --- a/editor.planx.uk/src/pages/Team.tsx +++ b/editor.planx.uk/src/pages/Team.tsx @@ -1,10 +1,11 @@ import { gql } from "@apollo/client"; -import Add from "@mui/icons-material/Add"; +import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import Edit from "@mui/icons-material/Edit"; import Visibility from "@mui/icons-material/Visibility"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import ButtonBase from "@mui/material/ButtonBase"; +import Container from "@mui/material/Container"; import Dialog from "@mui/material/Dialog"; import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; @@ -12,10 +13,11 @@ 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 orderBy from "lodash/orderBy"; 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 Dashboard from "ui/editor/Dashboard"; import { slugify } from "utils"; import { client } from "../lib/graphql"; @@ -24,21 +26,11 @@ import { useStore } from "./FlowEditor/lib/store"; import { formatLastEditMessage } from "./FlowEditor/utils"; const Root = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.dark, - color: "#fff", + backgroundColor: theme.palette.background.default, width: "100%", - flex: 1, - justifyContent: "flex-start", - alignItems: "center", -})); - -const Dashboard = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.dark, - color: "#fff", - width: "100%", - maxWidth: 600, - margin: "auto", - padding: theme.spacing(8, 0, 4, 0), + display: "flex", + alignItems: "flex-start", + flexGrow: 1, })); const DashboardList = styled("ul")(({ theme }) => ({ @@ -50,7 +42,13 @@ const DashboardList = styled("ul")(({ theme }) => ({ const DashboardListItem = styled("li")(({ theme }) => ({ listStyle: "none", position: "relative", - padding: theme.spacing(2.5, 2), + color: theme.palette.common.white, + margin: theme.spacing(1, 0), + background: theme.palette.text.primary, + display: "flex", + justifyContent: "space-between", + alignItems: "stretch", + borderRadius: "2px", })); const DashboardLink = styled(Link)(({ theme }) => ({ @@ -59,21 +57,23 @@ const DashboardLink = styled(Link)(({ theme }) => ({ textDecoration: "none", color: "currentColor", fontWeight: FONT_WEIGHT_SEMI_BOLD, - marginBottom: theme.spacing(1.5), - marginTop: 0, + padding: theme.spacing(2), + margin: 0, + width: "100%", + "&:focus-within": { + ...borderedFocusStyle, + }, })); const StyledSimpleMenu = styled(SimpleMenu)(({ theme }) => ({ - position: "absolute", - top: theme.spacing(2), - right: theme.spacing(1), + display: "flex", + borderLeft: `1px solid ${theme.palette.border.main}`, })); -const LinkSubText = styled(Box)(() => ({ - color: "#aaa", - "& a": { - color: "#fff", - }, +const LinkSubText = styled(Box)(({ theme }) => ({ + color: theme.palette.grey[400], + fontWeight: "normal", + paddingTop: "0.5em", })); const Confirm = ({ @@ -113,13 +113,12 @@ const Confirm = ({ ); const AddButtonRoot = styled(ButtonBase)(({ theme }) => ({ - width: "100%", - padding: theme.spacing(4), fontSize: 20, - backgroundColor: "rgba(255,255,255,0.25)", - display: "block", + display: "flex", + alignItems: "center", textAlign: "left", - marginTop: theme.spacing(2), + color: theme.palette.primary.main, + fontWeight: FONT_WEIGHT_SEMI_BOLD, })); function AddButton({ @@ -131,7 +130,7 @@ function AddButton({ }): FCReturn { return ( - {children} + {children} ); } @@ -191,17 +190,17 @@ const FlowItem: React.FC = ({ /> )} - - + + {flow.name} - + {formatLastEditMessage( flow.operations[0].createdAt, flow.operations[0]?.actor, )} - + {useStore.getState().canUserEditTeam(teamSlug) && ( { return ( - - - My services - - {useStore.getState().canUserEditTeam(slug) ? ( - - ) : ( - - )} - - {useStore.getState().canUserEditTeam(slug) && ( - { - const newFlowName = prompt("Service name"); - if (newFlowName) { - const newFlowSlug = slugify(newFlowName); - useStore - .getState() - .createFlow(teamId, newFlowSlug, newFlowName) - .then((newId: string) => { - navigation.navigate(`/${slug}/${newId}`); - }); - } + + - Add a new service - - )} - {flows && ( - - {flows.map((flow: any) => ( - { - fetchFlows(); + + + Services + + {useStore.getState().canUserEditTeam(slug) ? ( + + ) : ( + + )} + + {useStore.getState().canUserEditTeam(slug) && ( + { + const newFlowName = prompt("Service name"); + if (newFlowName) { + const newFlowSlug = slugify(newFlowName); + useStore + .getState() + .createFlow(teamId, newFlowSlug, newFlowName) + .then((newId: string) => { + navigation.navigate(`/${slug}/${newId}`); + }); + } }} - /> - ))} - - )} + > + Add a new service + + )} + + {flows && ( + + {flows.map((flow: any) => ( + { + fetchFlows(); + }} + /> + ))} + + )} + ); diff --git a/editor.planx.uk/src/pages/Teams.tsx b/editor.planx.uk/src/pages/Teams.tsx index ea11e0d1b1..f8208dd2c9 100644 --- a/editor.planx.uk/src/pages/Teams.tsx +++ b/editor.planx.uk/src/pages/Teams.tsx @@ -1,71 +1,109 @@ -import Edit from "@mui/icons-material/Edit"; -import Visibility from "@mui/icons-material/Visibility"; import Box from "@mui/material/Box"; import Card from "@mui/material/Card"; +import Container from "@mui/material/Container"; import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; import { Team } from "@opensystemslab/planx-core/types"; import React from "react"; import { Link } from "react-navi"; +import { borderedFocusStyle } from "theme"; +import Dashboard from "ui/editor/Dashboard"; import { useStore } from "./FlowEditor/lib/store"; +interface TeamTheme { + slug: string; + primaryColour: string; +} + interface Props { teams: Array; + teamTheme: Array; } -const Root = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.dark, - color: "#fff", +export const Root = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, width: "100%", - flex: 1, + display: "flex", + alignItems: "flex-start", + flexGrow: 1, +})); + +const StyledLink = styled(Link)(() => ({ + textDecoration: "none", + "&:focus-within > div": { + ...borderedFocusStyle, + }, +})); + +const TeamCard = styled(Card)(({ theme }) => ({ + display: "flex", justifyContent: "flex-start", alignItems: "center", + marginBottom: theme.spacing(2), + color: theme.palette.text.primary, + outline: `1px solid ${theme.palette.border.light}`, + outlineOffset: "-1px", + borderRadius: "1px", })); -const Dashboard = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.dark, - color: "#fff", - width: "100%", - maxWidth: 600, - margin: "auto", - padding: theme.spacing(8, 0, 4, 0), +const TeamColourBand = styled(Box)(({ theme }) => ({ + display: "flex", + alignSelf: "stretch", + width: theme.spacing(1.5), + zIndex: 1, })); -const StyledLink = styled(Link)(() => ({ - textDecoration: "none", -})); +const Teams: React.FC = ({ teams, teamTheme }) => { + const canUserEditTeam = useStore.getState().canUserEditTeam; + + const editableTeams: Team[] = []; + const viewOnlyTeams: Team[] = []; -const Teams: React.FC = ({ teams }) => { + teams.forEach((team) => + canUserEditTeam(team.slug) + ? editableTeams.push(team) + : viewOnlyTeams.push(team), + ); + + const renderTeams = (teamsToRender: Array) => + teamsToRender.map((team) => { + return ( + + + + + {team.name} + + + + ); + }); return ( - - + + Select a team - - {teams.map(({ name, slug }) => ( - - - - {name} + {editableTeams.length > 0 && ( + <> + + My teams + + {renderTeams(editableTeams)} + + )} + + {viewOnlyTeams.length > 0 && ( + <> + + Other teams (view only) - {useStore.getState().canUserEditTeam(slug) ? ( - - ) : ( - - )} - - - ))} + {renderTeams(viewOnlyTeams)} + + )} + ); diff --git a/editor.planx.uk/src/routes/authenticated.tsx b/editor.planx.uk/src/routes/authenticated.tsx index 659b907d79..b6a4745a52 100644 --- a/editor.planx.uk/src/routes/authenticated.tsx +++ b/editor.planx.uk/src/routes/authenticated.tsx @@ -30,6 +30,10 @@ const editorRoutes = compose( id name slug + theme { + primaryColour: primary_colour + logo + } } } `, @@ -39,7 +43,7 @@ const editorRoutes = compose( return { title: makeTitle("Teams"), - view: , + view: , }; }), diff --git a/editor.planx.uk/src/ui/editor/Dashboard.tsx b/editor.planx.uk/src/ui/editor/Dashboard.tsx new file mode 100644 index 0000000000..a18472da46 --- /dev/null +++ b/editor.planx.uk/src/ui/editor/Dashboard.tsx @@ -0,0 +1,22 @@ +import Box from "@mui/material/Box"; +import { containerClasses } from "@mui/material/Container"; +import { styled } from "@mui/material/styles"; +import { HEADER_HEIGHT } from "components/Header"; +import React, { PropsWithChildren } from "react"; + +const Root = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + display: "flex", + flexDirection: "row", + width: "100%", + minHeight: `calc(100vh - ${HEADER_HEIGHT}px)`, + [`& > .${containerClasses.root}`]: { + paddingTop: theme.spacing(6), + paddingBottom: theme.spacing(6), + }, +})); + +export default function Dashboard(props: PropsWithChildren) { + return {props.children}; +}