Refactor code to handle NaN and infinite values in

KpiCard and ProgressCircleChart components
This commit is contained in:
Pattadon 2023-11-26 01:13:32 +07:00
parent 779e45aa89
commit 75f194386f
6 changed files with 72 additions and 16 deletions

View File

@ -1,4 +1,11 @@
import { BadgeDelta, Card, Flex, Metric, ProgressBar, Text } from "@tremor/react"; import {
BadgeDelta,
Card,
Flex,
Metric,
ProgressBar,
Text,
} from "@tremor/react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { axiosInstance } from "src/api/AxiosConfig"; import { axiosInstance } from "src/api/AxiosConfig";
@ -46,10 +53,16 @@ export function KpiCard() {
<div> <div>
<Metric>{kpiCardData.completedThisWeek}</Metric> <Metric>{kpiCardData.completedThisWeek}</Metric>
</div> </div>
<BadgeDelta deltaType={kpiCardData.incOrdec}>{kpiCardData.percentage.toFixed(0)}%</BadgeDelta> <BadgeDelta deltaType={kpiCardData.incOrdec}>
{isNaN(kpiCardData.percentage) || !isFinite(kpiCardData.percentage)
? "0%"
: `${kpiCardData.percentage.toFixed(0)}%`}
</BadgeDelta>
</Flex> </Flex>
<Flex className="mt-4"> <Flex className="mt-4">
<Text className="truncate">vs. {kpiCardData.completedLastWeek} (last week)</Text> <Text className="truncate">
vs. {kpiCardData.completedLastWeek} (last week)
</Text>
</Flex> </Flex>
<ProgressBar value={kpiCardData.percentage} className="mt-2" /> <ProgressBar value={kpiCardData.percentage} className="mt-2" />
</Card> </Card>

View File

@ -33,9 +33,18 @@ export function ProgressCircleChart() {
return ( return (
<Card className="max-w-lg mx-auto"> <Card className="max-w-lg mx-auto">
<Flex className="flex-col items-center"> <Flex className="flex-col items-center">
<ProgressCircle className="mt-6" value={progressData} size={200} strokeWidth={10} radius={60} color="indigo"> <ProgressCircle
className="mt-6"
value={progressData}
size={200}
strokeWidth={10}
radius={60}
color="indigo"
>
<span className="h-12 w-12 rounded-full bg-indigo-100 flex items-center justify-center text-sm text-indigo-500 font-medium"> <span className="h-12 w-12 rounded-full bg-indigo-100 flex items-center justify-center text-sm text-indigo-500 font-medium">
{progressData.toFixed(0)} % {isNaN(progressData) || !isFinite(progressData)
? "0%"
: `${progressData.toFixed(0)}%`}
</span> </span>
</ProgressCircle> </ProgressCircle>
</Flex> </Flex>

View File

@ -154,7 +154,9 @@ export function Dashboard() {
color="rose" color="rose"
> >
<span className="h-12 w-12 rounded-full bg-rose-100 flex items-center justify-center text-sm text-rose-500 font-medium"> <span className="h-12 w-12 rounded-full bg-rose-100 flex items-center justify-center text-sm text-rose-500 font-medium">
{progressData.toFixed(0)} % {isNaN(progressData) || !isFinite(progressData)
? "0%"
: `${progressData.toFixed(0)}%`}
</span> </span>
</ProgressCircle> </ProgressCircle>
<br></br> <br></br>

View File

@ -1,6 +1,12 @@
import { useMemo, useState, useEffect } from "react"; import { useMemo, useState, useEffect } from "react";
import { ColumnContainerCard } from "./columnContainerWrapper"; import { ColumnContainerCard } from "./columnContainerWrapper";
import { DndContext, DragOverlay, PointerSensor, useSensor, useSensors } from "@dnd-kit/core"; import {
DndContext,
DragOverlay,
PointerSensor,
useSensor,
useSensors,
} from "@dnd-kit/core";
import { SortableContext, arrayMove } from "@dnd-kit/sortable"; import { SortableContext, arrayMove } from "@dnd-kit/sortable";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { TaskCard } from "./taskCard"; import { TaskCard } from "./taskCard";
@ -26,7 +32,9 @@ export function KanbanBoard() {
// ---------------- Task Handlers ---------------- // ---------------- Task Handlers ----------------
const handleTaskUpdate = (tasks, updatedTask) => { const handleTaskUpdate = (tasks, updatedTask) => {
const updatedTasks = tasks.map((task) => (task.id === updatedTask.id ? updatedTask : task)); const updatedTasks = tasks.map((task) =>
task.id === updatedTask.id ? updatedTask : task
);
setTasks(updatedTasks); setTasks(updatedTasks);
}; };
@ -168,8 +176,14 @@ export function KanbanBoard() {
justify-center justify-center
overflow-x-auto overflow-x-auto
overflow-y-hidden overflow-y-hidden
"> "
<DndContext sensors={sensors} onDragStart={onDragStart} onDragEnd={onDragEnd} onDragOver={onDragOver}> >
<DndContext
sensors={sensors}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
onDragOver={onDragOver}
>
<div className="flex gap-4"> <div className="flex gap-4">
<div className="flex gap-4"> <div className="flex gap-4">
{!isLoading ? ( {!isLoading ? (
@ -181,7 +195,9 @@ export function KanbanBoard() {
createTask={createTask} createTask={createTask}
deleteTask={deleteTask} deleteTask={deleteTask}
updateTask={updateTask} updateTask={updateTask}
tasks={(tasks || []).filter((task) => task.columnId === col.id)} tasks={(tasks || []).filter(
(task) => task.columnId === col.id
)}
/> />
))}{" "} ))}{" "}
</SortableContext> </SortableContext>
@ -194,7 +210,11 @@ export function KanbanBoard() {
{createPortal( {createPortal(
<DragOverlay className="bg-white" dropAnimation={null} zIndex={20}> <DragOverlay className="bg-white" dropAnimation={null} zIndex={20}>
{/* Render the active task as a draggable overlay */} {/* Render the active task as a draggable overlay */}
<TaskCard task={activeTask} deleteTask={deleteTask} updateTask={updateTask} /> <TaskCard
task={activeTask}
deleteTask={deleteTask}
updateTask={updateTask}
/>
</DragOverlay>, </DragOverlay>,
document.body document.body
)} )}
@ -302,7 +322,11 @@ export function KanbanBoard() {
const isOverAColumn = over.data.current?.type === "Column"; const isOverAColumn = over.data.current?.type === "Column";
// Move the Task to a different column and update columnId // Move the Task to a different column and update columnId
if (isActiveATask && isOverAColumn && tasks.some((task) => task.columnId !== overId)) { if (
isActiveATask &&
isOverAColumn &&
tasks.some((task) => task.columnId !== overId)
) {
setTasks((tasks) => { setTasks((tasks) => {
const activeIndex = tasks.findIndex((t) => t.id === activeId); const activeIndex = tasks.findIndex((t) => t.id === activeId);
axiosInstance axiosInstance

View File

@ -1,4 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { useEffect } from "react";
import { BsFillTrashFill } from "react-icons/bs"; import { BsFillTrashFill } from "react-icons/bs";
import { useSortable } from "@dnd-kit/sortable"; import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities"; import { CSS } from "@dnd-kit/utilities";
@ -6,6 +7,9 @@ import { TaskDetailModal } from "./taskDetailModal";
export function TaskCard({ task, deleteTask, updateTask }) { export function TaskCard({ task, deleteTask, updateTask }) {
const [mouseIsOver, setMouseIsOver] = useState(false); const [mouseIsOver, setMouseIsOver] = useState(false);
// console.log(task.challenge);
// console.log(task.importance);
// console.log(task.difficulty);
const { setNodeRef, attributes, listeners, transform, transition, isDragging } = useSortable({ const { setNodeRef, attributes, listeners, transform, transition, isDragging } = useSortable({
id: task.id, id: task.id,
@ -14,12 +18,13 @@ export function TaskCard({ task, deleteTask, updateTask }) {
task, task,
}, },
}); });
const style = { const style = {
transition, transition,
transform: CSS.Transform.toString(transform), transform: CSS.Transform.toString(transform),
}; };
{ {
/* If card is dragged */ /* If card is dragged */
} }
@ -60,7 +65,7 @@ export function TaskCard({ task, deleteTask, updateTask }) {
setMouseIsOver(false); setMouseIsOver(false);
}}> }}>
<p <p
className="p-2.5 my-auto w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-xl shadow bg-white" className={`p-2.5 my-auto w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-xl shadow bg-white`}
onClick={() => document.getElementById(`task_detail_modal_${task.id}`).showModal()}> onClick={() => document.getElementById(`task_detail_modal_${task.id}`).showModal()}>
{task.content} {task.content}
</p> </p>
@ -77,4 +82,4 @@ export function TaskCard({ task, deleteTask, updateTask }) {
</div> </div>
</div> </div>
); );
} }

View File

@ -7,6 +7,9 @@ export function TaskDetailModal({ title, description, tags, difficulty, challeng
const [isChallengeChecked, setChallengeChecked] = useState(challenge); const [isChallengeChecked, setChallengeChecked] = useState(challenge);
const [isImportantChecked, setImportantChecked] = useState(importance); const [isImportantChecked, setImportantChecked] = useState(importance);
const [currentDifficulty, setCurrentDifficulty] = useState(difficulty); const [currentDifficulty, setCurrentDifficulty] = useState(difficulty);
// console.log(currentDifficulty);
// console.log(isChallengeChecked);
// console.log(isImportantChecked);
const handleChallengeChange = () => { const handleChallengeChange = () => {
setChallengeChecked(!isChallengeChecked); setChallengeChecked(!isChallengeChecked);