Files
apex/client/src/components/Plans2DView.jsx
T
2026-02-13 00:21:53 +01:00

185 lines
5.9 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Plans2DView - Display 2D plans (assembly, side, top views)
* With tabs for different views and export options
*/
import { useState, useEffect, useCallback } from 'react';
import CuttingListTable from './CuttingListTable';
import styles from './Plans2DView.module.css';
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:3001';
export default function Plans2DView({ config, modules = [], colors = {}, onClose }) {
const [activeTab, setActiveTab] = useState('assembly');
const [plans, setPlans] = useState(null);
const [cuttingList, setCuttingList] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// Fetch plans and cutting list
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);
try {
// Fetch plans
const plansRes = await fetch(`${API_BASE}/api/plans/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ config, modules, colors })
});
const plansData = await plansRes.json();
if (plansData.success) {
setPlans(plansData.plans);
}
// Fetch cutting list
const cutRes = await fetch(`${API_BASE}/api/plans/cutting-list`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ config, modules, colors })
});
const cutData = await cutRes.json();
if (cutData.success) {
setCuttingList(cutData.cuttingList);
}
} catch (err) {
console.error('Error fetching plans:', err);
setError('Erreur lors de la génération des plans');
} finally {
setLoading(false);
}
};
fetchData();
}, [config, modules, colors]);
// Export handlers
const handleExport = useCallback(async (format) => {
try {
if (format === 'text') {
const res = await fetch(`${API_BASE}/api/plans/export-text`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ config, modules, colors })
});
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'liste-debitage.txt';
a.click();
URL.revokeObjectURL(url);
} else if (format === 'pdf') {
const res = await fetch(`${API_BASE}/api/plans/export-pdf`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ config, modules, colors })
});
const html = await res.text();
// Open in new window for printing
const printWindow = window.open('', '_blank');
printWindow.document.write(html);
printWindow.document.close();
printWindow.onload = () => {
printWindow.print();
};
}
} catch (err) {
console.error('Export error:', err);
alert('Erreur lors de l\'export');
}
}, [config, modules, colors]);
const tabs = [
{ id: 'assembly', label: 'Vue de face' },
{ id: 'side', label: 'Vue de côté' },
{ id: 'top', label: 'Vue de dessus' },
{ id: 'cutting', label: 'Liste de débitage' }
];
return (
<div className={styles.overlay}>
<div className={styles.modal}>
<div className={styles.header}>
<h2 className={styles.title}>Plans de fabrication</h2>
<button className={styles.closeBtn} onClick={onClose}>×</button>
</div>
<div className={styles.tabs}>
{tabs.map(tab => (
<button
key={tab.id}
className={`${styles.tab} ${activeTab === tab.id ? styles.active : ''}`}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
))}
</div>
<div className={styles.content}>
{loading ? (
<div className={styles.loading}>Génération des plans...</div>
) : error ? (
<div className={styles.error}>{error}</div>
) : (
<>
{activeTab === 'assembly' && plans?.assembly && (
<div
className={styles.planContainer}
dangerouslySetInnerHTML={{ __html: plans.assembly }}
/>
)}
{activeTab === 'side' && plans?.side && (
<div
className={styles.planContainer}
dangerouslySetInnerHTML={{ __html: plans.side }}
/>
)}
{activeTab === 'top' && plans?.top && (
<div
className={styles.planContainer}
dangerouslySetInnerHTML={{ __html: plans.top }}
/>
)}
{activeTab === 'cutting' && (
<CuttingListTable
cuttingList={cuttingList}
onExport={handleExport}
/>
)}
</>
)}
</div>
<div className={styles.footer}>
<div className={styles.specs}>
<span>{config.widthMm} × {config.heightMm} × {config.depthMm} mm</span>
<span>•</span>
<span>{config.installationType === 'niche' ? 'Encastrée' : 'Libre'}</span>
<span>•</span>
<span>{config.doorType === 'sliding' ? 'Coulissantes' : 'Battantes'}</span>
</div>
<div className={styles.actions}>
<button
className={styles.exportBtn}
onClick={() => handleExport('pdf')}
>
Imprimer / PDF
</button>
</div>
</div>
</div>
</div>
);
}