feat(pref): better form for Komga conf

This commit is contained in:
Julien Froidefond
2025-02-24 21:34:13 +01:00
parent 4c12356225
commit e80c26136a
3 changed files with 146 additions and 91 deletions

View File

@@ -1,14 +1,14 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { TestService } from "@/lib/services/test.service"; import { TestService } from "@/lib/services/test.service";
import { ConfigDBService } from "@/lib/services/config-db.service";
export async function POST() { export async function POST(request: Request) {
try { try {
const config = await ConfigDBService.getConfig(); const { serverUrl, username, password } = await request.json();
const authHeader = Buffer.from(`${username}:${password}`).toString("base64");
const { libraries } = await TestService.testConnection({ const { libraries } = await TestService.testConnection({
serverUrl: config.url, serverUrl,
authHeader: config.authHeader, authHeader,
}); });
return NextResponse.json({ return NextResponse.json({

View File

@@ -19,6 +19,7 @@ export default async function SettingsPage() {
url: mongoConfig.url, url: mongoConfig.url,
username: mongoConfig.username, username: mongoConfig.username,
userId: mongoConfig.userId, userId: mongoConfig.userId,
authHeader: mongoConfig.authHeader,
}; };
} }

View File

@@ -15,6 +15,7 @@ interface KomgaConfig {
username: string; username: string;
userId: string; userId: string;
password?: string; password?: string;
authHeader: string;
} }
interface TTLConfigData { interface TTLConfigData {
@@ -43,7 +44,10 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
serverUrl: initialConfig?.url || "", serverUrl: initialConfig?.url || "",
username: initialConfig?.username || "", username: initialConfig?.username || "",
password: initialConfig?.password || "", password: initialConfig?.password || "",
authHeader: initialConfig?.authHeader || "",
}); });
const [isEditingConfig, setIsEditingConfig] = useState(false);
const [localInitialConfig, setLocalInitialConfig] = useState(initialConfig);
const [ttlConfig, setTTLConfig] = useState<TTLConfigData>( const [ttlConfig, setTTLConfig] = useState<TTLConfigData>(
initialTTLConfig || { initialTTLConfig || {
defaultTTL: 5, defaultTTL: 5,
@@ -56,6 +60,10 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
); );
const { preferences, updatePreferences } = usePreferences(); const { preferences, updatePreferences } = usePreferences();
const hasToShowEditForm =
localInitialConfig && config.serverUrl !== null && config.username !== null;
const shouldShowForm = !hasToShowEditForm || isEditingConfig;
const handleClearCache = async () => { const handleClearCache = async () => {
setIsCacheClearing(true); setIsCacheClearing(true);
setError(null); setError(null);
@@ -93,6 +101,12 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
setError(null); setError(null);
setSuccess(null); setSuccess(null);
const form = document.querySelector("form") as HTMLFormElement;
const formData = new FormData(form);
const serverUrl = formData.get("serverUrl") as string;
const username = formData.get("username") as string;
const password = formData.get("password") as string;
try { try {
const response = await fetch("/api/komga/test", { const response = await fetch("/api/komga/test", {
method: "POST", method: "POST",
@@ -100,9 +114,9 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
serverUrl: config.serverUrl, serverUrl: serverUrl.trim(),
username: config.username, username,
password: config.password, password: password || config.password,
}), }),
}); });
@@ -142,6 +156,7 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
serverUrl: serverUrl.trim(), serverUrl: serverUrl.trim(),
username, username,
password, password,
authHeader: config.authHeader,
}; };
try { try {
@@ -162,19 +177,16 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
throw new Error(data.error || "Erreur lors de la sauvegarde de la configuration"); throw new Error(data.error || "Erreur lors de la sauvegarde de la configuration");
} }
const komgaConfig = { const savedConfig = await response.json();
serverUrl: newConfig.serverUrl,
credentials: {
username: newConfig.username,
password: newConfig.password,
},
};
setConfig(newConfig); setConfig(newConfig);
setLocalInitialConfig({
// Émettre un événement pour notifier les autres composants url: newConfig.serverUrl,
const configChangeEvent = new CustomEvent("komga-config-changed", { detail: komgaConfig }); username: newConfig.username,
window.dispatchEvent(configChangeEvent); userId: savedConfig.userId,
authHeader: savedConfig.authHeader,
});
setIsEditingConfig(false);
toast({ toast({
title: "Configuration sauvegardée", title: "Configuration sauvegardée",
@@ -389,84 +401,126 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
</p> </p>
</div> </div>
{/* Formulaire de configuration */} {!shouldShowForm ? (
<form onSubmit={handleSave} className="space-y-4"> <div className="space-y-4">
<div className="space-y-3"> <div className="space-y-3">
<div className="space-y-2"> <div className="space-y-2">
<label htmlFor="serverUrl" className="text-sm font-medium"> <label className="text-sm font-medium">L&apos;URL du serveur</label>
L&apos;URL du serveur <p className="text-sm text-muted-foreground">{config.serverUrl}</p>
</label> </div>
<input <div className="space-y-2">
type="url" <label className="text-sm font-medium">L&apos;adresse email de connexion</label>
id="serverUrl" <p className="text-sm text-muted-foreground">{config.username}</p>
name="serverUrl" </div>
required <div className="space-y-2">
value={config.serverUrl} <label className="text-sm font-medium">Mot de passe</label>
onChange={handleInputChange} <p className="text-sm text-muted-foreground"></p>
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" </div>
/>
</div> </div>
<div className="space-y-2">
<label htmlFor="username" className="text-sm font-medium">
L&apos;adresse email de connexion
</label>
<input
type="text"
id="username"
name="username"
required
value={config.username}
onChange={handleInputChange}
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
<div className="space-y-2">
<label htmlFor="password" className="text-sm font-medium">
Mot de passe
</label>
<input
type="password"
id="password"
name="password"
required
value={config.password}
onChange={handleInputChange}
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
</div>
<div className="flex gap-3">
<button
type="submit"
disabled={isSaving}
className="flex-1 inline-flex items-center justify-center rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
{isSaving ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Sauvegarde...
</>
) : (
"Sauvegarder"
)}
</button>
<button <button
type="button" type="button"
onClick={handleTest} onClick={() => setIsEditingConfig(true)}
disabled={isLoading} className="inline-flex items-center justify-center rounded-md bg-secondary px-3 py-2 text-sm font-medium text-secondary-foreground ring-offset-background transition-colors hover:bg-secondary/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
className="flex-1 inline-flex items-center justify-center rounded-md bg-secondary px-3 py-2 text-sm font-medium text-secondary-foreground ring-offset-background transition-colors hover:bg-secondary/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
> >
{isLoading ? ( Modifier la configuration
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Test en cours...
</>
) : (
"Tester la connexion"
)}
</button> </button>
</div> </div>
</form> ) : (
<form onSubmit={handleSave} className="space-y-4">
<div className="space-y-3">
<div className="space-y-2">
<label htmlFor="serverUrl" className="text-sm font-medium">
L&apos;URL du serveur
</label>
<input
type="url"
id="serverUrl"
name="serverUrl"
required
value={config.serverUrl}
onChange={handleInputChange}
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
<div className="space-y-2">
<label htmlFor="username" className="text-sm font-medium">
L&apos;adresse email de connexion
</label>
<input
type="text"
id="username"
name="username"
required
value={config.username}
onChange={handleInputChange}
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
<div className="space-y-2">
<label htmlFor="password" className="text-sm font-medium">
Mot de passe
</label>
<input
type="password"
id="password"
name="password"
required
value={config.password}
onChange={handleInputChange}
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
</div>
<div className="flex gap-3">
<button
type="submit"
disabled={isSaving}
className="flex-1 inline-flex items-center justify-center rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
{isSaving ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Sauvegarde...
</>
) : (
"Sauvegarder"
)}
</button>
<button
type="button"
onClick={handleTest}
disabled={isLoading}
className="flex-1 inline-flex items-center justify-center rounded-md bg-secondary px-3 py-2 text-sm font-medium text-secondary-foreground ring-offset-background transition-colors hover:bg-secondary/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
{isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Test en cours...
</>
) : (
"Tester la connexion"
)}
</button>
{initialConfig && (
<button
type="button"
onClick={() => {
setIsEditingConfig(false);
setConfig({
serverUrl: initialConfig.url,
username: initialConfig.username,
password: "",
authHeader: initialConfig.authHeader,
});
}}
className="flex-1 inline-flex items-center justify-center rounded-md bg-secondary px-3 py-2 text-sm font-medium text-secondary-foreground ring-offset-background transition-colors hover:bg-secondary/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
>
Annuler
</button>
)}
</div>
</form>
)}
</div> </div>
</div> </div>