templates/user/register.html.twig line 398

Open in your IDE?
  1. {% extends "base_security.html.twig" %}
  2. {% block fos_user_title %}
  3.   <h4 class="auth-header text-center" style="font-weight: 600; color: #2c3e50; margin-bottom: 0;">{{ 'title.new_user_account' | trans }}</h4>
  4. {% endblock fos_user_title %}
  5. {% block fos_user_content %}
  6.     <style>
  7.         .auth-box-w {
  8.             background: white;
  9.             border-radius: 16px;
  10.             padding: 25px;
  11.             box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
  12.             transition: transform 0.3s ease, box-shadow 0.3s ease;
  13.         }
  14.         
  15.         .auth-box-w:hover {
  16.             box-shadow: 0 15px 40px rgba(0, 0, 0, 0.12);
  17.         }
  18.         
  19.         .form-container {
  20.             max-width: 380px;
  21.             margin: 0 auto;
  22.         }
  23.         img {
  24.             width: 35px;
  25.             transition: transform 0.3s ease;
  26.         }
  27.         
  28.         img:hover {
  29.             transform: scale(1.05);
  30.         }
  31.         
  32.         /* Header avec Langue */
  33.         .header-separator {
  34.             display: flex;
  35.             justify-content: space-between;
  36.             align-items: center;
  37.             margin-bottom: 20px;
  38.             padding-bottom: 12px;
  39.             border-bottom: 2px solid #e8ecf1;
  40.         }
  41.         
  42.         .header-separator h4 {
  43.             background: linear-gradient(135deg, #1976d2, #0d47a1);
  44.             -webkit-background-clip: text;
  45.             background-clip: text;
  46.             color: transparent;
  47.         }
  48.         
  49.         .language-selector {
  50.             display: flex;
  51.             align-items: center;
  52.             gap: 8px;
  53.         }
  54.         
  55.         .language-selector label {
  56.             margin: 0;
  57.             color: #666;
  58.             font-size: 12px;
  59.             font-weight: 500;
  60.         }
  61.         
  62.         .language-selector select {
  63.             padding: 4px 8px;
  64.             border: 1px solid #e0e4e8;
  65.             border-radius: 6px;
  66.             background: #fafbfc;
  67.             font-size: 12px;
  68.             cursor: pointer;
  69.             transition: all 0.2s ease;
  70.         }
  71.         
  72.         .language-selector select:hover {
  73.             border-color: #1976d2;
  74.             background: white;
  75.         }
  76.         
  77.         .form-group {
  78.             margin-bottom: 14px;
  79.         }
  80.         
  81.         .form-group label {
  82.             display: block;
  83.             margin-bottom: 5px;
  84.             font-weight: 600;
  85.             color: #2c3e50;
  86.             font-size: 12px;
  87.             letter-spacing: 0.3px;
  88.         }
  89.         
  90.         .form-control, .custom-select {
  91.             width: 100%;
  92.             padding: 8px 12px;
  93.             border: 1.5px solid #e8ecf1;
  94.             border-radius: 8px;
  95.             font-size: 13px;
  96.             transition: all 0.3s ease;
  97.             background: #fafbfc;
  98.             font-family: inherit;
  99.         }
  100.         
  101.         .form-control:hover, .custom-select:hover {
  102.             border-color: #bdc3c7;
  103.             background: white;
  104.         }
  105.         
  106.         .form-control:focus, .custom-select:focus {
  107.             outline: none;
  108.             border-color: #1976d2;
  109.             background: white;
  110.             box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);
  111.         }
  112.         
  113.         select.form-control, select.custom-select {
  114.             appearance: none;
  115.             background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="%23666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>');
  116.             background-repeat: no-repeat;
  117.             background-position: right 10px center;
  118.             background-size: 10px;
  119.         }
  120.         
  121.         /* Groupe téléphone */
  122.         .phone-group {
  123.             display: flex;
  124.             gap: 8px;
  125.             align-items: center;
  126.         }
  127.         
  128.         .phone-prefix {
  129.             width: 70px;
  130.             flex-shrink: 0;
  131.         }
  132.         
  133.         .phone-prefix input {
  134.             text-align: center;
  135.             background: #f0f2f5;
  136.             border: 1.5px solid #e8ecf1;
  137.             font-weight: 600;
  138.             color: #1976d2;
  139.         }
  140.         
  141.         .phone-number {
  142.             flex: 1;
  143.         }
  144.         
  145.         .phone-hint {
  146.             font-size: 10px;
  147.             color: #95a5a6;
  148.             margin-top: 4px;
  149.             display: flex;
  150.             align-items: center;
  151.             gap: 4px;
  152.         }
  153.         
  154.         .phone-hint::before {
  155.             content: "💡";
  156.             font-size: 10px;
  157.         }
  158.         
  159.         /* Ligne de séparation */
  160.         .separator {
  161.             height: 1px;
  162.             background: linear-gradient(90deg, transparent, #e0e4e8, transparent);
  163.             margin: 18px 0;
  164.         }
  165.         
  166.         /* Boutons */
  167.         .buttons-group {
  168.             display: flex;
  169.             gap: 12px;
  170.             margin-top: 18px;
  171.         }
  172.         
  173.         .btn {
  174.             flex: 1;
  175.             padding: 9px;
  176.             border: none;
  177.             border-radius: 8px;
  178.             font-size: 13px;
  179.             font-weight: 600;
  180.             cursor: pointer;
  181.             transition: all 0.3s ease;
  182.             text-align: center;
  183.             text-decoration: none;
  184.             display: inline-block;
  185.             letter-spacing: 0.5px;
  186.         }
  187.         
  188.         .btn-primary {
  189.             background: linear-gradient(135deg, #1976d2, #0d47a1);
  190.             color: white;
  191.             box-shadow: 0 2px 4px rgba(25, 118, 210, 0.2);
  192.         }
  193.         
  194.         .btn-primary:hover {
  195.             transform: translateY(-2px);
  196.             box-shadow: 0 4px 12px rgba(25, 118, 210, 0.3);
  197.             background: linear-gradient(135deg, #1e88e5, #1565c0);
  198.         }
  199.         
  200.         .btn-primary:active {
  201.             transform: translateY(0);
  202.         }
  203.         
  204.         .btn-secondary {
  205.             background: white;
  206.             color: #666;
  207.             border: 1.5px solid #e0e4e8;
  208.         }
  209.         
  210.         .btn-secondary:hover {
  211.             background: #f8f9fa;
  212.             border-color: #1976d2;
  213.             color: #1976d2;
  214.             transform: translateY(-2px);
  215.         }
  216.         
  217.         /* Alertes */
  218.         .alert {
  219.             border-radius: 8px;
  220.             padding: 10px 12px;
  221.             margin-bottom: 15px;
  222.             border: none;
  223.             font-size: 12px;
  224.             display: flex;
  225.             align-items: center;
  226.             gap: 8px;
  227.         }
  228.         
  229.         .alert-danger {
  230.             background: linear-gradient(135deg, #fee, #fdd);
  231.             color: #c33;
  232.             border-left: 4px solid #c33;
  233.         }
  234.         
  235.         .text-danger {
  236.             color: #e74c3c;
  237.         }
  238.         
  239.         .help-block {
  240.             font-size: 10px;
  241.             margin-top: 3px;
  242.             display: flex;
  243.             align-items: center;
  244.             gap: 4px;
  245.         }
  246.         
  247.         .help-block::before {
  248.             
  249.             font-size: 9px;
  250.         }
  251.         
  252.         /* Footer logos */
  253.         .footer-logos {
  254.             margin-top: 18px;
  255.             padding-top: 15px;
  256.             border-top: 1px solid #e8ecf1;
  257.             text-align: center;
  258.         }
  259.         
  260.         .footer-logos span {
  261.             display: block;
  262.             color: #95a5a6;
  263.             font-size: 10px;
  264.             margin-bottom: 10px;
  265.             font-weight: 500;
  266.         }
  267.         
  268.         .logos-container {
  269.             display: flex;
  270.             justify-content: center;
  271.             gap: 20px;
  272.             flex-wrap: wrap;
  273.             align-items: center;
  274.         }
  275.         
  276.         .logos-container a {
  277.             display: inline-block;
  278.             line-height: 0;
  279.             transition: transform 0.3s ease;
  280.         }
  281.         
  282.         .logos-container a:hover {
  283.             transform: translateY(-2px);
  284.         }
  285.         
  286.         .logos-container img {
  287.             width: 32px !important;
  288.             height: auto !important;
  289.             max-width: 32px;
  290.             opacity: 0.6;
  291.             transition: all 0.3s ease;
  292.             border-radius: 6px;
  293.             filter: grayscale(0.2);
  294.         }
  295.         
  296.         .logos-container img:hover {
  297.             opacity: 1;
  298.             filter: grayscale(0);
  299.         }
  300.         
  301.         /* Diaspora container */
  302.         .diaspora-container {
  303.             background: linear-gradient(135deg, #f8f9fa, #f1f3f5);
  304.             border-radius: 8px;
  305.             padding: 10px 12px;
  306.             margin-top: 5px;
  307.             border: 1px solid #e8ecf1;
  308.         }
  309.         
  310.         .diaspora-container label {
  311.             margin-bottom: 6px;
  312.             font-size: 11px;
  313.             color: #2c3e50;
  314.         }
  315.         
  316.         /* Ligne pour mot de passe */
  317.         .password-row {
  318.             display: flex;
  319.             gap: 12px;
  320.             margin-bottom: 14px;
  321.         }
  322.         
  323.         .password-col {
  324.             flex: 1;
  325.         }
  326.         
  327.         .password-col .form-group {
  328.             margin-bottom: 0;
  329.         }
  330.         
  331.         /* Champ avec icône */
  332.         .input-icon {
  333.             position: relative;
  334.         }
  335.         
  336.         /* Checkbox personnalisée */
  337.         .custom-checkbox {
  338.             display: flex;
  339.             align-items: center;
  340.             gap: 8px;
  341.             cursor: pointer;
  342.         }
  343.         
  344.         /* Responsive */
  345.         @media (max-width: 576px) {
  346.             .form-container {
  347.                 max-width: 100%;
  348.                 padding: 0 10px;
  349.             }
  350.             
  351.             .auth-box-w {
  352.                 padding: 20px;
  353.             }
  354.             
  355.             .buttons-group {
  356.                 flex-direction: column;
  357.                 gap: 10px;
  358.             }
  359.             
  360.             .phone-group {
  361.                 flex-direction: column;
  362.                 gap: 8px;
  363.             }
  364.             
  365.             .phone-prefix {
  366.                 width: 100%;
  367.             }
  368.             
  369.             .header-separator {
  370.                 flex-direction: column;
  371.                 gap: 10px;
  372.                 align-items: flex-start;
  373.             }
  374.             
  375.             .password-row {
  376.                 flex-direction: column;
  377.                 gap: 14px;
  378.             }
  379.         }
  380.     </style>
  381.     <div class="form-container">
  382.         {% if app.session.flashBag.has('danger') == true %}
  383.             <div class="alert alert-danger alert-dismissible fade show" role="alert">
  384.                 <span>⚠️</span>
  385.                 {% for message in app.session.flashbag.get('danger') %}
  386.                     <p class="text-center w-100 mb-0">{{ message }}</p>
  387.                 {% endfor %}
  388.                 <button type="button" class="close" data-dismiss="alert" aria-label="Close" style="margin-left: auto;">
  389.                     <span aria-hidden="true">&times;</span>
  390.                 </button>
  391.             </div>
  392.         {% endif %}
  393.         <div class="help-block form-text with-errors text-danger" style="margin-bottom: 10px;">{{ form_errors(form) | raw }}</div>
  394.         
  395.         <form id="form-register" action="{{ path('register_create_account') }}" method="post" novalidate>
  396.             {{ form_widget(form._token) }}
  397.             
  398.             <!-- Header avec Langue -->
  399.             <div class="header-separator">
  400.                 <h4 style="margin: 0; font-size: 16px; font-weight: 700;">📝 {{ 'title.new_user_account' | trans }}</h4>
  401.                 <div class="language-selector">
  402.                     <label>🌐 {{ 'Langue' | trans }}</label>
  403.                     <select id="language-select">
  404.                         <option value="fr">Français</option>
  405.                         <option value="en">English</option>
  406.                         <option value="es">Español</option>
  407.                     </select>
  408.                 </div>
  409.             </div>
  410.             
  411.             <!-- Type d'utilisateur -->
  412.             <div class="form-group">
  413.                 <label>👤 {{ "menu.user_type" | trans }} <span class="text-danger">*</span></label>
  414.                 {{ form_widget(form.typePerson, { 'attr': {'class': 'form-control', 'required': 'required'} }) }}
  415.                 <div class="help-block text-danger">{{ form_errors(form.typePerson) }}</div>
  416.             </div>
  417.             
  418.             <!-- Diaspora -->
  419.             <div id="diaspora" class="form-group" style="display: none;">
  420.                 <div class="diaspora-container">
  421.                     <label>🌍 {{ 'menu.diaspora' | trans }}</label>
  422.                     <div class="d-flex align-items-center">
  423.                         {{ form_widget(form.diaspora, { 'attr': {'class': 'form-check-input', 'style': 'margin-right: 8px; width: 16px; height: 16px; cursor: pointer;'} }) }}
  424.                         <span style="font-size: 12px;">{{ 'menu.diaspora_help' | trans | default('Êtes-vous membre de la diaspora ?') }}</span>
  425.                     </div>
  426.                     <div class="help-block text-danger">{{ form_errors(form.diaspora) }}</div>
  427.                 </div>
  428.             </div>
  429.             
  430.             <!-- Région / Pays -->
  431.             <div class="form-group">
  432.                 <label>📍 
  433.                     {% if struct_province_country_city == "true" %}
  434.                         {{ 'menu.region' | trans }}
  435.                     {% else %}
  436.                         {{ 'menu.country' | trans }}
  437.                     {% endif %}
  438.                     <span class="text-danger">*</span>
  439.                 </label>
  440.                 {% if struct_province_country_city == "true" %}
  441.                     {{ create_translated_select(form.region, { 'attr': {'class': 'form-control custom-select', 'id': 'userbundle_user_region'} }) }}
  442.                     <div class="help-block text-danger">{{ form_errors(form.region) }}</div>
  443.                 {% else %}
  444.                     {{ create_translated_select(form.country, { 'attr': {'class': 'form-control custom-select', 'id': 'userbundle_user_country'} }) }}
  445.                     <div class="help-block text-danger">{{ form_errors(form.country) }}</div>
  446.                 {% endif %}
  447.             </div>
  448.             
  449.             <!-- Pays de résidence -->
  450.             <div id="residenceCountry" class="form-group" style="display: none">
  451.                 <label>🏠 {{ 'menu.residence_country' | trans }} <span class="text-danger">*</span></label>
  452.                 {% if struct_province_country_city == "true" %}
  453.                     {{ create_translated_select(form.residenceRegion, { 'attr': {'class': 'form-control custom-select', 'id': 'residence-select'} }) }}
  454.                     <div class="help-block text-danger">{{ form_errors(form.residenceRegion) }}</div>
  455.                 {% else %}
  456.                     {{ create_translated_select(form.residenceCountry, { 'attr': {'class': 'form-control custom-select', 'id': 'residence-select'} }) }}
  457.                     <div class="help-block text-danger">{{ form_errors(form.residenceCountry) }}</div>
  458.                 {% endif %}
  459.             </div>
  460.             
  461.             <!-- Téléphone -->
  462.             <div class="form-group">
  463.                 <label>📞 {{ 'menu.phone' | trans }} : {{ 'menu.phone_indicator' | trans }} (<span id="phone_code">+XX</span>) + N° <span class="text-danger">*</span></label>
  464.                 <div class="phone-group">
  465.                     <div class="phone-prefix">
  466.                         <input type="text" id="phone_prefix_display" class="form-control" value="+XX" readonly>
  467.                     </div>
  468.                     <div class="phone-number">
  469.                         {{ form_widget(form.phone, { 'attr': {'class': 'form-control', 'type': 'tel', 'required': 'required', 'placeholder': 'Numéro de téléphone'} }) }}
  470.                     </div>
  471.                 </div>
  472.                 <div class="phone-hint">{{ 'menu.phone_format_hint' | trans | default('Exemple: 612345678') }}</div>
  473.                 <div class="help-block text-danger">{{ form_errors(form.phone) }}</div>
  474.             </div>
  475.             
  476.             <!-- Champs optionnels cachés -->
  477.             <div class="form-group" style="display: none">
  478.                 <label>👤 Pseudo (Optionnel)</label>
  479.                 {{ form_widget(form.username, { 'attr': {'class': 'form-control'} }) }}
  480.             </div>
  481.             
  482.             <div class="form-group" style="display: none">
  483.                 <label>📧 Email (Optionnel)</label>
  484.                 {{ form_widget(form.email, { 'attr': {'class': 'form-control', 'type': 'email'} }) }}
  485.             </div>
  486.             
  487.             <!-- Mot de passe et Confirmation -->
  488.             <div class="password-row">
  489.                 <div class="password-col">
  490.                     <div class="form-group">
  491.                         <label>🔒 {{ "menu.password" | trans }} <span class="text-danger">*</span></label>
  492.                         {{ form_widget(form.plainPassword.first, { 'attr': {'class': 'form-control', 'type': 'password', 'required': 'required', 'minlength': '6', 'placeholder': '••••••'} }) }}
  493.                         <small class="phone-hint">Minimum 6 caractères</small>
  494.                         <div class="help-block text-danger">{{ form_errors(form.plainPassword.first) }}</div>
  495.                     </div>
  496.                 </div>
  497.                 <div class="password-col">
  498.                     <div class="form-group">
  499.                         <label>✓ {{ "menu.confirm" | trans }} <span class="text-danger">*</span></label>
  500.                         {{ form_widget(form.plainPassword.second, { 'attr': {'class': 'form-control', 'type': 'password', 'required': 'required', 'placeholder': '••••••'} }) }}
  501.                         <div class="help-block text-danger">{{ form_errors(form.plainPassword.second) }}</div>
  502.                     </div>
  503.                 </div>
  504.             </div>
  505.             
  506.             <!-- Ligne de séparation élégante -->
  507.             <div class="separator"></div>
  508.             
  509.             <!-- Boutons -->
  510.             <div class="buttons-group">
  511.                 <button type="submit" class="btn btn-primary" id="submitRegister">
  512.                     ✓ {{ 'menu.validate' | trans }}
  513.                 </button>
  514.                 <a href="{{ path('logout') }}" class="btn btn-secondary">
  515.                     ✗ {{ 'menu.cancel' | trans }}
  516.                 </a>
  517.             </div>
  518.             
  519.             <!-- Footer logos -->
  520.             <div class="footer-logos">
  521.                 <span>{{ "footer.dev_by" | trans }}</span>
  522.                 <div class="logos-container">
  523.                     <a target="_blank" href="https://ifef.francophonie.org/" rel="noopener noreferrer">
  524.                         <img src="{{ asset('build/images/pinsuprem/logo_ifef.jpg') }}" alt="IFEF" onerror="this.style.display='none'">
  525.                     </a>
  526.                     <a target="_blank" href="https://www.francophonie.org/" rel="noopener noreferrer">
  527.                         <img src="{{ asset('build/images/pinsuprem/logo_oif.jpg') }}" alt="OIF" onerror="this.style.display='none'">
  528.                     </a>
  529.                 </div>
  530.             </div>
  531.         </form>
  532.     </div>
  533.     
  534.     <select hidden id="indTel">
  535.         {% for country in allCountries %}
  536.             <option value="{{ country.id }}" data-code="{{ country.phoneCode }}">{{ country.phoneCode }}</option>
  537.         {% endfor %}
  538.     </select>
  539. {% endblock %}
  540. {% block fos_user_script %}
  541. <script>
  542. $(document).ready(function() {
  543.     $('#diaspora').hide();
  544.     $('#residenceCountry').hide();
  545.     
  546.     if ($('#userbundle_user_typePerson option:selected').val() == 'ROLE_DIPLOME') {
  547.         $('#diaspora').show();
  548.     }
  549.     
  550.     updatePhonePrefix();
  551.     
  552.     $('#userbundle_user_typePerson').on('change', function() {
  553.         if ($(this).val() == 'ROLE_DIPLOME') {
  554.             $('#diaspora').slideDown(200);
  555.         } else {
  556.             $('#userbundle_user_diaspora').prop('checked', false);
  557.             $('#diaspora').slideUp(200);
  558.             $('#residenceCountry').slideUp(200);
  559.             updatePhonePrefix();
  560.         }
  561.     });
  562.     
  563.     $('#userbundle_user_diaspora').on('change', function() {
  564.         if ($(this).is(':checked')) {
  565.             $('#residenceCountry').slideDown(200);
  566.             $('#residence-select').prop('required', true);
  567.         } else {
  568.             $('#residenceCountry').slideUp(200);
  569.             $('#residence-select').prop('required', false);
  570.             updatePhonePrefix();
  571.         }
  572.     });
  573.     
  574.     if ($('#userbundle_user_region').length) {
  575.         $('#userbundle_user_region').on('change', function() {
  576.             if(!$('#userbundle_user_diaspora').is(':checked')) {
  577.                 updatePhonePrefix();
  578.             }
  579.         });
  580.     }
  581.     
  582.     if ($('#userbundle_user_country').length) {
  583.         $('#userbundle_user_country').on('change', function() {
  584.             if(!$('#userbundle_user_diaspora').is(':checked')) {
  585.                 updatePhonePrefix();
  586.             }
  587.         });
  588.     }
  589.     
  590.     $('#residence-select').on('change', function() {
  591.         if($('#userbundle_user_diaspora').is(':checked')) {
  592.             updatePhonePrefix();
  593.         }
  594.     });
  595.     
  596.     function updatePhonePrefix() {
  597.         let selectedCountryId = null;
  598.         
  599.         if ($('#residenceCountry').is(':visible') && $('#residence-select').val()) {
  600.             selectedCountryId = $('#residence-select').val();
  601.         } else if ($('#userbundle_user_region').length && $('#userbundle_user_region').val()) {
  602.             selectedCountryId = $('#userbundle_user_region').val();
  603.         } else if ($('#userbundle_user_country').length && $('#userbundle_user_country').val()) {
  604.             selectedCountryId = $('#userbundle_user_country').val();
  605.         }
  606.         
  607.         if (selectedCountryId) {
  608.             let phoneCode = $('#indTel option[value="' + selectedCountryId + '"]').data('code');
  609.             if (phoneCode) {
  610.                 phoneCode = '+' + phoneCode;
  611.                 $('#phone_code').text(phoneCode);
  612.                 $('#phone_prefix_display').val(phoneCode);
  613.             } else {
  614.                 resetPhonePrefix();
  615.             }
  616.         } else {
  617.             resetPhonePrefix();
  618.         }
  619.     }
  620.     
  621.     function resetPhonePrefix() {
  622.         $('#phone_code').text('+XX');
  623.         $('#phone_prefix_display').val('+XX');
  624.     }
  625.     
  626.     $('#language-select').on('change', function() {
  627.         var lang = $(this).val();
  628.         console.log('Langue changée vers: ' + lang);
  629.     });
  630. });
  631. </script>
  632. {% endblock %}