|
import React, { useState, useEffect } from 'react'; |
|
import { useSelector, useDispatch } from 'react-redux'; |
|
import Arrow2 from './assets/dropdown-arrow.svg'; |
|
import ArrowLeft from './assets/arrow-left.svg'; |
|
import ArrowRight from './assets/arrow-right.svg'; |
|
import Trash from './assets/trash.svg'; |
|
import { |
|
selectPrompt, |
|
setPrompt, |
|
selectSourceDocs, |
|
} from './preferences/preferenceSlice'; |
|
import { Doc } from './preferences/preferenceApi'; |
|
type PromptProps = { |
|
prompts: { name: string; id: string; type: string }[]; |
|
selectedPrompt: { name: string; id: string; type: string }; |
|
onSelectPrompt: (name: string, id: string, type: string) => void; |
|
setPrompts: (prompts: { name: string; id: string; type: string }[]) => void; |
|
apiHost: string; |
|
}; |
|
|
|
const Setting: React.FC = () => { |
|
const tabs = ['General', 'Prompts', 'Documents']; |
|
|
|
|
|
const [activeTab, setActiveTab] = useState('General'); |
|
const [prompts, setPrompts] = useState< |
|
{ name: string; id: string; type: string }[] |
|
>([]); |
|
const selectedPrompt = useSelector(selectPrompt); |
|
const [isAddPromptModalOpen, setAddPromptModalOpen] = useState(false); |
|
const documents = useSelector(selectSourceDocs); |
|
const [isAddDocumentModalOpen, setAddDocumentModalOpen] = useState(false); |
|
|
|
const dispatch = useDispatch(); |
|
|
|
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; |
|
const [widgetScreenshot, setWidgetScreenshot] = useState<File | null>(null); |
|
|
|
const updateWidgetScreenshot = (screenshot: File | null) => { |
|
setWidgetScreenshot(screenshot); |
|
}; |
|
|
|
useEffect(() => { |
|
const fetchPrompts = async () => { |
|
try { |
|
const response = await fetch(`${apiHost}/api/get_prompts`); |
|
if (!response.ok) { |
|
throw new Error('Failed to fetch prompts'); |
|
} |
|
const promptsData = await response.json(); |
|
setPrompts(promptsData); |
|
} catch (error) { |
|
console.error(error); |
|
} |
|
}; |
|
|
|
fetchPrompts(); |
|
}, []); |
|
|
|
const onDeletePrompt = (name: string, id: string) => { |
|
setPrompts(prompts.filter((prompt) => prompt.id !== id)); |
|
|
|
fetch(`${apiHost}/api/delete_prompt`, { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
|
|
body: JSON.stringify({ id: id }), |
|
}) |
|
.then((response) => { |
|
if (!response.ok) { |
|
throw new Error('Failed to delete prompt'); |
|
} |
|
}) |
|
.catch((error) => { |
|
console.error(error); |
|
}); |
|
}; |
|
|
|
const handleDeleteClick = (index: number, doc: Doc) => { |
|
const docPath = 'indexes/' + 'local' + '/' + doc.name; |
|
|
|
fetch(`${apiHost}/api/delete_old?path=${docPath}`, { |
|
method: 'GET', |
|
}) |
|
.then(() => { |
|
|
|
const imageElement = document.querySelector( |
|
`#img-${index}`, |
|
) as HTMLElement; |
|
const parentElement = imageElement.parentNode as HTMLElement; |
|
parentElement.parentNode?.removeChild(parentElement); |
|
}) |
|
.catch((error) => console.error(error)); |
|
}; |
|
|
|
return ( |
|
<div className="p-4 pt-20 md:p-12 wa"> |
|
<p className="text-2xl font-bold text-eerie-black dark:text-bright-gray">Settings</p> |
|
<div className="mt-6 flex flex-row items-center space-x-4 overflow-x-auto md:space-x-8 "> |
|
<div className="md:hidden"> |
|
<button |
|
onClick={() => scrollTabs(-1)} |
|
className="flex h-8 w-8 items-center justify-center rounded-full border-2 border-purple-30 transition-all hover:bg-gray-100" |
|
> |
|
<img src={ArrowLeft} alt="left-arrow" className="h-6 w-6" /> |
|
</button> |
|
</div> |
|
<div className="flex flex-nowrap space-x-4 overflow-x-auto md:space-x-8"> |
|
{tabs.map((tab, index) => ( |
|
<button |
|
key={index} |
|
onClick={() => setActiveTab(tab)} |
|
className={`h-9 rounded-3xl px-4 font-bold ${activeTab === tab |
|
? 'bg-purple-3000 text-purple-30 dark:bg-dark-charcoal' |
|
: 'text-gray-6000' |
|
}`} |
|
> |
|
{tab} |
|
</button> |
|
))} |
|
</div> |
|
<div className="md:hidden"> |
|
<button |
|
onClick={() => scrollTabs(1)} |
|
className="flex h-8 w-8 items-center justify-center rounded-full border-2 border-purple-30 hover:bg-gray-100" |
|
> |
|
<img src={ArrowRight} alt="right-arrow" className="h-6 w-6" /> |
|
</button> |
|
</div> |
|
</div> |
|
{renderActiveTab()} |
|
|
|
{/* {activeTab === 'Widgets' && ( |
|
<Widgets |
|
widgetScreenshot={widgetScreenshot} |
|
onWidgetScreenshotChange={updateWidgetScreenshot} |
|
/> |
|
)} */} |
|
</div> |
|
); |
|
|
|
function scrollTabs(direction: number) { |
|
const container = document.querySelector('.flex-nowrap'); |
|
if (container) { |
|
container.scrollLeft += direction * 100; |
|
} |
|
} |
|
|
|
function renderActiveTab() { |
|
switch (activeTab) { |
|
case 'General': |
|
return <General />; |
|
case 'Prompts': |
|
return ( |
|
<Prompts |
|
prompts={prompts} |
|
selectedPrompt={selectedPrompt} |
|
onSelectPrompt={(name, id, type) => |
|
dispatch(setPrompt({ name: name, id: id, type: type })) |
|
} |
|
setPrompts={setPrompts} |
|
apiHost={apiHost} |
|
/> |
|
); |
|
case 'Documents': |
|
return ( |
|
<Documents |
|
documents={documents} |
|
handleDeleteDocument={handleDeleteClick} |
|
/> |
|
); |
|
case 'Widgets': |
|
return ( |
|
<Widgets |
|
widgetScreenshot={widgetScreenshot} // Add this line |
|
onWidgetScreenshotChange={updateWidgetScreenshot} // Add this line |
|
/> |
|
); |
|
default: |
|
return null; |
|
} |
|
} |
|
}; |
|
|
|
const General: React.FC = () => { |
|
const themes = ['Light', 'Dark']; |
|
const languages = ['English']; |
|
const [selectedTheme, setSelectedTheme] = useState(localStorage.getItem('selectedTheme') || themes[0]); |
|
const [selectedLanguage, setSelectedLanguage] = useState(languages[0]); |
|
|
|
useEffect(() => { |
|
if (selectedTheme === 'Dark') { |
|
document.documentElement.classList.add('dark'); |
|
document.documentElement.classList.add('dark:bg-raisin-black'); |
|
} else { |
|
document.documentElement.classList.remove('dark'); |
|
} |
|
localStorage.setItem('selectedTheme', selectedTheme); |
|
}, [selectedTheme]); |
|
|
|
return ( |
|
<div className="mt-[59px]"> |
|
<div className="mb-4"> |
|
<p className="font-bold text-jet dark:text-bright-gray">Select Theme</p> |
|
<Dropdown |
|
options={themes} |
|
selectedValue={selectedTheme} |
|
onSelect={setSelectedTheme} |
|
/> |
|
</div> |
|
<div> |
|
<p className="font-bold text-jet dark:text-bright-gray">Select Language</p> |
|
<Dropdown |
|
options={languages} |
|
selectedValue={selectedLanguage} |
|
onSelect={setSelectedLanguage} |
|
/> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
export default Setting; |
|
|
|
const Prompts: React.FC<PromptProps> = ({ |
|
prompts, |
|
selectedPrompt, |
|
onSelectPrompt, |
|
setPrompts, |
|
apiHost, |
|
}) => { |
|
const handleSelectPrompt = ({ |
|
name, |
|
id, |
|
type, |
|
}: { |
|
name: string; |
|
id: string; |
|
type: string; |
|
}) => { |
|
setNewPromptName(name); |
|
onSelectPrompt(name, id, type); |
|
}; |
|
const [newPromptName, setNewPromptName] = useState(selectedPrompt.name); |
|
const [newPromptContent, setNewPromptContent] = useState(''); |
|
|
|
const handleAddPrompt = async () => { |
|
try { |
|
const response = await fetch(`${apiHost}/api/create_prompt`, { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ |
|
name: newPromptName, |
|
content: newPromptContent, |
|
}), |
|
}); |
|
if (!response.ok) { |
|
throw new Error('Failed to add prompt'); |
|
} |
|
const newPrompt = await response.json(); |
|
if (setPrompts) { |
|
setPrompts([ |
|
...prompts, |
|
{ name: newPromptName, id: newPrompt.id, type: 'private' }, |
|
]); |
|
} |
|
onSelectPrompt(newPromptName, newPrompt.id, newPromptContent); |
|
setNewPromptName(newPromptName); |
|
} catch (error) { |
|
console.error(error); |
|
} |
|
}; |
|
|
|
const handleDeletePrompt = () => { |
|
setPrompts(prompts.filter((prompt) => prompt.id !== selectedPrompt.id)); |
|
console.log('selectedPrompt.id', selectedPrompt.id); |
|
|
|
fetch(`${apiHost}/api/delete_prompt`, { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ id: selectedPrompt.id }), |
|
}) |
|
.then((response) => { |
|
if (!response.ok) { |
|
throw new Error('Failed to delete prompt'); |
|
} |
|
|
|
if (prompts.length > 0) { |
|
onSelectPrompt(prompts[0].name, prompts[0].id, prompts[0].type); |
|
setNewPromptName(prompts[0].name); |
|
} |
|
}) |
|
.catch((error) => { |
|
console.error(error); |
|
}); |
|
}; |
|
|
|
useEffect(() => { |
|
const fetchPromptContent = async () => { |
|
console.log('fetching prompt content'); |
|
try { |
|
const response = await fetch( |
|
`${apiHost}/api/get_single_prompt?id=${selectedPrompt.id}`, |
|
{ |
|
method: 'GET', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
}, |
|
); |
|
if (!response.ok) { |
|
throw new Error('Failed to fetch prompt content'); |
|
} |
|
const promptContent = await response.json(); |
|
setNewPromptContent(promptContent.content); |
|
} catch (error) { |
|
console.error(error); |
|
} |
|
}; |
|
|
|
fetchPromptContent(); |
|
}, [selectedPrompt]); |
|
|
|
const handleSaveChanges = () => { |
|
fetch(`${apiHost}/api/update_prompt`, { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ |
|
id: selectedPrompt.id, |
|
name: newPromptName, |
|
content: newPromptContent, |
|
}), |
|
}) |
|
.then((response) => { |
|
if (!response.ok) { |
|
throw new Error('Failed to update prompt'); |
|
} |
|
onSelectPrompt(newPromptName, selectedPrompt.id, selectedPrompt.type); |
|
setNewPromptName(newPromptName); |
|
}) |
|
.catch((error) => { |
|
console.error(error); |
|
}); |
|
}; |
|
|
|
return ( |
|
<div className="mt-[59px]"> |
|
<div className="mb-4"> |
|
<p className="font-semibold dark:text-bright-gray">Active Prompt</p> |
|
<DropdownPrompt |
|
options={prompts} |
|
selectedValue={selectedPrompt.name} |
|
onSelect={handleSelectPrompt} |
|
/> |
|
</div> |
|
|
|
<div className="mb-4"> |
|
<p className='dark:text-bright-gray'>Prompt name </p>{' '} |
|
<p className="mb-2 text-xs italic text-eerie-black dark:text-bright-gray"> |
|
start by editing name |
|
</p> |
|
<input |
|
type="text" |
|
value={newPromptName} |
|
placeholder="Active Prompt Name" |
|
className="w-full rounded-lg border-2 p-2 dark:border-chinese-silver dark:bg-transparent dark:text-white" |
|
onChange={(e) => setNewPromptName(e.target.value)} |
|
/> |
|
</div> |
|
|
|
<div className="mb-4"> |
|
<p className="mb-2 dark:text-bright-gray">Prompt content</p> |
|
<textarea |
|
className="h-32 w-full rounded-lg border-2 p-2 dark:border-chinese-silver dark:text-white dark:bg-transparent" |
|
value={newPromptContent} |
|
onChange={(e) => setNewPromptContent(e.target.value)} |
|
placeholder="Active prompt contents" |
|
/> |
|
</div> |
|
|
|
<div className="flex justify-between"> |
|
<button |
|
className={`rounded-lg bg-green-500 px-4 py-2 font-bold text-white transition-all hover:bg-green-700 ${newPromptName === selectedPrompt.name |
|
? 'cursor-not-allowed opacity-50' |
|
: '' |
|
}`} |
|
onClick={handleAddPrompt} |
|
disabled={newPromptName === selectedPrompt.name} |
|
> |
|
Add New Prompt |
|
</button> |
|
<button |
|
className={`rounded-lg bg-red-500 px-4 py-2 font-bold text-white transition-all hover:bg-red-700 ${selectedPrompt.type === 'public' |
|
? 'cursor-not-allowed opacity-50' |
|
: '' |
|
}`} |
|
onClick={handleDeletePrompt} |
|
disabled={selectedPrompt.type === 'public'} |
|
> |
|
Delete Prompt |
|
</button> |
|
<button |
|
className={`rounded-lg bg-blue-500 px-4 py-2 font-bold text-white transition-all hover:bg-blue-700 ${selectedPrompt.type === 'public' |
|
? 'cursor-not-allowed opacity-50' |
|
: '' |
|
}`} |
|
onClick={handleSaveChanges} |
|
disabled={selectedPrompt.type === 'public'} |
|
> |
|
Save Changes |
|
</button> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
function DropdownPrompt({ |
|
options, |
|
selectedValue, |
|
onSelect, |
|
}: { |
|
options: { name: string; id: string; type: string }[]; |
|
selectedValue: string; |
|
onSelect: (value: { name: string; id: string; type: string }) => void; |
|
}) { |
|
const [isOpen, setIsOpen] = useState(false); |
|
|
|
return ( |
|
<div className="relative mt-2 w-32"> |
|
<button |
|
onClick={() => setIsOpen(!isOpen)} |
|
className="flex w-full cursor-pointer items-center rounded-xl border-2 dark:border-chinese-silver bg-white p-3 dark:bg-transparent" |
|
> |
|
<span className="flex-1 overflow-hidden text-ellipsis dark:text-bright-gray"> |
|
{selectedValue} |
|
</span> |
|
<img |
|
src={Arrow2} |
|
alt="arrow" |
|
className={`transform ${isOpen ? 'rotate-180' : 'rotate-0' |
|
} h-3 w-3 transition-transform`} |
|
/> |
|
</button> |
|
{isOpen && ( |
|
<div className="absolute left-0 right-0 z-50 -mt-3 rounded-b-xl border-2 dark:border-chinese-silver bg-white dark:bg-dark-charcoal shadow-lg"> |
|
{options.map((option, index) => ( |
|
<div |
|
key={index} |
|
className="flex cursor-pointer items-center justify-between hover:bg-gray-100 dark:hover:bg-purple-taupe dark:text-bright-gray " |
|
> |
|
<span |
|
onClick={() => { |
|
onSelect(option); |
|
setIsOpen(false); |
|
}} |
|
className="ml-2 flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap py-3" |
|
> |
|
{option.name} |
|
</span> |
|
</div> |
|
))} |
|
</div> |
|
)} |
|
</div> |
|
); |
|
} |
|
|
|
function Dropdown({ |
|
options, |
|
selectedValue, |
|
onSelect, |
|
showDelete, |
|
onDelete, |
|
}: { |
|
options: string[]; |
|
selectedValue: string; |
|
onSelect: (value: string) => void; |
|
showDelete?: boolean; // optional |
|
onDelete?: (value: string) => void; // optional |
|
}) { |
|
const [isOpen, setIsOpen] = useState(false); |
|
|
|
return ( |
|
<div className="relative mt-2 w-32"> |
|
<button |
|
onClick={() => setIsOpen(!isOpen)} |
|
className="flex w-full cursor-pointer items-center rounded-xl border-2 dark:border-chinese-silver bg-white p-3 dark:bg-transparent" |
|
> |
|
<span className="flex-1 overflow-hidden text-ellipsis dark:text-bright-gray"> |
|
{selectedValue} |
|
</span> |
|
<img |
|
src={Arrow2} |
|
alt="arrow" |
|
className={`transform ${isOpen ? 'rotate-180' : 'rotate-0' |
|
} h-3 w-3 transition-transform`} |
|
/> |
|
</button> |
|
{isOpen && ( |
|
<div className="absolute left-0 right-0 z-50 -mt-3 rounded-b-xl border-2 bg-white dark:border-chinese-silver dark:bg-dark-charcoal shadow-lg"> |
|
{options.map((option, index) => ( |
|
<div |
|
key={index} |
|
className="flex cursor-pointer items-center justify-between hover:bg-gray-100 dark:hover:bg-purple-taupe" |
|
> |
|
<span |
|
onClick={() => { |
|
onSelect(option); |
|
setIsOpen(false); |
|
}} |
|
className="ml-2 flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap py-3 dark:text-light-gray" |
|
> |
|
{option} |
|
</span> |
|
{showDelete && onDelete && ( |
|
<button onClick={() => onDelete(option)} className="p-2"> |
|
{/* Icon or text for delete button */} |
|
Delete |
|
</button> |
|
)} |
|
</div> |
|
))} |
|
</div> |
|
)} |
|
</div> |
|
); |
|
} |
|
|
|
type AddPromptModalProps = { |
|
newPromptName: string; |
|
onNewPromptNameChange: (name: string) => void; |
|
onAddPrompt: () => void; |
|
onClose: () => void; |
|
}; |
|
|
|
const AddPromptModal: React.FC<AddPromptModalProps> = ({ |
|
newPromptName, |
|
onNewPromptNameChange, |
|
onAddPrompt, |
|
onClose, |
|
}) => { |
|
return ( |
|
<div className="fixed top-0 left-0 flex h-screen w-screen items-center justify-center bg-gray-900 bg-opacity-50"> |
|
<div className="rounded-3xl bg-white p-4"> |
|
<p className="mb-2 text-2xl font-bold text-jet">Add New Prompt</p> |
|
<input |
|
type="text" |
|
placeholder="Enter Prompt Name" |
|
value={newPromptName} |
|
onChange={(e) => onNewPromptNameChange(e.target.value)} |
|
className="mb-4 w-full rounded-3xl border-2 dark:border-chinese-silver p-2" |
|
/> |
|
<button |
|
onClick={onAddPrompt} |
|
className="rounded-3xl bg-purple-300 px-4 py-2 font-bold text-white transition-all hover:bg-purple-600" |
|
> |
|
Save |
|
</button> |
|
<button |
|
onClick={onClose} |
|
className="mt-4 rounded-3xl px-4 py-2 font-bold text-red-500" |
|
> |
|
Cancel |
|
</button> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
type DocumentsProps = { |
|
documents: Doc[] | null; |
|
handleDeleteDocument: (index: number, document: Doc) => void; |
|
}; |
|
|
|
const Documents: React.FC<DocumentsProps> = ({ |
|
documents, |
|
handleDeleteDocument, |
|
}) => { |
|
return ( |
|
<div className="mt-8"> |
|
<div className="flex flex-col"> |
|
{/* <h2 className="text-xl font-semibold">Documents</h2> */} |
|
|
|
<div className="mt-[27px] overflow-x-auto border dark:border-chinese-silver rounded-xl w-max"> |
|
<table className="block w-full table-auto content-center justify-center text-center dark:text-bright-gray" > |
|
<thead> |
|
<tr> |
|
<th className="border-r p-4 md:w-[244px]">Document Name</th> |
|
<th className="border-r px-4 py-2 w-[244px]">Vector Date</th> |
|
<th className="border-r px-4 py-2 w-[244px]">Type</th> |
|
<th className="px-4 py-2"></th> |
|
</tr> |
|
</thead> |
|
<tbody> |
|
{documents && |
|
documents.map((document, index) => ( |
|
<tr key={index}> |
|
<td className="border-r border-t px-4 py-2">{document.name}</td> |
|
<td className="border-r border-t px-4 py-2">{document.date}</td> |
|
<td className="border-r border-t px-4 py-2"> |
|
{document.location === 'remote' |
|
? 'Pre-loaded' |
|
: 'Private'} |
|
</td> |
|
<td className="border-t px-4 py-2"> |
|
{document.location !== 'remote' && ( |
|
<img |
|
src={Trash} |
|
alt="Delete" |
|
className="h-4 w-4 cursor-pointer hover:opacity-50" |
|
id={`img-${index}`} |
|
onClick={(event) => { |
|
event.stopPropagation(); |
|
handleDeleteDocument(index, document); |
|
}} |
|
/> |
|
)} |
|
</td> |
|
</tr> |
|
))} |
|
</tbody> |
|
</table> |
|
</div> |
|
{/* <button |
|
onClick={toggleAddDocumentModal} |
|
className="mt-10 w-32 rounded-lg bg-purple-300 px-4 py-2 font-bold text-white transition-all hover:bg-purple-600" |
|
> |
|
Add New |
|
</button> */} |
|
</div> |
|
|
|
{/* {isAddDocumentModalOpen && ( |
|
<AddDocumentModal |
|
newDocument={newDocument} |
|
onNewDocumentChange={setNewDocument} |
|
onAddDocument={addDocument} |
|
onClose={toggleAddDocumentModal} |
|
/> |
|
)} */} |
|
</div> |
|
); |
|
}; |
|
|
|
type Document = { |
|
name: string; |
|
vectorDate: string; |
|
vectorLocation: string; |
|
}; |
|
|
|
|
|
type AddDocumentModalProps = { |
|
newDocument: Document; |
|
onNewDocumentChange: (document: Document) => void; |
|
onAddDocument: () => void; |
|
onClose: () => void; |
|
}; |
|
|
|
const AddDocumentModal: React.FC<AddDocumentModalProps> = ({ |
|
newDocument, |
|
onNewDocumentChange, |
|
onAddDocument, |
|
onClose, |
|
}) => { |
|
return ( |
|
<div className="fixed top-0 left-0 flex h-screen w-screen items-center justify-center bg-gray-900 bg-opacity-50"> |
|
<div className="w-[50%] rounded-lg bg-white p-4"> |
|
<p className="mb-2 text-2xl font-bold text-jet">Add New Document</p> |
|
<input |
|
type="text" |
|
placeholder="Document Name" |
|
value={newDocument.name} |
|
onChange={(e) => |
|
onNewDocumentChange({ ...newDocument, name: e.target.value }) |
|
} |
|
className="mb-4 w-full rounded-lg border-2 p-2" |
|
/> |
|
<input |
|
type="text" |
|
placeholder="Vector Date" |
|
value={newDocument.vectorDate} |
|
onChange={(e) => |
|
onNewDocumentChange({ ...newDocument, vectorDate: e.target.value }) |
|
} |
|
className="mb-4 w-full rounded-lg border-2 p-2" |
|
/> |
|
<input |
|
type="text" |
|
placeholder="Vector Location" |
|
value={newDocument.vectorLocation} |
|
onChange={(e) => |
|
onNewDocumentChange({ |
|
...newDocument, |
|
vectorLocation: e.target.value, |
|
}) |
|
} |
|
className="mb-4 w-full rounded-lg border-2 p-2" |
|
/> |
|
<button |
|
onClick={onAddDocument} |
|
className="rounded-lg bg-purple-300 px-4 py-2 font-bold text-white transition-all hover:bg-purple-600" |
|
> |
|
Save |
|
</button> |
|
<button |
|
onClick={onClose} |
|
className="mt-4 rounded-lg px-4 py-2 font-bold text-red-500" |
|
> |
|
Cancel |
|
</button> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
const Widgets: React.FC<{ |
|
widgetScreenshot: File | null; |
|
onWidgetScreenshotChange: (screenshot: File | null) => void; |
|
}> = ({ widgetScreenshot, onWidgetScreenshotChange }) => { |
|
const widgetSources = ['Source 1', 'Source 2', 'Source 3']; |
|
const widgetMethods = ['Method 1', 'Method 2', 'Method 3']; |
|
const widgetTypes = ['Type 1', 'Type 2', 'Type 3']; |
|
|
|
const [selectedWidgetSource, setSelectedWidgetSource] = useState( |
|
widgetSources[0], |
|
); |
|
const [selectedWidgetMethod, setSelectedWidgetMethod] = useState( |
|
widgetMethods[0], |
|
); |
|
const [selectedWidgetType, setSelectedWidgetType] = useState(widgetTypes[0]); |
|
|
|
|
|
const [widgetCode, setWidgetCode] = useState<string>(''); |
|
|
|
const handleScreenshotChange = ( |
|
event: React.ChangeEvent<HTMLInputElement>, |
|
) => { |
|
const files = event.target.files; |
|
|
|
if (files && files.length > 0) { |
|
const selectedScreenshot = files[0]; |
|
onWidgetScreenshotChange(selectedScreenshot); |
|
} |
|
}; |
|
|
|
const handleCopyToClipboard = () => { |
|
|
|
const textArea = document.createElement('textarea'); |
|
textArea.value = widgetCode; |
|
document.body.appendChild(textArea); |
|
|
|
|
|
textArea.select(); |
|
document.execCommand('copy'); |
|
|
|
|
|
document.body.removeChild(textArea); |
|
}; |
|
|
|
return ( |
|
<div> |
|
<div className="mt-[59px]"> |
|
<p className="font-bold text-jet">Widget Source</p> |
|
<Dropdown |
|
options={widgetSources} |
|
selectedValue={selectedWidgetSource} |
|
onSelect={setSelectedWidgetSource} |
|
/> |
|
</div> |
|
<div className="mt-5"> |
|
<p className="font-bold text-jet">Widget Method</p> |
|
<Dropdown |
|
options={widgetMethods} |
|
selectedValue={selectedWidgetMethod} |
|
onSelect={setSelectedWidgetMethod} |
|
/> |
|
</div> |
|
<div className="mt-5"> |
|
<p className="font-bold text-jet">Widget Type</p> |
|
<Dropdown |
|
options={widgetTypes} |
|
selectedValue={selectedWidgetType} |
|
onSelect={setSelectedWidgetType} |
|
/> |
|
</div> |
|
<div className="mt-6"> |
|
<p className="font-bold text-jet">Widget Code Snippet</p> |
|
<textarea |
|
rows={4} |
|
value={widgetCode} |
|
onChange={(e) => setWidgetCode(e.target.value)} |
|
className="mt-3 w-full rounded-lg border-2 p-2" |
|
/> |
|
</div> |
|
<div className="mt-1"> |
|
<button |
|
onClick={handleCopyToClipboard} |
|
className="rounded-lg bg-blue-400 px-2 py-2 font-bold text-white transition-all hover:bg-blue-600" |
|
> |
|
Copy |
|
</button> |
|
</div> |
|
|
|
<div className="mt-4"> |
|
<p className="text-lg font-semibold">Widget Screenshot</p> |
|
<input type="file" accept="image/*" onChange={handleScreenshotChange} /> |
|
</div> |
|
|
|
{widgetScreenshot && ( |
|
<div className="mt-4"> |
|
<img |
|
src={URL.createObjectURL(widgetScreenshot)} |
|
alt="Widget Screenshot" |
|
className="max-w-full rounded-lg border border-gray-300" |
|
/> |
|
</div> |
|
)} |
|
</div> |
|
); |
|
}; |
|
|