small fixes and animate side panel content
This commit is contained in:
@@ -17,7 +17,7 @@ export default function UserWithProfilePic({ prefix, name, profileImage, textSiz
|
|||||||
alt={name}
|
alt={name}
|
||||||
width={24}
|
width={24}
|
||||||
height={24}
|
height={24}
|
||||||
className="w-6 h-6 rounded-full"
|
className="rounded-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { ToastContainer } from "react-toastify";
|
|||||||
|
|
||||||
import approvalWorkflowsData from '../../demo/approval_workflows.json'; // to test locally
|
import approvalWorkflowsData from '../../demo/approval_workflows.json'; // to test locally
|
||||||
import Tip from "@/components/ApprovalWorkflows/Tip";
|
import Tip from "@/components/ApprovalWorkflows/Tip";
|
||||||
|
import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
|
||||||
const user = await requestUser(req, res);
|
const user = await requestUser(req, res);
|
||||||
@@ -149,106 +150,118 @@ export default function Home({ user, workflow, workflowAssignees, workflowReques
|
|||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className={`absolute inset-y-0 right-0 h-full bg-mti-purple-ultralight bg-opacity-50 shadow-xl shadow-mti-purple transition-all duration-300 overflow-hidden ${isPanelOpen ? 'w-2/5' : 'w-0'}`}>
|
{/* Side panel */}
|
||||||
{isPanelOpen && (
|
<AnimatePresence mode="wait">
|
||||||
<div className="relative inset-y-0 right-0 h-full p-6">
|
<LayoutGroup key="sidePanel">
|
||||||
<div className="flex flex-row gap-2">
|
<section className={`absolute inset-y-0 right-0 h-full bg-mti-purple-ultralight bg-opacity-50 shadow-xl shadow-mti-purple transition-all duration-300 overflow-hidden ${isPanelOpen ? 'w-2/5' : 'w-0'}`}>
|
||||||
<p className="text-2xl font-medium text-left align-middle">Step {selectedStepIndex + 1}</p>
|
{isPanelOpen && selectedStep && (
|
||||||
<div className="ml-auto flex flex-row">
|
<motion.div
|
||||||
<button
|
className="p-6"
|
||||||
className="min-w-fit max-h-fit text-lg font-medium flex items-center gap-2 text-left"
|
key={selectedStep.stepNumber}
|
||||||
onClick={() => setIsPanelOpen(false)}
|
initial={{ opacity: 0, x: 30 }}
|
||||||
>
|
animate={{ opacity: 1, x: 0 }}
|
||||||
Collapse
|
exit={{ opacity: 0, x: 30 }}
|
||||||
<MdOutlineDoubleArrow size={20} />
|
transition={{ duration: 0.2 }}
|
||||||
</button>
|
>
|
||||||
</div>
|
<div className="flex flex-row gap-2">
|
||||||
</div>
|
<p className="text-2xl font-medium text-left align-middle">Step {selectedStepIndex + 1}</p>
|
||||||
|
<div className="ml-auto flex flex-row">
|
||||||
<hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />
|
<button
|
||||||
|
className="min-w-fit max-h-fit text-lg font-medium flex items-center gap-2 text-left"
|
||||||
<div>
|
onClick={() => setIsPanelOpen(false)}
|
||||||
<div className="my-8 flex flex-row gap-4 items-center text-lg font-medium">
|
>
|
||||||
{selectedStep.stepType === "approval-by" ? (
|
Collapse
|
||||||
<>
|
<MdOutlineDoubleArrow size={20} />
|
||||||
<RiThumbUpLine size={30} />
|
</button>
|
||||||
Approval Step
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<FaWpforms size={30} />
|
|
||||||
Form Intake Step
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{selectedStep.completed ? (
|
|
||||||
<div className={"text-base font-medium text-gray-500 flex flex-col gap-6"}>
|
|
||||||
Approved on {new Date(selectedStep.completedDate!).toLocaleString("en-CA", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit",
|
|
||||||
hour12: false,
|
|
||||||
}).replace(", ", " at ")}
|
|
||||||
<div className="flex flex-row gap-1 text-sm">
|
|
||||||
<p className="text-base">Approved by:</p>
|
|
||||||
{(() => {
|
|
||||||
const assignee = workflowAssignees.find(
|
|
||||||
(assignee) => assignee.id === selectedStep.completedBy
|
|
||||||
);
|
|
||||||
return assignee ? (
|
|
||||||
<UserWithProfilePic
|
|
||||||
textSize="text-base"
|
|
||||||
prefix={getUserTypeLabelShort(assignee.type)}
|
|
||||||
name={assignee.name}
|
|
||||||
profileImage={assignee.profilePicture}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
"Unknown"
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
<p className="text-sm">No additional actions are required.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
) : (
|
|
||||||
<div className={"text-base font-medium text-gray-500"}>
|
|
||||||
One assignee is required to sign off to complete this step:
|
|
||||||
<div className="flex flex-col gap-2 mt-3">
|
|
||||||
{workflowAssignees.map(user => (
|
|
||||||
<span key={user.id}>
|
|
||||||
<UserWithProfilePic
|
|
||||||
textSize="text-sm"
|
|
||||||
prefix={getUserTypeLabelShort(user.type)}
|
|
||||||
name={user.name}
|
|
||||||
profileImage={user.profilePicture}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />
|
<hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />
|
||||||
|
|
||||||
<textarea
|
<div>
|
||||||
value={comments}
|
<div className="my-8 flex flex-row gap-4 items-center text-lg font-medium">
|
||||||
onChange={(e) => setComments(e.target.value)}
|
{selectedStep.stepType === "approval-by" ? (
|
||||||
placeholder="Input comments here"
|
<>
|
||||||
className="w-full h-80 p-2 border-2 rounded-xl shadow-lg focus:border-mti-purple focus:outline-none mt-4"
|
<RiThumbUpLine size={30} />
|
||||||
/>
|
Approval Step
|
||||||
<button onClick={saveComments} className="mt-4 px-6 py-2 bg-mti-purple-dark text-white rounded-full">
|
</>
|
||||||
Save Comments
|
) : (
|
||||||
</button>
|
<>
|
||||||
|
<FaWpforms size={30} />
|
||||||
|
Form Intake Step
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
{selectedStep.completed ? (
|
||||||
</div>
|
<div className={"text-base font-medium text-gray-500 flex flex-col gap-6"}>
|
||||||
)}
|
Approved on {new Date(selectedStep.completedDate!).toLocaleString("en-CA", {
|
||||||
</section>
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
hour12: false,
|
||||||
|
}).replace(", ", " at ")}
|
||||||
|
<div className="flex flex-row gap-1 text-sm">
|
||||||
|
<p className="text-base">Approved by:</p>
|
||||||
|
{(() => {
|
||||||
|
const assignee = workflowAssignees.find(
|
||||||
|
(assignee) => assignee.id === selectedStep.completedBy
|
||||||
|
);
|
||||||
|
return assignee ? (
|
||||||
|
<UserWithProfilePic
|
||||||
|
textSize="text-base"
|
||||||
|
prefix={getUserTypeLabelShort(assignee.type)}
|
||||||
|
name={assignee.name}
|
||||||
|
profileImage={assignee.profilePicture}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
"Unknown"
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm">No additional actions are required.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
) : (
|
||||||
|
<div className={"text-base font-medium text-gray-500"}>
|
||||||
|
One assignee is required to sign off to complete this step:
|
||||||
|
<div className="flex flex-col gap-2 mt-3">
|
||||||
|
{workflowAssignees.map(user => (
|
||||||
|
<span key={user.id}>
|
||||||
|
<UserWithProfilePic
|
||||||
|
textSize="text-sm"
|
||||||
|
prefix={getUserTypeLabelShort(user.type)}
|
||||||
|
name={user.name}
|
||||||
|
profileImage={user.profilePicture}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
value={comments}
|
||||||
|
onChange={(e) => setComments(e.target.value)}
|
||||||
|
placeholder="Input comments here"
|
||||||
|
className="w-full h-80 p-2 border-2 rounded-xl shadow-lg focus:border-mti-purple focus:outline-none mt-4"
|
||||||
|
/>
|
||||||
|
<button onClick={saveComments} className="mt-4 px-6 py-2 bg-mti-purple-dark text-white rounded-full">
|
||||||
|
Save Comments
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</LayoutGroup>
|
||||||
|
</AnimatePresence>
|
||||||
</Layout>
|
</Layout>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
const [entityId, setEntityId] = useState<string | null | undefined>(null);
|
const [entityId, setEntityId] = useState<string | null | undefined>(null);
|
||||||
const [entityTeachers, setEntityTeachers] = useState<TeacherUser[]>([]);
|
const [entityTeachers, setEntityTeachers] = useState<TeacherUser[]>([]);
|
||||||
const [entityCorporates, setEntityCorporates] = useState<CorporateUser[]>([]);
|
const [entityCorporates, setEntityCorporates] = useState<CorporateUser[]>([]);
|
||||||
|
const [isAdding, setIsAdding] = useState(false); // used to temporary timeout new workflow button. With animations, clicking too fast might cause state inconsistencies between renders.
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (entityId) {
|
if (entityId) {
|
||||||
@@ -88,6 +89,9 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleAddNewWorkflow = () => {
|
const handleAddNewWorkflow = () => {
|
||||||
|
if (isAdding) return;
|
||||||
|
setIsAdding(true);
|
||||||
|
|
||||||
const newId = uuidv4();
|
const newId = uuidv4();
|
||||||
const newWorkflow: EditableApprovalWorkflow = {
|
const newWorkflow: EditableApprovalWorkflow = {
|
||||||
id: newId,
|
id: newId,
|
||||||
@@ -104,6 +108,8 @@ export default function Home({ user, userEntitiesWithLabel, userEntitiesTeachers
|
|||||||
};
|
};
|
||||||
setWorkflows((prev) => [...prev, newWorkflow]);
|
setWorkflows((prev) => [...prev, newWorkflow]);
|
||||||
handleSelectWorkflow(newId);
|
handleSelectWorkflow(newId);
|
||||||
|
|
||||||
|
setTimeout(() => setIsAdding(false), 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWorkflowChange = (updatedWorkflow: EditableApprovalWorkflow) => {
|
const onWorkflowChange = (updatedWorkflow: EditableApprovalWorkflow) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user