Feat: added tab dashboard under General Settings (System Setting and Mail Settings). Improved the Setting API to dynamically use the baseURL.

This commit is contained in:
Tola Leng
2025-05-26 16:51:05 +08:00
parent e74bf35264
commit 7ccd413973
8 changed files with 873 additions and 172 deletions
+91 -47
View File
@@ -1,69 +1,113 @@
import { pb, getCurrentEndpoint } from '@/lib/pocketbase';
import { settingsService } from "@/services/settingsService";
const settingsApi = async (body: any) => { const settingsApi = async (body: any) => {
try { try {
const { action, data } = body; const { action, data } = body;
console.log('Settings API called with action:', action, 'data:', data);
const authToken = pb.authStore.token;
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (authToken) {
headers['Authorization'] = `Bearer ${authToken}`;
}
const baseUrl = getCurrentEndpoint();
switch (action) { switch (action) {
case 'getSettings': case 'getSettings':
const settings = await settingsService.getGeneralSettings(); try {
return { const response = await fetch(`${baseUrl}/api/settings`, {
status: 200, method: 'GET',
json: { headers,
success: true, });
data: settings
} if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
};
const settings = await response.json();
case 'updateSettings':
if (!data.id) {
return { return {
status: 400, status: 200,
json: { json: { success: true, data: settings },
success: false, };
message: 'Settings ID is required' } catch (error) {
} console.error('Error fetching settings:', error);
return {
status: 500,
json: { success: false, message: 'Failed to fetch settings' },
}; };
} }
const updatedSettings = await settingsService.updateGeneralSettings(data.id, data); case 'updateSettings':
return { try {
status: 200, let response = await fetch(`${baseUrl}/api/settings`, {
json: { method: 'PATCH',
success: true, headers,
data: updatedSettings body: JSON.stringify(data),
});
if (!response.ok && (response.status === 404 || response.status === 405)) {
response = await fetch(`${baseUrl}/api/settings`, {
method: 'POST',
headers,
body: JSON.stringify(data),
});
} }
};
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const updatedSettings = await response.json();
return {
status: 200,
json: { success: true, data: updatedSettings },
};
} catch (error) {
console.error('Error updating settings:', error);
return {
status: 500,
json: { success: false, message: 'Failed to update settings' },
};
}
case 'testEmailConnection': case 'testEmailConnection':
// This would typically connect to the SMTP server to test the connection try {
// For now, we'll just simulate a successful connection const response = await fetch(`${baseUrl}/api/settings/test-email`, {
return { method: 'POST',
status: 200, headers,
json: { body: JSON.stringify(data),
success: true, });
message: 'Connection successful'
} if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
};
const result = await response.json();
return {
status: 200,
json: {
success: result.success || false,
message:
result.message || (result.success ? 'Connection successful' : 'Connection failed'),
},
};
} catch (error) {
console.error('Error testing email connection:', error);
return {
status: 500,
json: { success: false, message: 'Failed to test email connection' },
};
}
default: default:
return { return {
status: 400, status: 400,
json: { json: { success: false, message: 'Invalid action' },
success: false,
message: 'Invalid action'
}
}; };
} }
} catch (error) { } catch (error) {
console.error('Error in settings API:', error); console.error('Unexpected error in settingsApi:', error);
return { return {
status: 500, status: 500,
json: { json: { success: false, message: 'Internal server error' },
success: false,
message: 'Internal server error'
}
}; };
} }
}; };
@@ -1,105 +1,4 @@
import React, { useState, useEffect } from 'react'; import GeneralSettingsPanel from './general/GeneralSettingsPanel';
import { useQuery } from '@tanstack/react-query';
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Settings } from "lucide-react";
import { settingsService, type GeneralSettings } from "@/services/settingsService";
import { useToast } from "@/hooks/use-toast";
const GeneralSettingsPanel = () => { export default GeneralSettingsPanel;
const { toast } = useToast();
const [formData, setFormData] = useState<Partial<GeneralSettings>>({});
const [isEditing, setIsEditing] = useState(false);
const { data: settings, isLoading, error, refetch } = useQuery({
queryKey: ['generalSettings'],
queryFn: settingsService.getGeneralSettings,
});
useEffect(() => {
if (settings) {
setFormData(settings);
}
}, [settings]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSave = async () => {
if (!settings?.id || !formData) return;
try {
const result = await settingsService.updateGeneralSettings(settings.id, formData);
if (result) {
toast({
title: "Settings updated",
description: "Your settings have been updated successfully.",
variant: "default",
});
refetch();
setIsEditing(false);
}
} catch (error) {
console.error("Error updating settings:", error);
toast({
title: "Update failed",
description: "There was a problem updating your settings.",
variant: "destructive",
});
}
};
if (isLoading) {
return <div className="p-4">Loading settings...</div>;
}
if (error) {
return <div className="p-4 text-red-500">Error loading settings</div>;
}
return (
<div className="p-4">
<Card>
<CardHeader>
<CardTitle>System Settings</CardTitle>
<CardDescription>
Configure your system settings and preferences
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="system_name">System Name</Label>
<Input
id="system_name"
name="system_name"
value={formData?.system_name || ''}
onChange={handleChange}
disabled={!isEditing}
placeholder="My Monitoring System"
/>
</div>
</CardContent>
<CardFooter className="flex justify-between">
{isEditing ? (
<>
<Button variant="outline" onClick={() => setIsEditing(false)}>Cancel</Button>
<Button onClick={handleSave}>Save Changes</Button>
</>
) : (
<Button onClick={() => setIsEditing(true)}>Edit Settings</Button>
)}
</CardFooter>
</Card>
</div>
);
};
export default GeneralSettingsPanel;
@@ -0,0 +1,216 @@
import React, { useState, useEffect } from 'react';
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Settings, Mail } from "lucide-react";
import { Form } from "@/components/ui/form";
import { useForm } from "react-hook-form";
import { useLanguage } from "@/contexts/LanguageContext";
import { useSystemSettings } from "@/hooks/useSystemSettings";
import { GeneralSettings } from "@/services/settingsService";
import SystemSettingsTab from './SystemSettingsTab';
import MailSettingsTab from './MailSettingsTab';
import { GeneralSettingsPanelProps } from './types';
const GeneralSettingsPanel: React.FC<GeneralSettingsPanelProps> = () => {
const { t } = useLanguage();
const [isEditing, setIsEditing] = useState(false);
const [activeTab, setActiveTab] = useState("system");
const {
settings,
isLoading,
error,
updateSettings,
isUpdating,
testEmailConnection,
isTestingConnection
} = useSystemSettings();
const form = useForm<GeneralSettings>({
defaultValues: {
meta: {
appName: '',
appURL: '',
senderName: '',
senderAddress: '',
hideControls: false
},
smtp: {
enabled: false,
port: 587,
host: '',
username: '',
authMethod: '',
tls: true,
localName: ''
}
}
});
useEffect(() => {
if (settings) {
// Initialize form with existing settings, using system_name for appName if meta.appName is not set
const appName = settings.meta?.appName || settings.system_name || '';
form.reset({
...settings,
meta: {
appName: appName,
appURL: settings.meta?.appURL || '',
senderName: settings.meta?.senderName || '',
senderAddress: settings.meta?.senderAddress || '',
hideControls: settings.meta?.hideControls || false
},
smtp: settings.smtp || {
enabled: false,
port: 587,
host: '',
username: '',
authMethod: '',
tls: true,
localName: ''
}
});
}
}, [settings, form]);
const handleSave = async (formData: GeneralSettings) => {
try {
// Prepare data for PocketBase settings update (no ID needed)
const dataToSave = {
...formData,
system_name: formData.meta?.appName || settings?.system_name
};
console.log('Saving settings data:', dataToSave);
await updateSettings(dataToSave);
setIsEditing(false);
} catch (error) {
console.error("Error updating settings:", error);
}
};
const handleTestConnection = async () => {
try {
const smtpConfig = form.getValues('smtp');
await testEmailConnection(smtpConfig);
} catch (error) {
console.error("Error testing connection:", error);
}
};
const handleEditClick = () => {
console.log('Edit button clicked, setting isEditing to true');
setIsEditing(true);
};
const handleCancelClick = () => {
console.log('Cancel button clicked, setting isEditing to false');
setIsEditing(false);
// Reset form to original values
if (settings) {
const appName = settings.meta?.appName || settings.system_name || '';
form.reset({
...settings,
meta: {
appName: appName,
appURL: settings.meta?.appURL || '',
senderName: settings.meta?.senderName || '',
senderAddress: settings.meta?.senderAddress || '',
hideControls: settings.meta?.hideControls || false
},
smtp: settings.smtp || {
enabled: false,
port: 587,
host: '',
username: '',
authMethod: '',
tls: true,
localName: ''
}
});
}
};
if (isLoading) {
return <div className="p-4">Loading settings...</div>;
}
if (error) {
return <div className="p-4 text-red-500">Error loading settings</div>;
}
return (
<div className="p-4">
<Card>
<CardHeader>
<CardTitle>{t("generalSettings", "menu")}</CardTitle>
<CardDescription>
{t("monitorSSLCertificates", "ssl")}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSave)}>
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="w-full mb-4">
<TabsTrigger value="system" className="flex items-center gap-2 flex-1">
<Settings className="h-4 w-4" />
{t("systemSettings", "settings")}
</TabsTrigger>
<TabsTrigger value="mail" className="flex items-center gap-2 flex-1">
<Mail className="h-4 w-4" />
{t("mailSettings", "settings")}
</TabsTrigger>
</TabsList>
<TabsContent value="system">
<SystemSettingsTab
form={form}
isEditing={isEditing}
settings={settings}
/>
</TabsContent>
<TabsContent value="mail">
<MailSettingsTab
form={form}
isEditing={isEditing}
settings={settings}
handleTestConnection={handleTestConnection}
isTestingConnection={isTestingConnection}
/>
</TabsContent>
</Tabs>
{/* Save and Cancel buttons - only show when editing */}
{isEditing && (
<div className="flex justify-between mt-6">
<Button type="button" variant="outline" onClick={handleCancelClick} disabled={isUpdating}>
{t("cancel", "common")}
</Button>
<Button type="submit" disabled={isUpdating}>
{isUpdating ? t("saving", "settings") : t("save", "settings")}
</Button>
</div>
)}
</form>
</Form>
</CardContent>
{/* Edit button - only show when not editing and outside the form */}
{!isEditing && (
<CardFooter>
<Button type="button" onClick={handleEditClick}>
{t("edit", "common")}
</Button>
</CardFooter>
)}
</Card>
</div>
);
};
export default GeneralSettingsPanel;
@@ -0,0 +1,217 @@
import React from 'react';
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Button } from "@/components/ui/button";
import { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form";
import { useLanguage } from "@/contexts/LanguageContext";
import { SettingsTabProps } from "./types";
interface MailSettingsTabProps extends SettingsTabProps {
handleTestConnection: () => Promise<void>;
isTestingConnection: boolean;
}
const MailSettingsTab: React.FC<MailSettingsTabProps> = ({
form,
isEditing,
handleTestConnection,
isTestingConnection
}) => {
const { t } = useLanguage();
return (
<div className="space-y-4">
<div>
<FormField
control={form.control}
name="meta.senderName"
render={({ field }) => (
<FormItem>
<FormLabel>{t("senderName", "settings")}</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing}
placeholder="Support"
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div>
<FormField
control={form.control}
name="meta.senderAddress"
render={({ field }) => (
<FormItem>
<FormLabel>{t("senderEmail", "settings")}</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing}
placeholder="support@example.com"
type="email"
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="flex items-center space-x-2 mb-4">
<FormField
control={form.control}
name="smtp.enabled"
render={({ field }) => (
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
disabled={!isEditing}
/>
</FormControl>
<FormLabel className="mt-0">{t("smtpEnabled", "settings")}</FormLabel>
</FormItem>
)}
/>
</div>
<div className={!form.watch('smtp.enabled') ? 'opacity-50' : ''}>
<div>
<FormField
control={form.control}
name="smtp.host"
render={({ field }) => (
<FormItem>
<FormLabel>{t("smtpHost", "settings")}</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing || !form.watch('smtp.enabled')}
placeholder="smtp.example.com"
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="mt-4">
<FormField
control={form.control}
name="smtp.port"
render={({ field }) => (
<FormItem>
<FormLabel>{t("smtpPort", "settings")}</FormLabel>
<FormControl>
<Input
type="number"
{...field}
onChange={e => field.onChange(parseInt(e.target.value) || 587)}
disabled={!isEditing || !form.watch('smtp.enabled')}
placeholder="587"
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="mt-4">
<FormField
control={form.control}
name="smtp.username"
render={({ field }) => (
<FormItem>
<FormLabel>{t("smtpUsername", "settings")}</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing || !form.watch('smtp.enabled')}
placeholder="user@example.com"
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="mt-4">
<FormField
control={form.control}
name="smtp.authMethod"
render={({ field }) => (
<FormItem>
<FormLabel>{t("smtpAuthMethod", "settings")}</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing || !form.watch('smtp.enabled')}
placeholder="Login"
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="flex items-center space-x-2 mt-4">
<FormField
control={form.control}
name="smtp.tls"
render={({ field }) => (
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
disabled={!isEditing || !form.watch('smtp.enabled')}
/>
</FormControl>
<FormLabel className="mt-0">{t("enableTLS", "settings")}</FormLabel>
</FormItem>
)}
/>
</div>
<div className="mt-4">
<FormField
control={form.control}
name="smtp.localName"
render={({ field }) => (
<FormItem>
<FormLabel>{t("localName", "settings")}</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing || !form.watch('smtp.enabled')}
placeholder="localhost"
/>
</FormControl>
</FormItem>
)}
/>
</div>
{isEditing && form.watch('smtp.enabled') && (
<div className="mt-4">
<Button
type="button"
variant="outline"
onClick={handleTestConnection}
disabled={isTestingConnection}
>
{isTestingConnection ? t("testingConnection", "settings") : t("testConnection", "settings")}
</Button>
</div>
)}
</div>
</div>
);
};
export default MailSettingsTab;
@@ -0,0 +1,130 @@
import React from 'react';
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form";
import { useLanguage } from "@/contexts/LanguageContext";
import { SettingsTabProps } from "./types";
const SystemSettingsTab: React.FC<SettingsTabProps> = ({ form, isEditing, settings }) => {
const { t } = useLanguage();
return (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<FormField
control={form.control}
name="meta.appName"
render={({ field }) => (
<FormItem>
<FormLabel className="text-sm font-medium text-foreground">
{t("appName", "settings")} <span className="text-destructive">*</span>
</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing}
placeholder="CheckCle"
className="bg-background border-input text-foreground placeholder:text-muted-foreground disabled:bg-muted disabled:text-muted-foreground"
value={field.value || settings?.system_name || ''}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div>
<FormField
control={form.control}
name="meta.appURL"
render={({ field }) => (
<FormItem>
<FormLabel className="text-sm font-medium text-foreground">
{t("appURL", "settings")} <span className="text-destructive">*</span>
</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing}
placeholder="https://pb-api.k8sops.asia"
className="bg-background border-input text-foreground placeholder:text-muted-foreground disabled:bg-muted disabled:text-muted-foreground"
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
<div>
<FormField
control={form.control}
name="meta.senderName"
render={({ field }) => (
<FormItem>
<FormLabel className="text-sm font-medium text-foreground">
{t("senderName", "settings")}
</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing}
placeholder="System Administrator"
className="bg-background border-input text-foreground placeholder:text-muted-foreground disabled:bg-muted disabled:text-muted-foreground"
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div>
<FormField
control={form.control}
name="meta.senderAddress"
render={({ field }) => (
<FormItem>
<FormLabel className="text-sm font-medium text-foreground">
{t("senderEmail", "settings")}
</FormLabel>
<FormControl>
<Input
{...field}
disabled={!isEditing}
placeholder="admin@example.com"
type="email"
className="bg-background border-input text-foreground placeholder:text-muted-foreground disabled:bg-muted disabled:text-muted-foreground"
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="flex items-center space-x-3 pt-4">
<FormField
control={form.control}
name="meta.hideControls"
render={({ field }) => (
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
disabled={!isEditing}
/>
</FormControl>
<FormLabel className="text-sm font-medium text-foreground cursor-pointer">
{t("hideControls", "settings")}
</FormLabel>
</FormItem>
)}
/>
</div>
</div>
);
};
export default SystemSettingsTab;
@@ -0,0 +1,12 @@
import { GeneralSettings } from "@/services/settingsService";
export interface SettingsTabProps {
form: any;
isEditing: boolean;
settings?: GeneralSettings;
}
export interface GeneralSettingsPanelProps {
// No props needed for now, but we can add them in the future
}
+142 -5
View File
@@ -1,8 +1,20 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { settingsService } from "@/services/settingsService"; import { toast } from "@/components/ui/use-toast";
import { useLanguage } from "@/contexts/LanguageContext";
import { GeneralSettings } from "@/services/settingsService";
interface ApiResponse {
success: boolean;
data?: GeneralSettings;
message?: string;
}
export function useSystemSettings() { export function useSystemSettings() {
const queryClient = useQueryClient();
const { t } = useLanguage();
// Fetch settings from API
const { const {
data: settings, data: settings,
isLoading, isLoading,
@@ -10,7 +22,128 @@ export function useSystemSettings() {
refetch refetch
} = useQuery({ } = useQuery({
queryKey: ['generalSettings'], queryKey: ['generalSettings'],
queryFn: settingsService.getGeneralSettings, queryFn: async (): Promise<GeneralSettings | null> => {
try {
console.log('Fetching settings from API...');
const response = await fetch('/api/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'getSettings' })
});
console.log('API response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result: ApiResponse = await response.json();
console.log('API response data:', result);
if (!result.success) {
throw new Error(result.message || 'Failed to fetch settings');
}
return result.data || null;
} catch (error) {
console.error('Error fetching settings:', error);
toast({
title: t("errorFetchingSettings", "settings"),
description: error instanceof Error ? error.message : String(error),
variant: "destructive",
});
return null;
}
}
});
// Update settings mutation
const updateSettingsMutation = useMutation({
mutationFn: async (updatedSettings: GeneralSettings): Promise<GeneralSettings> => {
console.log('Updating settings:', updatedSettings);
const response = await fetch('/api/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'updateSettings',
data: updatedSettings
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result: ApiResponse = await response.json();
if (!result.success || !result.data) {
throw new Error(result.message || 'Failed to update settings');
}
return result.data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['generalSettings'] });
toast({
title: t("settingsUpdated", "settings"),
description: "",
variant: "default",
});
},
onError: (error) => {
console.error('Error updating settings:', error);
toast({
title: t("errorSavingSettings", "settings"),
description: error instanceof Error ? error.message : String(error),
variant: "destructive",
});
}
});
// Test email connection
const testEmailConnectionMutation = useMutation({
mutationFn: async (smtpConfig: any): Promise<{success: boolean, message: string}> => {
console.log('Testing email connection:', smtpConfig);
const response = await fetch('/api/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'testEmailConnection',
data: smtpConfig
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
return {
success: result.success,
message: result.message || ''
};
},
onSuccess: (result) => {
toast({
title: result.success ? t("connectionSuccess", "settings") : t("connectionFailed", "settings"),
description: result.message,
variant: result.success ? "default" : "destructive",
});
},
onError: (error) => {
console.error('Error testing connection:', error);
toast({
title: t("connectionFailed", "settings"),
description: error instanceof Error ? error.message : String(error),
variant: "destructive",
});
}
}); });
return { return {
@@ -18,6 +151,10 @@ export function useSystemSettings() {
isLoading, isLoading,
error, error,
refetch, refetch,
systemName: settings?.system_name || 'ReamStack', updateSettings: updateSettingsMutation.mutate,
isUpdating: updateSettingsMutation.isPending,
testEmailConnection: testEmailConnectionMutation.mutate,
isTestingConnection: testEmailConnectionMutation.isPending,
systemName: settings?.system_name || settings?.meta?.appName || 'ReamStack',
}; };
} }
+63 -17
View File
@@ -1,11 +1,9 @@
import { pb } from "@/lib/pocketbase";
export interface GeneralSettings { export interface GeneralSettings {
id: string; id?: string;
created: string; created?: string;
updated: string; updated?: string;
system_name: string; system_name?: string;
system_name_kh?: string; system_name_kh?: string;
logo_url?: string; logo_url?: string;
system_description?: string; system_description?: string;
@@ -47,22 +45,55 @@ export interface GeneralSettings {
export const settingsService = { export const settingsService = {
async getGeneralSettings(): Promise<GeneralSettings | null> { async getGeneralSettings(): Promise<GeneralSettings | null> {
try { try {
const result = await pb.collection('general_settings').getList(1, 1); console.log('Fetching settings from /api/settings endpoint...');
if (result.items.length > 0) { const response = await fetch('/api/settings', {
// Type cast to GeneralSettings to resolve the type mismatch method: 'POST',
return result.items[0] as unknown as GeneralSettings; headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ action: 'getSettings' })
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
} }
return null;
const result = await response.json();
console.log('Settings API response:', result);
return result.success ? result.data : null;
} catch (error) { } catch (error) {
console.error("Failed to fetch general settings:", error); console.error("Failed to fetch general settings:", error);
return null; return null;
} }
}, },
async updateGeneralSettings(id: string, data: Partial<GeneralSettings>): Promise<GeneralSettings | null> { async updateGeneralSettings(data: Partial<GeneralSettings>): Promise<GeneralSettings | null> {
try { try {
// Type cast to GeneralSettings to resolve the type mismatch console.log('Updating settings via /api/settings:', data);
return await pb.collection('general_settings').update(id, data) as unknown as GeneralSettings;
// Remove id and timestamp fields for settings update
const { id, created, updated, ...updateData } = data;
const response = await fetch('/api/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'updateSettings',
data: updateData
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
console.log('Settings update response:', result);
return result.success ? result.data : null;
} catch (error) { } catch (error) {
console.error("Failed to update general settings:", error); console.error("Failed to update general settings:", error);
return null; return null;
@@ -71,9 +102,24 @@ export const settingsService = {
async testEmailConnection(smtpConfig: any): Promise<boolean> { async testEmailConnection(smtpConfig: any): Promise<boolean> {
try { try {
// In a real application, we would send a test email here console.log('Testing email connection via /api/settings:', smtpConfig);
// For now, let's simulate a successful connection if the host is provided const response = await fetch('/api/settings', {
return !!smtpConfig.host; method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'testEmailConnection',
data: smtpConfig
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
return result.success || false;
} catch (error) { } catch (error) {
console.error("Failed to test email connection:", error); console.error("Failed to test email connection:", error);
return false; return false;