Zhitomir's picture
chat init
aa2e6af
Raw
History Blame Contribute Delete
7.82 kB
import filenamify from 'filenamify';
import { useEffect, useState } from 'react';
import type { TConversation } from 'librechat-data-provider';
import { Dialog, DialogButton, Input, Label, Checkbox, Dropdown } from '~/components/ui';
import { useLocalize, useExportConversation } from '~/hooks';
import DialogTemplate from '~/components/ui/DialogTemplate';
import { cn, defaultTextProps } from '~/utils';
export default function ExportModal({
open,
onOpenChange,
conversation,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
conversation: TConversation | null;
}) {
const localize = useLocalize();
const [filename, setFileName] = useState('');
const [type, setType] = useState('Select a file type');
const [includeOptions, setIncludeOptions] = useState<boolean | 'indeterminate'>(true);
const [exportBranches, setExportBranches] = useState<boolean | 'indeterminate'>(false);
const [recursive, setRecursive] = useState<boolean | 'indeterminate'>(true);
const typeOptions = [
{ value: 'screenshot', display: 'screenshot (.png)' },
{ value: 'text', display: 'text (.txt)' },
{ value: 'markdown', display: 'markdown (.md)' },
{ value: 'json', display: 'json (.json)' },
{ value: 'csv', display: 'csv (.csv)' },
];
useEffect(() => {
setFileName(filenamify(String(conversation?.title || 'file')));
setType('screenshot');
setIncludeOptions(true);
setExportBranches(false);
setRecursive(true);
}, [conversation?.title, open]);
const _setType = (newType: string) => {
const exportBranchesSupport = newType === 'json' || newType === 'csv' || newType === 'webpage';
const exportOptionsSupport = newType !== 'csv' && newType !== 'screenshot';
setExportBranches(exportBranchesSupport);
setIncludeOptions(exportOptionsSupport);
setType(newType);
};
const exportBranchesSupport = type === 'json' || type === 'csv' || type === 'webpage';
const exportOptionsSupport = type !== 'csv' && type !== 'screenshot';
const { exportConversation } = useExportConversation({
conversation,
filename,
type,
includeOptions,
exportBranches,
recursive,
});
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogTemplate
title={localize('com_nav_export_conversation')}
className="max-w-full sm:max-w-2xl"
main={
<div className="flex w-full flex-col items-center gap-6">
<div className="grid w-full gap-6 sm:grid-cols-2">
<div className="col-span-1 flex flex-col items-start justify-start gap-2">
<Label htmlFor="filename" className="text-left text-sm font-medium">
{localize('com_nav_export_filename')}
</Label>
<Input
id="filename"
value={filename}
onChange={(e) => setFileName(filenamify(e.target.value || ''))}
placeholder={localize('com_nav_export_filename_placeholder')}
className={cn(
defaultTextProps,
'flex h-10 max-h-10 w-full resize-none px-3 py-2',
)}
/>
</div>
<div className="col-span-1 flex w-full flex-col items-start justify-start gap-2">
<Label htmlFor="type" className="text-left text-sm font-medium">
{localize('com_nav_export_type')}
</Label>
<Dropdown value={type} onChange={_setType} options={typeOptions} />
</div>
</div>
<div className="grid w-full gap-6 sm:grid-cols-2">
<div className="col-span-1 flex flex-col items-start justify-start gap-2">
<div className="grid w-full items-center gap-2">
<Label htmlFor="includeOptions" className="text-left text-sm font-medium">
{localize('com_nav_export_include_endpoint_options')}
</Label>
<div className="flex h-[40px] w-full items-center space-x-3">
<Checkbox
id="includeOptions"
disabled={!exportOptionsSupport}
checked={includeOptions}
className="focus:ring-opacity-20 dark:border-gray-500 dark:bg-gray-700 dark:text-gray-50 dark:focus:ring-gray-600 dark:focus:ring-opacity-50 dark:focus:ring-offset-0"
onCheckedChange={setIncludeOptions}
/>
<label
htmlFor="includeOptions"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-gray-50"
>
{exportOptionsSupport
? localize('com_nav_enabled')
: localize('com_nav_not_supported')}
</label>
</div>
</div>
</div>
<div className="grid w-full items-center gap-2">
<Label htmlFor="exportBranches" className="text-left text-sm font-medium">
{localize('com_nav_export_all_message_branches')}
</Label>
<div className="flex h-[40px] w-full items-center space-x-3">
<Checkbox
id="exportBranches"
disabled={!exportBranchesSupport}
checked={exportBranches}
className="focus:ring-opacity-20 dark:border-gray-500 dark:bg-gray-700 dark:text-gray-50 dark:focus:ring-gray-600 dark:focus:ring-opacity-50 dark:focus:ring-offset-0"
onCheckedChange={setExportBranches}
/>
<label
htmlFor="exportBranches"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-gray-50"
>
{exportBranchesSupport
? localize('com_nav_enabled')
: localize('com_nav_not_supported')}
</label>
</div>
</div>
{type === 'json' ? (
<div className="grid w-full items-center gap-2">
<Label htmlFor="recursive" className="text-left text-sm font-medium">
{localize('com_nav_export_recursive_or_sequential')}
</Label>
<div className="flex h-[40px] w-full items-center space-x-3">
<Checkbox
id="recursive"
checked={recursive}
className="focus:ring-opacity-20 dark:border-gray-500 dark:bg-gray-700 dark:text-gray-50 dark:focus:ring-gray-600 dark:focus:ring-opacity-50 dark:focus:ring-offset-0"
onCheckedChange={setRecursive}
/>
<label
htmlFor="recursive"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-gray-50"
>
{localize('com_nav_export_recursive')}
</label>
</div>
</div>
) : null}
</div>
</div>
}
buttons={
<>
<DialogButton
onClick={exportConversation}
className="dark:hover:gray-400 border-gray-700 bg-green-500 text-white hover:bg-green-600 dark:hover:bg-green-600"
>
{localize('com_endpoint_export')}
</DialogButton>
</>
}
selection={undefined}
/>
</Dialog>
);
}