Skip to content

Commit

Permalink
feat: add import taxonomy button
Browse files Browse the repository at this point in the history
  • Loading branch information
rpenido committed Nov 8, 2023
1 parent 080b03e commit acf683e
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 7 deletions.
28 changes: 21 additions & 7 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 {
Add,
} from '@edx/paragon/icons';
import { StudioFooter } from '@edx/frontend-component-footer';
import { useIntl } from '@edx/frontend-platform/i18n';

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 './taxonomy-card';
import { useTaxonomyListDataResponse, useIsTaxonomyListDataLoaded } from './data/apiHooks';

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

const TaxonomyListPage = () => {
const intl = useIntl();
const useTaxonomyListData = () => {
Expand All @@ -23,12 +43,6 @@ const TaxonomyListPage = () => {

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 @@ -50,7 +64,7 @@ const TaxonomyListPage = () => {
<SubHeader
title={intl.formatMessage(messages.headerTitle)}
titleActions={getOrgSelect()}
headerActions={getHeaderButtons()}
headerActions={<TaxonomyListHeaderButtons />}
hideBorder
/>
</Container>
Expand Down
91 changes: 91 additions & 0 deletions src/taxonomy/import-tags/data/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// @ts-check
import messages from '../messages';
import { importNewTaxonomy } from './api';

/**
* Import a new taxonomy
* @param {import("@edx/frontend-platform/i18").intlShape} intl

Check failure on line 7 in src/taxonomy/import-tags/data/actions.js

View workflow job for this annotation

GitHub Actions / tests

Cannot find module '@edx/frontend-platform/i18' or its corresponding type declarations.
* @returns {Promise<void>}
*/
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) => {
/*
* 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 failure on line 33 in src/taxonomy/import-tags/data/actions.js

View workflow job for this annotation

GitHub Actions / tests

'event.target' is possibly 'null'.

Check failure on line 33 in src/taxonomy/import-tags/data/actions.js

View workflow job for this annotation

GitHub Actions / tests

Property 'files' does not exist on type 'EventTarget'.
if (!file) {
resolve(null);
}
resolve(file);
document.body.removeChild(fileInput);
});

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

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

if (taxonomyName == null) {
break;
}

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

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

const file = await selectFile();

if (!file) {
return;
}

const taxonomyName = getTaxonomyName();
if (taxonomyName == null) {
return;
}

const taxonomyDescription = getTaxonomyDescription();
if (taxonomyDescription == null) {
return;
}

importNewTaxonomy(taxonomyName, taxonomyDescription, file)
.then(() => {
alert(intl.formatMessage(messages.importTaxonomySuccess));
})
.catch((error) => {
alert(intl.formatMessage(messages.importTaxonomyError));
console.error(error.response);
});
};

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);

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

return camelCaseObject(data);
}
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 acf683e

Please sign in to comment.