feat(scanner): Schwellen im Dashboard pro Benutzer
"Anzeige ab %" und "Hinweis ab %" liegen jetzt am CRM-Benutzer (crm_users.scanner_display_threshold/scanner_match_threshold) statt global in den Scanner-Einstellungen. NULL = globaler Standard. Die Mail-Schwelle bleibt global, die KI-Suche nimmt die Anzeigeschwelle des Suchenden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e1b69fffbd
commit
69001e9687
@@ -21,6 +21,7 @@ import {
|
||||
type ScannerAgent,
|
||||
type ScannerMatch,
|
||||
type ScannerSettings,
|
||||
type ScannerThresholds,
|
||||
type ScannerStatus,
|
||||
} from '../../lib/scannerApi';
|
||||
|
||||
@@ -165,13 +166,14 @@ export const ScannerDashboard: React.FC = () => {
|
||||
async (silent = false) => {
|
||||
if (!silent) setIsLoading(true);
|
||||
try {
|
||||
const [agentRows, portalRows, matchRows, settings, statusRow] = await Promise.all([
|
||||
const [agentRows, portalRows, matchRows, settings, statusRow, mine] = await Promise.all([
|
||||
scannerApi.agents(),
|
||||
scannerApi.portals(),
|
||||
scannerApi.matches({ limit: 2000 }),
|
||||
// Fehlende Einstellungen sind kein Grund, das Dashboard leer zu lassen.
|
||||
scannerApi.settings().catch((): Partial<ScannerSettings> => ({})),
|
||||
scannerApi.status().catch((): ScannerStatus | null => null),
|
||||
scannerApi.myThresholds().catch((): ScannerThresholds | null => null),
|
||||
]);
|
||||
|
||||
setAgents(agentRows);
|
||||
@@ -181,16 +183,15 @@ export const ScannerDashboard: React.FC = () => {
|
||||
// den Knopf hier genauso sperren wie ein selbst gestarteter.
|
||||
if (statusRow) setIsScanning(statusRow.scanRunning);
|
||||
|
||||
const parsedDisplay = parseInt(
|
||||
settings.display_threshold ?? SCANNER_SETTINGS_DEFAULTS.display_threshold,
|
||||
10
|
||||
);
|
||||
// Eigene Schwellen des Benutzers gehen vor, sonst der globale Standard.
|
||||
const parsedDisplay =
|
||||
mine?.display_threshold ??
|
||||
parseInt(settings.display_threshold ?? SCANNER_SETTINGS_DEFAULTS.display_threshold, 10);
|
||||
if (!Number.isNaN(parsedDisplay)) setDisplayThreshold(parsedDisplay);
|
||||
|
||||
const parsedNotify = parseInt(
|
||||
settings.match_threshold ?? SCANNER_SETTINGS_DEFAULTS.match_threshold,
|
||||
10
|
||||
);
|
||||
const parsedNotify =
|
||||
mine?.match_threshold ??
|
||||
parseInt(settings.match_threshold ?? SCANNER_SETTINGS_DEFAULTS.match_threshold, 10);
|
||||
if (!Number.isNaN(parsedNotify)) {
|
||||
notifyThreshold.current = parsedNotify;
|
||||
setMatchThreshold(parsedNotify);
|
||||
@@ -282,15 +283,15 @@ export const ScannerDashboard: React.FC = () => {
|
||||
if (saveTimer.current) window.clearTimeout(saveTimer.current);
|
||||
}, []);
|
||||
|
||||
/** Beide Schwellenwerte liegen in den Einstellungen - gebuendelt speichern. */
|
||||
/** Beide Schwellenwerte gehoeren dem Benutzer - gebuendelt speichern. */
|
||||
const persistThresholds = (display: number, notify: number) => {
|
||||
if (saveTimer.current) window.clearTimeout(saveTimer.current);
|
||||
setSaveState('pending');
|
||||
saveTimer.current = window.setTimeout(async () => {
|
||||
try {
|
||||
await scannerApi.saveSettings({
|
||||
display_threshold: String(display),
|
||||
match_threshold: String(notify),
|
||||
await scannerApi.saveMyThresholds({
|
||||
display_threshold: display,
|
||||
match_threshold: notify,
|
||||
});
|
||||
setSaveState('saved');
|
||||
window.setTimeout(() => setSaveState('idle'), 1500);
|
||||
|
||||
@@ -167,7 +167,7 @@ export const ScannerSettings: React.FC = () => {
|
||||
className={inputClass}
|
||||
/>
|
||||
<p className="mt-1 text-xs text-ink-400">
|
||||
Treffer darunter bleiben im Dashboard ausgeblendet.
|
||||
Standard: Treffer darunter bleiben im Dashboard ausgeblendet. Jeder Benutzer kann im Dashboard einen eigenen Wert setzen.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@@ -181,7 +181,7 @@ export const ScannerSettings: React.FC = () => {
|
||||
className={inputClass}
|
||||
/>
|
||||
<p className="mt-1 text-xs text-ink-400">
|
||||
Ab diesem Wert gibt es Signalton und E-Mail.
|
||||
Ab diesem Wert gibt es eine E-Mail. Für Signalton und Hinweis ist es der Standard, jeder Benutzer kann im Dashboard einen eigenen Wert setzen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -104,6 +104,11 @@ export interface ScannerSettings {
|
||||
applied_check_days: string;
|
||||
}
|
||||
|
||||
export interface ScannerThresholds {
|
||||
display_threshold: number | null;
|
||||
match_threshold: number | null;
|
||||
}
|
||||
|
||||
export const SCANNER_SETTINGS_DEFAULTS: ScannerSettings = {
|
||||
scanning_active: 'true',
|
||||
search_interval: '60',
|
||||
@@ -214,6 +219,14 @@ export const scannerApi = {
|
||||
body: JSON.stringify(input),
|
||||
}),
|
||||
|
||||
// Eigene Schwellen des angemeldeten Benutzers; null = globaler Standard.
|
||||
myThresholds: () => apiRequest<ScannerThresholds>('/scanner/my-thresholds'),
|
||||
saveMyThresholds: (input: ScannerThresholds) =>
|
||||
apiRequest<ScannerThresholds>('/scanner/my-thresholds', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(input),
|
||||
}),
|
||||
|
||||
// ---------- Scan ----------
|
||||
scan: () => apiRequest<{ message: string }>('/scanner/scan', { method: 'POST' }),
|
||||
status: () => apiRequest<ScannerStatus>('/scanner/status'),
|
||||
|
||||
Reference in New Issue
Block a user