From 89fbfd9dfc8df9eccad3bb315e218b6b12493b4c Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Tue, 1 Feb 2022 00:16:40 +0100 Subject: [PATCH] feat: make flow name updatable --- packages/backend/src/models/flow.ts | 4 +- .../components/EditableTypography/index.tsx | 76 +++++++++++++++++++ .../components/EditableTypography/style.ts | 25 ++++++ .../web/src/components/EditorLayout/index.tsx | 39 ++++++++-- .../web/src/graphql/mutations/update-flow.ts | 10 +++ 5 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 packages/web/src/components/EditableTypography/index.tsx create mode 100644 packages/web/src/components/EditableTypography/style.ts create mode 100644 packages/web/src/graphql/mutations/update-flow.ts diff --git a/packages/backend/src/models/flow.ts b/packages/backend/src/models/flow.ts index 59bd5569..de6891c1 100644 --- a/packages/backend/src/models/flow.ts +++ b/packages/backend/src/models/flow.ts @@ -2,7 +2,7 @@ import Base from './base'; import Step from './step'; class Flow extends Base { - id!: number; + id!: string; name: string; userId!: number; active: boolean; @@ -14,7 +14,7 @@ class Flow extends Base { type: 'object', properties: { - id: { type: 'integer' }, + id: { type: 'string' }, name: { type: 'string' }, userId: { type: 'integer' }, active: { type: 'boolean' }, diff --git a/packages/web/src/components/EditableTypography/index.tsx b/packages/web/src/components/EditableTypography/index.tsx new file mode 100644 index 00000000..7e247bbf --- /dev/null +++ b/packages/web/src/components/EditableTypography/index.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import Typography from '@mui/material/Typography'; +import type { TypographyProps } from '@mui/material/Typography'; +import EditIcon from '@mui/icons-material/Edit'; + +import { Box, TextField } from './style'; + +type EditableTypographyProps = TypographyProps & { + children: string; + onNameSubmit?: (value: string) => void; +}; + +const noop = () => null; + +function EditableTypography(props: EditableTypographyProps) { + const { children, onNameSubmit = noop, sx, ...typographyProps } = props; + const [editing, setEditing] = React.useState(false); + + const handleClick = React.useCallback(() => { + setEditing(editing => !editing); + }, []); + + const handleTextFieldClick = React.useCallback((event: React.SyntheticEvent) => { + event.stopPropagation(); + }, []); + + const handleTextFieldKeyDown = React.useCallback(async (event: React.KeyboardEvent) => { + const target = event.target as HTMLInputElement; + if (event.key === 'Enter') { + if (target.value !== children) { + await onNameSubmit(target.value); + } + + setEditing(false); + } + }, [children]); + + const handleTextFieldBlur = React.useCallback(async (event: React.FocusEvent) => { + const value = event.target.value; + + if (value !== children) { + await onNameSubmit(value); + } + + setEditing(false); + }, [onNameSubmit, children]); + + let component = ( + + {children} + + ); + + if (editing) { + component = ( + + ); + }; + + return ( + + + + {component} + + ); +} + +export default EditableTypography; diff --git a/packages/web/src/components/EditableTypography/style.ts b/packages/web/src/components/EditableTypography/style.ts new file mode 100644 index 00000000..928ed881 --- /dev/null +++ b/packages/web/src/components/EditableTypography/style.ts @@ -0,0 +1,25 @@ +import { styled } from '@mui/material/styles'; +import MuiBox from '@mui/material/Box'; +import MuiTextField from '@mui/material/TextField'; +import { inputClasses } from '@mui/material/Input'; + +type BoxProps = { + editing?: boolean; +} + +const boxShouldForwardProp = (prop: string) => !['editing'].includes(prop); +export const Box = styled(MuiBox, { shouldForwardProp: boxShouldForwardProp })` + display: flex; + flex: 1; + width: 300px; + height: 33px; + align-items: center; + ${({ editing }) => editing && `border-bottom: 1px dashed #000;`} +`; + +export const TextField = styled(MuiTextField)({ + width: '100%', + [`.${inputClasses.root}:before, .${inputClasses.root}:after, .${inputClasses.root}:hover`]: { + borderBottom: '0 !important', + } +}); diff --git a/packages/web/src/components/EditorLayout/index.tsx b/packages/web/src/components/EditorLayout/index.tsx index 14a67e89..7b3a9eaa 100644 --- a/packages/web/src/components/EditorLayout/index.tsx +++ b/packages/web/src/components/EditorLayout/index.tsx @@ -1,17 +1,18 @@ import * as React from 'react'; import { Link, useParams } from 'react-router-dom'; -import { useQuery } from '@apollo/client'; +import { useMutation, useQuery } from '@apollo/client'; import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; -import Typography from '@mui/material/Typography'; import FormControlLabel from '@mui/material/FormControlLabel'; import Switch from '@mui/material/Switch'; import IconButton from '@mui/material/IconButton'; import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew'; +import EditableTypography from 'components/EditableTypography'; import Container from 'components/Container'; import Editor from 'components/Editor'; import useFormatMessage from 'hooks/useFormatMessage'; +import { UPDATE_FLOW } from 'graphql/mutations/update-flow'; import { GET_FLOW } from 'graphql/queries/get-flow'; import type { Flow } from 'types/flow'; import * as URLS from 'config/urls'; @@ -19,9 +20,26 @@ import * as URLS from 'config/urls'; export default function EditorLayout(): React.ReactElement { const { flowId } = useParams(); const formatMessage = useFormatMessage(); - const { data } = useQuery(GET_FLOW, { variables: { id: flowId }}); + const [updateFlow] = useMutation(UPDATE_FLOW); + const { data, loading } = useQuery(GET_FLOW, { variables: { id: flowId }}); const flow: Flow = data?.getFlow; + const onFlowNameUpdate = React.useCallback(async (name: string) => { + await updateFlow({ + variables: { + id: flowId, + name, + }, + optimisticResponse: { + __typename: 'Mutation', + updateFlow: { + id: flow?.id, + name, + } + } + }); + }, [flow?.id]); + return ( <> @@ -35,9 +53,16 @@ export default function EditorLayout(): React.ReactElement { - - {flow?.name} - + {!loading && ( + + {flow?.name} + + )} @@ -52,7 +77,7 @@ export default function EditorLayout(): React.ReactElement { - {!flow && 'not found'} + {!flow && !loading && 'not found'} {flow && } diff --git a/packages/web/src/graphql/mutations/update-flow.ts b/packages/web/src/graphql/mutations/update-flow.ts new file mode 100644 index 00000000..58eca4b5 --- /dev/null +++ b/packages/web/src/graphql/mutations/update-flow.ts @@ -0,0 +1,10 @@ +import { gql } from '@apollo/client'; + +export const UPDATE_FLOW = gql` + mutation UpdateFlow($id: String!, $name: String!) { + updateFlow(id: $id, name: $name) { + id + name + } + } +`;