Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions app/controllers/course/duplications_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@ def create

def authorize_duplication
authorize!(:duplicate_from, current_course)
Comment thread
adi-herwana-nus marked this conversation as resolved.
return if instance_params == current_tenant.id

destination_tenant = Instance.find(instance_params)

authorize!(:duplicate_across_instances, current_tenant)
authorize!(:duplicate_across_instances, destination_tenant)
end

Expand Down
4 changes: 1 addition & 3 deletions app/controllers/course/object_duplications_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,13 @@ def load_videos_component_data
def load_destination_instances_data
@destination_instances = if current_user.administrator?
Instance.all
elsif can?(:duplicate_across_instances, current_tenant)
else
instance_ids = InstanceUser.unscope(where: :instance_id).
where(user_id: current_user.id,
role: [InstanceUser.roles[:instructor],
InstanceUser.roles[:administrator]]).
pluck(:instance_id)
Instance.where(id: instance_ids)
else
Instance.where(id: current_tenant.id)
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/views/course/object_duplications/new.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ json.destinationInstances sorted_destination_instances do |instance|
end

json.metadata do
json.canDuplicateToAnotherInstance can?(:duplicate_across_instances, current_tenant)
json.currentInstanceId current_tenant.id
json.currentInstanceHost current_tenant.host
end

json.partial! 'course_duplication_data'
12 changes: 11 additions & 1 deletion client/app/bundles/course/courses/pages/CoursesIndex/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ const CoursesIndex: FC = () => {
const [params] = useSearchParams();

const [isLoading, setIsLoading] = useState(true);
const [isRoleRequestDialogOpen, setRoleRequestDialogOpen] = useState(false);
const courses = useAppSelector(getAllCourseMiniEntities);

const coursesPermissions = useAppSelector(getCoursePermissions);
Expand All @@ -67,6 +66,13 @@ const CoursesIndex: FC = () => {
shouldOpenNewCourseDialog,
);

const shouldOpenRoleRequestDialog =
Boolean(params.get('request_instructor')) && !coursesPermissions?.canCreate;

const [isRoleRequestDialogOpen, setRoleRequestDialogOpen] = useState(
shouldOpenRoleRequestDialog,
);

useEffect(() => {
dispatch(fetchCourses())
.finally(() => setIsLoading(false))
Expand All @@ -77,6 +83,10 @@ const CoursesIndex: FC = () => {
setIsNewCourseDialogOpen(shouldOpenNewCourseDialog);
}, [shouldOpenNewCourseDialog]);

useEffect(() => {
setRoleRequestDialogOpen(shouldOpenRoleRequestDialog);
}, [shouldOpenRoleRequestDialog]);

// Adding appropriate button to the header
const headerToolbars: ReactElement[] = [];
if (coursesPermissions?.canCreate) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CSSProperties, FC, ReactNode } from 'react';
import { Checkbox, CheckboxProps, FormControlLabel } from '@mui/material';

const styles = {
tabSize: 15,
};

interface IndentedCheckboxProps extends CheckboxProps {
indentLevel?: number;
children?: ReactNode;
label: JSX.Element | string;
}

const IndentedCheckbox: FC<IndentedCheckboxProps> = ({
indentLevel = 0,
children = [],
label,
...props
}) => {
const checkboxStyle: CSSProperties = {
marginLeft: indentLevel * styles.tabSize,
};
if (children && Array.isArray(children) && children.length > 0) {
checkboxStyle.width = 'auto';
}

return (
<div className="flex items-center">
<FormControlLabel
className="py-2 px-0 w-auto"
control={
<Checkbox className="py-0 px-2" style={checkboxStyle} {...props} />
}
label={<b>{label}</b>}
/>
{children}
</div>
);
};

export default IndentedCheckbox;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { FC } from 'react';
import { defineMessages, MessageDescriptor } from 'react-intl';
import { Typography } from '@mui/material';

import { DuplicableItemType } from 'course/duplication/types';
import useTranslation from 'lib/hooks/useTranslation';

const translations: Record<DuplicableItemType, MessageDescriptor> =
defineMessages({
ASSESSMENT: {
id: 'course.duplication.TypeBadge.assessment',
defaultMessage: 'Assessment',
},
CATEGORY: {
id: 'course.duplication.TypeBadge.category',
defaultMessage: 'Category',
},
TAB: {
id: 'course.duplication.TypeBadge.tab',
defaultMessage: 'Tab',
},
SURVEY: {
id: 'course.duplication.TypeBadge.survey',
defaultMessage: 'Survey',
},
ACHIEVEMENT: {
id: 'course.duplication.TypeBadge.achievement',
defaultMessage: 'Achievement',
},
FOLDER: {
id: 'course.duplication.TypeBadge.folder',
defaultMessage: 'Folder',
},
MATERIAL: {
id: 'course.duplication.TypeBadge.material',
defaultMessage: 'Material',
},
VIDEO: {
id: 'course.duplication.TypeBadge.video',
defaultMessage: 'Video',
},
VIDEO_TAB: {
id: 'course.duplication.TypeBadge.video_tab',
defaultMessage: 'Tab',
},
});

const TypeBadge: FC<{ text?: string; itemType: DuplicableItemType }> = ({
text,
itemType,
}) => {
const { t } = useTranslation();

return (
<Typography
className="px-2 py-1 border border-solid rounded-md mr-3"
fontWeight="inherit"
variant="caption"
>
{text || t(translations[itemType])}
</Typography>
);
};

export default TypeBadge;
26 changes: 0 additions & 26 deletions client/app/bundles/course/duplication/constants.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,5 @@
import mirrorCreator from 'mirror-creator';

export const duplicationModes = mirrorCreator(['OBJECT', 'COURSE']);

// These are mirrored in app/helpers/course/object_duplications_helper.rb
export const duplicableItemTypes = mirrorCreator([
'ASSESSMENT',
'TAB',
'CATEGORY',
'SURVEY',
'ACHIEVEMENT',
'FOLDER',
'MATERIAL',
'VIDEO',
'VIDEO_TAB',
]);

// These are mirrored in app/helpers/course/object_duplications_helper.rb
export const itemSelectorPanels = mirrorCreator([
'ASSESSMENTS',
'SURVEYS',
'ACHIEVEMENTS',
'MATERIALS',
'VIDEOS',
]);

export const formNames = mirrorCreator(['NEW_COURSE']);

const actionTypes = mirrorCreator([
'LOAD_OBJECTS_LIST_REQUEST',
'LOAD_OBJECTS_LIST_SUCCESS',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import { defineMessages, FormattedMessage } from 'react-intl';
import MyLocation from '@mui/icons-material/MyLocation';
import { Autocomplete, Box, IconButton, Tooltip } from '@mui/material';

import {
selectDestinationInstances,
selectMetadata,
} from 'course/duplication/selectors/destinationInstance';
import { selectDestinationInstances } from 'course/duplication/selectors';
import TextField from 'lib/components/core/fields/TextField';
import { formatErrorMessage } from 'lib/components/form/fields/utils/mapError';
import { useAppSelector } from 'lib/hooks/store';
Expand Down Expand Up @@ -40,7 +37,6 @@ const translations = defineMessages({
const InstanceDropdown: FC<InstanceDropdownProps> = (props) => {
const { currentInstanceId, disabled, field, fieldState, setValue } = props;
const instances = useAppSelector(selectDestinationInstances);
const metadata = useAppSelector(selectMetadata);
const instanceIds = useMemo(
() =>
Object.keys(instances).toSorted(
Expand All @@ -54,7 +50,7 @@ const InstanceDropdown: FC<InstanceDropdownProps> = (props) => {
<div className="flex">
<Autocomplete
{...field}
disabled={disabled || !metadata.canDuplicateToAnotherInstance}
disabled={disabled || !instanceIds.length}
fullWidth
getOptionLabel={(instanceId): string =>
instances[instanceId]?.name ?? ''
Expand Down Expand Up @@ -85,7 +81,7 @@ const InstanceDropdown: FC<InstanceDropdownProps> = (props) => {
<div className="flex items-end">
<Tooltip title={t(translations.currentInstance)}>
<IconButton
disabled={disabled}
disabled={disabled || !(currentInstanceId in instances)}
onClick={() =>
setValue('destination_instance_id', currentInstanceId)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import CourseDropdownMenu from 'course/duplication/components/CourseDropdownMenu';
import { duplicationModes } from 'course/duplication/constants';
import { duplicateCourse } from 'course/duplication/operations';
import { courseShape, sourceCourseShape } from 'course/duplication/propTypes';
import { actions } from 'course/duplication/store';
Expand Down Expand Up @@ -116,7 +115,7 @@ class DestinationCourseSelector extends Component {
render() {
const { duplicationMode } = this.props;

return duplicationMode === duplicationModes.COURSE
return duplicationMode === 'COURSE'
? this.renderNewCourseForm()
: this.renderExistingCourseForm();
}
Expand Down Expand Up @@ -144,7 +143,9 @@ export default connect(({ duplication }) => ({
destinationCourseId: duplication.destinationCourseId,
duplicationMode: duplication.duplicationMode,
sourceCourse: duplication.sourceCourse,
currentInstanceId: duplication.metadata.currentInstanceId,
currentInstanceId:
duplication.destinationInstances[duplication.metadata.currentInstanceId]
?.id,
Comment thread
adi-herwana-nus marked this conversation as resolved.
destinationInstances: duplication.destinationInstances,
isDuplicating: duplication.isDuplicating,
}))(injectIntl(DestinationCourseSelector));
Loading