2
2
<div
3
3
x-data =" menuItemsManager({
4
4
maxDepth: {{ $this -> getMaxDepth () } } ,
5
- menuId: {{ $this -> record -> id } } ,
6
- items: @js ($this -> getMenuItems () )
5
+ menuId: {{ $this -> record -> id } }
7
6
})"
8
7
x-init =" init()"
9
8
class =" space-y-6"
10
9
>
11
10
<!-- Tree Container -->
12
11
<x-filament::section >
13
12
<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
35
37
</div >
36
38
</x-filament::section >
37
39
@@ -54,7 +56,6 @@ class="space-y-6"
54
56
<script >
55
57
function menuItemsManager (config ) {
56
58
return {
57
- items: config .items || [],
58
59
maxDepth: config .maxDepth || 2 ,
59
60
menuId: config .menuId ,
60
61
loading: false ,
@@ -73,8 +74,8 @@ function menuItemsManager(config) {
73
74
74
75
refreshMenuItems () {
75
76
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 (() => {
78
79
this .loading = false ;
79
80
// Re-initialize sortable after items are updated
80
81
this .$nextTick (() => {
@@ -93,104 +94,14 @@ function menuItemsManager(config) {
93
94
handle: ' .drag-handle' ,
94
95
onEnd : (evt ) => {
95
96
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 );
97
99
}
98
100
}
99
101
});
100
102
}
101
103
}
102
104
},
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
- }
194
105
}
195
106
}
196
107
</script >
0 commit comments