Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raised for testing purpose - Contains all code of GSoC 2024 #5946

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e5661dc
Added functionality for creating new Training Libraries
omChauhanDev Jun 3, 2024
317e48a
Added rspec test cases
omChauhanDev Jun 5, 2024
ab25d9c
Changed the validation pattern
omChauhanDev Jun 6, 2024
201c7d8
Added more scoping for styles
omChauhanDev Jun 6, 2024
8aba0ec
Developed functionality of adding categories to training libraries
omChauhanDev Jun 8, 2024
111fac8
Added functionality of deleting category which are having no modules
omChauhanDev Jun 12, 2024
ae7973b
Created rspec tests for adding and removing category in Training Library
omChauhanDev Jun 13, 2024
08e6c93
Merge branch 'FunctionalityToCreateNewTrainingContent' of https://git…
ragesoss Jun 17, 2024
8e753d2
Improved: Refreshing the page, adjustedtest cases accordingly
omChauhanDev Jun 20, 2024
ff60b57
Added Functionality to Switch between Edit and View Mode for Training…
omChauhanDev Jun 20, 2024
535fbd7
refactored the code: reduced backend calls
omChauhanDev Jun 21, 2024
cc301e9
Merge pull request #5839 from omChauhanDev/feature/switch-edit-view-m…
ragesoss Jun 21, 2024
e8ffd42
created-functionality-of-adding-new-training-module
omChauhanDev Jun 25, 2024
3cd087e
Merge pull request #5855 from omChauhanDev/feature/add-module-to-trai…
ragesoss Jul 1, 2024
1a3de22
added feature of transferring modules between categories and created …
omChauhanDev Jul 8, 2024
c06f5fb
Simplified code and removed redundancy
omChauhanDev Jul 11, 2024
9c64bb7
modified approach: using #react_root instead of parsing data from html
omChauhanDev Jul 30, 2024
0fe5c74
removed unnecessary function
omChauhanDev Jul 30, 2024
e3c9023
developed functionality for adding slides to training module
omChauhanDev Jul 30, 2024
a488647
added test cases for adding slides to training module
omChauhanDev Jul 30, 2024
3233c56
developed functionality for adding slides to training module
omChauhanDev Jul 30, 2024
d7000dd
added test cases for adding slides to training module
omChauhanDev Jul 30, 2024
828bd46
added functionality for removing training slides
omChauhanDev Jul 31, 2024
01bb5f9
added test case for removing training slide
omChauhanDev Jul 31, 2024
19d2f49
Merge pull request #5889 from omChauhanDev/feature/transfer-module-be…
ragesoss Aug 12, 2024
eb8b587
added functionality for reordering training slides
omChauhanDev Aug 15, 2024
1d764de
added rspec tests for reordering of training slides
omChauhanDev Aug 15, 2024
0bdf3ed
added access rights for modifying training slide
omChauhanDev Aug 15, 2024
08ce977
Merge pull request #5914 from omChauhanDev/feature/adding-slide-in-tr…
ragesoss Aug 15, 2024
63e4664
Merge branch 'Om-Training' into feature/ordering-slides-of-training-m…
omChauhanDev Sep 17, 2024
596a1a4
added new line at the end
Sep 18, 2024
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
272 changes: 272 additions & 0 deletions app/assets/javascripts/actions/training_modification_actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
import { API_FAIL } from '../constants/api';
import { addNotification } from '../actions/notification_actions';
import logErrorMessage from '../utils/log_error_message';
import request from '../utils/request';
import { setInvalid } from './validation_actions';
import { SET_TRAINING_MODE } from '../constants/training';

// For Modifying Training Content
const libraryValidationRules = [
{ keyword: 'name', field: 'name' },
{ keyword: 'slug', field: 'slug' },
{ keyword: 'introduction', field: 'introduction' }
];

const categoryValidationRules = [
{ keyword: 'title', field: 'title' },
{ keyword: 'description', field: 'description' }
];

const moduleValidationRules = [
{ keyword: 'name', field: 'name' },
{ keyword: 'slug', field: 'slug' },
{ keyword: 'description', field: 'description' }
];

const slideValidationRules = [
{ keyword: 'title', field: 'title' },
{ keyword: 'slug', field: 'slug' },
{ keyword: 'wikipage', field: 'wiki_page' }
];

// For switching between edit and view mode
export const getTrainingMode = () => (dispatch) => {
dispatch({
type: SET_TRAINING_MODE,
});
};

const updateTrainingModePromise = async (editMode, setUpdatingEditMode) => {
const body = {
edit_mode: editMode,
};
const response = await request('training_mode/update', {
method: 'POST',
body: JSON.stringify(body),
});
setUpdatingEditMode(false);
if (!response.ok) {
logErrorMessage(response);
const data = await response.text();
response.responseText = data;
throw response;
}
return response.json();
};

export const updateTrainingMode = (editMode, setUpdatingEditMode) => (dispatch) => {
return updateTrainingModePromise(editMode, setUpdatingEditMode)
.then(() => window.location.reload())
.catch(data => dispatch({ type: API_FAIL, data }));
};

const performValidation = (error, dispatch, validationRules) => {
const errorMessages = error.responseText.errorMessages;
let apiFailDispatched = false;

for (let i = 0; i < errorMessages.length; i += 1) {
const message = errorMessages[i];
const lowercaseMessage = message.toLowerCase();
const matchedRule = validationRules.find(rule => lowercaseMessage.includes(rule.keyword));

if (matchedRule) {
dispatch(setInvalid(matchedRule.field, message));
} else {
if (!apiFailDispatched) {
dispatch({ type: API_FAIL, data: error });
apiFailDispatched = true;
}
return;
}
}
};


// For Creating New Library
const createLibraryPromise = async (library, setSubmitting) => {
const response = await request('/training/create_library', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(library),
});
setSubmitting(false);
if (!response.ok) {
logErrorMessage(response);
const data = await response.json();
response.responseText = data;
throw response;
}
return response.json();
};

export const createLibrary = (library, setSubmitting, toggleModal) => (dispatch) => {
return createLibraryPromise(library, setSubmitting)
.then(() => {
toggleModal();
dispatch(addNotification({
type: 'success',
message: 'Library Created Successfully.',
closable: true
}));
window.location.reload();
})
.catch((error) => {
performValidation(error, dispatch, libraryValidationRules);
});
};

// For Creating New Category
const createCategoryPromise = async (library_id, category, setSubmitting) => {
const response = await request(`/training/${library_id}/create_category`, {
method: 'POST',
body: JSON.stringify({ category }),
});
setSubmitting(false);
if (!response.ok) {
logErrorMessage(response);
const data = await response.json();
response.responseText = data;
throw response;
}
return response.json();
};

export const createCategory = (library_id, category, setSubmitting, toggleModal) => (dispatch) => {
return createCategoryPromise(library_id, category, setSubmitting)
.then(() => {
toggleModal();
dispatch(addNotification({
type: 'success',
message: 'Category Created Successfully.',
closable: true
}));
window.location.reload();
})
.catch((error) => {
performValidation(error, dispatch, categoryValidationRules);
});
};

// For Adding New Module
const addModulePromise = async (library_id, category_id, module, setSubmitting) => {
const response = await request(`/training/${library_id}/${category_id}/add_module`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ module }),
});
setSubmitting(false);
if (!response.ok) {
logErrorMessage(response);
const data = await response.json();
response.responseText = data;
throw response;
}
return response.json();
};

export const addModule = (library_id, category_id, module, setSubmitting) => (dispatch) => {
return addModulePromise(library_id, category_id, module, setSubmitting)
.then(() => {
window.location.href = `/training/${library_id}`;
})
.catch((error) => {
performValidation(error, dispatch, moduleValidationRules);
});
};

// For Transferring Modules
const transferModulesPromise = async (library_id, transferInfo, setSubmitting) => {
const response = await request(`/training/${library_id}/transfer_modules`, {
method: 'PUT',
body: JSON.stringify({ transferInfo }),
});
setSubmitting(false);
if (!response.ok) {
logErrorMessage(response);
const data = await response.json();
response.responseText = data;
throw response;
}
return response.json();
};

export const transferModules = (library_id, transferInfo, setSubmitting) => (dispatch) => {
return transferModulesPromise(library_id, transferInfo, setSubmitting)
.then(() => {
window.location.reload();
})
.catch(data => dispatch({ type: API_FAIL, data }));
};

// For Adding New Slide to Training Module
const addSlidePromise = async (library_id, module_id, slide, setSubmitting) => {
const response = await request(`/training/${library_id}/${module_id}/add_slide`, {
method: 'POST',
body: JSON.stringify({ slide }),
});
setSubmitting(false);
if (!response.ok) {
logErrorMessage(response);
const data = await response.json();
response.responseText = data;
throw response;
}
return response.json();
};

export const addSlide = (library_id, module_id, slide, setSubmitting) => (dispatch) => {
return addSlidePromise(library_id, module_id, slide, setSubmitting)
.then(() => window.location.reload())
.catch((error) => {
performValidation(error, dispatch, slideValidationRules);
});
};

// For Removing Slide from Training Module
const removeSlidesPromise = async (module_id, slideSlugList) => {
const response = await request(`/training/${module_id}/remove_slide`, {
method: 'DELETE',
body: JSON.stringify({ slideSlugList }),
});
if (!response.ok) {
logErrorMessage(response);
const data = await response.json();
response.responseText = data;
throw response;
}
return response.json();
};

export const removeSlides = (module_id, slideSlugList) => (dispatch) => {
return removeSlidesPromise(module_id, slideSlugList)
.then(() => window.location.reload())
.catch(data => dispatch({ type: API_FAIL, data }));
};

// For Reordering Slides
const reorderSlidesPromise = async (module_id, slides, setSubmitting) => {
// Only sending necessary data to server
const reorderedSlides = slides.map(slide => slide.slug);
const response = await request(`/training/${module_id}/reorder_slides`, {
method: 'PUT',
body: JSON.stringify({ reorderedSlides }),
});
setSubmitting(false);
if (!response.ok) {
logErrorMessage(response);
const data = await response.json();
response.responseText = data;
throw response;
}
return response.json();
};

export const reorderSlides = (module_id, slides, setSubmitting) => (dispatch) => {
return reorderSlidesPromise(module_id, slides, setSubmitting)
.then(() => window.location.reload())
.catch(data => dispatch({ type: API_FAIL, data }));
};
18 changes: 15 additions & 3 deletions app/assets/javascripts/components/common/text_area_input.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ const TextAreaInput = ({
markdown,
className,
clearOnSubmit,
invalid
invalid,
label,
spacer
}) => {
const [tinymceLoaded, setTinymceLoaded] = useState(false);
const [activeEditor, setActiveEditor] = useState(null);
const labelContent = label ? `${label}${spacer || ': '}` : undefined;

useEffect(() => {
if (wysiwyg) {
Expand Down Expand Up @@ -113,7 +116,14 @@ const TextAreaInput = ({
</div>
);
}
return <div>{inputElement}</div>;
return (
<div className="form-group">
<label id={`${id}-label`} htmlFor={id} className={invalid ? 'red' : ''}>
{labelContent}
</label>
{inputElement}
</div>
);
}

// ////////////
Expand Down Expand Up @@ -143,7 +153,9 @@ TextAreaInput.propTypes = {
wysiwyg: PropTypes.bool, // use rich text editor instead of plain text
markdown: PropTypes.bool, // render value as Markdown when in read mode
className: PropTypes.string,
clearOnSubmit: PropTypes.bool
clearOnSubmit: PropTypes.bool,
label: PropTypes.string,
spacer: PropTypes.string,
};

export default InputHOC(TextAreaInput);
1 change: 1 addition & 0 deletions app/assets/javascripts/constants/training.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export const SET_CURRENT_SLIDE = 'SET_CURRENT_SLIDE';
export const RECEIVE_ALL_TRAINING_MODULES = 'RECEIVE_ALL_TRAINING_MODULES';
export const SLIDE_COMPLETED = 'SLIDE_COMPLETED';
export const EXERCISE_COMPLETION_UPDATE = 'EXERCISE_COMPLETION_UPDATE';
export const SET_TRAINING_MODE = 'SET_TRAINING_MODE';
12 changes: 10 additions & 2 deletions app/assets/javascripts/reducers/training.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { findIndex } from 'lodash-es';
import {
RECEIVE_TRAINING_MODULE, MENU_TOGGLE, REVIEW_ANSWER,
SET_CURRENT_SLIDE, RECEIVE_ALL_TRAINING_MODULES,
SLIDE_COMPLETED
SLIDE_COMPLETED, SET_TRAINING_MODE
} from '../constants';

const mainDiv = document.getElementById('main');

const reviewAnswer = function (state, answer) {
const answerId = parseInt(answer);
const temp = { ...state, currentSlide: { ...state.currentSlide, selectedAnswer: answerId } };
Expand Down Expand Up @@ -69,7 +71,8 @@ const initialState = {
enabledSlides: [],
loading: true,
completed: null,
valid: false
valid: false,
editMode: false,
};

export default function training(state = initialState, action) {
Expand Down Expand Up @@ -100,6 +103,11 @@ export default function training(state = initialState, action) {
enabledSlides: [...state.enabledSlides, data.slide.id],
completed: data.completed
};
case SET_TRAINING_MODE:
return {
...state,
editMode: JSON.parse(mainDiv.getAttribute('data-training_mode')).editMode
};
default:
return state;
}
Expand Down
Loading
Loading