feat: implement admin-users management with super admin restriction

- Add /admin/admin-users routes for managing admin users (super admin only)
- Remove vendor role from user creation form (vendors created via company hierarchy)
- Add admin-users.html and admin-user-detail.html templates
- Add admin-users.js and admin-user-detail.js for frontend logic
- Move database operations to admin_platform_service (list, get, create, delete, toggle status)
- Update sidebar to show Admin Users section only for super admins
- Add isSuperAdmin computed property to init-alpine.js
- Fix /api/v1 prefix issues in JS files (apiClient already adds prefix)
- Update architecture rule JS-012 to catch more variable patterns (url, endpoint, path)
- Replace inline SVGs with $icon() helper in select-platform.html

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-24 21:28:46 +01:00
parent 9d28210cf1
commit 7e68b93132
16 changed files with 1691 additions and 325 deletions

View File

@@ -2,12 +2,12 @@
{% extends "admin/base.html" %}
{% from 'shared/macros/headers.html' import page_header %}
{% block title %}Create User{% endblock %}
{% block title %}Create Admin User{% endblock %}
{% block alpine_data %}adminUserCreate(){% endblock %}
{% block content %}
{{ page_header('Create New User', subtitle='Add a new user to the platform', back_url='/admin/users', back_label='Back to Users') }}
{{ page_header('Create Admin User', subtitle='Add a new admin user to manage platforms', back_url='/admin/admin-users', back_label='Back to Admin Users') }}
<!-- Create Form -->
<form @submit.prevent="handleSubmit" class="px-4 py-3 mb-8 bg-white rounded-lg shadow-md dark:bg-gray-800">
@@ -74,29 +74,9 @@
<span x-show="errors.password" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.password"></span>
</label>
<!-- Role -->
<label class="block mb-4 text-sm">
<span class="text-gray-700 dark:text-gray-400">
Role <span class="text-red-600">*</span>
</span>
<select
x-model="formData.role"
@change="onRoleChange()"
:disabled="saving"
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-select"
>
<option value="vendor">Vendor</option>
<option value="admin">Admin</option>
</select>
<span class="text-xs text-gray-600 dark:text-gray-400 mt-1">
Vendor: Can own companies and manage stores. Admin: Platform management access.
</span>
</label>
<!-- Admin-specific options -->
<template x-if="formData.role === 'admin'">
<div class="mt-4 p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg border border-purple-200 dark:border-purple-800">
<h4 class="text-sm font-medium text-purple-800 dark:text-purple-300 mb-3">Admin Settings</h4>
<!-- Admin Settings -->
<div class="mt-4 p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg border border-purple-200 dark:border-purple-800">
<h4 class="text-sm font-medium text-purple-800 dark:text-purple-300 mb-3">Admin Settings</h4>
<!-- Super Admin Toggle -->
<label class="flex items-center mb-4">
@@ -114,37 +94,36 @@
Super admins have access to all platforms and can manage other admins.
</p>
<!-- Platform Assignment (only if not super admin) -->
<template x-if="!formData.is_super_admin">
<div>
<label class="block text-sm">
<span class="text-gray-700 dark:text-gray-400">
Assigned Platforms <span class="text-red-600">*</span>
</span>
<div class="mt-2 space-y-2 max-h-48 overflow-y-auto">
<template x-for="platform in platforms" :key="platform.id">
<label class="flex items-center p-2 rounded hover:bg-purple-100 dark:hover:bg-purple-900/30 cursor-pointer">
<input
type="checkbox"
:value="platform.id"
x-model="formData.platform_ids"
:disabled="saving"
class="w-4 h-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500 dark:border-gray-600 dark:bg-gray-700"
>
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300" x-text="platform.name"></span>
<span class="ml-2 text-xs text-gray-500 dark:text-gray-400" x-text="'(' + platform.code + ')'"></span>
</label>
</template>
</div>
<p x-show="platforms.length === 0" class="text-sm text-gray-500 dark:text-gray-400 mt-2">
No platforms available. Create a platform first.
</p>
<span x-show="errors.platform_ids" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.platform_ids"></span>
</label>
</div>
</template>
</div>
</template>
<!-- Platform Assignment (only if not super admin) -->
<template x-if="!formData.is_super_admin">
<div>
<label class="block text-sm">
<span class="text-gray-700 dark:text-gray-400">
Assigned Platforms <span class="text-red-600">*</span>
</span>
<div class="mt-2 space-y-2 max-h-48 overflow-y-auto">
<template x-for="platform in platforms" :key="platform.id">
<label class="flex items-center p-2 rounded hover:bg-purple-100 dark:hover:bg-purple-900/30 cursor-pointer">
<input
type="checkbox"
:value="platform.id"
x-model="formData.platform_ids"
:disabled="saving"
class="w-4 h-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500 dark:border-gray-600 dark:bg-gray-700"
>
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300" x-text="platform.name"></span>
<span class="ml-2 text-xs text-gray-500 dark:text-gray-400" x-text="'(' + platform.code + ')'"></span>
</label>
</template>
</div>
<p x-show="platforms.length === 0" class="text-sm text-gray-500 dark:text-gray-400 mt-2">
No platforms available. Create a platform first.
</p>
<span x-show="errors.platform_ids" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.platform_ids"></span>
</label>
</div>
</template>
</div>
</div>
<!-- Right Column: Personal Info -->
@@ -188,7 +167,7 @@
<!-- Submit Button -->
<div class="flex items-center justify-end gap-3 pt-6 border-t dark:border-gray-700">
<a
href="/admin/users"
href="/admin/admin-users"
class="px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition-colors duration-150 bg-white border border-gray-300 rounded-lg dark:text-gray-400 dark:border-gray-600 dark:bg-gray-800 hover:border-gray-400 focus:outline-none">
Cancel
</a>
@@ -198,7 +177,7 @@
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple disabled:opacity-50 disabled:cursor-not-allowed">
<span x-show="!saving">
<span x-html="$icon('user-plus', 'w-4 h-4 mr-2 inline')"></span>
Create User
Create Admin User
</span>
<span x-show="saving" class="flex items-center">
<span x-html="$icon('spinner', 'w-4 h-4 mr-2')"></span>