feat: add modules management view with DevExtreme data grid and level permissions interface
This commit is contained in:
@@ -56,167 +56,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="block-content tab-content overflow-hidden">
|
<div class="block-content tab-content overflow-hidden">
|
||||||
<!-- Modules Tab -->
|
|
||||||
<div class="tab-pane active" id="btabs-modules" role="tabpanel">
|
<div class="tab-pane active" id="btabs-modules" role="tabpanel">
|
||||||
<div class="row mb-4">
|
<div id="types-grid"></div>
|
||||||
<div class="col-12">
|
</div>
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<span class="input-group-text bg-white border-right-0">
|
|
||||||
<i class="si si-magnifier"></i>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<input type="text" name="search" id="search" placeholder="{{e2("Search Module...")}}"
|
|
||||||
class="form-control border-left-0 pl-0 bg-white">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-hover table-vcenter table-sm custom-table">
|
|
||||||
<thead class="thead-light">
|
|
||||||
<tr>
|
|
||||||
<th class="text-center" style="width: 70px;">{{__("Status")}}</th>
|
|
||||||
<th class="text-center" style="width: 70px;">{{__("Mobile")}}</th>
|
|
||||||
<th class="text-center" style="width: 50px;">{{__("Icon")}}</th>
|
|
||||||
<th>{{__("Module Info")}}</th>
|
|
||||||
<th>{{__("Slug / Parent")}}</th>
|
|
||||||
<th class="text-center" style="width: 80px;">{{__("Order")}}</th>
|
|
||||||
<th>{{__("Permissions")}}</th>
|
|
||||||
<th class="text-center" style="width: 120px;"><i class="fa fa-cog"></i></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="types">
|
|
||||||
@foreach($types as $a)
|
|
||||||
<tr>
|
|
||||||
<td class="text-center">
|
|
||||||
<select name="enabled" table="types" data-id="{{$a->id}}"
|
|
||||||
class="form-control form-control-sm edit custom-select-status">
|
|
||||||
<option value="1" @if($a->enabled) selected @endif>✅ Active</option>
|
|
||||||
<option value="0" @if(!$a->enabled) selected @endif>🟥 Passive</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<select name="is_mobile" table="types" data-id="{{$a->id}}"
|
|
||||||
class="form-control form-control-sm edit custom-select-status">
|
|
||||||
<option value="1" @if($a->is_mobile) selected @endif>✅ Yes</option>
|
|
||||||
<option value="0" @if(!$a->is_mobile) selected @endif>🟥 No</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<div class="icon-wrapper bg-light p-1 rounded">
|
|
||||||
<img src="{{url("assets/icons/" . $a->icon . ".png")}}" width="24" alt=""
|
|
||||||
class="img-fluid">
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="d-flex flex-column">
|
|
||||||
<input type="text" name="title" value="{{$a->title}}" table="types"
|
|
||||||
data-id="{{$a->id}}"
|
|
||||||
class="form-control form-control-sm edit mb-1 font-w600 title{{$a->id}}"
|
|
||||||
placeholder="Title" />
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<span
|
|
||||||
class="input-group-text border-right-0 bg-transparent text-muted"><i
|
|
||||||
class="fa fa-image"></i></span>
|
|
||||||
</div>
|
|
||||||
<input type="text" name="icon" value="{{$a->icon}}" table="types"
|
|
||||||
data-id="{{$a->id}}"
|
|
||||||
class="form-control form-control-sm edit border-left-0"
|
|
||||||
placeholder="Icon name" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="d-flex flex-column">
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<button class="btn btn-alt-primary"
|
|
||||||
onclick="$.get('{{url('admin-ajax/slug?title=')}}'+$('.title{{$a->id}}').val(),function(d){ $('.slug{{$a->id}}').val(d).blur(); })">
|
|
||||||
<i class="si si-refresh"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<input type="text" name="slug" value="{{$a->slug}}" table="types"
|
|
||||||
data-id="{{$a->id}}" class="slug{{$a->id}} form-control edit"
|
|
||||||
placeholder="Slug" />
|
|
||||||
</div>
|
|
||||||
<input type="text" name="kid" value="{{$a->kid}}" table="types"
|
|
||||||
data-id="{{$a->id}}" class="form-control form-control-sm edit"
|
|
||||||
placeholder="Parent ID" />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<input type="number" name="s" value="{{$a->s}}" table="types" data-id="{{$a->id}}"
|
|
||||||
class="form-control form-control-sm edit text-center" />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="permissions-container d-flex flex-wrap gap-2">
|
|
||||||
@php
|
|
||||||
$permTypes = [
|
|
||||||
['key' => 'full_control', 'label' => 'Full', 'icon' => 'fa-lock'],
|
|
||||||
['key' => 'write', 'label' => 'Write', 'icon' => 'fa-pencil-alt'],
|
|
||||||
['key' => 'read', 'label' => 'Read', 'icon' => 'fa-eye'],
|
|
||||||
['key' => 'modify', 'label' => 'Mod', 'icon' => 'fa-edit'],
|
|
||||||
];
|
|
||||||
@endphp
|
|
||||||
@foreach($permTypes as $pt)
|
|
||||||
<div class="perm-item mb-2 mr-2">
|
|
||||||
<small class="text-muted d-block mb-1 font-w600"><i
|
|
||||||
class="fa {{ $pt['icon'] }} mr-1"></i>{{ $pt['label'] }}</small>
|
|
||||||
<div class="level-input mini">
|
|
||||||
<div class="level-tags">
|
|
||||||
@foreach(explode(',', $a->{$pt['key']}) as $level)
|
|
||||||
@if($level !== '')
|
|
||||||
<span
|
|
||||||
class="badge badge-pill badge-{{ levelColor(trim($level)) }}">{{ trim($level) }}</span>
|
|
||||||
@endif
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-xs btn-alt-primary edit-levels"
|
|
||||||
data-target="{{ $pt['key'] }}_{{ $a->id }}"
|
|
||||||
data-type="{{ $pt['key'] }}">
|
|
||||||
<i class="fa fa-cog"></i>
|
|
||||||
</button>
|
|
||||||
<input type="hidden" name="{{ $pt['key'] }}" table="types"
|
|
||||||
data-id="{{ $a->id }}" value="{{ $a->{$pt['key']} }}" class="edit"
|
|
||||||
id="{{ $pt['key'] }}_{{ $a->id }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<div class="btn-group">
|
|
||||||
<a href="{{ url('admin/types/' . $a->slug) }}" class="btn btn-sm btn-alt-primary"
|
|
||||||
title="Go to Link">
|
|
||||||
<i class="fa fa-link"></i>
|
|
||||||
</a>
|
|
||||||
<button type="button" class="btn btn-sm btn-alt-info open-specific-permissions"
|
|
||||||
data-id="{{ $a->id }}" data-title="{{ $a->title }}"
|
|
||||||
data-slug="{{ $a->slug }}"
|
|
||||||
data-permissions='{{ setting("type_{$a->id}_specific_permissions") }}'
|
|
||||||
data-mobile-columns='{{ setting("type_{$a->id}_mobile_columns") }}'
|
|
||||||
title="Specific Permissions">
|
|
||||||
<i class="fa fa-cogs"></i>
|
|
||||||
</button>
|
|
||||||
<a href="{{ url('admin/types/' . $a->id . '/delete') }}"
|
|
||||||
class="btn btn-sm btn-alt-danger delete-confirm" title="Delete">
|
|
||||||
<i class="fa fa-times"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td style="display: none;" class="module-data">
|
|
||||||
<?php echo json_encode_tr([
|
|
||||||
'icon' => $a->icon,
|
|
||||||
'title' => $a->title
|
|
||||||
]); ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Levels Tab -->
|
<!-- Levels Tab -->
|
||||||
@@ -268,13 +110,289 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(function () {
|
||||||
$("#search").on("keyup", function () {
|
const levelsData = @json(levels());
|
||||||
var value = $(this).val().toLowerCase();
|
const idToName = {};
|
||||||
$("#types tr").filter(function () {
|
Object.entries(levelsData).forEach(([name, data]) => {
|
||||||
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
|
const id = data[0].toString();
|
||||||
});
|
if (!idToName[id]) idToName[id] = [];
|
||||||
|
idToName[id].push(name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const typesData = [
|
||||||
|
@foreach($types as $a)
|
||||||
|
{
|
||||||
|
id: {{ $a->id }},
|
||||||
|
enabled: {{ $a->enabled ? 1 : 0 }},
|
||||||
|
is_mobile: {{ $a->is_mobile ? 1 : 0 }},
|
||||||
|
icon: "{{ $a->icon }}",
|
||||||
|
title: "{{ $a->title }}",
|
||||||
|
slug: "{{ $a->slug }}",
|
||||||
|
kid: "{{ $a->kid }}",
|
||||||
|
s: {{ (int)$a->s }},
|
||||||
|
full_control: "{{ $a->full_control }}",
|
||||||
|
write: "{{ $a->write }}",
|
||||||
|
read: "{{ $a->read }}",
|
||||||
|
modify: "{{ $a->modify }}",
|
||||||
|
specific_permissions: '{!! addslashes(setting("type_{$a->id}_specific_permissions")) !!}',
|
||||||
|
mobile_columns: '{!! addslashes(setting("type_{$a->id}_mobile_columns")) !!}'
|
||||||
|
},
|
||||||
|
@endforeach
|
||||||
|
];
|
||||||
|
|
||||||
|
// Helper to open level modal directly
|
||||||
|
window.openLevelEdit = function(id, type, currentVal, title, icon) {
|
||||||
|
currentTarget = `target_${type}_${id}`;
|
||||||
|
currentType = type;
|
||||||
|
|
||||||
|
// Ensure target input exists
|
||||||
|
if ($(`#${currentTarget}`).length === 0) {
|
||||||
|
$("<input>").attr("type", "hidden").attr("id", currentTarget).val(currentVal).appendTo("body");
|
||||||
|
|
||||||
|
// Listen for changes
|
||||||
|
$(`#${currentTarget}`).on('change', function() {
|
||||||
|
const newVal = $(this).val();
|
||||||
|
const rowIndex = grid.dxDataGrid("instance").getRowIndexByKey(id);
|
||||||
|
if (rowIndex > -1) {
|
||||||
|
grid.dxDataGrid("instance").cellValue(rowIndex, type, newVal);
|
||||||
|
grid.dxDataGrid("instance").saveEditData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$(`#${currentTarget}`).val(currentVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup modal data
|
||||||
|
$('.modal-icon').attr('src', `{{url("assets/icons")}}/${icon}.png`);
|
||||||
|
$('.modal-module-title').text(title);
|
||||||
|
$('.modal-permission-title').text(getPermissionTitle(type));
|
||||||
|
|
||||||
|
// Check checkboxes
|
||||||
|
const currentLevels = currentVal ? currentVal.toString().split(',').filter(l => l) : [];
|
||||||
|
$('.level-checkbox').prop('checked', false);
|
||||||
|
currentLevels.forEach(level => {
|
||||||
|
$('#level_' + parseInt(level)).prop('checked', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#levelModal').modal('show');
|
||||||
|
};
|
||||||
|
|
||||||
|
const grid = $("#types-grid").dxDataGrid({
|
||||||
|
dataSource: typesData,
|
||||||
|
keyExpr: "id",
|
||||||
|
showBorders: true,
|
||||||
|
columnAutoWidth: true,
|
||||||
|
allowColumnResizing: true,
|
||||||
|
filterRow: { visible: true },
|
||||||
|
headerFilter: { visible: true },
|
||||||
|
searchPanel: { visible: true, width: 240, placeholder: "{{e2('Search Module...')}}" },
|
||||||
|
editing: {
|
||||||
|
mode: "cell",
|
||||||
|
allowUpdating: true,
|
||||||
|
startEditAction: "click"
|
||||||
|
},
|
||||||
|
onRowUpdating: function(e) {
|
||||||
|
const id = e.key;
|
||||||
|
const values = e.newData;
|
||||||
|
Object.entries(values).forEach(([name, value]) => {
|
||||||
|
$.post('{{url("admin-ajax/input-edit")}}', {
|
||||||
|
table: "types",
|
||||||
|
value: value,
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
_token: '{{ csrf_token() }}'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
dataField: "enabled",
|
||||||
|
caption: "{{__('Status')}}",
|
||||||
|
width: 100,
|
||||||
|
alignment: "center",
|
||||||
|
lookup: {
|
||||||
|
dataSource: [
|
||||||
|
{ id: 1, text: "✅ Active" },
|
||||||
|
{ id: 0, text: "🟥 Passive" }
|
||||||
|
],
|
||||||
|
valueExpr: "id",
|
||||||
|
displayExpr: "text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataField: "is_mobile",
|
||||||
|
caption: "{{__('Mobile')}}",
|
||||||
|
width: 80,
|
||||||
|
alignment: "center",
|
||||||
|
lookup: {
|
||||||
|
dataSource: [
|
||||||
|
{ id: 1, text: "✅ Yes" },
|
||||||
|
{ id: 0, text: "🟥 No" }
|
||||||
|
],
|
||||||
|
valueExpr: "id",
|
||||||
|
displayExpr: "text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataField: "icon",
|
||||||
|
caption: "{{__('Icon')}}",
|
||||||
|
width: 60,
|
||||||
|
alignment: "center",
|
||||||
|
allowEditing: true,
|
||||||
|
cellTemplate: function(container, options) {
|
||||||
|
const icon = options.value || "default";
|
||||||
|
$("<div>")
|
||||||
|
.addClass("icon-wrapper bg-light p-1 rounded")
|
||||||
|
.append($("<img>").attr("src", `{{url('assets/icons')}}/${icon}.png`).attr("width", 24))
|
||||||
|
.appendTo(container);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataField: "title",
|
||||||
|
caption: "{{__('Title')}}",
|
||||||
|
cssClass: "font-w600"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataField: "slug",
|
||||||
|
caption: "{{__('Slug')}}",
|
||||||
|
cellTemplate: function(container, options) {
|
||||||
|
const $group = $("<div>").addClass("input-group input-group-sm");
|
||||||
|
const $btn = $("<button>")
|
||||||
|
.addClass("btn btn-alt-primary")
|
||||||
|
.append($("<i>").addClass("si si-refresh"))
|
||||||
|
.on("click", function() {
|
||||||
|
$.get('{{url("admin-ajax/slug?title=")}}' + options.data.title, function(d) {
|
||||||
|
grid.dxDataGrid("instance").cellValue(options.rowIndex, "slug", d);
|
||||||
|
grid.dxDataGrid("instance").saveEditData();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("<div>").addClass("input-group-prepend").append($btn).appendTo($group);
|
||||||
|
$("<input>")
|
||||||
|
.addClass("form-control form-control-sm")
|
||||||
|
.val(options.value)
|
||||||
|
.on("change", function() {
|
||||||
|
grid.dxDataGrid("instance").cellValue(options.rowIndex, "slug", $(this).val());
|
||||||
|
grid.dxDataGrid("instance").saveEditData();
|
||||||
|
})
|
||||||
|
.appendTo($group);
|
||||||
|
$group.appendTo(container);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataField: "kid",
|
||||||
|
caption: "{{__('Parent')}}",
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataField: "s",
|
||||||
|
caption: "{{__('Order')}}",
|
||||||
|
width: 60,
|
||||||
|
alignment: "center"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
caption: "{{__('Permissions')}}",
|
||||||
|
columns: [
|
||||||
|
{ dataField: "full_control", caption: "Full", cellTemplate: renderPermissionCell },
|
||||||
|
{ dataField: "write", caption: "Write", cellTemplate: renderPermissionCell },
|
||||||
|
{ dataField: "read", caption: "Read", cellTemplate: renderPermissionCell },
|
||||||
|
{ dataField: "modify", caption: "Mod", cellTemplate: renderPermissionCell }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "buttons",
|
||||||
|
width: 150,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
icon: "link",
|
||||||
|
hint: "Go to Link",
|
||||||
|
onClick: function(e) {
|
||||||
|
window.open("{{ url('admin/types') }}/" + e.row.data.slug, "_blank");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "key",
|
||||||
|
hint: "General Permissions",
|
||||||
|
onClick: function(e) {
|
||||||
|
const data = e.row.data;
|
||||||
|
// Default to Full Control modal as a starting point, or we could add a multi-edit modal
|
||||||
|
window.openLevelEdit(data.id, 'full_control', data.full_control, data.title, data.icon);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "preferences",
|
||||||
|
hint: "Specific Permissions",
|
||||||
|
onClick: function(e) {
|
||||||
|
const data = e.row.data;
|
||||||
|
const $btn = $("<button>")
|
||||||
|
.data('id', data.id)
|
||||||
|
.data('title', data.title)
|
||||||
|
.data('slug', data.slug)
|
||||||
|
.data('permissions', data.specific_permissions)
|
||||||
|
.data('mobile-columns', data.mobile_columns)
|
||||||
|
.addClass('open-specific-permissions')
|
||||||
|
.hide()
|
||||||
|
.appendTo('body');
|
||||||
|
$btn.trigger('click');
|
||||||
|
$btn.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "trash",
|
||||||
|
hint: "Delete",
|
||||||
|
onClick: function(e) {
|
||||||
|
Swal.fire({
|
||||||
|
title: '{{__("Are you sure?")}}',
|
||||||
|
text: '{{__("This module and all its settings will be deleted!")}}',
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#d33',
|
||||||
|
cancelButtonColor: '#3085d6',
|
||||||
|
confirmButtonText: '{{__("Yes, delete it!")}}',
|
||||||
|
cancelButtonText: '{{__("Cancel")}}'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
window.location.href = "{{ url('admin/types') }}/" + e.row.data.id + "/delete";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderPermissionCell(container, options) {
|
||||||
|
const val = options.value || "";
|
||||||
|
const ids = val.split(',').filter(x => x);
|
||||||
|
const $wrapper = $("<div>").addClass("d-flex flex-wrap gap-1 align-items-center");
|
||||||
|
|
||||||
|
if (ids.length === 0) {
|
||||||
|
$wrapper.append($("<small>").addClass("text-muted").text("-"));
|
||||||
|
} else {
|
||||||
|
ids.forEach(id => {
|
||||||
|
const name = idToName[id] ? idToName[id][0] : id;
|
||||||
|
const color = levelColor(id);
|
||||||
|
$("<span>")
|
||||||
|
.addClass(`badge badge-pill badge-${color}`)
|
||||||
|
.css({ "cursor": "help", "font-size": "11px" })
|
||||||
|
.attr("title", name)
|
||||||
|
.text(id) // Show ID (Number)
|
||||||
|
.appendTo($wrapper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$("<button>")
|
||||||
|
.addClass("btn btn-xs btn-alt-danger ml-auto")
|
||||||
|
.css({ "border-radius": "50%", "width": "24px", "height": "24px", "padding": "0" })
|
||||||
|
.on("click", function() {
|
||||||
|
window.openLevelEdit(options.data.id, options.column.dataField, val, options.data.title, options.data.icon);
|
||||||
|
})
|
||||||
|
.append($("<i>").addClass("fa fa-cog"))
|
||||||
|
.appendTo($wrapper);
|
||||||
|
|
||||||
|
$wrapper.appendTo(container);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -416,6 +534,14 @@
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: '{{__("Saved")}}',
|
||||||
|
text: '{{__("Permissions updated successfully")}}',
|
||||||
|
timer: 1500,
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
|
||||||
$('#levelModal').modal('hide');
|
$('#levelModal').modal('hide');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -857,18 +983,34 @@
|
|||||||
try {
|
try {
|
||||||
var res = typeof response === 'string' ? JSON.parse(response) : response;
|
var res = typeof response === 'string' ? JSON.parse(response) : response;
|
||||||
if (res.status === 'success') {
|
if (res.status === 'success') {
|
||||||
$('#specificPermissionsModal').modal('hide');
|
Swal.fire({
|
||||||
location.reload();
|
icon: 'success',
|
||||||
|
title: '{{__("Saved")}}',
|
||||||
|
text: '{{__("Specific permissions updated successfully")}}',
|
||||||
|
timer: 1500,
|
||||||
|
showConfirmButton: false
|
||||||
|
}).then(() => {
|
||||||
|
$('#specificPermissionsModal').modal('hide');
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
alert('Error: ' + (res.message || 'Unknown error'));
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: '{{e2("Error")}}',
|
||||||
|
text: res.message || 'Unknown error'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Response parse error", e);
|
console.error("Response parse error", e);
|
||||||
location.reload(); // Assume success if parse fails but request worked
|
location.reload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
alert('An error occurred while saving.');
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: '{{e2("Error")}}',
|
||||||
|
text: 'An error occurred while saving.'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user