User create page: - When role=admin, show super admin toggle - If not super admin, show platform multi-select - Admin users created via /api/v1/admin/admin-users endpoint - Vendor users created via existing /admin/users endpoint Vendor create page: - Add platform selection section - Vendors can be assigned to multiple platforms on creation - Update VendorCreate schema to accept platform_ids - Update AdminService.create_vendor() to create VendorPlatform records Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
215 lines
11 KiB
HTML
215 lines
11 KiB
HTML
{# app/templates/admin/user-create.html #}
|
|
{% extends "admin/base.html" %}
|
|
{% from 'shared/macros/headers.html' import page_header %}
|
|
|
|
{% block title %}Create 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') }}
|
|
|
|
<!-- Create Form -->
|
|
<form @submit.prevent="handleSubmit" class="px-4 py-3 mb-8 bg-white rounded-lg shadow-md dark:bg-gray-800">
|
|
<div class="grid gap-6 mb-8 md:grid-cols-2">
|
|
<!-- Left Column: Account Info -->
|
|
<div>
|
|
<h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
|
|
Account Information
|
|
</h3>
|
|
|
|
<!-- Username -->
|
|
<label class="block mb-4 text-sm">
|
|
<span class="text-gray-700 dark:text-gray-400">
|
|
Username <span class="text-red-600">*</span>
|
|
</span>
|
|
<input
|
|
type="text"
|
|
x-model="formData.username"
|
|
required
|
|
maxlength="50"
|
|
:disabled="saving"
|
|
placeholder="johndoe"
|
|
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-input"
|
|
:class="{ 'border-red-600 focus:border-red-400 focus:shadow-outline-red': errors.username }"
|
|
>
|
|
<span x-show="errors.username" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.username"></span>
|
|
<span x-show="!errors.username" class="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
|
Letters, numbers, and underscores only
|
|
</span>
|
|
</label>
|
|
|
|
<!-- Email -->
|
|
<label class="block mb-4 text-sm">
|
|
<span class="text-gray-700 dark:text-gray-400">
|
|
Email <span class="text-red-600">*</span>
|
|
</span>
|
|
<input
|
|
type="email"
|
|
x-model="formData.email"
|
|
required
|
|
:disabled="saving"
|
|
placeholder="john@example.com"
|
|
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-input"
|
|
:class="{ 'border-red-600 focus:border-red-400 focus:shadow-outline-red': errors.email }"
|
|
>
|
|
<span x-show="errors.email" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.email"></span>
|
|
</label>
|
|
|
|
<!-- Password -->
|
|
<label class="block mb-4 text-sm">
|
|
<span class="text-gray-700 dark:text-gray-400">
|
|
Password <span class="text-red-600">*</span>
|
|
</span>
|
|
<input
|
|
type="password"
|
|
x-model="formData.password"
|
|
required
|
|
minlength="6"
|
|
:disabled="saving"
|
|
placeholder="Minimum 6 characters"
|
|
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-input"
|
|
:class="{ 'border-red-600 focus:border-red-400 focus:shadow-outline-red': errors.password }"
|
|
>
|
|
<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>
|
|
|
|
<!-- Super Admin Toggle -->
|
|
<label class="flex items-center mb-4">
|
|
<input
|
|
type="checkbox"
|
|
x-model="formData.is_super_admin"
|
|
: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">
|
|
Super Admin
|
|
</span>
|
|
</label>
|
|
<p class="text-xs text-gray-600 dark:text-gray-400 mb-4 -mt-2 ml-6">
|
|
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>
|
|
</div>
|
|
|
|
<!-- Right Column: Personal Info -->
|
|
<div>
|
|
<h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
|
|
Personal Information
|
|
</h3>
|
|
|
|
<!-- First Name -->
|
|
<label class="block mb-4 text-sm">
|
|
<span class="text-gray-700 dark:text-gray-400">
|
|
First Name
|
|
</span>
|
|
<input
|
|
type="text"
|
|
x-model="formData.first_name"
|
|
maxlength="100"
|
|
:disabled="saving"
|
|
placeholder="John"
|
|
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-input"
|
|
>
|
|
</label>
|
|
|
|
<!-- Last Name -->
|
|
<label class="block mb-4 text-sm">
|
|
<span class="text-gray-700 dark:text-gray-400">
|
|
Last Name
|
|
</span>
|
|
<input
|
|
type="text"
|
|
x-model="formData.last_name"
|
|
maxlength="100"
|
|
:disabled="saving"
|
|
placeholder="Doe"
|
|
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-input"
|
|
>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Submit Button -->
|
|
<div class="flex items-center justify-end gap-3 pt-6 border-t dark:border-gray-700">
|
|
<a
|
|
href="/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>
|
|
<button
|
|
type="submit"
|
|
:disabled="saving"
|
|
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
|
|
</span>
|
|
<span x-show="saving" class="flex items-center">
|
|
<span x-html="$icon('spinner', 'w-4 h-4 mr-2')"></span>
|
|
Creating...
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
{% endblock %}
|
|
|
|
{% block extra_scripts %}
|
|
<script src="{{ url_for('static', path='admin/js/user-create.js') }}"></script>
|
|
{% endblock %}
|