refactoring

This commit is contained in:
2025-11-10 13:49:49 +01:00
parent 7b3625ae3b
commit 33626c7e45
6 changed files with 106 additions and 309 deletions

View File

@@ -1,139 +0,0 @@
import { useCallback, useEffect, useState } from 'react';
import { sortEntriesByLabel } from '../utils/configUtils';
import { formatDateValue } from '../utils/dateUtils';
const API_URL = '/api/iobroker/pickup-config';
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const usePickupConfig = () => {
const [config, setConfig] = useState([]);
const [loading, setLoading] = useState(true);
const [status, setStatus] = useState('');
const [error, setError] = useState('');
const fetchConfig = useCallback(async () => {
setLoading(true);
setError('');
try {
const response = await fetch(API_URL);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
setConfig(sortEntriesByLabel(Array.isArray(data) ? data : []));
} catch (err) {
setError('Fehler beim Laden der Konfiguration: ' + err.message);
} finally {
setLoading(false);
}
}, []);
const saveConfig = useCallback(async () => {
setStatus('Speichere...');
setError('');
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
setStatus('Konfiguration erfolgreich gespeichert!');
setTimeout(() => setStatus(''), 3000);
} catch (err) {
setError('Fehler beim Speichern: ' + err.message);
}
}, []);
const updateEntryByIndex = useCallback((index, updater) => {
setConfig((prev) => {
if (!prev[index]) {
return prev;
}
const next = [...prev];
next[index] = updater(next[index]);
return next;
});
}, []);
const updateEntryById = useCallback((entryId, updater) => {
setConfig((prev) =>
prev.map((item) => {
if (item.id !== entryId) {
return item;
}
return updater(item);
})
);
}, []);
const toggleActive = useCallback((index) => {
updateEntryByIndex(index, (entry) => ({ ...entry, active: !entry.active }));
}, [updateEntryByIndex]);
const toggleProfileCheck = useCallback((index) => {
updateEntryByIndex(index, (entry) => ({ ...entry, checkProfileId: !entry.checkProfileId }));
}, [updateEntryByIndex]);
const toggleOnlyNotify = useCallback((index) => {
updateEntryByIndex(index, (entry) => ({ ...entry, onlyNotify: !entry.onlyNotify }));
}, [updateEntryByIndex]);
const changeWeekday = useCallback((index, value) => {
updateEntryByIndex(index, (entry) => {
const next = { ...entry, desiredWeekday: value };
if (next.desiredDateRange) {
delete next.desiredDateRange;
}
return next;
});
}, [updateEntryByIndex]);
const updateDateRange = useCallback((entryId, startDate, endDate) => {
const startValue = formatDateValue(startDate);
const endValue = formatDateValue(endDate);
updateEntryById(entryId, (entry) => {
const next = { ...entry };
if (startValue || endValue) {
next.desiredDateRange = {
start: startValue || endValue,
end: endValue || startValue
};
if (next.desiredWeekday) {
delete next.desiredWeekday;
}
} else if (next.desiredDateRange) {
delete next.desiredDateRange;
}
if (next.desiredDate) {
delete next.desiredDate;
}
return next;
});
}, [updateEntryById]);
useEffect(() => {
fetchConfig();
}, [fetchConfig]);
return {
API_URL,
config,
loading,
status,
error,
fetchConfig,
saveConfig,
toggleActive,
toggleProfileCheck,
toggleOnlyNotify,
changeWeekday,
updateDateRange
};
};
export default usePickupConfig;

View File

@@ -1,84 +0,0 @@
import React from 'react';
import { act, render } from '@testing-library/react';
import usePickupConfig from './usePickupConfig';
describe('usePickupConfig', () => {
const mockConfig = [
{ id: '2', label: 'Beta Store', active: false, checkProfileId: true, onlyNotify: false },
{ id: '1', label: 'alpha Shop', active: false, checkProfileId: true, onlyNotify: false }
];
const advanceTimers = async (ms = 0) => {
await act(async () => {
jest.advanceTimersByTime(ms);
await Promise.resolve();
});
};
const renderHook = () => {
let latest;
const TestHarness = () => {
latest = usePickupConfig();
return null;
};
render(<TestHarness />);
return () => latest;
};
beforeEach(() => {
jest.useFakeTimers();
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(mockConfig)
})
);
});
afterEach(() => {
global.fetch.mockRestore?.();
jest.useRealTimers();
});
test('fetchConfig loads sorted configuration', async () => {
const getHook = renderHook();
await advanceTimers(600);
const hook = getHook();
expect(hook.loading).toBe(false);
expect(hook.config[0].label).toBe('alpha Shop');
expect(hook.config[hook.config.length - 1].label).toBe('Beta Store');
});
test('mutators update entries as expected', async () => {
const getHook = renderHook();
await advanceTimers(600);
await act(async () => {
getHook().toggleActive(0);
getHook().toggleProfileCheck(0);
getHook().toggleOnlyNotify(0);
});
let updated = getHook().config[0];
expect(updated.active).toBe(true);
expect(updated.checkProfileId).toBe(false);
expect(updated.onlyNotify).toBe(true);
await act(async () => {
getHook().changeWeekday(0, 'Montag');
});
updated = getHook().config[0];
expect(updated.desiredWeekday).toBe('Montag');
expect(updated.desiredDateRange).toBeUndefined();
await act(async () => {
getHook().updateDateRange(updated.id, new Date('2025-01-01'), new Date('2025-01-03'));
});
updated = getHook().config[0];
expect(updated.desiredDateRange).toEqual({
start: '2025-01-01',
end: '2025-01-03'
});
expect(updated.desiredWeekday).toBeUndefined();
});
});