fix: surface saveUserPrefsAction errors, convert empty strings to null
- saveUserPrefsAction now returns {ok, error} instead of void
- empty string values converted to null before writing (Appwrite nullable attrs)
- getUserPrefs treats empty strings as absent (str() helper)
- ThemeCustomizer savePrefs wrapper shows toast on failure
- manually cleared corrupted colorTheme/tweakcnTheme empty strings from DB
This commit is contained in:
@@ -9,6 +9,7 @@ import { useThemeManager } from '@/hooks/use-theme-manager'
|
|||||||
import { useTheme } from '@/hooks/use-theme'
|
import { useTheme } from '@/hooks/use-theme'
|
||||||
import { useSidebarConfig } from '@/contexts/sidebar-context'
|
import { useSidebarConfig } from '@/contexts/sidebar-context'
|
||||||
import { tweakcnThemes } from '@/config/theme-data'
|
import { tweakcnThemes } from '@/config/theme-data'
|
||||||
|
import { toast } from 'sonner'
|
||||||
import { saveUserPrefsAction } from '@/lib/appwrite/user-prefs-actions'
|
import { saveUserPrefsAction } from '@/lib/appwrite/user-prefs-actions'
|
||||||
import type { UserPrefs as ThemePrefs } from '@/lib/appwrite/user-prefs-actions'
|
import type { UserPrefs as ThemePrefs } from '@/lib/appwrite/user-prefs-actions'
|
||||||
import { getLocalThemePrefs, saveLocalThemePrefs } from '@/lib/local-theme-prefs'
|
import { getLocalThemePrefs, saveLocalThemePrefs } from '@/lib/local-theme-prefs'
|
||||||
@@ -58,6 +59,12 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto
|
|||||||
})
|
})
|
||||||
}, [sidebarConfig.variant, sidebarConfig.collapsible, sidebarConfig.side])
|
}, [sidebarConfig.variant, sidebarConfig.collapsible, sidebarConfig.side])
|
||||||
|
|
||||||
|
const savePrefs = (update: Parameters<typeof saveUserPrefsAction>[0]) => {
|
||||||
|
saveUserPrefsAction(update).then((res) => {
|
||||||
|
if (!res.ok) toast.error("Tercihler kaydedilemedi: " + res.error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setSelectedTheme("default")
|
setSelectedTheme("default")
|
||||||
setSelectedTweakcnTheme("")
|
setSelectedTweakcnTheme("")
|
||||||
@@ -68,7 +75,7 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto
|
|||||||
applyRadius("0.5rem")
|
applyRadius("0.5rem")
|
||||||
updateSidebarConfig({ variant: "inset", collapsible: "offcanvas", side: "left" })
|
updateSidebarConfig({ variant: "inset", collapsible: "offcanvas", side: "left" })
|
||||||
saveLocalThemePrefs({ colorTheme: "default", tweakcnTheme: "", radius: "0.5rem", sidebarVariant: "inset", sidebarCollapsible: "offcanvas", sidebarSide: "left" })
|
saveLocalThemePrefs({ colorTheme: "default", tweakcnTheme: "", radius: "0.5rem", sidebarVariant: "inset", sidebarCollapsible: "offcanvas", sidebarSide: "left" })
|
||||||
void saveUserPrefsAction({ colorTheme: "default", tweakcnTheme: "", radius: "0.5rem", sidebarVariant: "inset", sidebarCollapsible: "offcanvas", sidebarSide: "left" })
|
savePrefs({ colorTheme: "default", tweakcnTheme: "", radius: "0.5rem", sidebarVariant: "inset", sidebarCollapsible: "offcanvas", sidebarSide: "left" })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleImport = (themeData: ImportedTheme) => {
|
const handleImport = (themeData: ImportedTheme) => {
|
||||||
@@ -165,20 +172,20 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto
|
|||||||
setSelectedTheme(value)
|
setSelectedTheme(value)
|
||||||
setSelectedTweakcnTheme("")
|
setSelectedTweakcnTheme("")
|
||||||
saveLocalThemePrefs({ colorTheme: value, tweakcnTheme: "" })
|
saveLocalThemePrefs({ colorTheme: value, tweakcnTheme: "" })
|
||||||
void saveUserPrefsAction({ colorTheme: value, tweakcnTheme: "" })
|
savePrefs({ colorTheme: value, tweakcnTheme: "" })
|
||||||
}}
|
}}
|
||||||
selectedTweakcnTheme={selectedTweakcnTheme}
|
selectedTweakcnTheme={selectedTweakcnTheme}
|
||||||
setSelectedTweakcnTheme={(value) => {
|
setSelectedTweakcnTheme={(value) => {
|
||||||
setSelectedTweakcnTheme(value)
|
setSelectedTweakcnTheme(value)
|
||||||
setSelectedTheme("")
|
setSelectedTheme("")
|
||||||
saveLocalThemePrefs({ tweakcnTheme: value, colorTheme: "" })
|
saveLocalThemePrefs({ tweakcnTheme: value, colorTheme: "" })
|
||||||
void saveUserPrefsAction({ tweakcnTheme: value, colorTheme: "" })
|
savePrefs({ tweakcnTheme: value, colorTheme: "" })
|
||||||
}}
|
}}
|
||||||
selectedRadius={selectedRadius}
|
selectedRadius={selectedRadius}
|
||||||
setSelectedRadius={(value) => {
|
setSelectedRadius={(value) => {
|
||||||
setSelectedRadius(value)
|
setSelectedRadius(value)
|
||||||
saveLocalThemePrefs({ radius: value })
|
saveLocalThemePrefs({ radius: value })
|
||||||
void saveUserPrefsAction({ radius: value })
|
savePrefs({ radius: value })
|
||||||
}}
|
}}
|
||||||
setImportedTheme={setImportedTheme}
|
setImportedTheme={setImportedTheme}
|
||||||
onImportClick={handleImportClick}
|
onImportClick={handleImportClick}
|
||||||
|
|||||||
@@ -30,11 +30,12 @@ export async function getUserPrefs(): Promise<UserPrefs> {
|
|||||||
if (result.rows.length === 0) return {};
|
if (result.rows.length === 0) return {};
|
||||||
|
|
||||||
const row = result.rows[0] as Record<string, unknown>;
|
const row = result.rows[0] as Record<string, unknown>;
|
||||||
|
const str = (v: unknown) => (v && typeof v === "string" ? v : undefined);
|
||||||
return {
|
return {
|
||||||
theme: (row.theme as UserPrefs["theme"]) ?? undefined,
|
theme: (row.theme as UserPrefs["theme"]) ?? undefined,
|
||||||
colorTheme: (row.colorTheme as string) ?? undefined,
|
colorTheme: str(row.colorTheme),
|
||||||
tweakcnTheme: (row.tweakcnTheme as string) ?? undefined,
|
tweakcnTheme: str(row.tweakcnTheme),
|
||||||
radius: (row.radius as string) ?? undefined,
|
radius: str(row.radius),
|
||||||
sidebarVariant: (row.sidebarVariant as UserPrefs["sidebarVariant"]) ?? undefined,
|
sidebarVariant: (row.sidebarVariant as UserPrefs["sidebarVariant"]) ?? undefined,
|
||||||
sidebarCollapsible: (row.sidebarCollapsible as UserPrefs["sidebarCollapsible"]) ?? undefined,
|
sidebarCollapsible: (row.sidebarCollapsible as UserPrefs["sidebarCollapsible"]) ?? undefined,
|
||||||
sidebarSide: (row.sidebarSide as UserPrefs["sidebarSide"]) ?? undefined,
|
sidebarSide: (row.sidebarSide as UserPrefs["sidebarSide"]) ?? undefined,
|
||||||
@@ -44,18 +45,21 @@ export async function getUserPrefs(): Promise<UserPrefs> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveUserPrefsAction(update: Partial<UserPrefs>): Promise<void> {
|
export async function saveUserPrefsAction(
|
||||||
|
update: Partial<UserPrefs>,
|
||||||
|
): Promise<{ ok: boolean; error?: string }> {
|
||||||
try {
|
try {
|
||||||
const { account } = await createSessionClient();
|
const { account } = await createSessionClient();
|
||||||
const user = await account.get();
|
const user = await account.get();
|
||||||
const { tablesDB } = createAdminClient();
|
const { tablesDB } = createAdminClient();
|
||||||
|
|
||||||
// Sadece tanımlı (undefined olmayan) alanları yaz
|
// undefined → skip, "" → null (Appwrite rejects empty strings on nullable attrs)
|
||||||
const clean: Record<string, unknown> = {};
|
const clean: Record<string, unknown> = {};
|
||||||
for (const [k, v] of Object.entries(update)) {
|
for (const [k, v] of Object.entries(update)) {
|
||||||
if (v !== undefined) clean[k] = v;
|
if (v === undefined) continue;
|
||||||
|
clean[k] = v === "" ? null : v;
|
||||||
}
|
}
|
||||||
if (Object.keys(clean).length === 0) return;
|
if (Object.keys(clean).length === 0) return { ok: true };
|
||||||
|
|
||||||
const existing = await tablesDB.listRows({
|
const existing = await tablesDB.listRows({
|
||||||
databaseId: DATABASE_ID,
|
databaseId: DATABASE_ID,
|
||||||
@@ -85,7 +89,11 @@ export async function saveUserPrefsAction(update: Partial<UserPrefs>): Promise<v
|
|||||||
clean,
|
clean,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
// best-effort
|
return { ok: true };
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
|
console.error("[saveUserPrefsAction]", msg);
|
||||||
|
return { ok: false, error: msg };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user