Add ToggleSwitch component and QuillSense settings management

- Added `ToggleSwitch` component for handling toggles with labels, descriptions, and disabled states.
- Introduced `QuillSenseSetting` component for managing QuillSense enablement and advanced prompt settings.
- Integrated `QuillSenseSettings` model and API calls for fetching and updating settings.
This commit is contained in:
natreex
2026-01-13 19:53:35 -05:00
parent 306262caba
commit 707da73551
3 changed files with 193 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
'use client'
import React, {ChangeEvent, forwardRef, useContext, useEffect, useImperativeHandle, useState} from "react";
import {BookContext} from "@/context/BookContext";
import {SessionContext} from "@/context/SessionContext";
import {AlertContext} from "@/context/AlertContext";
import {LangContext} from "@/context/LangContext";
import System from "@/lib/models/System";
import {QuillSenseSettingsProps} from "@/lib/models/QuillSenseSettings";
import {useTranslations} from "next-intl";
import ToggleSwitch from "@/components/form/ToggleSwitch";
import TexteAreaInput from "@/components/form/TexteAreaInput";
import InputField from "@/components/form/InputField";
import {faMagicWandSparkles, faToggleOn} from "@fortawesome/free-solid-svg-icons";
const QuillSenseSetting = forwardRef(function QuillSenseSetting(props, ref) {
const t = useTranslations();
const {book, setBook} = useContext(BookContext);
const {session} = useContext(SessionContext);
const {errorMessage, successMessage} = useContext(AlertContext);
const {lang} = useContext(LangContext);
const [quillsenseEnabled, setQuillsenseEnabled] = useState<boolean>(true);
const [advancedPrompt, setAdvancedPrompt] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(true);
useImperativeHandle(ref, () => ({
handleSave
}));
useEffect((): void => {
if (book?.bookId) {
fetchQuillSenseSettings();
}
}, [book?.bookId]);
async function fetchQuillSenseSettings(): Promise<void> {
try {
setIsLoading(true);
const settings: QuillSenseSettingsProps = await System.authGetQueryToServer<QuillSenseSettingsProps>(
'book/quillsense-settings',
session.accessToken,
lang,
{bookId: book?.bookId}
);
setQuillsenseEnabled(settings.quillsenseEnabled);
setAdvancedPrompt(settings.advancedPrompt ?? '');
} catch (e: unknown) {
if (e instanceof Error) {
errorMessage(e.message);
}
} finally {
setIsLoading(false);
}
}
async function handleSave(): Promise<void> {
try {
const updateResult: boolean = await System.authPutToServer<boolean>(
'book/quillsense-settings',
{
bookId: book?.bookId,
quillsenseEnabled: quillsenseEnabled,
advancedPrompt: advancedPrompt
},
session.accessToken,
lang
);
if (updateResult) {
successMessage(t('quillsenseSetting.saveSuccess'));
// Mettre a jour le contexte du livre
if (setBook && book) {
setBook({...book, quillsenseEnabled: quillsenseEnabled});
}
} else {
errorMessage(t('quillsenseSetting.saveError'));
}
} catch (e: unknown) {
if (e instanceof Error) {
errorMessage(e.message);
}
}
}
if (isLoading) {
return (
<div className="flex items-center justify-center p-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
</div>
);
}
return (
<div className="space-y-6 p-4">
<div className="bg-secondary/20 rounded-xl p-5 shadow-inner border border-secondary/30">
<InputField
icon={faToggleOn}
fieldName={t('quillsenseSetting.enableQuillsense')}
input={
<ToggleSwitch
checked={quillsenseEnabled}
onChange={(checked: boolean): void => setQuillsenseEnabled(checked)}
/>
}
/>
<p className="text-muted text-sm mt-2">
{t('quillsenseSetting.enableDescription')}
</p>
</div>
<div className="bg-secondary/20 rounded-xl p-5 shadow-inner border border-secondary/30">
<InputField
icon={faMagicWandSparkles}
fieldName={t('quillsenseSetting.advancedPrompt')}
input={
<TexteAreaInput
value={advancedPrompt}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setAdvancedPrompt(e.target.value)}
placeholder={t('quillsenseSetting.advancedPromptPlaceholder')}
/>
}
/>
<p className="text-muted text-sm mt-2">
{t('quillsenseSetting.advancedPromptDescription')}
</p>
</div>
</div>
);
});
export default QuillSenseSetting;

View File

@@ -0,0 +1,59 @@
'use client';
import {Dispatch, SetStateAction} from 'react';
interface ToggleSwitchProps {
enabled: boolean;
setEnabled: Dispatch<SetStateAction<boolean>>;
label?: string;
description?: string;
disabled?: boolean;
}
export default function ToggleSwitch({
enabled,
setEnabled,
label,
description,
disabled = false
}: ToggleSwitchProps) {
function handleToggle(): void {
if (!disabled) {
setEnabled(!enabled);
}
}
return (
<div className="flex items-center justify-between">
<div className="flex flex-col">
{label && (
<span className="text-text-primary font-medium">{label}</span>
)}
{description && (
<span className="text-text-secondary text-sm mt-1">{description}</span>
)}
</div>
<button
type="button"
onClick={handleToggle}
disabled={disabled}
className={`
relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
${enabled ? 'bg-primary' : 'bg-secondary'}
${disabled ? 'opacity-50 cursor-not-allowed' : ''}
`}
role="switch"
aria-checked={enabled}
>
<span
className={`
pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0
transition duration-200 ease-in-out
${enabled ? 'translate-x-5' : 'translate-x-0'}
`}
/>
</button>
</div>
);
}

View File

@@ -0,0 +1,4 @@
export interface QuillSenseSettingsProps {
quillsenseEnabled: boolean;
advancedPrompt: string | null;
}