Skip to content

Commit

Permalink
feat: LibraryPublishStatus component added
Browse files Browse the repository at this point in the history
- Connection with API to publish an revert changes
- LibraryPublishStatus component created with draft and publish status
- Component create with the feature to publish and revert changes
  • Loading branch information
ChrisChV committed Jul 9, 2024
1 parent 473fc27 commit cacda87
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 34 deletions.
1 change: 1 addition & 0 deletions src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
@import "export-page/CourseExportPage";
@import "import-page/CourseImportPage";
@import "taxonomy";
@import "library-authoring";
@import "files-and-videos";
@import "content-tags-drawer";
@import "course-outline/CourseOutline";
Expand Down
7 changes: 2 additions & 5 deletions src/library-authoring/LibraryAuthoringPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,8 @@ const LibraryAuthoringPage = () => {
}, [location]);

useEffect(() => {
// Open Library Info sidebar by default
if (!isLoading && libraryData) {
openInfoSidebar();
};
}, [isLoading, libraryData]);
openInfoSidebar();
}, []);

if (isLoading) {
return <Loading />;
Expand Down
30 changes: 30 additions & 0 deletions src/library-authoring/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export const getContentLibraryApiUrl = (libraryId: string) => `${getApiBaseUrl()
* Get the URL for create content in library.
*/
export const getCreateLibraryBlockUrl = (libraryId: string) => `${getApiBaseUrl()}/api/libraries/v2/${libraryId}/blocks/`;
/**
* Get the URL for commit/revert changes in library.
*/
export const getCommitLibraryChangesUrl = (libraryId: string) => `${getApiBaseUrl()}/api/libraries/v2/${libraryId}/commit/`


export interface ContentLibrary {
id: string;
Expand All @@ -21,6 +26,9 @@ export interface ContentLibrary {
numBlocks: number;
version: number;
lastPublished: Date | null;
lastDraftCreated: Date | null;
publishedBy: string | null;
lastDraftCreatedBy: string | null;
allowLti: boolean;
allowPublicLearning: boolean;
allowPublicRead: boolean;
Expand Down Expand Up @@ -75,3 +83,25 @@ export async function createLibraryBlock({

return camelCaseObject(data);
}

/**
* Commit library changes.
*/
export async function commitLibraryChanges(libraryId: string): Promise<any> {
const client = getAuthenticatedHttpClient();

const { data } = await client.post(getCommitLibraryChangesUrl(libraryId));

return camelCaseObject(data);
}

/**
* Revert library changes.
*/
export async function revertLibraryChanges(libraryId: string): Promise<any> {
const client = getAuthenticatedHttpClient();

const { data } = await client.delete(getCommitLibraryChangesUrl(libraryId));

return camelCaseObject(data);
}
29 changes: 27 additions & 2 deletions src/library-authoring/data/apiHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { MeiliSearch } from 'meilisearch';

import { useContentSearchConnection, useContentSearchResults } from '../../search-modal';
import { createLibraryBlock, getContentLibrary } from './api';
import {
createLibraryBlock,
getContentLibrary,
commitLibraryChanges,
revertLibraryChanges
} from './api';

export const libraryQueryKeys = {
/**
Expand All @@ -21,7 +26,7 @@ export const libraryQueryKeys = {
*/
export const useContentLibrary = (libraryId?: string) => (
useQuery({
queryKey: ['contentLibrary', libraryId],
queryKey: libraryQueryKeys.contentLibrary(libraryId),
queryFn: () => getContentLibrary(libraryId),
})
);
Expand Down Expand Up @@ -71,3 +76,23 @@ export const useLibraryComponentCount = (libraryId: string, searchKeywords: stri
collectionCount,
};
};

export const useCommitLibraryChanges = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: commitLibraryChanges,
onSettled: (_data, _error, libraryId) => {
queryClient.invalidateQueries({ queryKey: libraryQueryKeys.contentLibrary(libraryId) });
},
});
};

export const useRevertLibraryChanges = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: revertLibraryChanges,
onSettled: (_data, _error, libraryId) => {
queryClient.invalidateQueries({ queryKey: libraryQueryKeys.contentLibrary(libraryId) });
},
});
};
1 change: 1 addition & 0 deletions src/library-authoring/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "library-authoring/library-info/LibraryPublishStatus"
20 changes: 9 additions & 11 deletions src/library-authoring/library-info/LibraryInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
import React from "react";
import { Stack } from "@openedx/paragon";
import { useIntl } from '@edx/frontend-platform/i18n';
import React from "react";
import messages from "./messages";
import { convertToStringFromDateAndFormat } from "../../utils";
import { COMMA_SEPARATED_DATE_FORMAT } from "../../constants";
import LibraryPublishStatus from "./LibraryPublishStatus";
import { ContentLibrary } from "../data/api";

type LibraryInfoProps = {
orgName: string,
createdAt: Date,
updatedAt: Date,
library: ContentLibrary,
};

const LibraryInfo = ({ orgName, createdAt, updatedAt } : LibraryInfoProps) => {
const LibraryInfo = ({ library } : LibraryInfoProps) => {
const intl = useIntl();

return (
<Stack direction='vertical' gap={2.5}>
<div>
Published section
</div>
<LibraryPublishStatus library={library}/>
<Stack direction='vertical'>
<span className="font-weight-bold">
{intl.formatMessage(messages.organizationSectionTitle)}
</span>
<span>
{orgName}
{library.org}
</span>
</Stack>
<Stack>
Expand All @@ -36,15 +34,15 @@ const LibraryInfo = ({ orgName, createdAt, updatedAt } : LibraryInfoProps) => {
{intl.formatMessage(messages.lastModifiedLabel)}
</span>
<span className="small">
{convertToStringFromDateAndFormat(updatedAt, COMMA_SEPARATED_DATE_FORMAT)}
{convertToStringFromDateAndFormat(library.updated, COMMA_SEPARATED_DATE_FORMAT)}
</span>
</Stack>
<Stack>
<span className="small text-gray-500">
{intl.formatMessage(messages.createdLabel)}
</span>
<span className="small">
{convertToStringFromDateAndFormat(createdAt, COMMA_SEPARATED_DATE_FORMAT)}
{convertToStringFromDateAndFormat(library.created, COMMA_SEPARATED_DATE_FORMAT)}
</span>
</Stack>
</Stack>
Expand Down
10 changes: 5 additions & 5 deletions src/library-authoring/library-info/LibraryInfoHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ import { Icon, IconButton, Stack } from "@openedx/paragon";
import { Edit } from '@openedx/paragon/icons';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from "./messages";
import { ContentLibrary } from "../data/api";

type LibraryInfoHeaderProps = {
displayName: string,
canEditLibrary: boolean,
library: ContentLibrary,
};

const LibraryInfoHeader = ({ displayName, canEditLibrary} : LibraryInfoHeaderProps) => {
const LibraryInfoHeader = ({ library } : LibraryInfoHeaderProps) => {
const intl = useIntl();

return (
<Stack direction='horizontal'>
<span className="font-weight-bold m-1.5">
{displayName}
{library.title}
</span>
{canEditLibrary && (
{library.canEditLibrary && (
<IconButton
src={Edit}
iconAs={Icon}
Expand Down
13 changes: 13 additions & 0 deletions src/library-authoring/library-info/LibraryPublishStatus.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.library-publish-status {

&.draft-status {
background-color: #FDF3E9;
border-top: 4px solid #F4B57B;
}

&.published-status {
background-color: $info-100;
border-top: 4px solid $info-400;
}
}

136 changes: 136 additions & 0 deletions src/library-authoring/library-info/LibraryPublishStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import React, { useCallback, useContext, useMemo } from "react";
import { Button, Container, Stack } from "@openedx/paragon";
import { ContentLibrary } from "../data/api";
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from "./messages";
import classNames from 'classnames';
import { useCommitLibraryChanges, useRevertLibraryChanges } from "../data/apiHook";
import { ToastContext } from "../../generic/toast-context";
import { convertToStringFromDateAndFormat } from "../../utils";
import { COMMA_SEPARATED_DATE_FORMAT, TIME_FORMAT } from "../../constants";

type LibraryPublishStatusProps = {
library: ContentLibrary,
}

const LibraryPublishStatus = ({ library } : LibraryPublishStatusProps) => {
const intl = useIntl();
const commitLibraryChanges = useCommitLibraryChanges();
const revertLibraryChanges = useRevertLibraryChanges();
const { showToast } = useContext(ToastContext);

const commit = useCallback(() => {
commitLibraryChanges.mutateAsync(library.id)
.then(() => {
showToast(intl.formatMessage(messages.publishSuccessMsg));
}).catch(() => {
showToast(intl.formatMessage(messages.publishErrorMsg));
});
}, []);

const revert = useCallback(() => {
revertLibraryChanges.mutateAsync(library.id)
.then(() => {
showToast(intl.formatMessage(messages.revertSuccessMsg));
}).catch(() => {
showToast(intl.formatMessage(messages.revertErrorMsg));
});
}, []);

const {
isPublished,
statusMessage,
extraStatusMessage,
bodyMessage,
} = useMemo(() => {
let isPublished : boolean;
let statusMessage : string;
let extraStatusMessage : string | undefined = undefined;
let bodyMessage : string | undefined = undefined;
const buildDraftBodyMessage = (() => {
if (library.lastDraftCreatedBy) {
return intl.formatMessage(messages.lastDraftMsg, {
date: <b>{convertToStringFromDateAndFormat(library.lastDraftCreated, COMMA_SEPARATED_DATE_FORMAT)}</b>,
time: <b>{convertToStringFromDateAndFormat(library.lastDraftCreated, TIME_FORMAT)}</b>,
user: <b>{library.lastDraftCreatedBy}</b>,
});
} else {
return intl.formatMessage(messages.lastDraftMsgWithoutUser, {
date: <b>{convertToStringFromDateAndFormat(library.lastDraftCreated, COMMA_SEPARATED_DATE_FORMAT)}</b>,
time: <b>{convertToStringFromDateAndFormat(library.lastDraftCreated, TIME_FORMAT)}</b>,
});
}
});

if (!library.lastPublished) {
// Library is never published (new)
isPublished = false;
statusMessage = intl.formatMessage(messages.draftStatusLabel);
extraStatusMessage = intl.formatMessage(messages.neverPublishedLabel);
bodyMessage = buildDraftBodyMessage();
} else if (library.hasUnpublishedChanges || library.hasUnpublishedDeletes) {
// Library is on Draft state
isPublished = false;
statusMessage = intl.formatMessage(messages.draftStatusLabel);
extraStatusMessage = intl.formatMessage(messages.unpublishedStatusLabel);
bodyMessage = buildDraftBodyMessage();
} else {
// Library is published
isPublished = true;
statusMessage = intl.formatMessage(messages.publishedStatusLabel);
if (library.publishedBy) {
bodyMessage = intl.formatMessage(messages.lastPublishedMsg, {
date: <b>{convertToStringFromDateAndFormat(library.lastPublished, COMMA_SEPARATED_DATE_FORMAT)}</b>,
time: <b>{convertToStringFromDateAndFormat(library.lastPublished, TIME_FORMAT)}</b>,
user: <b>{library.publishedBy}</b>,
})
} else {
bodyMessage = intl.formatMessage(messages.lastPublishedMsgWithoutUser, {
date: <b>{convertToStringFromDateAndFormat(library.lastPublished, COMMA_SEPARATED_DATE_FORMAT)}</b>,
time: <b>{convertToStringFromDateAndFormat(library.lastPublished, TIME_FORMAT)}</b>,
})
}
}
return {
isPublished,
statusMessage,
extraStatusMessage,
bodyMessage,
}
}, [library])

return (
<Stack>
<Container className={classNames("library-publish-status", {
"draft-status": !isPublished,
"published-status": isPublished,
})}>
<span className="font-weight-bold">
{statusMessage}
</span>
{ extraStatusMessage && (
<span className="ml-1">
{extraStatusMessage}
</span>
)}
</Container>
<Container>
<Stack>
<span>
{bodyMessage}
</span>
<Button disabled={isPublished} onClick={commit}>
{intl.formatMessage(messages.publishButtonLabel)}
</Button>
<div className='d-flex justify-content-end'>
<Button disabled={isPublished} variant='link' onClick={revert}>
{intl.formatMessage(messages.discardChangesButtonLabel)}
</Button>
</div>
</Stack>
</Container>
</Stack>
);
};

export default LibraryPublishStatus;
Loading

0 comments on commit cacda87

Please sign in to comment.