22 <div
33 x-data =" menuItemsManager({
44 maxDepth: {{ $this -> getMaxDepth () } } ,
5- menuId: {{ $this -> record -> id } } ,
6- items: @js ($this -> getMenuItems () )
5+ menuId: {{ $this -> record -> id } }
76 })"
87 x-init =" init()"
98 class =" space-y-6"
109 >
1110 <!-- Tree Container -->
1211 <x-filament::section >
1312 <div class =" min-h-[400px]" >
14- <!-- Empty State -->
15- <div x-show =" items.length === 0" class =" text-center py-12" >
16- <svg class =" mx-auto h-12 w-12 text-gray-400" fill =" none" viewBox =" 0 0 24 24" stroke =" currentColor" >
17- <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M4 6h16M4 10h16M4 14h16M4 18h16" />
18- </svg >
19- <h3 class =" mt-2 text-sm font-medium text-gray-900 dark:text-white" >
20- {{ flexiblePagesTrans (' menu_items.tree.empty_state' ) } }
21- </h3 >
22- <p class =" mt-1 text-sm text-gray-500 dark:text-gray-400" >
23- {{ flexiblePagesTrans (' menu_items.manage.empty_description' ) } }
24- </p >
25- </div >
26-
27- <!-- Tree Items -->
28- <div x-show =" items.length > 0" class =" space-y-2" id =" menu-items-container" >
29- <template x-for =" (item, index) in items" :key =" item.id" >
30- <div class =" menu-tree-item" x-bind:data-item-id =" item.id" >
31- <div x-html =" renderTreeItem(item, 0)" ></div >
32- </div >
33- </template >
34- </div >
13+ @if (count ($this -> getMenuItems ()) === 0 )
14+ <!-- Empty State -->
15+ <div class =" text-center py-12" >
16+ <svg class =" mx-auto h-12 w-12 text-gray-400" fill =" none" viewBox =" 0 0 24 24" stroke =" currentColor" >
17+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M4 6h16M4 10h16M4 14h16M4 18h16" />
18+ </svg >
19+ <h3 class =" mt-2 text-sm font-medium text-gray-900 dark:text-white" >
20+ {{ flexiblePagesTrans (' menu_items.tree.empty_state' ) } }
21+ </h3 >
22+ <p class =" mt-1 text-sm text-gray-500 dark:text-gray-400" >
23+ {{ flexiblePagesTrans (' menu_items.manage.empty_description' ) } }
24+ </p >
25+ </div >
26+ @else
27+ <!-- Tree Items -->
28+ <div class =" space-y-2" id =" menu-items-container" >
29+ @foreach ($this -> getMenuItems () as $item )
30+ @livewire (' filament-flexible-content-block-pages::menu-tree-item' , [
31+ ' item' => $item ,
32+ ' maxDepth' => $this -> getMaxDepth ()
33+ ], key ($item [' id' ]) )
34+ @endforeach
35+ </div >
36+ @endif
3537 </div >
3638 </x-filament::section >
3739
@@ -54,7 +56,6 @@ class="space-y-6"
5456 <script >
5557 function menuItemsManager (config ) {
5658 return {
57- items: config .items || [],
5859 maxDepth: config .maxDepth || 2 ,
5960 menuId: config .menuId ,
6061 loading: false ,
@@ -73,8 +74,8 @@ function menuItemsManager(config) {
7374
7475 refreshMenuItems () {
7576 this .loading = true ;
76- this . $wire . call ( ' getMenuItems ' ). then (( items ) => {
77- this .items = items;
77+ // Refresh the page component - Livewire will handle the re-render
78+ this .$wire . $refresh (). finally (() => {
7879 this .loading = false ;
7980 // Re-initialize sortable after items are updated
8081 this .$nextTick (() => {
@@ -93,104 +94,14 @@ function menuItemsManager(config) {
9394 handle: ' .drag-handle' ,
9495 onEnd : (evt ) => {
9596 if (evt .oldIndex !== evt .newIndex ) {
96- this .reorderItems (evt .oldIndex , evt .newIndex );
97+ // TODO: Implement reordering with proper nested set handling
98+ console .log (' Reorder from' , evt .oldIndex , ' to' , evt .newIndex );
9799 }
98100 }
99101 });
100102 }
101103 }
102104 },
103-
104- renderTreeItem (item , depth ) {
105- const canHaveChildren = depth < this .maxDepth ;
106- const hasChildren = item .children && item .children .length > 0 ;
107- const indentClass = depth > 0 ? ` ml-${ depth * 8 } ` : ' ' ;
108-
109- return `
110- <div class="flex items-center p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 group hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors ${ indentClass} ">
111- <!-- Drag Handle -->
112- <div class="drag-handle cursor-move text-gray-400 hover:text-gray-600 mr-4 opacity-0 group-hover:opacity-100 transition-opacity">
113- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
114- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16"></path>
115- </svg>
116- </div>
117-
118- <!-- Item Content -->
119- <div class="flex-1 min-w-0">
120- <div class="flex items-center justify-between">
121- <div class="flex items-center space-x-3">
122- ${ item .icon ? ` <span class="text-gray-500 text-lg">${ item .icon } </span>` : ' ' }
123- <div>
124- <p class="text-sm font-medium text-gray-900 dark:text-white truncate">
125- ${ this .getItemDisplayLabel (item)}
126- </p>
127- <p class="text-xs text-gray-500 dark:text-gray-400 truncate">
128- ${ this .getItemTypeLabel (item)}
129- </p>
130- </div>
131- ${ ! item .is_visible ? ' <span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300">{{ flexiblePagesTrans (' menu_items.status.hidden' ) } } </span>' : ' ' }
132- </div>
133-
134- <!-- Actions -->
135- <div class="flex items-center space-x-2 opacity-0 group-hover:opacity-100 transition-opacity">
136- ${ canHaveChildren ? `
137- <button onclick="$wire.mountAction('addMenuItem', { parent_id: ${ item .id } })"
138- class="inline-flex items-center px-2 py-1 border border-gray-300 rounded text-xs font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">
139- {{ flexiblePagesTrans (' menu_items.tree.add_child' ) } }
140- </button>
141- ` : ' ' }
142- <button onclick="$wire.mountAction('editMenuItem', { itemId: ${ item .id } })"
143- class="inline-flex items-center px-2 py-1 border border-primary-300 rounded text-xs font-medium text-primary-700 bg-primary-50 hover:bg-primary-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">
144- {{ flexiblePagesTrans (' menu_items.tree.edit' ) } }
145- </button>
146- <button onclick="$wire.mountAction('deleteMenuItem', { itemId: ${ item .id } })"
147- class="inline-flex items-center px-2 py-1 border border-red-300 rounded text-xs font-medium text-red-700 bg-red-50 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
148- {{ flexiblePagesTrans (' menu_items.tree.delete' ) } }
149- </button>
150- </div>
151- </div>
152- </div>
153- </div>
154-
155- ${ hasChildren ? `
156- <div class="space-y-2 mt-2">
157- ${ item .children .map (child => this .renderTreeItem (child, depth + 1 )).join (' ' )}
158- </div>
159- ` : ' ' }
160- ` ;
161- },
162-
163- getItemDisplayLabel (item ) {
164- if (item .use_model_title && item .linkable && item .linkable .title ) {
165- return item .linkable .title ;
166- }
167- return item .label || ' {{ flexiblePagesTrans (' menu_items.status.no_label' ) } }' ;
168- },
169-
170- getItemTypeLabel (item ) {
171- if (item .linkable_type && item .linkable ) {
172- return ` {{ flexiblePagesTrans (' menu_items.tree.linked_to' ) } } ${ item .linkable_type } ` ;
173- }
174- if (item .url ) {
175- return ` {{ flexiblePagesTrans (' menu_items.tree.external_url' ) } } : ${ item .url } ` ;
176- }
177- return ' {{ flexiblePagesTrans (' menu_items.tree.no_link' ) } }' ;
178- },
179-
180- reorderItems (oldIndex , newIndex ) {
181- // Move item in array
182- const item = this .items .splice (oldIndex, 1 )[0 ];
183- this .items .splice (newIndex, 0 , item);
184-
185- // Send new order to server
186- const orderedIds = this .items .map (item => item .id );
187- this .loading = true ;
188- this .$wire .call (' reorderMenuItems' , orderedIds).then (() => {
189- this .loading = false ;
190- // Refresh items to get updated structure
191- location .reload ();
192- });
193- }
194105 }
195106 }
196107 </script >
0 commit comments