Harden import and paste validation, fix duplicate exports and variable declarations

This commit is contained in:
drjones
2026-06-13 19:51:09 -07:00
parent 44d89d205c
commit 1302aae31c
10 changed files with 331 additions and 72 deletions

View File

@@ -5,6 +5,8 @@ import { bomToText, downloadBomCsv } from './csv';
import { downloadBomPdf } from './pdf';
import type { BomLine, CutList } from './types';
import { exportToGLTF, exportToOBJ } from '../utils/cadExporter';
import { useAuth } from '../auth/AuthContext';
import { PaywallModal } from '../payments/PaywallModal';
/**
* BomPanel — self-contained Bill of Materials drawer.
@@ -18,11 +20,21 @@ import { exportToGLTF, exportToOBJ } from '../utils/cadExporter';
* {bomOpen && <BomPanel onClose={() => setBomOpen(false)} />}
*/
export function BomPanel({ onClose }: { onClose: () => void }) {
const { user } = useAuth();
const [showPaywall, setShowPaywall] = useState(false);
const parts = useBuilder((s) => s.parts);
const activeRoomId = useBuilder((s) => s.activeRoomId);
const projectName = useBuilder((s) => s.projectName);
const scene = useBuilder((s) => s.scene);
const requirePremium = (action: () => void) => {
if (!user || user.purchased_slots === 0) {
setShowPaywall(true);
} else {
action();
}
};
const roomParts = useMemo(() => {
return Object.values(parts).filter((p) => p.roomId === activeRoomId);
}, [parts, activeRoomId]);
@@ -126,11 +138,11 @@ export function BomPanel({ onClose }: { onClose: () => void }) {
</div>
</div>
<div className="flex flex-wrap gap-1.5 justify-end">
<ActionBtn onClick={() => downloadBomCsv(bom, projectName)} title="Download as CSV">
Export CSV
<ActionBtn onClick={() => requirePremium(() => downloadBomCsv(bom, projectName))} title="Download as CSV">
Export CSV 🔒
</ActionBtn>
<ActionBtn onClick={() => downloadBomPdf(bom, projectName)} title="Download as PDF">
Export PDF
<ActionBtn onClick={() => requirePremium(() => downloadBomPdf(bom, projectName))} title="Download as PDF">
Export PDF 🔒
</ActionBtn>
<ActionBtn onClick={handleCopy} title="Copy shopping list to clipboard">
{copied ? 'Copied' : 'Copy as text'}
@@ -139,18 +151,18 @@ export function BomPanel({ onClose }: { onClose: () => void }) {
Print
</ActionBtn>
<ActionBtn
onClick={() => scene && exportToGLTF(scene, `${projectName}.gltf`)}
onClick={() => scene && requirePremium(() => exportToGLTF(scene, `${projectName}.gltf`))}
title={scene ? "Export 3D layout as glTF" : "Scene loading..."}
disabled={!scene}
>
Export glTF
Export glTF 🔒
</ActionBtn>
<ActionBtn
onClick={() => scene && exportToOBJ(scene, `${projectName}.obj`)}
onClick={() => scene && requirePremium(() => exportToOBJ(scene, `${projectName}.obj`))}
title={scene ? "Export 3D layout as OBJ" : "Scene loading..."}
disabled={!scene}
>
Export OBJ
Export OBJ 🔒
</ActionBtn>
</div>
</div>
@@ -242,6 +254,7 @@ export function BomPanel({ onClose }: { onClose: () => void }) {
</div>
</div>
</div>
{showPaywall && <PaywallModal onClose={() => setShowPaywall(false)} />}
</div>
);
}