fix(dienstgrad): add ASB→Abschnittssachbearbeiter, remove non-existent ranks (FA/FF/BOI/BAM variants), sync DB constraint, TS types, and display map
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
-- Migration 090: Add 'Abschnittssachbearbeiter' and remove non-existent ranks
|
||||||
|
-- (Feuerwehranwärter, Feuerwehrfrau variants, Brandoberinspektor, Brandamtmann)
|
||||||
|
-- from the mitglieder_profile dienstgrad CHECK constraint.
|
||||||
|
|
||||||
|
-- Null out any existing rows with ranks that are being removed
|
||||||
|
UPDATE mitglieder_profile
|
||||||
|
SET dienstgrad = NULL
|
||||||
|
WHERE dienstgrad IN (
|
||||||
|
'Feuerwehranwärter',
|
||||||
|
'Feuerwehrfrau',
|
||||||
|
'Oberfeuerwehrfrau',
|
||||||
|
'Hauptfeuerwehrfrau',
|
||||||
|
'Brandoberinspektor',
|
||||||
|
'Brandamtmann',
|
||||||
|
'Ehren-Feuerwehrfrau',
|
||||||
|
'Ehren-Oberfeuerwehrfrau',
|
||||||
|
'Ehren-Hauptfeuerwehrfrau',
|
||||||
|
'Ehren-Brandoberinspektor',
|
||||||
|
'Ehren-Brandamtmann'
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE mitglieder_profile
|
||||||
|
DROP CONSTRAINT IF EXISTS mitglieder_profile_dienstgrad_check;
|
||||||
|
|
||||||
|
ALTER TABLE mitglieder_profile
|
||||||
|
ADD CONSTRAINT mitglieder_profile_dienstgrad_check
|
||||||
|
CHECK (dienstgrad IS NULL OR dienstgrad IN (
|
||||||
|
-- Standard Dienstgrade
|
||||||
|
'Jugendfeuerwehrmann',
|
||||||
|
'Probefeuerwehrmann',
|
||||||
|
'Feuerwehrmann',
|
||||||
|
'Oberfeuerwehrmann',
|
||||||
|
'Hauptfeuerwehrmann',
|
||||||
|
'Löschmeister',
|
||||||
|
'Oberlöschmeister',
|
||||||
|
'Hauptlöschmeister',
|
||||||
|
'Brandmeister',
|
||||||
|
'Oberbrandmeister',
|
||||||
|
'Hauptbrandmeister',
|
||||||
|
'Brandinspektor',
|
||||||
|
'Oberbrandinspektor',
|
||||||
|
'Verwaltungsmeister',
|
||||||
|
'Oberverwaltungsmeister',
|
||||||
|
'Hauptverwaltungsmeister',
|
||||||
|
'Verwalter',
|
||||||
|
'Sachbearbeiter',
|
||||||
|
'Abschnittssachbearbeiter',
|
||||||
|
-- Ehrendienstgrade
|
||||||
|
'Ehren-Feuerwehrmann',
|
||||||
|
'Ehren-Oberfeuerwehrmann',
|
||||||
|
'Ehren-Hauptfeuerwehrmann',
|
||||||
|
'Ehren-Löschmeister',
|
||||||
|
'Ehren-Oberlöschmeister',
|
||||||
|
'Ehren-Hauptlöschmeister',
|
||||||
|
'Ehren-Brandmeister',
|
||||||
|
'Ehren-Oberbrandmeister',
|
||||||
|
'Ehren-Hauptbrandmeister',
|
||||||
|
'Ehren-Brandinspektor',
|
||||||
|
'Ehren-Oberbrandinspektor',
|
||||||
|
'Ehren-Verwaltungsmeister',
|
||||||
|
'Ehren-Oberverwaltungsmeister',
|
||||||
|
'Ehren-Hauptverwaltungsmeister',
|
||||||
|
'Ehren-Verwalter',
|
||||||
|
'Ehren-Sachbearbeiter',
|
||||||
|
'Ehren-Abschnittssachbearbeiter'
|
||||||
|
));
|
||||||
@@ -6,13 +6,11 @@ import { z } from 'zod';
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export const DIENSTGRAD_VALUES = [
|
export const DIENSTGRAD_VALUES = [
|
||||||
'Feuerwehranwärter',
|
'Jugendfeuerwehrmann',
|
||||||
|
'Probefeuerwehrmann',
|
||||||
'Feuerwehrmann',
|
'Feuerwehrmann',
|
||||||
'Feuerwehrfrau',
|
|
||||||
'Oberfeuerwehrmann',
|
'Oberfeuerwehrmann',
|
||||||
'Oberfeuerwehrfrau',
|
|
||||||
'Hauptfeuerwehrmann',
|
'Hauptfeuerwehrmann',
|
||||||
'Hauptfeuerwehrfrau',
|
|
||||||
'Löschmeister',
|
'Löschmeister',
|
||||||
'Oberlöschmeister',
|
'Oberlöschmeister',
|
||||||
'Hauptlöschmeister',
|
'Hauptlöschmeister',
|
||||||
@@ -21,8 +19,12 @@ export const DIENSTGRAD_VALUES = [
|
|||||||
'Hauptbrandmeister',
|
'Hauptbrandmeister',
|
||||||
'Brandinspektor',
|
'Brandinspektor',
|
||||||
'Oberbrandinspektor',
|
'Oberbrandinspektor',
|
||||||
'Brandoberinspektor',
|
'Verwaltungsmeister',
|
||||||
'Brandamtmann',
|
'Oberverwaltungsmeister',
|
||||||
|
'Hauptverwaltungsmeister',
|
||||||
|
'Verwalter',
|
||||||
|
'Sachbearbeiter',
|
||||||
|
'Abschnittssachbearbeiter',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const STATUS_VALUES = [
|
export const STATUS_VALUES = [
|
||||||
|
|||||||
@@ -63,25 +63,25 @@ import { ConfirmDialog, StatusChip, PageHeader } from '../components/templates';
|
|||||||
// ── Helpers ──
|
// ── Helpers ──
|
||||||
|
|
||||||
const DIENSTGRAD_KURZ: Record<string, string> = {
|
const DIENSTGRAD_KURZ: Record<string, string> = {
|
||||||
'Feuerwehranwärter': 'FA', 'Jugendfeuerwehrmann': 'JFM', 'Probefeuerwehrmann': 'PFM',
|
'Jugendfeuerwehrmann': 'JFM', 'Probefeuerwehrmann': 'PFM',
|
||||||
'Feuerwehrmann': 'FM', 'Feuerwehrfrau': 'FF',
|
'Feuerwehrmann': 'FM',
|
||||||
'Oberfeuerwehrmann': 'OFM', 'Oberfeuerwehrfrau': 'OFF',
|
'Oberfeuerwehrmann': 'OFM',
|
||||||
'Hauptfeuerwehrmann': 'HFM', 'Hauptfeuerwehrfrau': 'HFF',
|
'Hauptfeuerwehrmann': 'HFM',
|
||||||
'Löschmeister': 'LM', 'Oberlöschmeister': 'OLM', 'Hauptlöschmeister': 'HLM',
|
'Löschmeister': 'LM', 'Oberlöschmeister': 'OLM', 'Hauptlöschmeister': 'HLM',
|
||||||
'Brandmeister': 'BM', 'Oberbrandmeister': 'OBM', 'Hauptbrandmeister': 'HBM',
|
'Brandmeister': 'BM', 'Oberbrandmeister': 'OBM', 'Hauptbrandmeister': 'HBM',
|
||||||
'Brandinspektor': 'BI', 'Oberbrandinspektor': 'OBI', 'Brandoberinspektor': 'BOI',
|
'Brandinspektor': 'BI', 'Oberbrandinspektor': 'OBI',
|
||||||
'Brandamtmann': 'BAM',
|
|
||||||
'Verwaltungsmeister': 'VM', 'Oberverwaltungsmeister': 'OVM',
|
'Verwaltungsmeister': 'VM', 'Oberverwaltungsmeister': 'OVM',
|
||||||
'Hauptverwaltungsmeister': 'HVM', 'Verwalter': 'V',
|
'Hauptverwaltungsmeister': 'HVM', 'Verwalter': 'V',
|
||||||
'Ehren-Feuerwehrmann': 'E-FM', 'Ehren-Feuerwehrfrau': 'E-FF',
|
'Sachbearbeiter': 'SB', 'Abschnittssachbearbeiter': 'ASB',
|
||||||
'Ehren-Oberfeuerwehrmann': 'E-OFM', 'Ehren-Oberfeuerwehrfrau': 'E-OFF',
|
'Ehren-Feuerwehrmann': 'E-FM',
|
||||||
'Ehren-Hauptfeuerwehrmann': 'E-HFM', 'Ehren-Hauptfeuerwehrfrau': 'E-HFF',
|
'Ehren-Oberfeuerwehrmann': 'E-OFM',
|
||||||
|
'Ehren-Hauptfeuerwehrmann': 'E-HFM',
|
||||||
'Ehren-Löschmeister': 'E-LM', 'Ehren-Oberlöschmeister': 'E-OLM', 'Ehren-Hauptlöschmeister': 'E-HLM',
|
'Ehren-Löschmeister': 'E-LM', 'Ehren-Oberlöschmeister': 'E-OLM', 'Ehren-Hauptlöschmeister': 'E-HLM',
|
||||||
'Ehren-Brandmeister': 'E-BM', 'Ehren-Oberbrandmeister': 'E-OBM', 'Ehren-Hauptbrandmeister': 'E-HBM',
|
'Ehren-Brandmeister': 'E-BM', 'Ehren-Oberbrandmeister': 'E-OBM', 'Ehren-Hauptbrandmeister': 'E-HBM',
|
||||||
'Ehren-Brandinspektor': 'E-BI', 'Ehren-Oberbrandinspektor': 'E-OBI', 'Ehren-Brandoberinspektor': 'E-BOI',
|
'Ehren-Brandinspektor': 'E-BI', 'Ehren-Oberbrandinspektor': 'E-OBI',
|
||||||
'Ehren-Brandamtmann': 'E-BAM',
|
|
||||||
'Ehren-Verwaltungsmeister': 'E-VM', 'Ehren-Oberverwaltungsmeister': 'E-OVM',
|
'Ehren-Verwaltungsmeister': 'E-VM', 'Ehren-Oberverwaltungsmeister': 'E-OVM',
|
||||||
'Ehren-Hauptverwaltungsmeister': 'E-HVM', 'Ehren-Verwalter': 'E-V',
|
'Ehren-Hauptverwaltungsmeister': 'E-HVM', 'Ehren-Verwalter': 'E-V',
|
||||||
|
'Ehren-Sachbearbeiter': 'ESB', 'Ehren-Abschnittssachbearbeiter': 'EASB',
|
||||||
};
|
};
|
||||||
|
|
||||||
const kurzDienstgrad = (d?: string) => (d ? (DIENSTGRAD_KURZ[d] ?? d) : undefined);
|
const kurzDienstgrad = (d?: string) => (d ? (DIENSTGRAD_KURZ[d] ?? d) : undefined);
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ function Mitglieder() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [debouncedSearch, selectedStatus, selectedDienstgrad, page]);
|
}, [debouncedSearch, selectedStatus, selectedDienstgrad, page, pageSize]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Reset to page 0 when search changes
|
// Reset to page 0 when search changes
|
||||||
@@ -137,8 +137,7 @@ function Mitglieder() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetchMembers();
|
fetchMembers();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [page, pageSize, selectedStatus, selectedDienstgrad]);
|
||||||
}, [page, selectedStatus, selectedDienstgrad]);
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Event handlers
|
// Event handlers
|
||||||
@@ -275,6 +274,22 @@ function Mitglieder() {
|
|||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
|
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
|
||||||
|
<TablePagination
|
||||||
|
component="div"
|
||||||
|
count={total}
|
||||||
|
page={page}
|
||||||
|
onPageChange={(_e, newPage) => setPage(newPage)}
|
||||||
|
rowsPerPage={pageSize}
|
||||||
|
rowsPerPageOptions={[25, 50, 100, { value: -1, label: 'Alle' }]}
|
||||||
|
onRowsPerPageChange={(e) => {
|
||||||
|
setPageSize(parseInt(e.target.value, 10));
|
||||||
|
setPage(0);
|
||||||
|
}}
|
||||||
|
labelRowsPerPage="Einträge pro Seite:"
|
||||||
|
labelDisplayedRows={({ from, to, count }) =>
|
||||||
|
`${from}–${to} von ${count !== -1 ? count : `mehr als ${to}`}`
|
||||||
|
}
|
||||||
|
/>
|
||||||
<DataTable<MemberListItem>
|
<DataTable<MemberListItem>
|
||||||
columns={[
|
columns={[
|
||||||
{ key: 'profile_picture_url', label: 'Foto', width: 56, sortable: false, searchable: false, render: (member) => {
|
{ key: 'profile_picture_url', label: 'Foto', width: 56, sortable: false, searchable: false, render: (member) => {
|
||||||
@@ -330,22 +345,6 @@ function Mitglieder() {
|
|||||||
stickyHeader
|
stickyHeader
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TablePagination
|
|
||||||
component="div"
|
|
||||||
count={total}
|
|
||||||
page={page}
|
|
||||||
onPageChange={(_e, newPage) => setPage(newPage)}
|
|
||||||
rowsPerPage={pageSize}
|
|
||||||
rowsPerPageOptions={[25, 50, 100, { value: -1, label: 'Alle' }]}
|
|
||||||
onRowsPerPageChange={(e) => {
|
|
||||||
setPageSize(parseInt(e.target.value, 10));
|
|
||||||
setPage(0);
|
|
||||||
}}
|
|
||||||
labelRowsPerPage="Einträge pro Seite:"
|
|
||||||
labelDisplayedRows={({ from, to, count }) =>
|
|
||||||
`${from}–${to} von ${count !== -1 ? count : `mehr als ${to}`}`
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,11 @@
|
|||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
export const DIENSTGRAD_VALUES = [
|
export const DIENSTGRAD_VALUES = [
|
||||||
'Feuerwehranwärter',
|
'Jugendfeuerwehrmann',
|
||||||
|
'Probefeuerwehrmann',
|
||||||
'Feuerwehrmann',
|
'Feuerwehrmann',
|
||||||
'Feuerwehrfrau',
|
|
||||||
'Oberfeuerwehrmann',
|
'Oberfeuerwehrmann',
|
||||||
'Oberfeuerwehrfrau',
|
|
||||||
'Hauptfeuerwehrmann',
|
'Hauptfeuerwehrmann',
|
||||||
'Hauptfeuerwehrfrau',
|
|
||||||
'Löschmeister',
|
'Löschmeister',
|
||||||
'Oberlöschmeister',
|
'Oberlöschmeister',
|
||||||
'Hauptlöschmeister',
|
'Hauptlöschmeister',
|
||||||
@@ -19,8 +17,12 @@ export const DIENSTGRAD_VALUES = [
|
|||||||
'Hauptbrandmeister',
|
'Hauptbrandmeister',
|
||||||
'Brandinspektor',
|
'Brandinspektor',
|
||||||
'Oberbrandinspektor',
|
'Oberbrandinspektor',
|
||||||
'Brandoberinspektor',
|
'Verwaltungsmeister',
|
||||||
'Brandamtmann',
|
'Oberverwaltungsmeister',
|
||||||
|
'Hauptverwaltungsmeister',
|
||||||
|
'Verwalter',
|
||||||
|
'Sachbearbeiter',
|
||||||
|
'Abschnittssachbearbeiter',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const STATUS_VALUES = [
|
export const STATUS_VALUES = [
|
||||||
|
|||||||
@@ -17,15 +17,11 @@ function log(msg: string) {
|
|||||||
*/
|
*/
|
||||||
function mapDienstgrad(raw: string): string | null {
|
function mapDienstgrad(raw: string): string | null {
|
||||||
const abbrevMap: Record<string, string> = {
|
const abbrevMap: Record<string, string> = {
|
||||||
'fa': 'Feuerwehranwärter',
|
|
||||||
'jfm': 'Jugendfeuerwehrmann',
|
'jfm': 'Jugendfeuerwehrmann',
|
||||||
'pfm': 'Probefeuerwehrmann',
|
'pfm': 'Probefeuerwehrmann',
|
||||||
'fm': 'Feuerwehrmann',
|
'fm': 'Feuerwehrmann',
|
||||||
'ff': 'Feuerwehrfrau',
|
|
||||||
'ofm': 'Oberfeuerwehrmann',
|
'ofm': 'Oberfeuerwehrmann',
|
||||||
'off': 'Oberfeuerwehrfrau',
|
|
||||||
'hfm': 'Hauptfeuerwehrmann',
|
'hfm': 'Hauptfeuerwehrmann',
|
||||||
'hff': 'Hauptfeuerwehrfrau',
|
|
||||||
'lm': 'Löschmeister',
|
'lm': 'Löschmeister',
|
||||||
'olm': 'Oberlöschmeister',
|
'olm': 'Oberlöschmeister',
|
||||||
'hlm': 'Hauptlöschmeister',
|
'hlm': 'Hauptlöschmeister',
|
||||||
@@ -34,13 +30,12 @@ function mapDienstgrad(raw: string): string | null {
|
|||||||
'hbm': 'Hauptbrandmeister',
|
'hbm': 'Hauptbrandmeister',
|
||||||
'bi': 'Brandinspektor',
|
'bi': 'Brandinspektor',
|
||||||
'obi': 'Oberbrandinspektor',
|
'obi': 'Oberbrandinspektor',
|
||||||
'boi': 'Brandoberinspektor',
|
|
||||||
'bam': 'Brandamtmann',
|
|
||||||
'vm': 'Verwaltungsmeister',
|
'vm': 'Verwaltungsmeister',
|
||||||
'ovm': 'Oberverwaltungsmeister',
|
'ovm': 'Oberverwaltungsmeister',
|
||||||
'hvm': 'Hauptverwaltungsmeister',
|
'hvm': 'Hauptverwaltungsmeister',
|
||||||
'v': 'Verwalter',
|
'v': 'Verwalter',
|
||||||
'sb': 'Sachbearbeiter',
|
'sb': 'Sachbearbeiter',
|
||||||
|
'asb': 'Abschnittssachbearbeiter',
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalized = raw.trim().toLowerCase().replace(/\*/g, '');
|
const normalized = raw.trim().toLowerCase().replace(/\*/g, '');
|
||||||
|
|||||||
Reference in New Issue
Block a user