@@ -2945,4 +2945,122 @@ public static function advancedPanelEnd(): string
29452945 </details>
29462946 ' ;
29472947 }
2948+
2949+ /**
2950+ * Render a consistent "Subscribe users to this session" header + tabs layout
2951+ * reused across legacy admin pages (add users, usergroups, etc).
2952+ *
2953+ * $activeTab: users|classes|teachers|students
2954+ * $options:
2955+ * - return_to: string (used to preserve the "Back" destination in the Classes tab)
2956+ * - header_title: string (defaults to get_lang('Subscribe users to this session'))
2957+ * - users_url/classes_url/teachers_url/students_url: override URLs if needed
2958+ * - wrapper_class: string
2959+ * - card_class: string
2960+ * - tabs_bar_class: string
2961+ * - content_class: string
2962+ */
2963+ public static function sessionSubscriptionPage (
2964+ int $ sessionId ,
2965+ string $ sessionTitle ,
2966+ string $ backUrl ,
2967+ string $ activeTab ,
2968+ string $ contentHtml ,
2969+ array $ options = []
2970+ ): string {
2971+ $ safeTitle = htmlspecialchars ($ sessionTitle , ENT_QUOTES , api_get_system_encoding ());
2972+
2973+ $ headerTitle = $ options ['header_title ' ] ?? get_lang ('Subscribe users to this session ' );
2974+
2975+ $ returnTo = (string ) ($ options ['return_to ' ] ?? '' );
2976+
2977+ $ usersUrl = $ options ['users_url ' ]
2978+ ?? api_get_path (WEB_CODE_PATH ).'session/add_users_to_session.php?id_session= ' .$ sessionId .'&add=true ' ;
2979+
2980+ $ classesUrl = $ options ['classes_url ' ]
2981+ ?? api_get_path (WEB_CODE_PATH ).'admin/usergroups.php?from_session= ' .$ sessionId
2982+ .(!empty ($ returnTo ) ? '&return_to= ' .rawurlencode ($ returnTo ) : '' );
2983+
2984+ $ teachersUrl = $ options ['teachers_url ' ]
2985+ ?? api_get_path (WEB_CODE_PATH ).'session/add_teachers_to_session.php?id= ' .$ sessionId ;
2986+
2987+ $ studentsUrl = $ options ['students_url ' ]
2988+ ?? api_get_path (WEB_CODE_PATH ).'session/add_students_to_session.php?id= ' .$ sessionId ;
2989+
2990+ $ wrapperClass = $ options ['wrapper_class ' ] ?? 'mx-auto w-full p-4 space-y-4 ' ;
2991+ $ cardClass = $ options ['card_class ' ] ?? 'rounded-lg border border-gray-30 bg-white shadow-sm ' ;
2992+ $ tabsBarClass = $ options ['tabs_bar_class ' ] ?? 'flex flex-wrap items-center gap-2 border-b border-gray-20 px-3 py-2 ' ;
2993+ $ contentClass = $ options ['content_class ' ] ?? 'p-4 ' ;
2994+
2995+ $ btnNeutral = 'inline-flex items-center gap-2 rounded-md border border-gray-30 bg-white px-3 py-1.5 text-sm font-medium text-gray-90 shadow-sm hover:bg-gray-10 ' ;
2996+
2997+ $ tabBase = 'inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm ' ;
2998+ $ tabActive = 'font-semibold bg-gray-10 text-gray-90 ' ;
2999+ $ tabIdle = 'font-medium text-gray-90 hover:bg-gray-10 ' ;
3000+
3001+ $ tabs = [
3002+ 'users ' => [
3003+ 'url ' => $ usersUrl ,
3004+ 'label ' => get_lang ('Users ' ),
3005+ 'icon ' => \Chamilo \CoreBundle \Enums \ObjectIcon::USER ,
3006+ ],
3007+ 'classes ' => [
3008+ 'url ' => $ classesUrl ,
3009+ 'label ' => get_lang ('Enrolment by classes ' ),
3010+ 'icon ' => \Chamilo \CoreBundle \Enums \ObjectIcon::MULTI_ELEMENT ,
3011+ ],
3012+ 'teachers ' => [
3013+ 'url ' => $ teachersUrl ,
3014+ 'label ' => get_lang ('Enroll trainers from existing sessions ' ),
3015+ 'icon ' => \Chamilo \CoreBundle \Enums \ObjectIcon::TEACHER ,
3016+ ],
3017+ 'students ' => [
3018+ 'url ' => $ studentsUrl ,
3019+ 'label ' => get_lang ('Enroll students from existing sessions ' ),
3020+ 'icon ' => \Chamilo \CoreBundle \Enums \ObjectIcon::USER ,
3021+ ],
3022+ ];
3023+
3024+ if (!isset ($ tabs [$ activeTab ])) {
3025+ $ activeTab = 'users ' ;
3026+ }
3027+
3028+ $ html = '' ;
3029+ $ html .= '<div class=" ' .$ wrapperClass .'"> ' ;
3030+
3031+ // Header card
3032+ $ html .= ' <div class=" ' .$ cardClass .' p-4"> ' ;
3033+ $ html .= ' <div class="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between"> ' ;
3034+ $ html .= ' <div class="min-w-0"> ' ;
3035+ $ html .= ' <h1 class="text-lg font-semibold text-gray-90"> ' .htmlspecialchars ($ headerTitle , ENT_QUOTES , api_get_system_encoding ()).'</h1> ' ;
3036+ $ html .= ' <p class="text-sm text-gray-50"> ' .$ safeTitle .'</p> ' ;
3037+ $ html .= ' </div> ' ;
3038+ $ html .= ' <div class="flex items-center gap-2"> ' ;
3039+ $ html .= ' <a href=" ' .htmlspecialchars ($ backUrl , ENT_QUOTES , api_get_system_encoding ()).'" class=" ' .$ btnNeutral .'"> ' .get_lang ('Back ' ).'</a> ' ;
3040+ $ html .= ' </div> ' ;
3041+ $ html .= ' </div> ' ;
3042+ $ html .= ' </div> ' ;
3043+
3044+ // Tabs + content in the SAME card (so it looks like real tabs)
3045+ $ html .= ' <div class=" ' .$ cardClass .'"> ' ;
3046+ $ html .= ' <div class=" ' .$ tabsBarClass .'"> ' ;
3047+
3048+ foreach ($ tabs as $ key => $ tab ) {
3049+ $ cls = $ tabBase .' ' .($ key === $ activeTab ? $ tabActive : $ tabIdle );
3050+ $ ariaCurrent = $ key === $ activeTab ? ' aria-current="page" ' : '' ;
3051+
3052+ $ html .= ' <a href=" ' .htmlspecialchars ($ tab ['url ' ], ENT_QUOTES , api_get_system_encoding ()).'" class=" ' .$ cls .'" ' .$ ariaCurrent .'> ' ;
3053+ $ html .= self ::getMdiIcon ($ tab ['icon ' ], 'ch-tool-icon ' , null , ICON_SIZE_SMALL , $ tab ['label ' ]);
3054+ $ html .= ' <span> ' .$ tab ['label ' ].'</span> ' ;
3055+ $ html .= ' </a> ' ;
3056+ }
3057+
3058+ $ html .= ' </div> ' ;
3059+ $ html .= ' <div class=" ' .$ contentClass .'"> ' .$ contentHtml .'</div> ' ;
3060+ $ html .= ' </div> ' ;
3061+
3062+ $ html .= '</div> ' ;
3063+
3064+ return $ html ;
3065+ }
29483066}
0 commit comments