mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-03 12:22:38 +02:00
Use editable-field with save for scenarios
This commit is contained in:
parent
f1e0a83bd1
commit
68cf6d6fd1
1 changed files with 38 additions and 83 deletions
|
|
@ -1,10 +1,11 @@
|
|||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback, ChangeEvent } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import { WithStringId } from '../../../../lib/types/types';
|
||||
import { Scenario } from "../../../../lib/types/testing_types";
|
||||
import { z } from 'zod';
|
||||
import { EditableField } from '../../../../lib/components/editable-field';
|
||||
|
||||
type ScenarioType = WithStringId<z.infer<typeof Scenario>>;
|
||||
|
||||
|
|
@ -16,49 +17,20 @@ interface ScenarioViewerProps {
|
|||
|
||||
export function ScenarioViewer({ scenario, onSave, onClose }: ScenarioViewerProps) {
|
||||
const [editedScenario, setEditedScenario] = useState<ScenarioType>(scenario);
|
||||
const [saveTimeout, setSaveTimeout] = useState<NodeJS.Timeout | null>(null);
|
||||
|
||||
// Reset state when scenario changes
|
||||
useEffect(() => {
|
||||
setEditedScenario(scenario);
|
||||
}, [scenario]);
|
||||
|
||||
const handleChange = useCallback((field: keyof ScenarioType, event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
event.preventDefault();
|
||||
const value = event.target.value;
|
||||
|
||||
setEditedScenario(prev => ({
|
||||
...prev,
|
||||
const handleFieldChange = (field: keyof ScenarioType) => (value: string) => {
|
||||
const updatedScenario = {
|
||||
...editedScenario,
|
||||
[field]: value,
|
||||
}));
|
||||
|
||||
if (saveTimeout) {
|
||||
clearTimeout(saveTimeout);
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
onSave({
|
||||
...editedScenario,
|
||||
[field]: value,
|
||||
});
|
||||
}, 1000); // Increased debounce time to 1 second
|
||||
|
||||
setSaveTimeout(timeoutId);
|
||||
}, [editedScenario, onSave, saveTimeout]);
|
||||
|
||||
// Cleanup timeout on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (saveTimeout) {
|
||||
clearTimeout(saveTimeout);
|
||||
}
|
||||
};
|
||||
}, [saveTimeout]);
|
||||
|
||||
const adjustTextareaHeight = useCallback((element: HTMLTextAreaElement) => {
|
||||
element.style.height = 'auto';
|
||||
element.style.height = `${element.scrollHeight}px`;
|
||||
}, []);
|
||||
setEditedScenario(updatedScenario);
|
||||
onSave(updatedScenario);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -73,62 +45,45 @@ export function ScenarioViewer({ scenario, onSave, onClose }: ScenarioViewerProp
|
|||
</button>
|
||||
</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>
|
||||
<input
|
||||
type="text"
|
||||
value={editedScenario.name}
|
||||
onChange={(e) => handleChange('name', e)}
|
||||
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"
|
||||
autoComplete="off"
|
||||
spellCheck="false"
|
||||
/>
|
||||
</div>
|
||||
<EditableField
|
||||
label="NAME"
|
||||
value={editedScenario.name}
|
||||
onChange={handleFieldChange('name')}
|
||||
placeholder="Enter scenario name..."
|
||||
/>
|
||||
|
||||
<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>
|
||||
<textarea
|
||||
value={editedScenario.description}
|
||||
onChange={(e) => handleChange('description', e)}
|
||||
onInput={(e) => adjustTextareaHeight(e.target as HTMLTextAreaElement)}
|
||||
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"
|
||||
style={{ height: 'auto', minHeight: '24px' }}
|
||||
autoComplete="off"
|
||||
spellCheck="false"
|
||||
/>
|
||||
</div>
|
||||
<EditableField
|
||||
label="DESCRIPTION"
|
||||
value={editedScenario.description}
|
||||
onChange={handleFieldChange('description')}
|
||||
placeholder="Enter scenario description..."
|
||||
multiline
|
||||
markdown
|
||||
/>
|
||||
|
||||
<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>
|
||||
<textarea
|
||||
value={editedScenario.criteria}
|
||||
onChange={(e) => handleChange('criteria', e)}
|
||||
onInput={(e) => adjustTextareaHeight(e.target as HTMLTextAreaElement)}
|
||||
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"
|
||||
style={{ height: 'auto', minHeight: '24px' }}
|
||||
autoComplete="off"
|
||||
spellCheck="false"
|
||||
/>
|
||||
</div>
|
||||
<EditableField
|
||||
label="CRITERIA"
|
||||
value={editedScenario.criteria}
|
||||
onChange={handleFieldChange('criteria')}
|
||||
placeholder="Enter success criteria..."
|
||||
multiline
|
||||
markdown
|
||||
/>
|
||||
|
||||
<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>
|
||||
<textarea
|
||||
value={editedScenario.context}
|
||||
onChange={(e) => handleChange('context', e)}
|
||||
onInput={(e) => adjustTextareaHeight(e.target as HTMLTextAreaElement)}
|
||||
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"
|
||||
style={{ height: 'auto', minHeight: '24px' }}
|
||||
autoComplete="off"
|
||||
spellCheck="false"
|
||||
/>
|
||||
</div>
|
||||
<EditableField
|
||||
label="CONTEXT"
|
||||
value={editedScenario.context}
|
||||
onChange={handleFieldChange('context')}
|
||||
placeholder="Enter scenario context..."
|
||||
multiline
|
||||
markdown
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue