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 = {
tasksError?: string | null;
onTaskFileChange?: (fileId: string) => void;
onTaskChange?: (taskId: string) => void;
onChangeTaskArea?: () => void;
onPreviousTask?: () => void;
isPreviousTaskDisabled?: boolean;
onNextTask?: () => void;
isNextTaskDisabled?: boolean;
};
export default function TaskPanel({
......@@ -30,6 +35,11 @@ export default function TaskPanel({
tasksError,
onTaskFileChange,
onTaskChange,
onChangeTaskArea,
onPreviousTask,
isPreviousTaskDisabled = false,
onNextTask,
isNextTaskDisabled = false,
}: TaskPanelProps) {
const taskDisplayRef = useRef<HTMLDivElement | null>(null);
......@@ -49,7 +59,13 @@ export default function TaskPanel({
<div className="task-panel-header">
<div className="task-panel-title">{t("task")}</div>
{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">
<select
......@@ -98,6 +114,27 @@ export default function TaskPanel({
<div className="task-panel-content" ref={taskDisplayRef}>
{selectedTaskText || t("noTaskSelected")}
</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}
</section>
);
......
......@@ -50,7 +50,9 @@
taskFile: "Task Set",
taskId: "Task ID",
solveWithTutor: "Solve with Tutor",
taskLocked: "Task locked",
changeTaskArea: "Change Task Area",
previousTask: "Previous Task",
nextTask: "Next Task",
},
de: {
chats: "Chats",
......@@ -107,7 +109,9 @@
taskFile: "Aufgabenset",
taskId: "Aufgaben-ID",
solveWithTutor: "Mit Tutor lösen",
taskLocked: "Aufgabe fixiert",
changeTaskArea: "Aufgabengebiet ändern",
previousTask: "Vorherige Aufgabe",
nextTask: "Nächste Aufgabe",
},
} as const;
......
......@@ -9,7 +9,7 @@ import type { ChatMessage } from "../components/Chat/MessageList";
import type { RetrievedDoc } from "../components/Retrieval/DocPanel";
import { t } from "../i18n";
import { selectTask } from "../api/taskApi";
import { useTutorSession } from "../state/tutorSession";
import { createSessionId, useTutorSession } from "../state/tutorSession";
const initialMessages: ChatMessage[] = [];
......@@ -99,6 +99,7 @@ export default function ChatPage() {
isTasksInitialized,
selectedTaskRef,
selectedTask,
selectedTaskFile,
taskLocked,
lockTask,
unlockTask,
......@@ -181,6 +182,40 @@ export default function ChatPage() {
return { bySourceKey, bySlug };
}, [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 trimmed = draft.trim();
if (!trimmed) {
......@@ -427,6 +462,67 @@ export default function ChatPage() {
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 = () => {
setIsSidebarOpen(true);
void loadArchives();
......@@ -605,6 +701,11 @@ export default function ChatPage() {
selectedTaskText={selectedTask.fullText}
selectedFileLabel={selectedTask.title || selectedTask.fileId}
selectedTaskId={selectedTask.taskId}
onChangeTaskArea={handleChangeTaskArea}
onPreviousTask={handlePreviousTask}
isPreviousTaskDisabled={!previousTaskRef || isArchiving}
onNextTask={handleNextTask}
isNextTaskDisabled={!nextTaskRef || isArchiving}
/>
) : null}
......
......@@ -618,16 +618,6 @@ body {
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 {
display: flex;
justify-content: space-between;
......@@ -636,6 +626,25 @@ body {
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) {
.task-select-controls {
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