Skip to content

Commit

Permalink
feat: add form message UI
Browse files Browse the repository at this point in the history
  • Loading branch information
AhyoungRyu committed Sep 18, 2023
1 parent 428f33d commit c9e9322
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 9 deletions.
44 changes: 44 additions & 0 deletions src/components/CustomMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
Expand All @@ -52,6 +77,25 @@ export default function CustomMessage(props: Props) {
return <div>{<AdminMessage message={message} />}</div>;
}

if (mockForms) {
// if (!!message.extendedMessage.forms) {
return (
<BotMessageWithBodyInput
botUser={botUser}
message={message as UserMessage}
bodyComponent={
<FormMessage forms={mockForms[0]} />
// <FormMessage forms={message.extendedMessage.forms} />
}
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 (
Expand Down
126 changes: 126 additions & 0 deletions src/components/FormInput.tsx
Original file line number Diff line number Diff line change
@@ -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 => (
<Label
className="sendbird-input-label"
style={css`
margin-bottom: 8px;
`}
type={LabelTypography.CAPTION_2}
color={LabelColors.ONBACKGROUND_2}
>
{children}
</Label>
);

const Root = styled.div<Pick<InputProps, 'hasError'>>`
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<HTMLInputElement>) => void;
}
const FormInput = (props: InputProps) => {
const {
name,
required,
disabled,
hasError,
isValid,
value,
onChange,
placeHolder,
} = props;

return (
<Root hasError={hasError}>
<div className="sendbird-input">
<InputLabel>{name}</InputLabel>
<InputContainer>
<Input
className="sendbird-input__input"
name={name}
required={required}
disabled={disabled}
value={value}
onChange={(event) => {
onChange?.(event);
}}
placeholder={placeHolder}
/>
{isValid && (
<CheckIcon
type={IconTypes.DONE}
fillColor={IconColors.SECONDARY}
width="24px"
height="24px"
/>
)}
</InputContainer>
{hasError && (
<ErrorLabel type={LabelTypography.CAPTION_4}>
Please check the value
</ErrorLabel>
)}
</div>
</Root>
);
};

export default FormInput;
114 changes: 114 additions & 0 deletions src/components/FormMessage.tsx
Original file line number Diff line number Diff line change
@@ -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<string, string>;
};
}

type FormValues = Record<
string,
{ value: string; hasError: boolean; isValid: boolean }
>;

export default function FormMessage(props: Props) {
const {
forms: { fields },
} = props;
const [formValues, setInputValue] = useState<FormValues>(
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 (
<Root>
{fields.map(({ title, placeholder, key, required, regex }) => (
<Input
key={key}
placeHolder={placeholder}
hasError={formValues[key].hasError}
isValid={formValues[key].isValid}
name={title}
required={required}
onChange={(event) => {
const value = event.target.value;
const isValid = regex.test(value);
setInputValue(() => ({
...formValues,
[key]: { value, hasError: !isValid, isValid },
}));
}}
/>
))}
<SubmitButton onClick={handleSubmit}>
<Label type={LabelTypography.BUTTON_2} color={LabelColors.ONCONTENT_1}>
Submit
</Label>
</SubmitButton>
</Root>
);
}
18 changes: 9 additions & 9 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down

0 comments on commit c9e9322

Please sign in to comment.