diff --git a/assets/src/js/v3/@types/index.d.ts b/assets/src/js/v3/@types/index.d.ts
index ccef5f4850..4b25dd5663 100644
--- a/assets/src/js/v3/@types/index.d.ts
+++ b/assets/src/js/v3/@types/index.d.ts
@@ -187,6 +187,7 @@ declare global {
enable_individual_tax_control: boolean;
is_tax_included_in_price: boolean;
pagination_per_page: string;
+ quiz_attempts_allowed: string;
};
tutor_currency: {
symbol: string;
diff --git a/assets/src/js/v3/entries/addon-list/components/App.tsx b/assets/src/js/v3/entries/addon-list/components/App.tsx
index ce5535fb5a..b67034f82a 100644
--- a/assets/src/js/v3/entries/addon-list/components/App.tsx
+++ b/assets/src/js/v3/entries/addon-list/components/App.tsx
@@ -2,6 +2,7 @@ import { Global } from '@emotion/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ToastProvider from '@TutorShared/atoms/Toast';
import RTLProvider from '@TutorShared/components/RTLProvider';
+import { SVGIconConfigProvider } from '@TutorShared/contexts/SVGIconConfigContext';
import { createGlobalCss } from '@TutorShared/utils/style-utils';
import { useState } from 'react';
import Main from './layout/Main';
@@ -27,10 +28,12 @@ function App() {
return (
-
-
-
-
+
+
+
+
+
+
);
diff --git a/assets/src/js/v3/entries/coupon-details/components/App.tsx b/assets/src/js/v3/entries/coupon-details/components/App.tsx
index e84684d87a..696e88f3e7 100644
--- a/assets/src/js/v3/entries/coupon-details/components/App.tsx
+++ b/assets/src/js/v3/entries/coupon-details/components/App.tsx
@@ -3,6 +3,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ToastProvider from '@TutorShared/atoms/Toast';
import { ModalProvider } from '@TutorShared/components/modals/Modal';
import RTLProvider from '@TutorShared/components/RTLProvider';
+import { SVGIconConfigProvider } from '@TutorShared/contexts/SVGIconConfigContext';
import { createGlobalCss } from '@TutorShared/utils/style-utils';
import { useState } from 'react';
import Main from './layout/Main';
@@ -27,12 +28,14 @@ function App() {
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
diff --git a/assets/src/js/v3/entries/course-builder/components/App.tsx b/assets/src/js/v3/entries/course-builder/components/App.tsx
index 8e8469d6da..7389f19e13 100644
--- a/assets/src/js/v3/entries/course-builder/components/App.tsx
+++ b/assets/src/js/v3/entries/course-builder/components/App.tsx
@@ -3,6 +3,7 @@ import { CourseBuilderSlotProvider } from '@CourseBuilderContexts/CourseBuilderS
import ToastProvider from '@TutorShared/atoms/Toast';
import RTLProvider from '@TutorShared/components/RTLProvider';
import { ModalProvider } from '@TutorShared/components/modals/Modal';
+import { SVGIconConfigProvider } from '@TutorShared/contexts/SVGIconConfigContext';
import { createGlobalCss } from '@TutorShared/utils/style-utils';
import { Global } from '@emotion/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -32,14 +33,16 @@ const App = () => {
return (
-
-
-
-
- {routers}
-
-
-
+
+
+
+
+
+ {routers}
+
+
+
+
);
diff --git a/assets/src/js/v3/entries/course-builder/components/modals/QuizModal.tsx b/assets/src/js/v3/entries/course-builder/components/modals/QuizModal.tsx
index fa86c556e9..5b9c6b0466 100644
--- a/assets/src/js/v3/entries/course-builder/components/modals/QuizModal.tsx
+++ b/assets/src/js/v3/entries/course-builder/components/modals/QuizModal.tsx
@@ -44,6 +44,7 @@ import { POPOVER_PLACEMENTS } from '@TutorShared/hooks/usePortalPopover';
import { validateQuizQuestion } from '@TutorShared/utils/quiz';
import { type ID, isDefined, type TopicContentType } from '@TutorShared/utils/types';
import { findSlotFields } from '@TutorShared/utils/util';
+import { tutorConfig } from '@TutorShared/config/config';
interface QuizModalProps extends ModalProps {
quizId?: ID;
@@ -56,6 +57,7 @@ interface QuizModalProps extends ModalProps {
type QuizTabs = 'details' | 'settings';
const courseId = getCourseId();
+const defaultQuizAttemptsAllowed = tutorConfig.settings?.quiz_attempts_allowed ?? 10;
const QuizModal = ({
closeModal,
@@ -89,7 +91,7 @@ const QuizModal = ({
hide_quiz_time_display: false,
limit_attempts_allowed: false,
limit_questions_to_answer: false,
- attempts_allowed: 10,
+ attempts_allowed: defaultQuizAttemptsAllowed,
passing_grade: 80,
max_questions_for_answer: contentType === 'tutor_h5p_quiz' ? 0 : 10,
quiz_auto_start: false,
diff --git a/assets/src/js/v3/entries/course-builder/services/quiz.ts b/assets/src/js/v3/entries/course-builder/services/quiz.ts
index 2f1a0a849a..0c21947507 100644
--- a/assets/src/js/v3/entries/course-builder/services/quiz.ts
+++ b/assets/src/js/v3/entries/course-builder/services/quiz.ts
@@ -163,6 +163,7 @@ interface QuizUpdateQuestionPayload {
}
export const convertQuizResponseToFormData = (quiz: QuizDetailsResponse, slotFields: string[]): QuizForm => {
+ const defaultQuizAttemptsAllowed = tutorConfig.settings?.quiz_attempts_allowed ?? 10;
const legacyQuizOption = quiz.quiz_option as QuizDetailsResponse['quiz_option'] & {
feedback_mode?: 'default' | 'reveal' | 'retry';
};
@@ -182,7 +183,7 @@ export const convertQuizResponseToFormData = (quiz: QuizDetailsResponse, slotFie
limit_attempts_allowed: isDefined(quiz.quiz_option.limit_attempts_allowed)
? quiz.quiz_option.limit_attempts_allowed === '1'
: legacyQuizOption.feedback_mode === 'retry',
- attempts_allowed: quiz.quiz_option.attempts_allowed ?? 10,
+ attempts_allowed: quiz.quiz_option.attempts_allowed ?? defaultQuizAttemptsAllowed,
pass_is_required: quiz.quiz_option.pass_is_required === '1',
passing_grade: quiz.quiz_option.passing_grade ?? 80,
limit_questions_to_answer: !!Number(quiz.quiz_option.max_questions_for_answer),
diff --git a/assets/src/js/v3/entries/import-export/components/App.tsx b/assets/src/js/v3/entries/import-export/components/App.tsx
index 84beede438..2cd814e012 100644
--- a/assets/src/js/v3/entries/import-export/components/App.tsx
+++ b/assets/src/js/v3/entries/import-export/components/App.tsx
@@ -6,6 +6,7 @@ import Main from '@ImportExport/components/Main';
import ToastProvider from '@TutorShared/atoms/Toast';
import RTLProvider from '@TutorShared/components/RTLProvider';
import { ModalProvider } from '@TutorShared/components/modals/Modal';
+import { SVGIconConfigProvider } from '@TutorShared/contexts/SVGIconConfigContext';
import { createGlobalCss } from '@TutorShared/utils/style-utils';
function App() {
@@ -29,12 +30,14 @@ function App() {
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
diff --git a/assets/src/js/v3/entries/order-details/components/App.tsx b/assets/src/js/v3/entries/order-details/components/App.tsx
index 4b7f6b4980..f043072233 100644
--- a/assets/src/js/v3/entries/order-details/components/App.tsx
+++ b/assets/src/js/v3/entries/order-details/components/App.tsx
@@ -6,6 +6,7 @@ import ToastProvider from '@TutorShared/atoms/Toast';
import RTLProvider from '@TutorShared/components/RTLProvider';
import { ModalProvider } from '@TutorShared/components/modals/Modal';
+import { SVGIconConfigProvider } from '@TutorShared/contexts/SVGIconConfigContext';
import { createGlobalCss } from '@TutorShared/utils/style-utils';
import Main from './layout/Main';
@@ -30,12 +31,14 @@ function App() {
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
diff --git a/assets/src/js/v3/entries/payment-settings/components/App.tsx b/assets/src/js/v3/entries/payment-settings/components/App.tsx
index c47eb3fb7c..84194e955d 100644
--- a/assets/src/js/v3/entries/payment-settings/components/App.tsx
+++ b/assets/src/js/v3/entries/payment-settings/components/App.tsx
@@ -5,6 +5,7 @@ import { useState } from 'react';
import ToastProvider from '@TutorShared/atoms/Toast';
import RTLProvider from '@TutorShared/components/RTLProvider';
import { ModalProvider } from '@TutorShared/components/modals/Modal';
+import { SVGIconConfigProvider } from '@TutorShared/contexts/SVGIconConfigContext';
import { createGlobalCss } from '@TutorShared/utils/style-utils';
import { PaymentProvider } from '../contexts/payment-context';
@@ -31,14 +32,16 @@ function App() {
return (
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
);
diff --git a/assets/src/js/v3/entries/tax-settings/components/App.tsx b/assets/src/js/v3/entries/tax-settings/components/App.tsx
index 235918190e..fe0d357527 100644
--- a/assets/src/js/v3/entries/tax-settings/components/App.tsx
+++ b/assets/src/js/v3/entries/tax-settings/components/App.tsx
@@ -5,6 +5,7 @@ import { useState } from 'react';
import ToastProvider from '@TutorShared/atoms/Toast';
import RTLProvider from '@TutorShared/components/RTLProvider';
import { ModalProvider } from '@TutorShared/components/modals/Modal';
+import { SVGIconConfigProvider } from '@TutorShared/contexts/SVGIconConfigContext';
import { createGlobalCss } from '@TutorShared/utils/style-utils';
import TaxSettingsPage from './TaxSettings';
@@ -30,12 +31,14 @@ function App() {
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
diff --git a/assets/src/js/v3/shared/atoms/SVGIcon.tsx b/assets/src/js/v3/shared/atoms/SVGIcon.tsx
index 30383f1b4f..a6e97bd390 100644
--- a/assets/src/js/v3/shared/atoms/SVGIcon.tsx
+++ b/assets/src/js/v3/shared/atoms/SVGIcon.tsx
@@ -1,4 +1,5 @@
import { tutorConfig } from '@TutorShared/config/config';
+import { useSVGIconConfig } from '@TutorShared/contexts/SVGIconConfigContext';
import { type IconCollection } from '@TutorShared/icons/types';
import { type SerializedStyles, css } from '@emotion/react';
import { memo, useEffect, useState } from 'react';
@@ -28,16 +29,10 @@ interface IconCacheEntry {
const iconCache: Record = {};
-const SVGIcon = ({
- name,
- width = 16,
- height = 16,
- style,
- isColorIcon = false,
- ignoreKids = false,
- ...rest
-}: SVGIconProps) => {
- const cacheKey = ignoreKids ? `${name}-ignoreKids` : name;
+const SVGIcon = ({ name, width = 16, height = 16, style, isColorIcon = false, ignoreKids, ...rest }: SVGIconProps) => {
+ const { showKidsIcons } = useSVGIconConfig();
+ const shouldIgnoreKids = ignoreKids ?? !showKidsIcons;
+ const cacheKey = shouldIgnoreKids ? `${name}-ignoreKids` : name;
const [icon, setIcon] = useState(iconCache[cacheKey]?.icon || null);
const [isLoading, setIsLoading] = useState(!iconCache[cacheKey]?.icon);
@@ -50,7 +45,7 @@ const SVGIcon = ({
setIsLoading(true);
- fetchIcon(name, cacheKey, width, height, ignoreKids)
+ fetchIcon(name, cacheKey, width, height, shouldIgnoreKids)
.then((loadedIcon) => {
setIcon(loadedIcon);
})
@@ -60,7 +55,7 @@ const SVGIcon = ({
.finally(() => {
setIsLoading(false);
});
- }, [name, width, height, ignoreKids, cacheKey]);
+ }, [name, width, height, shouldIgnoreKids, cacheKey]);
const additionalAttributes = {
...(isColorIcon && { 'data-colorize': true }),
diff --git a/assets/src/js/v3/shared/contexts/SVGIconConfigContext.tsx b/assets/src/js/v3/shared/contexts/SVGIconConfigContext.tsx
new file mode 100644
index 0000000000..80404643e5
--- /dev/null
+++ b/assets/src/js/v3/shared/contexts/SVGIconConfigContext.tsx
@@ -0,0 +1,31 @@
+import React, { useContext } from 'react';
+
+interface SVGIconConfigContextValue {
+ supportKidsIcon: boolean;
+}
+
+const defaultValue: SVGIconConfigContextValue = {
+ supportKidsIcon: false,
+};
+
+const SVGIconConfigContext = React.createContext(defaultValue);
+
+export const useSVGIconConfig = () => useContext(SVGIconConfigContext);
+
+export const SVGIconConfigProvider = ({
+ children,
+ supportKidsIcon = false,
+}: {
+ children: React.ReactNode;
+ supportKidsIcon?: boolean;
+}) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/classes/Course.php b/classes/Course.php
index 70b2fc1ad7..ad8c871a38 100644
--- a/classes/Course.php
+++ b/classes/Course.php
@@ -1543,6 +1543,7 @@ public function localize_course_builder_data( $data ) {
'instructor_can_publish_course',
'instructor_can_change_course_author',
'instructor_can_manage_co_instructors',
+ 'quiz_attempts_allowed',
);
$full_settings = get_option( 'tutor_option', array() );