Files
orion/app/templates/admin/user-edit.html
Samir Boulahtit 0b4291d893 refactor(js): migrate JavaScript files to module directories
Move 47 JS files from static/{admin,vendor,shared}/js/ to their
respective module directories app/modules/*/static/*/js/:

- Orders: orders.js, order-detail.js
- Catalog: products.js (renamed from vendor-products.js), product-*.js
- Inventory: inventory.js (admin & vendor)
- Customers: customers.js, users.js, user-*.js
- Billing: billing-history.js, subscriptions.js, subscription-tiers.js,
  billing.js, invoices.js, feature-store.js, upgrade-prompts.js
- Messaging: messages.js, notifications.js, email-templates.js
- Marketplace: marketplace*.js, letzshop*.js, onboarding.js
- Monitoring: monitoring.js, background-tasks.js, imports.js, logs.js
- Dev Tools: testing-*.js, code-quality-*.js

Update 39 templates to reference new module static paths using
url_for('{module}_static', path='...') pattern.

Files staying in static/ (platform core):
- admin: dashboard, login, platforms, vendors, companies, admin-users,
  settings, components, init-alpine, module-config
- vendor: dashboard, login, profile, settings, team, media, init-alpine
- shared: api-client, utils, money, icons, log-config, vendor-selector,
  media-picker

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:08:20 +01:00

274 lines
14 KiB
HTML

{# app/templates/admin/user-edit.html #}
{% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import loading_state %}
{% from 'shared/macros/headers.html' import edit_page_header %}
{% block title %}Edit User{% endblock %}
{% block alpine_data %}adminUserEdit(){% endblock %}
{% block content %}
{% call edit_page_header('Edit User', '/admin/users', subtitle_show='user', back_label='Back to Users') %}
@<span x-text="user?.username"></span>
{% endcall %}
{{ loading_state('Loading user...', show_condition='loadingUser') }}
<!-- Edit Form -->
<div x-show="!loadingUser && user">
<!-- Quick Actions Card -->
<div class="px-4 py-3 mb-6 bg-white rounded-lg shadow-md dark:bg-gray-800">
<h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
Quick Actions
</h3>
<div class="flex flex-wrap items-center gap-3">
<button
@click="toggleStatus()"
:disabled="saving"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none disabled:opacity-50"
:class="user?.is_active ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'">
<span x-html="$icon(user?.is_active ? 'user-x' : 'user-check', 'w-4 h-4 mr-2')"></span>
<span x-text="user?.is_active ? 'Deactivate' : 'Activate'"></span>
</button>
<!-- Status Badges -->
<div class="ml-auto flex items-center gap-2">
<span
class="inline-flex items-center px-3 py-1 text-xs font-semibold leading-tight rounded-full capitalize"
:class="{
'text-orange-700 bg-orange-100 dark:bg-orange-700 dark:text-orange-100': user?.role === 'admin',
'text-purple-700 bg-purple-100 dark:bg-purple-700 dark:text-purple-100': user?.role === 'vendor'
}"
x-text="user?.role">
</span>
<span
x-show="user?.is_active"
class="inline-flex items-center px-3 py-1 text-xs font-semibold leading-tight text-green-700 bg-green-100 rounded-full dark:bg-green-700 dark:text-green-100">
Active
</span>
<span
x-show="!user?.is_active"
class="inline-flex items-center px-3 py-1 text-xs font-semibold leading-tight text-red-700 bg-red-100 rounded-full dark:bg-red-700 dark:text-red-100">
Inactive
</span>
</div>
</div>
</div>
<!-- Form Card -->
<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>
<!-- User ID (readonly) -->
<label class="block mb-4 text-sm">
<span class="text-gray-700 dark:text-gray-400">
User ID
</span>
<input
type="text"
:value="user?.id"
disabled
class="block w-full mt-1 text-sm bg-gray-100 border-gray-300 rounded-md dark:bg-gray-700 dark:text-gray-400 dark:border-gray-600 cursor-not-allowed"
>
<span class="text-xs text-gray-600 dark:text-gray-400 mt-1">
System-generated identifier
</span>
</label>
<!-- 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"
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>
</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"
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>
<!-- 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"
: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>
</label>
</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"
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"
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>
<!-- Email Verified -->
<label class="block mb-4 text-sm">
<span class="text-gray-700 dark:text-gray-400">
Email Verification
</span>
<div class="flex items-center mt-2">
<input
type="checkbox"
x-model="formData.is_email_verified"
:disabled="saving"
class="form-checkbox text-purple-600"
>
<span class="ml-2 text-gray-700 dark:text-gray-400">
Email is verified
</span>
</div>
</label>
</div>
</div>
<!-- User Statistics (readonly) -->
<template x-if="user?.owned_companies_count !== undefined">
<div class="mb-8">
<h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
User Statistics
</h3>
<div class="grid gap-4 md:grid-cols-3">
<div class="p-4 bg-gray-50 rounded-lg dark:bg-gray-700">
<p class="text-sm text-gray-600 dark:text-gray-400">Companies Owned</p>
<p class="text-2xl font-semibold text-gray-700 dark:text-gray-200" x-text="user.owned_companies_count || 0"></p>
</div>
<div class="p-4 bg-gray-50 rounded-lg dark:bg-gray-700">
<p class="text-sm text-gray-600 dark:text-gray-400">Vendor Memberships</p>
<p class="text-2xl font-semibold text-gray-700 dark:text-gray-200" x-text="user.vendor_memberships_count || 0"></p>
</div>
<div class="p-4 bg-gray-50 rounded-lg dark:bg-gray-700">
<p class="text-sm text-gray-600 dark:text-gray-400">Last Login</p>
<p class="text-sm font-semibold text-gray-700 dark:text-gray-200" x-text="user.last_login ? formatDate(user.last_login) : 'Never'"></p>
</div>
</div>
</div>
</template>
<!-- Save Button -->
<div class="flex items-center justify-end gap-3 pt-6 border-t dark:border-gray-700">
<a
:href="`/admin/users/${userId}`"
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('check', 'w-4 h-4 mr-2 inline')"></span>
Save Changes
</span>
<span x-show="saving" class="flex items-center">
<span x-html="$icon('spinner', 'w-4 h-4 mr-2')"></span>
Saving...
</span>
</button>
</div>
</form>
<!-- More Actions Section -->
<div class="px-4 py-3 bg-white rounded-lg shadow-md dark:bg-gray-800">
<h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
More Actions
</h3>
<div class="flex flex-wrap gap-3">
<!-- View User Button -->
<a
:href="`/admin/users/${userId}`"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition-colors duration-150 bg-blue-600 border border-transparent rounded-lg hover:bg-blue-700 focus:outline-none focus:shadow-outline-blue"
>
<span x-html="$icon('eye', 'w-4 h-4 mr-2')"></span>
View User
</a>
<!-- Delete User Button -->
<button
@click="deleteUser()"
:disabled="saving || (user?.owned_companies_count > 0)"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white transition-colors duration-150 bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:shadow-outline-red disabled:opacity-50"
:title="user?.owned_companies_count > 0 ? 'Cannot delete user who owns companies' : 'Delete this user'"
>
<span x-html="$icon('delete', 'w-4 h-4 mr-2')"></span>
Delete User
</button>
</div>
<p class="mt-3 text-xs text-gray-500 dark:text-gray-400">
<span x-html="$icon('information-circle', 'w-4 h-4 inline mr-1')"></span>
<span x-show="user?.owned_companies_count > 0" class="text-orange-600 dark:text-orange-400">
User cannot be deleted while they own companies (<span x-text="user?.owned_companies_count"></span> companies).
</span>
<span x-show="!user?.owned_companies_count">
User can be deleted.
</span>
</p>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script src="{{ url_for('customers_static', path='admin/js/user-edit.js') }}"></script>
{% endblock %}