v1.32: deutsches Datumsformat in allen Modulen (fmtDatum, ISO-Normalisierung)
This commit is contained in:
+7
-2
@@ -211,6 +211,11 @@ const mapBeleg = (b) => ({
|
|||||||
|
|
||||||
const deutschesDatum = () => new Date().toLocaleDateString('de-DE')
|
const deutschesDatum = () => new Date().toLocaleDateString('de-DE')
|
||||||
const deutschesDatumZeit = () => new Date().toLocaleString('de-DE')
|
const deutschesDatumZeit = () => new Date().toLocaleString('de-DE')
|
||||||
|
/** ISO-Eingaben (YYYY-MM-DD, z. B. aus date-Feldern) in das deutsche Format wandeln. */
|
||||||
|
const datumDeutsch = (s) => {
|
||||||
|
const iso = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(s || '').trim())
|
||||||
|
return iso ? `${iso[3]}.${iso[2]}.${iso[1]}` : String(s || '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
// ── Health ───────────────────────────────────────────────────
|
// ── Health ───────────────────────────────────────────────────
|
||||||
app.get('/api/healthz', async (_req, res) => {
|
app.get('/api/healthz', async (_req, res) => {
|
||||||
@@ -566,7 +571,7 @@ app.post('/api/meters', auth, adminOnly, async (req, res, next) => {
|
|||||||
if (!ZAEHLER_ARTEN.includes(art)) return res.status(400).json({ fehler: 'Ungültige Zählerart.' })
|
if (!ZAEHLER_ARTEN.includes(art)) return res.status(400).json({ fehler: 'Ungültige Zählerart.' })
|
||||||
const standZahl = Number(stand)
|
const standZahl = Number(stand)
|
||||||
if (!Number.isFinite(standZahl) || standZahl < 0) return res.status(400).json({ fehler: 'Ungültiger Zählerstand.' })
|
if (!Number.isFinite(standZahl) || standZahl < 0) return res.status(400).json({ fehler: 'Ungültiger Zählerstand.' })
|
||||||
const datum = String(standAm || '').trim() || deutschesDatum()
|
const datum = datumDeutsch(standAm) || deutschesDatum()
|
||||||
if (!zaehlerVerlauf.datumGueltig(datum)) return res.status(400).json({ fehler: 'Ungültiges Ablesedatum.' })
|
if (!zaehlerVerlauf.datumGueltig(datum)) return res.status(400).json({ fehler: 'Ungültiges Ablesedatum.' })
|
||||||
// Wohnung angegeben → Haus aus der Wohnung ableiten; sonst muss das Haus im Body stehen
|
// Wohnung angegeben → Haus aus der Wohnung ableiten; sonst muss das Haus im Body stehen
|
||||||
let einheitId = unitId ? String(unitId) : null
|
let einheitId = unitId ? String(unitId) : null
|
||||||
@@ -665,7 +670,7 @@ app.post('/api/meters/:id/readings', auth, async (req, res, next) => {
|
|||||||
const { stand, standAm } = req.body || {}
|
const { stand, standAm } = req.body || {}
|
||||||
const standZahl = Number(stand)
|
const standZahl = Number(stand)
|
||||||
if (!Number.isFinite(standZahl) || standZahl < 0) return res.status(400).json({ fehler: 'Ungültiger Zählerstand.' })
|
if (!Number.isFinite(standZahl) || standZahl < 0) return res.status(400).json({ fehler: 'Ungültiger Zählerstand.' })
|
||||||
const datum = String(standAm || '').trim() || deutschesDatum()
|
const datum = datumDeutsch(standAm) || deutschesDatum()
|
||||||
if (!zaehlerVerlauf.datumGueltig(datum)) return res.status(400).json({ fehler: 'Ungültiges Ablesedatum.' })
|
if (!zaehlerVerlauf.datumGueltig(datum)) return res.status(400).json({ fehler: 'Ungültiges Ablesedatum.' })
|
||||||
const { eintrag, anomalie, grund } = await zaehlerVerlauf.standEintragen({
|
const { eintrag, anomalie, grund } = await zaehlerVerlauf.standEintragen({
|
||||||
meter: m, stand: standZahl, standAm: datum,
|
meter: m, stand: standZahl, standAm: datum,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { useStore } from '../store/store'
|
import { useStore } from '../store/store'
|
||||||
|
import { fmtDatum } from '../lib/format'
|
||||||
import type { DocumentItem } from '../types'
|
import type { DocumentItem } from '../types'
|
||||||
|
|
||||||
/** PDF-Lightbox (v1.31): Inline-Vorschau erzeugter/gespeicherter PDFs mit
|
/** PDF-Lightbox (v1.31): Inline-Vorschau erzeugter/gespeicherter PDFs mit
|
||||||
@@ -68,7 +69,7 @@ export default function PdfLightbox({ dokument, trigger }: {
|
|||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<DialogTitle className="truncate text-base">{dokument.title}</DialogTitle>
|
<DialogTitle className="truncate text-base">{dokument.title}</DialogTitle>
|
||||||
<p className="truncate text-xs text-slate-500 dark:text-slate-400">
|
<p className="truncate text-xs text-slate-500 dark:text-slate-400">
|
||||||
{dokument.category} · {dokument.date}
|
{dokument.category} · {fmtDatum(dokument.date)}
|
||||||
{dokument.fileName && ` · ${dokument.fileName}`}
|
{dokument.fileName && ` · ${dokument.fileName}`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/** Zentrale Datumsformate (v1.32): Anzeige immer deutsch (TT.MM.JJJJ).
|
||||||
|
* Ältere Einträge können ISO-Formate (YYYY-MM-DD) enthalten – diese werden
|
||||||
|
* robust umgerechnet, deutsche Werte unverändert durchgereicht. */
|
||||||
|
|
||||||
|
const ISO_DATUM = /^(\d{4})-(\d{2})-(\d{2})$/
|
||||||
|
const ISO_DATUMZEIT = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?/
|
||||||
|
|
||||||
|
/** „2026-10-04" → „04.10.2026"; „04.10.2026" bleibt; unbekannt → unverändert. */
|
||||||
|
export function fmtDatum(wert: string | null | undefined): string {
|
||||||
|
if (!wert) return '–'
|
||||||
|
const iso = ISO_DATUM.exec(wert.trim())
|
||||||
|
if (iso) return `${iso[3]}.${iso[2]}.${iso[1]}`
|
||||||
|
return wert
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wie fmtDatum, aber mit Uhrzeit: ISO-Datetime → „04.10.2026, 15:23". */
|
||||||
|
export function fmtDatumZeit(wert: string | null | undefined): string {
|
||||||
|
if (!wert) return '–'
|
||||||
|
const iso = ISO_DATUMZEIT.exec(wert.trim())
|
||||||
|
if (iso) {
|
||||||
|
const sek = iso[6] ? `:${iso[6]}` : ''
|
||||||
|
return `${iso[3]}.${iso[2]}.${iso[1]}, ${iso[4]}:${iso[5]}${sek}`
|
||||||
|
}
|
||||||
|
return fmtDatum(wert)
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import { Input } from '@/components/ui/input'
|
|||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
import { useStore } from '../store/store'
|
import { useStore } from '../store/store'
|
||||||
|
import { fmtDatumZeit } from '../lib/format'
|
||||||
import type { Benutzer, BenutzerRolle } from '../types'
|
import type { Benutzer, BenutzerRolle } from '../types'
|
||||||
|
|
||||||
function Chip({ label, value, tone = 'slate' }: { label: string; value: number | string; tone?: 'slate' | 'amber' | 'teal' | 'blue' }) {
|
function Chip({ label, value, tone = 'slate' }: { label: string; value: number | string; tone?: 'slate' | 'amber' | 'teal' | 'blue' }) {
|
||||||
@@ -419,7 +420,7 @@ export default function BenutzerVerwaltung() {
|
|||||||
<div className="truncate text-xs text-slate-500">
|
<div className="truncate text-xs text-slate-500">
|
||||||
@{b.benutzername}
|
@{b.benutzername}
|
||||||
{einheit && ` · ${einheit.building}, Whg. ${einheit.number}`}
|
{einheit && ` · ${einheit.building}, Whg. ${einheit.number}`}
|
||||||
{b.letzterLogin && ` · zuletzt: ${b.letzterLogin}`}
|
{b.letzterLogin && ` · zuletzt: ${fmtDatumZeit(b.letzterLogin)}`}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 flex items-center gap-1.5">
|
<div className="mt-1 flex items-center gap-1.5">
|
||||||
<Mail className="h-3.5 w-3.5 shrink-0 text-slate-400" />
|
<Mail className="h-3.5 w-3.5 shrink-0 text-slate-400" />
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { Link } from 'react-router'
|
import { Link } from 'react-router'
|
||||||
import { useStore, fmtEUR } from '../store/store'
|
import { useStore, fmtEUR } from '../store/store'
|
||||||
|
import { fmtDatum } from '../lib/format'
|
||||||
|
|
||||||
const monthShort = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
|
const monthShort = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
|
||||||
|
|
||||||
@@ -146,7 +147,7 @@ function TenantDashboard() {
|
|||||||
<span className="text-xs font-semibold uppercase tracking-wide text-teal-700">
|
<span className="text-xs font-semibold uppercase tracking-wide text-teal-700">
|
||||||
{a.tag}
|
{a.tag}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-slate-400">{a.date}</span>
|
<span className="text-xs text-slate-400">{fmtDatum(a.date)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 text-sm font-medium">{a.title}</div>
|
<div className="mt-1 text-sm font-medium">{a.title}</div>
|
||||||
<div className="mt-0.5 line-clamp-2 text-xs text-slate-500">{a.text}</div>
|
<div className="mt-0.5 line-clamp-2 text-xs text-slate-500">{a.text}</div>
|
||||||
@@ -174,7 +175,7 @@ function TenantDashboard() {
|
|||||||
<li key={t.id} className="flex items-center justify-between rounded-xl bg-slate-50 px-4 py-3">
|
<li key={t.id} className="flex items-center justify-between rounded-xl bg-slate-50 px-4 py-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium">{t.title}</div>
|
<div className="text-sm font-medium">{t.title}</div>
|
||||||
<div className="text-xs text-slate-400">{t.category} · {t.createdAt}</div>
|
<div className="text-xs text-slate-400">{t.category} · {fmtDatum(t.createdAt)}</div>
|
||||||
</div>
|
</div>
|
||||||
<span className={`rounded-full px-2.5 py-1 text-xs font-semibold ${
|
<span className={`rounded-full px-2.5 py-1 text-xs font-semibold ${
|
||||||
t.status === 'offen' ? 'bg-amber-100 text-amber-800' : 'bg-blue-100 text-blue-800'
|
t.status === 'offen' ? 'bg-amber-100 text-amber-800' : 'bg-blue-100 text-blue-800'
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from '@/components/ui/table'
|
} from '@/components/ui/table'
|
||||||
import { useStore } from '../store/store'
|
import { useStore } from '../store/store'
|
||||||
|
import { fmtDatum } from '../lib/format'
|
||||||
import PdfLightbox from '../components/PdfLightbox'
|
import PdfLightbox from '../components/PdfLightbox'
|
||||||
import { apiUpload, ApiFehler } from '../lib/api'
|
import { apiUpload, ApiFehler } from '../lib/api'
|
||||||
import type { DocumentItem, Unit } from '../types'
|
import type { DocumentItem, Unit } from '../types'
|
||||||
@@ -343,7 +344,7 @@ export default function Documents() {
|
|||||||
{d.category}
|
{d.category}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{d.date}</TableCell>
|
<TableCell>{fmtDatum(d.date)}</TableCell>
|
||||||
<TableCell>{d.size}</TableCell>
|
<TableCell>{d.size}</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from '@/components/ui/table'
|
} from '@/components/ui/table'
|
||||||
import { useStore, fmtEUR } from '../store/store'
|
import { useStore, fmtEUR } from '../store/store'
|
||||||
|
import { fmtDatum } from '../lib/format'
|
||||||
import type { Beleg, DocumentItem, KostenItem } from '../types'
|
import type { Beleg, DocumentItem, KostenItem } from '../types'
|
||||||
import PdfLightbox from '../components/PdfLightbox'
|
import PdfLightbox from '../components/PdfLightbox'
|
||||||
|
|
||||||
@@ -408,7 +409,7 @@ function BelegeListe() {
|
|||||||
{b.notiz && <div className="text-xs font-normal text-slate-400">{b.notiz}</div>}
|
{b.notiz && <div className="text-xs font-normal text-slate-400">{b.notiz}</div>}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-slate-500">
|
<TableCell className="text-slate-500">
|
||||||
{b.datum || '–'}
|
{b.datum ? fmtDatum(b.datum) : '–'}
|
||||||
{b.jahr && <span className="text-xs text-slate-400"> · {b.jahr}</span>}
|
{b.jahr && <span className="text-xs text-slate-400"> · {b.jahr}</span>}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-slate-500">{b.kategorie || '–'}</TableCell>
|
<TableCell className="text-slate-500">{b.kategorie || '–'}</TableCell>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from '@/components/ui/dialog'
|
} from '@/components/ui/dialog'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { useStore } from '../store/store'
|
import { useStore } from '../store/store'
|
||||||
|
import { fmtDatumZeit } from '../lib/format'
|
||||||
|
|
||||||
function NewMessageDialog({ onCreated }: { onCreated: (id: string) => void }) {
|
function NewMessageDialog({ onCreated }: { onCreated: (id: string) => void }) {
|
||||||
const { newThread, role, meineEinheit } = useStore()
|
const { newThread, role, meineEinheit } = useStore()
|
||||||
@@ -185,7 +186,7 @@ export default function Messages() {
|
|||||||
)}
|
)}
|
||||||
<div className="whitespace-pre-wrap">{m.text}</div>
|
<div className="whitespace-pre-wrap">{m.text}</div>
|
||||||
<div className={`mt-1 text-[10px] ${mine ? 'text-teal-100/80' : 'text-slate-400'}`}>
|
<div className={`mt-1 text-[10px] ${mine ? 'text-teal-100/80' : 'text-slate-400'}`}>
|
||||||
{m.at}
|
{fmtDatumZeit(m.at)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { useStore } from '../store/store'
|
import { useStore } from '../store/store'
|
||||||
|
import { fmtDatum } from '../lib/format'
|
||||||
|
|
||||||
export default function Profil() {
|
export default function Profil() {
|
||||||
const { me, role, meineEinheit, units, aktualisiereProfil } = useStore()
|
const { me, role, meineEinheit, units, aktualisiereProfil } = useStore()
|
||||||
@@ -88,7 +89,7 @@ export default function Profil() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-xs uppercase tracking-wide text-slate-400">Mitglied seit</dt>
|
<dt className="text-xs uppercase tracking-wide text-slate-400">Mitglied seit</dt>
|
||||||
<dd className="font-medium">{me.erstelltAm}</dd>
|
<dd className="font-medium">{fmtDatum(me.erstelltAm)}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
import { useStore } from '../store/store'
|
import { useStore } from '../store/store'
|
||||||
|
import { fmtDatum, fmtDatumZeit } from '../lib/format'
|
||||||
import type { TicketPriority, TicketStatus } from '../types'
|
import type { TicketPriority, TicketStatus } from '../types'
|
||||||
|
|
||||||
const statusStyle: Record<TicketStatus, string> = {
|
const statusStyle: Record<TicketStatus, string> = {
|
||||||
@@ -214,7 +215,7 @@ export default function Tickets() {
|
|||||||
{role === 'admin' && ` · ${t.tenant}`}
|
{role === 'admin' && ` · ${t.tenant}`}
|
||||||
</span>
|
</span>
|
||||||
<span className="inline-flex items-center gap-1">
|
<span className="inline-flex items-center gap-1">
|
||||||
<CalendarDays className="h-3.5 w-3.5" /> {t.createdAt}
|
<CalendarDays className="h-3.5 w-3.5" /> {fmtDatum(t.createdAt)}
|
||||||
</span>
|
</span>
|
||||||
<span>Kategorie: {t.category}</span>
|
<span>Kategorie: {t.category}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -265,7 +266,7 @@ export default function Tickets() {
|
|||||||
<div className="mt-2 text-xs text-slate-600">
|
<div className="mt-2 text-xs text-slate-600">
|
||||||
<span className="font-semibold">{letzte.author}:</span>{' '}
|
<span className="font-semibold">{letzte.author}:</span>{' '}
|
||||||
<span className="line-clamp-2">{letzte.text}</span>
|
<span className="line-clamp-2">{letzte.text}</span>
|
||||||
<span className="mt-0.5 block text-slate-400">{letzte.at}</span>
|
<span className="mt-0.5 block text-slate-400">{fmtDatumZeit(letzte.at)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
import { useStore } from '../store/store'
|
import { useStore } from '../store/store'
|
||||||
|
import { fmtDatum } from '../lib/format'
|
||||||
import type { Meter, MeterReading, ZaehlerArt } from '../types'
|
import type { Meter, MeterReading, ZaehlerArt } from '../types'
|
||||||
|
|
||||||
const ARTEN: ZaehlerArt[] = ['Strom', 'Wasser', 'Gas', 'Wärme', 'Sonstiges']
|
const ARTEN: ZaehlerArt[] = ['Strom', 'Wasser', 'Gas', 'Wärme', 'Sonstiges']
|
||||||
@@ -282,7 +283,7 @@ function VerlaufDialog({ meter }: { meter: Meter }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 text-xs text-slate-500 dark:text-slate-400">
|
<div className="mt-1 text-xs text-slate-500 dark:text-slate-400">
|
||||||
Ablesedatum: {r.standAm}
|
Ablesedatum: {fmtDatum(r.standAm)}
|
||||||
{r.erfasstVon && <span> · erfasst von {r.erfasstVon}</span>}
|
{r.erfasstVon && <span> · erfasst von {r.erfasstVon}</span>}
|
||||||
</div>
|
</div>
|
||||||
{r.anomalie && r.anomalieGrund && (
|
{r.anomalie && r.anomalieGrund && (
|
||||||
@@ -317,7 +318,7 @@ function StandEintragenDialog({ meter }: { meter: Meter }) {
|
|||||||
setSende(true)
|
setSende(true)
|
||||||
setFehler(null)
|
setFehler(null)
|
||||||
setWarnung(null)
|
setWarnung(null)
|
||||||
const r = await zaehlerstandEintragen(meter.id, standZahl, datum)
|
const r = await zaehlerstandEintragen(meter.id, standZahl, datum.split('-').reverse().join('.'))
|
||||||
setSende(false)
|
setSende(false)
|
||||||
if (r.fehler) return setFehler(r.fehler)
|
if (r.fehler) return setFehler(r.fehler)
|
||||||
if (r.anomalie) {
|
if (r.anomalie) {
|
||||||
@@ -528,7 +529,7 @@ export default function Zaehler() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<CalendarDays className="h-3.5 w-3.5 shrink-0" />
|
<CalendarDays className="h-3.5 w-3.5 shrink-0" />
|
||||||
Stand: {m.standAm}
|
Stand: {fmtDatum(m.standAm)}
|
||||||
<span className="text-slate-400 dark:text-slate-500">(gepflegt: {m.geaendertAm})</span>
|
<span className="text-slate-400 dark:text-slate-500">(gepflegt: {m.geaendertAm})</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -572,7 +573,7 @@ export default function Zaehler() {
|
|||||||
<td className="px-3 py-2.5 text-right font-semibold tabular-nums text-slate-800 dark:text-slate-100">
|
<td className="px-3 py-2.5 text-right font-semibold tabular-nums text-slate-800 dark:text-slate-100">
|
||||||
{fmtStand(m.stand)}
|
{fmtStand(m.stand)}
|
||||||
</td>
|
</td>
|
||||||
<td className="hidden px-3 py-2.5 text-slate-500 dark:text-slate-400 lg:table-cell">{m.standAm}</td>
|
<td className="hidden px-3 py-2.5 text-slate-500 dark:text-slate-400 lg:table-cell">{fmtDatum(m.standAm)}</td>
|
||||||
<td className="px-3 py-2.5">
|
<td className="px-3 py-2.5">
|
||||||
{m.letzteAnomalie && (
|
{m.letzteAnomalie && (
|
||||||
<TriangleAlert
|
<TriangleAlert
|
||||||
|
|||||||
Reference in New Issue
Block a user