mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 18:05:24 +02:00
Add Launch Date Inconsistencies and Upcoming Launch Date tabs
- Add Launch Date Inconsistencies tab: shows when Ready to Order > Launch Date - Add Upcoming Launch Date tab: alerts when launch is within 180 days (yellow) or 120 days (red) - Show days until launch countdown
This commit is contained in:
@@ -96,6 +96,7 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
{ col: COLUMNS.CLASSIFICATION, label: 'Classification', width: 130 },
|
{ col: COLUMNS.CLASSIFICATION, label: 'Classification', width: 130 },
|
||||||
...(launchDateCol >= 0 ? [{ col: launchDateCol, label: launchHeader, width: 130 }] : []),
|
...(launchDateCol >= 0 ? [{ col: launchDateCol, label: launchHeader, width: 130 }] : []),
|
||||||
...(readyToOrderCol >= 0 ? [{ col: readyToOrderCol, label: readyHeader, width: 130 }] : []),
|
...(readyToOrderCol >= 0 ? [{ col: readyToOrderCol, label: readyHeader, width: 130 }] : []),
|
||||||
|
...((activeTab === 'launchInconsistent' || activeTab === 'upcomingLaunch') ? [{ col: -1, label: 'Days Until Launch', width: 120 }] : []),
|
||||||
];
|
];
|
||||||
|
|
||||||
const columnUniqueValues = useMemo(() => {
|
const columnUniqueValues = useMemo(() => {
|
||||||
@@ -124,7 +125,10 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
};
|
};
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
let result = data.map((row, index) => ({ row, index }));
|
let result = data.map((row, index) => ({ row, index, status: 'ok' as string, daysUntil: 0 }));
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
if (activeTab === 'missingLaunch') {
|
if (activeTab === 'missingLaunch') {
|
||||||
result = result.filter(r => {
|
result = result.filter(r => {
|
||||||
@@ -133,6 +137,60 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (activeTab === 'launchInconsistent' || activeTab === 'upcomingLaunch') {
|
||||||
|
result = result.map(r => {
|
||||||
|
const launchVal = launchDateCol >= 0 ? r.row[launchDateCol] : undefined;
|
||||||
|
const readyVal = readyToOrderCol >= 0 ? r.row[readyToOrderCol] : undefined;
|
||||||
|
const launchDate = formatDateValue(launchVal);
|
||||||
|
const readyDate = formatDateValue(readyVal);
|
||||||
|
let daysUntil = 0;
|
||||||
|
let status = 'ok';
|
||||||
|
|
||||||
|
if (launchDate && /^\d{4}-\d{2}-\d{2}$/.test(launchDate)) {
|
||||||
|
const launchD = new Date(launchDate);
|
||||||
|
const diffTime = launchD.getTime() - today.getTime();
|
||||||
|
daysUntil = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
if (activeTab === 'launchInconsistent') {
|
||||||
|
if (readyDate && /^\d{4}-\d{2}-\d{2}$/.test(readyDate)) {
|
||||||
|
const readyD = new Date(readyDate);
|
||||||
|
if (readyD > launchD) {
|
||||||
|
status = 'error_ready_after_launch';
|
||||||
|
} else {
|
||||||
|
status = 'ok';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status = 'ok';
|
||||||
|
}
|
||||||
|
} else if (activeTab === 'upcomingLaunch') {
|
||||||
|
if (daysUntil <= 0) {
|
||||||
|
status = 'past';
|
||||||
|
} else if (daysUntil <= 120) {
|
||||||
|
status = 'critical';
|
||||||
|
} else if (daysUntil <= 180) {
|
||||||
|
status = 'warning';
|
||||||
|
} else {
|
||||||
|
status = 'ok';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status = 'missing_launch';
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...r, status, daysUntil };
|
||||||
|
});
|
||||||
|
|
||||||
|
if (activeTab === 'launchInconsistent') {
|
||||||
|
result = result.filter(r =>
|
||||||
|
r.status === 'error_ready_after_launch' || (r.status === 'ok' && r.daysUntil > 0 && isEmptyOrEpoch(launchDateCol >= 0 ? r.row[readyToOrderCol] : undefined))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTab === 'upcomingLaunch') {
|
||||||
|
result = result.filter(r => r.status === 'warning' || r.status === 'critical' || r.status === 'past');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
const s = search.toLowerCase();
|
const s = search.toLowerCase();
|
||||||
result = result.filter(r =>
|
result = result.filter(r =>
|
||||||
@@ -228,6 +286,8 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
|
|
||||||
const tabs: { id: TabType; label: string }[] = [
|
const tabs: { id: TabType; label: string }[] = [
|
||||||
{ id: 'missingLaunch', label: 'Missing Launch Date' },
|
{ id: 'missingLaunch', label: 'Missing Launch Date' },
|
||||||
|
{ id: 'launchInconsistent', label: 'Launch Date Inconsistencies' },
|
||||||
|
{ id: 'upcomingLaunch', label: 'Upcoming Launch Date' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const editingRow = editing ? data[editing.rowIndex] : null;
|
const editingRow = editing ? data[editing.rowIndex] : null;
|
||||||
@@ -363,8 +423,14 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-slate-700/30">
|
<tbody className="divide-y divide-slate-700/30">
|
||||||
{paginatedData.map(({ row, index }) => (
|
{paginatedData.map(({ row, index, status, daysUntil }) => (
|
||||||
<tr key={index} className="hover:bg-slate-700/20 transition-colors">
|
<tr key={index} className={cn(
|
||||||
|
"hover:bg-slate-700/20 transition-colors",
|
||||||
|
activeTab === 'launchInconsistent' && status === 'error_ready_after_launch' && "bg-red-500/10",
|
||||||
|
activeTab === 'upcomingLaunch' && status === 'warning' && "bg-yellow-500/10",
|
||||||
|
activeTab === 'upcomingLaunch' && status === 'critical' && "bg-red-500/10",
|
||||||
|
activeTab === 'upcomingLaunch' && status === 'past' && "bg-red-600/20"
|
||||||
|
)}>
|
||||||
<td className="px-3 py-2 font-mono text-indigo-400 truncate" style={{ width: 100 }}>
|
<td className="px-3 py-2 font-mono text-indigo-400 truncate" style={{ width: 100 }}>
|
||||||
{row[COLUMNS.ARTICLE_NO]}
|
{row[COLUMNS.ARTICLE_NO]}
|
||||||
</td>
|
</td>
|
||||||
@@ -403,6 +469,29 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
|
{(activeTab === 'launchInconsistent' || activeTab === 'upcomingLaunch') && (
|
||||||
|
<td className="px-3 py-2 font-mono" style={{ width: 120 }}>
|
||||||
|
{status === 'error_ready_after_launch' ? (
|
||||||
|
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold bg-red-500/20 text-red-400 border border-red-500/40">
|
||||||
|
Ready > Launch
|
||||||
|
</span>
|
||||||
|
) : status === 'warning' ? (
|
||||||
|
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold bg-yellow-500/20 text-yellow-400 border border-yellow-500/40">
|
||||||
|
{daysUntil} days
|
||||||
|
</span>
|
||||||
|
) : status === 'critical' ? (
|
||||||
|
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold bg-red-500/20 text-red-400 border border-red-500/40">
|
||||||
|
{daysUntil} days
|
||||||
|
</span>
|
||||||
|
) : status === 'past' ? (
|
||||||
|
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold bg-red-600/30 text-red-300 border border-red-500/50">
|
||||||
|
Past
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-slate-500">—</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
<td className="px-3 py-2 text-right" style={{ width: 60 }}>
|
<td className="px-3 py-2 text-right" style={{ width: 60 }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => openEdit(index, row)}
|
onClick={() => openEdit(index, row)}
|
||||||
|
|||||||
Reference in New Issue
Block a user