Skip to content

Commit

Permalink
[Enhancement #581] Implement vocabulary translations import.
Browse files Browse the repository at this point in the history
UI is a part of the vocabulary content import dialog - using tabs to separate the two modes.
  • Loading branch information
ledsoft committed Dec 2, 2024
1 parent afe82ae commit d9f208c
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 110 deletions.
9 changes: 8 additions & 1 deletion src/action/AsyncImportActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ import { Action } from "redux";
import { loadVocabulary } from "./AsyncActions";
import Utils from "../util/Utils";

export function importIntoExistingVocabulary(vocabularyIri: IRI, data: File) {
export function importIntoExistingVocabulary(
vocabularyIri: IRI,
data: File,
translationsOnly: boolean = false
) {
const action = { type: ActionType.IMPORT_VOCABULARY };
const formData = new FormData();
formData.append("file", data, "thesaurus");
formData.append("namespace", vocabularyIri.namespace!);
if (translationsOnly) {
formData.append("translationsOnly", true.toString());
}
return (dispatch: ThunkDispatch) => {
dispatch(asyncActionRequest(action, true));
return Ajax.post(
Expand Down
6 changes: 3 additions & 3 deletions src/component/vocabulary/VocabularyActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
GoCloudUpload,
GoRepoForked,
} from "react-icons/go";
import ImportBackupOfVocabulary from "./importing/ImportBackupOfVocabulary";
import LoadVocabularyFromFile from "./importing/LoadVocabularyFromFile";
import { FaCamera } from "react-icons/fa";
import Vocabulary from "../../model/Vocabulary";
import IfVocabularyActionAuthorized from "./authorization/IfVocabularyActionAuthorized";
Expand All @@ -28,7 +28,7 @@ interface VocabularyActionsProps {
vocabulary: Vocabulary;
onAnalyze: () => void;
onExport: () => void;
onImport: (file: File, rename: Boolean) => Promise<any>;
onImport: (file: File, translationsOnly: boolean) => Promise<any>;
onCreateSnapshot: () => void;
}

Expand All @@ -46,7 +46,7 @@ const VocabularyActions: React.FC<VocabularyActionsProps> = ({

return (
<>
<ImportBackupOfVocabulary
<LoadVocabularyFromFile
onImport={onImport}
showDialog={showImportDialog}
closeDialog={() => setShowImportDialog(false)}
Expand Down
19 changes: 10 additions & 9 deletions src/component/vocabulary/VocabularySummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ interface VocabularySummaryProps
) => Promise<boolean>;
updateVocabulary: (vocabulary: Vocabulary) => Promise<any>;
removeVocabulary: (vocabulary: Vocabulary) => Promise<any>;
importSkos: (iri: IRI, file: File) => Promise<any>;
importSkos: (
iri: IRI,
file: File,
translationsOnly?: boolean
) => Promise<any>;
executeTextAnalysisOnAllTerms: (iri: IRI) => void;
createSnapshot: (iri: IRI) => Promise<any>;
updateDocument: (document: Document) => Promise<Resource | null>;
Expand Down Expand Up @@ -210,16 +214,13 @@ export class VocabularySummary extends EditableComponent<
this.setState({ showSnapshotDialog: !this.state.showSnapshotDialog });
};

private onImport = (file: File) =>
private onImport = (file: File, translationsOnly: boolean) =>
this.props.importSkos(
VocabularyUtils.create(this.props.vocabulary.iri),
file
file,
translationsOnly
);

public onFileAdded = () => {
this.loadVocabulary();
};

private onExecuteTextAnalysisOnAllTerms = () => {
this.props.executeTextAnalysisOnAllTerms(
VocabularyUtils.create(this.props.vocabulary.iri)
Expand Down Expand Up @@ -370,8 +371,8 @@ export default connect(
dispatch(updateVocabulary(vocabulary)),
removeVocabulary: (vocabulary: Vocabulary) =>
dispatch(removeVocabulary(vocabulary)),
importSkos: (iri: IRI, file: File) =>
dispatch(importIntoExistingVocabulary(iri, file)),
importSkos: (iri: IRI, file: File, translationsOnly?: boolean) =>
dispatch(importIntoExistingVocabulary(iri, file, translationsOnly)),
executeTextAnalysisOnAllTerms: (iri: IRI) =>
dispatch(executeTextAnalysisOnAllTerms(iri)),
createSnapshot: (iri: IRI) => dispatch(createVocabularySnapshot(iri)),
Expand Down
86 changes: 0 additions & 86 deletions src/component/vocabulary/importing/ImportBackupOfVocabulary.tsx

This file was deleted.

64 changes: 64 additions & 0 deletions src/component/vocabulary/importing/ImportTranslationsDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from "react";
import UploadFile from "../../resource/file/UploadFile";
import { Button, ButtonToolbar, Col, Form, Row } from "reactstrap";
import { useI18n } from "../../hook/useI18n";
import { FormattedMessage } from "react-intl";

export const ImportTranslationsDialog: React.FC<{
onSubmit: (file: File) => void;
onCancel: () => void;
onDownloadTemplate: () => void;
}> = ({ onSubmit, onCancel, onDownloadTemplate }) => {
const { i18n } = useI18n();
const [file, setFile] = React.useState<File>();
const cannotSubmit = () => !file;
return (
<>
<Form id="vocabulary-translations-import">
<div className="mb-3">
<FormattedMessage
id="vocabulary.summary.import.translations.label"
values={{
a: (chunks: any) => (
<span
role="button"
className="bold btn-link link-like"
onClick={onDownloadTemplate}
title={i18n(
"vocabulary.summary.import.excel.template.tooltip"
)}
>
{chunks}
</span>
),
}}
/>
</div>
<UploadFile setFile={setFile} />
<Row>
<Col xs={12}>
<ButtonToolbar className="d-flex justify-content-center mt-4">
<Button
id="upload-translations-file-submit"
onClick={() => onSubmit(file!)}
color="success"
size="sm"
disabled={cannotSubmit()}
>
{i18n("vocabulary.summary.import.action")}
</Button>
<Button
id="upload-translations-file-cancel"
onClick={onCancel}
color="outline-dark"
size="sm"
>
{i18n("cancel")}
</Button>
</ButtonToolbar>
</Col>
</Row>
</Form>
</>
);
};
4 changes: 2 additions & 2 deletions src/component/vocabulary/importing/ImportVocabularyDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import "./ImportVocabularyDialog.scss";

interface ImportVocabularyDialogProps {
propKeyPrefix: string;
onCreate: (file: File, rename: Boolean) => any;
onCreate: (file: File, rename: boolean) => any;
onCancel: () => void;
allowRename?: boolean;
}

const ImportVocabularyDialog = (props: ImportVocabularyDialogProps) => {
const { i18n } = useI18n();
const [file, setFile] = useState<File>();
const [rename, setRename] = useState<Boolean>(false);
const [rename, setRename] = useState<boolean>(false);

const onCreate = () => {
if (!file) {
Expand Down
116 changes: 116 additions & 0 deletions src/component/vocabulary/importing/LoadVocabularyFromFile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { useState } from "react";
import { Alert, Modal, ModalBody, ModalHeader } from "reactstrap";
import { useI18n } from "../../hook/useI18n";
import PromiseTrackingMask from "../../misc/PromiseTrackingMask";
import { trackPromise } from "react-promise-tracker";
import { FormattedMessage } from "react-intl";
import ImportVocabularyDialog from "./ImportVocabularyDialog";
import { useDispatch, useSelector } from "react-redux";
import { ThunkDispatch } from "../../../util/Types";
import { downloadExcelTemplate } from "../../../action/AsyncImportActions";
import TermItState from "../../../model/TermItState";
import Tabs from "../../misc/Tabs";
import { ImportTranslationsDialog } from "./ImportTranslationsDialog";

interface LoadVocabularyFromFileProps {
showDialog: boolean;
onImport: (file: File, translationsOnly: boolean) => Promise<any>;
closeDialog: () => void;
}

export const LoadVocabularyFromFile: React.FC<LoadVocabularyFromFileProps> = ({
showDialog,
closeDialog,
onImport,
}) => {
const { i18n } = useI18n();
const dispatch: ThunkDispatch = useDispatch();
const [selectedTab, setSelectedTab] = useState<string>(
"vocabulary.summary.import.dialog.tab.replaceContent"
);
const onClose = () => {
closeDialog();
setSelectedTab("vocabulary.summary.import.dialog.tab.replaceContent");
};
const onImportContent = (file: File) =>
trackPromise(onImport(file, false), "vocabulary-import").then(onClose);
const onImportTranslations = (file: File) => {
trackPromise(onImport(file, true), "vocabulary-import").then(onClose);
};
const downloadTemplate = () => {
dispatch(downloadExcelTemplate());
};
const vocabularyNotEmpty =
(useSelector((state: TermItState) => state.vocabulary.termCount) || 0) > 0;

return (
<>
<Modal isOpen={showDialog} toggle={onClose}>
<ModalHeader>
{i18n("vocabulary.summary.import.dialog.title")}
</ModalHeader>
<ModalBody>
<PromiseTrackingMask area="vocabulary-import" />
<Tabs
activeTabLabelKey={selectedTab}
contentClassName="mt-3"
tabs={{
"vocabulary.summary.import.dialog.tab.replaceContent": (
<>
<div className="mb-1">
<FormattedMessage id="vocabulary.summary.import.dialog.label" />
</div>
<ul>
<li>
<FormattedMessage id="vocabulary.summary.import.dialog.skosImport" />
</li>
<li>
<FormattedMessage
id="vocabulary.summary.import.dialog.excelImport"
values={{
a: (chunks: any) => (
<span
role="button"
className="bold btn-link link-like"
onClick={downloadTemplate}
title={i18n(
"vocabulary.summary.import.excel.template.tooltip"
)}
>
{chunks}
</span>
),
}}
/>
</li>
</ul>
{vocabularyNotEmpty && (
<Alert color="warning">
<FormattedMessage id="vocabulary.summary.import.nonEmpty.warning" />
</Alert>
)}
<ImportVocabularyDialog
propKeyPrefix="vocabulary.summary.import"
onCreate={onImportContent}
onCancel={onClose}
allowRename={false}
/>
</>
),
"vocabulary.summary.import.dialog.tab.translations": (
<ImportTranslationsDialog
onSubmit={onImportTranslations}
onCancel={onClose}
onDownloadTemplate={downloadTemplate}
/>
),
}}
changeTab={setSelectedTab as (t: string) => void}
/>
</ModalBody>
</Modal>
</>
);
};

export default LoadVocabularyFromFile;
Loading

0 comments on commit d9f208c

Please sign in to comment.