From 6e4314175e8db169ad933f49f31299c199a9df4c Mon Sep 17 00:00:00 2001 From: Francis Terrero Date: Tue, 13 Jan 2026 19:26:02 -0400 Subject: [PATCH 1/5] feature: update @internxt/sdk to version 1.12.1 and enhance folder details in ItemDetailsDialog --- package.json | 2 +- .../ItemDetailsDialog/ItemDetailsDialog.tsx | 98 +++++++++++++------ .../components/ItemDetailsSkeleton.tsx | 5 +- src/app/drive/services/new-storage.service.ts | 7 ++ src/app/drive/types/index.ts | 1 + src/app/i18n/locales/en.json | 6 +- yarn.lock | 8 +- 7 files changed, 92 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 114f38ae6f..6d9dff9584 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@iconscout/react-unicons": "^1.1.6", "@internxt/css-config": "1.1.0", "@internxt/lib": "1.4.1", - "@internxt/sdk": "=1.11.17", + "@internxt/sdk": "=1.12.1", "@internxt/ui": "0.1.1", "@phosphor-icons/react": "^2.1.7", "@popperjs/core": "^2.11.6", diff --git a/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx b/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx index 95656f1597..bf1d1613c4 100644 --- a/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx +++ b/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx @@ -13,6 +13,8 @@ import { STORAGE_KEYS } from 'services/storage-keys'; import { DriveItemData, DriveItemDetails, ItemDetailsProps } from 'app/drive/types'; import newStorageService from 'app/drive/services/new-storage.service'; import errorService from 'services/error.service'; +import { FolderStatsResponse } from '@internxt/sdk/dist/drive/storage/types'; +import { ItemType } from '@internxt/sdk/dist/workspaces/types'; import ItemDetailsSkeleton from './components/ItemDetailsSkeleton'; import { AdvancedSharedItem } from 'app/share/types'; import { useSelector } from 'react-redux'; @@ -56,13 +58,33 @@ const ItemsDetails = ({ item, translate }: { item: ItemDetailsProps; translate: ); }; +async function getFolderStatsOrUndefined( + itemUuid: string, + isFolder: boolean, +): Promise { + if (!isFolder) return undefined; + + return newStorageService.getFolderStats(itemUuid).catch(() => undefined); +} + +function calculateItemSize(item: DriveItemDetails, folderStats: FolderStatsResponse | undefined): string | undefined { + if (!item.isFolder) { + return bytesToString(item.size); + } + if (folderStats?.totalSize !== undefined) { + return bytesToString(folderStats.totalSize, false); + } + return undefined; +} + /** * Return all the details of the item selected * The data is: * - Name * - Shared - * - Size (only for files) + * - Size (for files and folders) * - Type (only for files) + * - Number of files (only for folders) * - Uploaded * - Modified * - Uploaded by @@ -123,6 +145,42 @@ const ItemDetailsDialog = ({ onClose(); } + const MAX_DISPLAYABLE_FILE_COUNT = 1000; + + function formatFileCount(count: number | undefined): string | undefined { + if (count === undefined) return undefined; + if (count > MAX_DISPLAYABLE_FILE_COUNT) return translate('modals.itemDetailsModal.fileCountMoreThan1000'); + return translate('modals.itemDetailsModal.fileCount', { count }); + } + + async function getItemLocation( + item: DriveItemDetails, + itemType: ItemType, + itemUuid: string, + itemFolderUuid: string, + token: string | undefined, + ): Promise { + if (!isWorkspaceSelected) { + const ancestors = await newStorageService.getFolderAncestors(itemFolderUuid); + return getLocation(item, ancestors as unknown as DriveItemData[]); + } + + const itemCreatorUuid = item.user?.uuid; + const isUserOwner = itemCreatorUuid && user?.uuid === itemCreatorUuid; + + if (item.view === 'Drive' || (item.view === 'Shared' && isUserOwner)) { + const ancestors = await newStorageService.getFolderAncestorsInWorkspace( + workspaceSelected.workspace.id, + itemType, + itemUuid, + token, + ); + return getLocation(item, ancestors as unknown as DriveItemData[]); + } + + return '/Shared'; + } + async function getDetailsData( item: DriveItemDetails, isShared: string, @@ -130,45 +188,29 @@ const ItemDetailsDialog = ({ modified: string, email: string, ) { - const itemType = item.isFolder ? 'folder' : 'file'; + const itemType: ItemType = item.isFolder ? 'folder' : 'file'; const itemUuid = item.uuid; const itemFolderUuid = item.isFolder ? itemUuid : item.folderUuid; - const itemCreatorUuid = item.user?.uuid; - const isUserOwner = (itemCreatorUuid && user && user.uuid === itemCreatorUuid) || false; const storageKey = item.isFolder ? STORAGE_KEYS.FOLDER_ACCESS_TOKEN : STORAGE_KEYS.FILE_ACCESS_TOKEN; const token = localStorageService.get(storageKey) || undefined; - let location = ''; + const [location, folderStats] = await Promise.all([ + getItemLocation(item, itemType, itemUuid, itemFolderUuid, token), + getFolderStatsOrUndefined(itemUuid, item.isFolder), + ]); + const size = calculateItemSize(item, folderStats); - if (isWorkspaceSelected) { - if (item.view === 'Drive' || (item.view === 'Shared' && isUserOwner)) { - const ancestors = await newStorageService.getFolderAncestorsInWorkspace( - workspaceSelected.workspace.id, - itemType, - itemUuid, - token, - ); - location = getLocation(item, ancestors as unknown as DriveItemData[]); - } else { - location = '/Shared'; - } - } else { - const ancestors = await newStorageService.getFolderAncestors(itemFolderUuid); - location = getLocation(item, ancestors as unknown as DriveItemData[]); - } - - const details: ItemDetailsProps = { + return { name: item.name, shared: isShared, type: item.isFolder ? undefined : item.type, - size: item.isFolder ? undefined : bytesToString(item.size), - uploaded: uploaded, - modified: modified, + numberOfFiles: item.isFolder ? formatFileCount(folderStats?.fileCount) : undefined, + size, + uploaded, + modified, uploadedBy: item.user?.email ?? item.userEmail ?? email, location, }; - - return details; } return ( diff --git a/src/app/drive/components/ItemDetailsDialog/components/ItemDetailsSkeleton.tsx b/src/app/drive/components/ItemDetailsDialog/components/ItemDetailsSkeleton.tsx index 54c8e944a5..ca86799b4e 100644 --- a/src/app/drive/components/ItemDetailsDialog/components/ItemDetailsSkeleton.tsx +++ b/src/app/drive/components/ItemDetailsDialog/components/ItemDetailsSkeleton.tsx @@ -13,8 +13,11 @@ const ItemDetailsSkeleton = ({ shared: '', ...(!isFolder && { type: '', - size: '', }), + ...(isFolder && { + numberOfFiles: '', + }), + size: '', uploaded: '', modified: '', uploadedBy: '', diff --git a/src/app/drive/services/new-storage.service.ts b/src/app/drive/services/new-storage.service.ts index dd1bd04efe..d3a9c71f57 100644 --- a/src/app/drive/services/new-storage.service.ts +++ b/src/app/drive/services/new-storage.service.ts @@ -6,6 +6,7 @@ import { FolderAncestor, FolderMeta, FolderAncestorWorkspace, + FolderStatsResponse, } from '@internxt/sdk/dist/drive/storage/types'; import { SdkFactory } from 'app/core/factory/sdk'; import { RequestCanceler } from '@internxt/sdk/dist/shared/http/types'; @@ -37,6 +38,11 @@ export async function getFolderMeta(uuid: string, workspaceId?: string, resource return storageClient.getFolderMeta(uuid, workspaceId, resourcesToken); } +export async function getFolderStats(uuid: string): Promise { + const storageClient = SdkFactory.getNewApiInstance().createNewStorageClient(); + return storageClient.getFolderStats(uuid); +} + export async function checkDuplicatedFiles( folderUuid: string, filesList: FileStructure[], @@ -92,6 +98,7 @@ const newStorageService = { getFolderAncestors, getFolderAncestorsInWorkspace, getFolderMeta, + getFolderStats, checkDuplicatedFiles, checkDuplicatedFolders, getFolderContentByUuid, diff --git a/src/app/drive/types/index.ts b/src/app/drive/types/index.ts index 34dd3b9ebb..7c776af45f 100644 --- a/src/app/drive/types/index.ts +++ b/src/app/drive/types/index.ts @@ -171,4 +171,5 @@ export type ItemDetailsProps = { shared: string; type?: string; size?: string; + numberOfFiles?: string; }; diff --git a/src/app/i18n/locales/en.json b/src/app/i18n/locales/en.json index e1844a2e8c..1304fb5f9f 100644 --- a/src/app/i18n/locales/en.json +++ b/src/app/i18n/locales/en.json @@ -881,9 +881,13 @@ "uploadedBy": "Uploaded by", "shared": "Shared", "size": "Size", + "numberOfFiles": "Number of files", "modified": "Modified", "location": "Location" - } + }, + "fileCount_one": "{{count}} file", + "fileCount_other": "{{count}} files", + "fileCountMoreThan1000": "1000+ files" }, "shareModal": { "title": "Share \"{{name}}\"", diff --git a/yarn.lock b/yarn.lock index 927e9c5792..f42b4c1489 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1906,10 +1906,10 @@ version "1.0.2" resolved "https://codeload.github.com/internxt/prettier-config/tar.gz/9fa74e9a2805e1538b50c3809324f1c9d0f3e4f9" -"@internxt/sdk@=1.11.17": - version "1.11.17" - resolved "https://registry.yarnpkg.com/@internxt/sdk/-/sdk-1.11.17.tgz#2f5bdada5d3cbf5cfc685a21c24b5df3ff51d8c8" - integrity sha512-91iEUvZizlwX6KBEFJ3JdFiGrhMBQ9R54sTc3Pei9QtV2FYTU8nTVEPYAg39tLOGzT/kVuplYOtBxfk6wFtSDA== +"@internxt/sdk@=1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@internxt/sdk/-/sdk-1.12.1.tgz#fb3659eaf894d3fc21c5e622b41d1901ae2c9e17" + integrity sha512-JpPaGLOP3IAppjvLoDa8zQCvkDikFJosPeENzYHoqb66V+tp6zF7J6qdF0uyhFsR1gdr6FJMD2ymZJrWwoySxg== dependencies: axios "1.13.2" uuid "11.1.0" From 1560e315e93a691deff96231a21481d5330b77aa Mon Sep 17 00:00:00 2001 From: Francis Terrero Date: Tue, 13 Jan 2026 19:27:31 -0400 Subject: [PATCH 2/5] feature: add file count translations for multiple languages in i18n files --- src/app/i18n/locales/de.json | 8 ++++++-- src/app/i18n/locales/es.json | 6 +++++- src/app/i18n/locales/fr.json | 6 +++++- src/app/i18n/locales/it.json | 6 +++++- src/app/i18n/locales/ru.json | 8 +++++++- src/app/i18n/locales/tw.json | 6 +++++- src/app/i18n/locales/zh.json | 6 +++++- 7 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/app/i18n/locales/de.json b/src/app/i18n/locales/de.json index 22ef4311b3..5d0f2e33f3 100644 --- a/src/app/i18n/locales/de.json +++ b/src/app/i18n/locales/de.json @@ -784,9 +784,13 @@ "uploadedBy": "Hochgeladen von", "shared": "Freigegeben", "size": "Größe", + "numberOfFiles": "Anzahl der Dateien", "modified": "Geändert", - "Ort": "Ort" - } + "location": "Ort" + }, + "fileCount_one": "{{count}} Datei", + "fileCount_other": "{{count}} Dateien", + "fileCountMoreThan1000": "Mehr als 1000 Dateien" }, "shareModal": { "title": "Aktie \"{{name}}\"", diff --git a/src/app/i18n/locales/es.json b/src/app/i18n/locales/es.json index ee2eb3d0ff..f083818e62 100644 --- a/src/app/i18n/locales/es.json +++ b/src/app/i18n/locales/es.json @@ -863,9 +863,13 @@ "uploadedBy": "Subido por", "shared": "Compartido", "size": "Tamaño", + "numberOfFiles": "Número de archivos", "modified": "Modificado", "location": "Ubicación" - } + }, + "fileCount_one": "{{count}} archivo", + "fileCount_other": "{{count}} archivos", + "fileCountMoreThan1000": "Más de 1000 archivos" }, "shareModal": { "title": "Compartir \"{{name}}\"", diff --git a/src/app/i18n/locales/fr.json b/src/app/i18n/locales/fr.json index 3709a48908..14e5ea5989 100644 --- a/src/app/i18n/locales/fr.json +++ b/src/app/i18n/locales/fr.json @@ -805,9 +805,13 @@ "uploadedBy": "Téléchargé par", "shared": "Partagé", "size": "Taille", + "numberOfFiles": "Nombre de fichiers", "modified": "Modifié", "location": "Emplacement" - } + }, + "fileCount_one": "{{count}} fichier", + "fileCount_other": "{{count}} fichiers", + "fileCountMoreThan1000": "Plus de 1000 fichiers" }, "newFolderModal": { "title": "Nouveau dossier", diff --git a/src/app/i18n/locales/it.json b/src/app/i18n/locales/it.json index 9d37c04d25..49cc0092aa 100644 --- a/src/app/i18n/locales/it.json +++ b/src/app/i18n/locales/it.json @@ -918,8 +918,12 @@ "uploaded": "Caricato", "uploadedBy": "Caricato da", "shared": "Condiviso", + "numberOfFiles": "Numero di file", "location": "Posizione" - } + }, + "fileCount_one": "{{count}} file", + "fileCount_other": "{{count}} file", + "fileCountMoreThan1000": "Più di 1000 file" }, "shareModal": { "title": "Condividi \"{{name}}\"", diff --git a/src/app/i18n/locales/ru.json b/src/app/i18n/locales/ru.json index 91db029fbc..fc87c664e7 100644 --- a/src/app/i18n/locales/ru.json +++ b/src/app/i18n/locales/ru.json @@ -824,9 +824,15 @@ "uploadedBy": "Загружено", "shared": "Общий", "size": "Размер", + "numberOfFiles": "Количество файлов", "modified": "Изменено", "location": "Расположение" - } + }, + "fileCount_one": "{{count}} файл", + "fileCount_few": "{{count}} файла", + "fileCount_many": "{{count}} файлов", + "fileCount_other": "{{count}} файлов", + "fileCountMoreThan1000": "Более 1000 файлов" }, "shareModal": { "title": "Поделиться \"{{name}}\"", diff --git a/src/app/i18n/locales/tw.json b/src/app/i18n/locales/tw.json index eabbd975e4..d8a626b095 100644 --- a/src/app/i18n/locales/tw.json +++ b/src/app/i18n/locales/tw.json @@ -811,9 +811,13 @@ "uploadedBy": "上傳者", "shared": "已共享", "size": "大小", + "numberOfFiles": "文件數量", "modified": "修改時間", "location": "位置" - } + }, + "fileCount_one": "{{count}} 個文件", + "fileCount_other": "{{count}} 個文件", + "fileCountMoreThan1000": "超過 1000 個文件" }, "shareModal": { "title": "分享“{{name}}”", diff --git a/src/app/i18n/locales/zh.json b/src/app/i18n/locales/zh.json index 8bec6dc6e1..79bbd38d3a 100644 --- a/src/app/i18n/locales/zh.json +++ b/src/app/i18n/locales/zh.json @@ -847,9 +847,13 @@ "uploadedBy": "上传者", "shared": "共享", "size": "大小", + "numberOfFiles": "文件数量", "modified": "已修改", "location": "位置" - } + }, + "fileCount_one": "{{count}} 个文件", + "fileCount_other": "{{count}} 个文件", + "fileCountMoreThan1000": "超过 1000 个文件" }, "shareModal": { "title": "分享 \"{{name}}\"", From cb8074bd69711ad58959ca19b425f606a9c491cf Mon Sep 17 00:00:00 2001 From: Francis Terrero Date: Tue, 13 Jan 2026 21:59:26 -0400 Subject: [PATCH 3/5] test: add unit test for getFolderStats service method --- .../services/new-storage.service.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/app/drive/services/new-storage.service.test.ts b/src/app/drive/services/new-storage.service.test.ts index 2215ffd642..a27603770e 100644 --- a/src/app/drive/services/new-storage.service.test.ts +++ b/src/app/drive/services/new-storage.service.test.ts @@ -96,4 +96,24 @@ describe('newStorageService', () => { expect(mockGetFolderContentByUuid).toHaveBeenCalledWith(params); }); }); + + describe('Get Folder Statistics', () => { + test('When requesting folder statistics, then it returns file count and total size', async () => { + const mockUuid = 'test-folder-uuid'; + const mockStatsResponse = { + fileCount: 42, + totalSize: 1024000, + }; + const mockGetFolderStats = vi.fn().mockResolvedValue(mockStatsResponse); + const mockStorageClient = { getFolderStats: mockGetFolderStats }; + (SdkFactory.getNewApiInstance as Mock).mockReturnValue({ + createNewStorageClient: () => mockStorageClient, + }); + + const result = await newStorageService.getFolderStats(mockUuid); + + expect(mockGetFolderStats).toHaveBeenCalledWith(mockUuid); + expect(result).toEqual(mockStatsResponse); + }); + }); }); From 2e6a54a775a6a99744027ad672214435968ebdf8 Mon Sep 17 00:00:00 2001 From: Francis Terrero Date: Wed, 14 Jan 2026 09:10:31 -0400 Subject: [PATCH 4/5] refactor: convert function declarations to arrow functions in ItemDetailsDialog --- .../ItemDetailsDialog/ItemDetailsDialog.tsx | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx b/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx index bf1d1613c4..3eaee2054d 100644 --- a/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx +++ b/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx @@ -58,16 +58,10 @@ const ItemsDetails = ({ item, translate }: { item: ItemDetailsProps; translate: ); }; -async function getFolderStatsOrUndefined( - itemUuid: string, - isFolder: boolean, -): Promise { - if (!isFolder) return undefined; - - return newStorageService.getFolderStats(itemUuid).catch(() => undefined); -} - -function calculateItemSize(item: DriveItemDetails, folderStats: FolderStatsResponse | undefined): string | undefined { +const calculateItemSize = ( + item: DriveItemDetails, + folderStats: FolderStatsResponse | undefined, +): string | undefined => { if (!item.isFolder) { return bytesToString(item.size); } @@ -75,7 +69,7 @@ function calculateItemSize(item: DriveItemDetails, folderStats: FolderStatsRespo return bytesToString(folderStats.totalSize, false); } return undefined; -} +}; /** * Return all the details of the item selected @@ -136,30 +130,30 @@ const ItemDetailsDialog = ({ }, 300); }; - function formateDate(dateString: string) { + const formateDate = (dateString: string) => { return dateService.formatDefaultDate(dateString, translate); - } + }; - function handleButtonItemClick() { + const handleButtonItemClick = () => { onDetailsButtonClicked(item as AdvancedSharedItem); onClose(); - } + }; const MAX_DISPLAYABLE_FILE_COUNT = 1000; - function formatFileCount(count: number | undefined): string | undefined { + const formatFileCount = (count: number | undefined) => { if (count === undefined) return undefined; if (count > MAX_DISPLAYABLE_FILE_COUNT) return translate('modals.itemDetailsModal.fileCountMoreThan1000'); return translate('modals.itemDetailsModal.fileCount', { count }); - } + }; - async function getItemLocation( + const getItemLocation = async ( item: DriveItemDetails, itemType: ItemType, itemUuid: string, itemFolderUuid: string, token: string | undefined, - ): Promise { + ) => { if (!isWorkspaceSelected) { const ancestors = await newStorageService.getFolderAncestors(itemFolderUuid); return getLocation(item, ancestors as unknown as DriveItemData[]); @@ -179,24 +173,26 @@ const ItemDetailsDialog = ({ } return '/Shared'; - } + }; - async function getDetailsData( + const getDetailsData = async ( item: DriveItemDetails, isShared: string, uploaded: string, modified: string, email: string, - ) { + ) => { const itemType: ItemType = item.isFolder ? 'folder' : 'file'; const itemUuid = item.uuid; const itemFolderUuid = item.isFolder ? itemUuid : item.folderUuid; const storageKey = item.isFolder ? STORAGE_KEYS.FOLDER_ACCESS_TOKEN : STORAGE_KEYS.FILE_ACCESS_TOKEN; const token = localStorageService.get(storageKey) || undefined; + const folderStatsPromise = item.isFolder ? newStorageService.getFolderStats(itemUuid) : undefined; + const [location, folderStats] = await Promise.all([ getItemLocation(item, itemType, itemUuid, itemFolderUuid, token), - getFolderStatsOrUndefined(itemUuid, item.isFolder), + folderStatsPromise, ]); const size = calculateItemSize(item, folderStats); @@ -211,7 +207,7 @@ const ItemDetailsDialog = ({ uploadedBy: item.user?.email ?? item.userEmail ?? email, location, }; - } + }; return ( From 2de24318e6a399c28866ab8c246440113d9aa45c Mon Sep 17 00:00:00 2001 From: Francis Terrero Date: Wed, 14 Jan 2026 11:49:31 -0400 Subject: [PATCH 5/5] feature: add getFolderStats function to retrieve folder statistics in ItemDetailsDialog --- .../components/ItemDetailsDialog/ItemDetailsDialog.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx b/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx index 3eaee2054d..1aa62c6f8b 100644 --- a/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx +++ b/src/app/drive/components/ItemDetailsDialog/ItemDetailsDialog.tsx @@ -147,6 +147,10 @@ const ItemDetailsDialog = ({ return translate('modals.itemDetailsModal.fileCount', { count }); }; + const getFolderStats = (item: DriveItemDetails, itemUuid: string) => { + return item.isFolder ? newStorageService.getFolderStats(itemUuid) : undefined; + }; + const getItemLocation = async ( item: DriveItemDetails, itemType: ItemType, @@ -188,11 +192,9 @@ const ItemDetailsDialog = ({ const storageKey = item.isFolder ? STORAGE_KEYS.FOLDER_ACCESS_TOKEN : STORAGE_KEYS.FILE_ACCESS_TOKEN; const token = localStorageService.get(storageKey) || undefined; - const folderStatsPromise = item.isFolder ? newStorageService.getFolderStats(itemUuid) : undefined; - const [location, folderStats] = await Promise.all([ getItemLocation(item, itemType, itemUuid, itemFolderUuid, token), - folderStatsPromise, + getFolderStats(item, itemUuid), ]); const size = calculateItemSize(item, folderStats);