Files
ERitors-Scribe-Desktop/components/book/settings/guideline/GuideLineSetting.tsx
natreex d4765e6576 Add foundational components and logic for migration, UI design, and input handling
- Introduced foundational UI components (`Badge`, `LockCard`, `SectionHeader`, `AvatarIcon`, etc.) for flexible layouts and consistent design.
- Added migration support with the `MigrationModal` component and backend integration for exporting/importing data between Electron and Tauri.
- Extended form components with `TextAreaInput`, `OrderInput`, `ToggleField`, and `ToolbarSelect` for improved input handling.
- Updated `ScribeShell` with migration popup logic to prompt users for data migration.
- Integrated `AlertStack` for better alert handling and notification management.
- Enhanced Rust/Tauri services with migration command implementations.
- Added translations and styles for new components.
2026-04-05 12:52:54 -04:00

208 lines
9.3 KiB
TypeScript

'use client'
import React, {ChangeEvent, forwardRef, useContext, useEffect, useImperativeHandle, useState} from 'react';
import {apiGet, apiPost} from '@/lib/api/client';
import {isDesktop} from '@/lib/configs';
import * as tauri from '@/lib/tauri';
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
import {BookContext, BookContextProps} from '@/context/BookContext';
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
import {GuideLine} from "@/lib/types/book";
import TextAreaInput from "@/components/form/TextAreaInput";
import InputField from "@/components/form/InputField";
import {useTranslations} from '@/lib/i18n';
import {LangContext, LangContextProps} from "@/context/LangContext";
import {SettingRef} from "@/lib/types/settings";
function GuideLineSetting(_props: object, ref: React.ForwardedRef<SettingRef>): React.JSX.Element {
const t = useTranslations();
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext);
const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
const userToken: string = session?.accessToken ? session?.accessToken : '';
const {errorMessage, successMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
const bookId: string = book?.bookId ?? '';
const [tone, setTone] = useState<string>('');
const [atmosphere, setAtmosphere] = useState<string>('');
const [writingStyle, setWritingStyle] = useState<string>('');
const [themes, setThemes] = useState<string>('');
const [symbolism, setSymbolism] = useState<string>('');
const [motifs, setMotifs] = useState<string>('');
const [narrativeVoice, setNarrativeVoice] = useState<string>('');
const [pacing, setPacing] = useState<string>('');
const [intendedAudience, setIntendedAudience] = useState<string>('');
const [keyMessages, setKeyMessages] = useState<string>('');
useEffect((): void => {
getGuideLine().then();
}, []);
useImperativeHandle(ref, (): SettingRef => ({
handleSave: savePersonal
}));
async function getGuideLine(): Promise<void> {
try {
let response: GuideLine;
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
response = await tauri.getGuideLine(bookId);
} else {
response = await apiGet<GuideLine>(
`book/guide-line`,
userToken,
lang,
{id: bookId},
);
}
if (response) {
setTone(response.tone);
setAtmosphere(response.atmosphere);
setWritingStyle(response.writingStyle);
setThemes(response.themes);
setSymbolism(response.symbolism);
setMotifs(response.motifs);
setNarrativeVoice(response.narrativeVoice);
setPacing(response.pacing);
setIntendedAudience(response.intendedAudience);
setKeyMessages(response.keyMessages);
}
} catch (error: unknown) {
if (error instanceof Error) {
errorMessage(error.message);
} else {
errorMessage(t("guideLineSetting.errorUnknown"));
}
}
}
async function savePersonal(): Promise<void> {
try {
let response: boolean;
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
response = await tauri.updateGuideLine({
bookId: bookId,
tone: tone,
atmosphere: atmosphere,
writingStyle: writingStyle,
themes: themes,
symbolism: symbolism,
motifs: motifs,
narrativeVoice: narrativeVoice,
pacing: pacing,
intendedAudience: intendedAudience,
keyMessages: keyMessages,
});
} else {
response = await apiPost<boolean>(
'book/guide-line',
{
bookId: bookId,
tone: tone,
atmosphere: atmosphere,
writingStyle: writingStyle,
themes: themes,
symbolism: symbolism,
motifs: motifs,
narrativeVoice: narrativeVoice,
pacing: pacing,
intendedAudience: intendedAudience,
keyMessages: keyMessages,
},
userToken,
lang,
);
}
if (!response) {
errorMessage(t("guideLineSetting.saveError"));
return;
}
successMessage(t("guideLineSetting.saveSuccess"));
} catch (error: unknown) {
if (error instanceof Error) {
errorMessage(error.message);
} else {
errorMessage(t("guideLineSetting.errorUnknown"));
}
}
}
return (
<div className="space-y-4">
<InputField fieldName={t("guideLineSetting.tone")} input={
<TextAreaInput
value={tone}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setTone(e.target.value)}
placeholder={t("guideLineSetting.tonePlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.atmosphere")} input={
<TextAreaInput
value={atmosphere}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setAtmosphere(e.target.value)}
placeholder={t("guideLineSetting.atmospherePlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.writingStyle")} input={
<TextAreaInput
value={writingStyle}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setWritingStyle(e.target.value)}
placeholder={t("guideLineSetting.writingStylePlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.themes")} input={
<TextAreaInput
value={themes}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setThemes(e.target.value)}
placeholder={t("guideLineSetting.themesPlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.symbolism")} input={
<TextAreaInput
value={symbolism}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setSymbolism(e.target.value)}
placeholder={t("guideLineSetting.symbolismPlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.motifs")} input={
<TextAreaInput
value={motifs}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setMotifs(e.target.value)}
placeholder={t("guideLineSetting.motifsPlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.narrativeVoice")} input={
<TextAreaInput
value={narrativeVoice}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setNarrativeVoice(e.target.value)}
placeholder={t("guideLineSetting.narrativeVoicePlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.pacing")} input={
<TextAreaInput
value={pacing}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setPacing(e.target.value)}
placeholder={t("guideLineSetting.pacingPlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.intendedAudience")} input={
<TextAreaInput
value={intendedAudience}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setIntendedAudience(e.target.value)}
placeholder={t("guideLineSetting.intendedAudiencePlaceholder")}
/>
}/>
<InputField fieldName={t("guideLineSetting.keyMessages")} input={
<TextAreaInput
value={keyMessages}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setKeyMessages(e.target.value)}
placeholder={t("guideLineSetting.keyMessagesPlaceholder")}
/>
}/>
</div>
);
}
export default forwardRef<SettingRef, object>(GuideLineSetting);