diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 030e967bd..a7db0d900 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,5 +1,6 @@ import type { Preview } from '@storybook/react' import { ThemeProvider } from '@iqss/dataverse-design-system' +import { MemoryRouter } from 'react-router-dom' const preview: Preview = { parameters: { @@ -14,7 +15,9 @@ const preview: Preview = { decorators: [ (Story) => ( - + + + ) ] diff --git a/merged-coverage/lcov.info b/merged-coverage/lcov.info index 9c8729e8c..8d7a2182a 100644 --- a/merged-coverage/lcov.info +++ b/merged-coverage/lcov.info @@ -7,7 +7,7 @@ DA:5,108 BRDA:1,0,0,108 BRDA:1,0,1,108 end_of_record -SF:src/sections/hello-dataverse/HelloDataverse.tsx +SF:src/sections/hello-dataverse/Home.tsx DA:5,76 DA:6,76 DA:8,38 diff --git a/public/locales/en/files.json b/public/locales/en/files.json index 98fc2b0bb..bbae885f1 100644 --- a/public/locales/en/files.json +++ b/public/locales/en/files.json @@ -8,9 +8,6 @@ "clearSelection": "Clear selection." }, "zipDownloadExceedsLimit": "The overall size of the files selected ({{selectionTotalSize}}) for download exceeds the zip limit of {{zipDownloadSizeLimit}}. Please unselect some files to continue.", - "pagination": { - "pageSize": "Files per page" - }, "tabularData": { "name": "Tabular Data", "variables": "Variables", diff --git a/public/locales/en/helloDataverse.json b/public/locales/en/helloDataverse.json deleted file mode 100644 index 8c947e521..000000000 --- a/public/locales/en/helloDataverse.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title": "Hello Dataverse", - "linkText": "Dataverse", - "description": "Edit <1>src/sections/hello-dataverse/HelloDataverse.tsx and save to reload.", - "altImage": "Dataverse logo" -} diff --git a/public/locales/en/home.json b/public/locales/en/home.json new file mode 100644 index 000000000..2da05e5f9 --- /dev/null +++ b/public/locales/en/home.json @@ -0,0 +1,7 @@ +{ + "title": "Root", + "noDatasetsMessage": { + "authenticated": "This dataverse currently has no datasets. You can add to it by using the Add Data button on this page.", + "anonymous": "This dataverse currently has no datasets. Please <1>log in to see if you are able to add to it." + } +} diff --git a/public/locales/en/pagination.json b/public/locales/en/pagination.json new file mode 100644 index 000000000..e30599934 --- /dev/null +++ b/public/locales/en/pagination.json @@ -0,0 +1,4 @@ +{ + "results": "{{start}} to {{end}} of {{total}} {{item}}s", + "pageSize": "{{item}}s per page" +} diff --git a/src/Router.tsx b/src/Router.tsx index 38d016834..286a80f63 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -1,8 +1,8 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom' -import { HelloDataverse } from './sections/hello-dataverse/HelloDataverse' import { Layout } from './sections/layout/Layout' import { Route } from './sections/Route.enum' import { DatasetFactory } from './sections/dataset/DatasetFactory' +import { HomeFactory } from './sections/home/HomeFactory' const router = createBrowserRouter( [ @@ -12,7 +12,7 @@ const router = createBrowserRouter( children: [ { path: Route.HOME, - element: + element: HomeFactory.create() }, { path: `${Route.DATASETS}`, diff --git a/src/dataset/domain/models/Dataset.ts b/src/dataset/domain/models/Dataset.ts index caaaae045..657fb4eac 100644 --- a/src/dataset/domain/models/Dataset.ts +++ b/src/dataset/domain/models/Dataset.ts @@ -291,7 +291,7 @@ export class Dataset { public readonly privateUrl?: PrivateUrl ) {} - public getTitle(): string { + public get title(): string { return this.metadataBlocks[0].fields.title } diff --git a/src/dataset/domain/models/DatasetPaginationInfo.ts b/src/dataset/domain/models/DatasetPaginationInfo.ts new file mode 100644 index 000000000..e9d88e6f4 --- /dev/null +++ b/src/dataset/domain/models/DatasetPaginationInfo.ts @@ -0,0 +1,7 @@ +import { PaginationInfo } from '../../../shared/domain/models/PaginationInfo' + +export class DatasetPaginationInfo extends PaginationInfo { + constructor(page = 1, pageSize = 10, totalItems = 0, itemName = 'Dataset') { + super(page, pageSize, totalItems, itemName) + } +} diff --git a/src/dataset/domain/models/DatasetPreview.ts b/src/dataset/domain/models/DatasetPreview.ts new file mode 100644 index 000000000..849ba4691 --- /dev/null +++ b/src/dataset/domain/models/DatasetPreview.ts @@ -0,0 +1,22 @@ +import { DatasetLabel, DatasetVersion } from './Dataset' + +export class DatasetPreview { + constructor( + public persistentId: string, + public title: string, + public version: DatasetVersion, + public citation: string, + public labels: DatasetLabel[], + public isDeaccessioned: boolean, + public releaseOrCreateDate: Date, + public description: string, + public thumbnail?: string + ) {} + + get abbreviatedDescription(): string { + if (this.description.length > 280) { + return `${this.description.substring(0, 280)}...` + } + return this.description + } +} diff --git a/src/dataset/domain/models/TotalDatasetsCount.ts b/src/dataset/domain/models/TotalDatasetsCount.ts new file mode 100644 index 000000000..acc302966 --- /dev/null +++ b/src/dataset/domain/models/TotalDatasetsCount.ts @@ -0,0 +1 @@ +export type TotalDatasetsCount = number diff --git a/src/dataset/domain/repositories/DatasetRepository.ts b/src/dataset/domain/repositories/DatasetRepository.ts index a836e867b..0cc96a13d 100644 --- a/src/dataset/domain/repositories/DatasetRepository.ts +++ b/src/dataset/domain/repositories/DatasetRepository.ts @@ -1,6 +1,11 @@ import { Dataset } from '../models/Dataset' +import { TotalDatasetsCount } from '../models/TotalDatasetsCount' +import { DatasetPaginationInfo } from '../models/DatasetPaginationInfo' +import { DatasetPreview } from '../models/DatasetPreview' export interface DatasetRepository { getByPersistentId: (persistentId: string, version?: string) => Promise getByPrivateUrlToken: (privateUrlToken: string) => Promise + getAll: (paginationInfo: DatasetPaginationInfo) => Promise + getTotalDatasetsCount: () => Promise } diff --git a/src/dataset/domain/useCases/getDatasets.ts b/src/dataset/domain/useCases/getDatasets.ts new file mode 100644 index 000000000..e10f74a2c --- /dev/null +++ b/src/dataset/domain/useCases/getDatasets.ts @@ -0,0 +1,12 @@ +import { DatasetRepository } from '../repositories/DatasetRepository' +import { DatasetPaginationInfo } from '../models/DatasetPaginationInfo' +import { DatasetPreview } from '../models/DatasetPreview' + +export async function getDatasets( + datasetRepository: DatasetRepository, + paginationInfo: DatasetPaginationInfo +): Promise { + return datasetRepository.getAll(paginationInfo).catch((error: Error) => { + throw new Error(error.message) + }) +} diff --git a/src/dataset/domain/useCases/getTotalDatasetsCount.ts b/src/dataset/domain/useCases/getTotalDatasetsCount.ts new file mode 100644 index 000000000..5ba855d06 --- /dev/null +++ b/src/dataset/domain/useCases/getTotalDatasetsCount.ts @@ -0,0 +1,10 @@ +import { DatasetRepository } from '../repositories/DatasetRepository' +import { TotalDatasetsCount } from '../models/TotalDatasetsCount' + +export async function getTotalDatasetsCount( + datasetRepository: DatasetRepository +): Promise { + return datasetRepository.getTotalDatasetsCount().catch((error: Error) => { + throw new Error(error.message) + }) +} diff --git a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts index c3773641b..b5ddf718c 100644 --- a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts +++ b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts @@ -16,9 +16,32 @@ import { FileDownloadSizeMode } from '@iqss/dataverse-client-javascript' import { JSDatasetMapper } from '../mappers/JSDatasetMapper' +import { TotalDatasetsCount } from '../../domain/models/TotalDatasetsCount' +import { DatasetPaginationInfo } from '../../domain/models/DatasetPaginationInfo' +import { DatasetPreview } from '../../domain/models/DatasetPreview' +import { DatasetPreviewMother } from '../../../../tests/component/dataset/domain/models/DatasetPreviewMother' const includeDeaccessioned = true export class DatasetJSDataverseRepository implements DatasetRepository { + // eslint-disable-next-line unused-imports/no-unused-vars + getAll(paginationInfo: DatasetPaginationInfo): Promise { + // TODO - Implement using the js-dataverse-client + return new Promise((resolve) => { + setTimeout(() => { + resolve(DatasetPreviewMother.createManyRealistic(10)) + }, 1000) + }) + } + + getTotalDatasetsCount(): Promise { + // TODO - Implement using the js-dataverse-client + return new Promise((resolve) => { + setTimeout(() => { + resolve(200) + }, 1000) + }) + } + getByPersistentId( persistentId: string, version?: string, diff --git a/src/files/domain/models/FilePaginationInfo.ts b/src/files/domain/models/FilePaginationInfo.ts index fbef8d177..f2b4386af 100644 --- a/src/files/domain/models/FilePaginationInfo.ts +++ b/src/files/domain/models/FilePaginationInfo.ts @@ -1,52 +1,7 @@ -export class FilePaginationInfo { - constructor( - public readonly page: number = 1, - public readonly pageSize: number = 10, - public readonly totalFiles: number = 0 - ) {} +import { PaginationInfo } from '../../../shared/domain/models/PaginationInfo' - withTotal(total: number): FilePaginationInfo { - return new FilePaginationInfo(this.page, this.pageSize, total) - } - goToPage(page: number): FilePaginationInfo { - return new FilePaginationInfo(page, this.pageSize, this.totalFiles) - } - - goToPreviousPage(): FilePaginationInfo { - if (!this.previousPage) throw new Error('No previous page') - return this.goToPage(this.previousPage) - } - - goToNextPage(): FilePaginationInfo { - if (!this.nextPage) throw new Error('No next page') - return this.goToPage(this.nextPage) - } - - withPageSize(pageSize: number): FilePaginationInfo { - const getNewPage = (oldPageSize: number, newPageSize: number) => { - const newPage = Math.ceil(((this.page - 1) * oldPageSize + 1) / newPageSize) - return newPage > 0 ? newPage : 1 - } - return new FilePaginationInfo(getNewPage(this.pageSize, pageSize), pageSize, this.totalFiles) - } - - get totalPages(): number { - return Math.ceil(this.totalFiles / this.pageSize) - } - - get hasPreviousPage(): boolean { - return this.page > 1 - } - - get hasNextPage(): boolean { - return this.page < this.totalPages - } - - get previousPage(): number | undefined { - return this.hasPreviousPage ? this.page - 1 : undefined - } - - get nextPage(): number | undefined { - return this.hasNextPage ? this.page + 1 : undefined +export class FilePaginationInfo extends PaginationInfo { + constructor(page = 1, pageSize = 10, totalItems = 0, itemName = 'File') { + super(page, pageSize, totalItems, itemName) } } diff --git a/src/files/domain/repositories/FileRepository.ts b/src/files/domain/repositories/FileRepository.ts index 9b861084d..278fc9502 100644 --- a/src/files/domain/repositories/FileRepository.ts +++ b/src/files/domain/repositories/FileRepository.ts @@ -1,9 +1,9 @@ import { File, FileDownloadMode } from '../models/File' import { FileCriteria } from '../models/FileCriteria' import { FilesCountInfo } from '../models/FilesCountInfo' -import { FilePaginationInfo } from '../models/FilePaginationInfo' import { FileUserPermissions } from '../models/FileUserPermissions' import { DatasetVersion } from '../../../dataset/domain/models/Dataset' +import { FilePaginationInfo } from '../models/FilePaginationInfo' export interface FileRepository { getAllByDatasetPersistentId: ( diff --git a/src/files/domain/useCases/getFilesByDatasetPersistentId.ts b/src/files/domain/useCases/getFilesByDatasetPersistentId.ts index 326633b7d..9370c7ebe 100644 --- a/src/files/domain/useCases/getFilesByDatasetPersistentId.ts +++ b/src/files/domain/useCases/getFilesByDatasetPersistentId.ts @@ -1,8 +1,8 @@ import { FileRepository } from '../repositories/FileRepository' import { File } from '../models/File' import { FileCriteria } from '../models/FileCriteria' -import { FilePaginationInfo } from '../models/FilePaginationInfo' import { DatasetVersion } from '../../../dataset/domain/models/Dataset' +import { FilePaginationInfo } from '../models/FilePaginationInfo' export async function getFilesByDatasetPersistentId( fileRepository: FileRepository, diff --git a/src/files/infrastructure/FileJSDataverseRepository.ts b/src/files/infrastructure/FileJSDataverseRepository.ts index 96affa553..64b46ce53 100644 --- a/src/files/infrastructure/FileJSDataverseRepository.ts +++ b/src/files/infrastructure/FileJSDataverseRepository.ts @@ -1,7 +1,6 @@ import { FileRepository } from '../domain/repositories/FileRepository' import { File, FileDownloadMode } from '../domain/models/File' import { FilesCountInfo } from '../domain/models/FilesCountInfo' -import { FilePaginationInfo } from '../domain/models/FilePaginationInfo' import { FileUserPermissions } from '../domain/models/FileUserPermissions' import { File as JSFile, @@ -19,6 +18,7 @@ import { FileCriteria } from '../domain/models/FileCriteria' import { DomainFileMapper } from './mappers/DomainFileMapper' import { JSFileMapper } from './mappers/JSFileMapper' import { DatasetVersion } from '../../dataset/domain/models/Dataset' +import { FilePaginationInfo } from '../domain/models/FilePaginationInfo' const includeDeaccessioned = true diff --git a/src/files/infrastructure/mappers/DomainFileMapper.ts b/src/files/infrastructure/mappers/DomainFileMapper.ts index 71082838d..9c8921102 100644 --- a/src/files/infrastructure/mappers/DomainFileMapper.ts +++ b/src/files/infrastructure/mappers/DomainFileMapper.ts @@ -1,4 +1,3 @@ -import { FilePaginationInfo } from '../../domain/models/FilePaginationInfo' import { FileAccessOption, FileCriteria, @@ -11,6 +10,7 @@ import { FileOrderCriteria as JSFileOrderCriteria } from '@iqss/dataverse-client-javascript' import { FileType } from '../../domain/models/File' +import { FilePaginationInfo } from '../../domain/models/FilePaginationInfo' export class DomainFileMapper { static toJSPagination(paginationInfo: FilePaginationInfo): { diff --git a/src/sections/dataset/Dataset.tsx b/src/sections/dataset/Dataset.tsx index 8095e0ae8..86e6b69f1 100644 --- a/src/sections/dataset/Dataset.tsx +++ b/src/sections/dataset/Dataset.tsx @@ -51,14 +51,14 @@ export function Dataset({ fileRepository }: DatasetProps) {
-

{dataset.getTitle()}

+

{dataset.title}

- } - - return ( -
- -
- ) -} diff --git a/src/sections/dataset/dataset-citation/DatasetCitation.module.scss b/src/sections/dataset/dataset-citation/DatasetCitation.module.scss index 4e3ecc9ea..4dfa0ae50 100644 --- a/src/sections/dataset/dataset-citation/DatasetCitation.module.scss +++ b/src/sections/dataset/dataset-citation/DatasetCitation.module.scss @@ -17,20 +17,11 @@ min-height: 150px; border: 1px solid $dv-danger-color; } -.icon { - color: #428BCA; - font-size: 7.5em; - line-height: 1.1; -} - -.preview-image { - width: 100%; - max-width: 140px; - height: auto; - max-height: 140px; -} - .row { margin-right: -15px; margin-left: -15px; +} + +.thumbnail { + font-size: 7.5em; } \ No newline at end of file diff --git a/src/sections/dataset/dataset-citation/DatasetCitation.tsx b/src/sections/dataset/dataset-citation/DatasetCitation.tsx index 9b523c7c7..9f1450830 100644 --- a/src/sections/dataset/dataset-citation/DatasetCitation.tsx +++ b/src/sections/dataset/dataset-citation/DatasetCitation.tsx @@ -1,9 +1,10 @@ -import { Col, QuestionMarkTooltip, Row } from '@iqss/dataverse-design-system' +import { Col, Row } from '@iqss/dataverse-design-system' import styles from './DatasetCitation.module.scss' import { useTranslation } from 'react-i18next' import { DatasetPublishingStatus, DatasetVersion } from '../../../dataset/domain/models/Dataset' -import parse from 'html-react-parser' -import { CitationThumbnail } from './CitationThumbnail' +import { DatasetThumbnail } from '../dataset-thumbnail/DatasetThumbnail' +import { CitationDescription } from '../../shared/citation/CitationDescription' +import { DatasetCitationTooltip } from './DatasetCitationTooltip' interface DatasetCitationProps { thumbnail?: string @@ -23,16 +24,19 @@ export function DatasetCitation({ thumbnail, title, citation, version }: Dataset : styles.container }> - - + - + + + +
@@ -52,35 +56,3 @@ export function DatasetCitation({ thumbnail, title, citation, version }: Dataset ) } - -function CitationDescription({ citation, version }: { citation: string; version: DatasetVersion }) { - const citationAsReactElement = parse(citation) - - return ( - - {citationAsReactElement} - - - ) -} - -interface CitationDatasetStatusProps { - status: DatasetPublishingStatus -} - -function CitationTooltip({ status }: CitationDatasetStatusProps) { - const { t } = useTranslation('dataset') - - if (status !== DatasetPublishingStatus.RELEASED) { - return ( - <> - {' '} - - - ) - } - return <> -} diff --git a/src/sections/dataset/dataset-citation/DatasetCitationTooltip.tsx b/src/sections/dataset/dataset-citation/DatasetCitationTooltip.tsx new file mode 100644 index 000000000..0ae1676b1 --- /dev/null +++ b/src/sections/dataset/dataset-citation/DatasetCitationTooltip.tsx @@ -0,0 +1,24 @@ +import { DatasetPublishingStatus } from '../../../dataset/domain/models/Dataset' +import { useTranslation } from 'react-i18next' +import { QuestionMarkTooltip } from '@iqss/dataverse-design-system' + +interface DatasetCitationTooltipProps { + status: DatasetPublishingStatus +} + +export function DatasetCitationTooltip({ status }: DatasetCitationTooltipProps) { + const { t } = useTranslation('dataset') + + if (status !== DatasetPublishingStatus.RELEASED) { + return ( + <> + {' '} + + + ) + } + return <> +} diff --git a/src/sections/dataset/dataset-files/DatasetFiles.tsx b/src/sections/dataset/dataset-files/DatasetFiles.tsx index cccd061a5..afb96ec8b 100644 --- a/src/sections/dataset/dataset-files/DatasetFiles.tsx +++ b/src/sections/dataset/dataset-files/DatasetFiles.tsx @@ -4,9 +4,9 @@ import { FilesTable } from './files-table/FilesTable' import { FileCriteriaForm } from './file-criteria-form/FileCriteriaForm' import { FileCriteria } from '../../../files/domain/models/FileCriteria' import { useFiles } from './useFiles' -import { FilePaginationInfo } from '../../../files/domain/models/FilePaginationInfo' -import { FilesPagination } from './files-pagination/FilesPagination' +import { PaginationControls } from '../../shared/pagination/PaginationControls' import { DatasetVersion } from '../../../dataset/domain/models/Dataset' +import { FilePaginationInfo } from '../../../files/domain/models/FilePaginationInfo' interface DatasetFilesProps { filesRepository: FileRepository @@ -44,10 +44,8 @@ export function DatasetFiles({ filesTotalDownloadSize={filesTotalDownloadSize} criteria={criteria} /> - diff --git a/src/sections/dataset/dataset-files/files-table/FilesTable.tsx b/src/sections/dataset/dataset-files/files-table/FilesTable.tsx index fc71ef801..b1988bba8 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTable.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTable.tsx @@ -6,10 +6,10 @@ import { File } from '../../../../files/domain/models/File' import { RowSelectionMessage } from './row-selection/RowSelectionMessage' import { ZipDownloadLimitMessage } from './zip-download-limit-message/ZipDownloadLimitMessage' import { SpinnerSymbol } from './spinner-symbol/SpinnerSymbol' -import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' import { useEffect, useState } from 'react' import { FileSelection } from './row-selection/useFileSelection' import { FileCriteria } from '../../../../files/domain/models/FileCriteria' +import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' interface FilesTableProps { files: File[] @@ -56,7 +56,7 @@ export function FilesTable({ } return ( - {`${startIndex} to ${endIndex} of ${fileCount} Files`} + ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx index 61ddb0c44..054ab4b89 100644 --- a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx @@ -1,17 +1,13 @@ import { FileDate as FileDateModel } from '../../../../../../../files/domain/models/File' import { useTranslation } from 'react-i18next' +import { DateHelper } from '../../../../../../../shared/domain/helpers/DateHelper' export function FileDate({ date }: { date: FileDateModel }) { const { t } = useTranslation('files') return (
- {t(`table.date.${date.type}`)}{' '} - {date.date.toLocaleDateString(Intl.DateTimeFormat().resolvedOptions().locale, { - year: 'numeric', - month: 'short', - day: 'numeric' - })} + {t(`table.date.${date.type}`)} {DateHelper.toDisplayFormat(date.date)}
) diff --git a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileEmbargoDate.tsx b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileEmbargoDate.tsx index 4c61171b9..50db565fa 100644 --- a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileEmbargoDate.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileEmbargoDate.tsx @@ -1,5 +1,6 @@ import { FileEmbargo, FilePublishingStatus } from '../../../../../../../files/domain/models/File' import { useTranslation } from 'react-i18next' +import { DateHelper } from '../../../../../../../shared/domain/helpers/DateHelper' interface FileEmbargoDateProps { embargo: FileEmbargo | undefined @@ -17,11 +18,7 @@ export function FileEmbargoDate({ embargo, publishingStatus }: FileEmbargoDatePr
{t(embargoTypeOfDate(embargo.isActive, publishingStatus))}{' '} - {embargo.dateAvailable.toLocaleDateString(Intl.DateTimeFormat().resolvedOptions().locale, { - year: 'numeric', - month: 'short', - day: 'numeric' - })} + {DateHelper.toDisplayFormat(embargo.dateAvailable)}
) diff --git a/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts b/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts index ede451ad0..6fe2a12eb 100644 --- a/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts +++ b/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react' -import { FilePaginationInfo } from '../../../../../files/domain/models/FilePaginationInfo' import { File } from '../../../../../files/domain/models/File' import { Row } from '@tanstack/react-table' import { RowSelection } from '../useFilesTable' +import { FilePaginationInfo } from '../../../../../files/domain/models/FilePaginationInfo' export type FileSelection = { [key: string]: File | undefined @@ -61,7 +61,7 @@ export function useFileSelection( const selectAllFiles = () => { setCurrentPageRowSelection(createRowSelection(paginationInfo.pageSize)) - const totalFilesFileSelection = createFileSelection(paginationInfo.totalFiles) + const totalFilesFileSelection = createFileSelection(paginationInfo.totalItems) const newFileSelection = { ...totalFilesFileSelection, ...fileSelection } setFileSelection(newFileSelection) } diff --git a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx index b403e9782..0d4f35fd1 100644 --- a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx +++ b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx @@ -2,8 +2,8 @@ import { useEffect, useState } from 'react' import { File } from '../../../../files/domain/models/File' import { getCoreRowModel, Row, useReactTable } from '@tanstack/react-table' import { createColumnsDefinition } from './FilesTableColumnsDefinition' -import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' import { useFileSelection } from './row-selection/useFileSelection' +import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' export type RowSelection = { [key: string]: boolean diff --git a/src/sections/dataset/dataset-files/useFiles.tsx b/src/sections/dataset/dataset-files/useFiles.tsx index 538da0921..ce628deb3 100644 --- a/src/sections/dataset/dataset-files/useFiles.tsx +++ b/src/sections/dataset/dataset-files/useFiles.tsx @@ -5,11 +5,11 @@ import { getFilesByDatasetPersistentId } from '../../../files/domain/useCases/ge import { FileCriteria } from '../../../files/domain/models/FileCriteria' import { FilesCountInfo } from '../../../files/domain/models/FilesCountInfo' import { getFilesCountInfoByDatasetPersistentId } from '../../../files/domain/useCases/getFilesCountInfoByDatasetPersistentId' -import { FilePaginationInfo } from '../../../files/domain/models/FilePaginationInfo' import { useFilePermissions } from '../../file/file-permissions/FilePermissionsContext' import { FilePermission } from '../../../files/domain/models/FileUserPermissions' import { DatasetVersion } from '../../../dataset/domain/models/Dataset' import { getFilesTotalDownloadSize } from '../../../files/domain/useCases/getFilesTotalDownloadSize' +import { FilePaginationInfo } from '../../../files/domain/models/FilePaginationInfo' export function useFiles( filesRepository: FileRepository, @@ -33,7 +33,7 @@ export function useFiles( ) .then((filesCountInfo: FilesCountInfo) => { setFilesCountInfo(filesCountInfo) - if (filesCountInfo.total !== paginationInfo.totalFiles) { + if (filesCountInfo.total !== paginationInfo.totalItems) { onPaginationInfoChange(paginationInfo.withTotal(filesCountInfo.total)) } return filesCountInfo diff --git a/src/sections/dataset/dataset-icon/DatasetIcon.module.scss b/src/sections/dataset/dataset-icon/DatasetIcon.module.scss new file mode 100644 index 000000000..4025c0bbe --- /dev/null +++ b/src/sections/dataset/dataset-icon/DatasetIcon.module.scss @@ -0,0 +1,6 @@ +@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; + +.icon { + color: $dv-info-border-color; + line-height: 1.1; +} \ No newline at end of file diff --git a/src/sections/dataset/dataset-icon/DatasetIcon.tsx b/src/sections/dataset/dataset-icon/DatasetIcon.tsx new file mode 100644 index 000000000..ac68ba92b --- /dev/null +++ b/src/sections/dataset/dataset-icon/DatasetIcon.tsx @@ -0,0 +1,10 @@ +import styles from './DatasetIcon.module.scss' +import { Icon, IconName } from '@iqss/dataverse-design-system' + +export function DatasetIcon() { + return ( +
+ +
+ ) +} diff --git a/src/sections/dataset/dataset-thumbnail/DatasetThumbnail.module.scss b/src/sections/dataset/dataset-thumbnail/DatasetThumbnail.module.scss new file mode 100644 index 000000000..a464f4dc4 --- /dev/null +++ b/src/sections/dataset/dataset-thumbnail/DatasetThumbnail.module.scss @@ -0,0 +1,6 @@ +.preview-image { + width: 100%; + max-width: 140px; + height: auto; + max-height: 140px; +} \ No newline at end of file diff --git a/src/sections/dataset/dataset-thumbnail/DatasetThumbnail.tsx b/src/sections/dataset/dataset-thumbnail/DatasetThumbnail.tsx new file mode 100644 index 000000000..628cdd8ac --- /dev/null +++ b/src/sections/dataset/dataset-thumbnail/DatasetThumbnail.tsx @@ -0,0 +1,16 @@ +import styles from './DatasetThumbnail.module.scss' +import { DatasetIcon } from '../dataset-icon/DatasetIcon' + +interface DatasetThumbnailProps { + thumbnail?: string + title: string + isDeaccessioned?: boolean +} + +export function DatasetThumbnail({ thumbnail, title, isDeaccessioned }: DatasetThumbnailProps) { + if (thumbnail && !isDeaccessioned) { + return {title} + } + + return +} diff --git a/src/sections/hello-dataverse/HelloDataverse.module.scss b/src/sections/hello-dataverse/HelloDataverse.module.scss deleted file mode 100644 index 505b1cb9c..000000000 --- a/src/sections/hello-dataverse/HelloDataverse.module.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; -@import "src/sections/assets/variables"; - -.container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-height: $body-available-height; - font-size: calc(10px + 2vmin); - text-align: center; -} - -.title { - color: $dv-brand-color; -} - -.logo { - height: 40vmin; - pointer-events: none; -} - -.link { - color: $dv-link-color; -} - diff --git a/src/sections/hello-dataverse/HelloDataverse.tsx b/src/sections/hello-dataverse/HelloDataverse.tsx deleted file mode 100644 index e955d0e66..000000000 --- a/src/sections/hello-dataverse/HelloDataverse.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import logo from '../assets/logo.svg' -import styles from './HelloDataverse.module.scss' -import { Trans, useTranslation } from 'react-i18next' - -export function HelloDataverse() { - const { t } = useTranslation('helloDataverse') - - return ( -
-

{t('title')}

- {t('altImage')} -

- }} /> -

- - {t('linkText')} - -
- ) -} diff --git a/src/sections/home/Home.tsx b/src/sections/home/Home.tsx new file mode 100644 index 000000000..803e2a1d6 --- /dev/null +++ b/src/sections/home/Home.tsx @@ -0,0 +1,20 @@ +import { Row } from '@iqss/dataverse-design-system' +import { useTranslation } from 'react-i18next' +import { DatasetRepository } from '../../dataset/domain/repositories/DatasetRepository' +import { DatasetsList } from './datasets-list/DatasetsList' +interface HomeProps { + datasetRepository: DatasetRepository +} + +export function Home({ datasetRepository }: HomeProps) { + const { t } = useTranslation('home') + + return ( + +
+

{t('title')}

+
+ +
+ ) +} diff --git a/src/sections/home/HomeFactory.tsx b/src/sections/home/HomeFactory.tsx new file mode 100644 index 000000000..f37a69caa --- /dev/null +++ b/src/sections/home/HomeFactory.tsx @@ -0,0 +1,10 @@ +import { ReactElement } from 'react' +import { Home } from './Home' +import { DatasetJSDataverseRepository } from '../../dataset/infrastructure/repositories/DatasetJSDataverseRepository' + +const datasetRepository = new DatasetJSDataverseRepository() +export class HomeFactory { + static create(): ReactElement { + return + } +} diff --git a/src/sections/home/datasets-list/DatasetsList.module.scss b/src/sections/home/datasets-list/DatasetsList.module.scss new file mode 100644 index 000000000..15aff10a5 --- /dev/null +++ b/src/sections/home/datasets-list/DatasetsList.module.scss @@ -0,0 +1,13 @@ +@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; + +.container { + min-height: calc(100vh + 100px); + padding:15px; + border: 1px solid #ddd; + border-radius: 4px; +} + +.empty-message-container { + padding: .5em 1em; + background: $dv-warning-box-color; +} diff --git a/src/sections/home/datasets-list/DatasetsList.tsx b/src/sections/home/datasets-list/DatasetsList.tsx new file mode 100644 index 000000000..da13f06d3 --- /dev/null +++ b/src/sections/home/datasets-list/DatasetsList.tsx @@ -0,0 +1,53 @@ +import { useDatasets } from './useDatasets' +import styles from './DatasetsList.module.scss' +import { DatasetRepository } from '../../../dataset/domain/repositories/DatasetRepository' +import { useEffect, useState } from 'react' +import { PaginationResultsInfo } from '../../shared/pagination/PaginationResultsInfo' +import { PaginationControls } from '../../shared/pagination/PaginationControls' +import { DatasetPaginationInfo } from '../../../dataset/domain/models/DatasetPaginationInfo' +import { useLoading } from '../../loading/LoadingContext' +import { DatasetsListSkeleton } from './DatasetsListSkeleton' +import { NoDatasetsMessage } from './NoDatasetsMessage' +import { DatasetCard } from './dataset-card/DatasetCard' + +interface DatasetsListProps { + datasetRepository: DatasetRepository +} +const NO_DATASETS = 0 +export function DatasetsList({ datasetRepository }: DatasetsListProps) { + const { setIsLoading } = useLoading() + const [paginationInfo, setPaginationInfo] = useState( + new DatasetPaginationInfo() + ) + const { datasets, isLoading } = useDatasets(datasetRepository, setPaginationInfo, paginationInfo) + + useEffect(() => { + setIsLoading(isLoading) + }, [isLoading]) + + if (isLoading) { + return + } + + return ( +
+ {datasets.length === NO_DATASETS ? ( + + ) : ( + <> +
+ +
+ {datasets.map((dataset) => ( + + ))} + + + )} +
+ ) +} diff --git a/src/sections/home/datasets-list/DatasetsListSkeleton.tsx b/src/sections/home/datasets-list/DatasetsListSkeleton.tsx new file mode 100644 index 000000000..b07bc39d3 --- /dev/null +++ b/src/sections/home/datasets-list/DatasetsListSkeleton.tsx @@ -0,0 +1,26 @@ +import styles from './DatasetsList.module.scss' +import Skeleton, { SkeletonTheme } from 'react-loading-skeleton' + +export function DatasetsListSkeleton() { + return ( + +
+
+ +
+
+ + + + + + + + + + +
+
+
+ ) +} diff --git a/src/sections/home/datasets-list/NoDatasetsMessage.tsx b/src/sections/home/datasets-list/NoDatasetsMessage.tsx new file mode 100644 index 000000000..a7ad3cfc0 --- /dev/null +++ b/src/sections/home/datasets-list/NoDatasetsMessage.tsx @@ -0,0 +1,24 @@ +import styles from './DatasetsList.module.scss' +import { Trans, useTranslation } from 'react-i18next' +import { useSession } from '../../session/SessionContext' +import { Route } from '../../Route.enum' + +export function NoDatasetsMessage() { + const { t } = useTranslation('home') + const { user } = useSession() + + return ( +
+ {user ? ( +

{t('noDatasetsMessage.authenticated')}

+ ) : ( + +

+ This dataverse currently has no datasets. Please log in to + see if you are able to add to it. +

+
+ )} +
+ ) +} diff --git a/src/sections/home/datasets-list/dataset-card/DatasetCard.module.scss b/src/sections/home/datasets-list/dataset-card/DatasetCard.module.scss new file mode 100644 index 000000000..92f7696b2 --- /dev/null +++ b/src/sections/home/datasets-list/dataset-card/DatasetCard.module.scss @@ -0,0 +1,71 @@ +@use 'sass:color'; +@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; +@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module"; + +.container { + margin: 6px 0; + padding: 4px 10px; + border: 1px solid $dv-info-border-color; +} + +.header { + display: flex; + justify-content: space-between; +} + +.title { + display: flex; + + > * { + margin-right: .5em; + } +} + +.icon { + margin-top: 2px; + font-size: 1.3em; + + > div >span { + margin-right: 0; + } +} + +.thumbnail { + width: 48px; + margin: 8px 12px 6px 0; + font-size: 2.8em; + + img { + vertical-align: top; + } +} + +.info { + display: flex; +} + +.description { + display: flex; + flex-direction: column; + width: 100%; + font-size: $dv-font-size-sm; +} + +.date { + color: $dv-subtext-color; + +} + +.citation-box { + margin-top: 4px; + margin-bottom: .5em; + padding: 4px; + background-color: color.adjust($dv-primary-color, $lightness: 51%) ; +} + +.citation-box-deaccessioned { + margin-top: 4px; + margin-bottom: .5em; + padding: 4px; + background-color: color.adjust($dv-danger-box-color, $lightness: 6%); +} \ No newline at end of file diff --git a/src/sections/home/datasets-list/dataset-card/DatasetCard.tsx b/src/sections/home/datasets-list/dataset-card/DatasetCard.tsx new file mode 100644 index 000000000..6fe1f807e --- /dev/null +++ b/src/sections/home/datasets-list/dataset-card/DatasetCard.tsx @@ -0,0 +1,21 @@ +import { DatasetPreview } from '../../../../dataset/domain/models/DatasetPreview' +import styles from './DatasetCard.module.scss' +import { DatasetCardHeader } from './DatasetCardHeader' +import { DatasetCardThumbnail } from './DatasetCardThumbnail' +import { DatasetCardInfo } from './DatasetCardInfo' + +interface DatasetCardProps { + dataset: DatasetPreview +} + +export function DatasetCard({ dataset }: DatasetCardProps) { + return ( +
+ +
+ + +
+
+ ) +} diff --git a/src/sections/home/datasets-list/dataset-card/DatasetCardHeader.tsx b/src/sections/home/datasets-list/dataset-card/DatasetCardHeader.tsx new file mode 100644 index 000000000..9e927b2fc --- /dev/null +++ b/src/sections/home/datasets-list/dataset-card/DatasetCardHeader.tsx @@ -0,0 +1,25 @@ +import styles from './DatasetCard.module.scss' +import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' +import { Route } from '../../../Route.enum' +import { DatasetLabels } from '../../../dataset/dataset-labels/DatasetLabels' +import { DatasetPreview } from '../../../../dataset/domain/models/DatasetPreview' +import { DatasetIcon } from '../../../dataset/dataset-icon/DatasetIcon' + +interface DatasetCardHeaderProps { + dataset: DatasetPreview +} +export function DatasetCardHeader({ dataset }: DatasetCardHeaderProps) { + return ( +
+
+ + {dataset.title} + + +
+
+ +
+
+ ) +} diff --git a/src/sections/home/datasets-list/dataset-card/DatasetCardInfo.tsx b/src/sections/home/datasets-list/dataset-card/DatasetCardInfo.tsx new file mode 100644 index 000000000..76a03a645 --- /dev/null +++ b/src/sections/home/datasets-list/dataset-card/DatasetCardInfo.tsx @@ -0,0 +1,23 @@ +import styles from './DatasetCard.module.scss' +import { DateHelper } from '../../../../shared/domain/helpers/DateHelper' +import { DatasetPreview } from '../../../../dataset/domain/models/DatasetPreview' +import { CitationDescription } from '../../../shared/citation/CitationDescription' + +interface DatasetCardInfoProps { + dataset: DatasetPreview +} + +export function DatasetCardInfo({ dataset }: DatasetCardInfoProps) { + return ( +
+ {DateHelper.toDisplayFormat(dataset.releaseOrCreateDate)} + + + + {dataset.abbreviatedDescription} +
+ ) +} diff --git a/src/sections/home/datasets-list/dataset-card/DatasetCardThumbnail.tsx b/src/sections/home/datasets-list/dataset-card/DatasetCardThumbnail.tsx new file mode 100644 index 000000000..cba65b215 --- /dev/null +++ b/src/sections/home/datasets-list/dataset-card/DatasetCardThumbnail.tsx @@ -0,0 +1,23 @@ +import styles from './DatasetCard.module.scss' +import { DatasetPreview } from '../../../../dataset/domain/models/DatasetPreview' +import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' +import { Route } from '../../../Route.enum' +import { DatasetThumbnail } from '../../../dataset/dataset-thumbnail/DatasetThumbnail' + +interface DatasetCardThumbnailProps { + dataset: DatasetPreview +} + +export function DatasetCardThumbnail({ dataset }: DatasetCardThumbnailProps) { + return ( +
+ + + +
+ ) +} diff --git a/src/sections/home/datasets-list/useDatasets.tsx b/src/sections/home/datasets-list/useDatasets.tsx new file mode 100644 index 000000000..8bb900423 --- /dev/null +++ b/src/sections/home/datasets-list/useDatasets.tsx @@ -0,0 +1,65 @@ +import { useEffect, useState } from 'react' +import { DatasetRepository } from '../../../dataset/domain/repositories/DatasetRepository' +import { getDatasets } from '../../../dataset/domain/useCases/getDatasets' +import { getTotalDatasetsCount } from '../../../dataset/domain/useCases/getTotalDatasetsCount' +import { TotalDatasetsCount } from '../../../dataset/domain/models/TotalDatasetsCount' +import { DatasetPaginationInfo } from '../../../dataset/domain/models/DatasetPaginationInfo' +import { DatasetPreview } from '../../../dataset/domain/models/DatasetPreview' + +export function useDatasets( + datasetRepository: DatasetRepository, + onPaginationInfoChange: (paginationInfo: DatasetPaginationInfo) => void, + paginationInfo: DatasetPaginationInfo +) { + const [datasets, setDatasets] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [totalDatasetsCount, setTotalDatasetsCount] = useState() + + const fetchTotalDatasetsCount: () => Promise = () => { + return getTotalDatasetsCount(datasetRepository) + .then((totalDatasetsCount: TotalDatasetsCount) => { + setTotalDatasetsCount(totalDatasetsCount) + if (totalDatasetsCount !== paginationInfo.totalItems) { + onPaginationInfoChange(paginationInfo.withTotal(totalDatasetsCount)) + } + return totalDatasetsCount + }) + .catch(() => { + throw new Error('There was an error getting the datasets count info') + }) + } + const fetchDatasets = (totalDatasetsCount: TotalDatasetsCount) => { + if (typeof totalDatasetsCount !== 'undefined') { + if (totalDatasetsCount === 0) { + setIsLoading(false) + return + } + return getDatasets(datasetRepository, paginationInfo.withTotal(totalDatasetsCount)) + .then((datasets: DatasetPreview[]) => { + setDatasets(datasets) + setIsLoading(false) + return datasets + }) + .catch(() => { + throw new Error('There was an error getting the datasets') + }) + } + } + + useEffect(() => { + setIsLoading(true) + + fetchTotalDatasetsCount() + .then((totalDatasetsCount) => fetchDatasets(totalDatasetsCount)) + .catch(() => { + console.error('There was an error getting the datasets') + setIsLoading(false) + }) + }, [datasetRepository, paginationInfo.page]) + + return { + datasets, + totalDatasetsCount, + isLoading + } +} diff --git a/src/sections/layout/header/Header.tsx b/src/sections/layout/header/Header.tsx index e83b454be..7121bd1f1 100644 --- a/src/sections/layout/header/Header.tsx +++ b/src/sections/layout/header/Header.tsx @@ -3,22 +3,31 @@ import { useTranslation } from 'react-i18next' import { Navbar } from '@iqss/dataverse-design-system' import { Route } from '../../Route.enum' import { useSession } from '../../session/SessionContext' +import { useNavigate } from 'react-router-dom' +const currentPage = 0 export function Header() { const { t } = useTranslation('header') const { user, logout } = useSession() + const navigate = useNavigate() const baseRemoteUrl = import.meta.env.VITE_DATAVERSE_BACKEND_URL as string + const onLogoutClick = () => { + void logout().then(() => { + navigate(currentPage) + }) + } + return ( {user ? ( - + {t('logOut')} diff --git a/src/sections/session/SessionContext.ts b/src/sections/session/SessionContext.ts index 4840d510a..5bafb77cf 100644 --- a/src/sections/session/SessionContext.ts +++ b/src/sections/session/SessionContext.ts @@ -4,12 +4,12 @@ import { User } from '../../users/domain/models/User' interface SessionContextProps { user: User | null setUser: (user: User) => void - logout: () => void + logout: () => Promise } export const SessionContext = createContext({ user: null, setUser: () => {}, - logout: () => {} + logout: () => Promise.resolve() }) export const useSession = () => useContext(SessionContext) diff --git a/src/sections/session/SessionProvider.tsx b/src/sections/session/SessionProvider.tsx index 56fa07d81..4fb95b74b 100644 --- a/src/sections/session/SessionProvider.tsx +++ b/src/sections/session/SessionProvider.tsx @@ -20,7 +20,7 @@ export function SessionProvider({ repository, children }: PropsWithChildren { - logOut(repository) + return logOut(repository) .then(() => { setUser(null) }) diff --git a/src/sections/shared/citation/CitationDescription.tsx b/src/sections/shared/citation/CitationDescription.tsx new file mode 100644 index 000000000..eb2730bd2 --- /dev/null +++ b/src/sections/shared/citation/CitationDescription.tsx @@ -0,0 +1,11 @@ +import parse from 'html-react-parser' + +interface CitationDescriptionProps { + citation: string +} + +export function CitationDescription({ citation }: CitationDescriptionProps) { + const citationAsReactElement = parse(citation) + + return {citationAsReactElement} +} diff --git a/src/sections/shared/link-to-page/LinkToPage.tsx b/src/sections/shared/link-to-page/LinkToPage.tsx new file mode 100644 index 000000000..dd38a28af --- /dev/null +++ b/src/sections/shared/link-to-page/LinkToPage.tsx @@ -0,0 +1,20 @@ +import { Link } from 'react-router-dom' +import { PropsWithChildren } from 'react' +import { Route } from '../../Route.enum' + +interface LinkToPageProps { + page: Route + searchParams?: Record +} + +export function LinkToPage({ children, page, searchParams }: PropsWithChildren) { + const searchParamsString: string = searchParams ? '?' + encodeSearchParamsToURI(searchParams) : '' + + return {children} +} + +const encodeSearchParamsToURI = (searchParams: Record) => { + return Object.entries(searchParams) + .map(([key, value]) => `${key}=${value}`) + .join('&') +} diff --git a/src/sections/dataset/dataset-files/files-pagination/PageNumbersButtons.tsx b/src/sections/shared/pagination/PageNumbersButtons.tsx similarity index 100% rename from src/sections/dataset/dataset-files/files-pagination/PageNumbersButtons.tsx rename to src/sections/shared/pagination/PageNumbersButtons.tsx diff --git a/src/sections/dataset/dataset-files/files-pagination/PageNumbersButtonsWithEllipsis.tsx b/src/sections/shared/pagination/PageNumbersButtonsWithEllipsis.tsx similarity index 100% rename from src/sections/dataset/dataset-files/files-pagination/PageNumbersButtonsWithEllipsis.tsx rename to src/sections/shared/pagination/PageNumbersButtonsWithEllipsis.tsx diff --git a/src/sections/dataset/dataset-files/files-pagination/PageSizeSelector.tsx b/src/sections/shared/pagination/PageSizeSelector.tsx similarity index 80% rename from src/sections/dataset/dataset-files/files-pagination/PageSizeSelector.tsx rename to src/sections/shared/pagination/PageSizeSelector.tsx index a5acbf945..2378453df 100644 --- a/src/sections/dataset/dataset-files/files-pagination/PageSizeSelector.tsx +++ b/src/sections/shared/pagination/PageSizeSelector.tsx @@ -1,20 +1,24 @@ -import styles from './FilesPagination.module.scss' +import styles from './Pagination.module.scss' import { useTranslation } from 'react-i18next' export function PageSizeSelector({ + itemName, pageSize, setPageSize }: { + itemName: string pageSize: number setPageSize: (pageSize: number) => void }) { - const { t } = useTranslation('files') + const { t } = useTranslation('pagination') const availableSizes = [10, 25, 50] return (