aktueller stand

This commit is contained in:
2025-12-29 19:51:45 +01:00
parent f4323e20de
commit 41ef5107aa
9 changed files with 438 additions and 98 deletions

View File

@@ -347,6 +347,13 @@ const AdminSettingsPanel = ({
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Bot-Token"
/>
<input
type="text"
value={adminSettings.notifications?.telegram?.chatId || ''}
onChange={(event) => onNotificationChange('telegram', 'chatId', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Admin-Chat-ID"
/>
</div>
</div>
</div>

View File

@@ -1,4 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { formatDistanceToNowStrict } from 'date-fns';
import { de } from 'date-fns/locale';
import {
createColumnHelper,
flexRender,
@@ -47,6 +49,57 @@ const ColumnSelectFilter = ({ column, options, placeholder = 'Alle' }) => {
);
};
const formatLastPickupLabel = (value) => {
if (!value) {
return 'Unbekannt';
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return 'Unbekannt';
}
return date.toLocaleDateString('de-DE');
};
const formatLastPickupRelative = (value) => {
if (!value) {
return 'Unbekannt';
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return 'Unbekannt';
}
return formatDistanceToNowStrict(date, { addSuffix: true, locale: de });
};
const getLastPickupTimestamp = (value) => {
if (!value) {
return -1;
}
const date = new Date(value);
const time = date.getTime();
return Number.isNaN(time) ? -1 : time;
};
const getLastPickupStyle = (value) => {
const time = getLastPickupTimestamp(value);
if (time <= 0) {
return {};
}
const ageDays = (Date.now() - time) / (1000 * 60 * 60 * 24);
if (ageDays < 90) {
return {};
}
const minDays = 90;
const maxDays = 180;
const ratio = Math.min(Math.max((ageDays - minDays) / (maxDays - minDays), 0), 1);
const start = { r: 220, g: 38, b: 38 };
const end = { r: 127, g: 29, b: 29 };
const r = Math.round(start.r + (end.r - start.r) * ratio);
const g = Math.round(start.g + (end.g - start.g) * ratio);
const b = Math.round(start.b + (end.b - start.b) * ratio);
return { color: `rgb(${r}, ${g}, ${b})` };
};
function readConfigTableState() {
if (typeof window === 'undefined') {
return { sorting: [], columnFilters: [] };
@@ -222,11 +275,53 @@ const DashboardView = ({
sortingFn: 'alphanumeric',
filterFn: 'includesString'
}),
columnHelper.accessor('lastPickupAt', {
header: ({ column }) => (
<button
type="button"
className="flex w-full items-center justify-between text-left font-semibold"
onClick={column.getToggleSortingHandler()}
>
<span>Letzte Abholung</span>
{column.getIsSorted() ? (
<span className="text-xs text-gray-500">{column.getIsSorted() === 'asc' ? '▲' : '▼'}</span>
) : (
<span className="text-xs text-gray-400"></span>
)}
</button>
),
cell: ({ row }) => {
const lastPickupAt = row.original.lastPickupAt;
const style = getLastPickupStyle(lastPickupAt);
return (
<span
className="text-sm text-gray-700"
style={style}
title={formatLastPickupLabel(lastPickupAt)}
>
{formatLastPickupRelative(lastPickupAt)}
</span>
);
},
sortingFn: (rowA, rowB, columnId) => {
const a = getLastPickupTimestamp(rowA.getValue(columnId));
const b = getLastPickupTimestamp(rowB.getValue(columnId));
return a - b;
},
enableColumnFilter: false
}),
columnHelper.display({
id: 'checkProfileId',
header: () => <span>Profil prüfen</span>,
cell: ({ row }) => (
<div className="text-center" title={row.original.lastFetchDate ? `Letzte Abholung: ${row.original.lastFetchDate}` : 'Keine Info zur letzten Abholung'}>
<div
className="text-center"
title={
row.original.lastPickupAt
? `Letzte Abholung: ${formatLastPickupLabel(row.original.lastPickupAt)}`
: 'Keine Info zur letzten Abholung'
}
>
<input
type="checkbox"
className="h-5 w-5"
@@ -242,7 +337,14 @@ const DashboardView = ({
id: 'skipDormantCheck',
header: () => <span>Ruhe-Prüfung</span>,
cell: ({ row }) => (
<div className="text-center" title={row.original.lastFetchDate ? `Letzte Abholung: ${row.original.lastFetchDate}` : 'Keine Info zur letzten Abholung'}>
<div
className="text-center"
title={
row.original.lastPickupAt
? `Letzte Abholung: ${formatLastPickupLabel(row.original.lastPickupAt)}`
: 'Keine Info zur letzten Abholung'
}
>
<input
type="checkbox"
className="h-5 w-5"

View File

@@ -29,7 +29,8 @@ export const normalizeAdminSettings = (raw) => {
},
telegram: {
enabled: !!raw.notifications?.telegram?.enabled,
botToken: raw.notifications?.telegram?.botToken || ''
botToken: raw.notifications?.telegram?.botToken || '',
chatId: raw.notifications?.telegram?.chatId || ''
}
}
};
@@ -72,7 +73,8 @@ export const serializeAdminSettings = (adminSettings) => {
},
telegram: {
enabled: !!adminSettings.notifications?.telegram?.enabled,
botToken: adminSettings.notifications?.telegram?.botToken || ''
botToken: adminSettings.notifications?.telegram?.botToken || '',
chatId: adminSettings.notifications?.telegram?.chatId || ''
}
}
};