-
${fx_static.t(h_section.get('link_title'))}
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_faq_section_v1.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_faq_section_v1.html
index f3a440730..69a36d2df 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_faq_section_v1.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_faq_section_v1.html
@@ -22,16 +22,19 @@
'answer': answer,
'item_id': fx_static.fx_random_string(),
})
+
+background_color = page_contents.get('background_color', 'transparent')
+style = f"background-color: {background_color};"
%>
-
+
-
${fx_title | fx_static.br}
-
+
+
% for faq_item in fx_faq_items:
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_featured_courses_v1.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_featured_courses_v1.html
index c49c12319..d30d0d1c3 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_featured_courses_v1.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_featured_courses_v1.html
@@ -1,6 +1,11 @@
<%page args="page_contents"/>
<%namespace name='fx_static' file='../fx_static_content.html'/>
+<%!
+from django.utils.translation import gettext as _
+import random
+import string
+%>
<%
id_tag = page_contents.get('div_id')
@@ -12,18 +17,57 @@
fx_grid_type = page_contents.get('grid_type', '')
if fx_grid_type not in ['max-3', 'max-6', 'max-9', 'all', 'categorised']:
fx_grid_type = 'max-9'
+
+background_color = page_contents.get('background_color', 'transparent')
+style = f"background-color: {background_color};"
+
+# Get course categories for categorised grid type
+fx_course_categories = []
+if fx_grid_type == 'categorised':
+ # Call the module-level function defined in fx_static_content.html
+ from futurex_openedx_extensions.helpers.tenants import get_config_current_request
+ from django.conf import settings
+
+ config_key = getattr(settings, 'FX_COURSE_CATEGORY_CONFIG_KEY', 'course_categories')
+ config_value = get_config_current_request([config_key])
+
+ if config_value:
+ result = config_value.get('values', {}).get(config_key, {})
+ if result and isinstance(result, dict):
+ categories = result.get('categories', {})
+ sorting = result.get('sorting', [])
+
+ # Build ordered list of categories
+ for cat_id in sorting:
+ if cat_id in categories:
+ cat_data = categories[cat_id]
+ fx_course_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ # Add any categories not in sorting list
+ for cat_id, cat_data in categories.items():
+ if cat_id not in sorting:
+ fx_course_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+# Generate unique ID for this section's tabs
+section_id = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
%>
-
-
-
- <%include file="../../courses_list.html" args="fx_grid_type=fx_grid_type"/>
+- ${fx_title | fx_static.br} -
-- ${fx_description | fx_static.br} -
-
+
+
+ % if fx_grid_type == 'categorised' and fx_course_categories:
+ <%include file="../../courses_list_categorised.html" args="fx_course_categories=fx_course_categories, section_id=section_id, section_title=page_contents.get('title', ''), section_description=page_contents.get('description', '')"/>
+ % else:
+ <%include file="../../courses_list.html" args="fx_grid_type=fx_grid_type, section_title=page_contents.get('title', ''), section_description=page_contents.get('description', '')"/>
+ % endif
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_hero_section_v1.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_hero_section_v1.html
index 36384effd..05b347b10 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_hero_section_v1.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_hero_section_v1.html
@@ -10,10 +10,12 @@
fx_title = fx_static.t(page_contents.get('title'))
fx_description = fx_static.t(page_contents.get('description'))
image_url = page_contents.get('hero_image')
+background_color = page_contents.get('background_color', 'transparent')
+
if image_url:
- style = f"background-image: url({image_url}); background-repeat: no-repeat;"
+ style = f"background-image: url({image_url}); background-repeat: no-repeat; background-color: {background_color};"
else:
- style = "background-color: black;"
+ style = f"background-color: {background_color if background_color != 'transparent' else 'black'};"
%>
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_live_metrics_section_v1.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_live_metrics_section_v1.html
index 3db0c45ee..06850b305 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_live_metrics_section_v1.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_live_metrics_section_v1.html
@@ -63,9 +63,12 @@
alignment = page_contents.get('alignment')
alignment = alignment if alignment in ['start', 'end'] else 'center'
+
+background_color = page_contents.get('background_color', 'transparent')
+style = f"background-color: {background_color};"
%>
-
+
% if fx_title or fx_description:
% if fx_title:
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_paragraph_section_v1.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_paragraph_section_v1.html
index f5cad3d8d..27af5166b 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_paragraph_section_v1.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_paragraph_section_v1.html
@@ -8,9 +8,12 @@
id_tag = f'id="{id_tag}"'
fx_html_content = fx_static.t(page_contents.get('paragraph'))
+
+background_color = page_contents.get('background_color', 'transparent')
+style = f"background-color: {background_color};"
%>
-
+
${fx_html_content | n}
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_side_image_v1.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_side_image_v1.html
index 4df38387a..f50425306 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_side_image_v1.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_side_image_v1.html
@@ -17,6 +17,12 @@
if isinstance(is_reversed, str):
is_reversed = is_reversed.lower() == 'yes'
fx_style = f'flex-direction: row-reverse' if is_reversed else ''
+
+background_color = page_contents.get('background_color', 'transparent')
+if fx_style:
+ fx_style = f"{fx_style}; background-color: {background_color};"
+else:
+ fx_style = f"background-color: {background_color};"
%>
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_static_metrics_section_v1.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_static_metrics_section_v1.html
index 21d7f3b95..54de5e6de 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_static_metrics_section_v1.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_static_metrics_section_v1.html
@@ -13,9 +13,12 @@
alignment = page_contents.get('alignment')
alignment = alignment if alignment in ['start', 'end'] else 'center'
+
+background_color = page_contents.get('background_color', 'transparent')
+style = f"background-color: {background_color};"
%>
-
+
% if fx_title or fx_description:
% if fx_title:
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_two_columns_v1.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_two_columns_v1.html
index 9df0eab01..5f99dbd01 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_two_columns_v1.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_page/_section_two_columns_v1.html
@@ -14,9 +14,12 @@
columns_sections = []
for i in range(min(3, len(declared_columns_section))):
columns_sections.append(declared_columns_section[i])
+
+background_color = page_contents.get('background_color', 'transparent')
+style = f"background-color: {background_color};"
%>
-
+
% if fx_title:
${fx_title | fx_static.br}
@@ -27,7 +30,7 @@
${fx_title | fx_static.br}
@@ -27,7 +30,7 @@
${fx_description | fx_static.br}
% endif
-
+
% for columns_section in columns_sections:
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_static_content.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_static_content.html
index a43dd5f1b..1010cb16e 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_static_content.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_static_content.html
@@ -4,6 +4,57 @@
from django.utils.translation import get_language
from django.conf import settings
from lms.djangoapps.branding import api as branding_api
+
+
+def get_course_categories_config():
+ """
+ Get course categories from tenant configuration.
+ Returns a dict with 'categories' and 'sorting' keys.
+ """
+ config_key = getattr(settings, 'FX_COURSE_CATEGORY_CONFIG_KEY', 'course_categories')
+ config_value = get_config_current_request([config_key])
+ if not config_value:
+ return {'categories': {}, 'sorting': []}
+ result = config_value['values'].get(config_key, {})
+ if not result or not isinstance(result, dict):
+ return {'categories': {}, 'sorting': []}
+ return {
+ 'categories': result.get('categories', {}),
+ 'sorting': result.get('sorting', []),
+ }
+
+
+def get_course_categories_list():
+ """
+ Get course categories configuration for the current tenant.
+ Returns a list of category dicts with 'id', 'label', and 'courses' keys,
+ sorted according to the tenant's sorting preference.
+ """
+ config = get_course_categories_config()
+ categories = config.get('categories', {})
+ sorting = config.get('sorting', [])
+
+ # Build ordered list of categories
+ ordered_categories = []
+ for cat_id in sorting:
+ if cat_id in categories:
+ cat_data = categories[cat_id]
+ ordered_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ # Add any categories not in sorting list
+ for cat_id, cat_data in categories.items():
+ if cat_id not in sorting:
+ ordered_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ return ordered_categories
%>
<%def name="br(text)" buffered="True"><%
@@ -144,3 +195,60 @@
import string
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
%>%def>
+
+
+<%def name="get_fx_course_categories()" buffered="True"><%
+ """
+ Get course categories configuration for the current tenant.
+ Returns a list of category dicts with 'id', 'label', and 'courses' keys,
+ sorted according to the tenant's sorting preference.
+ """
+ config = get_course_categories_config()
+ categories = config.get('categories', {})
+ sorting = config.get('sorting', [])
+
+ # Build ordered list of categories
+ ordered_categories = []
+ for cat_id in sorting:
+ if cat_id in categories:
+ cat_data = categories[cat_id]
+ ordered_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ # Add any categories not in sorting list
+ for cat_id, cat_data in categories.items():
+ if cat_id not in sorting:
+ ordered_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ return ordered_categories
+%>%def>
+
+
+<%def name="get_fx_courses_by_category(all_courses, category_courses)" buffered="True"><%
+ """
+ Filter courses by category course IDs.
+ Returns a list of courses that match the category's course_ids.
+ """
+ if not category_courses:
+ return []
+
+ # Convert category_courses to set for faster lookup
+ category_course_set = set(category_courses)
+
+ # Filter courses that are in the category
+ filtered = []
+ for course in all_courses:
+ course_id = str(course.id)
+ if course_id in category_course_set:
+ filtered.append(course)
+
+ return filtered
+%>%def>
+
+
% for columns_section in columns_sections:
diff --git a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_static_content.html b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_static_content.html
index a43dd5f1b..1010cb16e 100644
--- a/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_static_content.html
+++ b/tutorindigo/templates/indigo/lms/templates/fx_templates/fx_static_content.html
@@ -4,6 +4,57 @@
from django.utils.translation import get_language
from django.conf import settings
from lms.djangoapps.branding import api as branding_api
+
+
+def get_course_categories_config():
+ """
+ Get course categories from tenant configuration.
+ Returns a dict with 'categories' and 'sorting' keys.
+ """
+ config_key = getattr(settings, 'FX_COURSE_CATEGORY_CONFIG_KEY', 'course_categories')
+ config_value = get_config_current_request([config_key])
+ if not config_value:
+ return {'categories': {}, 'sorting': []}
+ result = config_value['values'].get(config_key, {})
+ if not result or not isinstance(result, dict):
+ return {'categories': {}, 'sorting': []}
+ return {
+ 'categories': result.get('categories', {}),
+ 'sorting': result.get('sorting', []),
+ }
+
+
+def get_course_categories_list():
+ """
+ Get course categories configuration for the current tenant.
+ Returns a list of category dicts with 'id', 'label', and 'courses' keys,
+ sorted according to the tenant's sorting preference.
+ """
+ config = get_course_categories_config()
+ categories = config.get('categories', {})
+ sorting = config.get('sorting', [])
+
+ # Build ordered list of categories
+ ordered_categories = []
+ for cat_id in sorting:
+ if cat_id in categories:
+ cat_data = categories[cat_id]
+ ordered_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ # Add any categories not in sorting list
+ for cat_id, cat_data in categories.items():
+ if cat_id not in sorting:
+ ordered_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ return ordered_categories
%>
<%def name="br(text)" buffered="True"><%
@@ -144,3 +195,60 @@
import string
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
%>%def>
+
+
+<%def name="get_fx_course_categories()" buffered="True"><%
+ """
+ Get course categories configuration for the current tenant.
+ Returns a list of category dicts with 'id', 'label', and 'courses' keys,
+ sorted according to the tenant's sorting preference.
+ """
+ config = get_course_categories_config()
+ categories = config.get('categories', {})
+ sorting = config.get('sorting', [])
+
+ # Build ordered list of categories
+ ordered_categories = []
+ for cat_id in sorting:
+ if cat_id in categories:
+ cat_data = categories[cat_id]
+ ordered_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ # Add any categories not in sorting list
+ for cat_id, cat_data in categories.items():
+ if cat_id not in sorting:
+ ordered_categories.append({
+ 'id': cat_id,
+ 'label': cat_data.get('label', {}),
+ 'courses': cat_data.get('courses', []),
+ })
+
+ return ordered_categories
+%>%def>
+
+
+<%def name="get_fx_courses_by_category(all_courses, category_courses)" buffered="True"><%
+ """
+ Filter courses by category course IDs.
+ Returns a list of courses that match the category's course_ids.
+ """
+ if not category_courses:
+ return []
+
+ # Convert category_courses to set for faster lookup
+ category_course_set = set(category_courses)
+
+ # Filter courses that are in the category
+ filtered = []
+ for course in all_courses:
+ course_id = str(course.id)
+ if course_id in category_course_set:
+ filtered.append(course)
+
+ return filtered
+%>%def>
+