diff --git a/frontend/src/components/appNav/NavButton/NavButton.tsx b/frontend/src/components/appNav/NavButton/NavButton.tsx new file mode 100644 index 0000000..31a0f6f --- /dev/null +++ b/frontend/src/components/appNav/NavButton/NavButton.tsx @@ -0,0 +1,36 @@ +/** + * @summary A reusable nav button component used for routing to different pages + * @param path - is the path the Link will route to + * @param text - is the text displayed on the button + * @param hex - HexValue for the Nav Button background + * @param icon - Image to place in Nav Container + * @type {(path: string, text: string, icon: string, hex: string)} + */ +import { Link } from 'react-router-dom'; +import { ButtonCont, ImageCont, TextCont, Image } from './navButton.styles'; + +export type RoutingLinkProps = { + path: string; + text: string; + icon?: string; + hex: string; +}; + +export default function NavButton({ path, text, icon, hex }: RoutingLinkProps) { + return ( + + + + {text} + + {text} + + + ); +} diff --git a/frontend/src/components/appNav/NavButton/navButton.styles.ts b/frontend/src/components/appNav/NavButton/navButton.styles.ts new file mode 100644 index 0000000..797c779 --- /dev/null +++ b/frontend/src/components/appNav/NavButton/navButton.styles.ts @@ -0,0 +1,55 @@ +/** + * @summary Styling for RoutingLink component + */ +import styled from '@emotion/styled'; + +type NavProps = { + hex: string; +}; + +export const ButtonCont = styled.div` + height: 75pt; + width: 220pt; + border-radius: 8pt; + align-items: center; + justify-content: flex-start; + background-color: ${({ hex }) => hex || '#000'}; + padding: 5pt; + display: flex; + &:hover { + transform: scale(0.95); + } +`; + +export const ImageCont = styled.div` + display: flex; + align-items: center; + justify-content: center; + width: 50pt; + height: 45pt; + border-radius: 8pt; + margin: 0 0 0 10pt; + padding: 0; +`; + +export const TextCont = styled.div` + display: flex; + flex-direction: column; + text-align: center; + justify-content: center; + font-size: 16pt; + text-wrap: auto; + width: 100%; + height: 70pt; + color: black; + margin: 0; + padding: 0 0 0 1em; +`; +export const Image = styled.img` + height: 100%; + display: flex; + margin: 0; + padding: 0; + justify-content: center; + align-items: center; +`; diff --git a/frontend/src/components/appNav/index.ts b/frontend/src/components/appNav/index.ts new file mode 100644 index 0000000..5c6c9b2 --- /dev/null +++ b/frontend/src/components/appNav/index.ts @@ -0,0 +1,3 @@ +import NavButton from './NavButton/NavButton'; + +export default NavButton; diff --git a/frontend/src/components/common/Checkbox/Checkbox.tsx b/frontend/src/components/common/Checkbox/Checkbox.tsx new file mode 100644 index 0000000..8eef46b --- /dev/null +++ b/frontend/src/components/common/Checkbox/Checkbox.tsx @@ -0,0 +1,28 @@ +/** + * @summary Reusable Checkbox Component + */ +import React from 'react'; +import { CheckboxWrapper, StyledLabel } from './checkbox.styles'; + +interface CheckboxProps { + label: string; + name: string; + checked: boolean; + onChange: (e: React.ChangeEvent) => void; +} + +function Checkbox({ label, name, checked, onChange }: CheckboxProps) { + return ( + + + {label} + + ); +} + +export default Checkbox; diff --git a/frontend/src/components/common/Checkbox/checkbox.styles.ts b/frontend/src/components/common/Checkbox/checkbox.styles.ts new file mode 100644 index 0000000..875e52f --- /dev/null +++ b/frontend/src/components/common/Checkbox/checkbox.styles.ts @@ -0,0 +1,14 @@ +/** + * @summary Styles for reusable Checkbox component + */ +import styled from '@emotion/styled'; + +export const CheckboxWrapper = styled.div` + display: flex; + align-items: center; +`; + +export const StyledLabel = styled.label` + margin-left: 8px; + font-size: 14px; +`; diff --git a/frontend/src/components/common/DropDown/DropDown.tsx b/frontend/src/components/common/DropDown/DropDown.tsx new file mode 100644 index 0000000..c24c53f --- /dev/null +++ b/frontend/src/components/common/DropDown/DropDown.tsx @@ -0,0 +1,38 @@ +/** + * @summary A reusable Dropdown component + */ +import React from 'react'; +import { DropdownWrapper, StyledLabel, StyledSelect } from './dropDown.styles'; + +interface DropdownProps { + label: string; + name: string; + value: string; + options: { value: string; label: string }[]; + onChange: (e: React.ChangeEvent) => void; + flex?: string; +} + +function Dropdown({ label, name, value, options, onChange, flex }: DropdownProps) { + return ( + + {label} + + {options.map((option) => ( + + ))} + + + ); +} + +export default Dropdown; diff --git a/frontend/src/components/common/DropDown/dropDown.styles.ts b/frontend/src/components/common/DropDown/dropDown.styles.ts new file mode 100644 index 0000000..9d52a0d --- /dev/null +++ b/frontend/src/components/common/DropDown/dropDown.styles.ts @@ -0,0 +1,28 @@ +/** + * @summary Styling for Dropdown component + */ +import styled from '@emotion/styled'; + +export const DropdownWrapper = styled.div<{ flex?: string }>` + display: flex; + flex-direction: column; + margin-bottom: 16px; + flex: ${({ flex }) => flex || '1'}; +`; + +export const StyledLabel = styled.label` + margin-bottom: 8px; + font-size: 14px; + text-align: left; +`; + +export const StyledSelect = styled.select` + padding: 8px; + font-size: 16px; + border: 1px solid #c8c8c8; + border-radius: 4px; + &:focus { + border-color: #c8c8c8; + outline: none; + } +`; diff --git a/frontend/src/components/common/Footer/footer.styles.ts b/frontend/src/components/common/Footer/footer.styles.ts index 3be0bef..23beb11 100644 --- a/frontend/src/components/common/Footer/footer.styles.ts +++ b/frontend/src/components/common/Footer/footer.styles.ts @@ -1,6 +1,5 @@ /** - * @summary Styles for reusable Header component - * @author Dallas Richmond + * @summary Styles for reusable Footer component */ import styled from '@emotion/styled'; import screenSizes from '../../../constants/screenSizes'; diff --git a/frontend/src/components/common/Header/header.styles.ts b/frontend/src/components/common/Header/header.styles.ts index 7f5a0c4..e54f333 100644 --- a/frontend/src/components/common/Header/header.styles.ts +++ b/frontend/src/components/common/Header/header.styles.ts @@ -1,6 +1,5 @@ /** * @summary Styles for reusable Header component - * @author Dallas Richmond */ import styled from '@emotion/styled'; import screenSizes from '../../../constants/screenSizes'; @@ -41,6 +40,7 @@ export const Banner = styled.div` align-items: center; margin: 0; `; + export const BannerRight = styled.div` min-width: 35pt; display: flex; diff --git a/frontend/src/components/common/InputField/InputField.tsx b/frontend/src/components/common/InputField/InputField.tsx new file mode 100644 index 0000000..63c7c95 --- /dev/null +++ b/frontend/src/components/common/InputField/InputField.tsx @@ -0,0 +1,30 @@ +/** + * @summary Reusable Input Field Component + */ +import React from 'react'; +import { InputWrapper, StyledLabel, StyledInput } from './inputField.styles'; + +interface InputFieldProps { + label: string; + type: string; + name: string; + value: string; + onChange: (e: React.ChangeEvent) => void; + flex?: string; +} + +function InputField({ label, type, name, value, onChange, flex }: InputFieldProps) { + return ( + + {label} + + + ); +} + +export default InputField; diff --git a/frontend/src/components/common/InputField/inputField.styles.ts b/frontend/src/components/common/InputField/inputField.styles.ts new file mode 100644 index 0000000..fe86d0c --- /dev/null +++ b/frontend/src/components/common/InputField/inputField.styles.ts @@ -0,0 +1,28 @@ +/** + * @summary Styling for InputField component + */ +import styled from '@emotion/styled'; + +export const InputWrapper = styled.div<{ flex?: string }>` + display: flex; + flex-direction: column; + margin-bottom: 16px; + flex: ${({ flex }) => flex || '1'}; +`; + +export const StyledLabel = styled.label` + margin-bottom: 8px; + font-size: 14px; + text-align: left; +`; + +export const StyledInput = styled.input` + padding: 8px; + font-size: 16px; + border: 1px solid #c8c8c8; + border-radius: 4px; + &:focus { + border-color: #c8c8c8; + outline: none; + } +`; diff --git a/frontend/src/components/common/RadioButton/RadioButton.tsx b/frontend/src/components/common/RadioButton/RadioButton.tsx new file mode 100644 index 0000000..44867e8 --- /dev/null +++ b/frontend/src/components/common/RadioButton/RadioButton.tsx @@ -0,0 +1,28 @@ +// frontend/src/components/common/RadioButton/RadioButton.tsx +import React from 'react'; +import { RadioButtonWrapper, StyledLabel } from './radioButton.styles'; + +interface RadioButtonProps { + label: string; + name: string; + value: string; + checked: boolean; + onChange: (e: React.ChangeEvent) => void; +} + +function RadioButton({ label, name, value, checked, onChange }: RadioButtonProps) { + return ( + + + {label} + + ); +} + +export default RadioButton; diff --git a/frontend/src/components/common/RadioButton/radioButton.styles.ts b/frontend/src/components/common/RadioButton/radioButton.styles.ts new file mode 100644 index 0000000..71d89a7 --- /dev/null +++ b/frontend/src/components/common/RadioButton/radioButton.styles.ts @@ -0,0 +1,14 @@ +/** + * @summary Styling for RadioButton component + */ +import styled from '@emotion/styled'; + +export const RadioButtonWrapper = styled.div` + display: flex; + align-items: center; +`; + +export const StyledLabel = styled.label` + margin-left: 8px; + font-size: 14px; +`; diff --git a/frontend/src/components/common/index.ts b/frontend/src/components/common/index.ts index bec1b29..59ab5cc 100644 --- a/frontend/src/components/common/index.ts +++ b/frontend/src/components/common/index.ts @@ -1,5 +1,9 @@ import Header from './Header/Header'; import { Button } from './Button/Button'; import Footer from './Footer/Footer'; +import InputField from './InputField/InputField'; +import RadioButton from './RadioButton/RadioButton'; +import Checkbox from './Checkbox/Checkbox'; +import Dropdown from './DropDown/DropDown'; -export { Header, Button, Footer }; +export { Header, Button, Footer, InputField, RadioButton, Checkbox, Dropdown }; diff --git a/frontend/src/routes/ViewRouter.tsx b/frontend/src/routes/ViewRouter.tsx index b50c146..fca5fa9 100644 --- a/frontend/src/routes/ViewRouter.tsx +++ b/frontend/src/routes/ViewRouter.tsx @@ -4,6 +4,7 @@ import { Routes, Route } from 'react-router-dom'; import LandingPage from '../views/LandingPage/LandingPage'; +import FarmInformation from '../views/FarmInformation/FarmInformation'; export default function ViewRouter() { return ( @@ -12,6 +13,10 @@ export default function ViewRouter() { path="/" Component={LandingPage} /> + ); } diff --git a/frontend/src/views/FarmInformation/FarmInformation.tsx b/frontend/src/views/FarmInformation/FarmInformation.tsx new file mode 100644 index 0000000..d6cae7e --- /dev/null +++ b/frontend/src/views/FarmInformation/FarmInformation.tsx @@ -0,0 +1,112 @@ +/** + * @summary The Farm Information page for the application + */ +import React, { useState } from 'react'; +import { + ViewContainer, + Card, + CardHeader, + Banner, + Heading, + InputFieldsContainer, + SelectorContainer, + RegionContainer, +} from './farmInformation.styles'; +import { InputField, RadioButton, Checkbox, Dropdown } from '../../components/common'; + +export default function FarmInformation() { + const [formData, setFormData] = useState({ + Year: '', + FarmName: '', + FarmRegion: '', + Crops: 'false', + HasVegetables: false, + HasBerries: false, + }); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value, type, checked } = e.target as HTMLInputElement; + setFormData({ ...formData, [name]: type === 'checkbox' ? checked : value }); + }; + + const regionOptions = [ + { value: '0', label: 'Select a region' }, + { value: '1', label: 'Bulkley-Nechako' }, + { value: '2', label: 'Cariboo' }, + { value: '3', label: 'Columbia Shuswap' }, + ]; + + return ( + + + + + Farm Information + + + + + + + + + + + I have crops + + + + {formData.Crops === 'true' && ( + + Select your crops: + + + + )} + + + ); +} diff --git a/frontend/src/views/FarmInformation/farmInformation.styles.ts b/frontend/src/views/FarmInformation/farmInformation.styles.ts new file mode 100644 index 0000000..bbeb3cd --- /dev/null +++ b/frontend/src/views/FarmInformation/farmInformation.styles.ts @@ -0,0 +1,94 @@ +/** + * @summary Styling for FarmInformation view + */ +import styled from '@emotion/styled'; +import screenSizes from '../../constants/screenSizes'; + +export const ViewContainer = styled.div` + position: absolute; + top: 0; + left: 0; + display: flex; + height: 100svh; + justify-content: center; + width: 100%; + align-items: center; +`; + +export const Card = styled.div` + background-color: rgba(255, 255, 255, 0.8); + height: 500px; + width: 600px; + padding-top: 0; + justify-content: flex-start; + align-items: center; + display: flex; + flex-direction: column; + object-fit: scale-down; + border-radius: 8px; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); + padding: 20px; + text-align: center; + position: relative; +`; + +export const CardHeader = styled.div` + background-color: rgba(200, 200, 200, 0.3); + padding: 0; + color: #fff; + display: flex; + justify-content: space-between; + align-items: center; + height: 65px; + width: 100%; + position: absolute; + top: 0; + left: 0; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + @media (min-width: ${screenSizes.tablet}) { + justify-content: flex-start; + padding-left: 2em; + } + z-index: 2000; +`; + +export const Banner = styled.div` + display: flex; + align-items: center; + margin: 0; +`; + +export const Heading = styled.h2` + color: #494949; + font-size: 16pt; + font-weight: 500; + min-width: 150px; + display: contents; + text-decoration: none; +`; + +export const InputFieldsContainer = styled.div` + display: flex; + width: 100%; + gap: 16px; + margin-top: 100px; +`; + +export const RegionContainer = styled.div` + display: flex; + gap: 16px; + margin-top: 16px; + width: 100%; + justify-content: flex-start; + align-items: center; +`; + +export const SelectorContainer = styled.div` + display: flex; + gap: 16px; + margin-top: 16px; + width: 100%; + justify-content: flex-start; + align-items: center; +`; diff --git a/frontend/src/views/LandingPage/LandingPage.tsx b/frontend/src/views/LandingPage/LandingPage.tsx index fb76b76..8a58051 100644 --- a/frontend/src/views/LandingPage/LandingPage.tsx +++ b/frontend/src/views/LandingPage/LandingPage.tsx @@ -1,6 +1,7 @@ /** * @summary The landing page for the application */ +import { useNavigate } from 'react-router-dom'; import { ButtonWrapper, ViewContainer, @@ -11,6 +12,8 @@ import { import { Button } from '../../components/common'; export default function LandingPage() { + const navigate = useNavigate(); + const handleUpload = () => { const upload = document.getElementById('fileUp'); if (upload) upload.click(); @@ -33,16 +36,14 @@ export default function LandingPage() { // The alert is temporary, will be removed once the data is being used // eslint-disable-next-line no-alert alert(data.toString()); + navigate('/farm-information'); } }; }; const newCalcHandler = () => { localStorage.clear(); - console.log('New Calculation'); - // The alert is temporary, will be removed once the data is being used - // eslint-disable-next-line no-alert - alert('New Calculation'); + navigate('/farm-information'); }; return (