mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-15 06:58:51 +01:00
* 🎨 feat: Enhance Import Conversations UI with loading state and new localization key * fix: Correct pluralization in selected items message in translation.json * Refactor Chat Input File Table Headers to Use SortFilterHeader Component - Replaced button-based sorting headers in the Chat Input Files Table with a new SortFilterHeader component for better code organization and consistency. - Updated the header for filename, updatedAt, and bytes columns to utilize the new component. Enhance Navigation Component with Skeleton Loading States - Added Skeleton loading states to the Nav component for better user experience during data fetching. - Updated Suspense fallbacks for AgentMarketplaceButton and BookmarkNav components to display Skeletons. Refactor Avatar Component for Improved UI - Enhanced the Avatar component by adding a Label for drag-and-drop functionality. - Improved styling and structure for the file upload area. Update Shared Links Component for Better Error Handling and Sorting - Improved error handling in the Shared Links component for fetching next pages and deleting shared links. - Simplified the header rendering for sorting columns and added sorting functionality to the title and createdAt columns. Refactor Archived Chats Component - Merged ArchivedChats and ArchivedChatsTable components into a single ArchivedChats component for better maintainability. - Implemented sorting and searching functionality with debouncing for improved performance. - Enhanced the UI with better loading states and error handling. Update DataTable Component for Sorting Icons - Added sorting icons (ChevronUp, ChevronDown, ChevronsUpDown) to the DataTable headers for better visual feedback on sorting state. Localization Updates - Updated translation.json to fix missing translations and improve existing ones for better user experience. * ✨ feat: Update DataTable component to streamline props and enhance sorting icons * fix: TS issues * feat: polish and redefine DataTable + shared links and archived chats * feat: enhance DataTable with column pinning and improve sorting functionality * feat: enhance deepEqual function for array support and improve column style stability * refactor: DataTable and ArchivedChats; fix: sorting ArchivedChats API * feat(DataTable): Implement new DataTable component with hooks and optimized features - Added DataTable component with support for virtual scrolling, row selection, and customizable columns. - Introduced hooks for debouncing search input, managing row selection, and calculating column styles. - Enhanced accessibility with keyboard navigation and selection checkboxes. - Implemented skeleton loading state for better user experience during data fetching. - Added DataTableSearch component for filtering data with debounced input. - Created utility logger for improved debugging in development. - Updated translations to support new UI elements and actions. * refactor: update SharedLinks and ArchivedChats to use desktopOnly instead of hideOnMobile; remove unused DataTableColumnHeader component * fix: ensure desktopOnly columns are hidden on mobile in DataTable * refactor: reorganize imports in DataTable components and update index exports * refactor: improve styling and animations in Artifacts, ArtifactsSubMenu, and MCPSubMenu components; update border-radius in style.css * refactor(Artifacts): enhance button toggle functionality and manage expanded state with useEffect * refactor: comment out desktopOnly property in SharedLinks and ArchivedChats components; update translation.json with new keys for link actions * refactor(DataTable): streamline column visibility logic and enhance type definitions; improve cleanup timers and optimize rendering * refactor(DataTable): enhance type definitions for processed data rows and update custom actions renderer type * refactor(DataTable): optimize processed data handling and improve warning for missing IDs; streamline DataTableComponents imports * refactor(DataTable): enhance accessibility features and improve localization for selection and loading states * refactor: improve padding in dialog content and enhance row selection functionality in ArchivedChats and DataTable components * refactor(DataTable): remove unnecessary role and tabindex attributes from select all button for improved accessibility * refactor(translation): remove outdated error messages and unused UI strings for cleaner localization * refactor(DataTable): enhance virtualization and scrolling performance with dynamic overscan adjustments * refactor(DataTableErrorBoundary): enhance error handling and localization support * refactor(DataTable): improve column sizing and visibility handling; remove deprecated features * refactor: enhance UI components with improved class handling and state management * refactor(DataTable): improve column width handling and responsiveness; disable row selection * refactor(DataTable): enhance accessibility with row header support and improve column visibility handling * chore(DataTable): comments update * refactor(Table): add unwrapped prop for direct table rendering; adjust minWidth calculation for responsiveness * refactor(DataTable): simplify search handling by removing unnecessary trimming; adjust column width handling for better responsiveness * refactor(translation): remove redundant drag and drop UI text for clarity * refactor(parsers): change uiResources to a constant and streamline artifacts handling * chore: remove unused file, bump @librechat/client to 0.3.2; fix(SharedLinks): missing import; * refactor: change button variant from destructive to ghost for delete actions in SharedLinks and ArchivedChats components * refactor(DataTable): simplify aria-sort assignment for better readability * refactor(DataTable): update aria-label and ariaLabel to use indexed placeholder for localization * refactor(translation): update no data messages for consistency * Refactor code structure for improved readability and maintainability * chore: restore linting fixes * chore: restore linting fixes 2; refactor: remove unused translation keys * feat(tests): add unit tests for DataTable components and error handling - Implement tests for SelectionCheckbox and SkeletonRows components in DataTable. - Add tests for DataTableErrorBoundary to ensure proper error handling and UI rendering. - Create tests for DataTableSearch to validate search functionality and accessibility. - Update DialogTemplate tests to reflect hardcoded cancel text. - Remove redundant IntersectionObserver mock in SplitText tests. - Unmock react-i18next in Translation tests to validate actual i18n functionality. * refactor: Remove jest-environment-jsdom dependency from package.json; fix: reset package-lock * chore: revert lint fixes * chore: clean up package.json by removing unused devDependencies and redundant test scripts * chore: update package dependencies in package.json and package-lock.json - Added new devDependencies: @babel/core, @babel/preset-env, @babel/preset-react, @babel/preset-typescript, @tanstack/react-table, @tanstack/react-virtual, @testing-library/jest-dom, identity-obj-proxy, jest, jest-environment-jsdom, and lucide-react. - Updated existing devDependencies to their latest versions. - Added new module @asamuzakjp/css-color to package-lock.json with its dependencies. - Updated version of @babel/plugin-transform-destructuring and added @babel/plugin-transform-explicit-resource-management in package-lock.json. --------- Co-authored-by: Danny Avila <danny@librechat.ai>
188 lines
5.7 KiB
TypeScript
188 lines
5.7 KiB
TypeScript
/* eslint-disable react-hooks/rules-of-hooks */
|
|
import { Database } from 'lucide-react';
|
|
import { FileSources, FileContext } from 'librechat-data-provider';
|
|
import { Checkbox, OpenAIMinimalIcon, AzureMinimalIcon, useMediaQuery } from '@librechat/client';
|
|
import type { ColumnDef } from '@tanstack/react-table';
|
|
import type { TFile } from 'librechat-data-provider';
|
|
import ImagePreview from '~/components/Chat/Input/Files/ImagePreview';
|
|
import FilePreview from '~/components/Chat/Input/Files/FilePreview';
|
|
import { TranslationKeys, useLocalize } from '~/hooks';
|
|
import { SortFilterHeader } from './SortFilterHeader';
|
|
import { formatDate, getFileType } from '~/utils';
|
|
|
|
const contextMap: Record<any, TranslationKeys> = {
|
|
[FileContext.avatar]: 'com_ui_avatar',
|
|
[FileContext.unknown]: 'com_ui_unknown',
|
|
[FileContext.assistants]: 'com_ui_assistants',
|
|
[FileContext.image_generation]: 'com_ui_image_gen',
|
|
[FileContext.assistants_output]: 'com_ui_assistants_output',
|
|
[FileContext.message_attachment]: 'com_ui_attachment',
|
|
};
|
|
|
|
export const columns: ColumnDef<TFile>[] = [
|
|
{
|
|
id: 'select',
|
|
header: ({ table }) => {
|
|
return (
|
|
<Checkbox
|
|
checked={
|
|
table.getIsAllPageRowsSelected() ||
|
|
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
|
}
|
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
|
aria-label="Select all"
|
|
className="flex"
|
|
/>
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
return (
|
|
<Checkbox
|
|
checked={row.getIsSelected()}
|
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
|
aria-label="Select row"
|
|
className="flex"
|
|
/>
|
|
);
|
|
},
|
|
enableSorting: false,
|
|
enableHiding: false,
|
|
},
|
|
{
|
|
meta: {
|
|
size: '150px',
|
|
},
|
|
accessorKey: 'filename',
|
|
header: ({ column }) => {
|
|
const localize = useLocalize();
|
|
return <SortFilterHeader column={column} title={localize('com_ui_name')} aria-hidden="true" />;
|
|
},
|
|
cell: ({ row }) => {
|
|
const file = row.original;
|
|
if (file.type?.startsWith('image')) {
|
|
return (
|
|
<div className="flex gap-2">
|
|
<ImagePreview
|
|
url={file.filepath}
|
|
className="relative h-10 w-10 shrink-0 overflow-visible rounded-md"
|
|
source={file.source}
|
|
/>
|
|
<span className="self-center truncate">{file.filename}</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const fileType = getFileType(file.type);
|
|
return (
|
|
<div className="flex gap-2">
|
|
{fileType && <FilePreview fileType={fileType} className="relative" file={file} />}
|
|
<span className="self-center truncate">{file.filename}</span>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'updatedAt',
|
|
header: ({ column }) => {
|
|
const localize = useLocalize();
|
|
return <SortFilterHeader column={column} title={localize('com_ui_date')} aria-hidden="true" />;
|
|
},
|
|
cell: ({ row }) => {
|
|
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
|
return formatDate(row.original.updatedAt?.toString() ?? '', isSmallScreen);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'filterSource',
|
|
header: ({ column }) => {
|
|
const localize = useLocalize();
|
|
return (
|
|
<SortFilterHeader
|
|
column={column}
|
|
title={localize('com_ui_storage')}
|
|
filters={{
|
|
Storage: Object.values(FileSources).filter(
|
|
(value) =>
|
|
value === FileSources.local ||
|
|
value === FileSources.openai ||
|
|
value === FileSources.azure,
|
|
),
|
|
}}
|
|
valueMap={{
|
|
[FileSources.azure]: 'com_ui_azure',
|
|
[FileSources.openai]: 'com_ui_openai',
|
|
[FileSources.local]: 'com_ui_host',
|
|
}}
|
|
/>
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
const localize = useLocalize();
|
|
const { source } = row.original;
|
|
if (source === FileSources.openai) {
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<OpenAIMinimalIcon className="icon-sm text-green-600/50" />
|
|
{'OpenAI'}
|
|
</div>
|
|
);
|
|
} else if (source === FileSources.azure) {
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<AzureMinimalIcon className="icon-sm text-cyan-700" />
|
|
{'Azure'}
|
|
</div>
|
|
);
|
|
}
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<Database className="icon-sm text-cyan-700" aria-hidden="true" />
|
|
{localize('com_ui_host')}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'context',
|
|
header: ({ column }) => {
|
|
const localize = useLocalize();
|
|
return (
|
|
<SortFilterHeader
|
|
column={column}
|
|
title={localize('com_ui_context')}
|
|
filters={{
|
|
Context: Object.values(FileContext).filter(
|
|
(value) => value === FileContext[value ?? ''],
|
|
),
|
|
}}
|
|
valueMap={contextMap}
|
|
/>
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
const { context } = row.original;
|
|
const localize = useLocalize();
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
{localize(contextMap[context ?? FileContext.unknown])}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'bytes',
|
|
header: ({ column }) => {
|
|
const localize = useLocalize();
|
|
return <SortFilterHeader column={column} title={localize('com_ui_size')} aria-hidden="true" />;
|
|
},
|
|
cell: ({ row }) => {
|
|
const suffix = ' MB';
|
|
const value = Number((Number(row.original.bytes) / 1024 / 1024).toFixed(2));
|
|
if (value < 0.01) {
|
|
return '< 0.01 MB';
|
|
}
|
|
|
|
return `${value}${suffix}`;
|
|
},
|
|
},
|
|
];
|