Commit 9a38c477 authored by Kantz's avatar Kantz
Browse files

wechsel der Aufgabe nun auch innerhalb des Chats möglich

parent 4cb2901e
...@@ -17,6 +17,11 @@ type TaskPanelProps = { ...@@ -17,6 +17,11 @@ type TaskPanelProps = {
tasksError?: string | null; tasksError?: string | null;
onTaskFileChange?: (fileId: string) => void; onTaskFileChange?: (fileId: string) => void;
onTaskChange?: (taskId: string) => void; onTaskChange?: (taskId: string) => void;
onChangeTaskArea?: () => void;
onPreviousTask?: () => void;
isPreviousTaskDisabled?: boolean;
onNextTask?: () => void;
isNextTaskDisabled?: boolean;
}; };
export default function TaskPanel({ export default function TaskPanel({
...@@ -30,6 +35,11 @@ export default function TaskPanel({ ...@@ -30,6 +35,11 @@ export default function TaskPanel({
tasksError, tasksError,
onTaskFileChange, onTaskFileChange,
onTaskChange, onTaskChange,
onChangeTaskArea,
onPreviousTask,
isPreviousTaskDisabled = false,
onNextTask,
isNextTaskDisabled = false,
}: TaskPanelProps) { }: TaskPanelProps) {
const taskDisplayRef = useRef<HTMLDivElement | null>(null); const taskDisplayRef = useRef<HTMLDivElement | null>(null);
...@@ -49,7 +59,13 @@ export default function TaskPanel({ ...@@ -49,7 +59,13 @@ export default function TaskPanel({
<div className="task-panel-header"> <div className="task-panel-header">
<div className="task-panel-title">{t("task")}</div> <div className="task-panel-title">{t("task")}</div>
{readOnly ? ( {readOnly ? (
<div className="task-panel-lock">{t("taskLocked")}</div> <button
type="button"
className="btn task-panel-change-btn"
onClick={onChangeTaskArea}
>
{t("changeTaskArea")}
</button>
) : ( ) : (
<div className="task-panel-controls"> <div className="task-panel-controls">
<select <select
...@@ -98,6 +114,27 @@ export default function TaskPanel({ ...@@ -98,6 +114,27 @@ export default function TaskPanel({
<div className="task-panel-content" ref={taskDisplayRef}> <div className="task-panel-content" ref={taskDisplayRef}>
{selectedTaskText || t("noTaskSelected")} {selectedTaskText || t("noTaskSelected")}
</div> </div>
{readOnly ? (
<div className="task-panel-nav">
<button
type="button"
className="btn task-panel-nav-btn"
onClick={onPreviousTask}
disabled={!onPreviousTask || isPreviousTaskDisabled}
>
{t("previousTask")}
</button>
<button
type="button"
className="btn task-panel-nav-btn"
onClick={onNextTask}
disabled={!onNextTask || isNextTaskDisabled}
>
{t("nextTask")}
</button>
</div>
) : null}
{tasksError ? <div className="task-panel-error">{tasksError}</div> : null} {tasksError ? <div className="task-panel-error">{tasksError}</div> : null}
</section> </section>
); );
......
...@@ -50,7 +50,9 @@ ...@@ -50,7 +50,9 @@
taskFile: "Task Set", taskFile: "Task Set",
taskId: "Task ID", taskId: "Task ID",
solveWithTutor: "Solve with Tutor", solveWithTutor: "Solve with Tutor",
taskLocked: "Task locked", changeTaskArea: "Change Task Area",
previousTask: "Previous Task",
nextTask: "Next Task",
}, },
de: { de: {
chats: "Chats", chats: "Chats",
...@@ -107,7 +109,9 @@ ...@@ -107,7 +109,9 @@
taskFile: "Aufgabenset", taskFile: "Aufgabenset",
taskId: "Aufgaben-ID", taskId: "Aufgaben-ID",
solveWithTutor: "Mit Tutor lösen", solveWithTutor: "Mit Tutor lösen",
taskLocked: "Aufgabe fixiert", changeTaskArea: "Aufgabengebiet ändern",
previousTask: "Vorherige Aufgabe",
nextTask: "Nächste Aufgabe",
}, },
} as const; } as const;
......
...@@ -9,7 +9,7 @@ import type { ChatMessage } from "../components/Chat/MessageList"; ...@@ -9,7 +9,7 @@ import type { ChatMessage } from "../components/Chat/MessageList";
import type { RetrievedDoc } from "../components/Retrieval/DocPanel"; import type { RetrievedDoc } from "../components/Retrieval/DocPanel";
import { t } from "../i18n"; import { t } from "../i18n";
import { selectTask } from "../api/taskApi"; import { selectTask } from "../api/taskApi";
import { useTutorSession } from "../state/tutorSession"; import { createSessionId, useTutorSession } from "../state/tutorSession";
const initialMessages: ChatMessage[] = []; const initialMessages: ChatMessage[] = [];
...@@ -99,6 +99,7 @@ export default function ChatPage() { ...@@ -99,6 +99,7 @@ export default function ChatPage() {
isTasksInitialized, isTasksInitialized,
selectedTaskRef, selectedTaskRef,
selectedTask, selectedTask,
selectedTaskFile,
taskLocked, taskLocked,
lockTask, lockTask,
unlockTask, unlockTask,
...@@ -181,6 +182,40 @@ export default function ChatPage() { ...@@ -181,6 +182,40 @@ export default function ChatPage() {
return { bySourceKey, bySlug }; return { bySourceKey, bySlug };
}, [directChildren, taskChildren, indirectChildren, subsections, sections]); }, [directChildren, taskChildren, indirectChildren, subsections, sections]);
const nextTaskRef = useMemo(() => {
if (!selectedTaskFile || !selectedTaskRef) {
return null;
}
const currentIndex = selectedTaskFile.tasks.findIndex(
(task) => task.task_id === selectedTaskRef.taskId
);
if (currentIndex < 0) {
return null;
}
const nextTask = selectedTaskFile.tasks[currentIndex + 1];
if (!nextTask) {
return null;
}
return { fileId: selectedTaskFile.file_id, taskId: nextTask.task_id };
}, [selectedTaskFile, selectedTaskRef]);
const previousTaskRef = useMemo(() => {
if (!selectedTaskFile || !selectedTaskRef) {
return null;
}
const currentIndex = selectedTaskFile.tasks.findIndex(
(task) => task.task_id === selectedTaskRef.taskId
);
if (currentIndex <= 0) {
return null;
}
const previousTask = selectedTaskFile.tasks[currentIndex - 1];
if (!previousTask) {
return null;
}
return { fileId: selectedTaskFile.file_id, taskId: previousTask.task_id };
}, [selectedTaskFile, selectedTaskRef]);
const handleSend = async () => { const handleSend = async () => {
const trimmed = draft.trim(); const trimmed = draft.trim();
if (!trimmed) { if (!trimmed) {
...@@ -427,6 +462,67 @@ export default function ChatPage() { ...@@ -427,6 +462,67 @@ export default function ChatPage() {
navigate(isTaskModeEnabled ? "/select-task" : "/chat"); navigate(isTaskModeEnabled ? "/select-task" : "/chat");
}; };
const handleSwitchTask = async (target: { fileId: string; taskId: string } | null) => {
if (!target) {
return;
}
if (messages.length) {
setIsArchiving(true);
try {
await fetch(`/api/chat/archive`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
messages: messages.map((message) => ({
role: message.role,
text: message.text,
})),
draft: chatSessionId,
}),
});
} catch (error) {
void error;
} finally {
setIsArchiving(false);
}
}
const nextSessionId = createSessionId();
resetChatState();
setChatSessionId(nextSessionId);
setTaskRef(target);
lockTask();
try {
await selectTask({
draft: nextSessionId,
fileId: target.fileId,
taskId: target.taskId,
});
} catch (error) {
void error;
return;
}
await loadArchives();
navigate("/chat");
};
const handleNextTask = async () => {
await handleSwitchTask(nextTaskRef);
};
const handlePreviousTask = async () => {
await handleSwitchTask(previousTaskRef);
};
const handleChangeTaskArea = () => {
unlockTask();
navigate("/select-task");
};
const handleOpenSidebar = () => { const handleOpenSidebar = () => {
setIsSidebarOpen(true); setIsSidebarOpen(true);
void loadArchives(); void loadArchives();
...@@ -605,6 +701,11 @@ export default function ChatPage() { ...@@ -605,6 +701,11 @@ export default function ChatPage() {
selectedTaskText={selectedTask.fullText} selectedTaskText={selectedTask.fullText}
selectedFileLabel={selectedTask.title || selectedTask.fileId} selectedFileLabel={selectedTask.title || selectedTask.fileId}
selectedTaskId={selectedTask.taskId} selectedTaskId={selectedTask.taskId}
onChangeTaskArea={handleChangeTaskArea}
onPreviousTask={handlePreviousTask}
isPreviousTaskDisabled={!previousTaskRef || isArchiving}
onNextTask={handleNextTask}
isNextTaskDisabled={!nextTaskRef || isArchiving}
/> />
) : null} ) : null}
......
...@@ -618,16 +618,6 @@ body { ...@@ -618,16 +618,6 @@ body {
align-self: flex-end; align-self: flex-end;
} }
.task-panel-lock {
margin-left: auto;
font-size: 12px;
color: #6f675d;
border: 1px solid #d8d1c4;
border-radius: 999px;
padding: 4px 8px;
background: #fff;
}
.task-panel-meta { .task-panel-meta {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
...@@ -636,6 +626,25 @@ body { ...@@ -636,6 +626,25 @@ body {
color: #6f675d; color: #6f675d;
} }
.task-panel-change-btn {
margin-left: auto;
padding: 4px 10px;
font-size: 11px;
}
.task-panel-nav-btn {
padding: 4px 10px;
font-size: 11px;
}
.task-panel-nav {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 11px;
gap: 10px;
}
@media (max-width: 900px) { @media (max-width: 900px) {
.task-select-controls { .task-select-controls {
grid-template-columns: 1fr; grid-template-columns: 1fr;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment