mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-28 19:05:31 +02:00
Remove extra clicks for edit and save scenarios
This commit is contained in:
parent
cca55c3773
commit
2f7688b1a6
2 changed files with 53 additions and 118 deletions
|
|
@ -19,7 +19,7 @@ import { type WithStringId } from '../../../lib/types/types';
|
||||||
import { Scenario, SimulationRun, SimulationResult } from "../../../lib/types/testing_types";
|
import { Scenario, SimulationRun, SimulationResult } from "../../../lib/types/testing_types";
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { SimulationResultCard, ScenarioResultCard } from './components/RunComponents';
|
import { SimulationResultCard, ScenarioResultCard } from './components/RunComponents';
|
||||||
import { ScenarioViewer, ScenarioEditor } from './components/ScenarioComponents';
|
import { ScenarioViewer } from './components/ScenarioComponents';
|
||||||
|
|
||||||
type ScenarioType = WithStringId<z.infer<typeof Scenario>>;
|
type ScenarioType = WithStringId<z.infer<typeof Scenario>>;
|
||||||
type SimulationRunType = WithStringId<z.infer<typeof SimulationRun>>;
|
type SimulationRunType = WithStringId<z.infer<typeof SimulationRun>>;
|
||||||
|
|
@ -339,19 +339,11 @@ export default function SimulationApp() {
|
||||||
{/* Main content */}
|
{/* Main content */}
|
||||||
<div className="flex-1 p-6 overflow-auto">
|
<div className="flex-1 p-6 overflow-auto">
|
||||||
{selectedScenario ? (
|
{selectedScenario ? (
|
||||||
isEditing ? (
|
<ScenarioViewer
|
||||||
<ScenarioEditor
|
scenario={selectedScenario}
|
||||||
scenario={selectedScenario}
|
onSave={handleUpdateScenario}
|
||||||
onSave={handleUpdateScenario}
|
onClose={handleCloseScenario}
|
||||||
onCancel={() => setIsEditing(false)}
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ScenarioViewer
|
|
||||||
scenario={selectedScenario}
|
|
||||||
onEdit={() => setIsEditing(true)}
|
|
||||||
onClose={handleCloseScenario}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { PencilIcon, XMarkIcon, DocumentDuplicateIcon } from '@heroicons/react/24/outline';
|
import { PencilIcon, XMarkIcon, DocumentDuplicateIcon } from '@heroicons/react/24/outline';
|
||||||
import { WithStringId } from '../../../../lib/types/types';
|
import { WithStringId } from '../../../../lib/types/types';
|
||||||
import { Scenario } from "../../../../lib/types/testing_types";
|
import { Scenario } from "../../../../lib/types/testing_types";
|
||||||
|
|
@ -10,77 +10,18 @@ type ScenarioType = WithStringId<z.infer<typeof Scenario>>;
|
||||||
|
|
||||||
interface ScenarioViewerProps {
|
interface ScenarioViewerProps {
|
||||||
scenario: ScenarioType;
|
scenario: ScenarioType;
|
||||||
onEdit: () => void;
|
onSave: (scenario: ScenarioType) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ScenarioViewer({ scenario, onEdit, onClose }: ScenarioViewerProps) {
|
export function ScenarioViewer({ scenario, onSave, onClose }: ScenarioViewerProps) {
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="flex justify-between items-center mb-6">
|
|
||||||
<h1 className="text-2xl font-bold">{scenario.name}</h1>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button
|
|
||||||
onClick={onEdit}
|
|
||||||
className="p-2 rounded-full hover:bg-gray-100"
|
|
||||||
title="Edit"
|
|
||||||
>
|
|
||||||
<PencilIcon className="h-5 w-5 text-gray-600" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="p-2 rounded-full hover:bg-gray-100"
|
|
||||||
title="Close"
|
|
||||||
>
|
|
||||||
<XMarkIcon className="h-5 w-5 text-gray-600" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">NAME</div>
|
|
||||||
<div className="text-base">{scenario.name}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border-t border-gray-200 my-4"></div>
|
|
||||||
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">DESCRIPTION</div>
|
|
||||||
<div className="text-base whitespace-pre-wrap">{scenario.description}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border-t border-gray-200 my-4"></div>
|
|
||||||
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">CRITERIA</div>
|
|
||||||
<div className="text-base whitespace-pre-wrap">{scenario.criteria}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border-t border-gray-200 my-4"></div>
|
|
||||||
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">CONTEXT</div>
|
|
||||||
<div className="text-base whitespace-pre-wrap">{scenario.context}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ScenarioEditorProps {
|
|
||||||
scenario: ScenarioType;
|
|
||||||
onSave: (scenario: ScenarioType) => void;
|
|
||||||
onCancel: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ScenarioEditor({ scenario, onSave, onCancel }: ScenarioEditorProps) {
|
|
||||||
const [name, setName] = useState(scenario.name);
|
const [name, setName] = useState(scenario.name);
|
||||||
const [description, setDescription] = useState(scenario.description);
|
const [description, setDescription] = useState(scenario.description);
|
||||||
const [criteria, setCriteria] = useState(scenario.criteria || '');
|
const [criteria, setCriteria] = useState(scenario.criteria || '');
|
||||||
const [context, setContext] = useState(scenario.context || '');
|
const [context, setContext] = useState(scenario.context || '');
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
// Save changes whenever any field changes
|
||||||
e.preventDefault();
|
useEffect(() => {
|
||||||
onSave({
|
onSave({
|
||||||
...scenario,
|
...scenario,
|
||||||
name,
|
name,
|
||||||
|
|
@ -88,80 +29,82 @@ export function ScenarioEditor({ scenario, onSave, onCancel }: ScenarioEditorPro
|
||||||
criteria,
|
criteria,
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
};
|
}, [name, description, criteria, context]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
<h1 className="text-2xl font-bold">Edit Scenario</h1>
|
<h1 className="text-2xl font-bold">Scenario Details</h1>
|
||||||
<div className="flex gap-2">
|
<button
|
||||||
<button
|
onClick={onClose}
|
||||||
onClick={() => handleSubmit({ preventDefault: () => {} } as React.FormEvent)}
|
className="p-2 rounded-full hover:bg-gray-100"
|
||||||
className="p-2 rounded-full hover:bg-gray-100"
|
title="Close"
|
||||||
title="Save"
|
>
|
||||||
>
|
<XMarkIcon className="h-5 w-5 text-gray-600" />
|
||||||
<DocumentDuplicateIcon className="h-5 w-5 text-gray-600" />
|
</button>
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={onCancel}
|
|
||||||
className="p-2 rounded-full hover:bg-gray-100"
|
|
||||||
title="Close"
|
|
||||||
>
|
|
||||||
<XMarkIcon className="h-5 w-5 text-gray-600" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div>
|
<div className="flex flex-col">
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">NAME</div>
|
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">NAME</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
className="mt-1 block w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 px-3 py-2"
|
className="text-base border border-gray-200 rounded px-2 py-1 hover:border-gray-300 focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
|
||||||
placeholder="An identifiable scenario name"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-t border-gray-200 my-4"></div>
|
<div className="border-t border-gray-200 my-4"></div>
|
||||||
|
|
||||||
<div>
|
<div className="flex flex-col">
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">DESCRIPTION</div>
|
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">DESCRIPTION</div>
|
||||||
<textarea
|
<textarea
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
rows={4}
|
className="text-base border border-gray-200 rounded px-2 py-1 hover:border-gray-300 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 min-h-[24px] resize-none"
|
||||||
className="mt-1 block w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 px-3 py-2"
|
style={{ height: 'auto', minHeight: '24px' }}
|
||||||
placeholder="Specify the user scenario that the simulator should simulate"
|
onInput={(e) => {
|
||||||
|
const target = e.target as HTMLTextAreaElement;
|
||||||
|
target.style.height = 'auto';
|
||||||
|
target.style.height = `${target.scrollHeight}px`;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-t border-gray-200 my-4"></div>
|
<div className="border-t border-gray-200 my-4"></div>
|
||||||
|
|
||||||
<div>
|
<div className="flex flex-col">
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">CRITERIA</div>
|
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">CRITERIA</div>
|
||||||
<textarea
|
<textarea
|
||||||
value={criteria}
|
value={criteria}
|
||||||
onChange={(e) => setCriteria(e.target.value)}
|
onChange={(e) => setCriteria(e.target.value)}
|
||||||
rows={4}
|
className="text-base border border-gray-200 rounded px-2 py-1 hover:border-gray-300 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 min-h-[24px] resize-none"
|
||||||
className="mt-1 block w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 px-3 py-2"
|
style={{ height: 'auto', minHeight: '24px' }}
|
||||||
placeholder="Enter success criteria for this scenario"
|
onInput={(e) => {
|
||||||
|
const target = e.target as HTMLTextAreaElement;
|
||||||
|
target.style.height = 'auto';
|
||||||
|
target.style.height = `${target.scrollHeight}px`;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-t border-gray-200 my-4"></div>
|
<div className="border-t border-gray-200 my-4"></div>
|
||||||
|
|
||||||
<div>
|
<div className="flex flex-col">
|
||||||
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">CONTEXT</div>
|
<div className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-4">CONTEXT</div>
|
||||||
<textarea
|
<textarea
|
||||||
value={context}
|
value={context}
|
||||||
onChange={(e) => setContext(e.target.value)}
|
onChange={(e) => setContext(e.target.value)}
|
||||||
rows={4}
|
className="text-base border border-gray-200 rounded px-2 py-1 hover:border-gray-300 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 min-h-[24px] resize-none"
|
||||||
className="mt-1 block w-full rounded-md border-2 border-gray-300 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 px-3 py-2"
|
style={{ height: 'auto', minHeight: '24px' }}
|
||||||
placeholder="Provide context about the user to the assistant at the start of chat"
|
onInput={(e) => {
|
||||||
|
const target = e.target as HTMLTextAreaElement;
|
||||||
|
target.style.height = 'auto';
|
||||||
|
target.style.height = `${target.scrollHeight}px`;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue