changed creating logic to modal
This commit is contained in:
parent
72b40e965a
commit
a893c09fcf
|
|
@ -0,0 +1,27 @@
|
|||
import { Controller } from "@hotwired/stimulus";
|
||||
|
||||
export default class extends Controller {
|
||||
async fetchAndRenderApplications(targetElement) {
|
||||
try {
|
||||
const response = await fetch('/application/data/all');
|
||||
const apps = await response.json();
|
||||
|
||||
targetElement.innerHTML = apps.map(app => `
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="form-check border p-2 rounded d-flex align-items-center gap-2">
|
||||
<input class="form-check-input ms-1" type="checkbox" name="applications[]" value="${app.id}" id="app_${app.id}">
|
||||
<label class="form-check-label d-flex align-items-center gap-2" for="app_${app.id}">
|
||||
<img src="${app.logoMiniUrl}" alt="${app.name}" style="height: 20px; width: 20px; object-fit: contain;">
|
||||
${app.name}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
return apps;
|
||||
} catch (error) {
|
||||
targetElement.innerHTML = '<div class="text-danger">Erreur de chargement.</div>';
|
||||
console.error("App load error:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,10 @@ import {Controller} from '@hotwired/stimulus';
|
|||
import { Modal } from "bootstrap";
|
||||
import {TabulatorFull as Tabulator} from 'tabulator-tables';
|
||||
import {eyeIconLink, pencilIcon, TABULATOR_FR_LANG, trashIcon} from "../js/global.js";
|
||||
import base_controller from "./base_controller.js";
|
||||
|
||||
|
||||
export default class extends Controller {
|
||||
export default class extends base_controller {
|
||||
static values = {
|
||||
listProject : Boolean,
|
||||
orgId: Number,
|
||||
|
|
@ -175,13 +176,13 @@ export default class extends Controller {
|
|||
this.currentProjectId = projectId;
|
||||
|
||||
this.modal.show();
|
||||
|
||||
this.nameInputTarget.disabled = true;
|
||||
this.formTitleTarget.textContent = "Modifier le projet";
|
||||
|
||||
try {
|
||||
// 1. Ensure checkboxes are loaded first
|
||||
await this.loadApplications();
|
||||
|
||||
const apps = await this.fetchAndRenderApplications(this.appListTarget);
|
||||
// 2. Fetch the project data
|
||||
const response = await fetch(`/project/data/${projectId}`);
|
||||
const project = await response.json();
|
||||
|
|
@ -203,13 +204,13 @@ export default class extends Controller {
|
|||
}
|
||||
}
|
||||
// Update your openCreateModal to reset the state
|
||||
openCreateModal() {
|
||||
async openCreateModal() {
|
||||
this.currentProjectId = null;
|
||||
this.modal.show();
|
||||
this.nameInputTarget.disabled = false;
|
||||
this.nameInputTarget.value = "";
|
||||
this.formTitleTarget.textContent = "Nouveau Projet";
|
||||
this.loadApplications();
|
||||
await this.fetchAndRenderApplications();
|
||||
}
|
||||
|
||||
async deleteProject(event) {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import {
|
|||
trashIconForm
|
||||
} from "../js/global.js";
|
||||
import { Modal } from "bootstrap";
|
||||
import base_controller from "./base_controller.js";
|
||||
|
||||
|
||||
export default class extends Controller {
|
||||
export default class extends base_controller {
|
||||
static values = {
|
||||
rolesArray: Array,
|
||||
selectedRoleIds: Array,
|
||||
|
|
@ -26,7 +27,7 @@ export default class extends Controller {
|
|||
orgId: Number
|
||||
}
|
||||
|
||||
static targets = ["select", "statusButton", "modal", "userSelect"];
|
||||
static targets = ["select", "statusButton", "modal", "userSelect", "appList"];
|
||||
|
||||
connect() {
|
||||
this.roleSelect();
|
||||
|
|
@ -1018,4 +1019,38 @@ export default class extends Controller {
|
|||
alert("Une erreur réseau est survenue.");
|
||||
}
|
||||
}
|
||||
|
||||
async openNewUserModal() {
|
||||
this.modal.show();
|
||||
// Call the shared logic and pass the target
|
||||
await this.fetchAndRenderApplications(this.appListTarget);
|
||||
}
|
||||
|
||||
async submitNewUser(event) {
|
||||
event.preventDefault();
|
||||
const form = event.currentTarget;
|
||||
const formData = new FormData(form);
|
||||
|
||||
try {
|
||||
const response = await fetch('/user/new/ajax', { // Adjust path if prefix is different
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
this.modal.hide();
|
||||
form.reset(); // Clear the form
|
||||
location.reload();
|
||||
} else {
|
||||
alert("Erreur: " + (result.error || "Une erreur est survenue lors de la création."));
|
||||
}
|
||||
} catch (error) {
|
||||
alert("Erreur réseau.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,88 +195,88 @@ class UserController extends AbstractController
|
|||
|
||||
}
|
||||
|
||||
#[Route('/new', name: 'new', methods: ['GET', 'POST'])]
|
||||
public function new(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||
try {
|
||||
$actingUser =$this->getUser();
|
||||
|
||||
$user = new User();
|
||||
$form = $this->createForm(UserForm::class, $user);
|
||||
$form->handleRequest($request);
|
||||
|
||||
$orgId = $request->query->get('organizationId') ?? $request->request->get('organizationId');
|
||||
if ($orgId) {
|
||||
$org = $this->organizationRepository->find($orgId);
|
||||
if (!$org) {
|
||||
$this->loggerService->logEntityNotFound('Organization', ['id' => $orgId], $actingUser->getUserIdentifier());
|
||||
$this->addFlash('danger', "L'organisation n'existe pas.");
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
if (!$this->isGranted('ROLE_ADMIN') && !$this->userService->isAdminOfOrganization($org)) {
|
||||
$this->loggerService->logAccessDenied($actingUser->getUserIdentifier());
|
||||
$this->addFlash('danger', "Accès non autorisé.");
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
}
|
||||
} else{
|
||||
$this->loggerService->logAccessDenied($actingUser->getUserIdentifier());
|
||||
$this->addFlash('danger', "Accès non autorisé.");
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$existingUser = $this->userRepository->findOneBy(['email' => $user->getEmail()]);
|
||||
|
||||
// Case : User exists -> link him to given organization if not already linked, else error message
|
||||
if ($existingUser && $org) {
|
||||
$this->userService->addExistingUserToOrganization(
|
||||
$existingUser,
|
||||
$org,
|
||||
);
|
||||
|
||||
if ($this->isGranted('ROLE_ADMIN')) {
|
||||
$this->loggerService->logSuperAdmin(
|
||||
$existingUser->getId(),
|
||||
$actingUser->getUserIdentifier(),
|
||||
"Super Admin linked user to organization",
|
||||
$org->getId(),
|
||||
);
|
||||
}
|
||||
$this->addFlash('success', 'Utilisateur ajouté avec succès à l\'organisation. ');
|
||||
return $this->redirectToRoute('organization_show', ['id' => $orgId]);
|
||||
}
|
||||
|
||||
// Case : user doesn't already exist
|
||||
|
||||
$picture = $form->get('pictureUrl')->getData();
|
||||
$this->userService->createNewUser($user, $actingUser, $picture);
|
||||
|
||||
$this->userService->linkUserToOrganization(
|
||||
$user,
|
||||
$org,
|
||||
);
|
||||
$this->addFlash('success', 'Nouvel utilisateur créé et ajouté à l\'organisation avec succès. ');
|
||||
return $this->redirectToRoute('organization_show', ['id' => $orgId]);
|
||||
}
|
||||
|
||||
return $this->render('user/new.html.twig', [
|
||||
'user' => $user,
|
||||
'form' => $form->createView(),
|
||||
'organizationId' => $orgId,
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->errorLogger->critical($e->getMessage());
|
||||
|
||||
if ($orgId) {
|
||||
$this->addFlash('danger', 'Une erreur est survenue lors de la création de l\'utilisateur pour l\'organisation .');
|
||||
return $this->redirectToRoute('organization_show', ['id' => $orgId]);
|
||||
}
|
||||
$this->addFlash('danger', 'Une erreur est survenue lors de la création de l\'utilisateur.');
|
||||
return $this->redirectToRoute('user_index');
|
||||
}
|
||||
}
|
||||
// #[Route('/new', name: 'new', methods: ['GET', 'POST'])]
|
||||
// public function new(Request $request): Response
|
||||
// {
|
||||
// $this->denyAccessUnlessGranted('ROLE_USER');
|
||||
// try {
|
||||
// $actingUser =$this->getUser();
|
||||
//
|
||||
// $user = new User();
|
||||
// $form = $this->createForm(UserForm::class, $user);
|
||||
// $form->handleRequest($request);
|
||||
//
|
||||
// $orgId = $request->query->get('organizationId') ?? $request->request->get('organizationId');
|
||||
// if ($orgId) {
|
||||
// $org = $this->organizationRepository->find($orgId);
|
||||
// if (!$org) {
|
||||
// $this->loggerService->logEntityNotFound('Organization', ['id' => $orgId], $actingUser->getUserIdentifier());
|
||||
// $this->addFlash('danger', "L'organisation n'existe pas.");
|
||||
// throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
// }
|
||||
// if (!$this->isGranted('ROLE_ADMIN') && !$this->userService->isAdminOfOrganization($org)) {
|
||||
// $this->loggerService->logAccessDenied($actingUser->getUserIdentifier());
|
||||
// $this->addFlash('danger', "Accès non autorisé.");
|
||||
// throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
// }
|
||||
// } else{
|
||||
// $this->loggerService->logAccessDenied($actingUser->getUserIdentifier());
|
||||
// $this->addFlash('danger', "Accès non autorisé.");
|
||||
// throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
// }
|
||||
//
|
||||
// if ($form->isSubmitted() && $form->isValid()) {
|
||||
// $existingUser = $this->userRepository->findOneBy(['email' => $user->getEmail()]);
|
||||
//
|
||||
// // Case : User exists -> link him to given organization if not already linked, else error message
|
||||
// if ($existingUser && $org) {
|
||||
// $this->userService->addExistingUserToOrganization(
|
||||
// $existingUser,
|
||||
// $org,
|
||||
// );
|
||||
//
|
||||
// if ($this->isGranted('ROLE_ADMIN')) {
|
||||
// $this->loggerService->logSuperAdmin(
|
||||
// $existingUser->getId(),
|
||||
// $actingUser->getUserIdentifier(),
|
||||
// "Super Admin linked user to organization",
|
||||
// $org->getId(),
|
||||
// );
|
||||
// }
|
||||
// $this->addFlash('success', 'Utilisateur ajouté avec succès à l\'organisation. ');
|
||||
// return $this->redirectToRoute('organization_show', ['id' => $orgId]);
|
||||
// }
|
||||
//
|
||||
// // Case : user doesn't already exist
|
||||
//
|
||||
// $picture = $form->get('pictureUrl')->getData();
|
||||
// $this->userService->createNewUser($user, $actingUser, $picture);
|
||||
//
|
||||
// $this->userService->linkUserToOrganization(
|
||||
// $user,
|
||||
// $org,
|
||||
// );
|
||||
// $this->addFlash('success', 'Nouvel utilisateur créé et ajouté à l\'organisation avec succès. ');
|
||||
// return $this->redirectToRoute('organization_show', ['id' => $orgId]);
|
||||
// }
|
||||
//
|
||||
// return $this->render('user/new.html.twig', [
|
||||
// 'user' => $user,
|
||||
// 'form' => $form->createView(),
|
||||
// 'organizationId' => $orgId,
|
||||
// ]);
|
||||
//
|
||||
// } catch (\Exception $e) {
|
||||
// $this->errorLogger->critical($e->getMessage());
|
||||
//
|
||||
// if ($orgId) {
|
||||
// $this->addFlash('danger', 'Une erreur est survenue lors de la création de l\'utilisateur pour l\'organisation .');
|
||||
// return $this->redirectToRoute('organization_show', ['id' => $orgId]);
|
||||
// }
|
||||
// $this->addFlash('danger', 'Une erreur est survenue lors de la création de l\'utilisateur.');
|
||||
// return $this->redirectToRoute('user_index');
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Endpoint to activate/deactivate a user (soft delete)
|
||||
|
|
@ -832,6 +832,86 @@ class UserController extends AbstractController
|
|||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[Route('/new/ajax', name: 'new_ajax', methods: ['POST'])]
|
||||
public function newUserAjax(Request $request): JsonResponse
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||
$actingUser = $this->getUser();
|
||||
|
||||
try {
|
||||
$data = $request->request->all();
|
||||
$orgId = $data['organizationId'] ?? null;
|
||||
$selectedApps = $data['applications'] ?? [];
|
||||
|
||||
//unset data that are not part of the User entity to avoid form errors
|
||||
unset($data['organizationId'], $data['applications']);
|
||||
$user = new User();
|
||||
|
||||
$form = $this->createForm(UserForm::class, $user, [
|
||||
'csrf_protection' => false,
|
||||
'allow_extra_fields' => true,
|
||||
]);
|
||||
|
||||
$form->submit($data, false);
|
||||
if (!$orgId) {
|
||||
return $this->json(['error' => 'ID Organisation manquant.'], 400);
|
||||
}
|
||||
|
||||
$org = $this->organizationRepository->find($orgId);
|
||||
if (!$org) {
|
||||
return $this->json(['error' => "L'organisation n'existe pas."], 404);
|
||||
}
|
||||
|
||||
// 3. Permissions Check
|
||||
if (!$this->isGranted('ROLE_ADMIN') && !$this->userService->isAdminOfOrganization($org)) {
|
||||
$this->loggerService->logAccessDenied($actingUser->getUserIdentifier());
|
||||
return $this->json(['error' => "Accès non autorisé."], 403);
|
||||
}
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$email = $user->getEmail();
|
||||
$existingUser = $this->userRepository->findOneBy(['email' => $email]);
|
||||
|
||||
// CASE A: User exists -> Add to org
|
||||
if ($existingUser) {
|
||||
// Check if already in org to avoid logic errors or duplicate logs
|
||||
$this->userService->addExistingUserToOrganization($existingUser, $org, $selectedApps);
|
||||
|
||||
if ($this->isGranted('ROLE_ADMIN')) {
|
||||
$this->loggerService->logSuperAdmin(
|
||||
$existingUser->getId(),
|
||||
$actingUser->getUserIdentifier(),
|
||||
"Super Admin linked user to organization via AJAX",
|
||||
$org->getId(),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'message' => 'Utilisateur existant ajouté à l\'organisation.'
|
||||
]);
|
||||
}
|
||||
|
||||
// CASE B: New User -> Create
|
||||
// Fetch picture from $request->files since it's a multipart request
|
||||
$picture = $request->files->get('pictureUrl');
|
||||
|
||||
$this->userService->createNewUser($user, $actingUser, $picture);
|
||||
$this->userService->linkUserToOrganization($user, $org, $selectedApps);
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'message' => 'Nouvel utilisateur créé et ajouté.'
|
||||
]);
|
||||
}
|
||||
|
||||
// If form is invalid, return the specific errors
|
||||
return $this->json(['error' => 'Données de formulaire invalides.'], 400);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->errorLogger->critical("AJAX User Creation Error: " . $e->getMessage());
|
||||
return $this->json(['error' => 'Une erreur interne est survenue.'], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Service;
|
||||
|
||||
|
||||
use App\Entity\Apps;
|
||||
use App\Entity\Organizations;
|
||||
use App\Entity\Roles;
|
||||
use App\Entity\User;
|
||||
|
|
@ -533,17 +534,17 @@ class UserService
|
|||
*
|
||||
* @param User $user
|
||||
* @param Organizations $organization
|
||||
* @return void
|
||||
* @param array $selectedApps
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handleExistingUser(User $user, Organizations $organization): int
|
||||
public function reactivateUser(User $user, Organizations $organization, array $selectedApps): int
|
||||
{
|
||||
if (!$user->isActive()) {
|
||||
$user->setIsActive(true);
|
||||
$this->entityManager->persist($user);
|
||||
}
|
||||
$uo = $this->linkUserToOrganization($user, $organization);
|
||||
|
||||
return $uo->getId();
|
||||
return $this->linkUserToOrganization($user, $organization, $selectedApps)->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -554,6 +555,8 @@ class UserService
|
|||
* Handle picture if provided
|
||||
*
|
||||
* @param User $user
|
||||
* @param $picture
|
||||
* @param bool $setPassword
|
||||
* @return void
|
||||
*/
|
||||
public function formatUserData(User $user, $picture, bool $setPassword = false): void
|
||||
|
|
@ -589,11 +592,12 @@ class UserService
|
|||
public function addExistingUserToOrganization(
|
||||
User $existingUser,
|
||||
Organizations $org,
|
||||
array $selectedApps
|
||||
): int
|
||||
{
|
||||
try {
|
||||
$uoId = $this->handleExistingUser($existingUser, $org);
|
||||
$actingUser = $this->getUserByIdentifier($this->security->getUser()->getUserIdentifier());
|
||||
try {
|
||||
$uoId = $this->reactivateUser($existingUser, $org, $selectedApps);
|
||||
$this->loggerService->logExistingUserAddedToOrg(
|
||||
$existingUser->getId(),
|
||||
$org->getId(),
|
||||
|
|
@ -647,6 +651,7 @@ class UserService
|
|||
public function linkUserToOrganization(
|
||||
User $user,
|
||||
Organizations $org,
|
||||
array $selectedApps
|
||||
): UsersOrganizations
|
||||
{
|
||||
$actingUser = $this->getUserByIdentifier($this->security->getUser()->getUserIdentifier());
|
||||
|
|
@ -660,6 +665,7 @@ class UserService
|
|||
$uo->setRole($roleUser);
|
||||
$uo->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->entityManager->persist($uo);
|
||||
$this->linkUOToApps($uo, $selectedApps);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->loggerService->logUserOrganizationLinkCreated(
|
||||
|
|
@ -731,4 +737,18 @@ class UserService
|
|||
}
|
||||
}
|
||||
|
||||
private function linkUOToApps(UsersOrganizations $uo, array $selectedApps):void
|
||||
{
|
||||
foreach ($selectedApps as $appId){
|
||||
$uoa = new UserOrganizationApp();
|
||||
$uoa->setUserOrganization($uo);
|
||||
$app = $this->entityManager->getRepository(Apps::class)->find($appId);
|
||||
if ($app) {
|
||||
$uoa->setApplication($app);
|
||||
$this->entityManager->persist($uoa);
|
||||
}
|
||||
}
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,20 +52,70 @@
|
|||
{# User tables #}
|
||||
<div class="col-9">
|
||||
<div class="row mb-3 d-flex gap-2 ">
|
||||
<div class="col mb-3 card no-header-bg">
|
||||
<div class="col mb-3 card no-header-bg"
|
||||
data-controller="user"
|
||||
data-user-org-id-value="{{ organization.id }}"
|
||||
data-user-new-value="true"
|
||||
data-user-list-small-value="true">
|
||||
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2>
|
||||
Nouveaux utilisateurs
|
||||
</h2>
|
||||
<a href="{{ path('user_new', {'organizationId': organization.id}) }}"
|
||||
class="btn btn-primary">Ajouter un utilisateur</a>
|
||||
<h2>Nouveaux utilisateurs</h2>
|
||||
{# Button to trigger modal #}
|
||||
<button type="button" class="btn btn-primary" data-action="click->user#openNewUserModal">
|
||||
Ajouter un utilisateur
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="tabulator-userListSmall" data-controller="user"
|
||||
data-user-aws-value="{{ aws_url }}"
|
||||
data-user-new-value="true"
|
||||
data-user-list-small-value="true"
|
||||
data-user-org-id-value="{{ organization.id }}">
|
||||
<div id="tabulator-userListSmall"></div>
|
||||
</div>
|
||||
|
||||
{# New User Modal #}
|
||||
<div class="modal fade" id="newUserModal" tabindex="-1" aria-hidden="true" data-user-target="modal">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Créer un nouvel utilisateur</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form data-action="submit->user#submitNewUser">
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Email*</label>
|
||||
<input type="email" name="email" class="form-control" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label">Prénom*</label>
|
||||
<input type="text" name="name" class="form-control" required>
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label">Nom*</label>
|
||||
<input type="text" name="surname" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Numéro de téléphone</label>
|
||||
<input type="text" name="phoneNumber" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Photo de profil</label>
|
||||
<input type="file" name="pictureUrl" class="form-control" accept="image/*">
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<label class="form-label"><b>Applications à associer**</b></label>
|
||||
<div class="row" data-user-target="appList">
|
||||
{# Applications will be injected here #}
|
||||
<div class="text-center p-3 text-muted">Chargement des applications...</div>
|
||||
</div>
|
||||
<input type="hidden" name="organizationId" value="{{ organization.id }}">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||
<button type="submit" class="btn btn-primary">Créer l'utilisateur</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue