import json from django import forms from django.utils.html import escape from django.utils.safestring import mark_safe from apps.account.models.role import Role, PERMISSION_CATEGORIES, ALL_PERMISSION_FIELDS from apps.account.admin.access_modal_widget import UserAccessManagementWidget CATEGORY_ICONS = { "Identity & Profiles": "badge", "Chat & Communications": "forum", "CMS & Articles": "article", "LMS & Academics": "school", "Meetings": "video_camera_front", "Events": "event", "Community Projects": "task_alt", "Donations & Charity": "volunteer_activism", "Support & Tickets": "confirmation_number", "Dynamic Forms": "dynamic_form", "Diplomacy & Institutional": "public", "Administration & Security": "admin_panel_settings", } class GroupPillSelectMultipleWidget(forms.CheckboxSelectMultiple): """ Renders Django auth Groups as modern, interactive clickable pill cards. Replaces the cumbersome dual-listbox with instant one-click toggle chips. """ def render(self, name, value, attrs=None, renderer=None): if value is None: value = [] elif isinstance(value, str): value = [value] else: value = [str(v) for v in value] attrs = attrs or {} output = [ '
', '
' ] has_choices = False for option_value, option_label in self.choices: if not option_value: continue has_choices = True is_checked = str(option_value) in value checked_attr = 'checked' if is_checked else '' card_class = ( 'group-pill-card cursor-pointer select-none inline-flex items-center gap-2 px-3.5 py-2 rounded-lg border text-sm transition-all duration-150 ' + ('bg-indigo-50 border-indigo-400 text-indigo-700 shadow-xs dark:bg-indigo-950/60 dark:border-indigo-600 dark:text-indigo-300 font-semibold' if is_checked else 'bg-white border-slate-200 text-slate-700 hover:bg-slate-50 dark:bg-slate-900 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800') ) icon_name = 'check_circle' if is_checked else 'add_circle' icon_class = 'material-symbols-outlined text-base pill-icon ' + ('text-indigo-600 dark:text-indigo-400' if is_checked else 'text-slate-400') output.append( f'' ) if not has_choices: output.append('No groups available in system.') output.append('''
Click any group chip to instantly assign or revoke membership for this user.
''') return mark_safe('\n'.join(output)) class RegionPillSelectMultipleWidget(forms.CheckboxSelectMultiple): """ Renders Regions as modern, interactive clickable pill cards with map/public icons. Allows easy point-and-click assignment of managed regions to administrators. """ def render(self, name, value, attrs=None, renderer=None): if value is None: value = [] elif isinstance(value, str): value = [value] else: value = [str(v.pk if hasattr(v, 'pk') else v) for v in value] attrs = attrs or {} output = [ '
', '
' ] has_choices = False for option_value, option_label in self.choices: if not option_value: continue has_choices = True is_checked = str(option_value) in value checked_attr = 'checked' if is_checked else '' card_class = ( 'region-pill-card cursor-pointer select-none inline-flex items-center gap-2 px-3.5 py-2 rounded-lg border text-sm transition-all duration-150 ' + ('bg-emerald-50 border-emerald-400 text-emerald-700 shadow-xs dark:bg-emerald-950/60 dark:border-emerald-600 dark:text-emerald-300 font-semibold' if is_checked else 'bg-white border-slate-200 text-slate-700 hover:bg-slate-50 dark:bg-slate-900 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800') ) icon_name = 'check_circle' if is_checked else 'public' icon_class = 'material-symbols-outlined text-base pill-icon ' + ('text-emerald-600 dark:text-emerald-400' if is_checked else 'text-slate-400') output.append( f'' ) if not has_choices: output.append('No regions available in system.') output.append('''
Click any region chip to assign or unassign geographic jurisdiction for this regional admin.
''') return mark_safe('\n'.join(output)) class RolePermissionsMatrixWidget(forms.Widget): """ Renders an interactive, categorized visual permissions matrix. Allows point-and-click permission overriding (Inherit / Allow / Deny) with real-time inheritance tracking from the user's Assigned Role. """ def render(self, name, value, attrs=None, renderer=None): if isinstance(value, str): try: overrides = json.loads(value) if value.strip() else {} except Exception: overrides = {} elif isinstance(value, dict): overrides = value else: overrides = {} # Fetch all roles and their permissions dictionary roles_data = {} for r in Role.objects.all(): roles_data[str(r.id)] = r.get_permissions_dict() categories_data = [] for cat_label, field_names in PERMISSION_CATEGORIES.items(): cat_name_str = str(cat_label) icon = CATEGORY_ICONS.get(cat_name_str, "admin_panel_settings") perms_list = [] for field_name in field_names: try: f = Role._meta.get_field(field_name) vname = str(f.verbose_name) except Exception: vname = field_name.replace("_", " ").title() perms_list.append({ "name": field_name, "label": vname, }) categories_data.append({ "title": cat_name_str, "icon": icon, "perms": perms_list, }) roles_json_str = json.dumps(roles_data) overrides_json_str = json.dumps(overrides) html_parts = [ '
', f'', f'', f'', '''

tune Application Permissions Matrix

Permissions are automatically inherited from the user's Assigned Role. You can selectively override any permission below.

search
''' ] for cat in categories_data: cat_title = cat["title"] cat_icon = cat["icon"] cat_perms = cat["perms"] html_parts.append(f'''
{cat_icon} {escape(cat_title)} {len(cat_perms)} permissions
expand_more
''') for p in cat_perms: p_name = p["name"] p_label = p["label"] html_parts.append(f'''
{escape(p_label)} Checking role...
''') html_parts.append('''
''') html_parts.append(f'''
''') return mark_safe('\n'.join(html_parts))