From c9e93222be86a786b3ff9d04f531fc9df10d2a3c Mon Sep 17 00:00:00 2001 From: Irene Ryu Date: Mon, 18 Sep 2023 17:17:27 +0900 Subject: [PATCH] feat: add form message UI --- src/components/CustomMessage.tsx | 44 +++++++++++ src/components/FormInput.tsx | 126 +++++++++++++++++++++++++++++++ src/components/FormMessage.tsx | 114 ++++++++++++++++++++++++++++ src/const.ts | 18 ++--- 4 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 src/components/FormInput.tsx create mode 100644 src/components/FormMessage.tsx diff --git a/src/components/CustomMessage.tsx b/src/components/CustomMessage.tsx index dda9cfe64..912ff992c 100644 --- a/src/components/CustomMessage.tsx +++ b/src/components/CustomMessage.tsx @@ -8,6 +8,7 @@ import AdminMessage from './AdminMessage'; import BotMessageWithBodyInput from './BotMessageWithBodyInput'; import CurrentUserMessage from './CurrentUserMessage'; import CustomMessageBody from './CustomMessageBody'; +import FormMessage from './FormMessage'; import ParsedBotMessageBody from './ParsedBotMessageBody'; import PendingMessage from './PendingMessage'; import SuggestedReplyMessageBody from './SuggestedReplyMessageBody'; @@ -31,6 +32,30 @@ type Props = { isBotWelcomeMessage: boolean; }; +const mockForms = [ + { + key: 'personal_info', + fields: [ + { + key: 'company_name', + title: 'Company Name', + input_type: 'text', + required: true, + regex: /^(?!\s*$).+/, + placeholder: 'Enter company name', + }, + { + key: 'phone_number', + title: 'Phone Number', + input_type: 'text', + required: true, + regex: /^(?!\s*$).+/, + placeholder: 'Enter phone number', + }, + ], + }, +]; + export default function CustomMessage(props: Props) { const { message, @@ -52,6 +77,25 @@ export default function CustomMessage(props: Props) { return
{}
; } + if (mockForms) { + // if (!!message.extendedMessage.forms) { + return ( + + // + } + bodyStyle={{ maxWidth: '320px', width: 'calc(100% - 98px)' }} + messageCount={allMessages.length} + chainTop={chainTop} + chainBottom={chainBottom} + isBotWelcomeMessage={isBotWelcomeMessage} + /> + ); + } + // Sent by current user if ((message as UserMessage).sender.userId !== botUser.userId) { return ( diff --git a/src/components/FormInput.tsx b/src/components/FormInput.tsx new file mode 100644 index 000000000..551084b23 --- /dev/null +++ b/src/components/FormInput.tsx @@ -0,0 +1,126 @@ +import Icon, { IconTypes, IconColors } from '@sendbird/uikit-react/ui/Icon'; +import { + default as UIKitLabel, + LabelTypography, + LabelColors, +} from '@sendbird/uikit-react/ui/Label'; +import { ReactElement, ChangeEvent, ReactNode } from 'react'; +import styled, { css } from 'styled-components'; + +export interface InputLabelProps { + children: ReactNode; +} + +const Label = styled(UIKitLabel)` + font-size: 11px; + position: relative; + bottom: 4px; +`; + +export const InputLabel = ({ children }: InputLabelProps): ReactElement => ( + +); + +const Root = styled.div>` + padding-bottom: 12px; + width: 100%; + .sendbird-input .sendbird-input__input { + background-color: #fff; + border: ${({ hasError }) => + `solid 1px ${hasError ? '#DE360B' : 'rgba(0, 0, 0, 0.12)'}`}; + } + .sendbird-input .sendbird-input__input:focus { + border: ${({ hasError }) => (hasError ? 'solid 1px #DE360B' : 'none')}; + box-shadow: none; + } +`; + +const Input = styled.input` + ::placeholder { + color: rgba(0, 0, 0, 0.38); + } +`; + +const ErrorLabel = styled(Label)` + position: relative; + top: 0; + color: #de360b; +`; + +const CheckIcon = styled(Icon)` + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); +`; + +const InputContainer = styled.div` + position: relative; +`; +export interface InputProps { + name: string; + required?: boolean; + disabled?: boolean; + isValid?: boolean; + hasError?: boolean; + value?: string; + placeHolder?: string; + onChange?: (event: ChangeEvent) => void; +} +const FormInput = (props: InputProps) => { + const { + name, + required, + disabled, + hasError, + isValid, + value, + onChange, + placeHolder, + } = props; + + return ( + +
+ {name} + + { + onChange?.(event); + }} + placeholder={placeHolder} + /> + {isValid && ( + + )} + + {hasError && ( + + Please check the value + + )} +
+
+ ); +}; + +export default FormInput; diff --git a/src/components/FormMessage.tsx b/src/components/FormMessage.tsx new file mode 100644 index 000000000..23ce3cb73 --- /dev/null +++ b/src/components/FormMessage.tsx @@ -0,0 +1,114 @@ +import Button from '@sendbird/uikit-react/ui/Button'; +import Label, { + LabelTypography, + LabelColors, +} from '@sendbird/uikit-react/ui/Label'; +import { useCallback, useState } from 'react'; +import styled from 'styled-components'; + +import Input from './FormInput'; +import { delay } from '../utils'; + +const Root = styled.div` + max-width: 244px; + display: flex; + flex-direction: column; + align-items: flex-start; + padding: 16px 12px; + gap: 8px; + border-radius: 16px; + background-color: #eeeeee; +`; + +const SubmitButton = styled(Button)` + width: 100%; +`; + +interface Field { + key: string; + title: string; + placeholder: string; + required: boolean; + regex: RegExp; +} +interface Props { + forms: { + key: string; + fields: Field[]; + data: Record; + }; +} + +type FormValues = Record< + string, + { value: string; hasError: boolean; isValid: boolean } +>; + +export default function FormMessage(props: Props) { + const { + forms: { fields }, + } = props; + const [formValues, setInputValue] = useState( + fields.reduce( + (acc, { key }) => ({ + ...acc, + [key]: { value: '', hasError: false, isValid: false }, + }), + {} + ) + ); + + const handleSubmit = useCallback(async () => { + try { + Object.keys(formValues).reduce((acc, key) => { + if (formValues[key].value.length === 0) { + acc[key] = formValues[key]; + setInputValue((prev) => ({ + ...prev, + [key]: { ...prev[key], hasError: true }, + })); + } + return acc; + }, {} as FormValues); + + const hasError = Object.values(formValues).some( + ({ hasError }) => hasError + ); + if (hasError) { + return; + } + await delay(500); + console.log(formValues); + } catch (error) { + console.error(error); + } + }, [formValues]); + + return ( + + {fields.map(({ title, placeholder, key, required, regex }) => ( + { + const value = event.target.value; + const isValid = regex.test(value); + setInputValue(() => ({ + ...formValues, + [key]: { value, hasError: !isValid, isValid }, + })); + }} + /> + ))} + + + + + ); +} diff --git a/src/const.ts b/src/const.ts index fd7a26068..5adba86cd 100644 --- a/src/const.ts +++ b/src/const.ts @@ -50,15 +50,15 @@ export const DEFAULT_CONSTANT: Constant = { ], }, firstMessageData: [ - // { - // data: { - // suggested_replies: [ - // 'What can I learn from Pre-K 8th grade?', - // 'Tell me about Math', - // ], - // }, - // message: "Hi~ I'm Khan Academy Support ChatBot. Ask me anything!", - // }, + { + data: { + suggested_replies: [ + 'What can I learn from Pre-K 8th grade?', + 'Tell me about Math', + ], + }, + message: "Hi~ I'm Khan Academy Support ChatBot. Ask me anything!", + }, ], createGroupChannelParams: { name: 'Sendbird AI Chatbot',