From d5612a21b59ba8c2dd0a8e9aca5d04e825aef1d4 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Wed, 4 Sep 2024 11:46:18 -0700 Subject: [PATCH] feat: developer info panel --- .../component-info/ComponentDeveloperInfo.tsx | 33 ++++++++++++++++ .../component-info/ComponentInfo.tsx | 5 +++ .../component-info/ComponentInfoHeader.tsx | 4 +- src/library-authoring/data/api.ts | 14 ++++++- src/library-authoring/data/apiHooks.ts | 38 ++++++++++++++----- 5 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 src/library-authoring/component-info/ComponentDeveloperInfo.tsx diff --git a/src/library-authoring/component-info/ComponentDeveloperInfo.tsx b/src/library-authoring/component-info/ComponentDeveloperInfo.tsx new file mode 100644 index 0000000000..983b2034ea --- /dev/null +++ b/src/library-authoring/component-info/ComponentDeveloperInfo.tsx @@ -0,0 +1,33 @@ +/* istanbul ignore file */ +/* eslint-disable import/prefer-default-export */ +// This file doesn't need test coverage nor i18n because it's only seen by devs +import React from 'react'; +import { LoadingSpinner } from '../../generic/Loading'; +import { useXBlockOLX } from '../data/apiHooks'; + +interface Props { + usageKey: string; +} + +export const ComponentDeveloperInfo: React.FC = ({ usageKey }) => { + const { data: olx, isLoading: isOLXLoading } = useXBlockOLX(usageKey); + return ( + <> +
+

Developer Component Details

+

(This panel is only visible in development builds.)

+
+
Usage key
+
{usageKey}
+
OLX
+
+ { + olx ? {olx} : // eslint-disable-line + isOLXLoading ? : // eslint-disable-line + Error + } +
+
+ + ); +}; diff --git a/src/library-authoring/component-info/ComponentInfo.tsx b/src/library-authoring/component-info/ComponentInfo.tsx index 56f1835c13..fa6db58296 100644 --- a/src/library-authoring/component-info/ComponentInfo.tsx +++ b/src/library-authoring/component-info/ComponentInfo.tsx @@ -10,6 +10,7 @@ import { Link } from 'react-router-dom'; import { getEditUrl } from '../components/utils'; import { ComponentMenu } from '../components'; +import { ComponentDeveloperInfo } from './ComponentDeveloperInfo'; import messages from './messages'; interface ComponentInfoProps { @@ -52,6 +53,10 @@ const ComponentInfo = ({ usageKey } : ComponentInfoProps) => { Details tab placeholder + + { + (process.env.NODE_ENV === 'development' ? : null) + } diff --git a/src/library-authoring/component-info/ComponentInfoHeader.tsx b/src/library-authoring/component-info/ComponentInfoHeader.tsx index 8f576fe1be..205acecfd4 100644 --- a/src/library-authoring/component-info/ComponentInfoHeader.tsx +++ b/src/library-authoring/component-info/ComponentInfoHeader.tsx @@ -24,9 +24,9 @@ const ComponentInfoHeader = ({ library, usageKey }: ComponentInfoHeaderProps) => const { data: xblockFields, - } = useXBlockFields(library.id, usageKey); + } = useXBlockFields(usageKey); - const updateMutation = useUpdateXBlockFields(library.id, usageKey); + const updateMutation = useUpdateXBlockFields(usageKey); const { showToast } = useContext(ToastContext); const handleSaveDisplayName = useCallback( diff --git a/src/library-authoring/data/api.ts b/src/library-authoring/data/api.ts index 4c5a0b04c4..78ed9f134b 100644 --- a/src/library-authoring/data/api.ts +++ b/src/library-authoring/data/api.ts @@ -25,9 +25,13 @@ export const getCommitLibraryChangesUrl = (libraryId: string) => `${getApiBaseUr */ export const getLibraryPasteClipboardUrl = (libraryId: string) => `${getApiBaseUrl()}/api/libraries/v2/${libraryId}/paste_clipboard/`; /** - * Get the URL for the xblock metadata API. + * Get the URL for the xblock fields/metadata API. */ export const getXBlockFieldsApiUrl = (usageKey: string) => `${getApiBaseUrl()}/api/xblock/v2/xblocks/${usageKey}/fields/`; +/** + * Get the URL for the xblock OLX API + */ +export const getXBlockOLXApiUrl = (usageKey: string) => `${getApiBaseUrl()}/api/libraries/v2/blocks/${usageKey}/olx/`; export interface ContentLibrary { id: string; @@ -242,3 +246,11 @@ export async function updateXBlockFields(usageKey:string, xblockData: UpdateXBlo const client = getAuthenticatedHttpClient(); await client.post(getXBlockFieldsApiUrl(usageKey), xblockData); } + +/** + * Fetch the OLX for the given XBlock. + */ +export async function getXBlockOLX(usageKey: string): Promise { + const { data } = await getAuthenticatedHttpClient().get(getXBlockOLXApiUrl(usageKey)); + return data.olx; +} diff --git a/src/library-authoring/data/apiHooks.ts b/src/library-authoring/data/apiHooks.ts index f101bdd59c..1ffe488774 100644 --- a/src/library-authoring/data/apiHooks.ts +++ b/src/library-authoring/data/apiHooks.ts @@ -7,6 +7,7 @@ import { type QueryClient, } from '@tanstack/react-query'; +import { getLibraryId } from '../components/utils'; import { type GetLibrariesV2CustomParams, type ContentLibrary, @@ -22,6 +23,7 @@ import { libraryPasteClipboard, getXBlockFields, updateXBlockFields, + getXBlockOLX, } from './api'; const libraryQueryPredicate = (query: Query, libraryId: string): boolean => { @@ -56,12 +58,21 @@ export const libraryAuthoringQueryKeys = { 'content', 'libraryBlockTypes', ], - xblockFields: (contentLibraryId: string, usageKey: string) => [ + xblockFields: (usageKey: string) => [ ...libraryAuthoringQueryKeys.all, - ...libraryAuthoringQueryKeys.contentLibrary(contentLibraryId), + ...libraryAuthoringQueryKeys.contentLibrary(getLibraryId(usageKey)), + 'content', + 'xblock', + usageKey, + 'fields', + ], + xblockOLX: (usageKey: string) => [ + ...libraryAuthoringQueryKeys.all, + ...libraryAuthoringQueryKeys.contentLibrary(getLibraryId(usageKey)), 'content', - 'xblockFields', + 'xblock', usageKey, + 'OLX', ], }; @@ -77,7 +88,7 @@ export const libraryAuthoringQueryKeys = { * @param usageKey The usage ID of the XBlock ("lb:...") */ export function invalidateComponentData(queryClient: QueryClient, contentLibraryId: string, usageKey: string) { - queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.xblockFields(contentLibraryId, usageKey) }); + queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.xblockFields(usageKey) }); queryClient.invalidateQueries({ predicate: (query) => libraryQueryPredicate(query, contentLibraryId) }); } @@ -188,20 +199,21 @@ export const useLibraryPasteClipboard = () => { }); }; -export const useXBlockFields = (contentLibrayId: string, usageKey: string) => ( +export const useXBlockFields = (usageKey: string) => ( useQuery({ - queryKey: libraryAuthoringQueryKeys.xblockFields(contentLibrayId, usageKey), + queryKey: libraryAuthoringQueryKeys.xblockFields(usageKey), queryFn: () => getXBlockFields(usageKey), enabled: !!usageKey, }) ); -export const useUpdateXBlockFields = (contentLibraryId: string, usageKey: string) => { +export const useUpdateXBlockFields = (usageKey: string) => { + const contentLibraryId = getLibraryId(usageKey); const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: UpdateXBlockFieldsRequest) => updateXBlockFields(usageKey, data), onMutate: async (data) => { - const queryKey = libraryAuthoringQueryKeys.xblockFields(contentLibraryId, usageKey); + const queryKey = libraryAuthoringQueryKeys.xblockFields(usageKey); const previousBlockData = queryClient.getQueriesData(queryKey)[0][1] as XBlockFields; const formatedData = camelCaseObject(data); @@ -220,7 +232,7 @@ export const useUpdateXBlockFields = (contentLibraryId: string, usageKey: string }, onError: (_err, _data, context) => { queryClient.setQueryData( - libraryAuthoringQueryKeys.xblockFields(contentLibraryId, usageKey), + libraryAuthoringQueryKeys.xblockFields(usageKey), context?.previousBlockData, ); }, @@ -229,3 +241,11 @@ export const useUpdateXBlockFields = (contentLibraryId: string, usageKey: string }, }); }; + +export const useXBlockOLX = (usageKey: string) => ( + useQuery({ + queryKey: libraryAuthoringQueryKeys.xblockOLX(usageKey), + queryFn: () => getXBlockOLX(usageKey), + enabled: !!usageKey, + }) +);