2025-08-04 07:08:08 +00:00

216 lines
5.5 KiB
TypeScript

import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
import Axios from 'axios';
import CardBox from '../CardBox';
import CardBoxModal from '../CardBoxModal';
import { AsyncThunk } from '@reduxjs/toolkit';
import { useDrop } from 'react-dnd';
import KanbanCard from './KanbanCard';
type Props = {
column: { id: string; label: string };
entityName: string;
columnFieldName: string;
showFieldName: string;
filtersQuery: any;
deleteThunk: AsyncThunk<any, any, any>;
updateThunk: AsyncThunk<any, any, any>;
};
type DropResult = {
sourceColumn: { id: string; label: string };
item: any;
};
const perPage = 10;
const KanbanColumn = ({
column,
entityName,
columnFieldName,
showFieldName,
filtersQuery,
deleteThunk,
updateThunk,
}: Props) => {
const [currentPage, setCurrentPage] = useState(0);
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [itemIdToDelete, setItemIdToDelete] = useState('');
const currentUser = useAppSelector((state) => state.auth.currentUser);
const listInnerRef = useRef<HTMLDivElement | null>(null);
const dispatch = useAppDispatch();
const [{ dropResult }, drop] = useDrop<
{
item: any;
column: {
id: string;
label: string;
};
},
unknown,
{
dropResult: DropResult;
}
>(
() => ({
accept: 'box',
drop: ({
item,
column: sourceColumn,
}: {
item: any;
column: { id: string; label: string };
}) => {
if (sourceColumn.id === column.id) return;
dispatch(
updateThunk({
id: item.id,
data: {
[columnFieldName]: column.id,
},
}),
).then((res) => {
setData((prevState) => (prevState ? [...prevState, item] : [item]));
setCount((prevState) => prevState + 1);
});
return { sourceColumn, item };
},
collect: (monitor) => ({
dropResult: monitor.getDropResult(),
}),
}),
[],
);
const loadData = useCallback(
(page: number, filters = '') => {
const query = `?page=${page}&limit=${perPage}&field=createdAt&sort=desc&${columnFieldName}=${column.id}&${filters}`;
setLoading(true);
Axios.get(`${entityName}${query}`)
.then((res) => {
setData((prevState) =>
page === 0 ? res.data.rows : [...prevState, ...res.data.rows],
);
setCount(res.data.count);
setCurrentPage(page);
})
.catch((err) => {
console.error(err);
})
.finally(() => {
setLoading(false);
});
},
[currentUser, column],
);
useEffect(() => {
if (!currentUser) return;
loadData(0, filtersQuery);
}, [currentUser, loadData, filtersQuery]);
useEffect(() => {
loadData(0, filtersQuery);
}, [loadData, filtersQuery]);
useEffect(() => {
if (dropResult?.sourceColumn && dropResult.sourceColumn.id === column.id) {
setData((prevState) =>
prevState.filter((item) => item.id !== dropResult.item.id),
);
setCount((prevState) => prevState - 1);
}
}, [dropResult]);
const onScroll = () => {
if (listInnerRef.current) {
const { scrollTop, scrollHeight, clientHeight } = listInnerRef.current;
if (Math.floor(scrollTop + clientHeight) === scrollHeight) {
if (data.length < count && !loading) {
loadData(currentPage + 1, filtersQuery);
}
}
}
};
const onDeleteConfirm = () => {
if (!itemIdToDelete) return;
dispatch(deleteThunk(itemIdToDelete))
.then((res) => {
if (res.meta.requestStatus === 'fulfilled') {
setItemIdToDelete('');
loadData(0, filtersQuery);
}
})
.catch((err) => {
console.error(err);
})
.finally(() => {
setItemIdToDelete('');
});
};
return (
<>
<CardBox
hasComponentLayout
className={
'w-72 rounded-md h-fit max-h-full overflow-hidden flex flex-col'
}
>
<div className={'flex items-center justify-between p-3'}>
<p className={'uppercase'}>{column.label}</p>
<p>{count}</p>
</div>
<div
ref={(node) => {
drop(node);
listInnerRef.current = node;
}}
className={'p-3 space-y-3 flex-1 overflow-y-auto max-h-[400px]'}
onScroll={onScroll}
>
{data?.map((item) => (
<div key={item.id}>
<KanbanCard
item={item}
column={column}
showFieldName={showFieldName}
entityName={entityName}
setItemIdToDelete={setItemIdToDelete}
/>
</div>
))}
{!data?.length && (
<p
className={
'text-center py-8 bg-midnightBlueTheme-cardColor dark:bg-dark-800'
}
>
No data
</p>
)}
</div>
</CardBox>
<CardBoxModal
title='Please confirm'
buttonColor='info'
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
isActive={!!itemIdToDelete}
onConfirm={onDeleteConfirm}
onCancel={() => setItemIdToDelete('')}
>
<p>Are you sure you want to delete this item?</p>
</CardBoxModal>
</>
);
};
export default KanbanColumn;