Update Tasks

This commit is contained in:
Possible
2024-06-26 15:42:26 +01:00
parent 3ce5e57376
commit 7adba815e2
16 changed files with 712 additions and 8 deletions
+3
View File
@@ -25,6 +25,8 @@ All this task must be done in 1 day
- in src/pages/AdminListReceipts.jsx - there is a table listing receipts
- fix the list table, the localReceiptData need data from src/utils/data.jsx, for some reason it is not getting that data
- fix the bind issue in src/components/MkdListTable/MkdListTableBindOperations.jsx - all //TO DO
the `MkdListTableV2` component has actions props which is an object of key:value pair of action definitions
For example the edit action,
@@ -44,6 +46,7 @@ All this task must be done in 1 day
},
}
</pre>
- complete the open and close action feature, update the status to open and close to see the above binding work, also not that close action is meant to show open if status is open, and open is only meant to show is status is closed - `lines -> 326,330,444,464`
> the edit action binds via the bind property to a column "receipt_status", if the value of receipt_status is 1 then the edit should be hidden, i.e not shown
@@ -0,0 +1,232 @@
import React, { useEffect } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import MkdSDK from "Utils/MkdSDK";
import {
GlobalContext,
RequestItems,
createRequest,
customRequest,
deleteRequest,
updateRequest,
} from "Context/Global";
import { AuthContext } from "Context/Auth";
import { MkdInput } from "Components/MkdInput";
import { InteractiveButton } from "Components/InteractiveButton";
import { AddButton } from "Components/AddButton";
export const ActionConfirmation = ({
data = { id: null },
options = { endpoint: null, method: "GET" },
onSuccess,
onClose,
multiple = false,
action = "",
mode = "create",
table = "",
inputConfirmation = true,
}) => {
let sdk = new MkdSDK();
const schema = yup
.object({
confirm: yup
.string()
.required()
.oneOf([action], `Confirmation must be "${action}"`),
})
.required();
const {
state: { createModel, updateModel, deleteModel },
dispatch: globalDispatch,
} = React.useContext(GlobalContext);
const { state: authState, dispatch } = React.useContext(AuthContext);
const {
register,
handleSubmit,
setValue,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
const onSubmit = async () => {
if (!multiple) {
switch (mode) {
case "create":
return createData(data);
case "update":
return editData(data);
case "delete":
return deleteData(data);
case "custom":
return customData(data);
case "manual":
return manualData(data);
}
} else {
makeRequests();
}
};
const makeRequests = async () => {
const result = await createRequest(globalDispatch, dispatch, table, data);
if (!result.error && onSuccess) {
onSuccess();
}
};
const editData = async (data) => {
console.log("data >>", data);
const result = await updateRequest(
globalDispatch,
dispatch,
table,
data?.id,
data
);
if (!result?.error && onSuccess) {
onSuccess();
}
};
const createData = async (data) => {
if (action === "move") return moveRequest(data);
const result = await createRequest(globalDispatch, dispatch, table, data);
if (!result?.error && onSuccess) {
onSuccess();
}
};
const deleteData = async (data) => {
const result = await deleteRequest(
globalDispatch,
dispatch,
table,
data.id
);
if (!result?.error && onSuccess) {
onSuccess();
}
};
const customData = async (data) => {
const result = await customRequest(
globalDispatch,
dispatch,
{
endpoint: options?.endpoint,
method: options?.method,
payload: data,
},
RequestItems.createModel
);
if (
result &&
result?.hasOwnProperty("error") &&
!result?.error &&
onSuccess
) {
onSuccess(result);
}
};
const manualData = (data) => {
if (onSuccess) {
onSuccess(data);
}
};
const moveRequest = async (data) => {
const result = await customRequest(
globalDispatch,
dispatch,
{
endpoint: "/v3/api/custom/qualitysign/inventory/move",
method: "POST",
payload: data,
},
RequestItems.createModel
);
if (!result?.error && onSuccess) {
onSuccess(result);
}
};
React.useEffect(() => {
if (!inputConfirmation) {
setValue("confirm", action);
}
}, [inputConfirmation]);
return (
// <div className={`px-5 ${multiple ? "flex justify-center" : ""}`}>
<div className="mx-auto flex h-fit flex-col items-center justify-start rounded !font-inter leading-snug tracking-wide">
<form
className={`flex h-fit w-full flex-col text-start`}
onSubmit={handleSubmit(onSubmit)}
>
<div className="space-y-5">
<div className="my-2">
<div>
Are you sure you want to {action}{" "}
{data?.id && data?.id?.length && data?.id?.length > 1
? "these"
: "this"}{" "}
{table}?
</div>
</div>
<div className={`!mb-10 ${inputConfirmation ? "" : "hidden"}`}>
<MkdInput
type={"text"}
page={"items"}
name={`confirm`}
errors={errors}
register={register}
label={
<div className="font-bold text-black">
Type '{action}' below
</div>
}
className={"grow"}
/>
</div>
<div className="mt-5 flex w-full grow gap-5">
<AddButton
type="button"
onClick={() => onClose()}
className="grow self-end !border-soft-200 !bg-transparent font-bold !text-[#4F46E5]"
>
Cancel
</AddButton>
<InteractiveButton
type="submit"
loading={
createModel?.loading ||
updateModel?.loading ||
deleteModel?.loading
}
disabled={
createModel?.loading ||
updateModel?.loading ||
deleteModel?.loading
}
className="focus:shadow-outline bg-primaryBlue !w-1/2 self-end rounded px-4 py-2 font-bold capitalize text-white focus:outline-none"
>
{action}
</InteractiveButton>
</div>
</div>
</form>
</div>
);
};
export default ActionConfirmation;
@@ -0,0 +1 @@
export { default as ActionConfirmation } from "./ActionConfirmation";
@@ -0,0 +1,48 @@
import React, { useEffect } from "react";
import { Modal } from "Components/Modal";
import { ActionConfirmation } from "Components/ActionConfirmation";
export const ActionConfirmationModal = ({
data = { id: null },
options = { endpoint: null, method: "GET" },
onSuccess,
onClose,
multiple = false,
action = "",
mode = "create",
table = "",
title = "",
isOpen = false,
inputConfirmation = true,
modalClasses = {
modalDialog:
"max-h-[90%] min-h-[12rem] overflow-y-auto !w-full md:!w-[29.0625rem]",
modal: "h-full",
},
}) => {
return (
<Modal
isOpen={isOpen}
modalCloseClick={onClose}
title={title}
modalHeader
classes={modalClasses}
>
{isOpen && (
<ActionConfirmation
data={data}
mode={mode}
table={table}
action={action}
onClose={onClose}
options={options}
multiple={multiple}
onSuccess={onSuccess}
inputConfirmation={inputConfirmation}
/>
)}
</Modal>
);
};
export default ActionConfirmationModal;
@@ -0,0 +1 @@
export { default as ActionConfirmationModal } from "./ActionConfirmationModal";
+1 -1
View File
@@ -37,7 +37,7 @@ const AddButton = ({
onClick={onClickHandle}
className={`${animate && "animate-wiggle"} ${
classes.button
} relative flex h-[3rem] cursor-pointer items-center justify-center gap-2 overflow-hidden rounded-[.625rem] border border-primary bg-primary px-[.625rem] py-2 font-inter text-sm font-medium leading-loose tracking-wide text-white ${className}`}
} relative flex h-[3rem] cursor-pointer items-center justify-center gap-2 overflow-hidden rounded-[.625rem] border border-[#4F46E5] bg-[#4F46E5] px-[.625rem] py-2 font-inter text-sm font-medium leading-loose tracking-wide text-white ${className}`}
>
<>
<MoonLoader
@@ -8,11 +8,11 @@
text-decoration: none;
overflow: hidden;
cursor: pointer;
/* background-color: #7B1113; */
/* background-color:#4F46E5; */
}
.button:after {
content: "";
background-color: #7b1113;
background-color: #4f46e5;
/* background: #4f46e5; */
display: block;
position: absolute;
@@ -0,0 +1,43 @@
import React, { memo, useId } from "react";
import MoonLoader from "react-spinners/MoonLoader";
import classes from "./InteractiveButton.module.css";
const InteractiveButton = ({
loading = false,
disabled,
children,
type = "button",
className,
loaderclasses,
onClick,
color = "#ffffff",
}) => {
const override = {
borderColor: "#ffffff",
};
const id = useId();
return (
<button
type={type}
disabled={disabled}
className={`${classes.button} relative flex h-[3rem] cursor-pointer items-center justify-center gap-5 overflow-hidden rounded-[.625rem] border border-[#4F46E5] bg-[#4F46E5] px-[.625rem] py-[.5625rem] text-sm font-medium leading-none text-white ${className}`}
onClick={onClick}
>
<>
<MoonLoader
color={color}
loading={loading}
cssOverride={override}
size={14}
className={loaderclasses}
// aria-label="Loading Spinner"
data-testid={id}
/>
{children ? <span>{children}</span> : null}
</>
</button>
);
};
export default memo(InteractiveButton);
@@ -0,0 +1,32 @@
.button {
position: relative;
border: none;
color: #ffffff;
text-align: center;
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
text-decoration: none;
overflow: hidden;
cursor: pointer;
/* background-color: #4F46E5; */
}
.button:after {
content: "";
background-color: #4f46e5;
/* background: #4f46e5; */
display: block;
position: absolute;
padding-top: 300%;
padding-left: 350%;
margin-left: -20px !important;
margin-top: -120%;
opacity: 0;
transition: all 0.8s;
}
.button:active:after {
padding: 0;
margin: 0;
opacity: 1;
transition: 0s;
}
@@ -0,0 +1 @@
export { default as InteractiveButton } from "./InteractiveButton";
@@ -36,7 +36,7 @@ const MkdListTableRowDropdown = ({
actions[key]?.action([row[actionId]]);
}
}}
className={`!h-[2rem] !w-[2.0713rem] !border-soft-200 !bg-white`}
className={`!h-[2rem] !w-[2.0713rem] !border-gray-200 !bg-white`}
>
{actions[key]?.icon
? actions[key]?.icon
@@ -63,7 +63,7 @@ const MkdListTableRowDropdown = ({
// actions[key]?.action([row[actionId]]);
// }
}}
className={`!h-[2rem] !w-[2.0713rem] !border-soft-200 !bg-white`}
className={`!h-[2rem] !w-[2.0713rem] !border-gray-200 !bg-white`}
>
{actions[key]?.icon
? actions[key]?.icon
+89
View File
@@ -0,0 +1,89 @@
import React, { memo, useEffect, useRef } from "react";
import { MdClose } from "react-icons/md";
const Modal = ({
children,
title,
modalCloseClick,
modalHeader,
classes = { modal: "h-full", modalDialog: "h-[90%]", modalContent: "" },
page = "",
isOpen,
}) => {
const modalRef = useRef(null);
// useEffect(() => {
// if (isOpen) {
// document.body.style.overflow = "hidden";
// } else {
// document.body.style.overflow = "auto";
// }
// }, [isOpen]);
useEffect(() => {
const scrollableElements = document.querySelectorAll(
"body, .scrollable-container" // Add other selectors if needed
);
if (isOpen) {
scrollableElements.forEach((element) => {
element.style.overflow = "hidden";
});
} else {
scrollableElements.forEach((element) => {
element.style.overflow = "auto";
});
}
return () => {
scrollableElements.forEach((element) => {
element.style.overflow = "auto";
});
};
}, [isOpen]);
return (
<div
ref={modalRef}
style={{
zIndex: 999999999999,
backgroundColor: "rgba(0, 0, 0, 0.5)",
}}
className={`fixed bottom-0 left-0 right-0 top-0 flex w-full scale-0 items-center justify-center bg-[#00000099] p-5 backdrop-blur-sm transition-all ${
isOpen ? "scale-100" : "scale-0"
} ${classes?.modal}`}
>
<div
className={`${
page === "ManagePermissionAddRole" ? "w-fit" : "w-[80%]"
} relative overflow-auto rounded-lg bg-white pb-5 shadow ${
classes?.modalDialog
}`}
>
{modalHeader && (
<div
className={`sticky inset-x-0 top-0 !z-50 m-auto flex w-full justify-between border-b bg-white px-5 py-4`}
>
<div className="text-center font-inter text-[1.125rem] font-bold capitalize leading-[1.5rem] tracking-[-1.5%]">
{title}
</div>
<button
type="button"
className="modal-close cursor-pointer"
onClick={modalCloseClick}
>
<MdClose className="text-xl" />
</button>
</div>
)}
<div className={`-z-10 mt-4 px-5 ${classes?.modalContent}`}>
{children}
</div>
</div>
</div>
);
};
const ModalMemo = memo(Modal);
export { ModalMemo as Modal };
+56
View File
@@ -0,0 +1,56 @@
// import { Close, danger, warning } from 'Assets/svgs'
import React from 'react'
const ModalAlert = ( { closeModalFunction, message, title, messageClasses, titleClasses, buttonText = "OK" } ) => {
return (
<aside
className='fixed top-0 right-0 bottom-0 left-0 flex justify-center items-center '
style={ {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
zIndex: '1000',
} }
>
<section className='bg-white min-w-[25rem] w-[25rem] rounded-[.5rem] py-6 px-6 flex flex-col gap-6'>
<div className='flex justify-between '>
<div>
{/* <img src={danger} width={30} height={30} alt='danger' /> */ }
</div>
<button onClick={ closeModalFunction }>
{/* <img src={Close} width={30} height={30} alt='close' /> */ }
</button>
</div>
{
title ?
<div className={ ` ${ titleClasses }` }>
{ title }
{/* <div className='py-3 text-[1rem] leading-[1.75rem] text-[#333333] '>{message}</div> */ }
</div> : null
}
{
message ?
<div className={ `text-[1.5rem] leading-[1.5rem] text-[#667085] font-normal ${ messageClasses }` }>
{ message }
{/* <div className='py-3 text-[1rem] leading-[1.75rem] text-[#333333] '>{message}</div> */ }
</div> : null
}
<div className='w-full flex justify-center items-center font-medium text-[base] leading-[1.5rem]'>
<button
className='border border-[#DC5A5D] bg-[#DC5A5D] rounded-[.5rem] w-[10.375rem] h-[2.75rem] flex items-center justify-center text-white'
style={ {
width: '10.375rem',
height: '2.75rem',
} }
onClick={ closeModalFunction }
>
{ buttonText }
</button>
</div>
</section>
</aside>
)
}
export default ModalAlert
+98
View File
@@ -0,0 +1,98 @@
import React from "react";
import { CloseIcon, DangerIcon } from "Assets/svgs";
import { InteractiveButton } from "Components/InteractiveButton";
import { MdClose } from "react-icons/md";
// type Props = {
// closeModalFunction: () => void
// actionHandler: any
// message?: string
// title?: string
// rejectText?: string
// acceptText?: string
// titleClasses?: string
// messageClasses?: string
// }
const ModalPrompt = ({
open,
closeModalFunction,
actionHandler,
message,
title,
messageClasses,
titleClasses,
acceptText = "",
rejectText = "Cancel",
loading = false,
allowAccept = true,
}) => {
return (
<aside
className={`fixed inset-0 m-auto flex items-center justify-center backdrop-blur-sm transition-all ${
open ? "scale-100" : "scale-0"
}`}
style={{
backgroundColor: "rgba(0, 0, 0, 0.5)",
zIndex: "91000",
}}
>
<section className="flex w-[25rem] min-w-[25rem] flex-col gap-6 rounded-md bg-white px-6 py-6">
<div className="flex justify-between ">
<div>
{title ? (
<div className={` ${titleClasses}`}>
{title}
{/* <div className='py-3 text-[1rem] leading-[1.75rem] text-[#333333] '>{message}</div> */}
</div>
) : null}
{/* <DangerIcon className={`h-5 w-5`} /> */}
{/* <img src={danger} width={30} height={30} alt='danger' /> */}
</div>
<button disabled={loading} onClick={closeModalFunction}>
<MdClose className="text-xl" />
{/* <img src={Close} width={30} height={30} alt='close' /> */}
</button>
</div>
{/* {title ? (
<div className={` ${titleClasses}`}>
{title}
</div>
) : null} */}
{/* <div className='py-3 text-[1rem] leading-[1.75rem] text-[#333333] '>{message}</div> */}
{message ? (
<div className={`text-[#667085] ${messageClasses}`}>
{message}
{/* <div className='py-3 text-[1rem] leading-[1.75rem] text-[#333333] '>{message}</div> */}
</div>
) : null}
<div className="flex w-full justify-between gap-2 font-medium uppercase leading-[1.5rem] text-[base]">
<InteractiveButton
disabled={loading}
loading={loading}
className={`${
allowAccept ? "flex !w-1/2" : "hidden w-0"
} h-[2.75rem] !rounded-[.625rem] bg-primary uppercase !text-white`}
onClick={actionHandler}
>
{acceptText && acceptText.toLowerCase() !== "yes"
? `Yes ${acceptText}`
: "Yes"}
</InteractiveButton>
<button
disabled={loading}
className={`flex h-[2.75rem] items-center justify-center rounded-[.625rem] border border-[#d8dae5] text-[#667085] ${
allowAccept ? " w-1/2" : "grow"
}`}
onClick={closeModalFunction}
>
{rejectText}
</button>
</div>
</section>
</aside>
);
};
export default ModalPrompt;
+3
View File
@@ -0,0 +1,3 @@
export { Modal } from "./Modal";
export { default as ModalPrompt } from "./ModalPrompt";
export { default as ModalAlert } from "./ModalAlert";
+100 -3
View File
@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import MkdSDK from "Utils/MkdSDK";
import { useNavigate } from "react-router-dom";
import { AuthContext, tokenExpireError } from "Context/Auth";
@@ -8,10 +8,16 @@ import {
setGLobalProperty,
} from "Context/Global";
import { MkdListTableV2 } from "Components/MkdListTable";
import { EditIcon2, TrashIcon } from "Assets/svgs";
import {
CircleCheckMarkIcon,
EditIcon2,
RotateIcon,
TrashIcon,
} from "Assets/svgs";
import { AiFillEye } from "react-icons/ai";
import { operations } from "Components/MkdListTable/MkdListTableBindOperations";
import { receiptData } from "Utils/data";
import { ActionConfirmationModal } from "Components/ActionConfirmationModal";
let sdk = new MkdSDK();
@@ -309,12 +315,29 @@ const AdminListReceipts = () => {
dispatch: globalDispatch,
state: { confirmRequest },
} = React.useContext(GlobalContext);
const [selectedItems, setSelectedItems] = React.useState([]);
const [localReceiptData, setLocalReceiptData] = React.useState([]);
const [showOpenModal, setShowOpenModal] = React.useState(false);
const [showCloseModal, setShowCloseModal] = React.useState(false);
const onToggleModal = (modal, toggle, ids = []) => {
switch (modal) {
case "close":
// TO DO
setSelectedItems(ids);
break;
case "open":
// TO DO
setSelectedItems(ids);
break;
}
};
return (
<div className="overflow-x-auto rounded bg-white p-5 shadow">
<MkdListTableV2
defaultColumns={columns}
externalData={receiptData}
externalData={localReceiptData}
useExternalData={true}
tableRole={"admin"}
table={"receipts"}
@@ -355,6 +378,39 @@ const AdminListReceipts = () => {
ifValue: 1,
},
},
close: {
show: true,
action: (ids) => onToggleModal("close", true, ids),
multiple: true,
locations: ["buttons"],
children: "Close",
icon: <CircleCheckMarkIcon className="h-[.955rem] w-[.955rem]" />,
bind: {
column: "receipt_status",
action: "hide",
operator: "eq",
ifValue: 0,
},
},
open: {
show: true,
action: (ids) => onToggleModal("open", true, ids),
multiple: true,
locations: ["buttons"],
children: "Open",
icon: (
<RotateIcon
className="h-[.955rem] w-[.955rem]"
stroke={"#717179"}
/>
),
bind: {
column: "receipt_status",
action: "hide",
operator: "eq",
ifValue: 1,
},
},
add: {
show: false,
// action: () => navigate("/admin/add-receipts"),
@@ -373,6 +429,47 @@ const AdminListReceipts = () => {
// allowEditing
refreshRef={refreshRef}
/>
<ActionConfirmationModal
isOpen={showCloseModal}
title="Close Receipt"
modalClasses={{
modalDialog:
"max-h-[90%] min-h-[12rem] overflow-y-auto !w-full md:!w-[29.0625rem]",
modal: "h-full",
}}
data={{ id: selectedItems[0] }}
onClose={() => onToggleModal("close", false, [])}
onSuccess={(data) => {
// TO DO - Update the receipt_status to closed here
onToggleModal("close", false, []);
}}
action="close"
mode="manual"
multiple={false}
table="receipts"
inputConfirmation={false}
/>
<ActionConfirmationModal
isOpen={showOpenModal}
title="Open Receipt"
modalClasses={{
modalDialog:
"max-h-[90%] min-h-[12rem] overflow-y-auto !w-full md:!w-[29.0625rem]",
modal: "h-full",
}}
data={{ id: selectedItems[0] }}
onClose={() => onToggleModal("open", false, [])}
onSuccess={(data) => {
// TO DO - Update the receipt_status to open here
onToggleModal("open", false, []);
}}
action="open"
mode="manual"
multiple={false}
table="receipts"
inputConfirmation={false}
/>
</div>
);
};