| | import React, { useState, useEffect } from 'react'; |
| | import { |
| | OGDialog, |
| | OGDialogTitle, |
| | OGDialogPortal, |
| | OGDialogOverlay, |
| | OGDialogContent, |
| | } from '@librechat/client'; |
| | import type { SharePointBatchProgress } from '~/data-provider/Files/sharepoint'; |
| | import { useSharePointPicker, useLocalize } from '~/hooks'; |
| |
|
| | interface SharePointPickerDialogProps { |
| | isOpen: boolean; |
| | onOpenChange: (open: boolean) => void; |
| | onFilesSelected?: (files: any[]) => void; |
| | disabled?: boolean; |
| | isDownloading?: boolean; |
| | downloadProgress?: SharePointBatchProgress | null; |
| | maxSelectionCount?: number; |
| | } |
| |
|
| | export default function SharePointPickerDialog({ |
| | isOpen, |
| | onOpenChange, |
| | onFilesSelected, |
| | disabled = false, |
| | isDownloading = false, |
| | downloadProgress = null, |
| | maxSelectionCount, |
| | }: SharePointPickerDialogProps) { |
| | const [containerNode, setContainerNode] = useState<HTMLDivElement | null>(null); |
| | const localize = useLocalize(); |
| |
|
| | const { openSharePointPicker, closeSharePointPicker, cleanup } = useSharePointPicker({ |
| | containerNode, |
| | onFilesSelected, |
| | disabled, |
| | onClose: () => handleOpenChange(false), |
| | maxSelectionCount, |
| | }); |
| | const handleOpenChange = (open: boolean) => { |
| | if (!open) { |
| | closeSharePointPicker(); |
| | } |
| | onOpenChange(open); |
| | }; |
| | |
| | const containerCallbackRef = React.useCallback((node: HTMLDivElement | null) => { |
| | setContainerNode(node); |
| | }, []); |
| |
|
| | useEffect(() => { |
| | if (containerNode && isOpen) { |
| | openSharePointPicker(); |
| | } |
| | return () => { |
| | if (!isOpen) { |
| | cleanup(); |
| | } |
| | }; |
| | |
| | |
| | }, [containerNode, isOpen]); |
| | return ( |
| | <OGDialog open={isOpen} onOpenChange={handleOpenChange}> |
| | <OGDialogPortal> |
| | <OGDialogOverlay className="bg-black/50" /> |
| | <OGDialogContent |
| | className="sharepoint-picker-bg fixed left-1/2 top-1/2 z-50 h-[680px] max-h-[90vh] max-w-[90vw] -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-surface-primary p-2 shadow-lg focus:outline-none" |
| | showCloseButton={true} |
| | > |
| | <OGDialogTitle className="sr-only"> |
| | {localize('com_files_sharepoint_picker_title')} |
| | </OGDialogTitle> |
| | <div ref={containerCallbackRef} className="sharepoint-picker-bg relative flex p-2"> |
| | {/* SharePoint iframe will be injected here by the hook */} |
| | |
| | {isDownloading && ( |
| | <div className="absolute inset-0 z-10 flex items-center justify-center rounded-lg bg-black/30 backdrop-blur-sm"> |
| | <div className="mx-4 w-full max-w-sm rounded-lg bg-surface-primary p-6 shadow-lg"> |
| | <div className="text-center"> |
| | <div className="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-b-2 border-blue-600"></div> |
| | <h3 className="mb-2 text-lg font-semibold text-text-primary"> |
| | {localize('com_files_downloading')} |
| | </h3> |
| | {downloadProgress && ( |
| | <div className="space-y-2"> |
| | <p className="text-sm text-text-secondary"> |
| | {localize('com_files_download_progress', { |
| | 0: downloadProgress.completed, |
| | 1: downloadProgress.total, |
| | })} |
| | </p> |
| | {downloadProgress.currentFile && ( |
| | <p className="truncate text-xs text-text-tertiary"> |
| | {downloadProgress.currentFile} |
| | </p> |
| | )} |
| | <div className="h-2 w-full rounded-full bg-surface-tertiary"> |
| | <div |
| | className="h-2 rounded-full bg-blue-600 transition-all duration-300" |
| | style={{ |
| | width: `${Math.round((downloadProgress.completed / downloadProgress.total) * 100)}%`, |
| | }} |
| | ></div> |
| | </div> |
| | <p className="text-xs text-text-tertiary"> |
| | {localize('com_files_download_percent_complete', { |
| | 0: Math.round( |
| | (downloadProgress.completed / downloadProgress.total) * 100, |
| | ), |
| | })} |
| | </p> |
| | {downloadProgress.failed.length > 0 && ( |
| | <p className="text-xs text-red-500"> |
| | {localize('com_files_download_failed', { |
| | 0: downloadProgress.failed.length, |
| | })} |
| | </p> |
| | )} |
| | </div> |
| | )} |
| | {!downloadProgress && ( |
| | <p className="text-sm text-text-secondary"> |
| | {localize('com_files_preparing_download')} |
| | </p> |
| | )} |
| | </div> |
| | </div> |
| | </div> |
| | )} |
| | </div> |
| | </OGDialogContent> |
| | </OGDialogPortal> |
| | </OGDialog> |
| | ); |
| | } |
| |
|