update some recomended changes #8
@ -92,6 +92,47 @@ public function destroyApp($id)
|
||||
return redirect()->route('admin.dashboard')->with('success', 'Service app deleted successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an app / service.
|
||||
*/
|
||||
public function updateApp(Request $request, $id)
|
||||
{
|
||||
$app = App::findOrFail($id);
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'url' => 'required|url|max:255',
|
||||
'icon_svg' => 'nullable|string',
|
||||
'logo' => 'nullable|file|mimes:jpg,jpeg,png,webp,svg|max:2048',
|
||||
'color' => 'nullable|string|max:20',
|
||||
'desc' => 'nullable|string',
|
||||
'tag' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$logoPath = $app->logo;
|
||||
if ($request->hasFile('logo')) {
|
||||
if ($app->logo && file_exists(public_path($app->logo))) {
|
||||
@unlink(public_path($app->logo));
|
||||
}
|
||||
$file = $request->file('logo');
|
||||
$filename = time() . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
|
||||
$file->move(public_path('uploads/logos'), $filename);
|
||||
$logoPath = 'uploads/logos/' . $filename;
|
||||
}
|
||||
|
||||
$app->update([
|
||||
'name' => $request->name,
|
||||
'url' => $request->url,
|
||||
'icon_svg' => $request->icon_svg,
|
||||
'logo' => $logoPath,
|
||||
'color' => $request->color ?? '#4f46e5',
|
||||
'desc' => $request->desc,
|
||||
'tag' => $request->tag ?? 'General',
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.dashboard')->with('success', 'Service app updated successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new role.
|
||||
*/
|
||||
|
||||
@ -58,6 +58,24 @@ public function authorizedApps()
|
||||
->pluck('app_id')
|
||||
->toArray();
|
||||
|
||||
// If no roles are assigned, user gets default access to Outlook email and Drive apps if registered
|
||||
if (empty($roleIds)) {
|
||||
$defaultAppIds = App::where(function ($query) {
|
||||
$query->whereRaw('LOWER(name) LIKE ?', ['%outlook%'])
|
||||
->orWhereRaw('LOWER(name) LIKE ?', ['%mail%'])
|
||||
->orWhereRaw('LOWER(name) LIKE ?', ['%email%'])
|
||||
->orWhereRaw('LOWER(name) LIKE ?', ['%drive%'])
|
||||
->orWhereRaw('LOWER(name) LIKE ?', ['%onedrive%'])
|
||||
->orWhereRaw('LOWER(tag) LIKE ?', ['%outlook%'])
|
||||
->orWhereRaw('LOWER(tag) LIKE ?', ['%mail%'])
|
||||
->orWhereRaw('LOWER(tag) LIKE ?', ['%email%'])
|
||||
->orWhereRaw('LOWER(tag) LIKE ?', ['%drive%'])
|
||||
->orWhereRaw('LOWER(tag) LIKE ?', ['%onedrive%']);
|
||||
})->pluck('id')->toArray();
|
||||
|
||||
$roleAppIds = array_unique(array_merge($roleAppIds, $defaultAppIds));
|
||||
}
|
||||
|
||||
// Get user overrides
|
||||
$overrides = $this->overrides()->get();
|
||||
$allowedAppIds = $overrides->where('type', 'allow')->pluck('app_id')->toArray();
|
||||
|
||||
BIN
public/uploads/logos/1784030089_6a562389a4f9b.png
Normal file
BIN
public/uploads/logos/1784030089_6a562389a4f9b.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
@ -654,11 +654,22 @@
|
||||
</td>
|
||||
<td><span class="badge" style="background-color:rgba(255,255,255,0.05); color:#cbd5e1; border:1px solid rgba(255,255,255,0.1);">{{ $app->tag }}</span></td>
|
||||
<td style="text-align: right;">
|
||||
<form action="{{ route('admin.apps.destroy', $app->id) }}" method="POST" onsubmit="return confirm('Are you sure you want to delete this service?')" style="display:inline;">
|
||||
<div style="display: inline-flex; gap: 6px; justify-content: flex-end; align-items: center;">
|
||||
<button class="action-btn" onclick="openEditAppModal({{ $app->id }}, {{ json_encode($app->name) }}, {{ json_encode($app->url) }}, {{ json_encode($app->tag) }}, {{ json_encode($app->icon_svg) }}, {{ json_encode($app->color) }}, {{ json_encode($app->desc) }}, {{ json_encode($app->logo) }})" style="color:var(--info-color); border-color:rgba(6,182,212,0.2); padding:6px; display:flex; align-items:center; justify-content:center; border-radius:6px;" title="Edit Service">
|
||||
<svg viewBox="0 0 24 24" style="width: 14px; height: 14px; fill: currentColor;">
|
||||
<path d="M14.06,9L15,9.94L5.92,19H5V18.06L14.06,9M17.66,3C17.41,3 17.15,3.1 16.96,3.29L15.13,5.12L18.88,8.87L20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18.17,3.09 17.92,3 17.66,3M14.06,6.19L3,17.25V21H6.75L17.81,9.94L14.06,6.19Z" />
|
||||
</svg>
|
||||
</button>
|
||||
<form action="{{ route('admin.apps.destroy', $app->id) }}" method="POST" onsubmit="return confirm('Are you sure you want to delete this service?')" style="margin: 0; display: inline;">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="action-btn" style="color:#ef4444; border-color:rgba(239,68,68,0.2); font-size:0.75rem; padding:4px 8px;">Delete</button>
|
||||
<button type="submit" class="action-btn" style="color:#ef4444; border-color:rgba(239,68,68,0.2); padding:6px; display:flex; align-items:center; justify-content:center; border-radius:6px;" title="Delete Service">
|
||||
<svg viewBox="0 0 24 24" style="width: 14px; height: 14px; fill: currentColor;">
|
||||
<path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" />
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
@ -994,6 +1005,56 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit App Modal -->
|
||||
<div id="edit-app-modal" class="admin-modal">
|
||||
<div class="admin-modal-content" style="max-height: 90vh; overflow-y: auto;">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Edit Service App</h3>
|
||||
<button class="modal-close" onclick="closeModal('edit-app-modal')">×</button>
|
||||
</div>
|
||||
<form id="edit-app-form" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px;">
|
||||
<div>
|
||||
<label style="display:block; font-size:0.75rem; color:var(--text-muted); margin-bottom:6px; font-weight:500;">Service Name</label>
|
||||
<input type="text" id="edit-app-name" name="name" required class="form-input" style="width:100%; padding:10px; background:rgba(255,255,255,0.03); border:1px solid var(--card-border); border-radius:8px; color:white;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="display:block; font-size:0.75rem; color:var(--text-muted); margin-bottom:6px; font-weight:500;">Service URL</label>
|
||||
<input type="url" id="edit-app-url" name="url" required class="form-input" style="width:100%; padding:10px; background:rgba(255,255,255,0.03); border:1px solid var(--card-border); border-radius:8px; color:white;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px;">
|
||||
<div>
|
||||
<label style="display:block; font-size:0.75rem; color:var(--text-muted); margin-bottom:6px; font-weight:500;">Brand Accent Color (Hex)</label>
|
||||
<input type="text" id="edit-app-color" name="color" placeholder="#4f46e5" class="form-input" style="width:100%; padding:10px; background:rgba(255,255,255,0.03); border:1px solid var(--card-border); border-radius:8px; color:white;">
|
||||
</div>
|
||||
<div>
|
||||
<label style="display:block; font-size:0.75rem; color:var(--text-muted); margin-bottom:6px; font-weight:500;">Portal Tag</label>
|
||||
<input type="text" id="edit-app-tag" name="tag" placeholder="HR Portal" class="form-input" style="width:100%; padding:10px; background:rgba(255,255,255,0.03); border:1px solid var(--card-border); border-radius:8px; color:white;">
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<label style="display:block; font-size:0.75rem; color:var(--text-muted); margin-bottom:6px; font-weight:500;">Icon SVG Code or Preset Name (outlook/teams/keka)</label>
|
||||
<textarea id="edit-app-icon_svg" name="icon_svg" rows="2" placeholder="e.g. keka, outlook, teams, or raw <svg>...</svg>" class="form-input" style="width:100%; padding:10px; background:rgba(255,255,255,0.03); border:1px solid var(--card-border); border-radius:8px; color:white; font-family:monospace; font-size:0.8rem;"></textarea>
|
||||
</div>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<label style="display:block; font-size:0.75rem; color:var(--text-muted); margin-bottom:6px; font-weight:500;">Upload Service Logo (Optional - overrides preset SVG)</label>
|
||||
<input type="file" name="logo" accept=".jpg,.jpeg,.png,.webp,.svg" class="form-input" style="width:100%; padding:8px; background:rgba(255,255,255,0.03); border:1px solid var(--card-border); border-radius:8px; color:white;">
|
||||
<div style="font-size:0.7rem; color:var(--text-muted); margin-top:4px;">Currently: <span id="edit-app-logo-status" style="font-weight:600; color:#38bdf8;">None</span></div>
|
||||
</div>
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="display:block; font-size:0.75rem; color:var(--text-muted); margin-bottom:6px; font-weight:500;">Service Description</label>
|
||||
<input type="text" id="edit-app-desc" name="desc" placeholder="Brief description of the service..." class="form-input" style="width:100%; padding:10px; background:rgba(255,255,255,0.03); border:1px solid var(--card-border); border-radius:8px; color:white;">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-cancel" onclick="closeModal('edit-app-modal')">Cancel</button>
|
||||
<button type="submit" class="action-btn" style="background-color:var(--accent-primary); color:white; border-color:transparent; font-weight:600; padding:8px 16px; border-radius:8px; cursor:pointer;">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Assign User Roles Modal -->
|
||||
<div id="assign-roles-modal" class="admin-modal">
|
||||
<div class="admin-modal-content">
|
||||
@ -1038,7 +1099,7 @@ function closeModal(modalId) {
|
||||
|
||||
function openEditRoleModal(id, name, description, appIds) {
|
||||
const form = document.getElementById('edit-role-form');
|
||||
form.action = `/admin/roles/${id}/update`;
|
||||
form.action = "{{ route('admin.roles.update', ':id') }}".replace(':id', id);
|
||||
|
||||
document.getElementById('edit-role-name').value = name;
|
||||
document.getElementById('edit-role-description').value = description || '';
|
||||
@ -1053,7 +1114,7 @@ function openEditRoleModal(id, name, description, appIds) {
|
||||
|
||||
function openAssignRolesModal(userId, name, roleIds) {
|
||||
const form = document.getElementById('assign-roles-form');
|
||||
form.action = `/admin/users/${userId}/roles`;
|
||||
form.action = "{{ route('admin.users.roles.update', ':id') }}".replace(':id', userId);
|
||||
|
||||
document.getElementById('assign-user-name').innerText = name;
|
||||
|
||||
@ -1064,6 +1125,29 @@ function openAssignRolesModal(userId, name, roleIds) {
|
||||
|
||||
openModal('assign-roles-modal');
|
||||
}
|
||||
|
||||
function openEditAppModal(id, name, url, tag, iconSvg, color, desc, logoPath) {
|
||||
const form = document.getElementById('edit-app-form');
|
||||
form.action = "{{ route('admin.apps.update', ':id') }}".replace(':id', id);
|
||||
|
||||
document.getElementById('edit-app-name').value = name;
|
||||
document.getElementById('edit-app-url').value = url;
|
||||
document.getElementById('edit-app-color').value = color || '';
|
||||
document.getElementById('edit-app-tag').value = tag || '';
|
||||
document.getElementById('edit-app-icon_svg').value = iconSvg || '';
|
||||
document.getElementById('edit-app-desc').value = desc || '';
|
||||
|
||||
const logoStatus = document.getElementById('edit-app-logo-status');
|
||||
if (logoPath) {
|
||||
logoStatus.innerText = 'Has custom logo uploaded';
|
||||
logoStatus.style.color = '#10b981';
|
||||
} else {
|
||||
logoStatus.innerText = 'No custom logo uploaded (using preset SVG/initials)';
|
||||
logoStatus.style.color = '#9ca3af';
|
||||
}
|
||||
|
||||
openModal('edit-app-modal');
|
||||
}
|
||||
</script>
|
||||
|
||||
<footer style="text-align: center; padding: 40px 0; font-size: 0.75rem; color: var(--text-muted); z-index: 10;">
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
Route::middleware(['auth', 'role:admin'])->group(function () {
|
||||
Route::get('/admin/dashboard', [AdminController::class, 'index'])->name('admin.dashboard')->middleware('verify-ms-session');
|
||||
Route::post('/admin/apps', [AdminController::class, 'storeApp'])->name('admin.apps.store');
|
||||
Route::post('/admin/apps/{id}/update', [AdminController::class, 'updateApp'])->name('admin.apps.update');
|
||||
Route::delete('/admin/apps/{id}', [AdminController::class, 'destroyApp'])->name('admin.apps.destroy');
|
||||
Route::post('/admin/users/{id}/toggle-block', [AdminController::class, 'toggleBlockUser'])->name('admin.users.toggle-block');
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user