From c9f8f309016fe8a4b3e737b1fc60f5efd482697c Mon Sep 17 00:00:00 2001 From: Kent Caparas <144957476+kcaparas1630@users.noreply.github.com> Date: Fri, 21 Jun 2024 20:30:14 -0700 Subject: [PATCH] feat: [BB-191] Feature** Fields and Soil Stage 2 (#192) * primary changes * format * configured button to accept children * format? * organized logic * format * last commit for the day, scratch again tomorrow * ugh * scratched earlier work, simpler workflow with dividers * removed fieldsInfo * forgot to save * remoevd setField hook * fixed some bugs, finished styling * Removed formik and values, don't need it anymore * Added id for fields * switched button types for add and next * format * separated into components, formik stays the same * forgot to save * imported StyledButtonGroupContainer * changed h4 into h2 * set initial values to empty for second adding new field * format * cleanup * deploy again * removed comment deploy * button styling consistency fixed * format * Created a function for buttonSizes using switch * format * error message style defined * format * minor revision done, ready for another review * format --- frontend/src/Commons/Button/Button.style.ts | 32 +-- frontend/src/Commons/Button/Button.tsx | 17 +- .../src/Commons/Button/ButtonSizeConstant.ts | 21 ++ .../Commons/CustomLink/CustomLink.style.ts | 14 +- .../FieldsAndSoil/FieldsAndSoil.style.ts | 34 +-- .../FieldsAndSoil/FieldsAndSoil.tsx | 210 ++++++++++-------- .../FieldsButtonComponent.styles.ts | 49 ++++ .../FieldsAndSoil/FieldsButtonComponent.tsx | 57 +++++ .../FieldsListComponent.styles.ts | 96 ++++++++ .../FieldsAndSoil/FieldsListComponent.tsx | 55 +++++ frontend/src/Commons/Input/ErrorMessage.css | 4 + .../src/Commons/Input/Field/CustomField.tsx | 6 +- .../Commons/Input/TextArea/CustomTextArea.tsx | 7 +- .../MainPageHeader/MainPageHeader.styles.ts | 1 + frontend/src/Constants/ComponentText.ts | 5 + frontend/src/Constants/InitialFarmDetails.ts | 2 +- .../src/Interface/FieldDetailsInterface.tsx | 1 + 17 files changed, 460 insertions(+), 151 deletions(-) create mode 100644 frontend/src/Commons/Button/ButtonSizeConstant.ts create mode 100644 frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsButtonComponent.styles.ts create mode 100644 frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsButtonComponent.tsx create mode 100644 frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsListComponent.styles.ts create mode 100644 frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsListComponent.tsx create mode 100644 frontend/src/Commons/Input/ErrorMessage.css diff --git a/frontend/src/Commons/Button/Button.style.ts b/frontend/src/Commons/Button/Button.style.ts index 36e910d4..5cef2ba5 100644 --- a/frontend/src/Commons/Button/Button.style.ts +++ b/frontend/src/Commons/Button/Button.style.ts @@ -1,31 +1,32 @@ import styled from '@emotion/styled'; import * as tokens from '@bcgov/design-tokens/js'; import screenSizes from '@Constants/ScreenSize'; +import ComponentText from '@Constants/ComponentText'; +import getButtonSize from './ButtonSizeConstant'; type StyledButtonProps = { size: string; - type: string; disabled: boolean; + radius: string; + actions: string; }; const StyledButton = styled.button` display: flex; align-items: center; justify-content: center; - max-height: ${(props) => (props.size === 'sm' ? '27px' : props.size === 'md' ? '25px' : '40px')}; + max-height: 42px; height: 100%; width: 100%; - max-width: ${(props) => (props.size === 'sm' ? '52px' : props.size === 'md' ? '200px' : '300px')}; - background-color: ${(props) => (props.type === 'button' || props.type === 'submit' + max-width: ${(props) => getButtonSize(props.size, ComponentText.ISMOBILE)}; + background-color: ${(props) => (props.actions === 'primary' ? tokens.surfaceColorPrimaryButtonDefault : tokens.surfaceColorSecondaryButtonDefault)}; - color: ${(props) => (props.type === 'button' || props.type === 'submit' + color: ${(props) => (props.actions === 'primary' ? tokens.typographyColorPrimaryInvert - : tokens.typographyColorPrimary)}; - border-radius: 8px; - border: ${(props) => (props.type === 'button' || props.type === 'submit' - ? 0 - : `1px solid ${tokens.surfaceColorBorderMedium}`)}; + : tokens.typographyColorSecondary)}; + border-radius: ${(props) => props.radius}; + border: ${(props) => (props.actions === 'primary' ? 0 : `1px solid ${tokens.surfaceColorBorderMedium}`)}; padding: 20px 30px; font-family: ${tokens.typographyFontFamiliesBcSans}; font-weight: ${tokens.typographyFontWeightsBold}; @@ -35,9 +36,14 @@ const StyledButton = styled.button` } @media (min-width: ${screenSizes.desktop}) { - max-height: ${(props) => (props.size === 'sm' ? '27px' : props.size === 'md' ? '25px' : '40px')}; - max-width: ${(props) => (props.size === 'sm' ? '52px' : props.size === 'md' ? '200px' : '300px')}; + max-width: ${(props) => getButtonSize(props.size, ComponentText.ISDESKTOP)}; } `; -export default StyledButton; +const StyledChildrenContainer = styled.div` + display: flex; + align-items: center; + gap: 5px; +`; + +export { StyledButton, StyledChildrenContainer }; diff --git a/frontend/src/Commons/Button/Button.tsx b/frontend/src/Commons/Button/Button.tsx index ff74e34e..65ba4ade 100644 --- a/frontend/src/Commons/Button/Button.tsx +++ b/frontend/src/Commons/Button/Button.tsx @@ -1,6 +1,8 @@ -import StyledButton from './Button.style'; +import React from 'react'; +import { StyledButton, StyledChildrenContainer } from './Button.style'; type ButtonSizes = 'sm' | 'md' | 'lg'; +type ButtonActions = 'primary' | 'secondary'; type ButtonTypes = 'button' | 'submit' | 'reset' | undefined; type ButtonProps = { @@ -9,6 +11,9 @@ type ButtonProps = { size?: ButtonSizes; disabled?: boolean; type?: ButtonTypes; + radius?: string; + actions?: ButtonActions; + children?: React.ReactNode; }; const Button = ({ @@ -16,7 +21,10 @@ const Button = ({ text, size = 'md', disabled = false, + radius = '8px', type = 'button', + actions = 'primary', + children, }: ButtonProps) => { const handleClickWrapper = () => { if (handleClick) { @@ -31,8 +39,13 @@ const Button = ({ onClick={handleClickWrapper} value="" type={type} + radius={radius} + actions={actions} > - {text} + + {children} + {text} + ); }; diff --git a/frontend/src/Commons/Button/ButtonSizeConstant.ts b/frontend/src/Commons/Button/ButtonSizeConstant.ts new file mode 100644 index 00000000..0af21ce5 --- /dev/null +++ b/frontend/src/Commons/Button/ButtonSizeConstant.ts @@ -0,0 +1,21 @@ +const getButtonSize = (size: string, isDesktop: boolean): string => { + let buttonSize; + + switch (size) { + case 'sm': + buttonSize = '62px'; + break; + case 'md': + buttonSize = '178px'; + break; + case 'lg': + buttonSize = isDesktop ? '483px' : '327px'; + break; + default: + buttonSize = '100%'; + break; + } + + return buttonSize; +}; +export default getButtonSize; diff --git a/frontend/src/Commons/CustomLink/CustomLink.style.ts b/frontend/src/Commons/CustomLink/CustomLink.style.ts index 5009cd1a..7d950cad 100644 --- a/frontend/src/Commons/CustomLink/CustomLink.style.ts +++ b/frontend/src/Commons/CustomLink/CustomLink.style.ts @@ -1,6 +1,7 @@ import styled from '@emotion/styled'; import * as tokens from '@bcgov/design-tokens/js'; import screenSizes from '@Constants/ScreenSize'; +import getButtonSize from '@Commons/Button/ButtonSizeConstant'; type StyledLinkProps = { size: string; @@ -10,18 +11,21 @@ const StyledLinkContainer = styled.div` display: flex; align-items: center; justify-content: center; - height: ${(props) => (props.size === 'sm' ? '27px' : props.size === 'md' ? '25px' : '40px')}; - width: ${(props) => (props.size === 'sm' ? '52px' : props.size === 'md' ? '200px' : '300px')}; + height: 100%; + width: 100%; + max-height: 42px; + max-width: ${(props) => getButtonSize(props.size, false)}; background-color: ${tokens.surfaceColorPrimaryButtonDefault}; color: ${tokens.typographyColorPrimaryInvert}; border-radius: 8px; border: ${`1px solid ${tokens.surfaceColorBorderMedium}`}; font-family: ${tokens.typographyFontFamiliesBcSans}; font-weight: ${tokens.typographyFontWeightsBold}; - + padding: 20px 30px; @media (min-width: ${screenSizes.desktop}) { - max-height: ${(props) => (props.size === 'sm' ? '27px' : props.size === 'md' ? '25px' : '40px')}; - max-width: ${(props) => (props.size === 'sm' ? '52px' : props.size === 'md' ? '200px' : '300px')}; + height: 100%; + max-width: ${(props) => getButtonSize(props.size, true)}; + width: 100%; } a { display: flex; diff --git a/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.style.ts b/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.style.ts index 52a2ea8a..c7b0196e 100644 --- a/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.style.ts +++ b/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.style.ts @@ -1,11 +1,7 @@ /** - * - * THIS IS A SKELETON! - * THIS IS STILL TO BE IMPLEMENTED! - * * @desc Styling with BC Design Tokens and Emotion for - * Farm Information Input Module - * @author @GDamaso + * Field and Soil Input Module + * @author @Kcaparas */ import styled from '@emotion/styled'; import screenSizes from '@Constants/ScreenSize'; @@ -44,28 +40,4 @@ const StyledTextAreaContainer = styled.div` } `; -const StyledButtonGroupContainer = styled.div` - margin-top: -10px; - display: flex; - flex-direction: row; - gap: 20px; -`; - -const StyledListContainer = styled.div` - display: flex; - flex-direction: row; - gap: 20px; -`; -const StyledListItem = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; -`; - -export { - StyledFarmInfo, - StyledTextAreaContainer, - StyledButtonGroupContainer, - StyledListContainer, - StyledListItem, -}; +export { StyledFarmInfo, StyledTextAreaContainer }; diff --git a/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.tsx b/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.tsx index 37fcb9aa..46d07c3c 100644 --- a/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.tsx +++ b/frontend/src/Commons/Forms/InputModules/FieldsAndSoil/FieldsAndSoil.tsx @@ -17,117 +17,133 @@ import CustomTextArea from '@Commons/Input/TextArea/CustomTextArea'; import { faWheatAwn } from '@fortawesome/free-solid-svg-icons'; import initialFarmDetails from '@Constants/InitialFarmDetails'; import FieldDetailInterface from 'src/Interface/FieldDetailsInterface'; -import { - StyledFarmInfo, - StyledTextAreaContainer, - StyledButtonGroupContainer, - StyledListContainer, - StyledListItem, -} from './FieldsAndSoil.style'; - -const initialFieldValues = initialFarmDetails.Fields[0]; +import FieldsButtonComponent from './FieldsButtonComponent'; +import FieldsListComponent from './FieldsListComponent'; +import { StyledFarmInfo, StyledTextAreaContainer } from './FieldsAndSoil.style'; +import { StyledButtonGroupContainer } from './FieldsButtonComponent.styles'; const FieldsAndSoilComponent: React.FC = ({ farmDetails, updateFarmDetails }) => { - const [fieldsInfo, setFieldsInfo] = useState(farmDetails); - + // Builds field info inside the field form module. + const [, setFieldsInfo] = useState(farmDetails); + const [fieldIndex, setFieldIndex] = useState(0); + const [initialFieldValues, setInitialFieldValues] = useState( + initialFarmDetails.Fields[fieldIndex], + ); + // Only triggered once, it would show list and persists. + const [isSubmitted, setSubmitted] = useState(false); + // Would trigger when new field button is clicked. + const [isFieldAdded, setFieldAdd] = useState(false); const validationSchema = Yup.object().shape({ FieldName: Yup.string().max(24).required('Required'), - Area: Yup.number().min(1).max(100).required('Required'), - Comments: Yup.string().max(200), + Area: Yup.number() + .min(1, 'Area should be higher than 1') + .max(100, 'Area should be lower than 100') + .required('Required'), + Comments: Yup.string().max(200, 'Comments should be lower than 200 chars'), }); - - const onSubmit = ( - values: FieldDetailInterface, - { setSubmitting }: { setSubmitting: (isSubmitting: boolean) => void }, - ): void => { + /** + * + * @param values : It's of type FieldDetailInterface, it calls, FieldName, Area, and Comments + * @desc User data is inputted and then pushed into FarmDetailsInterface, + * initial values for formik is then set to empty values, state hooks gets triggered + * @author @Kcaparas + */ + const addFieldData = (values: FieldDetailInterface): void => { setTimeout(() => { - const newFarmDetails: FarmDetailsInterface = { ...farmDetails }; - const newField: FieldDetailInterface = initialFieldValues; - newField.FieldName = values.FieldName; - newField.Area = values.Area; - newField.Comments = values.Comments; - newFarmDetails.Fields.push(newField); - updateFarmDetails(newFarmDetails); - setSubmitting(false); + const farmInfo: FarmDetailsInterface = { ...farmDetails }; + farmInfo.Fields.push({ + Id: fieldIndex, + FieldName: values.FieldName, + Area: values.Area, + Comments: values.Comments, + }); + setInitialFieldValues(farmInfo.Fields[0]); + setFieldsInfo(farmInfo); + setFieldIndex((prevIndex) => prevIndex + 1); + setSubmitted(true); + setFieldAdd(false); }, 400); }; - - const addFarmInfo = (values: FieldDetailInterface) => { - const newFarmDetails: FarmDetailsInterface = { ...farmDetails }; - const newField: FieldDetailInterface = initialFieldValues; - newField.FieldName = values.FieldName; - newField.Area = values.Area; - newField.Comments = values.Comments; - newFarmDetails.Fields.push(newField); - setFieldsInfo(newFarmDetails); + /** + * @desc updateFarmDetails goes into the new form. Refer to MainPage.tsx + * @author @Kcaparas + */ + const submitFarmInfo = () => { + const farmInfo: FarmDetailsInterface = { ...farmDetails }; + updateFarmDetails(farmInfo); }; + const addNewField = () => { + setFieldAdd(true); + }; return ( - - {({ values }) => ( -
- -
- - -
- - - - + + + + +