refactor: enhance FormInput and FormSelect components with validation feedback and layout adjustments
This commit is contained in:
parent
03c4988ff1
commit
962102d44c
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user