- Removed unnecessary React imports. - Adjusted package.json scripts for Electron integration. - Updated components to replace Next.js-specific imports with Electron-compatible alternatives. - Minor tsconfig.json changes for better compatibility.
203 lines
12 KiB
TypeScript
203 lines
12 KiB
TypeScript
import {useState} from 'react';
|
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
|
import {faChevronDown, faChevronUp, faPlus, faTrash} from '@fortawesome/free-solid-svg-icons';
|
|
import {Incident, PlotPoint} from '@/lib/models/Book';
|
|
import {ActChapter, ChapterListProps} from '@/lib/models/Chapter';
|
|
import {SelectBoxProps} from '@/shared/interface';
|
|
import ActChapterItem from './ActChapter';
|
|
import InputField from '@/components/form/InputField';
|
|
import SelectBox from '@/components/form/SelectBox';
|
|
import {useTranslations} from 'next-intl';
|
|
|
|
interface ActPlotPointsProps {
|
|
plotPoints: PlotPoint[];
|
|
incidents: Incident[];
|
|
actId: number;
|
|
mainChapters: ChapterListProps[];
|
|
newPlotPointTitle: string;
|
|
setNewPlotPointTitle: (title: string) => void;
|
|
selectedIncidentId: string;
|
|
setSelectedIncidentId: (id: string) => void;
|
|
onAddPlotPoint: (actId: number) => Promise<void>;
|
|
onDeletePlotPoint: (actId: number, plotPointId: string) => Promise<void>;
|
|
onLinkChapter: (actId: number, chapterId: string, plotPointId: string) => Promise<void>;
|
|
onUpdateChapterSummary: (chapterId: string, summary: string, plotPointId: string) => void;
|
|
onUnlinkChapter: (chapterInfoId: string, chapterId: string, plotPointId: string) => Promise<void>;
|
|
sectionKey: string;
|
|
isExpanded: boolean;
|
|
onToggleSection: (sectionKey: string) => void;
|
|
}
|
|
|
|
export default function ActPlotPoints({
|
|
plotPoints,
|
|
incidents,
|
|
actId,
|
|
mainChapters,
|
|
newPlotPointTitle,
|
|
setNewPlotPointTitle,
|
|
selectedIncidentId,
|
|
setSelectedIncidentId,
|
|
onAddPlotPoint,
|
|
onDeletePlotPoint,
|
|
onLinkChapter,
|
|
onUpdateChapterSummary,
|
|
onUnlinkChapter,
|
|
sectionKey,
|
|
isExpanded,
|
|
onToggleSection,
|
|
}: ActPlotPointsProps) {
|
|
const t = useTranslations('actComponent');
|
|
const [expandedItems, setExpandedItems] = useState<{ [key: string]: boolean }>({});
|
|
const [selectedChapterId, setSelectedChapterId] = useState<string>('');
|
|
|
|
function toggleItem(itemKey: string): void {
|
|
setExpandedItems(prev => ({
|
|
...prev,
|
|
[itemKey]: !prev[itemKey],
|
|
}));
|
|
}
|
|
|
|
function getIncidentData(): SelectBoxProps[] {
|
|
return incidents.map((incident: Incident): SelectBoxProps => ({
|
|
value: incident.incidentId,
|
|
label: incident.title,
|
|
}));
|
|
}
|
|
|
|
return (
|
|
<div className="mb-4">
|
|
<button
|
|
className="flex justify-between items-center w-full bg-secondary/50 p-3 rounded-xl text-left hover:bg-secondary transition-all duration-200 shadow-sm"
|
|
onClick={(): void => onToggleSection(sectionKey)}
|
|
>
|
|
<span className="font-bold text-text-primary">{t('plotPointsTitle')}</span>
|
|
<FontAwesomeIcon
|
|
icon={isExpanded ? faChevronUp : faChevronDown}
|
|
className="text-text-primary w-3.5 h-3.5"
|
|
/>
|
|
</button>
|
|
|
|
{isExpanded && (
|
|
<div className="p-2">
|
|
{plotPoints && plotPoints.length > 0 ? (
|
|
plotPoints.map((item: PlotPoint) => {
|
|
const itemKey = `plotpoint_${item.plotPointId}`;
|
|
const isItemExpanded: boolean = expandedItems[itemKey];
|
|
const linkedIncident: Incident | undefined = incidents.find(
|
|
(inc: Incident): boolean => inc.incidentId === item.linkedIncidentId
|
|
);
|
|
|
|
return (
|
|
<div
|
|
key={`plot-point-${item.plotPointId}`}
|
|
className="bg-secondary/30 rounded-xl mb-3 overflow-hidden border border-secondary/40 shadow-sm hover:shadow-md transition-all duration-200"
|
|
>
|
|
<button
|
|
className="flex justify-between items-center w-full p-2 text-left"
|
|
onClick={(): void => toggleItem(itemKey)}
|
|
>
|
|
<div>
|
|
<p className="font-bold text-text-primary">{item.title}</p>
|
|
{linkedIncident && (
|
|
<p className="text-text-secondary text-sm italic">
|
|
{t('linkedTo')}: {linkedIncident.title}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<div className="flex items-center">
|
|
<FontAwesomeIcon
|
|
icon={isItemExpanded ? faChevronUp : faChevronDown}
|
|
className="text-text-primary w-3.5 h-3.5 mr-2"
|
|
/>
|
|
<button
|
|
onClick={async (e): Promise<void> => {
|
|
e.stopPropagation();
|
|
await onDeletePlotPoint(actId, item.plotPointId);
|
|
}}
|
|
className="text-error hover:bg-error/20 p-1.5 rounded-lg transition-all duration-200 hover:scale-110"
|
|
>
|
|
<FontAwesomeIcon icon={faTrash} className="w-3.5 h-3.5"/>
|
|
</button>
|
|
</div>
|
|
</button>
|
|
|
|
{isItemExpanded && (
|
|
<div className="p-3 bg-secondary/20">
|
|
{item.chapters && item.chapters.length > 0 ? (
|
|
item.chapters.map((chapter: ActChapter) => (
|
|
<ActChapterItem
|
|
key={`plot-chapter-${chapter.chapterId}-${chapter.chapterInfoId}`}
|
|
chapter={chapter}
|
|
onUpdateSummary={(chapterId, summary) =>
|
|
onUpdateChapterSummary(chapterId, summary, item.plotPointId)
|
|
}
|
|
onUnlink={(chapterInfoId, chapterId) =>
|
|
onUnlinkChapter(chapterInfoId, chapterId, item.plotPointId)
|
|
}
|
|
/>
|
|
))
|
|
) : (
|
|
<p className="text-text-secondary text-center text-sm p-2">
|
|
{t('noLinkedChapter')}
|
|
</p>
|
|
)}
|
|
|
|
<div className="flex items-center mt-2">
|
|
<select
|
|
onChange={(e) => setSelectedChapterId(e.target.value)}
|
|
className="flex-1 bg-secondary/50 text-text-primary rounded-xl px-4 py-2.5 mr-2 border border-secondary/50 focus:outline-none focus:ring-4 focus:ring-primary/20 focus:border-primary hover:bg-secondary hover:border-secondary transition-all duration-200"
|
|
>
|
|
<option value="">{t('selectChapterPlaceholder')}</option>
|
|
{mainChapters.map((chapter: ChapterListProps) => (
|
|
<option key={chapter.chapterId} value={chapter.chapterId}>
|
|
{`${chapter.chapterOrder}. ${chapter.title}`}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<button
|
|
className="bg-primary text-text-primary w-9 h-9 rounded-full flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed shadow-md hover:shadow-lg hover:scale-110 hover:bg-primary-dark transition-all duration-200"
|
|
onClick={() => onLinkChapter(actId, selectedChapterId, item.plotPointId)}
|
|
disabled={!selectedChapterId}
|
|
>
|
|
<FontAwesomeIcon icon={faPlus} className="w-3.5 h-3.5"/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})
|
|
) : (
|
|
<p className="text-text-secondary text-center text-sm p-2">
|
|
{t('noPlotPointAdded')}
|
|
</p>
|
|
)}
|
|
|
|
<div className="mt-2 space-y-2">
|
|
<div className="flex items-center">
|
|
<input
|
|
type="text"
|
|
className="flex-1 bg-secondary/50 text-text-primary rounded-xl px-4 py-2.5 border border-secondary/50 focus:outline-none focus:ring-4 focus:ring-primary/20 focus:border-primary hover:bg-secondary hover:border-secondary transition-all duration-200 placeholder:text-muted/60"
|
|
value={newPlotPointTitle}
|
|
onChange={(e) => setNewPlotPointTitle(e.target.value)}
|
|
placeholder={t('newPlotPointPlaceholder')}
|
|
/>
|
|
</div>
|
|
<InputField
|
|
input={
|
|
<SelectBox
|
|
defaultValue={``}
|
|
onChangeCallBack={(e) => setSelectedIncidentId(e.target.value)}
|
|
data={getIncidentData()}
|
|
/>
|
|
}
|
|
addButtonCallBack={(): Promise<void> => onAddPlotPoint(actId)}
|
|
isAddButtonDisabled={newPlotPointTitle.trim() === ''}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|