-
Notifications
You must be signed in to change notification settings - Fork 2
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
feat: additional error messages for duplicate flow name and moving a flow #4244
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ import omitBy from "lodash/omitBy"; | |
import { customAlphabet } from "nanoid-good"; | ||
import en from "nanoid-good/locale/en"; | ||
import { type } from "ot-json0"; | ||
import { slugify } from "utils"; | ||
import type { StateCreator } from "zustand"; | ||
import { persist } from "zustand/middleware"; | ||
|
||
|
@@ -177,7 +178,11 @@ export interface EditorStore extends Store.Store { | |
setLastPublishedDate: (date: string) => void; | ||
isFlowPublished: boolean; | ||
makeUnique: (id: NodeId, parent?: NodeId) => void; | ||
moveFlow: (flowId: string, teamSlug: string) => Promise<any>; | ||
moveFlow: ( | ||
flowId: string, | ||
teamName: string, | ||
flowName: string, | ||
) => Promise<any>; | ||
moveNode: ( | ||
id: NodeId, | ||
parent?: NodeId, | ||
|
@@ -485,7 +490,8 @@ export const editorStore: StateCreator< | |
send(ops); | ||
}, | ||
|
||
moveFlow(flowId: string, teamSlug: string) { | ||
moveFlow(flowId: string, teamName: string, flowName: string) { | ||
const teamSlug = slugify(teamName); | ||
const valid = get().canUserEditTeam(teamSlug); | ||
if (!valid) { | ||
alert( | ||
|
@@ -507,11 +513,18 @@ export const editorStore: StateCreator< | |
}, | ||
) | ||
.then((res) => alert(res?.data?.message)) | ||
.catch(() => | ||
alert( | ||
"Failed to move this flow. Make sure you're entering a valid team name and try again", | ||
), | ||
); | ||
.catch(({ response }) => { | ||
const { data } = response; | ||
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. Iterated on this a few times to get the cleanest code, I settled on this as it balanced explicit variable naming with concise lines of code in what is quite a long function 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. Would appreciate feedback here, other iterations included a similar conditional check for when to display the 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 feels like clear, conditional error handling and meets requirements of more specific error messages I think 👍 (A frank question: would these few lines alone satisfy the original Trello ticket?) |
||
if (data.error.toLowerCase().includes("uniqueness violation")) { | ||
alert( | ||
`The ${teamName} team already has a service with name '${flowName}'. Rename the service and try again `, | ||
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: Is it also worth keeping the "Failed to move flow" prefix, in which case we should use "flow" not "service" consistently? |
||
); | ||
} else { | ||
alert( | ||
"Failed to move this flow. Make sure you're entering a valid team name and try again", | ||
); | ||
} | ||
}); | ||
}, | ||
|
||
moveNode( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -140,10 +140,10 @@ const FlowItem: React.FC<FlowItemProps> = ({ | |
refreshFlows(); | ||
}); | ||
}; | ||
const handleMove = (newTeam: string) => { | ||
const handleMove = (newTeam: string, flowName: string) => { | ||
useStore | ||
.getState() | ||
.moveFlow(flow.id, newTeam) | ||
.moveFlow(flow.id, newTeam, flowName) | ||
.then(() => { | ||
refreshFlows(); | ||
}); | ||
|
@@ -182,11 +182,11 @@ const FlowItem: React.FC<FlowItemProps> = ({ | |
onClick: async () => { | ||
const newName = prompt("New name", flow.name); | ||
if (newName && newName !== flow.name) { | ||
const newSlug = slugify(newName); | ||
const duplicateFlowName = flows?.find( | ||
(flow: any) => flow.slug === newSlug, | ||
const verifiedNameAndSlug = checkForDuplicateFlowName( | ||
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: "uniqueFlow" (then "uniqueFlow.slug", "uniqueFlow.name") would be more clear to me than "verified". We also have a mix of team and flow names/slugs throughout this file, so nice to be specific about which we're dealing with! |
||
newName, | ||
flows, | ||
); | ||
if (!duplicateFlowName) { | ||
if (verifiedNameAndSlug) { | ||
await client.mutate({ | ||
mutation: gql` | ||
mutation UpdateFlowSlug( | ||
|
@@ -209,16 +209,12 @@ const FlowItem: React.FC<FlowItemProps> = ({ | |
variables: { | ||
teamId: teamId, | ||
slug: flow.slug, | ||
newSlug: newSlug, | ||
newName: newName, | ||
newSlug: verifiedNameAndSlug.slug, | ||
newName: verifiedNameAndSlug.name, | ||
}, | ||
}); | ||
|
||
refreshFlows(); | ||
} else if (duplicateFlowName) { | ||
alert( | ||
`The flow "${newName}" already exists. Enter a unique flow name to continue`, | ||
); | ||
} | ||
} | ||
}, | ||
|
@@ -240,7 +236,7 @@ const FlowItem: React.FC<FlowItemProps> = ({ | |
`This flow already belongs to ${teamSlug}, skipping move`, | ||
); | ||
} else { | ||
handleMove(slugify(newTeam)); | ||
handleMove(newTeam, flow.name); | ||
} | ||
} | ||
}, | ||
|
@@ -279,6 +275,21 @@ const GetStarted: React.FC<{ flows: FlowSummary[] }> = ({ flows }) => ( | |
</Box> | ||
); | ||
|
||
const checkForDuplicateFlowName = (name: string, flows: FlowSummary[]) => { | ||
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. There is part of me which questions the position of this function in the file and if it should be placed in a file outside of the main component, but considering the other work happening right now with this file for Filters and Search, it seems better to consider this after these have been added and polished 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: the name of this function confuses me on a couple levels:
Would 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. Also more to your original question - I don't mind this helper function being added into this existing file right now, but I do prefer helper functions to be added to the bottom rather than in between component definitions. |
||
const newFlowSlug = slugify(name); | ||
const duplicateFlowName = flows?.find((flow) => flow.slug === newFlowSlug); | ||
|
||
if (duplicateFlowName) { | ||
const updatedName = prompt( | ||
`A service already exists with the name '${name}', enter another name`, | ||
name, | ||
); | ||
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: again I think we're using "service" and "flow" inconsistently and should keep our prior language here? Eg |
||
if (!updatedName) return; | ||
return checkForDuplicateFlowName(updatedName, flows); | ||
} | ||
return { slug: newFlowSlug, name: name }; | ||
}; | ||
|
||
const AddFlowButton: React.FC<{ flows: FlowSummary[] }> = ({ flows }) => { | ||
const { navigate } = useNavigation(); | ||
const { teamId, createFlow, teamSlug } = useStore(); | ||
|
@@ -287,18 +298,16 @@ const AddFlowButton: React.FC<{ flows: FlowSummary[] }> = ({ flows }) => { | |
const newFlowName = prompt("Service name"); | ||
if (!newFlowName) return; | ||
|
||
const newFlowSlug = slugify(newFlowName); | ||
const duplicateFlowName = flows?.find((flow) => flow.slug === newFlowSlug); | ||
const verifiedNameAndSlug = checkForDuplicateFlowName(newFlowName, flows); | ||
|
||
if (duplicateFlowName) { | ||
alert( | ||
`The flow "${newFlowName}" already exists. Enter a unique flow name to continue`, | ||
if (verifiedNameAndSlug) { | ||
const newId = await createFlow( | ||
teamId, | ||
verifiedNameAndSlug.slug, | ||
verifiedNameAndSlug.name, | ||
); | ||
return; | ||
navigate(`/${teamSlug}/${newId}`); | ||
} | ||
|
||
const newId = await createFlow(teamId, newFlowSlug, newFlowName); | ||
navigate(`/${teamSlug}/${newId}`); | ||
}; | ||
|
||
return <AddButton onClick={addFlow}>Add a new service</AddButton>; | ||
|
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.
Why is
moveFlow
now accepting ateamName
rather thanteamSlug
? How isslugify(teamName)
handling examples like "Barking & Dagenham", "Epsom & Ewell"?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.
Assuming you've swapped
teamSlug
toteamName
for the sake of error message formatting alone, I don't think it's a problem for the raw team slug to be in the error message (it's a very basic native alert still afterall!)