Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/simulation/fixtures/edit/issue-7487/EditForm.tsx
13513 views
1
'use client';
2
3
import { Edit } from '@/database';
4
import { useRouter } from 'next/navigation';
5
import { useEffect, useState } from 'react';
6
7
interface EditFormProps {
8
storyUid: string;
9
edit?: Edit;
10
}
11
12
const EditForm: React.FC<EditFormProps> = ({ storyUid, edit: initialEdit }) => {
13
const [prompt, setPrompt] = useState('');
14
const [edit, setEdit] = useState<Edit | null>(initialEdit || null);
15
const [isLoading, setIsLoading] = useState(false);
16
const [isAccepting, setIsAccepting] = useState(false);
17
const [isDiscarding, setIsDiscarding] = useState(false);
18
const router = useRouter();
19
20
useEffect(() => {
21
if (edit) {
22
setPrompt(edit.prompt);
23
}
24
}, [edit]);
25
26
const handleSubmit = async (e: React.FormEvent) => {
27
e.preventDefault();
28
setIsLoading(true);
29
const response = await fetch(`/api/updateStory`, {
30
method: 'POST',
31
headers: {
32
'Content-Type': 'application/json',
33
},
34
body: JSON.stringify({ storyUid, prompt }),
35
});
36
37
if (!response.ok) {
38
console.error('Failed to fetch suggestions');
39
setIsLoading(false);
40
return;
41
}
42
43
const data = await response.json().catch(() => null);
44
if (data) {
45
setEdit(data);
46
} else {
47
console.error('Failed to parse JSON response');
48
}
49
setIsLoading(false);
50
};
51
52
const handleAcceptEdits = async () => {
53
if (!edit || !edit.operations.length) {
54
console.error('No edits to accept');
55
return;
56
}
57
58
setIsAccepting(true);
59
await fetch(`/api/acceptEdits`, {
60
method: 'POST',
61
headers: {
62
'Content-Type': 'application/json',
63
},
64
body: JSON.stringify({ storyUid }),
65
});
66
setEdit(null);
67
setIsAccepting(false);
68
router.refresh();
69
};
70
71
const handleDiscardEdits = async () => {
72
setIsDiscarding(true);
73
await fetch(`/api/discardEdits`, {
74
method: 'POST',
75
headers: {
76
'Content-Type': 'application/json',
77
},
78
body: JSON.stringify({ storyUid }),
79
});
80
setEdit(null);
81
setIsDiscarding(false);
82
router.refresh();
83
};
84
85
const handleFollowupClick = (followup: string) => {
86
setPrompt((prevPrompt) => `${prevPrompt.trim()}\n${followup.trim()}`);
87
};
88
89
return (
90
<div className="fixed top-0 right-0 w-1/3 p-4 bg-white shadow-lg h-full overflow-y-auto">
91
<form onSubmit={handleSubmit}>
92
<textarea
93
value={prompt}
94
onChange={(e) => setPrompt(e.target.value)}
95
className="w-full h-20 p-2 border border-gray-300 rounded"
96
placeholder="Enter your prompt here..."
97
disabled={isLoading}
98
/>
99
<button type="submit" className={`mt-2 p-2 text-white rounded ${isLoading ? 'bg-gray-400' : 'bg-blue-500'}`} disabled={isLoading}>
100
{isLoading ? 'Submitting...' : 'Submit'}
101
</button>
102
</form>
103
{edit && (
104
<div>
105
<div className="flex justify-between items-baseline mt-4">
106
<h2 className="text-xl font-bold">Suggestions</h2>
107
<div className="flex space-x-2">
108
<button onClick={handleAcceptEdits} className={`px-2 text-white rounded ${isAccepting ? 'bg-gray-400' : 'bg-green-500'}`} disabled={isAccepting}>
109
{isAccepting ? 'Accepting...' : 'Accept'}
110
</button>
111
<button onClick={handleDiscardEdits} className={`px-2 text-white rounded ${isDiscarding ? 'bg-gray-400' : 'bg-red-500'}`} disabled={isDiscarding}>
112
{isDiscarding ? 'Discarding...' : 'Discard'}
113
</button>
114
</div>
115
</div>
116
<div className="bg-gray-100 p-4 rounded">
117
<ul className="list-disc ml-4">
118
{edit.operations.map((suggestion, idx) => (
119
<li key={idx} className="mt-1">
120
<a href={`#edit-${idx}`} onMouseOver={() => {
121
const element = document.querySelector(`[name=edit-${idx}]`);
122
console.log(element);
123
124
element?.classList.add('bg-red-100');
125
element?.scrollIntoView({ behavior: 'smooth', block: 'center' });
126
}}
127
onMouseOut={() => {
128
document.querySelector(`[name=edit-${idx}]`)?.classList.remove('bg-red-100');
129
}}
130
className="font-medium">{suggestion.type}</a> at line {suggestion.line + 1}
131
{suggestion.text && (
132
<span>: {suggestion.text}</span>
133
)}
134
</li>
135
))}
136
</ul>
137
<h3 className="mt-4 text-lg font-bold">Follow-up Suggestions</h3>
138
<ul className="ml-4">
139
{edit.followup.map((followup, idx) => (
140
<li key={idx} className="mt-1">
141
<button
142
onClick={() => handleFollowupClick(followup)}
143
className="text-white text-sm rounded-full"
144
>
145
{followup}
146
</button>
147
</li>
148
))}
149
</ul>
150
</div>
151
</div>
152
)}
153
</div>
154
);
155
};
156
157
export default EditForm;
158
159