Skip to content

Commit

Permalink
feat: add import taxonomy feature
Browse files Browse the repository at this point in the history
  • Loading branch information
rpenido committed Nov 9, 2023
1 parent 78eb512 commit 0c60c37
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 8 deletions.
30 changes: 22 additions & 8 deletions src/taxonomy/TaxonomyListPage.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
import React from 'react';
import {
Button,
CardView,
Container,
DataTable,
Spinner,
} from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Add,
} from '@edx/paragon/icons';
import { injectIntl, intlShape, useIntl } from '@edx/frontend-platform/i18n';
import { StudioFooter } from '@edx/frontend-component-footer';

import Header from '../header';
import SubHeader from '../generic/sub-header/SubHeader';
import { actions as importActions } from './import-tags';
import messages from './messages';
import TaxonomyCard from './TaxonomyCard';
import { useTaxonomyListDataResponse, useIsTaxonomyListDataLoaded } from './api/hooks/selectors';

const TaxonomyListHeaderButtons = () => {
const intl = useIntl();
return (
<>
<Button variant="outline-primary" disabled>
{intl.formatMessage(messages.downloadTemplateButtonLabel)}
</Button>
<Button iconBefore={Add} onClick={() => importActions.importTaxonomy(intl)}>

Check warning on line 29 in src/taxonomy/TaxonomyListPage.jsx

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/TaxonomyListPage.jsx#L29

Added line #L29 was not covered by tests
{intl.formatMessage(messages.importButtonLabel)}
</Button>
</>
);
};

const TaxonomyListPage = ({ intl }) => {
const useTaxonomyListData = () => {
const taxonomyListData = useTaxonomyListDataResponse();
Expand All @@ -22,12 +42,6 @@ const TaxonomyListPage = ({ intl }) => {

const { taxonomyListData, isLoaded } = useTaxonomyListData();

const getHeaderButtons = () => (
// Download template and import buttons.
// TODO Add functionality to this buttons.
undefined
);

const getOrgSelect = () => (
// Organization select component
// TODO Add functionality to this component
Expand All @@ -49,7 +63,7 @@ const TaxonomyListPage = ({ intl }) => {
<SubHeader
title={intl.formatMessage(messages.headerTitle)}
titleActions={getOrgSelect()}
headerActions={getHeaderButtons()}
headerActions={<TaxonomyListHeaderButtons />}
hideBorder
/>
</Container>
Expand Down
85 changes: 85 additions & 0 deletions src/taxonomy/import-tags/data/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import messages from '../messages';
import { importNewTaxonomy } from './api';

const importTaxonomy = async (intl) => {
/*
* This function is a temporary "Barebones" implementation of the import
* functionality with `prompt` and `alert`. It is intended to be replaced
* with a component that shows a `ModalDialog` in the future.
* See: https://github.com/openedx/modular-learning/issues/116
*/
/* eslint-disable no-alert */
/* eslint-disable no-console */

const selectFile = async () => new Promise((resolve) => {

Check warning on line 14 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L14

Added line #L14 was not covered by tests
/*
* This function get a file from the user. It does this by creating a
* file input element, and then clicking it. This allows us to get a file
* from the user without using a form. The file input element is created
* and appended to the DOM, then clicked. When the user selects a file,
* the change event is fired, and the file is resolved.
* The file input element is then removed from the DOM.
*/
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json,.csv';
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];

Check warning on line 27 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L23-L27

Added lines #L23 - L27 were not covered by tests
if (!file) {
resolve(null);

Check warning on line 29 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L29

Added line #L29 was not covered by tests
}
resolve(file);
document.body.removeChild(fileInput);

Check warning on line 32 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L31-L32

Added lines #L31 - L32 were not covered by tests
});

document.body.appendChild(fileInput);
fileInput.click();

Check warning on line 36 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L35-L36

Added lines #L35 - L36 were not covered by tests
});

const getTaxonomyName = () => {
let taxonomyName = null;
while (!taxonomyName) {
taxonomyName = prompt(intl.formatMessage(messages.promptTaxonomyName));

Check warning on line 42 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L39-L42

Added lines #L39 - L42 were not covered by tests

if (taxonomyName == null) {
break;

Check warning on line 45 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L45

Added line #L45 was not covered by tests
}

if (!taxonomyName) {
alert(intl.formatMessage(messages.promptTaxonomyNameRequired));

Check warning on line 49 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L49

Added line #L49 was not covered by tests
}
}
return taxonomyName;

Check warning on line 52 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L52

Added line #L52 was not covered by tests
};

const getTaxonomyDescription = () => prompt(intl.formatMessage(messages.promptTaxonomyDescription));

Check warning on line 55 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L55

Added line #L55 was not covered by tests

const file = await selectFile();

Check warning on line 57 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L57

Added line #L57 was not covered by tests

if (!file) {
return;

Check warning on line 60 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L60

Added line #L60 was not covered by tests
}

const taxonomyName = getTaxonomyName();

Check warning on line 63 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L63

Added line #L63 was not covered by tests
if (taxonomyName == null) {
return;

Check warning on line 65 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L65

Added line #L65 was not covered by tests
}

const taxonomyDescription = getTaxonomyDescription();

Check warning on line 68 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L68

Added line #L68 was not covered by tests
if (taxonomyDescription == null) {
return;

Check warning on line 70 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L70

Added line #L70 was not covered by tests
}

importNewTaxonomy(taxonomyName, taxonomyDescription, file)
.then(() => {
alert(intl.formatMessage(messages.importTaxonomySuccess));

Check warning on line 75 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L73-L75

Added lines #L73 - L75 were not covered by tests
})
.catch((error) => {
alert(intl.formatMessage(messages.importTaxonomyError));
console.error(error.response);

Check warning on line 79 in src/taxonomy/import-tags/data/actions.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/actions.js#L77-L79

Added lines #L77 - L79 were not covered by tests
});
};

export default {
importTaxonomy,
};
28 changes: 28 additions & 0 deletions src/taxonomy/import-tags/data/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// @ts-check
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;

const getTaxonomyImportApiUrl = () => new URL(
'api/content_tagging/v1/taxonomies/import/',
getApiBaseUrl(),
).href;

/**
* Import a new taxonomy
* @param {string} taxonomyName
* @param {string} taxonomyDescription
* @param {File} file
* @returns {Promise<Object>}
*/ // eslint-disable-next-line import/prefer-default-export
export async function importNewTaxonomy(taxonomyName, taxonomyDescription, file) {
const formData = new FormData();
formData.append('taxonomy_name', taxonomyName);
formData.append('taxonomy_description', taxonomyDescription);
formData.append('file', file);

Check warning on line 23 in src/taxonomy/import-tags/data/api.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/api.js#L19-L23

Added lines #L19 - L23 were not covered by tests

const { data } = await getAuthenticatedHttpClient().post(getTaxonomyImportApiUrl(), formData);

Check warning on line 25 in src/taxonomy/import-tags/data/api.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/api.js#L25

Added line #L25 was not covered by tests

return camelCaseObject(data);

Check warning on line 27 in src/taxonomy/import-tags/data/api.js

View check run for this annotation

Codecov / codecov/patch

src/taxonomy/import-tags/data/api.js#L27

Added line #L27 was not covered by tests
}
5 changes: 5 additions & 0 deletions src/taxonomy/import-tags/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import actions from './data/actions';

export {
actions, // eslint-disable-line import/prefer-default-export
};
26 changes: 26 additions & 0 deletions src/taxonomy/import-tags/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineMessages } from '@edx/frontend-platform/i18n';

const messages = defineMessages({
promptTaxonomyName: {
id: 'course-authoring.import-tags.prompt.taxonomy-name',
defaultMessage: 'Enter a name for the new taxonomy',
},
promptTaxonomyNameRequired: {
id: 'course-authoring.import-tags.prompt.taxonomy-name.required',
defaultMessage: 'You must enter a name for the new taxonomy',
},
promptTaxonomyDescription: {
id: 'course-authoring.import-tags.prompt.taxonomy-description',
defaultMessage: 'Enter a description for the new taxonomy',
},
importTaxonomySuccess: {
id: 'course-authoring.import-tags.success',
defaultMessage: 'Taxonomy imported successfully',
},
importTaxonomyError: {
id: 'course-authoring.import-tags.error',
defaultMessage: 'Import failed - see details in the browser console',
},
});

export default messages;

0 comments on commit 0c60c37

Please sign in to comment.