fix: add TS types

This commit is contained in:
2025-08-17 19:48:41 +02:00
parent 8269f3adad
commit 1b17d6b50a
4 changed files with 74 additions and 62 deletions

View File

@@ -76,7 +76,7 @@ async function loadData<T>(type: DataType): Promise<T> {
await fs.access(filePath) await fs.access(filePath)
} catch { } catch {
// File doesn't exist, create it with default data // File doesn't exist, create it with default data
const initialData = getDefaultData(type) const initialData = getDefaultData<T>(type)
await fs.writeFile(filePath, JSON.stringify(initialData, null, 2)) await fs.writeFile(filePath, JSON.stringify(initialData, null, 2))
return initialData as T return initialData as T
} }
@@ -136,7 +136,7 @@ async function calculateServerFreshnessToken(): Promise<string | null> {
// Wishlist specific functions // Wishlist specific functions
export async function loadWishlistData(): Promise<WishlistData> { export async function loadWishlistData(): Promise<WishlistData> {
const user = await getCurrentUser() const user = await getCurrentUser()
if (!user) return getDefaultWishlistData() if (!user) return getDefaultWishlistData<WishlistData>()
const data = await loadData<WishlistData>('wishlist') const data = await loadData<WishlistData>('wishlist')
return { return {
@@ -173,7 +173,7 @@ export async function saveWishlistItems(data: WishlistData): Promise<void> {
// Habits specific functions // Habits specific functions
export async function loadHabitsData(): Promise<HabitsData> { export async function loadHabitsData(): Promise<HabitsData> {
const user = await getCurrentUser() const user = await getCurrentUser()
if (!user) return getDefaultHabitsData() if (!user) return getDefaultHabitsData<HabitsData>()
const data = await loadData<HabitsData>('habits') const data = await loadData<HabitsData>('habits')
return { return {
habits: data.habits.filter(x => user.isAdmin || x.userIds?.includes(user.id)) habits: data.habits.filter(x => user.isAdmin || x.userIds?.includes(user.id))
@@ -208,14 +208,14 @@ export async function saveHabitsData(data: HabitsData): Promise<void> {
export async function loadCoinsData(): Promise<CoinsData> { export async function loadCoinsData(): Promise<CoinsData> {
try { try {
const user = await getCurrentUser() const user = await getCurrentUser()
if (!user) return getDefaultCoinsData() if (!user) return getDefaultCoinsData<CoinsData>()
const data = await loadData<CoinsData>('coins') const data = await loadData<CoinsData>('coins')
return { return {
...data, ...data,
transactions: user.isAdmin ? data.transactions : data.transactions.filter(x => x.userId === user.id) transactions: user.isAdmin ? data.transactions : data.transactions.filter(x => x.userId === user.id)
} }
} catch { } catch {
return getDefaultCoinsData() return getDefaultCoinsData<CoinsData>()
} }
} }
@@ -278,7 +278,7 @@ export async function addCoins({
} }
export async function loadSettings(): Promise<Settings> { export async function loadSettings(): Promise<Settings> {
const defaultSettings = getDefaultSettings() const defaultSettings = getDefaultSettings<Settings>()
try { try {
const user = await getCurrentUser() const user = await getCurrentUser()
@@ -370,7 +370,7 @@ export async function loadUsersData(): Promise<UserData> {
try { try {
return await loadData<UserData>('auth') return await loadData<UserData>('auth')
} catch { } catch {
return getDefaultUsersData() return getDefaultUsersData<UserData>()
} }
} }

View File

@@ -16,6 +16,7 @@ import { atom } from "jotai";
import { atomFamily, atomWithStorage } from "jotai/utils"; import { atomFamily, atomWithStorage } from "jotai/utils";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { import {
CoinsData,
CompletionCache, CompletionCache,
Freq, Freq,
getDefaultCoinsData, getDefaultCoinsData,
@@ -25,7 +26,12 @@ import {
getDefaultUsersData, getDefaultUsersData,
getDefaultWishlistData, getDefaultWishlistData,
Habit, Habit,
UserId HabitsData,
ServerSettings,
Settings,
UserData,
UserId,
WishlistData
} from "./types"; } from "./types";
export interface BrowserSettings { export interface BrowserSettings {
@@ -40,12 +46,12 @@ export const browserSettingsAtom = atomWithStorage('browserSettings', {
expandedWishlist: false expandedWishlist: false
} as BrowserSettings) } as BrowserSettings)
export const usersAtom = atom(getDefaultUsersData()) export const usersAtom = atom(getDefaultUsersData<UserData>())
export const settingsAtom = atom(getDefaultSettings()); export const settingsAtom = atom(getDefaultSettings<Settings>());
export const habitsAtom = atom(getDefaultHabitsData()); export const habitsAtom = atom(getDefaultHabitsData<HabitsData>());
export const coinsAtom = atom(getDefaultCoinsData()); export const coinsAtom = atom(getDefaultCoinsData<CoinsData>());
export const wishlistAtom = atom(getDefaultWishlistData()); export const wishlistAtom = atom(getDefaultWishlistData<WishlistData>());
export const serverSettingsAtom = atom(getDefaultServerSettings()); export const serverSettingsAtom = atom(getDefaultServerSettings<ServerSettings>());
// Derived atom for coins earned today // Derived atom for coins earned today
export const coinsEarnedTodayAtom = atom((get) => { export const coinsEarnedTodayAtom = atom((get) => {

View File

@@ -96,52 +96,58 @@ export interface WishlistData {
} }
// Default value functions // Default value functions
export const getDefaultUsersData = (): UserData => ({ export function getDefaultUsersData<UserData>(): UserData {
users: [ return {
{ users: [
id: crypto.randomUUID(), {
username: 'admin', id: crypto.randomUUID(),
// password: '', // No default password for admin initially? Or set a secure default? username: 'admin',
isAdmin: true, // password: '', // No default password for admin initially? Or set a secure default?
lastNotificationReadTimestamp: undefined, // Initialize as undefined isAdmin: true,
} lastNotificationReadTimestamp: undefined, // Initialize as undefined
] }
}); ]
} as UserData;
};
export const getDefaultHabitsData = (): HabitsData => ({ export function getDefaultHabitsData<HabitsData>(): HabitsData {
habits: [] return { habits: [] } as HabitsData;
}); }
export function getDefaultTasksData<TasksData>(): TasksData {
return { tasks: [] } as TasksData;
};
export const getDefaultCoinsData = (): CoinsData => ({ export function getDefaultCoinsData<CoinsData>(): CoinsData {
balance: 0, return { balance: 0, transactions: [] } as CoinsData;
transactions: [] };
});
export const getDefaultWishlistData = (): WishlistData => ({ export function getDefaultWishlistData<WishlistData>(): WishlistData {
items: [] return { items: [] } as WishlistData;
}); }
export const getDefaultSettings = (): Settings => ({ export function getDefaultSettings<Settings>(): Settings {
ui: { return {
useNumberFormatting: true, ui: {
useGrouping: true, useNumberFormatting: true,
}, useGrouping: true,
system: { },
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, system: {
weekStartDay: 1, // Monday timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
autoBackupEnabled: true, // Add this line (default to true) weekStartDay: 1, // Monday
language: 'en', // Default language autoBackupEnabled: true, // Add this line (default to true)
}, language: 'en', // Default language
profile: {} },
}); profile: {}
} as Settings;
};
export const getDefaultServerSettings = (): ServerSettings => ({ export function getDefaultServerSettings<ServerSettings>(): ServerSettings {
isDemo: false return { isDemo: false } as ServerSettings;
}) }
// Map of data types to their default values // Map of data types to their default values
export const DATA_DEFAULTS = { export const DATA_DEFAULTS: { [key: string]: <T>() => T } = {
wishlist: getDefaultWishlistData, wishlist: getDefaultWishlistData,
habits: getDefaultHabitsData, habits: getDefaultHabitsData,
coins: getDefaultCoinsData, coins: getDefaultCoinsData,

View File

@@ -942,11 +942,11 @@ describe('convertMachineReadableFrequencyToHumanReadable', () => {
}) })
describe('freshness utilities', () => { describe('freshness utilities', () => {
const mockSettings: Settings = getDefaultSettings(); const mockSettings: Settings = getDefaultSettings<Settings>();
const mockHabits: HabitsData = getDefaultHabitsData(); const mockHabits: HabitsData = getDefaultHabitsData<HabitsData>();
const mockCoins: CoinsData = getDefaultCoinsData(); const mockCoins: CoinsData = getDefaultCoinsData<CoinsData>();
const mockWishlist: WishlistData = getDefaultWishlistData(); const mockWishlist: WishlistData = getDefaultWishlistData<WishlistData>();
const mockUsers: UserData = getDefaultUsersData(); const mockUsers: UserData = getDefaultUsersData<UserData>();
// Add a user to mockUsers for more realistic testing // Add a user to mockUsers for more realistic testing
mockUsers.users.push({ mockUsers.users.push({
@@ -991,11 +991,11 @@ describe('freshness utilities', () => {
}); });
test('should handle empty data consistently', () => { test('should handle empty data consistently', () => {
const emptySettings = getDefaultSettings(); const emptySettings = getDefaultSettings<Settings>();
const emptyHabits = getDefaultHabitsData(); const emptyHabits = getDefaultHabitsData<HabitsData>();
const emptyCoins = getDefaultCoinsData(); const emptyCoins = getDefaultCoinsData<CoinsData>();
const emptyWishlist = getDefaultWishlistData(); const emptyWishlist = getDefaultWishlistData<WishlistData>();
const emptyUsers = getDefaultUsersData(); const emptyUsers = getDefaultUsersData<UserData>();
const string1 = prepareDataForHashing(emptySettings, emptyHabits, emptyCoins, emptyWishlist, emptyUsers); const string1 = prepareDataForHashing(emptySettings, emptyHabits, emptyCoins, emptyWishlist, emptyUsers);
const string2 = prepareDataForHashing(emptySettings, emptyHabits, emptyCoins, emptyWishlist, emptyUsers); const string2 = prepareDataForHashing(emptySettings, emptyHabits, emptyCoins, emptyWishlist, emptyUsers);