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