@mytec: iter10 ready for testing
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { memo } from 'react';
|
||||
import type { CoveragePoint } from '@/types/index.ts';
|
||||
|
||||
interface CoverageStatsProps {
|
||||
@@ -32,16 +33,22 @@ function classifyPoints(points: CoveragePoint[]) {
|
||||
return counts;
|
||||
}
|
||||
|
||||
export default function CoverageStats({ points, resolution }: CoverageStatsProps) {
|
||||
export default memo(function CoverageStats({ points, resolution }: CoverageStatsProps) {
|
||||
if (points.length === 0) {
|
||||
return (
|
||||
<div className="bg-white dark:bg-dark-surface border border-gray-200 dark:border-dark-border rounded-lg shadow-sm p-4">
|
||||
<h3 className="text-sm font-semibold text-gray-800 dark:text-dark-text mb-2">
|
||||
Coverage Analysis
|
||||
</h3>
|
||||
<p className="text-xs text-gray-400 dark:text-dark-muted">
|
||||
No coverage data. Calculate coverage first.
|
||||
</p>
|
||||
<div className="text-center py-3">
|
||||
<div className="text-2xl mb-1 opacity-40">📊</div>
|
||||
<p className="text-xs text-gray-400 dark:text-dark-muted">
|
||||
No coverage data yet.
|
||||
</p>
|
||||
<p className="text-xs text-gray-400 dark:text-dark-muted mt-0.5">
|
||||
Press <kbd className="px-1 py-0.5 bg-gray-100 dark:bg-dark-border rounded text-[10px] font-mono">Ctrl+Enter</kbd> to calculate.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -135,4 +142,4 @@ export default function CoverageStats({ points, resolution }: CoverageStatsProps
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -83,9 +83,15 @@ export default function ExportPanel() {
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p className="text-xs text-gray-400 dark:text-dark-muted">
|
||||
No coverage data. Calculate coverage first to enable export.
|
||||
</p>
|
||||
<div className="text-center py-2">
|
||||
<div className="text-2xl mb-1 opacity-40">📁</div>
|
||||
<p className="text-xs text-gray-400 dark:text-dark-muted">
|
||||
No coverage data to export.
|
||||
</p>
|
||||
<p className="text-xs text-gray-400 dark:text-dark-muted mt-0.5">
|
||||
Calculate coverage first to enable CSV and GeoJSON export.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
|
||||
import { useProjectsStore } from '@/store/projects.ts';
|
||||
import { useToastStore } from '@/components/ui/Toast.tsx';
|
||||
import Button from '@/components/ui/Button.tsx';
|
||||
import { logger } from '@/utils/logger.ts';
|
||||
|
||||
export default function ProjectPanel() {
|
||||
const projects = useProjectsStore((s) => s.projects);
|
||||
@@ -33,7 +34,7 @@ export default function ProjectPanel() {
|
||||
setProjectName('');
|
||||
setShowSaveForm(false);
|
||||
} catch (err) {
|
||||
console.error('Save project error:', err);
|
||||
logger.error('Save project error:', err);
|
||||
addToast('Failed to save project', 'error');
|
||||
}
|
||||
};
|
||||
@@ -48,7 +49,7 @@ export default function ProjectPanel() {
|
||||
addToast('Project not found', 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Load project error:', err);
|
||||
logger.error('Load project error:', err);
|
||||
addToast('Failed to load project', 'error');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -60,7 +61,7 @@ export default function ProjectPanel() {
|
||||
await deleteProject(id);
|
||||
addToast(`Project "${name}" deleted`, 'info');
|
||||
} catch (err) {
|
||||
console.error('Delete project error:', err);
|
||||
logger.error('Delete project error:', err);
|
||||
addToast('Failed to delete project', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -130,6 +130,8 @@ export default function SiteForm({
|
||||
const [beamwidth, setBeamwidth] = useState(editSite?.beamwidth ?? 65);
|
||||
const [notes, setNotes] = useState(editSite?.notes ?? '');
|
||||
|
||||
// Sync pending map-click location into form fields
|
||||
/* eslint-disable react-hooks/set-state-in-effect */
|
||||
useEffect(() => {
|
||||
if (pendingLocation) {
|
||||
setLat(pendingLocation.lat);
|
||||
@@ -154,6 +156,7 @@ export default function SiteForm({
|
||||
setNotes(editSite.notes ?? '');
|
||||
}
|
||||
}, [editSite]);
|
||||
/* eslint-enable react-hooks/set-state-in-effect */
|
||||
|
||||
const applyTemplate = (key: keyof typeof TEMPLATES) => {
|
||||
const t = TEMPLATES[key];
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useRef } from 'react';
|
||||
import { useSitesStore } from '@/store/sites.ts';
|
||||
import { useToastStore } from '@/components/ui/Toast.tsx';
|
||||
import Button from '@/components/ui/Button.tsx';
|
||||
import { logger } from '@/utils/logger.ts';
|
||||
|
||||
/**
|
||||
* Import/Export site configurations as JSON.
|
||||
@@ -95,7 +96,7 @@ export default function SiteImportExport() {
|
||||
const count = await importSites(sitesData);
|
||||
addToast(`Imported ${count} site(s)`, 'success');
|
||||
} catch (error) {
|
||||
console.error('Import failed:', error);
|
||||
logger.error('Import failed:', error);
|
||||
addToast('Invalid JSON file', 'error');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user