mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-04-25 00:34:14 -04:00
a
This commit is contained in:
@@ -6,12 +6,14 @@ import { inEmbeddedApp } from '../utils/helpers/embeddedApp';
|
||||
interface BulkActionsDropdownProps {
|
||||
selectedCount: number;
|
||||
onActionSelect: (action: string) => void;
|
||||
hasContributorCourses?: boolean;
|
||||
}
|
||||
|
||||
interface BulkAction {
|
||||
value: string;
|
||||
label: string;
|
||||
enabled: boolean;
|
||||
allowsNoSelection?: boolean;
|
||||
}
|
||||
|
||||
interface BulkActionGroup {
|
||||
@@ -19,7 +21,7 @@ interface BulkActionGroup {
|
||||
actions: BulkAction[];
|
||||
}
|
||||
|
||||
export const BulkActionsDropdown: React.FC<BulkActionsDropdownProps> = ({ selectedCount, onActionSelect }) => {
|
||||
export const BulkActionsDropdown: React.FC<BulkActionsDropdownProps> = ({ selectedCount, onActionSelect, hasContributorCourses = false }) => {
|
||||
const isLmsMode = inEmbeddedApp();
|
||||
|
||||
const BULK_ACTION_GROUPS: BulkActionGroup[] = [
|
||||
@@ -56,18 +58,24 @@ export const BulkActionsDropdown: React.FC<BulkActionsDropdownProps> = ({ select
|
||||
{ value: 'change-owner', label: translateString('Change Owner'), enabled: true },
|
||||
{ value: 'copy-media', label: translateString('Copy Media'), enabled: true },
|
||||
{ value: 'delete-media', label: translateString('Delete Media'), enabled: true },
|
||||
...(isLmsMode && hasContributorCourses
|
||||
? [{ value: 'course-cleanup', label: translateString('Course Cleanup'), enabled: true, allowsNoSelection: true }]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
];
|
||||
const noSelection = selectedCount === 0;
|
||||
|
||||
|
||||
const allActions = BULK_ACTION_GROUPS.flatMap((g) => g.actions);
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const value = event.target.value;
|
||||
|
||||
if (!value) return;
|
||||
|
||||
if (noSelection) {
|
||||
const actionDef = allActions.find((a) => a.value === value);
|
||||
if (noSelection && !actionDef?.allowsNoSelection) {
|
||||
event.target.value = '';
|
||||
return;
|
||||
}
|
||||
@@ -95,7 +103,7 @@ export const BulkActionsDropdown: React.FC<BulkActionsDropdownProps> = ({ select
|
||||
{BULK_ACTION_GROUPS.map((group) => (
|
||||
<optgroup key={group.label} label={group.label}>
|
||||
{group.actions.map((action) => (
|
||||
<option key={action.value} value={action.value} disabled={noSelection || !action.enabled}>
|
||||
<option key={action.value} value={action.value} disabled={(!action.allowsNoSelection && noSelection) || !action.enabled}>
|
||||
{action.label}
|
||||
</option>
|
||||
))}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { BulkActionChangeOwnerModal } from './BulkActionChangeOwnerModal';
|
||||
import { BulkActionPublishStateModal } from './BulkActionPublishStateModal';
|
||||
import { BulkActionCategoryModal } from './BulkActionCategoryModal';
|
||||
import { BulkActionTagModal } from './BulkActionTagModal';
|
||||
import { BulkActionCourseCleanupModal } from './BulkActionCourseCleanupModal';
|
||||
|
||||
/**
|
||||
* Renders all bulk action modals
|
||||
@@ -58,6 +59,12 @@ export function BulkActionsModals({
|
||||
onTagModalSuccess,
|
||||
onTagModalError,
|
||||
|
||||
// Course cleanup modal props
|
||||
showCourseCleanupModal,
|
||||
onCourseCleanupModalCancel,
|
||||
onCourseCleanupModalSuccess,
|
||||
onCourseCleanupModalError,
|
||||
|
||||
// Common props
|
||||
csrfToken,
|
||||
|
||||
@@ -131,6 +138,15 @@ export function BulkActionsModals({
|
||||
csrfToken={csrfToken}
|
||||
/>
|
||||
|
||||
<BulkActionCourseCleanupModal
|
||||
isOpen={showCourseCleanupModal}
|
||||
selectedMediaIds={selectedMediaIds}
|
||||
onCancel={onCourseCleanupModalCancel}
|
||||
onSuccess={onCourseCleanupModalSuccess}
|
||||
onError={onCourseCleanupModalError}
|
||||
csrfToken={csrfToken}
|
||||
/>
|
||||
|
||||
{showNotification && (
|
||||
<div
|
||||
style={{
|
||||
@@ -193,6 +209,11 @@ BulkActionsModals.propTypes = {
|
||||
onTagModalSuccess: PropTypes.func.isRequired,
|
||||
onTagModalError: PropTypes.func.isRequired,
|
||||
|
||||
showCourseCleanupModal: PropTypes.bool.isRequired,
|
||||
onCourseCleanupModalCancel: PropTypes.func.isRequired,
|
||||
onCourseCleanupModalSuccess: PropTypes.func.isRequired,
|
||||
onCourseCleanupModalError: PropTypes.func.isRequired,
|
||||
|
||||
csrfToken: PropTypes.string.isRequired,
|
||||
|
||||
showNotification: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -22,6 +22,7 @@ interface MediaListWrapperProps {
|
||||
onSelectAll?: () => void;
|
||||
onDeselectAll?: () => void;
|
||||
showAddMediaButton?: boolean;
|
||||
hasContributorCourses?: boolean;
|
||||
}
|
||||
|
||||
export const MediaListWrapper: React.FC<MediaListWrapperProps> = ({
|
||||
@@ -38,6 +39,7 @@ export const MediaListWrapper: React.FC<MediaListWrapperProps> = ({
|
||||
onSelectAll = () => {},
|
||||
onDeselectAll = () => {},
|
||||
showAddMediaButton = false,
|
||||
hasContributorCourses = false,
|
||||
}) => {
|
||||
const [popupContentRef, PopupContent, PopupTrigger] = usePopup() as [any, any, any];
|
||||
|
||||
@@ -63,7 +65,7 @@ export const MediaListWrapper: React.FC<MediaListWrapperProps> = ({
|
||||
return (
|
||||
<div className="bulk-actions-container">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
|
||||
<BulkActionsDropdown selectedCount={selectedCount} onActionSelect={onBulkAction} />
|
||||
<BulkActionsDropdown selectedCount={selectedCount} onActionSelect={onBulkAction} hasContributorCourses={hasContributorCourses} />
|
||||
<SelectAllCheckbox
|
||||
totalCount={totalCount}
|
||||
selectedCount={selectedCount}
|
||||
|
||||
@@ -463,6 +463,7 @@ class ProfileMediaPage extends Page {
|
||||
onSelectAll={this.props.bulkActions.handleSelectAll}
|
||||
onDeselectAll={this.props.bulkActions.handleDeselectAll}
|
||||
showAddMediaButton={!isSelectMediaMode && isMediaAuthor}
|
||||
hasContributorCourses={this.props.bulkActions.hasContributorCourses}
|
||||
>
|
||||
<ProfileMediaFilters
|
||||
hidden={this.state.hiddenFilters}
|
||||
@@ -530,6 +531,10 @@ class ProfileMediaPage extends Page {
|
||||
onTagModalCancel={this.props.bulkActions.handleTagModalCancel}
|
||||
onTagModalSuccess={this.props.bulkActions.handleTagModalSuccess}
|
||||
onTagModalError={this.props.bulkActions.handleTagModalError}
|
||||
showCourseCleanupModal={this.props.bulkActions.showCourseCleanupModal}
|
||||
onCourseCleanupModalCancel={this.props.bulkActions.handleCourseCleanupModalCancel}
|
||||
onCourseCleanupModalSuccess={this.props.bulkActions.handleCourseCleanupModalSuccess}
|
||||
onCourseCleanupModalError={this.props.bulkActions.handleCourseCleanupModalError}
|
||||
/>
|
||||
) : null,
|
||||
];
|
||||
|
||||
@@ -486,6 +486,7 @@ class ProfileSharedByMePage extends Page {
|
||||
onBulkAction={this.props.bulkActions.handleBulkAction}
|
||||
onSelectAll={this.props.bulkActions.handleSelectAll}
|
||||
onDeselectAll={this.props.bulkActions.handleDeselectAll}
|
||||
hasContributorCourses={this.props.bulkActions.hasContributorCourses}
|
||||
>
|
||||
<ProfileMediaFilters
|
||||
hidden={this.state.hiddenFilters}
|
||||
@@ -554,6 +555,10 @@ class ProfileSharedByMePage extends Page {
|
||||
onTagModalCancel={this.props.bulkActions.handleTagModalCancel}
|
||||
onTagModalSuccess={this.props.bulkActions.handleTagModalSuccess}
|
||||
onTagModalError={this.props.bulkActions.handleTagModalError}
|
||||
showCourseCleanupModal={this.props.bulkActions.showCourseCleanupModal}
|
||||
onCourseCleanupModalCancel={this.props.bulkActions.handleCourseCleanupModalCancel}
|
||||
onCourseCleanupModalSuccess={this.props.bulkActions.handleCourseCleanupModalSuccess}
|
||||
onCourseCleanupModalError={this.props.bulkActions.handleCourseCleanupModalError}
|
||||
/>
|
||||
) : null,
|
||||
];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { translateString } from '../helpers';
|
||||
import { inEmbeddedApp } from '../helpers/embeddedApp';
|
||||
|
||||
/**
|
||||
* Custom hook for managing bulk actions on media items
|
||||
@@ -22,6 +23,20 @@ export function useBulkActions() {
|
||||
const [showPublishStateModal, setShowPublishStateModal] = useState(false);
|
||||
const [showCategoryModal, setShowCategoryModal] = useState(false);
|
||||
const [showTagModal, setShowTagModal] = useState(false);
|
||||
const [showCourseCleanupModal, setShowCourseCleanupModal] = useState(false);
|
||||
const [hasContributorCourses, setHasContributorCourses] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!inEmbeddedApp()) return;
|
||||
fetch('/api/v1/categories/contributor?lms_courses_only=true')
|
||||
.then((r) => r.ok ? r.json() : null)
|
||||
.then((data) => {
|
||||
if (!data) return;
|
||||
const courses = data.results || data;
|
||||
setHasContributorCourses(Array.isArray(courses) && courses.length > 0);
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
// Get CSRF token from cookies
|
||||
const getCsrfToken = () => {
|
||||
@@ -95,6 +110,11 @@ export function useBulkActions() {
|
||||
const handleBulkAction = (action) => {
|
||||
const selectedCount = selectedMedia.size;
|
||||
|
||||
if (action === 'course-cleanup') {
|
||||
setShowCourseCleanupModal(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCount === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -500,6 +520,22 @@ export function useBulkActions() {
|
||||
setShowTagModal(false);
|
||||
};
|
||||
|
||||
// Course cleanup modal handlers
|
||||
const handleCourseCleanupModalCancel = () => {
|
||||
setShowCourseCleanupModal(false);
|
||||
};
|
||||
|
||||
const handleCourseCleanupModalSuccess = (message) => {
|
||||
showNotificationMessage(message);
|
||||
clearSelectionAndRefresh();
|
||||
setShowCourseCleanupModal(false);
|
||||
};
|
||||
|
||||
const handleCourseCleanupModalError = (message) => {
|
||||
showNotificationMessage(message, 'error');
|
||||
setShowCourseCleanupModal(false);
|
||||
};
|
||||
|
||||
return {
|
||||
// State
|
||||
selectedMedia,
|
||||
@@ -517,6 +553,8 @@ export function useBulkActions() {
|
||||
showPublishStateModal,
|
||||
showCategoryModal,
|
||||
showTagModal,
|
||||
showCourseCleanupModal,
|
||||
hasContributorCourses,
|
||||
|
||||
// Handlers
|
||||
handleMediaSelection,
|
||||
@@ -544,6 +582,9 @@ export function useBulkActions() {
|
||||
handleTagModalCancel,
|
||||
handleTagModalSuccess,
|
||||
handleTagModalError,
|
||||
handleCourseCleanupModalCancel,
|
||||
handleCourseCleanupModalSuccess,
|
||||
handleCourseCleanupModalError,
|
||||
|
||||
// Utility
|
||||
getCsrfToken,
|
||||
|
||||
Reference in New Issue
Block a user