This commit is contained in:
parent
1f5d59ac7a
commit
d405f39d87
|
|
@ -361,6 +361,27 @@ const parseUTCDate = (dateStr: string): Date => {
|
||||||
return new Date(dateStr + 'Z');
|
return new Date(dateStr + 'Z');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to generate a unique signature for deduplication.
|
||||||
|
* This ensures reports for the same ticker on the same day are not squashed together.
|
||||||
|
*/
|
||||||
|
const getReportSignature = (report: any): string => {
|
||||||
|
if (report.cloudId) return report.cloudId;
|
||||||
|
if (typeof report.id === 'string' && report.id.length > 20) return report.id; // Looks like a UUID
|
||||||
|
|
||||||
|
const baseKey = `${report.ticker}_${report.analysis_date}_${report.market_type || 'us'}`;
|
||||||
|
|
||||||
|
let contentHash = "";
|
||||||
|
if (report.result) {
|
||||||
|
if (report.result.reports?.trader_investment_plan) {
|
||||||
|
contentHash = report.result.reports.trader_investment_plan.substring(0, 30).replace(/[\s\n\r]+/g, '');
|
||||||
|
} else {
|
||||||
|
contentHash = JSON.stringify(report.result).length.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `${baseKey}_${contentHash}`;
|
||||||
|
};
|
||||||
|
|
||||||
export default function HistoryPage() {
|
export default function HistoryPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { setAnalysisResult, setTaskId, setMarketType } = useAnalysisContext();
|
const { setAnalysisResult, setTaskId, setMarketType } = useAnalysisContext();
|
||||||
|
|
@ -424,12 +445,12 @@ export default function HistoryPage() {
|
||||||
// Get cloud reports to check for duplicates
|
// Get cloud reports to check for duplicates
|
||||||
const cloudReports = await getCloudReports();
|
const cloudReports = await getCloudReports();
|
||||||
const cloudKeys = new Set(
|
const cloudKeys = new Set(
|
||||||
cloudReports.map((r) => `${r.ticker}_${r.analysis_date}`),
|
cloudReports.map((r) => getReportSignature(r)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Find local-only reports to upload
|
// Find local-only reports to upload
|
||||||
const toUpload = allLocal.filter(
|
const toUpload = allLocal.filter(
|
||||||
(r) => !cloudKeys.has(`${r.ticker}_${r.analysis_date}`),
|
(r) => !cloudKeys.has(getReportSignature(r)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (toUpload.length === 0) {
|
if (toUpload.length === 0) {
|
||||||
|
|
@ -497,14 +518,14 @@ export default function HistoryPage() {
|
||||||
|
|
||||||
if (cloudFiltered.length > 0) {
|
if (cloudFiltered.length > 0) {
|
||||||
// Merge: prefer cloud data, but include local-only reports
|
// Merge: prefer cloud data, but include local-only reports
|
||||||
// Create a Set of cloud report keys (ticker + date) for deduplication
|
// Create a Set of cloud report keys (ticker + date + content snippet) for deduplication
|
||||||
const cloudKeys = new Set(
|
const cloudKeys = new Set(
|
||||||
cloudFiltered.map((r) => `${r.ticker}_${r.analysis_date}`),
|
cloudFiltered.map((r) => getReportSignature(r)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Find local reports that don't exist in cloud
|
// Find local reports that don't exist in cloud
|
||||||
const localOnly = localData.filter(
|
const localOnly = localData.filter(
|
||||||
(r) => !cloudKeys.has(`${r.ticker}_${r.analysis_date}`),
|
(r) => !cloudKeys.has(getReportSignature(r)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Combine: cloud reports + local-only reports
|
// Combine: cloud reports + local-only reports
|
||||||
|
|
@ -584,7 +605,7 @@ export default function HistoryPage() {
|
||||||
|
|
||||||
// Cloud report keys for deduplication
|
// Cloud report keys for deduplication
|
||||||
const cloudKeys = new Set(
|
const cloudKeys = new Set(
|
||||||
cloudReports.map(r => `${r.ticker}_${r.analysis_date}_${r.market_type}`)
|
cloudReports.map(r => getReportSignature(r))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Convert cloud reports to SavedReport format for language filtering
|
// Convert cloud reports to SavedReport format for language filtering
|
||||||
|
|
@ -603,13 +624,13 @@ export default function HistoryPage() {
|
||||||
|
|
||||||
// Count local-only reports (not in cloud) and filter by language
|
// Count local-only reports (not in cloud) and filter by language
|
||||||
const usLocalOnly = filterByLanguage(usLocal.filter(
|
const usLocalOnly = filterByLanguage(usLocal.filter(
|
||||||
r => !cloudKeys.has(`${r.ticker}_${r.analysis_date}_us`)
|
r => !cloudKeys.has(getReportSignature(r))
|
||||||
)).length;
|
)).length;
|
||||||
const twseLocalOnly = filterByLanguage(twseLocal.filter(
|
const twseLocalOnly = filterByLanguage(twseLocal.filter(
|
||||||
r => !cloudKeys.has(`${r.ticker}_${r.analysis_date}_twse`)
|
r => !cloudKeys.has(getReportSignature(r))
|
||||||
)).length;
|
)).length;
|
||||||
const tpexLocalOnly = filterByLanguage(tpexLocal.filter(
|
const tpexLocalOnly = filterByLanguage(tpexLocal.filter(
|
||||||
r => !cloudKeys.has(`${r.ticker}_${r.analysis_date}_tpex`)
|
r => !cloudKeys.has(getReportSignature(r))
|
||||||
)).length;
|
)).length;
|
||||||
|
|
||||||
// Cloud counts (already filtered by language)
|
// Cloud counts (already filtered by language)
|
||||||
|
|
@ -673,15 +694,14 @@ export default function HistoryPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Always try to delete from local IndexedDB as well
|
// 2. Always try to delete from local IndexedDB as well
|
||||||
// Find matching local report by ticker + analysis_date
|
// Find exact matching local report by signature
|
||||||
try {
|
try {
|
||||||
const localReports = await getReportsByMarketType(
|
const localReports = await getReportsByMarketType(
|
||||||
reportToDelete.market_type,
|
reportToDelete.market_type,
|
||||||
);
|
);
|
||||||
|
const targetSignature = getReportSignature(reportToDelete);
|
||||||
const matchingLocal = localReports.find(
|
const matchingLocal = localReports.find(
|
||||||
(r) =>
|
(r) => getReportSignature(r) === targetSignature
|
||||||
r.ticker === reportToDelete.ticker &&
|
|
||||||
r.analysis_date === reportToDelete.analysis_date,
|
|
||||||
);
|
);
|
||||||
if (matchingLocal && matchingLocal.id) {
|
if (matchingLocal && matchingLocal.id) {
|
||||||
console.log("🗑️ Deleting from local IndexedDB:", matchingLocal.id);
|
console.log("🗑️ Deleting from local IndexedDB:", matchingLocal.id);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue