From 823d2a966479b8efaf125205770b9a00c9e4f0cf Mon Sep 17 00:00:00 2001 From: Xavier Abad Date: Fri, 8 May 2026 13:14:07 +0200 Subject: [PATCH 1/4] feat(trash): add Original location column in trash --- src/app/drive/types/index.ts | 11 +++- src/app/i18n/locales/de.json | 3 +- src/app/i18n/locales/en.json | 3 +- src/app/i18n/locales/es.json | 3 +- src/app/i18n/locales/fr.json | 3 +- src/app/i18n/locales/it.json | 3 +- src/app/i18n/locales/ru.json | 3 +- src/app/i18n/locales/tw.json | 3 +- src/app/i18n/locales/zh.json | 3 +- .../DriveExplorerList/DriveExplorerList.tsx | 18 +++--- .../DriveExplorerListItem.tsx | 16 ++++- .../DriveExplorerList/getListHeaders.ts | 59 ++++++++++--------- 12 files changed, 77 insertions(+), 51 deletions(-) diff --git a/src/app/drive/types/index.ts b/src/app/drive/types/index.ts index 3d50fea367..2105bc233c 100644 --- a/src/app/drive/types/index.ts +++ b/src/app/drive/types/index.ts @@ -1,5 +1,6 @@ import { RenewalPeriod } from '@internxt/sdk/dist/drive/payments/types/types'; import { ShareLink } from '@internxt/sdk/dist/drive/share/types'; +import { FileStatus } from '@internxt/sdk/dist/drive/storage/types'; import { UserResumeData } from '@internxt/sdk/dist/drive/users/types'; import { AppSumoDetails } from '@internxt/sdk/dist/shared/types/appsumo'; import { AdvancedSharedItem } from 'app/share/types'; @@ -91,7 +92,13 @@ export interface DriveFileMetadataPayload { itemName: string; } -export type DriveItemData = DriveFileData & DriveFolderData; +export type DriveItemData = DriveFileData & + DriveFolderData & { + parent?: { + plainName: string; + status: FileStatus; + }; + }; export interface DriveItemPatch { name?: string; @@ -152,6 +159,8 @@ export enum FileViewMode { Grid = 'grid', } +export type ListHeaders = 'type' | 'name' | 'updatedAt' | 'size' | 'expiresAt'; + export enum DownloadFolderMethod { FileSystemAccessAPI = 'file-system-access-api', StreamSaver = 'stream-saver', diff --git a/src/app/i18n/locales/de.json b/src/app/i18n/locales/de.json index ec4b745f9d..5ffb6961ef 100644 --- a/src/app/i18n/locales/de.json +++ b/src/app/i18n/locales/de.json @@ -1536,7 +1536,8 @@ "modified": "Modifiziert", "size": "Größe", "actions": "Aktionen", - "autoDelete": "Automatisches Löschen" + "autoDelete": "Automatisches Löschen", + "originalLocation": "Ursprünglicher Speicherort" } }, "viewMode": { diff --git a/src/app/i18n/locales/en.json b/src/app/i18n/locales/en.json index 9dab096731..584f04510b 100644 --- a/src/app/i18n/locales/en.json +++ b/src/app/i18n/locales/en.json @@ -1618,7 +1618,8 @@ "modified": "Modified", "size": "Size", "actions": "Actions", - "autoDelete": "Auto-delete" + "autoDelete": "Auto-delete", + "originalLocation": "Original location" } }, "viewMode": { diff --git a/src/app/i18n/locales/es.json b/src/app/i18n/locales/es.json index d8b0db3d35..a94e4a03ff 100644 --- a/src/app/i18n/locales/es.json +++ b/src/app/i18n/locales/es.json @@ -1596,7 +1596,8 @@ "modified": "Modificado", "size": "Tamaño", "actions": "Acciones", - "autoDelete": "Eliminación automática" + "autoDelete": "Eliminación automática", + "originalLocation": "Ubicación original" } }, "viewMode": { diff --git a/src/app/i18n/locales/fr.json b/src/app/i18n/locales/fr.json index 2e6d8b00a3..057ec1da50 100644 --- a/src/app/i18n/locales/fr.json +++ b/src/app/i18n/locales/fr.json @@ -1542,7 +1542,8 @@ "modified": "Modifié", "size": "Taille", "actions": "Actions", - "autoDelete": "Suppression automatique" + "autoDelete": "Suppression automatique", + "originalLocation": "Emplacement d'origine" } }, "viewMode": { diff --git a/src/app/i18n/locales/it.json b/src/app/i18n/locales/it.json index 3f07c0a9f1..b600d7ccec 100644 --- a/src/app/i18n/locales/it.json +++ b/src/app/i18n/locales/it.json @@ -1649,7 +1649,8 @@ "modified": "Ultima modifica", "size": "Dimensione", "actions": "Azioni", - "autoDelete": "Eliminazione automatica" + "autoDelete": "Eliminazione automatica", + "originalLocation": "Posizione originale" } }, "viewMode": { diff --git a/src/app/i18n/locales/ru.json b/src/app/i18n/locales/ru.json index 647b13ca88..8df0601e85 100644 --- a/src/app/i18n/locales/ru.json +++ b/src/app/i18n/locales/ru.json @@ -1557,7 +1557,8 @@ "modified": "Изменено", "size": "Размер", "actions": "Действия", - "autoDelete": "Автоматическое удаление" + "autoDelete": "Автоматическое удаление", + "originalLocation": "Исходное расположение" } }, "viewMode": { diff --git a/src/app/i18n/locales/tw.json b/src/app/i18n/locales/tw.json index e6af4d31b7..3315952b8b 100644 --- a/src/app/i18n/locales/tw.json +++ b/src/app/i18n/locales/tw.json @@ -1548,7 +1548,8 @@ "modified": "修改日期", "size": "大小", "actions": "操作", - "autoDelete": "自動刪除" + "autoDelete": "自動刪除", + "originalLocation": "原始位置" } }, "viewMode": { diff --git a/src/app/i18n/locales/zh.json b/src/app/i18n/locales/zh.json index ecd7327116..2211eeb318 100644 --- a/src/app/i18n/locales/zh.json +++ b/src/app/i18n/locales/zh.json @@ -1583,7 +1583,8 @@ "modified": "修改日期", "size": "大小", "actions": "操作", - "autoDelete": "自动删除" + "autoDelete": "自动删除", + "originalLocation": "原始位置" } }, "viewMode": { diff --git a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx index 51fa6b6a04..ad8229d4ce 100644 --- a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx +++ b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx @@ -17,7 +17,7 @@ import { sharedThunks } from 'app/store/slices/sharedLinks'; import { storageActions } from 'app/store/slices/storage'; import { uiActions } from 'app/store/slices/ui'; import workspacesSelectors from 'app/store/slices/workspaces/workspaces.selectors'; -import { DriveItemData, DriveItemDetails } from 'app/drive/types'; +import { DriveItemData, DriveItemDetails, ListHeaders } from 'app/drive/types'; import EditItemNameDialog from 'app/drive/components/EditItemNameDialog/EditItemNameDialog'; import ShareWithTeamDialog from 'app/drive/components/ShareWithTeamDialog/ShareWithTeamDialog'; import DriveExplorerListItem from './DriveExplorerListItem'; @@ -59,9 +59,7 @@ interface DriveExplorerListProps { type ObjectWithId = { id: string | number }; -type SortField = 'type' | 'name' | 'updatedAt' | 'size' | 'expiresAt'; - -type ContextMenuDriveItem = DriveItemData | Pick | (ListShareLinksItem & { code: string }); +type ContextMenuDriveItem = DriveItemData | Pick | (ListShareLinksItem & { code: string }); function findUniqueItems(array1: T[], array2: T[]): T[] { const result: T[] = []; @@ -145,13 +143,13 @@ const DriveExplorerList: React.FC = memo((props) => { const isTrash = props.title === translate('trash.trash'); const skeleton = isTrash ? skinSkeletonTrash : skinSkeleton; - const sortBy = (value: { field: SortField; direction: 'ASC' | 'DESC' }) => { + const sortBy = (value: { field: Omit; direction: 'ASC' | 'DESC' }) => { let direction = OrderDirection.Asc; if (order.by === value.field) { direction = order.direction === OrderDirection.Desc ? OrderDirection.Asc : OrderDirection.Desc; } - dispatch(storageActions.setOrder({ by: value.field, direction })); + dispatch(storageActions.setOrder({ by: value.field as string, direction })); if (value.field === 'name') { if (isTrash) { @@ -200,7 +198,7 @@ const DriveExplorerList: React.FC = memo((props) => { ); const restoreItem = useCallback( - (item: DriveItemData | Pick) => { + (item: DriveItemData | Pick) => { dispatch(storageActions.setItemsToMove([item as DriveItemData])); dispatch(uiActions.setIsMoveItemsDialogOpen(true)); }, @@ -208,7 +206,7 @@ const DriveExplorerList: React.FC = memo((props) => { ); const deletePermanently = useCallback( - (item: DriveItemData | Pick) => { + (item: DriveItemData | Pick) => { dispatch(storageActions.setItemsToDelete([item as DriveItemData])); dispatch(uiActions.setIsDeleteItemsDialogOpen(true)); }, @@ -476,7 +474,7 @@ const DriveExplorerList: React.FC = memo((props) => { /> )} - + header={getListHeaders(translate, isRecents, isTrash)} checkboxDataCy="driveListHeaderCheckbox" disableKeyboardShortcuts={props.disableKeyboardShortcuts || props.showStopSharingConfirmation} @@ -515,7 +513,7 @@ const DriveExplorerList: React.FC = memo((props) => { }} onOrderByChanged={sortBy} orderBy={{ - field: props.order.by as SortField, + field: props.order.by as ListHeaders, direction: props.order.direction, }} onSelectedItemsChanged={onSelectedItemsChanged} diff --git a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerListItem.tsx b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerListItem.tsx index dc2edcbe9a..a81a3f83be 100644 --- a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerListItem.tsx +++ b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerListItem.tsx @@ -10,6 +10,7 @@ import { useDriveItemActions, useDriveItemDrag, useDriveItemDrop, useDriveItemSt import './DriveExplorerListItem.scss'; import { useTranslationContext } from 'app/i18n/provider/TranslationProvider'; import { WarningCircle } from '@phosphor-icons/react'; +import { FileStatus } from '@internxt/sdk/dist/drive/storage/types'; const getItemClassNames = (isSelected: boolean, isDraggingOver: boolean, isDragging: boolean): string => { const selectedClass = isSelected ? 'selected' : ''; @@ -86,6 +87,7 @@ const DriveExplorerListItem = ({ item, isTrash }: DriveExplorerItemProps): JSX.E const isItemShared = (item.sharings?.length ?? 0) > 0; const isInteractive = isItemInteractive(item); const itemClassNames = getItemClassNames(isItemSelected(item), isDraggingOverThisItem, isDraggingThisItem); + const parentFolderName = item.parent?.status === FileStatus.EXISTS ? (item.parent?.plainName ?? 'Drive') : '-'; const template = (
)} + {isTrash && ( +
+

{parentFolderName}

+
+ )} + {/* DATE */} -
- {dateService.formatDefaultDate(item.updatedAt, translate)} -
+ {!isTrash && ( +
+ {dateService.formatDefaultDate(item.updatedAt, translate)} +
+ )} {/* SIZE */}
diff --git a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/getListHeaders.ts b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/getListHeaders.ts index cbada6c410..1e8b81e392 100644 --- a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/getListHeaders.ts +++ b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/getListHeaders.ts @@ -1,19 +1,12 @@ -interface ListHeaderItem { - label: string; - width: string; - name: 'type' | 'name' | 'updatedAt' | 'size' | 'expiresAt'; - orderable: boolean; - defaultDirection: 'ASC' | 'DESC'; - buttonDataCy?: string; - textDataCy?: string; -} +import { DriveItemData, ListHeaders } from 'app/drive/types'; +import { HeaderProps } from '@internxt/ui/dist/components/data-display/list/ListHeader'; export const getListHeaders = ( translate: (key: string) => string, isRecents: boolean, isTrash: boolean, -): ListHeaderItem[] => { - const headers: ListHeaderItem[] = [ +): HeaderProps[] => { + const headers: HeaderProps[] = [ { label: translate('drive.list.columns.name'), width: 'flex grow items-center min-w-driveNameHeader', @@ -26,31 +19,39 @@ export const getListHeaders = ( ]; if (isTrash) { - headers.push({ - label: translate('drive.list.columns.autoDelete'), - width: 'w-date', - name: 'expiresAt', - orderable: true, - defaultDirection: 'ASC', - }); + headers.push( + { + label: translate('drive.list.columns.autoDelete'), + width: 'w-date', + name: 'expiresAt', + orderable: true, + defaultDirection: 'ASC', + }, + { + label: translate('drive.list.columns.originalLocation'), + width: 'w-date', + name: 'parent', + orderable: false, + }, + ); } - headers.push( - { + if (!isTrash) { + headers.push({ label: translate('drive.list.columns.modified'), width: 'w-date', name: 'updatedAt', orderable: !isRecents, defaultDirection: 'ASC', - }, - { - label: translate('drive.list.columns.size'), - orderable: false, - defaultDirection: 'ASC', - width: 'w-size', - name: 'size', - }, - ); + }); + } + + headers.push({ + label: translate('drive.list.columns.size'), + orderable: false, + width: 'w-size', + name: 'size', + }); return headers; }; From c381f413f1f37a4377d4e30f73986dfae415d1e6 Mon Sep 17 00:00:00 2001 From: Xavier Abad Date: Fri, 8 May 2026 13:19:51 +0200 Subject: [PATCH 2/4] refactor: update Sort Field type --- src/app/drive/types/index.ts | 2 +- .../DriveExplorerList/DriveExplorerList.tsx | 14 +++++++------- .../components/DriveExplorerList/getListHeaders.ts | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/app/drive/types/index.ts b/src/app/drive/types/index.ts index 2105bc233c..d95f9032b3 100644 --- a/src/app/drive/types/index.ts +++ b/src/app/drive/types/index.ts @@ -159,7 +159,7 @@ export enum FileViewMode { Grid = 'grid', } -export type ListHeaders = 'type' | 'name' | 'updatedAt' | 'size' | 'expiresAt'; +export type SortField = 'type' | 'name' | 'updatedAt' | 'size' | 'expiresAt'; export enum DownloadFolderMethod { FileSystemAccessAPI = 'file-system-access-api', diff --git a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx index ad8229d4ce..5a11f5a43d 100644 --- a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx +++ b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx @@ -17,7 +17,7 @@ import { sharedThunks } from 'app/store/slices/sharedLinks'; import { storageActions } from 'app/store/slices/storage'; import { uiActions } from 'app/store/slices/ui'; import workspacesSelectors from 'app/store/slices/workspaces/workspaces.selectors'; -import { DriveItemData, DriveItemDetails, ListHeaders } from 'app/drive/types'; +import { DriveItemData, DriveItemDetails, SortField } from 'app/drive/types'; import EditItemNameDialog from 'app/drive/components/EditItemNameDialog/EditItemNameDialog'; import ShareWithTeamDialog from 'app/drive/components/ShareWithTeamDialog/ShareWithTeamDialog'; import DriveExplorerListItem from './DriveExplorerListItem'; @@ -59,7 +59,7 @@ interface DriveExplorerListProps { type ObjectWithId = { id: string | number }; -type ContextMenuDriveItem = DriveItemData | Pick | (ListShareLinksItem & { code: string }); +type ContextMenuDriveItem = DriveItemData | Pick | (ListShareLinksItem & { code: string }); function findUniqueItems(array1: T[], array2: T[]): T[] { const result: T[] = []; @@ -143,7 +143,7 @@ const DriveExplorerList: React.FC = memo((props) => { const isTrash = props.title === translate('trash.trash'); const skeleton = isTrash ? skinSkeletonTrash : skinSkeleton; - const sortBy = (value: { field: Omit; direction: 'ASC' | 'DESC' }) => { + const sortBy = (value: { field: Omit; direction: 'ASC' | 'DESC' }) => { let direction = OrderDirection.Asc; if (order.by === value.field) { direction = order.direction === OrderDirection.Desc ? OrderDirection.Asc : OrderDirection.Desc; @@ -198,7 +198,7 @@ const DriveExplorerList: React.FC = memo((props) => { ); const restoreItem = useCallback( - (item: DriveItemData | Pick) => { + (item: DriveItemData | Pick) => { dispatch(storageActions.setItemsToMove([item as DriveItemData])); dispatch(uiActions.setIsMoveItemsDialogOpen(true)); }, @@ -206,7 +206,7 @@ const DriveExplorerList: React.FC = memo((props) => { ); const deletePermanently = useCallback( - (item: DriveItemData | Pick) => { + (item: DriveItemData | Pick) => { dispatch(storageActions.setItemsToDelete([item as DriveItemData])); dispatch(uiActions.setIsDeleteItemsDialogOpen(true)); }, @@ -474,7 +474,7 @@ const DriveExplorerList: React.FC = memo((props) => { /> )} - + header={getListHeaders(translate, isRecents, isTrash)} checkboxDataCy="driveListHeaderCheckbox" disableKeyboardShortcuts={props.disableKeyboardShortcuts || props.showStopSharingConfirmation} @@ -513,7 +513,7 @@ const DriveExplorerList: React.FC = memo((props) => { }} onOrderByChanged={sortBy} orderBy={{ - field: props.order.by as ListHeaders, + field: props.order.by as SortField, direction: props.order.direction, }} onSelectedItemsChanged={onSelectedItemsChanged} diff --git a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/getListHeaders.ts b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/getListHeaders.ts index 1e8b81e392..f5bb46c812 100644 --- a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/getListHeaders.ts +++ b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/getListHeaders.ts @@ -1,12 +1,12 @@ -import { DriveItemData, ListHeaders } from 'app/drive/types'; +import { DriveItemData, SortField } from 'app/drive/types'; import { HeaderProps } from '@internxt/ui/dist/components/data-display/list/ListHeader'; export const getListHeaders = ( translate: (key: string) => string, isRecents: boolean, isTrash: boolean, -): HeaderProps[] => { - const headers: HeaderProps[] = [ +): HeaderProps[] => { + const headers: HeaderProps[] = [ { label: translate('drive.list.columns.name'), width: 'flex grow items-center min-w-driveNameHeader', From 2f126f4a25fd2f5f3104a9b28bd4b1defb950d65 Mon Sep 17 00:00:00 2001 From: Xavier Abad Date: Fri, 8 May 2026 13:26:50 +0200 Subject: [PATCH 3/4] fix: wrong typing --- .../components/DriveExplorerList/DriveExplorerList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx index 5a11f5a43d..780c3a3767 100644 --- a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx +++ b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerList.tsx @@ -143,13 +143,13 @@ const DriveExplorerList: React.FC = memo((props) => { const isTrash = props.title === translate('trash.trash'); const skeleton = isTrash ? skinSkeletonTrash : skinSkeleton; - const sortBy = (value: { field: Omit; direction: 'ASC' | 'DESC' }) => { + const sortBy = (value: { field: SortField; direction: 'ASC' | 'DESC' }) => { let direction = OrderDirection.Asc; if (order.by === value.field) { direction = order.direction === OrderDirection.Desc ? OrderDirection.Asc : OrderDirection.Desc; } - dispatch(storageActions.setOrder({ by: value.field as string, direction })); + dispatch(storageActions.setOrder({ by: value.field, direction })); if (value.field === 'name') { if (isTrash) { From 872797bd25ca9f4a6437d6bc9dcc205cbffec260 Mon Sep 17 00:00:00 2001 From: Xavier Abad Date: Mon, 11 May 2026 12:31:08 +0200 Subject: [PATCH 4/4] fix: truncate long parent names --- .../components/DriveExplorerList/DriveExplorerListItem.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerListItem.tsx b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerListItem.tsx index a81a3f83be..73e646a41a 100644 --- a/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerListItem.tsx +++ b/src/views/Drive/components/DriveExplorer/components/DriveExplorerList/DriveExplorerListItem.tsx @@ -165,8 +165,8 @@ const DriveExplorerListItem = ({ item, isTrash }: DriveExplorerItemProps): JSX.E )} {isTrash && ( -
-

{parentFolderName}

+
+

{parentFolderName}

)}