1515 * @since 2023-01-04
1616 */
1717
18- import { fetchAllGroups , fetchAllMembers , fetchAllUsersForGroups , fetchGroup , fetchGroupRights } from '../api' ;
18+ import {
19+ fetchAllGroups ,
20+ fetchAllMembers ,
21+ fetchAllUsersForGroups ,
22+ fetchCategoriesForRestrictions ,
23+ fetchGroup ,
24+ fetchGroupCategoryRestrictions ,
25+ fetchGroupRights ,
26+ saveGroupCategoryRestrictions ,
27+ } from '../api' ;
1928import { selectAll , unSelectAll } from '../utils' ;
20- import { Group , Member , User } from '../interfaces' ;
29+ import { CategoryItem , CategoryRestrictions , Group , Member , User } from '../interfaces' ;
2130
2231export const handleGroups = async ( ) : Promise < void > => {
2332 clearGroupList ( ) ;
@@ -74,6 +83,26 @@ export const handleGroups = async (): Promise<void> => {
7483 unSelectAllMembers . addEventListener ( 'click' , ( ) : void => {
7584 unSelectAll ( 'group_member_list' ) ;
7685 } ) ;
86+
87+ // Category restrictions save button
88+ const saveCategoryRestrictions = document . getElementById ( 'saveCategoryRestrictions' ) as HTMLButtonElement ;
89+ if ( saveCategoryRestrictions ) {
90+ saveCategoryRestrictions . addEventListener ( 'click' , async ( event : Event ) : Promise < void > => {
91+ event . preventDefault ( ) ;
92+ await handleCategoryRestrictionsSave ( ) ;
93+ } ) ;
94+ }
95+
96+ // Update category restrictions panel when rights checkboxes change
97+ document . getElementById ( 'groupRights' ) ?. addEventListener ( 'change' , ( event : Event ) : void => {
98+ const target = event . target as HTMLInputElement ;
99+ if ( target . type === 'checkbox' && target . classList . contains ( 'permission' ) ) {
100+ const container = document . getElementById ( 'categoryRestrictionsBody' ) ;
101+ if ( container ) {
102+ renderCategoryRestrictions ( container ) ;
103+ }
104+ }
105+ } ) ;
77106} ;
78107
79108const handleGroupSelect = async ( event : Event ) : Promise < void > => {
@@ -88,6 +117,7 @@ const handleGroupSelect = async (event: Event): Promise<void> => {
88117 await getUserList ( ) ;
89118 clearMemberList ( ) ;
90119 await getMemberList ( groupId ) ;
120+ await loadCategoryRestrictions ( groupId ) ;
91121
92122 // Activate user inputs
93123 const saveGroupDetails = document . getElementById ( 'saveGroupDetails' ) as HTMLButtonElement ;
@@ -96,13 +126,17 @@ const handleGroupSelect = async (event: Event): Promise<void> => {
96126 const deleteGroup = document . getElementById ( 'deleteGroup' ) as HTMLButtonElement ;
97127 const groupAddMember = document . getElementById ( 'groupAddMember' ) as HTMLButtonElement ;
98128 const groupRemoveMember = document . getElementById ( 'groupRemoveMember' ) as HTMLButtonElement ;
129+ const saveCategoryRestrictions = document . getElementById ( 'saveCategoryRestrictions' ) as HTMLButtonElement ;
99130
100131 saveGroupDetails . disabled = false ;
101132 saveMembersList . disabled = false ;
102133 saveGroupRights . disabled = false ;
103134 deleteGroup . disabled = false ;
104135 groupAddMember . disabled = false ;
105136 groupRemoveMember . disabled = false ;
137+ if ( saveCategoryRestrictions ) {
138+ saveCategoryRestrictions . disabled = false ;
139+ }
106140
107141 document . querySelectorAll < HTMLInputElement > ( '.permission' ) . forEach ( ( item : HTMLInputElement ) : void => {
108142 item . disabled = false ;
@@ -279,3 +313,101 @@ const removeGroupMembers = (): void => {
279313 }
280314 }
281315} ;
316+
317+ let cachedCategories : CategoryItem [ ] = [ ] ;
318+ let currentRestrictions : CategoryRestrictions = { } ;
319+
320+ const loadCategoryRestrictions = async ( groupId : string ) : Promise < void > => {
321+ const container = document . getElementById ( 'categoryRestrictionsBody' ) ;
322+ if ( ! container ) {
323+ return ;
324+ }
325+
326+ if ( cachedCategories . length === 0 ) {
327+ cachedCategories = await fetchCategoriesForRestrictions ( ) ;
328+ }
329+
330+ currentRestrictions = await fetchGroupCategoryRestrictions ( groupId ) ;
331+
332+ renderCategoryRestrictions ( container ) ;
333+ } ;
334+
335+ const renderCategoryRestrictions = ( container : HTMLElement ) : void => {
336+ const checkedRights = document . querySelectorAll < HTMLInputElement > ( '#groupRights input[type=checkbox]:checked' ) ;
337+
338+ container . innerHTML = '' ;
339+
340+ if ( checkedRights . length === 0 ) {
341+ container . innerHTML = '<p class="text-muted">No permissions assigned to this group.</p>' ;
342+ return ;
343+ }
344+
345+ checkedRights . forEach ( ( checkbox : HTMLInputElement ) : void => {
346+ const rightId = checkbox . value ;
347+ const label = checkbox . closest ( '.form-check' ) ?. querySelector ( 'label' ) ?. textContent ?. trim ( ) || `Right ${ rightId } ` ;
348+ const restrictedCategoryIds = currentRestrictions [ rightId ] || [ ] ;
349+
350+ const wrapper = document . createElement ( 'div' ) ;
351+ wrapper . className = 'mb-3' ;
352+
353+ const labelElement = document . createElement ( 'label' ) ;
354+ labelElement . className = 'form-label fw-semibold' ;
355+ labelElement . textContent = label ;
356+ wrapper . appendChild ( labelElement ) ;
357+
358+ const select = document . createElement ( 'select' ) ;
359+ select . className = 'form-select form-select-sm' ;
360+ select . multiple = true ;
361+ select . size = 4 ;
362+ select . dataset . rightId = rightId ;
363+
364+ cachedCategories . forEach ( ( cat : CategoryItem ) : void => {
365+ const option = document . createElement ( 'option' ) ;
366+ option . value = String ( cat . id ) ;
367+ option . textContent = cat . name ;
368+ option . selected = restrictedCategoryIds . includes ( cat . id ) ;
369+ select . appendChild ( option ) ;
370+ } ) ;
371+
372+ wrapper . appendChild ( select ) ;
373+
374+ const helpText = document . createElement ( 'div' ) ;
375+ helpText . className = 'form-text' ;
376+ helpText . textContent = 'Select categories to restrict this permission. Leave empty for unrestricted access.' ;
377+ wrapper . appendChild ( helpText ) ;
378+
379+ container . appendChild ( wrapper ) ;
380+ } ) ;
381+ } ;
382+
383+ export const handleCategoryRestrictionsSave = async ( ) : Promise < void > => {
384+ const groupListSelect = document . getElementById ( 'group_list_select' ) as HTMLSelectElement ;
385+ if ( ! groupListSelect ) {
386+ return ;
387+ }
388+
389+ const groupId = groupListSelect . value ;
390+ if ( ! groupId ) {
391+ return ;
392+ }
393+
394+ const container = document . getElementById ( 'categoryRestrictionsBody' ) ;
395+ if ( ! container ) {
396+ return ;
397+ }
398+
399+ const selects = container . querySelectorAll < HTMLSelectElement > ( 'select[data-right-id]' ) ;
400+
401+ for ( const select of selects ) {
402+ const rightId = select . dataset . rightId ;
403+ if ( ! rightId ) {
404+ continue ;
405+ }
406+
407+ const selectedCategoryIds = [ ...select . options ]
408+ . filter ( ( option : HTMLOptionElement ) : boolean => option . selected )
409+ . map ( ( option : HTMLOptionElement ) : number => parseInt ( option . value ) ) ;
410+
411+ await saveGroupCategoryRestrictions ( groupId , rightId , selectedCategoryIds ) ;
412+ }
413+ } ;
0 commit comments