Files
isletmem-kovakcrm/src/components/theme-customizer/prefs-initializer.tsx
T
kovakmedya 971d8b0a58 feat: store user theme prefs in DB instead of Appwrite account.getPrefs
db: create user_preferences table (isletmem) — userId unique index,
    theme/colorTheme/tweakcnTheme/radius/sidebar* columns

- user-prefs-actions.ts: getUserPrefs (server-side read, plain object),
  saveUserPrefsAction (upsert by userId, Permission.user for row security)
- schema.ts: TABLES.userPreferences added
- layout.tsx: replace account.getPrefs+JSON.parse hack with getUserPrefs()
- dashboard-shell, prefs-initializer, theme-customizer: import UserPrefs
  type and saveUserPrefsAction instead of old saveThemePrefsAction
- theme-prefs-actions.ts: deleted (no remaining references)

Reason: account.updatePrefs is shared across all apps in the same Appwrite
project (İşletmem + Emlak share project 69f27b51). A dedicated per-app
table gives proper isolation, typed schema, and no prototype-object issues.
2026-05-08 17:48:31 +03:00

63 lines
2.5 KiB
TypeScript

"use client";
import { useEffect, useRef } from "react";
import { useSidebarConfig } from "@/contexts/sidebar-context";
import { useTheme } from "@/hooks/use-theme";
import { useThemeManager } from "@/hooks/use-theme-manager";
import { tweakcnThemes } from "@/config/theme-data";
import type { UserPrefs as ThemePrefs } from "@/lib/appwrite/user-prefs-actions";
import { getLocalThemePrefs } from "@/lib/local-theme-prefs";
export function PrefsInitializer({ prefs }: { prefs: ThemePrefs }) {
const { setTheme } = useTheme();
const { updateConfig } = useSidebarConfig();
const { applyTheme, applyTweakcnTheme, applyRadius } = useThemeManager();
const applied = useRef(false);
useEffect(() => {
if (applied.current) return;
applied.current = true;
// localStorage wins (most recent change on this device); Appwrite prefs are fallback
const local = getLocalThemePrefs();
const effectiveTheme = prefs.theme;
const effectiveColorTheme = local.colorTheme ?? prefs.colorTheme;
const effectiveTweakcnTheme = local.tweakcnTheme ?? prefs.tweakcnTheme;
const effectiveRadius = local.radius ?? prefs.radius;
const effectiveSidebarVariant = (local.sidebarVariant as ThemePrefs["sidebarVariant"]) ?? prefs.sidebarVariant;
const effectiveSidebarCollapsible = (local.sidebarCollapsible as ThemePrefs["sidebarCollapsible"]) ?? prefs.sidebarCollapsible;
const effectiveSidebarSide = (local.sidebarSide as ThemePrefs["sidebarSide"]) ?? prefs.sidebarSide;
if (effectiveTheme) setTheme(effectiveTheme);
const isDark =
effectiveTheme === "dark" ||
(effectiveTheme !== "light" &&
typeof window !== "undefined" &&
window.matchMedia("(prefers-color-scheme: dark)").matches);
if (effectiveRadius) applyRadius(effectiveRadius);
if (effectiveColorTheme) {
applyTheme(effectiveColorTheme, isDark);
}
if (effectiveTweakcnTheme) {
const preset = tweakcnThemes.find((t) => t.value === effectiveTweakcnTheme)?.preset;
if (preset) applyTweakcnTheme(preset, isDark);
}
if (effectiveSidebarVariant || effectiveSidebarCollapsible || effectiveSidebarSide) {
updateConfig({
...(effectiveSidebarVariant && { variant: effectiveSidebarVariant }),
...(effectiveSidebarCollapsible && { collapsible: effectiveSidebarCollapsible }),
...(effectiveSidebarSide && { side: effectiveSidebarSide }),
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
}