+
+ {{
+ channelVersion$({
+ name: channel ? channel.name : '',
+ version: displayedVersion,
+ })
+ }}
+
{{ detectedLanguages }}
@@ -130,8 +130,8 @@
class="metadata-line"
>
{{ detectedCategories }}
@@ -244,14 +244,14 @@
import Box from './Box';
import LoadingText from './LoadingText';
- import StatusChip from './StatusChip';
- import { useLatestCommunityLibrarySubmission } from './composables/useLatestCommunityLibrarySubmission';
import { useLicenseAudit } from './composables/useLicenseAudit';
- import { usePublishedData } from './composables/usePublishedData';
+ import { useVersionDetail } from './composables/useVersionDetail';
import InvalidLicensesNotice from './licenseCheck/InvalidLicensesNotice.vue';
import CompatibleLicensesNotice from './licenseCheck/CompatibleLicensesNotice.vue';
- import SpecialPermissionsList from './licenseCheck/SpecialPermissionsList.vue';
+ import SpecialPermissionsList from 'shared/views/communityLibrary/SpecialPermissionsList.vue';
+ import CommunityLibraryStatusChip from 'shared/views/communityLibrary/CommunityLibraryStatusChip';
+ import { useLatestCommunityLibrarySubmission } from 'shared/composables/useLatestCommunityLibrarySubmission';
import { translateMetadataString } from 'shared/utils/metadataStringsTranslation';
import countriesUtil from 'shared/utils/countries';
import { communityChannelsStrings } from 'shared/strings/communityChannelsStrings';
@@ -261,6 +261,7 @@
import CountryField from 'shared/views/form/CountryField';
import LanguagesMap from 'shared/leUtils/Languages';
import { CategoriesLookup, CommunityLibraryStatus } from 'shared/constants';
+ import { getUiSubmissionStatus } from 'shared/utils/communityLibrary';
export default {
name: 'SubmitToCommunityLibrarySidePanel',
@@ -268,7 +269,7 @@
SidePanelModal,
Box,
LoadingText,
- StatusChip,
+ CommunityLibraryStatusChip,
CountryField,
InvalidLicensesNotice,
CompatibleLicensesNotice,
@@ -329,7 +330,7 @@
isFinished: latestSubmissionIsFinished,
data: latestSubmissionData,
fetchData: fetchLatestSubmission,
- } = useLatestCommunityLibrarySubmission(props.channel.id);
+ } = useLatestCommunityLibrarySubmission({ channelId: props.channel.id });
function countryCodeToName(code) {
return countriesUtil.getName(code, 'en');
@@ -352,13 +353,7 @@
if (!latestSubmissionIsFinished.value) return undefined;
if (!latestSubmissionData.value) return null;
- // We do not need to distinguish LIVE from APPROVED in the UI
- const uiSubmissionStatus =
- latestSubmissionData.value.status == CommunityLibraryStatus.LIVE
- ? CommunityLibraryStatus.APPROVED
- : latestSubmissionData.value.status;
-
- return uiSubmissionStatus;
+ return getUiSubmissionStatus(latestSubmissionData.value.status);
});
const infoConfig = computed(() => {
@@ -415,11 +410,11 @@
});
const {
- isLoading: publishedDataIsLoading,
- isFinished: publishedDataIsFinished,
+ isLoading: versionDetailIsLoading,
+ isFinished: versionDetailIsFinished,
data: versionDetail,
- fetchData: fetchPublishedData,
- } = usePublishedData(props.channel.id);
+ fetchData: fetchVersionDetail,
+ } = useVersionDetail(props.channel.id);
// Use the latest version available from either channel or versionDetail
const displayedVersion = computed(() => {
@@ -442,7 +437,7 @@
checkAndTriggerAudit: checkAndTriggerLicenseAudit,
} = useLicenseAudit(props.channel, currentChannelVersion);
- const allSpecialPermissionsChecked = ref(true);
+ const allSpecialPermissionsChecked = ref(false);
const hasInvalidLicenses = computed(() => {
return invalidLicenses.value && invalidLicenses.value.length > 0;
@@ -455,7 +450,7 @@
!hasInvalidLicenses.value,
licenseAuditIsFinished.value,
canBeEdited.value,
- publishedDataIsFinished.value,
+ versionDetailIsFinished.value,
description.value.length >= 1,
];
@@ -469,7 +464,7 @@
// Watch for when publishing completes - fetch publishedData to get the new version's data
watch(isPublishing, async (newIsPublishing, oldIsPublishing) => {
if (oldIsPublishing === true && newIsPublishing === false) {
- await fetchPublishedData();
+ await fetchVersionDetail();
await checkAndTriggerLicenseAudit();
}
});
@@ -478,7 +473,7 @@
await fetchLatestSubmission();
if (!isPublishing.value) {
- await fetchPublishedData();
+ await fetchVersionDetail();
await checkAndTriggerLicenseAudit();
}
});
@@ -532,11 +527,15 @@
const submitDelayMs = 5000;
const timer = setTimeout(() => {
+ const categories = {};
+ for (const categoryId of versionDetail.value.included_categories || []) {
+ categories[categoryId] = true;
+ }
CommunityLibrarySubmission.create({
description: description.value,
channel: props.channel.id,
countries: countries.value.map(country => countriesUtil.getAlpha2Code(country, 'en')),
- categories: versionDetail.value.included_categories,
+ categories,
})
.then(() => {
showSnackbar({ text: submittedSnackbar$() });
@@ -578,8 +577,8 @@
displayedVersion,
channelVersionId,
canBeSubmitted,
- publishedDataIsLoading,
- publishedDataIsFinished,
+ versionDetailIsLoading,
+ versionDetailIsFinished,
detectedLanguages,
detectedCategories,
licenseAuditIsLoading,
@@ -641,7 +640,7 @@
font-weight: 600;
}
- .metadata-section {
+ .channel-metadata {
display: flex;
flex-direction: column;
gap: 4px;
diff --git a/contentcuration/contentcuration/frontend/shared/composables/__tests__/useLatestCommunityLibrarySubmission.spec.js b/contentcuration/contentcuration/frontend/shared/composables/__tests__/useLatestCommunityLibrarySubmission.spec.js
new file mode 100644
index 0000000000..a7633f0288
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/shared/composables/__tests__/useLatestCommunityLibrarySubmission.spec.js
@@ -0,0 +1,42 @@
+import { useLatestCommunityLibrarySubmission } from '../useLatestCommunityLibrarySubmission';
+import { AdminCommunityLibrarySubmission, CommunityLibrarySubmission } from 'shared/data/resources';
+
+const mockResponse = {
+ results: [],
+};
+
+jest.mock('shared/data/resources', () => {
+ return {
+ CommunityLibrarySubmission: {
+ fetchCollection: jest.fn(() => Promise.resolve(mockResponse)),
+ },
+ AdminCommunityLibrarySubmission: {
+ fetchCollection: jest.fn(() => Promise.resolve(mockResponse)),
+ },
+ };
+});
+
+describe('useLatestCommunityLibrarySubmission', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('by default uses non-admin endpoint', async () => {
+ const { fetchData } = useLatestCommunityLibrarySubmission({ channelId: 'channel-id' });
+ await fetchData();
+
+ expect(CommunityLibrarySubmission.fetchCollection).toHaveBeenCalled();
+ expect(AdminCommunityLibrarySubmission.fetchCollection).not.toHaveBeenCalled();
+ });
+
+ it('uses admin endpoint when initialized with admin=true', async () => {
+ const { fetchData } = useLatestCommunityLibrarySubmission({
+ channelId: 'channel-id',
+ admin: true,
+ });
+ await fetchData();
+
+ expect(CommunityLibrarySubmission.fetchCollection).not.toHaveBeenCalled();
+ expect(AdminCommunityLibrarySubmission.fetchCollection).toHaveBeenCalled();
+ });
+});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/composables/useFetch.js b/contentcuration/contentcuration/frontend/shared/composables/useFetch.js
similarity index 86%
rename from contentcuration/contentcuration/frontend/channelEdit/composables/useFetch.js
rename to contentcuration/contentcuration/frontend/shared/composables/useFetch.js
index 7fef1922c8..2e3ccfa975 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/composables/useFetch.js
+++ b/contentcuration/contentcuration/frontend/shared/composables/useFetch.js
@@ -15,9 +15,9 @@ export function useFetch({ asyncFetchFunc }) {
data.value = await asyncFetchFunc();
isLoading.value = false;
isFinished.value = true;
- } catch (error) {
- error.value = error;
- throw error;
+ } catch (caughtError) {
+ error.value = caughtError;
+ throw caughtError;
} finally {
isLoading.value = false;
}
diff --git a/contentcuration/contentcuration/frontend/shared/composables/useLatestCommunityLibrarySubmission.js b/contentcuration/contentcuration/frontend/shared/composables/useLatestCommunityLibrarySubmission.js
new file mode 100644
index 0000000000..2b4afe9e30
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/shared/composables/useLatestCommunityLibrarySubmission.js
@@ -0,0 +1,19 @@
+import { useFetch } from './useFetch';
+import { AdminCommunityLibrarySubmission, CommunityLibrarySubmission } from 'shared/data/resources';
+
+export function useLatestCommunityLibrarySubmission({ channelId, admin = false }) {
+ const Resource = admin ? AdminCommunityLibrarySubmission : CommunityLibrarySubmission;
+
+ function fetchLatestSubmission() {
+ // Submissions are ordered by most recent first in the backend
+ return Resource.fetchCollection({ channel: channelId, max_results: 1 }).then(response => {
+ if (response.results.length > 0) {
+ return response.results[0];
+ }
+ return null;
+ });
+ }
+ return useFetch({
+ asyncFetchFunc: fetchLatestSubmission,
+ });
+}
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/SubmitToCommunityLibrarySidePanel/composables/useSpecialPermissions.js b/contentcuration/contentcuration/frontend/shared/composables/useSpecialPermissions.js
similarity index 100%
rename from contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/SubmitToCommunityLibrarySidePanel/composables/useSpecialPermissions.js
rename to contentcuration/contentcuration/frontend/shared/composables/useSpecialPermissions.js
diff --git a/contentcuration/contentcuration/frontend/shared/constants.js b/contentcuration/contentcuration/frontend/shared/constants.js
index 431c12838a..0270613b60 100644
--- a/contentcuration/contentcuration/frontend/shared/constants.js
+++ b/contentcuration/contentcuration/frontend/shared/constants.js
@@ -336,3 +336,11 @@ export const CommunityLibraryStatus = {
SUPERSEDED: 'SUPERSEDED',
LIVE: 'LIVE',
};
+
+export const CommunityLibraryResolutionReason = {
+ INVALID_LICENSING: 'INVALID_LICENSING',
+ TECHNICAL_QUALITY_ASSURANCE: 'TECHNICAL_QUALITY_ASSURANCE',
+ INVALID_METADATA: 'INVALID_METADATA',
+ PORTABILITY_ISSUES: 'PORTABILITY_ISSUES',
+ OTHER: 'OTHER',
+};
diff --git a/contentcuration/contentcuration/frontend/shared/data/resources.js b/contentcuration/contentcuration/frontend/shared/data/resources.js
index e7a941fbda..657cb33a89 100644
--- a/contentcuration/contentcuration/frontend/shared/data/resources.js
+++ b/contentcuration/contentcuration/frontend/shared/data/resources.js
@@ -2420,6 +2420,23 @@ export const CommunityLibrarySubmission = new APIResource({
},
});
+export const AdminCommunityLibrarySubmission = new APIResource({
+ urlName: 'admin_community_library_submission',
+ fetchCollection(params) {
+ return client.get(this.collectionUrl(), { params }).then(response => {
+ return response.data || [];
+ });
+ },
+ async fetchModel(id) {
+ const response = await client.get(this.modelUrl(id));
+ return response.data;
+ },
+ async resolve(id, params) {
+ const response = await client.post(this.getUrlFunction('resolve')(id), params);
+ return response.data;
+ },
+});
+
export const AuditedSpecialPermissionsLicense = new APIResource({
urlName: 'audited_special_permissions_license',
async fetchCollection(params) {
@@ -2427,3 +2444,11 @@ export const AuditedSpecialPermissionsLicense = new APIResource({
return response.data || [];
},
});
+
+export const ChannelVersion = new APIResource({
+ urlName: 'channelversion',
+ async fetchCollection(params) {
+ const response = await client.get(this.collectionUrl(), { params });
+ return response.data;
+ },
+});
diff --git a/contentcuration/contentcuration/frontend/shared/utils/communityLibrary.js b/contentcuration/contentcuration/frontend/shared/utils/communityLibrary.js
new file mode 100644
index 0000000000..4449c3111b
--- /dev/null
+++ b/contentcuration/contentcuration/frontend/shared/utils/communityLibrary.js
@@ -0,0 +1,11 @@
+import { CommunityLibraryStatus } from 'shared/constants';
+
+export const getUiSubmissionStatus = status => {
+ // We do not need to distinguish LIVE from APPROVED in many parts of the UI
+ // nor SUPERSEDED from PENDING
+ const uiStatusMap = {
+ [CommunityLibraryStatus.LIVE]: CommunityLibraryStatus.APPROVED,
+ [CommunityLibraryStatus.SUPERSEDED]: CommunityLibraryStatus.PENDING,
+ };
+ return uiStatusMap[status] || status;
+};
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/SubmitToCommunityLibrarySidePanel/StatusChip.vue b/contentcuration/contentcuration/frontend/shared/views/communityLibrary/CommunityLibraryStatusChip.vue
similarity index 96%
rename from contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/SubmitToCommunityLibrarySidePanel/StatusChip.vue
rename to contentcuration/contentcuration/frontend/shared/views/communityLibrary/CommunityLibraryStatusChip.vue
index 8e2b8c4d37..2102f07ab7 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/sidePanels/SubmitToCommunityLibrarySidePanel/StatusChip.vue
+++ b/contentcuration/contentcuration/frontend/shared/views/communityLibrary/CommunityLibraryStatusChip.vue
@@ -20,7 +20,7 @@
import { CommunityLibraryStatus } from 'shared/constants';
export default {
- name: 'StatusChip',
+ name: 'CommunityLibraryStatusChip',
setup(props) {
const theme = themePalette();
@@ -79,9 +79,10 @@