diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts index 00a334b171..521ce4a38c 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts @@ -25,6 +25,7 @@ export interface TeamStore { fetchCurrentTeam: () => Promise; updateTeamTheme: (theme: Partial) => Promise; updateTeamSettings: (teamSettings: Partial) => Promise; + createTeam: (newTeam: { name: string; slug: string }) => Promise; } export const teamStore: StateCreator< @@ -65,6 +66,12 @@ export const teamStore: StateCreator< theme: get().teamTheme, }), + createTeam: async (newTeam) => { + const { $client } = get(); + const isSuccess = await $client.team.create(newTeam); + return isSuccess; + }, + initTeamStore: async (slug) => { const { data } = await client.query({ query: gql` diff --git a/editor.planx.uk/src/pages/Team.tsx b/editor.planx.uk/src/pages/Team.tsx index 58e0238ccf..0161df5c34 100644 --- a/editor.planx.uk/src/pages/Team.tsx +++ b/editor.planx.uk/src/pages/Team.tsx @@ -112,7 +112,7 @@ const AddButtonRoot = styled(ButtonBase)(({ theme }) => ({ fontWeight: FONT_WEIGHT_SEMI_BOLD, })); -function AddButton({ +export function AddButton({ children, onClick, }: { diff --git a/editor.planx.uk/src/pages/Teams.tsx b/editor.planx.uk/src/pages/Teams.tsx index 38649d671b..50a14b21cb 100644 --- a/editor.planx.uk/src/pages/Teams.tsx +++ b/editor.planx.uk/src/pages/Teams.tsx @@ -4,11 +4,15 @@ 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 navigation from "lib/navigation"; import React from "react"; import { Link } from "react-navi"; import { borderedFocusStyle } from "theme"; +import Permission from "ui/editor/Permission"; +import { slugify } from "utils"; import { useStore } from "./FlowEditor/lib/store"; +import { AddButton } from "./Team"; interface TeamTheme { slug: string; @@ -46,7 +50,10 @@ const TeamColourBand = styled(Box)(({ theme }) => ({ })); const Teams: React.FC = ({ teams, teamTheme }) => { - const canUserEditTeam = useStore.getState().canUserEditTeam; + const [canUserEditTeam, createTeam] = useStore((state) => [ + state.canUserEditTeam, + state.createTeam, + ]); const editableTeams: Team[] = []; const viewOnlyTeams: Team[] = []; @@ -72,9 +79,47 @@ const Teams: React.FC = ({ teams, teamTheme }) => { }); return ( - - Select a team - + + + Select a team + + + { + const newTeamName = prompt("Team name"); + + if (newTeamName) { + const newSlug = slugify(newTeamName); + const teamSlugDuplicate = teams.find( + (team) => team.slug === newSlug, + ); + if (teamSlugDuplicate !== undefined) { + alert( + `A team with the name "${teamSlugDuplicate.name}" already exists. Enter a unique team name to continue.`, + ); + } else { + await createTeam({ + name: newTeamName, + slug: newSlug, + }); + navigation.navigate(`/${newSlug}`); + } + } + }} + > + Add a new team + + + {editableTeams.length > 0 && ( <> diff --git a/hasura.planx.uk/metadata/tables.yaml b/hasura.planx.uk/metadata/tables.yaml index 09c260266e..c339ea6d57 100644 --- a/hasura.planx.uk/metadata/tables.yaml +++ b/hasura.planx.uk/metadata/tables.yaml @@ -1633,6 +1633,26 @@ - name: team using: foreign_key_constraint_on: team_id + insert_permissions: + - role: platformAdmin + permission: + check: {} + columns: + - id + - team_id + - production_bops_submission_url + - staging_bops_submission_url + - production_power_automate_api_key + - staging_power_automate_api_key + - has_planning_data + - production_file_api_key + - staging_file_api_key + - production_bops_secret + - staging_bops_secret + - production_govpay_secret + - staging_govpay_secret + - power_automate_webhook_url + comment: "" select_permissions: - role: api permission: @@ -1727,6 +1747,24 @@ - name: team using: foreign_key_constraint_on: team_id + insert_permissions: + - role: platformAdmin + permission: + check: {} + columns: + - id + - external_planning_site_name + - external_planning_site_url + - homepage + - help_email + - help_opening_hours + - help_phone + - email_reply_to_id + - team_id + - boundary_bbox + - reference_code + - boundary_url + comment: "" select_permissions: - role: api permission: @@ -1843,6 +1881,19 @@ - name: team using: foreign_key_constraint_on: team_id + insert_permissions: + - role: platformAdmin + permission: + check: {} + columns: + - id + - team_id + - favicon + - logo + - action_colour + - link_colour + - primary_colour + comment: "" select_permissions: - role: api permission: diff --git a/hasura.planx.uk/tests/team_integrations.test.js b/hasura.planx.uk/tests/team_integrations.test.js index a46a813f3b..33c11d6cc4 100644 --- a/hasura.planx.uk/tests/team_integrations.test.js +++ b/hasura.planx.uk/tests/team_integrations.test.js @@ -40,8 +40,10 @@ describe("team_integrations", () => { expect(i.queries).not.toContain("team_integrations"); }); - test("cannot create, update, or delete team_integrations", () => { - expect(i).toHaveNoMutationsFor("team_integrations"); + test("can create but, cannot update, or delete team_integrations", () => { + expect(i.mutations).toContain("insert_team_integrations"); + expect(i.mutations).not.toContain("update_team_integrations_by_pk"); + expect(i.mutations).not.toContain("delete_team_integrations"); }); }); diff --git a/hasura.planx.uk/tests/team_themes.test.js b/hasura.planx.uk/tests/team_themes.test.js index e89ac77fc7..6e22bff88a 100644 --- a/hasura.planx.uk/tests/team_themes.test.js +++ b/hasura.planx.uk/tests/team_themes.test.js @@ -37,7 +37,7 @@ describe("team_themes", () => { expect(i.queries).toContain("team_themes"); }); - test("cannot insert team_themes", () => { + test("cannot query insert team_themes", () => { expect(i.queries).not.toContain("insert_team_themes"); }); @@ -45,6 +45,10 @@ describe("team_themes", () => { expect(i.queries).toContain("team_themes"); }); + test("can insert team_themes", () => { + expect(i.mutations).toContain("insert_team_themes"); + }); + test("can mutate team_themes", async () => { expect(i.mutations).toContain("update_team_themes"); expect(i.mutations).toContain("update_team_themes_by_pk");