feat: add Steuertags concept to separate workflow from content tags
Build and Push Multi-Platform Images / build-and-push (push) Successful in 38s

- New steuertag_ids setting to mark tags as workflow-only (not editable)
- DocumentEditModal shows only content tags (non-Steuertags) as editable chips
- Backend preserves Steuertags when saving document tag changes
- ManuellBearbeitenPage renders content tag chips under document title
- New Steuertags settings tab with multi-select and color preview

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 11:46:39 +02:00
parent dad0136365
commit d96e06e86d
8 changed files with 245 additions and 5 deletions
@@ -9,6 +9,7 @@ import {
PlusOutlined, DeleteOutlined, EditOutlined, CloudUploadOutlined,
HistoryOutlined, MinusCircleOutlined, CopyOutlined, KeyOutlined,
QrcodeOutlined, UnorderedListOutlined, PrinterOutlined, GlobalOutlined,
TagsOutlined,
} from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import type { FormInstance } from 'antd';
@@ -2663,6 +2664,86 @@ function AgrarmonitorTab() {
}
// ═══════════════════════════════════════════════════════════════════
// Steuertags Tab
// ═══════════════════════════════════════════════════════════════════
function SteuertagsTab() {
const [tags, setTags] = useState<PaperlessTag[]>([]);
const [selected, setSelected] = useState<number[]>([]);
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
const load = useCallback(async () => {
setLoading(true);
try {
const [fetchedTags, steuertagIds] = await Promise.all([
paperlessApi.getTags(),
settingsApi.getSteuertagIds(),
]);
setTags(fetchedTags);
setSelected(steuertagIds);
} catch {
message.error('Tags konnten nicht geladen werden.');
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
load();
}, [load]);
const handleSave = async () => {
setSaving(true);
try {
await settingsApi.updateSteuertagIds(selected);
message.success('Steuertags gespeichert.');
} catch {
message.error('Steuertags konnten nicht gespeichert werden.');
} finally {
setSaving(false);
}
};
return (
<div>
<Alert
type="info"
showIcon
style={{ marginBottom: 16 }}
message="Steuertags"
description="Als Steuertag markierte Tags dienen der Workflow-Steuerung (z. B. Fertig/Nicht fertig, manuell bearbeiten) und werden NICHT als bearbeitbare Chips im Dokument angezeigt. Alle übrigen Tags gelten als inhaltliche Tags und erscheinen unter dem Titel sowie als Auswahlfeld im Bearbeiten-Dialog."
/>
<Select
mode="multiple"
allowClear
showSearch
optionFilterProp="label"
loading={loading}
style={{ width: '100%', maxWidth: 700 }}
placeholder="Steuertags auswählen"
value={selected}
onChange={setSelected}
options={tags.map((t) => ({ value: t.id, label: t.name }))}
optionRender={(opt) => {
const tag = tags.find((t) => t.id === opt.value);
return tag ? (
<Tag color={tag.color} style={{ color: tag.text_color }}>{tag.name}</Tag>
) : (
opt.label
);
}}
/>
<div style={{ marginTop: 16 }}>
<Button type="primary" loading={saving} onClick={handleSave}>
Speichern
</Button>
</div>
</div>
);
}
// ═══════════════════════════════════════════════════════════════════
// Settings Page
// ═══════════════════════════════════════════════════════════════════
@@ -2699,6 +2780,11 @@ export default function SettingsPage() {
label: <span><UserOutlined /> Korrespondenten</span>,
children: <CorrespondentsTab />,
},
{
key: 'steuertags',
label: <span><TagsOutlined /> Steuertags</span>,
children: <SteuertagsTab />,
},
{
key: 'export-targets',
label: <span><CloudUploadOutlined /> Export-Ziele</span>,