update some recomended changes

This commit is contained in:
subhajit 2026-07-14 17:38:09 +05:30
parent a842a71f40
commit 9f0aeee412
6 changed files with 151 additions and 7 deletions

View File

@ -92,6 +92,47 @@ public function destroyApp($id)
return redirect()->route('admin.dashboard')->with('success', 'Service app deleted successfully.'); 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. * Store a new role.
*/ */

View File

@ -58,6 +58,24 @@ public function authorizedApps()
->pluck('app_id') ->pluck('app_id')
->toArray(); ->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 // Get user overrides
$overrides = $this->overrides()->get(); $overrides = $this->overrides()->get();
$allowedAppIds = $overrides->where('type', 'allow')->pluck('app_id')->toArray(); $allowedAppIds = $overrides->where('type', 'allow')->pluck('app_id')->toArray();

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -654,11 +654,22 @@
</td> </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><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;"> <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;">
@csrf <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">
@method('DELETE') <svg viewBox="0 0 24 24" style="width: 14px; height: 14px; fill: currentColor;">
<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> <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" />
</form> </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); 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> </td>
</tr> </tr>
@empty @empty
@ -994,6 +1005,56 @@
</div> </div>
</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')">&times;</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 --> <!-- Assign User Roles Modal -->
<div id="assign-roles-modal" class="admin-modal"> <div id="assign-roles-modal" class="admin-modal">
<div class="admin-modal-content"> <div class="admin-modal-content">
@ -1038,7 +1099,7 @@ function closeModal(modalId) {
function openEditRoleModal(id, name, description, appIds) { function openEditRoleModal(id, name, description, appIds) {
const form = document.getElementById('edit-role-form'); 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-name').value = name;
document.getElementById('edit-role-description').value = description || ''; document.getElementById('edit-role-description').value = description || '';
@ -1053,7 +1114,7 @@ function openEditRoleModal(id, name, description, appIds) {
function openAssignRolesModal(userId, name, roleIds) { function openAssignRolesModal(userId, name, roleIds) {
const form = document.getElementById('assign-roles-form'); 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; document.getElementById('assign-user-name').innerText = name;
@ -1064,6 +1125,29 @@ function openAssignRolesModal(userId, name, roleIds) {
openModal('assign-roles-modal'); 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> </script>
<footer style="text-align: center; padding: 40px 0; font-size: 0.75rem; color: var(--text-muted); z-index: 10;"> <footer style="text-align: center; padding: 40px 0; font-size: 0.75rem; color: var(--text-muted); z-index: 10;">

View File

@ -34,6 +34,7 @@
Route::middleware(['auth', 'role:admin'])->group(function () { Route::middleware(['auth', 'role:admin'])->group(function () {
Route::get('/admin/dashboard', [AdminController::class, 'index'])->name('admin.dashboard')->middleware('verify-ms-session'); 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', [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::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'); Route::post('/admin/users/{id}/toggle-block', [AdminController::class, 'toggleBlockUser'])->name('admin.users.toggle-block');