refactor: enhance FormInput and FormSelect components with validation feedback and layout adjustments

This commit is contained in:
Md Asif 2024-12-24 01:31:07 +05:30
parent 03c4988ff1
commit 962102d44c
5 changed files with 53 additions and 24 deletions

View File

@ -1,22 +1,38 @@
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { motion, AnimatePresence } from "motion/react";
function FormInput({ function FormInput({
value, value,
onChange, onChange,
maxLength=17, valid = true,
maxLength = 17,
readOnly = false, readOnly = false,
className = "", className = "",
type = "text", type = "text",
}) { }) {
return ( return (
<input <div>
readOnly={readOnly} <input
value={value} readOnly={readOnly}
className={`w-1/2 md:w-1/3 lg:w-1/4 h-10 px-2 rounded-full dark:bg-white dark:text-grey border-2 text-grey focus:outline-grey ${className}`} value={value}
onChange={onChange} className={`w-72 h-10 px-2 rounded-full dark:bg-white dark:text-grey border-2 text-grey focus:outline-grey ${className}`}
type={type} onChange={onChange}
maxLength={maxLength} type={type}
/> maxLength={maxLength}
/>
<AnimatePresence>
{!valid && (
<motion.div
className="text-sm text-error ml-3 pt-1"
initial={{ y: 15, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 15, opacity: 0 }}
key="cabinetIdError"
>
Invalid Value
</motion.div>
)}
</AnimatePresence>
</div>
); );
} }
@ -27,6 +43,7 @@ FormInput.propTypes = {
className: PropTypes.string, className: PropTypes.string,
type: PropTypes.string, type: PropTypes.string,
maxLength: PropTypes.number, maxLength: PropTypes.number,
valid: PropTypes.bool,
}; };
export default FormInput; export default FormInput;

View File

@ -1,11 +1,13 @@
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { motion, AnimatePresence } from "motion/react";
function FormSelect({ value, onChange, options, className }) { function FormSelect({ value, onChange, options, className, valid = true }) {
return ( return (
<div>
<select <select
value={value} value={value}
className={ className={
"w-1/2 md:w-1/3 lg:w-1/4 h-10 px-2 rounded-full dark:bg-white dark:text-grey border-2 text-grey focus:outline-grey " + "w-72 h-10 px-2 rounded-full dark:bg-white dark:text-grey border-2 text-grey focus:outline-grey " +
className className
} }
onChange={onChange} onChange={onChange}
@ -19,6 +21,20 @@ function FormSelect({ value, onChange, options, className }) {
</option> </option>
))} ))}
</select> </select>
<AnimatePresence>
{!valid && (
<motion.div
className="text-sm text-error ml-3 pt-1"
initial={{ y: 15, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 15, opacity: 0 }}
key="cabinetIdError"
>
Invalid Value
</motion.div>
)}
</AnimatePresence>
</div>
); );
} }
@ -26,6 +42,7 @@ FormSelect.propTypes = {
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
className: PropTypes.string, className: PropTypes.string,
valid: PropTypes.bool,
options: PropTypes.arrayOf( options: PropTypes.arrayOf(
PropTypes.shape({ PropTypes.shape({
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,

View File

@ -9,13 +9,11 @@ import FormField from "../components/FormField";
import FormInput from "../components/FormInput"; import FormInput from "../components/FormInput";
import FormSelect from "../components/FormSelect"; import FormSelect from "../components/FormSelect";
import Button from "../components/Button"; import Button from "../components/Button";
import { useToast } from "../hooks/useToast";
import productInfo from "../util/productList"; import productInfo from "../util/productList";
import ProductListTable from "../components/ProductListTable"; import ProductListTable from "../components/ProductListTable";
function AccountCreation() { function AccountCreation() {
const { t } = useTranslation(); const { t } = useTranslation();
const showToast = useToast();
const [notification] = useState({ const [notification] = useState({
visible: false, visible: false,
message: "", message: "",
@ -179,7 +177,6 @@ function AccountCreation() {
setAccountDetails(newValidationState); setAccountDetails(newValidationState);
if (!isValid) { if (!isValid) {
showToast("Highlighted fields are invalid", "error");
return; return;
} }
console.log("Form is valid", accountDetails); console.log("Form is valid", accountDetails);
@ -216,6 +213,7 @@ function AccountCreation() {
const renerField = (field) => { const renerField = (field) => {
const commonProps = { const commonProps = {
value: accountDetails[field.name], value: accountDetails[field.name],
valid: accountDetails[`${field.name}Valid`],
onChange: (e) => { onChange: (e) => {
const newAccountDetails = { ...accountDetails }; const newAccountDetails = { ...accountDetails };
newAccountDetails[field.name] = e.target.value; newAccountDetails[field.name] = e.target.value;

View File

@ -25,7 +25,7 @@ function CabinetMaintenace() {
<FormBox title={t("cabinetMaintenance")}> <FormBox title={t("cabinetMaintenance")}>
<div className="p-2 pt-7"> <div className="p-2 pt-7">
<div className="flex"> <div className="flex">
<label className="mr-4 text-lg text-black dark:text-primary-dark w-[10%]"> <label className="mr-4 text-lg text-black dark:text-primary-dark w-[10%]">
{t("operation")} {t("operation")}
</label> </label>
<div className="w-full"> <div className="w-full">
@ -45,13 +45,13 @@ function CabinetMaintenace() {
</option> </option>
<option value="create">{t("create")}</option> <option value="create">{t("create")}</option>
</select> </select>
<AnimatePresence initial={false}> <AnimatePresence>
{!operation.valid && ( {!operation.valid && (
<motion.div <motion.div
className="w-1/5 text-sm text-error ml-3 pt-1" className="w-1/5 text-sm text-error ml-3 pt-1"
initial={{ opacity: 0,scale: 0 }} initial={{ y: 15, opacity: 0 }}
animate={{ opacity: 1,scale: 1 }} animate={{ y: 0, opacity: 1 }}
exit={{ opacity: 0,scale: 0 }} exit={{ y: 15, opacity: 0 }}
key="cabinetIdError" key="cabinetIdError"
> >
Invalid Operation Invalid Operation
@ -60,7 +60,6 @@ function CabinetMaintenace() {
</AnimatePresence> </AnimatePresence>
</div> </div>
</div> </div>
</div> </div>
<Button text={t("next")} onClick={(e) => handleNext(e)} /> <Button text={t("next")} onClick={(e) => handleNext(e)} />
</FormBox> </FormBox>

View File

@ -4,7 +4,6 @@ import FormInput from "../components/FormInput";
import FormSelect from "../components/FormSelect"; import FormSelect from "../components/FormSelect";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useState } from "react"; import { useState } from "react";
import { useToast } from "../hooks/useToast";
import clsx from "clsx"; import clsx from "clsx";
import Button from "../components/Button"; import Button from "../components/Button";
import { lockerService } from "../services/locker.service"; import { lockerService } from "../services/locker.service";
@ -14,7 +13,6 @@ import Notification from "../components/Notification";
function LockerStatus() { function LockerStatus() {
const { t } = useTranslation(); const { t } = useTranslation();
const showToast = useToast();
const [lockerDetails, setLockerDetails] = useState({ const [lockerDetails, setLockerDetails] = useState({
cabinetId: "", cabinetId: "",
lockerId: "", lockerId: "",
@ -73,7 +71,6 @@ function LockerStatus() {
setLockerDetails(newValidationState); setLockerDetails(newValidationState);
if (!isValid) { if (!isValid) {
showToast("Highlighted fields are invalid", "error");
return; return;
} }
@ -101,6 +98,7 @@ function LockerStatus() {
const renderField = (field) => { const renderField = (field) => {
const commonProps = { const commonProps = {
value: lockerDetails[field.name], value: lockerDetails[field.name],
valid: lockerDetails[`${field.name}Valid`],
onChange: (e) => { onChange: (e) => {
const newLockerDetails = { ...lockerDetails }; const newLockerDetails = { ...lockerDetails };
newLockerDetails[field.name] = e.target.value.toUpperCase(); newLockerDetails[field.name] = e.target.value.toUpperCase();