2025-07-16 16:12:40 +00:00
|
|
|
import { useState, useEffect, useMemo, useCallback } from "react";
|
|
|
|
|
import { FileItem, GroupedByStatus } from "../GroupedStatusView";
|
|
|
|
|
|
|
|
|
|
export type UseGroupedDataProps = {
|
|
|
|
|
getAllFiles: () => FileItem[];
|
|
|
|
|
processFiles: (files: FileItem[]) => GroupedByStatus;
|
|
|
|
|
subscribeToEvents: (onDataChange: () => void) => () => void;
|
|
|
|
|
searchFilter: string;
|
|
|
|
|
noteNameFilter: string;
|
2025-07-20 09:00:00 +00:00
|
|
|
templateFilter: string;
|
2025-07-16 16:12:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useGroupedData = ({
|
|
|
|
|
getAllFiles,
|
|
|
|
|
processFiles,
|
|
|
|
|
subscribeToEvents,
|
|
|
|
|
searchFilter,
|
|
|
|
|
noteNameFilter,
|
2025-07-20 09:00:00 +00:00
|
|
|
templateFilter,
|
2025-07-16 16:12:40 +00:00
|
|
|
}: UseGroupedDataProps) => {
|
|
|
|
|
const [groupedData, setGroupedData] = useState<GroupedByStatus>({});
|
|
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
|
|
|
|
|
|
const loadData = useCallback(async () => {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
const files = getAllFiles();
|
|
|
|
|
const processed = processFiles(files);
|
|
|
|
|
setGroupedData(processed);
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, [getAllFiles, processFiles]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
loadData();
|
|
|
|
|
|
|
|
|
|
const handleFileChange = () => {
|
|
|
|
|
loadData();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const unsubscribe = subscribeToEvents(handleFileChange);
|
|
|
|
|
|
|
|
|
|
return unsubscribe;
|
|
|
|
|
}, [loadData, subscribeToEvents]);
|
|
|
|
|
|
|
|
|
|
const filteredData = useMemo(() => {
|
2025-07-20 09:00:00 +00:00
|
|
|
if (
|
|
|
|
|
!searchFilter.trim() &&
|
|
|
|
|
!noteNameFilter.trim() &&
|
|
|
|
|
!templateFilter.trim()
|
|
|
|
|
)
|
|
|
|
|
return groupedData;
|
2025-07-16 16:12:40 +00:00
|
|
|
|
|
|
|
|
const filtered: GroupedByStatus = {};
|
|
|
|
|
const searchLower = searchFilter.toLowerCase();
|
|
|
|
|
const noteNameLower = noteNameFilter.toLowerCase();
|
|
|
|
|
|
|
|
|
|
Object.entries(groupedData).forEach(([tag, statusGroups]) => {
|
|
|
|
|
filtered[tag] = {};
|
|
|
|
|
Object.entries(statusGroups).forEach(([statusName, files]) => {
|
2025-07-20 09:00:00 +00:00
|
|
|
// Filter by template if templateFilter is provided
|
|
|
|
|
if (templateFilter.trim()) {
|
|
|
|
|
// Check if statusName contains the template (scoped: "template:status")
|
|
|
|
|
const statusTemplate = statusName.includes(":")
|
|
|
|
|
? statusName.split(":", 2)[0]
|
|
|
|
|
: "";
|
|
|
|
|
|
|
|
|
|
if (statusTemplate !== templateFilter) {
|
|
|
|
|
return; // Skip this status group if it doesn't match the template filter
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-16 16:12:40 +00:00
|
|
|
let matchingFiles = files;
|
|
|
|
|
|
|
|
|
|
// Filter by status/tag if searchFilter is provided
|
|
|
|
|
if (searchFilter.trim()) {
|
|
|
|
|
matchingFiles = matchingFiles.filter(
|
|
|
|
|
(file) =>
|
|
|
|
|
statusName.toLowerCase().includes(searchLower) ||
|
|
|
|
|
tag.toLowerCase().includes(searchLower),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Filter by note name if noteNameFilter is provided
|
|
|
|
|
if (noteNameFilter.trim()) {
|
|
|
|
|
matchingFiles = matchingFiles.filter(
|
|
|
|
|
(file) =>
|
|
|
|
|
file.name.toLowerCase().includes(noteNameLower) ||
|
|
|
|
|
file.path.toLowerCase().includes(noteNameLower),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (matchingFiles.length > 0) {
|
|
|
|
|
filtered[tag][statusName] = matchingFiles;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return filtered;
|
2025-07-20 09:00:00 +00:00
|
|
|
}, [groupedData, searchFilter, noteNameFilter, templateFilter]);
|
2025-07-16 16:12:40 +00:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
groupedData,
|
|
|
|
|
filteredData,
|
|
|
|
|
isLoading,
|
|
|
|
|
loadData,
|
|
|
|
|
};
|
|
|
|
|
};
|