@ubiquitypress/react-invenio-bulk-importer
Version:
React components for Invenio Bulk Importer
855 lines (770 loc) • 28.5 kB
TypeScript
import { default as cn } from 'classnames';
import { default as default_2 } from 'react';
import { FC } from 'react';
import { Icon } from 'semantic-ui-react';
import { Label } from 'semantic-ui-react';
/**
* Represents an individual bucket in an aggregation.
* This is the base bucket data from the API.
*/
export declare interface AggregationBucket {
key: string;
doc_count: number;
}
/**
* Represents the configuration for an aggregation.
*/
export declare interface AggregationConfig {
field: string;
aggName: string;
childAgg?: AggregationConfig;
}
/**
* Represents aggregation data structure from the API.
*/
export declare interface AggregationData {
buckets: AggregationBucket[];
}
/**
* Map of aggregation results from the API.
*/
export declare interface Aggregations {
[key: string]: AggregationData;
}
/**
* Extended bucket with UI state information.
* Used by aggregation components for display and interaction.
*/
export declare interface Bucket extends AggregationBucket {
label: string;
is_selected: boolean;
}
export declare const BulkImporter: {
ExportRecordButton: FC<ExportRecordButtonProps>;
ExportSearchResultsButton: FC<ExportSearchResultsButtonProps>;
Search: FC<SearchProps>;
TaskDetails: FC<TaskDetailsProps>;
};
/**
* Capitalizes the first letter of a given string.
*
* @param string - The input string to capitalize
* @returns The string with the first letter capitalized, or an empty string if input is null/undefined
*/
export declare const capitalizeFirstLetter: (string: string) => string;
export { cn }
/**
* Creates a new InvenioSearchApi instance with the provided configuration.
*
* @param config - The configuration object for the search API
* @returns A new InvenioSearchApi instance
*/
export declare const createSearchApi: (config: SearchApiConfig) => unknown;
/**
* Creates a configuration object for the search API with default settings.
*
* @param url - The base URL for the search API endpoint
* @param headers - Additional HTTP headers to include in requests. Defaults to an empty object
* @param withCredentials - Whether to include credentials (cookies, authorization headers) in cross-origin requests. Defaults to true
* @returns A SearchApiConfig object containing axios configuration with the specified parameters
*/
export declare const createSearchApiConfig: (url: string, headers?: Record<string, string>, withCredentials?: boolean) => SearchApiConfig;
/** Default sort option used when the query string is empty. */
export declare interface DefaultSortingOption {
/** Sort key sent to the API. */
sortBy: string;
}
/** Triggers a browser download for an in-memory Blob. */
export declare const downloadBlob: (blob: Blob, filename: string) => void;
/** Renders a button that exports a single Invenio record as CSV. */
export declare const ExportRecordButton: default_2.FC<ExportRecordButtonProps>;
/** Props for rendering a single-record CSV export button. */
export declare interface ExportRecordButtonProps extends RecordsExportButtonBaseProps {
/** Invenio record ID to export. */
recordId?: string | null;
}
/** Renders a button that exports the current records search as CSV. */
export declare const ExportSearchResultsButton: default_2.FC<ExportSearchResultsButtonProps>;
/** Props for rendering a search-results CSV export button. */
export declare interface ExportSearchResultsButtonProps extends RecordsExportButtonBaseProps {
}
export declare const FileUploader: default_2.FC<FileUploaderProps>;
export declare interface FileUploaderProps {
taskId?: string;
value?: File[];
onChange: (files: File[]) => void;
accept?: string;
maxFiles?: number;
maxTotalSize?: number;
skipEmptyFiles?: boolean;
disabled?: boolean;
error?: string;
}
export declare interface FileUploadProgress {
fileName: string;
loaded: number;
total: number;
percentage: number;
}
/**
* Represents a filter value in the search.
*/
export declare interface FilterValue {
field: string;
value: string;
label?: string;
}
export declare const formatDate: (dateString: string) => string;
/**
* Normalizes task field values for display in the UI.
*
* @param value - The raw task value to render.
* @param formatter - Optional formatter for string-like values.
* @returns A human-readable string suitable for UI display.
*/
export declare const formatDetailValue: (value?: string | number | boolean | null, formatter?: (value: string) => string) => string;
/**
* Formats the total size of selected files into a human-readable string.
* Uses binary prefixes (Bytes, KB, MB, GB) for formatting.
* @param bytes - The total size in bytes.
* @return A formatted string representing the file size.
*/
export declare const formatFileSize: (bytes: number) => string;
/**
* Formats an internal task option key into a human-readable label.
*
* @param optionKey - The raw option key, typically snake_case.
* @returns A UI-friendly label for the option.
*/
export declare const formatOptionLabel: (optionKey: string) => string;
/**
* Retrieves the CSRF token from browser cookies.
*
* This function looks for a cookie named 'csrftoken' and returns its value.
* CSRF tokens are commonly used to prevent Cross-Site Request Forgery attacks
* by ensuring that requests originate from the intended source.
*
* @returns The CSRF token string if found in cookies, otherwise null
*
*/
export declare const getCsrfToken: () => string | null;
export declare const getStatusColor: (status: InvenioImporterTaskStates | InvenioImporterRecordStates) => "green" | "red" | "blue" | "yellow" | "grey";
/**
* Returns ordered task option entries suitable for deterministic rendering.
*
* @param options - Task options to read.
* @returns Known task options in canonical display order.
*/
export declare const getTaskOptionEntries: (options?: TaskOptions | null) => [TaskOptionKey, boolean][];
export declare const getTotalSize: (uploadedFiles: UploadableFile[]) => number;
export declare const getTotalSizeFormatted: (uploadedFiles: UploadableFile[]) => string;
export declare interface ImporterTaskConfig {
serializers: string[];
options: TaskOptions;
}
export declare const ImporterTaskStates: {
readonly CREATED: "created";
readonly VALIDATING: "validating";
readonly VALIDATION_FAILED: "validated with failures";
readonly VALIDATED: "validated";
readonly IMPORTING: "importing";
readonly IMPORT_FAILED: "imported with failures";
readonly SUCCESS: "success";
readonly DAMAGED: "damaged";
};
export declare const ImportModal: () => default_2.JSX.Element;
/** Initial React-SearchKit query state. */
export declare interface InitialQueryState {
/** Selected facet filters. */
filters?: unknown[];
/** Hidden filters always sent with the query. */
hiddenParams?: unknown | null;
/** Active result layout. */
layout?: string;
/** Active page number. */
page?: number;
/** Number of results per page. */
size?: number;
/** Active sort key. */
sortBy?: string;
/** Free-text search query. */
queryString?: string;
}
export declare interface InvenioImporterRecord {
id: string;
created: string;
updated: string;
links: {
self: string;
self_html: string;
edit_html: string;
metadata: string;
};
revision_id: number;
status: InvenioImporterRecordStates;
src_data: {
id: string;
doi: string;
title: string;
version: string;
keywords: string;
filenames: string;
publisher: string;
'rights.id': string;
communities: string;
description: string;
'access.files': string;
'imprint.isbn': string;
'languages.id': string;
'rights.title': string;
'creators.name': string;
'creators.type': string;
'imprint.pages': string;
'imprint.place': string;
'locations.lat': string;
'locations.lon': string;
'imprint.volume': string;
'imprint.edition': string;
'locations.place': string;
'subjects.scheme': string;
'creators.role.id': string;
publication_date: string;
'resource_type.id': string;
'subjects.subject': string;
'contributors.name': string;
'contributors.type': string;
'identifiers.scheme': string;
'Imprint.series_name': string;
'creators.given_name': string;
'access.embargo.until': string;
'contributors.role.id': string;
'creators.family_name': string;
'references.reference': string;
'access.embargo.active': string;
'access.embargo.reason': string;
'locations.description': string;
'identifiers.identifier': string;
'contributors.given_name': string;
'contributors.family_name': string;
'creators.affiliations.id': string;
'creators.identifiers.gnd': string;
'creators.identifiers.ror': string;
'creators.identifiers.isni': string;
'creators.affiliations.name': string;
'creators.identifiers.orcid': string;
'related_identifiers.scheme': string;
'contributors.affiliations.id': string;
'contributors.identifiers.gnd': string;
'contributors.identifiers.ror': string;
'additional_descriptions.notes': string;
'contributors.identifiers.isni': string;
'contributors.affiliations.name': string;
'contributors.identifiers.orcid': string;
'related_identifiers.identifier': string;
'additional_descriptions.abstract': string;
'additional_descriptions.methods.eng': string;
'related_identifiers.relation_type.id': string;
'related_identifiers.resource_type.id': string;
'additional_titles.alternative-title.eng': string;
};
errors: Array<{
msg: string;
loc: string;
type: string;
}>;
task: {
id: string;
};
generated_record_id?: string;
serializer_data?: {
files?: {
enabled: boolean;
};
access?: {
files: string;
record: string;
};
metadata?: {
title: string;
rights?: Array<{
id: string;
}>;
version?: string;
creators?: Array<{
affiliations?: Array<{
name: string;
}>;
person_or_org?: {
type: string;
given_name: string;
family_name: string;
identifiers?: Array<{
scheme: string;
identifier: string;
}>;
};
}>;
languages?: Array<{
id: string;
}>;
publisher?: string;
description?: string;
contributors?: Array<{
role?: {
id: string;
};
affiliations?: Array<{
name: string;
}>;
person_or_org?: {
type: string;
given_name: string;
family_name: string;
identifiers?: Array<{
scheme: string;
identifier: string;
}>;
};
}>;
resource_type?: {
id: string;
};
publication_date?: string;
additional_descriptions?: Array<{
lang?: {
id: string;
};
type?: {
id: string;
};
description: string;
}>;
};
};
transformed_data?: {
files?: {
enabled: boolean;
};
access?: {
files: string;
record: string;
};
$schema?: string;
metadata?: {
title: string;
rights?: Array<{
id: string;
}>;
version?: string;
creators?: Array<{
affiliations?: Array<{
name: string;
}>;
person_or_org?: {
name: string;
type: string;
given_name: string;
family_name: string;
identifiers?: Array<{
scheme: string;
identifier: string;
}>;
};
}>;
languages?: Array<{
id: string;
}>;
publisher?: string;
description?: string;
contributors?: Array<{
role?: {
id: string;
};
affiliations?: Array<{
name: string;
}>;
person_or_org?: {
name: string;
type: string;
given_name: string;
family_name: string;
identifiers?: Array<{
scheme: string;
identifier: string;
}>;
};
}>;
resource_type?: {
id: string;
};
publication_date?: string;
additional_descriptions?: Array<{
lang?: {
id: string;
};
type?: {
id: string;
};
description: string;
}>;
};
};
community_uuids?: {
ids: string[];
default: string;
};
record_files?: string[];
validated_record_files?: Array<{
key: string;
size: number;
origin: string;
full_path: string;
}>;
existing_record_id?: string;
}
export declare interface InvenioImporterRecords {
hits: {
hits: InvenioImporterRecord[];
total: number;
};
}
export declare type InvenioImporterRecordStates = (typeof InvenioImporterRecordStatus)[keyof typeof InvenioImporterRecordStatus];
export declare const InvenioImporterRecordStatus: {
readonly CREATED: "created";
readonly VALIDATING: "validating";
readonly SERIALIZER_VALIDATION_FAILED: "serializer validation failed";
readonly VALIDATION_FAILED: "validation failed";
readonly VALIDATED: "validated";
readonly IMPORT_FAILED: "import failed";
readonly IMPORTED: "success";
};
export declare type InvenioImporterTaskStates = (typeof ImporterTaskStates)[keyof typeof ImporterTaskStates];
export declare interface InvenioNewImportTask {
title: string;
description: string;
mode: 'import' | 'delete';
status: string;
startTime?: null;
endTime?: null;
recordType: string;
serializer: string;
options?: TaskOptions;
}
export declare interface InvenioTask {
id: string;
created: string;
updated: string;
links: {
self: string;
self_html: string;
edit_html: string;
metadata: string;
};
revision_id: number;
title: string;
description: string;
mode: string;
record_type: string;
serializer: string;
start_time?: string | null;
end_time?: string | null;
records_status?: Record<InvenioImporterRecordStates, number> & {
total_records: number;
};
status: InvenioImporterTaskStates;
files: {
enabled: boolean;
};
started_by: {
id: number;
username: string | null;
email: string;
};
options: TaskOptions;
}
declare interface LabelProps extends default_2.ComponentProps<typeof Label> {
status: InvenioTask['status'] | InvenioImporterRecordStates;
}
/** Enabled search result layout modes. */
export declare interface LayoutOptions {
/** Whether grid view is available. */
gridView: boolean;
/** Whether list view is available. */
listView: boolean;
}
/**
* Converts a raw API option key into a supported canonical task option key.
*
* @param optionKey - Raw option key from the API or form state.
* @returns The canonical option key when supported, otherwise `null`.
*/
export declare const normalizeTaskOptionKey: (optionKey: string) => TaskOptionKey | null;
export declare type OrchestrationSteps = (typeof OrchestrationStepsEnum)[keyof typeof OrchestrationStepsEnum];
export declare const OrchestrationStepsEnum: {
readonly CREATING_TASK: "Creating Task";
readonly UPLOADING_METADATA: "Uploading Metadata";
readonly UPLOADING_FILES: "Uploading Files";
readonly UPDATING_METADATA: "Updating Metadata";
readonly UPDATING_FILES: "Updating Files";
readonly VALIDATING: "Validating";
readonly EXECUTING: "Executing";
readonly UPDATING: "Updating";
};
export declare interface OverridableComponents {
[key: string]: default_2.ComponentType<unknown>;
}
export declare interface OverridableComponentsTyped<T = Record<string, unknown>> {
[key: string]: default_2.ComponentType<T>;
}
/** Pagination configuration for search results. */
export declare interface PaginationOptions {
/** Default selected page size. */
defaultValue: number;
/** Maximum number of results SearchKit can page through. */
maxTotalResults: number;
/** Available page size options. */
resultsPerPage: ResultsPerPageOption[];
}
export declare const ProgressLoading: ({ progress, showPercentage }: ProgressLoadingProps) => default_2.JSX.Element;
declare interface ProgressLoadingProps {
progress: Record<OrchestrationSteps, number>;
showPercentage?: boolean;
}
/**
* Represents the query filters applied to the search.
*/
export declare interface QueryFilters {
[fieldName: string]: FilterValue[] | FilterValue;
}
/** CSV export API configuration for records search. */
export declare interface RecordsExportApiConfig {
/** Primary Accept header or ordered headers for export requests. */
accept: string | string[];
/** Additional Accept headers retried after 406 responses. */
fallbackAccepts?: string[];
/** Download filename used when the API does not provide one. */
filename: string;
/** Additional headers sent with the export request. */
headers?: Record<string, string>;
/** Whether exports should include current pagination params. */
includePagination: boolean;
/** Records API URL used for search exports. */
url: string;
}
/** Options shared by single-record and search-result CSV export requests. */
declare interface RecordsExportApiOptions {
/** Primary Accept header or ordered list of Accept headers to try first. */
accept?: string | string[];
/** Additional Accept headers to retry when the API rejects a media type. */
fallbackAccepts?: string[];
/** Download filename to use when the API does not provide one. */
filename?: string;
/** Additional request headers to send with the export request. */
headers?: Record<string, string>;
/** Records API URL used as the export endpoint. */
url?: string;
}
/** Shared visual options for CSV export buttons. */
declare interface RecordsExportButtonBaseProps extends RecordsExportApiOptions {
/** Whether the Semantic UI button should use basic styling. */
basic?: boolean;
/** Additional CSS class name applied to the button. */
className?: string;
/** Whether the Semantic UI button should use compact spacing. */
compact?: boolean;
/** Visible button label shown when iconOnly is false. */
content?: string;
/** Whether the export button is disabled. */
disabled?: boolean;
/** Whether to render only the export icon. */
iconOnly?: boolean;
/** Whether search exports should include current pagination params. */
includePagination?: boolean;
/** React-SearchKit query state used by search-result export. */
queryState?: Record<string, unknown>;
/** Semantic UI button size. */
size?: 'mini' | 'tiny' | 'small' | 'medium' | 'large';
/** Tooltip text shown when the export button has no error. */
title?: string;
}
/** Results-per-page dropdown option. */
export declare interface ResultsPerPageOption {
/** Human-readable option label. */
text: string;
/** Page size value sent to SearchKit. */
value: number;
}
/**
* Removes unsupported or malformed task option entries from an unknown input.
*
* @param options - Raw options payload from the API or form state.
* @returns A sanitized task options object containing only known boolean flags.
*/
export declare const sanitizeTaskOptions: (options: unknown) => TaskOptions;
/** Renders the reusable SearchKit-powered importer search UI. */
export declare const Search: default_2.FC<SearchProps>;
/** Axios configuration used by the SearchKit API client. */
export declare interface SearchApiConfig {
/** Axios request options for the search endpoint. */
axios: {
/** Headers sent with each search request. */
headers?: Record<string, string>;
/** Search endpoint URL. */
url?: string;
/** Whether requests should include browser credentials. */
withCredentials?: boolean;
};
/** Invenio-specific SearchKit serializer configuration. */
invenio?: {
/** Name of the request serializer used by React-SearchKit. */
requestSerializer?: string;
};
}
/** Search API config override accepted by the Search component. */
declare type SearchApiConfigOverride = Partial<Omit<SearchApiConfig, 'axios'>> & {
/** Partial Axios config merged with the default SearchKit Axios config. */
axios?: Partial<SearchApiConfig['axios']>;
};
/** Full configuration object for the reusable search UI. */
export declare interface SearchConfig {
/** Facet aggregation configuration consumed by SearchKit. */
aggs: unknown[];
/** SearchKit application ID used for overridable component keys. */
appId: string;
/** Sort options used before the user enters a query. */
defaultSortingOnEmptyQueryString: DefaultSortingOption[];
/** Records CSV export configuration. */
exportApi: RecordsExportApiConfig;
/** Initial SearchKit query state. */
initialQueryState: InitialQueryState;
/** Enabled result layout modes. */
layoutOptions: LayoutOptions;
/** Pagination and page-size configuration. */
paginationOptions: PaginationOptions;
/** Base UI route used for task detail links. */
resultPath: string;
/** Search API configuration. */
searchApi: SearchApiConfig;
/** Sort dropdown options. */
sortOptions: SortOption[];
/** Whether manual sort direction controls are disabled. */
sortOrderDisabled: boolean;
/** Whether to render the search-level CSV export button. */
showExportButton?: boolean;
/** Whether to render facet filters. */
showFacets?: boolean;
/** Whether to render the new import modal trigger. */
showImportModal?: boolean;
/** Whether to render pagination and page-size controls. */
showSearchFooter?: boolean;
}
/** Partial search config accepted by the Search component. */
export declare type SearchConfigOverride = Partial<Omit<SearchConfig, 'exportApi' | 'searchApi'>> & {
/** Partial CSV export config merged with the default export config. */
exportApi?: Partial<RecordsExportApiConfig>;
/** Partial search API config merged with the default search API config. */
searchApi?: SearchApiConfigOverride;
};
/** Props for rendering the reusable Search component. */
export declare interface SearchProps {
/** Optional search configuration overrides. */
config?: SearchConfigOverride;
/** Optional component override registry for react-overridable. */
overriddenComponents?: OverridableComponents;
}
/**
* Search results data structure from the API.
*/
export declare interface SearchResultsData {
aggregations?: Aggregations;
hits?: unknown[];
total?: number;
}
/** Sort option shown in the search sort dropdown. */
export declare interface SortOption {
/** Sort key sent to the API. */
sortBy: string;
/** Human-readable sort option label. */
text: string;
}
export declare const Spinner: () => default_2.JSX.Element;
export declare const StatusIcon: default_2.FC<StatusIconProps>;
declare interface StatusIconProps extends default_2.ComponentProps<typeof Icon> {
status: InvenioTask['status'] | InvenioImporterRecordStates;
}
export declare const StatusLabel: default_2.FC<LabelProps>;
/**
* Canonical task option keys supported by the UI and API payloads.
*
* This list is used as the single source of truth for typing, sanitization,
* and ordered rendering of task options across the application.
*/
export declare const TASK_OPTION_KEYS: readonly ["doi_minting", "publish"];
export declare const TaskConfiguration: default_2.FC<TaskConfigurationProps>;
declare interface TaskConfigurationProps {
task: InvenioTask;
}
export declare const TaskDetails: default_2.FC<TaskDetailsProps>;
export declare interface TaskDetailsProps {
taskId: string;
}
export declare const TaskHeader: default_2.FC<TaskHeaderProps>;
declare interface TaskHeaderProps {
task: InvenioTask;
totalRecords: number;
validatedRecords: number;
errorRecords: number;
successRecords: number;
isRefreshing: boolean;
isRunningTask: boolean;
onRefresh: () => void;
onRunTask: () => Promise<void>;
}
/**
* Union of supported task option keys.
*/
export declare type TaskOptionKey = (typeof TASK_OPTION_KEYS)[number];
/**
* Task option flags accepted by the importer.
*
* The shape is partial because the API may omit options that are not available
* for a given record type.
*/
export declare type TaskOptions = Partial<Record<TaskOptionKey, boolean>>;
export declare interface TaskOrchestrationOptions {
autoValidate?: boolean;
autoExecute?: boolean;
onProgress?: (step: OrchestrationSteps, progress?: number) => void;
onError?: (error: Error, step: OrchestrationSteps) => void;
}
export declare const TaskRecordItem: default_2.FC<TaskRecordItemProps>;
/** Props for rendering a single importer task record row. */
export declare interface TaskRecordItemProps {
/** Importer record displayed by the table row. */
result: InvenioImporterRecord;
/** Row index supplied by React-SearchKit. */
index: number;
}
export declare interface UploadableFile {
file: File;
id: string;
error?: string;
}
/**
* Custom hook for managing file uploads.
* Provides functionality to add, remove, and clear files,
* along with validation against accepted types and size limits.
*/
export declare const useFileUploader: ({ onUploadError }?: UseFileUploaderProps) => {
uploadFiles: UploadableFile[];
isUploading: boolean;
addFiles: (newFiles: File[], accept?: string, maxFiles?: number, maxTotalSize?: number, skipEmptyFiles?: boolean) => UploadableFile[];
removeFile: (fileId: string) => void;
clearFiles: () => void;
};
declare interface UseFileUploaderProps {
taskId?: string;
onUploadComplete?: (files: File[]) => void;
onUploadError?: (error: string) => void;
}
export { }