From a52cc8540ebff2f5ed9355262ec1c8f580c1fa0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B4mulo=20Penido?= Date: Fri, 20 Oct 2023 15:36:28 -0300 Subject: [PATCH] feat: add detail taxonomy page --- src/index.jsx | 19 ++-- src/taxonomy/api/hooks/api.js | 20 ++++ src/taxonomy/api/hooks/selectors.js | 34 ++++++- src/taxonomy/index.js | 2 +- src/taxonomy/taxonomy-detail/TagListTable.jsx | 42 ++++++++ .../taxonomy-detail/TaxonomyDetailPage.jsx | 97 +++++++++++++++++++ .../TaxonomyDetailSideCard.jsx | 27 ++++++ src/taxonomy/taxonomy-detail/index.js | 2 + 8 files changed, 235 insertions(+), 8 deletions(-) create mode 100644 src/taxonomy/taxonomy-detail/TagListTable.jsx create mode 100644 src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx create mode 100644 src/taxonomy/taxonomy-detail/TaxonomyDetailSideCard.jsx create mode 100644 src/taxonomy/taxonomy-detail/index.js diff --git a/src/index.jsx b/src/index.jsx index f671a90f84..46ba868924 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -22,7 +22,7 @@ import CourseAuthoringRoutes from './CourseAuthoringRoutes'; import Head from './head/Head'; import { StudioHome } from './studio-home'; import CourseRerun from './course-rerun'; -import { TaxonomyListPage } from './taxonomy'; +import { TaxonomyDetailPage, TaxonomyListPage } from './taxonomy'; import 'react-datepicker/dist/react-datepicker.css'; import './index.scss'; @@ -71,11 +71,18 @@ const App = () => { }} /> {process.env.ENABLE_TAGGING_TAXONOMY_PAGES === 'true' && ( - - - + <> + + { + const { params: { taxonomyId } } = match; + return ( + + ); + }} + /> + )} diff --git a/src/taxonomy/api/hooks/api.js b/src/taxonomy/api/hooks/api.js index 3efaea019a..3e4a8dba1c 100644 --- a/src/taxonomy/api/hooks/api.js +++ b/src/taxonomy/api/hooks/api.js @@ -8,6 +8,13 @@ const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL; const getTaxonomyListApiUrl = () => new URL('api/content_tagging/v1/taxonomies/?enabled=true', getApiBaseUrl()).href; const getExportTaxonomyApiUrl = (pk, format) => new URL( `api/content_tagging/v1/taxonomies/${pk}/export/?output_format=${format}`, +); + +/** + * @param {number} taxonomyId + */ +export const getTaxonomyDetailApiUrl = (taxonomyId) => new URL( + `api/content_tagging/v1/taxonomies/${taxonomyId}/`, getApiBaseUrl(), ).href; @@ -51,3 +58,16 @@ export const useExportTaxonomy = () => { return useMutation(exportTaxonomy); }; + +/** + * @param {number} taxonomyId + * @returns {import('@tanstack/react-query').UseQueryResult} + */ +export const useTaxonomyDetailData = (taxonomyId) => ( + useQuery({ + queryKey: ['taxonomyList', taxonomyId], + queryFn: () => getAuthenticatedHttpClient().get(getTaxonomyDetailApiUrl(taxonomyId)) + .then(camelCaseObject) + .then((response) => response.data), + }) +); diff --git a/src/taxonomy/api/hooks/selectors.js b/src/taxonomy/api/hooks/selectors.js index 970ae49392..c072c72e1e 100644 --- a/src/taxonomy/api/hooks/selectors.js +++ b/src/taxonomy/api/hooks/selectors.js @@ -1,5 +1,6 @@ // @ts-check import { + useTaxonomyDetailData, useTaxonomyListData, useExportTaxonomy, } from './api'; @@ -10,7 +11,7 @@ import { export const useTaxonomyListDataResponse = () => { const response = useTaxonomyListData(); if (response.status === 'success') { - return response.data.data; + return response.data; } return undefined; }; @@ -25,3 +26,34 @@ export const useIsTaxonomyListDataLoaded = () => ( export const useExportTaxonomyMutation = () => ( useExportTaxonomy() ); +/** + * @params {number} taxonomyId + * @returns {Pick} + */ +export const useTaxonomyDetailDataStatus = (taxonomyId) => { + const { + isError, + error, + isFetched, + isSuccess, + } = useTaxonomyDetailData(taxonomyId); + return { + isError, + error, + isFetched, + isSuccess, + }; +}; + +/** + * @params {number} taxonomyId + * @returns {import("../types.mjs").TaxonomyData | undefined} + */ +export const useTaxonomyDetailDataResponse = (taxonomyId) => { + const { isSuccess, data } = useTaxonomyDetailData(taxonomyId); + if (isSuccess) { + return data; + } + + return undefined; +}; diff --git a/src/taxonomy/index.js b/src/taxonomy/index.js index c857f10e6c..4fc3470bb7 100644 --- a/src/taxonomy/index.js +++ b/src/taxonomy/index.js @@ -1,2 +1,2 @@ -// eslint-disable-next-line import/prefer-default-export export { default as TaxonomyListPage } from './TaxonomyListPage'; +export { TaxonomyDetailPage } from './taxonomy-detail'; diff --git a/src/taxonomy/taxonomy-detail/TagListTable.jsx b/src/taxonomy/taxonomy-detail/TagListTable.jsx new file mode 100644 index 0000000000..3b37e06336 --- /dev/null +++ b/src/taxonomy/taxonomy-detail/TagListTable.jsx @@ -0,0 +1,42 @@ +import { + DataTable, + TextFilter, +} from '@edx/paragon'; +import Proptypes from 'prop-types'; + +const tagsSample = [ + { name: 'Tag 1' }, + { name: 'Tag 2' }, + { name: 'Tag 3' }, + { name: 'Tag 4' }, + { name: 'Tag 5' }, + { name: 'Tag 6' }, + { name: 'Tag 7' }, +]; + +const TagListTable = ({ tags }) => ( + + + + + + +); + +TagListTable.propTypes = { + tags: Proptypes.array.isRequired, +}; + +export default TagListTable; diff --git a/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx b/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx new file mode 100644 index 0000000000..d422efde18 --- /dev/null +++ b/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { + Container, + Layout, +} from '@edx/paragon'; +import Proptypes from 'prop-types'; + +import PermissionDeniedAlert from '../../generic/PermissionDeniedAlert'; +import Loading from '../../generic/Loading'; +import Header from '../../header'; +import SubHeader from '../../generic/sub-header/SubHeader'; +import TaxonomyDetailSideCard from './TaxonomyDetailSideCard'; +import TagListTable from './TagListTable'; +import { useTaxonomyDetailDataResponse, useTaxonomyDetailDataStatus } from '../api/hooks/selectors'; + +const TaxonomyDetailContent = ({ taxonomyId }) => { + const useTaxonomyDetailData = () => { + const { isError, isFetched } = useTaxonomyDetailDataStatus(taxonomyId); + const taxonomy = useTaxonomyDetailDataResponse(taxonomyId); + return { isError, isFetched, taxonomy }; + }; + + const { isError, isFetched, taxonomy } = useTaxonomyDetailData(taxonomyId); + + if (isError) { + return ( + + ); + } + + if (!isFetched) { + return ( + + ); + } + + if (taxonomy) { + return ( + <> +
+ + + +
+
+ + + + + + + + + +
+ + ); + } + + return undefined; +}; + +const TaxonomyDetailPage = ({ taxonomyId }) => ( + <> + +
+ + +); + +TaxonomyDetailPage.propTypes = { + taxonomyId: Proptypes.number, +}; + +TaxonomyDetailPage.defaultProps = { + taxonomyId: undefined, +}; + +TaxonomyDetailContent.propTypes = TaxonomyDetailPage.propTypes; +TaxonomyDetailContent.defaultProps = TaxonomyDetailPage.defaultProps; + +export default TaxonomyDetailPage; diff --git a/src/taxonomy/taxonomy-detail/TaxonomyDetailSideCard.jsx b/src/taxonomy/taxonomy-detail/TaxonomyDetailSideCard.jsx new file mode 100644 index 0000000000..3a27180ff5 --- /dev/null +++ b/src/taxonomy/taxonomy-detail/TaxonomyDetailSideCard.jsx @@ -0,0 +1,27 @@ +import { + Card, +} from '@edx/paragon'; +import Proptypes from 'prop-types'; + +const TaxonomyDetailSideCard = ({ taxonomy }) => ( + + + + {taxonomy.name} + + + + {taxonomy.description} + + + + No copyright added + + +); + +TaxonomyDetailSideCard.propTypes = { + taxonomy: Proptypes.object.isRequired, +}; + +export default TaxonomyDetailSideCard; diff --git a/src/taxonomy/taxonomy-detail/index.js b/src/taxonomy/taxonomy-detail/index.js new file mode 100644 index 0000000000..452695f08f --- /dev/null +++ b/src/taxonomy/taxonomy-detail/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export { default as TaxonomyDetailPage } from './TaxonomyDetailPage';