diff --git a/client/src/components/MonteurField.tsx b/client/src/components/MonteurField.tsx
new file mode 100644
index 0000000..0dbfd2b
--- /dev/null
+++ b/client/src/components/MonteurField.tsx
@@ -0,0 +1,42 @@
+import { useState } from 'react';
+
+export const MONTEURE = ['Michael', 'Daniel', 'Dietmar', 'Fidelis'];
+
+interface Props {
+ value: string;
+ onChange: (v: string) => void;
+ onCommit?: (v: string) => void;
+}
+
+export default function MonteurField({ value, onChange, onCommit }: Props) {
+ const [customMode, setCustomMode] = useState(() => value !== '' && !MONTEURE.includes(value));
+
+ function handleSelect(v: string) {
+ if (v === '__custom__') {
+ setCustomMode(true);
+ return;
+ }
+ setCustomMode(false);
+ onChange(v);
+ onCommit?.(v);
+ }
+
+ return (
+
+ );
+}
diff --git a/client/src/pages/Dashboard.tsx b/client/src/pages/Dashboard.tsx
index 7d5756e..e1c7c89 100644
--- a/client/src/pages/Dashboard.tsx
+++ b/client/src/pages/Dashboard.tsx
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { api } from '../lib/api';
import type { Projekt, ProjektStatus } from '../lib/types';
+import MonteurField from '../components/MonteurField';
function dd(s: string | null) {
if (!s) return '–';
@@ -101,9 +102,7 @@ export default function Dashboard() {
-
+ setForm({ ...form, monteur: v })} />
diff --git a/client/src/pages/ProjektDetail.tsx b/client/src/pages/ProjektDetail.tsx
index d3279a9..fff26b9 100644
--- a/client/src/pages/ProjektDetail.tsx
+++ b/client/src/pages/ProjektDetail.tsx
@@ -4,6 +4,7 @@ import { api } from '../lib/api';
import type { MaterialItem, Projekt } from '../lib/types';
import { kabelInfoForBez } from '../data/kabelData';
import KabelOptions from '../components/KabelOptions';
+import MonteurField from '../components/MonteurField';
const fmt1 = (v: number | string) => (Math.round((parseFloat(String(v)) || 0) * 10) / 10).toFixed(1);
const num1 = (v: number | string) => Math.round((parseFloat(String(v)) || 0) * 10) / 10;
@@ -216,25 +217,26 @@ export default function ProjektDetail() {
setRetourBusy(false);
}
- // ── CSV-Export (für Import in VenDoc o.ä. als Auftragspositionen) ──
+ // ── CSV-Export (für Import in VenDoc als Auftragspositionen) ──
// Semikolon-getrennt, Komma als Dezimaltrenner, UTF-8 mit BOM – Standardkonvention
- // für deutschsprachiges Excel/ERP, damit die Datei ohne Umweg direkt geöffnet/
- // importiert werden kann. Reine Positionstabelle ohne Kopf-Metazeilen, damit ein
- // einfacher Positions-Import (z. B. VenDoc Universalimport) sie unverändert lesen kann.
+ // für deutschsprachiges Excel/ERP. Spaltennamen (Position/EH/Positionstext/Aufmaß)
+ // richten sich nach VenDocs eigenem Aufmaßblatt-Export (Auftrag A00079/2026), das
+ // keine eigene Artikelnummer-Spalte führt – die Art.-Nr. wird daher wie dort in den
+ // Positionstext eingebettet. Ob das exakte Spaltenformat vom Universalimport 1:1
+ // übernommen wird, ist ungeprüft – ggf. anhand des ersten Testimports nachschärfen.
+ const EH_MAP: Record = { Stk: 'ST', m: 'M' }; // aus VenDoc-Beispiel bestätigt
function csvEscape(v: string): string {
if (/[;"\n\r]/.test(v)) return '"' + v.replace(/"/g, '""') + '"';
return v;
}
function exportCSV() {
if (!projekt) return;
- const header = ['Pos', 'Artikelnummer', 'Bezeichnung', 'Menge', 'Einheit', 'Notiz'];
+ const header = ['Position', 'EH', 'Positionstext', 'Aufmaß'];
const rows = matList.map((r, i) => [
String(i + 1),
- r.artNr ?? '',
- r.bezeichnung,
- fmt1(r.menge).replace('.', ','),
- r.einheit,
- r.notiz ?? ''
+ EH_MAP[r.einheit] ?? r.einheit,
+ r.artNr ? `${r.bezeichnung} (${r.artNr})` : r.bezeichnung,
+ fmt1(r.menge).replace('.', ',')
]);
const csv = [header, ...rows].map(row => row.map(csvEscape).join(';')).join('\r\n');
const safeName = (titel || 'Materialschein').replace(/[^\p{L}\p{N}_-]+/gu, '_');
@@ -305,9 +307,7 @@ export default function ProjektDetail() {
-
+ saveFeld('monteur', v)} />