7 Commits
2 changed files with 48 additions and 29 deletions
+46 -27
View File
@@ -12,7 +12,7 @@ export const useAdminState = () => {
const [periods, setPeriods] = useState<Period[]>([]); const [periods, setPeriods] = useState<Period[]>([]);
const [attendance, setAttendance] = useState<AttendanceDataMap>({}); const [attendance, setAttendance] = useState<AttendanceDataMap>({});
const [scheduleData, setScheduleData] = useState<any[]>([]); const [scheduleData, setScheduleData] = useState<any[]>([]);
const [dailyLogs, setDailyLogs] = useState<Record<string, string>>({}); // NYTT const [dailyLogs, setDailyLogs] = useState<Record<string, string>>({});
const [activePeriodId, setActivePeriodId] = useState<string>(''); const [activePeriodId, setActivePeriodId] = useState<string>('');
const [isLoadingData, setIsLoadingData] = useState<boolean>(true); const [isLoadingData, setIsLoadingData] = useState<boolean>(true);
@@ -56,7 +56,6 @@ export const useAdminState = () => {
return nextAttendance; return nextAttendance;
}; };
// NYTT: Hantera offfline-kö för dagböcker
const applyQueueToLogs = async (baseLogs: Record<string, string>) => { const applyQueueToLogs = async (baseLogs: Record<string, string>) => {
const queue = await localforage.getItem<any[]>('sync-queue') || []; const queue = await localforage.getItem<any[]>('sync-queue') || [];
const nextLogs = { ...baseLogs }; const nextLogs = { ...baseLogs };
@@ -85,20 +84,57 @@ export const useAdminState = () => {
const refreshData = useCallback(async (isInitialLoad = false) => { const refreshData = useCallback(async (isInitialLoad = false) => {
if (!isInitialLoad) setIsSyncing(true); if (!isInitialLoad) setIsSyncing(true);
if (isInitialLoad) {
try {
const cachedSched = await localforage.getItem<any[]>('cachedScheduleData');
const cachedData = await localforage.getItem<any[]>('cachedAdminData');
const cachedLogs = await localforage.getItem<Record<string, string>>('cachedDailyLogs');
if (cachedData) {
const { loadedPeriods, loadedAttendance } = mapDbDataToUI(cachedData);
const finalAttendance = await applyQueueToAttendance(loadedAttendance);
const finalLogs = await applyQueueToLogs(cachedLogs || {});
if (cachedSched) setScheduleData(cachedSched);
setPeriods(loadedPeriods);
setAttendance(finalAttendance);
setDailyLogs(finalLogs);
if (loadedPeriods.length > 0) {
const today = toIsoDate(new Date());
const activePeriods = loadedPeriods.filter(p => today >= p.startDate && today <= p.endDate);
if (activePeriods.length > 0) setActivePeriodId(activePeriods[0].id);
else setActivePeriodId(loadedPeriods[0].id);
}
setIsLoadingData(false);
}
} catch (e) {
console.warn("Kunde inte ladda lokal cache", e);
}
}
const withTimeout = <T>(promise: Promise<T>, ms: number = 5000): Promise<T> => {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error('NETWORK_TIMEOUT')), ms);
promise
.then(val => { clearTimeout(timer); resolve(val); })
.catch(err => { clearTimeout(timer); reject(err); });
});
};
try { try {
if (navigator.onLine) await flushOfflineQueue(); if (navigator.onLine) await flushOfflineQueue();
const schedRes = await readJsonFile('schedule.json'); const schedRes = await withTimeout(readJsonFile('schedule.json'));
if (schedRes.success && schedRes.data) { if (schedRes.success && schedRes.data) {
setScheduleData(schedRes.data); setScheduleData(schedRes.data);
await localforage.setItem('cachedScheduleData', schedRes.data); await localforage.setItem('cachedScheduleData', schedRes.data);
} }
const dbPeriods = await getAdminData(); const dbPeriods = await withTimeout(getAdminData());
await localforage.setItem('cachedAdminData', dbPeriods); await localforage.setItem('cachedAdminData', dbPeriods);
// NYTT: Ladda in loggarna const dbLogs = await withTimeout(getDailyLogsDb());
const dbLogs = await getDailyLogsDb();
const mappedLogs: Record<string, string> = {}; const mappedLogs: Record<string, string> = {};
dbLogs.forEach((l: { date: string | number; content: string; }) => mappedLogs[l.date] = l.content); dbLogs.forEach((l: { date: string | number; content: string; }) => mappedLogs[l.date] = l.content);
await localforage.setItem('cachedDailyLogs', mappedLogs); await localforage.setItem('cachedDailyLogs', mappedLogs);
@@ -113,35 +149,20 @@ export const useAdminState = () => {
setAttendance(finalAttendance); setAttendance(finalAttendance);
setDailyLogs(finalLogs); setDailyLogs(finalLogs);
if (isInitialLoad && loadedPeriods.length > 0) { if (isInitialLoad && periods.length === 0 && loadedPeriods.length > 0) {
const today = toIsoDate(new Date()); const today = toIsoDate(new Date());
const activePeriods = loadedPeriods.filter(p => today >= p.startDate && today <= p.endDate); const activePeriods = loadedPeriods.filter(p => today >= p.startDate && today <= p.endDate);
if (activePeriods.length > 0) setActivePeriodId(activePeriods[0].id); if (activePeriods.length > 0) setActivePeriodId(activePeriods[0].id);
else setActivePeriodId(loadedPeriods[0].id); else setActivePeriodId(loadedPeriods[0].id);
} }
} catch (error) { } catch (error) {
console.warn("Nätverkstimeout eller offline - faller tillbaka på cache.", error);
setIsOffline(true); setIsOffline(true);
const cachedSched = await localforage.getItem<any[]>('cachedScheduleData');
if (cachedSched) setScheduleData(cachedSched);
const cachedData = await localforage.getItem<any[]>('cachedAdminData');
if (cachedData) {
const { loadedPeriods, loadedAttendance } = mapDbDataToUI(cachedData);
const finalAttendance = await applyQueueToAttendance(loadedAttendance);
setPeriods(loadedPeriods);
setAttendance(finalAttendance);
const cachedLogs = await localforage.getItem<Record<string, string>>('cachedDailyLogs') || {};
const finalLogs = await applyQueueToLogs(cachedLogs);
setDailyLogs(finalLogs);
if (isInitialLoad && loadedPeriods.length > 0) setActivePeriodId(loadedPeriods[0].id);
}
} finally { } finally {
setIsLoadingData(false); setIsLoadingData(false);
setIsSyncing(false); setIsSyncing(false);
} }
}, [flushOfflineQueue]); }, [flushOfflineQueue, periods.length]);
useEffect(() => { refreshData(true); }, [refreshData]); useEffect(() => { refreshData(true); }, [refreshData]);
@@ -162,7 +183,6 @@ export const useAdminState = () => {
const addToOfflineQueue = async (action: any) => { const addToOfflineQueue = async (action: any) => {
const queue = await localforage.getItem<any[]>('sync-queue') || []; const queue = await localforage.getItem<any[]>('sync-queue') || [];
// Undvik dubbletter för just detta datum och denna åtgärd
let filteredQueue = queue; let filteredQueue = queue;
if (action.type === 'SET_DAILY_LOG') { if (action.type === 'SET_DAILY_LOG') {
filteredQueue = queue.filter(q => !(q.type === 'SET_DAILY_LOG' && q.payload.date === action.payload.date)); filteredQueue = queue.filter(q => !(q.type === 'SET_DAILY_LOG' && q.payload.date === action.payload.date));
@@ -242,7 +262,6 @@ export const useAdminState = () => {
else await removeAttendanceDb(date, youthId, shiftId).catch(async () => { await addToOfflineQueue({ type: 'REMOVE_ATTENDANCE', payload: { date, youthId, shiftId } }); }); else await removeAttendanceDb(date, youthId, shiftId).catch(async () => { await addToOfflineQueue({ type: 'REMOVE_ATTENDANCE', payload: { date, youthId, shiftId } }); });
}; };
// NYTT: Funktion för att spara dagbok lokalt och i databasen
const setDailyLog = async (date: string, content: string) => { const setDailyLog = async (date: string, content: string) => {
setDailyLogs(prev => ({ ...prev, [date]: content })); setDailyLogs(prev => ({ ...prev, [date]: content }));
@@ -257,7 +276,7 @@ export const useAdminState = () => {
return { return {
periods, attendance, activePeriodId, setActivePeriodId, isLoadingData, isOffline, isSyncing, scheduleData, periods, attendance, activePeriodId, setActivePeriodId, isLoadingData, isOffline, isSyncing, scheduleData,
dailyLogs, setDailyLog, // <-- Expotera här dailyLogs, setDailyLog,
createPeriod, deletePeriod, bulkAddYouth, removeYouth, createPeriod, deletePeriod, bulkAddYouth, removeYouth,
setManualAttendance, bulkSetManualAttendance, addPendingAttendance, removeAttendanceEntry, refreshData setManualAttendance, bulkSetManualAttendance, addPendingAttendance, removeAttendanceEntry, refreshData
}; };
+2 -2
View File
@@ -1,7 +1,7 @@
// prisma.config.ts // prisma.config.ts
import "dotenv/config"; import "dotenv/config";
import { defineConfig, env } from "prisma/config"; import { defineConfig } from "prisma/config";
export default defineConfig({ export default defineConfig({
schema: "prisma/schema.prisma", schema: "prisma/schema.prisma",
@@ -9,6 +9,6 @@ export default defineConfig({
path: "prisma/migrations", path: "prisma/migrations",
}, },
datasource: { datasource: {
url: env("DATABASE_URL"), url: process.env.DATABASE_URL,
}, },
}); });