README / client /src /lib /hierarchyUtils.ts
firoruheqo4's picture
Upload 139 files
926b211 verified
Raw
History Blame Contribute Delete
3.87 kB
/**
* Hierarchy Grouping Utilities for Leadership Overview
*
* Transforms flat project array into hierarchical structure:
* Project Name -> Product Name -> Mold Items
*/
import { ProjectData } from '@/types/project';
export interface MoldItem {
moldNumber: string;
updateDate: string;
progressDetails: string;
currentNode: string;
fullProject: ProjectData;
}
export interface ProductGroup {
productName: string;
molds: MoldItem[];
}
export interface ProjectGroup {
projectName: string;
products: ProductGroup[];
}
/**
* Group flat projects array into hierarchical structure
*/
export function groupProjectsByHierarchy(projects: ProjectData[]): ProjectGroup[] {
const projectMap = new Map<string, Map<string, MoldItem[]>>();
// First pass: organize data
projects.forEach(project => {
const projectName = project.identity.projectName || 'Unknown Project';
const productName = project.identity.productName || 'Unknown Product';
const moldNumber = project.identity.moldNumber || 'N/A';
if (!projectMap.has(projectName)) {
projectMap.set(projectName, new Map());
}
const productMap = projectMap.get(projectName)!;
if (!productMap.has(productName)) {
productMap.set(productName, []);
}
productMap.get(productName)!.push({
moldNumber,
updateDate: project.details.detailDate,
progressDetails: project.details.detailProgress,
currentNode: project.milestones.currentNode,
fullProject: project
});
});
// Second pass: convert to array structure
const result: ProjectGroup[] = [];
projectMap.forEach((productMap, projectName) => {
const products: ProductGroup[] = [];
productMap.forEach((molds, productName) => {
products.push({
productName,
molds: molds.sort((a, b) => a.moldNumber.localeCompare(b.moldNumber))
});
});
result.push({
projectName,
products: products.sort((a, b) => a.productName.localeCompare(b.productName))
});
});
return result.sort((a, b) => a.projectName.localeCompare(b.projectName));
}
/**
* Check if date is older than 7 days
*/
export function isDateStale(dateStr: string): boolean {
if (!dateStr || dateStr === '-') return false;
try {
// Handle Excel date format (e.g., "1/2/26" or "2026-01-02")
const date = new Date(dateStr);
if (isNaN(date.getTime())) return false;
const now = new Date();
const diffTime = now.getTime() - date.getTime();
const diffDays = diffTime / (1000 * 60 * 60 * 24);
return diffDays > 7;
} catch {
return false;
}
}
/**
* Format date for display (e.g., "1月2日")
*/
export function formatDateShort(dateStr: string): string {
if (!dateStr || dateStr === '-') return '-';
try {
const date = new Date(dateStr);
if (isNaN(date.getTime())) return dateStr;
const month = date.getMonth() + 1;
const day = date.getDate();
return `${month}${day}日`;
} catch {
return dateStr;
}
}
/**
* Get badge color class based on current node
*/
export function getNodeBadgeClass(node: string): string {
const normalizedNode = node?.toUpperCase() || '';
// Completed stages
if (normalizedNode.includes('MP') || normalizedNode.includes('完成')) {
return 'bg-green-100 text-green-700 border-green-300';
}
// In-progress stages
if (normalizedNode.includes('PQR') ||
normalizedNode.includes('T1') ||
normalizedNode.includes('VMP') ||
normalizedNode.includes('进行中')) {
return 'bg-blue-100 text-blue-700 border-blue-300';
}
// Early stages
if (normalizedNode.includes('KICK OFF') || normalizedNode.includes('G/L')) {
return 'bg-slate-100 text-slate-700 border-slate-300';
}
// Default
return 'bg-slate-100 text-slate-600 border-slate-300';
}