Files
ODOO4projects.com/odoo-community-developers/admin/index.html
T
webmaster e067fec168 feat(freelancer-directory): full-text search, OCA/projects/language filters, standardised languages
- Search box: substring, case- and accent-insensitive, over Name, description,
  Focus, specialities and language names; combines with every filter (AND).
- Filters: OCA membership (any / member / not a member), Language (canonical
  names only), plus Country/Speciality; a project MINIMUM ('N+ projects'),
  rendered only when a threshold can narrow the list.
- Languages standardised: a closed ISO 639-1 checklist on both forms, with the
  proficiency as a SEPARATE axis (language_levels, 'Spanish:beginner'). A stored
  'Spanish - beginners' now maps to the language 'Spanish' and never becomes a
  filter option.
- Fix: .dev-card/.dir-filters set display, which beat the UA [hidden] rule, so
  filtered-out cards and the JS-off filter bar stayed visible.
- Deterministic order by Name (the endpoint's order alternates between reads).
- Card states plainly that a photo shows only when it is a link (the stored
  value is base64 and is not rendered); admin Image field says the same.
- specialities stays free text.
2026-11-03 12:57:51 +00:00

1060 lines
98 KiB
HTML

<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Freelancer records — admin</title>
<!-- DRAFT / INTERNAL: not indexed, not linked, not in sitemap.xml / llms.txt.
Reachable only by this URL. All five draft conditions hold
(noindex · not in sitemap · not in llms.txt · no inbound links · no announcement
surface). Do NOT link to this page from anywhere — not from the directory, not
from the footer. Remove this robots meta only if it ever becomes public. -->
<meta name="robots" content="noindex, nofollow" />
<link rel="canonical" href="https://odoo4projects.com/odoo-community-developers/admin/" />
<link rel="icon" href="/images/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Marcellus&display=swap"
media="print"
onload="this.media='all'"
/>
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Marcellus&display=swap" /></noscript>
<link rel="stylesheet" href="/style.css" />
<style>
/* ---- Freelancer records — admin (page-scoped). Internal page, never linked. ---- */
.adm-bar {
background: var(--midnight-2);
border-bottom: 1px solid var(--line-brass);
padding: 0.85rem 0;
}
.adm-bar .container {
display: flex;
align-items: baseline;
gap: 1rem;
flex-wrap: wrap;
}
.adm-brand {
font-family: Marcellus, Georgia, serif;
font-size: 1.15rem;
letter-spacing: 0.04em;
color: #fff;
}
.adm-brand span {
color: var(--brass);
}
.adm-tag {
font-size: 0.78rem;
letter-spacing: 0.05em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.65);
}
.adm-wrap {
padding: 2.5rem 0 4rem;
}
.adm-wrap h1 {
font-family: Marcellus, Georgia, serif;
font-weight: 400;
font-size: 1.9rem;
color: var(--midnight);
margin: 0 0 0.6rem;
}
.adm-note {
max-width: 720px;
margin: 0 0 1.75rem;
color: var(--ink-soft);
line-height: 1.7;
}
.adm-note code {
font-size: 0.85em;
color: var(--midnight);
}
.form-card {
max-width: 860px;
background: var(--ivory);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
padding: 1.5rem;
box-shadow: var(--shadow-rest);
}
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.form-field {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.form-field.full,
.form-grid > .full {
grid-column: 1 / -1;
}
.form-field label {
font-size: 0.8rem;
font-weight: 600;
color: var(--midnight);
}
.form-field .hint {
font-size: 0.78rem;
color: var(--ink-soft);
}
.form-field input,
.form-field textarea,
.form-field select {
font-family: Inter, sans-serif;
font-size: 0.9rem;
padding: 0.6rem 0.7rem;
border-radius: var(--radius);
border: 1px solid var(--line);
background: #fff;
color: var(--ink);
width: 100%;
}
/* The standardised language multi-select: a closed, scrollable checklist. The
level <select> appears beside a language only once it is checked — pure CSS. */
.lang-field {
min-width: 0;
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 0.85rem 0.9rem 0.6rem;
}
.lang-field legend {
font-size: 0.8rem;
font-weight: 600;
color: var(--midnight);
padding: 0 0.35rem;
}
.lang-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
max-height: 220px;
overflow: auto;
padding-right: 0.35rem;
}
.lang-pick {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.88rem;
color: var(--ink);
}
.lang-pick input {
width: auto;
margin: 0;
flex-shrink: 0;
}
.lang-pick .lang-level {
display: none;
width: auto;
max-width: 9rem;
padding: 0.25rem 0.4rem;
font-size: 0.8rem;
}
.lang-pick input:checked ~ .lang-level {
display: inline-block;
}
.form-check {
grid-column: 1 / -1;
display: flex;
align-items: flex-start;
gap: 0.6rem;
font-size: 0.85rem;
color: var(--ink-soft);
}
.form-check input {
width: auto;
margin-top: 0.2rem;
flex-shrink: 0;
}
.form-actions {
grid-column: 1 / -1;
display: flex;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
margin-top: 0.25rem;
}
.form-status {
font-size: 0.85rem;
color: var(--ink-soft);
}
.form-status[data-state="ok"] {
color: var(--success, #3f7d4f);
}
.form-status[data-state="err"] {
color: #b3452f;
}
.adm-loaded {
grid-column: 1 / -1;
margin: 0;
font-size: 0.85rem;
line-height: 1.6;
color: var(--ink-soft);
}
.adm-loaded[data-state="ok"] {
color: var(--success, #3f7d4f);
}
.adm-loaded[data-state="err"] {
color: #b3452f;
}
.adm-sep {
grid-column: 1 / -1;
width: 100%;
border: 0;
border-top: 1px solid var(--line);
margin: 0.35rem 0;
}
/* Page-local quiet button: the shared .btn-outline is cream-on-transparent (built
for the dark sections) and would be near-invisible on this page's ivory card. */
.btn-quiet {
background: transparent;
color: var(--ink);
border-color: rgba(23, 28, 38, 0.22);
}
.btn-quiet:hover {
color: var(--brass-deep);
border-color: var(--brass-deep);
background: rgba(201, 162, 39, 0.08);
}
.btn:focus-visible {
outline: 3px solid var(--brass-deep);
outline-offset: 2px;
border-radius: var(--radius);
}
@media (max-width: 768px) {
.form-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="adm-bar">
<div class="container">
<span class="adm-brand">ODOO4<span>projects</span></span>
<span class="adm-tag">Freelancer records — internal</span>
</div>
</div>
<main class="adm-wrap">
<div class="container">
<h1>Add or correct a freelancer record</h1>
<p class="adm-note">
Internal page — not linked from the directory. Add a new record, or load an existing one by
its id, <strong>confirm it is the right person</strong>, then correct what is wrong. A save
posts to the one <code>odoo_experts</code> webhook and is applied after email confirmation.
Fields you leave alone keep the record's current value.
</p>
<form class="form-card" id="adminForm" novalidate>
<div class="form-grid">
<div class="form-field">
<label for="adId">Record id <span class="hint">(leave blank to create a new record)</span></label>
<input id="adId" name="id" type="number" min="1" />
</div>
<div class="form-field">
<label for="adLoadBtn">Confirm before you edit</label>
<button type="button" class="btn btn-quiet" id="adLoadBtn">Load this record</button>
</div>
<p class="adm-loaded" id="adLoadInfo" role="status">Enter a record id and load it — you will see who it is before anything is written.</p>
<hr class="adm-sep" />
<div class="form-field">
<label for="adName">Name</label>
<input id="adName" name="Name" type="text" />
</div>
<div class="form-field">
<label for="adEmail">Email</label>
<input id="adEmail" name="email" type="email" />
</div>
<div class="form-field">
<label for="adClients">Projects on ODOO4projects</label>
<input id="adClients" name="odoo4projects_clients" type="number" min="0" />
<span class="hint">A number we record ourselves — not the freelancer's, not a rating.</span>
</div>
<div class="form-field">
<label for="adFocus">Focus</label>
<input id="adFocus" name="Focus" type="text" />
</div>
<div class="form-field full">
<label for="adDescription">Description</label>
<textarea id="adDescription" name="description" rows="4"></textarea>
</div>
<div class="form-field">
<label for="adCountry">Country</label>
<select id="adCountry" name="Country" required>
<!-- COUNTRY-OPTIONS-START -->
<option value="">— choose —</option>
<option value="AD">Andorra</option>
<option value="AE">United Arab Emirates</option>
<option value="AF">Afghanistan</option>
<option value="AG">Antigua and Barbuda</option>
<option value="AI">Anguilla</option>
<option value="AL">Albania</option>
<option value="AM">Armenia</option>
<option value="AO">Angola</option>
<option value="AQ">Antarctica</option>
<option value="AR">Argentina</option>
<option value="AS">American Samoa</option>
<option value="AT">Austria</option>
<option value="AU">Australia</option>
<option value="AW">Aruba</option>
<option value="AX">Aland</option>
<option value="AZ">Azerbaijan</option>
<option value="BA">Bosnia and Herzegovina</option>
<option value="BB">Barbados</option>
<option value="BD">Bangladesh</option>
<option value="BE">Belgium</option>
<option value="BF">Burkina Faso</option>
<option value="BG">Bulgaria</option>
<option value="BH">Bahrain</option>
<option value="BI">Burundi</option>
<option value="BJ">Benin</option>
<option value="BL">Saint Barthelemy</option>
<option value="BM">Bermuda</option>
<option value="BN">Brunei</option>
<option value="BO">Bolivia</option>
<option value="BQ">Bonaire</option>
<option value="BR">Brazil</option>
<option value="BS">Bahamas</option>
<option value="BT">Bhutan</option>
<option value="BV">Bouvet Island</option>
<option value="BW">Botswana</option>
<option value="BY">Belarus</option>
<option value="BZ">Belize</option>
<option value="CA">Canada</option>
<option value="CC">Cocos (Keeling) Islands</option>
<option value="CD">Democratic Republic of the Congo</option>
<option value="CF">Central African Republic</option>
<option value="CG">Republic of the Congo</option>
<option value="CH">Switzerland</option>
<option value="CI">Ivory Coast</option>
<option value="CK">Cook Islands</option>
<option value="CL">Chile</option>
<option value="CM">Cameroon</option>
<option value="CN">China</option>
<option value="CO">Colombia</option>
<option value="CR">Costa Rica</option>
<option value="CU">Cuba</option>
<option value="CV">Cape Verde</option>
<option value="CW">Curacao</option>
<option value="CX">Christmas Island</option>
<option value="CY">Cyprus</option>
<option value="CZ">Czech Republic</option>
<option value="DE">Germany</option>
<option value="DJ">Djibouti</option>
<option value="DK">Denmark</option>
<option value="DM">Dominica</option>
<option value="DO">Dominican Republic</option>
<option value="DZ">Algeria</option>
<option value="EC">Ecuador</option>
<option value="EE">Estonia</option>
<option value="EG">Egypt</option>
<option value="EH">Western Sahara</option>
<option value="ER">Eritrea</option>
<option value="ES">Spain</option>
<option value="ET">Ethiopia</option>
<option value="FI">Finland</option>
<option value="FJ">Fiji</option>
<option value="FK">Falkland Islands</option>
<option value="FM">Micronesia</option>
<option value="FO">Faroe Islands</option>
<option value="FR">France</option>
<option value="GA">Gabon</option>
<option value="GB">United Kingdom</option>
<option value="GD">Grenada</option>
<option value="GE">Georgia</option>
<option value="GF">French Guiana</option>
<option value="GG">Guernsey</option>
<option value="GH">Ghana</option>
<option value="GI">Gibraltar</option>
<option value="GL">Greenland</option>
<option value="GM">Gambia</option>
<option value="GN">Guinea</option>
<option value="GP">Guadeloupe</option>
<option value="GQ">Equatorial Guinea</option>
<option value="GR">Greece</option>
<option value="GS">South Georgia and the South Sandwich Islands</option>
<option value="GT">Guatemala</option>
<option value="GU">Guam</option>
<option value="GW">Guinea-Bissau</option>
<option value="GY">Guyana</option>
<option value="HK">Hong Kong</option>
<option value="HM">Heard Island and McDonald Islands</option>
<option value="HN">Honduras</option>
<option value="HR">Croatia</option>
<option value="HT">Haiti</option>
<option value="HU">Hungary</option>
<option value="ID">Indonesia</option>
<option value="IE">Ireland</option>
<option value="IL">Israel</option>
<option value="IM">Isle of Man</option>
<option value="IN">India</option>
<option value="IO">British Indian Ocean Territory</option>
<option value="IQ">Iraq</option>
<option value="IR">Iran</option>
<option value="IS">Iceland</option>
<option value="IT">Italy</option>
<option value="JE">Jersey</option>
<option value="JM">Jamaica</option>
<option value="JO">Jordan</option>
<option value="JP">Japan</option>
<option value="KE">Kenya</option>
<option value="KG">Kyrgyzstan</option>
<option value="KH">Cambodia</option>
<option value="KI">Kiribati</option>
<option value="KM">Comoros</option>
<option value="KN">Saint Kitts and Nevis</option>
<option value="KP">North Korea</option>
<option value="KR">South Korea</option>
<option value="KW">Kuwait</option>
<option value="KY">Cayman Islands</option>
<option value="KZ">Kazakhstan</option>
<option value="LA">Laos</option>
<option value="LB">Lebanon</option>
<option value="LC">Saint Lucia</option>
<option value="LI">Liechtenstein</option>
<option value="LK">Sri Lanka</option>
<option value="LR">Liberia</option>
<option value="LS">Lesotho</option>
<option value="LT">Lithuania</option>
<option value="LU">Luxembourg</option>
<option value="LV">Latvia</option>
<option value="LY">Libya</option>
<option value="MA">Morocco</option>
<option value="MC">Monaco</option>
<option value="MD">Moldova</option>
<option value="ME">Montenegro</option>
<option value="MF">Saint Martin</option>
<option value="MG">Madagascar</option>
<option value="MH">Marshall Islands</option>
<option value="MK">North Macedonia</option>
<option value="ML">Mali</option>
<option value="MM">Myanmar (Burma)</option>
<option value="MN">Mongolia</option>
<option value="MO">Macao</option>
<option value="MP">Northern Mariana Islands</option>
<option value="MQ">Martinique</option>
<option value="MR">Mauritania</option>
<option value="MS">Montserrat</option>
<option value="MT">Malta</option>
<option value="MU">Mauritius</option>
<option value="MV">Maldives</option>
<option value="MW">Malawi</option>
<option value="MX">Mexico</option>
<option value="MY">Malaysia</option>
<option value="MZ">Mozambique</option>
<option value="NA">Namibia</option>
<option value="NC">New Caledonia</option>
<option value="NE">Niger</option>
<option value="NF">Norfolk Island</option>
<option value="NG">Nigeria</option>
<option value="NI">Nicaragua</option>
<option value="NL">Netherlands</option>
<option value="NO">Norway</option>
<option value="NP">Nepal</option>
<option value="NR">Nauru</option>
<option value="NU">Niue</option>
<option value="NZ">New Zealand</option>
<option value="OM">Oman</option>
<option value="PA">Panama</option>
<option value="PE">Peru</option>
<option value="PF">French Polynesia</option>
<option value="PG">Papua New Guinea</option>
<option value="PH">Philippines</option>
<option value="PK">Pakistan</option>
<option value="PL">Poland</option>
<option value="PM">Saint Pierre and Miquelon</option>
<option value="PN">Pitcairn Islands</option>
<option value="PR">Puerto Rico</option>
<option value="PS">Palestine</option>
<option value="PT">Portugal</option>
<option value="PW">Palau</option>
<option value="PY">Paraguay</option>
<option value="QA">Qatar</option>
<option value="RE">Reunion</option>
<option value="RO">Romania</option>
<option value="RS">Serbia</option>
<option value="RU">Russia</option>
<option value="RW">Rwanda</option>
<option value="SA">Saudi Arabia</option>
<option value="SB">Solomon Islands</option>
<option value="SC">Seychelles</option>
<option value="SD">Sudan</option>
<option value="SE">Sweden</option>
<option value="SG">Singapore</option>
<option value="SH">Saint Helena</option>
<option value="SI">Slovenia</option>
<option value="SJ">Svalbard and Jan Mayen</option>
<option value="SK">Slovakia</option>
<option value="SL">Sierra Leone</option>
<option value="SM">San Marino</option>
<option value="SN">Senegal</option>
<option value="SO">Somalia</option>
<option value="SR">Suriname</option>
<option value="SS">South Sudan</option>
<option value="ST">Sao Tome and Principe</option>
<option value="SV">El Salvador</option>
<option value="SX">Sint Maarten</option>
<option value="SY">Syria</option>
<option value="SZ">Eswatini</option>
<option value="TC">Turks and Caicos Islands</option>
<option value="TD">Chad</option>
<option value="TF">French Southern Territories</option>
<option value="TG">Togo</option>
<option value="TH">Thailand</option>
<option value="TJ">Tajikistan</option>
<option value="TK">Tokelau</option>
<option value="TL">East Timor</option>
<option value="TM">Turkmenistan</option>
<option value="TN">Tunisia</option>
<option value="TO">Tonga</option>
<option value="TR">Turkey</option>
<option value="TT">Trinidad and Tobago</option>
<option value="TV">Tuvalu</option>
<option value="TW">Taiwan</option>
<option value="TZ">Tanzania</option>
<option value="UA">Ukraine</option>
<option value="UG">Uganda</option>
<option value="UM">U.S. Minor Outlying Islands</option>
<option value="US">United States</option>
<option value="UY">Uruguay</option>
<option value="UZ">Uzbekistan</option>
<option value="VA">Vatican City</option>
<option value="VC">Saint Vincent and the Grenadines</option>
<option value="VE">Venezuela</option>
<option value="VG">British Virgin Islands</option>
<option value="VI">U.S. Virgin Islands</option>
<option value="VN">Vietnam</option>
<option value="VU">Vanuatu</option>
<option value="WF">Wallis and Futuna</option>
<option value="WS">Samoa</option>
<option value="YE">Yemen</option>
<option value="YT">Mayotte</option>
<option value="ZA">South Africa</option>
<option value="ZM">Zambia</option>
<option value="ZW">Zimbabwe</option>
<!-- COUNTRY-OPTIONS-END -->
</select>
<span class="hint">Stored as the ISO code (e.g. DE).</span>
</div>
<div class="form-field">
<label for="adVersions">Odoo versions</label>
<input id="adVersions" name="odoo_versions" type="text" />
<span class="hint">Separate with commas.</span>
</div>
<fieldset class="form-field full lang-field">
<legend>Languages</legend>
<div class="lang-list" id="adLanguages">
<!-- LANGUAGE-OPTIONS-START -->
<span class="lang-pick"><input type="checkbox" id="ad-lang-afrikaans" name="languages" value="Afrikaans" /><label for="ad-lang-afrikaans">Afrikaans</label><select class="lang-level" name="language_level" data-language="Afrikaans" aria-label="Level in Afrikaans"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-amharic" name="languages" value="Amharic" /><label for="ad-lang-amharic">Amharic</label><select class="lang-level" name="language_level" data-language="Amharic" aria-label="Level in Amharic"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-arabic" name="languages" value="Arabic" /><label for="ad-lang-arabic">Arabic</label><select class="lang-level" name="language_level" data-language="Arabic" aria-label="Level in Arabic"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-aymara" name="languages" value="Aymara" /><label for="ad-lang-aymara">Aymara</label><select class="lang-level" name="language_level" data-language="Aymara" aria-label="Level in Aymara"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-azerbaijani" name="languages" value="Azerbaijani" /><label for="ad-lang-azerbaijani">Azerbaijani</label><select class="lang-level" name="language_level" data-language="Azerbaijani" aria-label="Level in Azerbaijani"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-belarusian" name="languages" value="Belarusian" /><label for="ad-lang-belarusian">Belarusian</label><select class="lang-level" name="language_level" data-language="Belarusian" aria-label="Level in Belarusian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-bulgarian" name="languages" value="Bulgarian" /><label for="ad-lang-bulgarian">Bulgarian</label><select class="lang-level" name="language_level" data-language="Bulgarian" aria-label="Level in Bulgarian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-bislama" name="languages" value="Bislama" /><label for="ad-lang-bislama">Bislama</label><select class="lang-level" name="language_level" data-language="Bislama" aria-label="Level in Bislama"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-bengali" name="languages" value="Bengali" /><label for="ad-lang-bengali">Bengali</label><select class="lang-level" name="language_level" data-language="Bengali" aria-label="Level in Bengali"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-bosnian" name="languages" value="Bosnian" /><label for="ad-lang-bosnian">Bosnian</label><select class="lang-level" name="language_level" data-language="Bosnian" aria-label="Level in Bosnian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-catalan" name="languages" value="Catalan" /><label for="ad-lang-catalan">Catalan</label><select class="lang-level" name="language_level" data-language="Catalan" aria-label="Level in Catalan"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-chamorro" name="languages" value="Chamorro" /><label for="ad-lang-chamorro">Chamorro</label><select class="lang-level" name="language_level" data-language="Chamorro" aria-label="Level in Chamorro"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-czech" name="languages" value="Czech" /><label for="ad-lang-czech">Czech</label><select class="lang-level" name="language_level" data-language="Czech" aria-label="Level in Czech"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-danish" name="languages" value="Danish" /><label for="ad-lang-danish">Danish</label><select class="lang-level" name="language_level" data-language="Danish" aria-label="Level in Danish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-german" name="languages" value="German" /><label for="ad-lang-german">German</label><select class="lang-level" name="language_level" data-language="German" aria-label="Level in German"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-divehi" name="languages" value="Divehi" /><label for="ad-lang-divehi">Divehi</label><select class="lang-level" name="language_level" data-language="Divehi" aria-label="Level in Divehi"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-dzongkha" name="languages" value="Dzongkha" /><label for="ad-lang-dzongkha">Dzongkha</label><select class="lang-level" name="language_level" data-language="Dzongkha" aria-label="Level in Dzongkha"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-greek" name="languages" value="Greek" /><label for="ad-lang-greek">Greek</label><select class="lang-level" name="language_level" data-language="Greek" aria-label="Level in Greek"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-english" name="languages" value="English" /><label for="ad-lang-english">English</label><select class="lang-level" name="language_level" data-language="English" aria-label="Level in English"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-spanish" name="languages" value="Spanish" /><label for="ad-lang-spanish">Spanish</label><select class="lang-level" name="language_level" data-language="Spanish" aria-label="Level in Spanish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-estonian" name="languages" value="Estonian" /><label for="ad-lang-estonian">Estonian</label><select class="lang-level" name="language_level" data-language="Estonian" aria-label="Level in Estonian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-basque" name="languages" value="Basque" /><label for="ad-lang-basque">Basque</label><select class="lang-level" name="language_level" data-language="Basque" aria-label="Level in Basque"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-persian" name="languages" value="Persian" /><label for="ad-lang-persian">Persian</label><select class="lang-level" name="language_level" data-language="Persian" aria-label="Level in Persian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-peul" name="languages" value="Peul" /><label for="ad-lang-peul">Peul</label><select class="lang-level" name="language_level" data-language="Peul" aria-label="Level in Peul"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-finnish" name="languages" value="Finnish" /><label for="ad-lang-finnish">Finnish</label><select class="lang-level" name="language_level" data-language="Finnish" aria-label="Level in Finnish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-fijian" name="languages" value="Fijian" /><label for="ad-lang-fijian">Fijian</label><select class="lang-level" name="language_level" data-language="Fijian" aria-label="Level in Fijian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-faroese" name="languages" value="Faroese" /><label for="ad-lang-faroese">Faroese</label><select class="lang-level" name="language_level" data-language="Faroese" aria-label="Level in Faroese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-french" name="languages" value="French" /><label for="ad-lang-french">French</label><select class="lang-level" name="language_level" data-language="French" aria-label="Level in French"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-irish" name="languages" value="Irish" /><label for="ad-lang-irish">Irish</label><select class="lang-level" name="language_level" data-language="Irish" aria-label="Level in Irish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-galician" name="languages" value="Galician" /><label for="ad-lang-galician">Galician</label><select class="lang-level" name="language_level" data-language="Galician" aria-label="Level in Galician"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-guarani" name="languages" value="Guarani" /><label for="ad-lang-guarani">Guarani</label><select class="lang-level" name="language_level" data-language="Guarani" aria-label="Level in Guarani"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-manx" name="languages" value="Manx" /><label for="ad-lang-manx">Manx</label><select class="lang-level" name="language_level" data-language="Manx" aria-label="Level in Manx"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-hebrew" name="languages" value="Hebrew" /><label for="ad-lang-hebrew">Hebrew</label><select class="lang-level" name="language_level" data-language="Hebrew" aria-label="Level in Hebrew"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-hindi" name="languages" value="Hindi" /><label for="ad-lang-hindi">Hindi</label><select class="lang-level" name="language_level" data-language="Hindi" aria-label="Level in Hindi"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-croatian" name="languages" value="Croatian" /><label for="ad-lang-croatian">Croatian</label><select class="lang-level" name="language_level" data-language="Croatian" aria-label="Level in Croatian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-haitian" name="languages" value="Haitian" /><label for="ad-lang-haitian">Haitian</label><select class="lang-level" name="language_level" data-language="Haitian" aria-label="Level in Haitian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-hungarian" name="languages" value="Hungarian" /><label for="ad-lang-hungarian">Hungarian</label><select class="lang-level" name="language_level" data-language="Hungarian" aria-label="Level in Hungarian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-armenian" name="languages" value="Armenian" /><label for="ad-lang-armenian">Armenian</label><select class="lang-level" name="language_level" data-language="Armenian" aria-label="Level in Armenian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-indonesian" name="languages" value="Indonesian" /><label for="ad-lang-indonesian">Indonesian</label><select class="lang-level" name="language_level" data-language="Indonesian" aria-label="Level in Indonesian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-icelandic" name="languages" value="Icelandic" /><label for="ad-lang-icelandic">Icelandic</label><select class="lang-level" name="language_level" data-language="Icelandic" aria-label="Level in Icelandic"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-italian" name="languages" value="Italian" /><label for="ad-lang-italian">Italian</label><select class="lang-level" name="language_level" data-language="Italian" aria-label="Level in Italian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-japanese" name="languages" value="Japanese" /><label for="ad-lang-japanese">Japanese</label><select class="lang-level" name="language_level" data-language="Japanese" aria-label="Level in Japanese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-georgian" name="languages" value="Georgian" /><label for="ad-lang-georgian">Georgian</label><select class="lang-level" name="language_level" data-language="Georgian" aria-label="Level in Georgian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-kongo" name="languages" value="Kongo" /><label for="ad-lang-kongo">Kongo</label><select class="lang-level" name="language_level" data-language="Kongo" aria-label="Level in Kongo"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-kazakh" name="languages" value="Kazakh" /><label for="ad-lang-kazakh">Kazakh</label><select class="lang-level" name="language_level" data-language="Kazakh" aria-label="Level in Kazakh"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-greenlandic" name="languages" value="Greenlandic" /><label for="ad-lang-greenlandic">Greenlandic</label><select class="lang-level" name="language_level" data-language="Greenlandic" aria-label="Level in Greenlandic"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-cambodian" name="languages" value="Cambodian" /><label for="ad-lang-cambodian">Cambodian</label><select class="lang-level" name="language_level" data-language="Cambodian" aria-label="Level in Cambodian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-korean" name="languages" value="Korean" /><label for="ad-lang-korean">Korean</label><select class="lang-level" name="language_level" data-language="Korean" aria-label="Level in Korean"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-kurdish" name="languages" value="Kurdish" /><label for="ad-lang-kurdish">Kurdish</label><select class="lang-level" name="language_level" data-language="Kurdish" aria-label="Level in Kurdish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-kyrgyz" name="languages" value="Kyrgyz" /><label for="ad-lang-kyrgyz">Kyrgyz</label><select class="lang-level" name="language_level" data-language="Kyrgyz" aria-label="Level in Kyrgyz"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-latin" name="languages" value="Latin" /><label for="ad-lang-latin">Latin</label><select class="lang-level" name="language_level" data-language="Latin" aria-label="Level in Latin"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-luxembourgish" name="languages" value="Luxembourgish" /><label for="ad-lang-luxembourgish">Luxembourgish</label><select class="lang-level" name="language_level" data-language="Luxembourgish" aria-label="Level in Luxembourgish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-lingala" name="languages" value="Lingala" /><label for="ad-lang-lingala">Lingala</label><select class="lang-level" name="language_level" data-language="Lingala" aria-label="Level in Lingala"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-laotian" name="languages" value="Laotian" /><label for="ad-lang-laotian">Laotian</label><select class="lang-level" name="language_level" data-language="Laotian" aria-label="Level in Laotian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-lithuanian" name="languages" value="Lithuanian" /><label for="ad-lang-lithuanian">Lithuanian</label><select class="lang-level" name="language_level" data-language="Lithuanian" aria-label="Level in Lithuanian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-luba-katanga" name="languages" value="Luba-Katanga" /><label for="ad-lang-luba-katanga">Luba-Katanga</label><select class="lang-level" name="language_level" data-language="Luba-Katanga" aria-label="Level in Luba-Katanga"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-latvian" name="languages" value="Latvian" /><label for="ad-lang-latvian">Latvian</label><select class="lang-level" name="language_level" data-language="Latvian" aria-label="Level in Latvian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-malagasy" name="languages" value="Malagasy" /><label for="ad-lang-malagasy">Malagasy</label><select class="lang-level" name="language_level" data-language="Malagasy" aria-label="Level in Malagasy"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-marshallese" name="languages" value="Marshallese" /><label for="ad-lang-marshallese">Marshallese</label><select class="lang-level" name="language_level" data-language="Marshallese" aria-label="Level in Marshallese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-maori" name="languages" value="Maori" /><label for="ad-lang-maori">Maori</label><select class="lang-level" name="language_level" data-language="Maori" aria-label="Level in Maori"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-macedonian" name="languages" value="Macedonian" /><label for="ad-lang-macedonian">Macedonian</label><select class="lang-level" name="language_level" data-language="Macedonian" aria-label="Level in Macedonian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-mongolian" name="languages" value="Mongolian" /><label for="ad-lang-mongolian">Mongolian</label><select class="lang-level" name="language_level" data-language="Mongolian" aria-label="Level in Mongolian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-malay" name="languages" value="Malay" /><label for="ad-lang-malay">Malay</label><select class="lang-level" name="language_level" data-language="Malay" aria-label="Level in Malay"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-maltese" name="languages" value="Maltese" /><label for="ad-lang-maltese">Maltese</label><select class="lang-level" name="language_level" data-language="Maltese" aria-label="Level in Maltese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-burmese" name="languages" value="Burmese" /><label for="ad-lang-burmese">Burmese</label><select class="lang-level" name="language_level" data-language="Burmese" aria-label="Level in Burmese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-nauruan" name="languages" value="Nauruan" /><label for="ad-lang-nauruan">Nauruan</label><select class="lang-level" name="language_level" data-language="Nauruan" aria-label="Level in Nauruan"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-norwegian-bokmål" name="languages" value="Norwegian Bokmål" /><label for="ad-lang-norwegian-bokmål">Norwegian Bokmål</label><select class="lang-level" name="language_level" data-language="Norwegian Bokmål" aria-label="Level in Norwegian Bokmål"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-north-ndebele" name="languages" value="North Ndebele" /><label for="ad-lang-north-ndebele">North Ndebele</label><select class="lang-level" name="language_level" data-language="North Ndebele" aria-label="Level in North Ndebele"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-nepali" name="languages" value="Nepali" /><label for="ad-lang-nepali">Nepali</label><select class="lang-level" name="language_level" data-language="Nepali" aria-label="Level in Nepali"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-dutch" name="languages" value="Dutch" /><label for="ad-lang-dutch">Dutch</label><select class="lang-level" name="language_level" data-language="Dutch" aria-label="Level in Dutch"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-norwegian-nynorsk" name="languages" value="Norwegian Nynorsk" /><label for="ad-lang-norwegian-nynorsk">Norwegian Nynorsk</label><select class="lang-level" name="language_level" data-language="Norwegian Nynorsk" aria-label="Level in Norwegian Nynorsk"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-norwegian" name="languages" value="Norwegian" /><label for="ad-lang-norwegian">Norwegian</label><select class="lang-level" name="language_level" data-language="Norwegian" aria-label="Level in Norwegian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-south-ndebele" name="languages" value="South Ndebele" /><label for="ad-lang-south-ndebele">South Ndebele</label><select class="lang-level" name="language_level" data-language="South Ndebele" aria-label="Level in South Ndebele"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-chichewa" name="languages" value="Chichewa" /><label for="ad-lang-chichewa">Chichewa</label><select class="lang-level" name="language_level" data-language="Chichewa" aria-label="Level in Chichewa"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-occitan" name="languages" value="Occitan" /><label for="ad-lang-occitan">Occitan</label><select class="lang-level" name="language_level" data-language="Occitan" aria-label="Level in Occitan"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-panjabi-/-punjabi" name="languages" value="Panjabi / Punjabi" /><label for="ad-lang-panjabi-/-punjabi">Panjabi / Punjabi</label><select class="lang-level" name="language_level" data-language="Panjabi / Punjabi" aria-label="Level in Panjabi / Punjabi"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-polish" name="languages" value="Polish" /><label for="ad-lang-polish">Polish</label><select class="lang-level" name="language_level" data-language="Polish" aria-label="Level in Polish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-pashto" name="languages" value="Pashto" /><label for="ad-lang-pashto">Pashto</label><select class="lang-level" name="language_level" data-language="Pashto" aria-label="Level in Pashto"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-portuguese" name="languages" value="Portuguese" /><label for="ad-lang-portuguese">Portuguese</label><select class="lang-level" name="language_level" data-language="Portuguese" aria-label="Level in Portuguese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-quechua" name="languages" value="Quechua" /><label for="ad-lang-quechua">Quechua</label><select class="lang-level" name="language_level" data-language="Quechua" aria-label="Level in Quechua"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-kirundi" name="languages" value="Kirundi" /><label for="ad-lang-kirundi">Kirundi</label><select class="lang-level" name="language_level" data-language="Kirundi" aria-label="Level in Kirundi"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-romanian" name="languages" value="Romanian" /><label for="ad-lang-romanian">Romanian</label><select class="lang-level" name="language_level" data-language="Romanian" aria-label="Level in Romanian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-russian" name="languages" value="Russian" /><label for="ad-lang-russian">Russian</label><select class="lang-level" name="language_level" data-language="Russian" aria-label="Level in Russian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-rwandi" name="languages" value="Rwandi" /><label for="ad-lang-rwandi">Rwandi</label><select class="lang-level" name="language_level" data-language="Rwandi" aria-label="Level in Rwandi"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-sango" name="languages" value="Sango" /><label for="ad-lang-sango">Sango</label><select class="lang-level" name="language_level" data-language="Sango" aria-label="Level in Sango"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-sinhalese" name="languages" value="Sinhalese" /><label for="ad-lang-sinhalese">Sinhalese</label><select class="lang-level" name="language_level" data-language="Sinhalese" aria-label="Level in Sinhalese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-slovak" name="languages" value="Slovak" /><label for="ad-lang-slovak">Slovak</label><select class="lang-level" name="language_level" data-language="Slovak" aria-label="Level in Slovak"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-slovenian" name="languages" value="Slovenian" /><label for="ad-lang-slovenian">Slovenian</label><select class="lang-level" name="language_level" data-language="Slovenian" aria-label="Level in Slovenian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-samoan" name="languages" value="Samoan" /><label for="ad-lang-samoan">Samoan</label><select class="lang-level" name="language_level" data-language="Samoan" aria-label="Level in Samoan"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-shona" name="languages" value="Shona" /><label for="ad-lang-shona">Shona</label><select class="lang-level" name="language_level" data-language="Shona" aria-label="Level in Shona"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-somalia" name="languages" value="Somalia" /><label for="ad-lang-somalia">Somalia</label><select class="lang-level" name="language_level" data-language="Somalia" aria-label="Level in Somalia"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-albanian" name="languages" value="Albanian" /><label for="ad-lang-albanian">Albanian</label><select class="lang-level" name="language_level" data-language="Albanian" aria-label="Level in Albanian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-serbian" name="languages" value="Serbian" /><label for="ad-lang-serbian">Serbian</label><select class="lang-level" name="language_level" data-language="Serbian" aria-label="Level in Serbian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-swati" name="languages" value="Swati" /><label for="ad-lang-swati">Swati</label><select class="lang-level" name="language_level" data-language="Swati" aria-label="Level in Swati"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-southern-sotho" name="languages" value="Southern Sotho" /><label for="ad-lang-southern-sotho">Southern Sotho</label><select class="lang-level" name="language_level" data-language="Southern Sotho" aria-label="Level in Southern Sotho"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-swedish" name="languages" value="Swedish" /><label for="ad-lang-swedish">Swedish</label><select class="lang-level" name="language_level" data-language="Swedish" aria-label="Level in Swedish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-swahili" name="languages" value="Swahili" /><label for="ad-lang-swahili">Swahili</label><select class="lang-level" name="language_level" data-language="Swahili" aria-label="Level in Swahili"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-tamil" name="languages" value="Tamil" /><label for="ad-lang-tamil">Tamil</label><select class="lang-level" name="language_level" data-language="Tamil" aria-label="Level in Tamil"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-tajik" name="languages" value="Tajik" /><label for="ad-lang-tajik">Tajik</label><select class="lang-level" name="language_level" data-language="Tajik" aria-label="Level in Tajik"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-thai" name="languages" value="Thai" /><label for="ad-lang-thai">Thai</label><select class="lang-level" name="language_level" data-language="Thai" aria-label="Level in Thai"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-tigrinya" name="languages" value="Tigrinya" /><label for="ad-lang-tigrinya">Tigrinya</label><select class="lang-level" name="language_level" data-language="Tigrinya" aria-label="Level in Tigrinya"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-turkmen" name="languages" value="Turkmen" /><label for="ad-lang-turkmen">Turkmen</label><select class="lang-level" name="language_level" data-language="Turkmen" aria-label="Level in Turkmen"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-tswana" name="languages" value="Tswana" /><label for="ad-lang-tswana">Tswana</label><select class="lang-level" name="language_level" data-language="Tswana" aria-label="Level in Tswana"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-tonga" name="languages" value="Tonga" /><label for="ad-lang-tonga">Tonga</label><select class="lang-level" name="language_level" data-language="Tonga" aria-label="Level in Tonga"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-turkish" name="languages" value="Turkish" /><label for="ad-lang-turkish">Turkish</label><select class="lang-level" name="language_level" data-language="Turkish" aria-label="Level in Turkish"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-tsonga" name="languages" value="Tsonga" /><label for="ad-lang-tsonga">Tsonga</label><select class="lang-level" name="language_level" data-language="Tsonga" aria-label="Level in Tsonga"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-ukrainian" name="languages" value="Ukrainian" /><label for="ad-lang-ukrainian">Ukrainian</label><select class="lang-level" name="language_level" data-language="Ukrainian" aria-label="Level in Ukrainian"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-urdu" name="languages" value="Urdu" /><label for="ad-lang-urdu">Urdu</label><select class="lang-level" name="language_level" data-language="Urdu" aria-label="Level in Urdu"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-uzbek" name="languages" value="Uzbek" /><label for="ad-lang-uzbek">Uzbek</label><select class="lang-level" name="language_level" data-language="Uzbek" aria-label="Level in Uzbek"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-venda" name="languages" value="Venda" /><label for="ad-lang-venda">Venda</label><select class="lang-level" name="language_level" data-language="Venda" aria-label="Level in Venda"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-vietnamese" name="languages" value="Vietnamese" /><label for="ad-lang-vietnamese">Vietnamese</label><select class="lang-level" name="language_level" data-language="Vietnamese" aria-label="Level in Vietnamese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-xhosa" name="languages" value="Xhosa" /><label for="ad-lang-xhosa">Xhosa</label><select class="lang-level" name="language_level" data-language="Xhosa" aria-label="Level in Xhosa"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-chinese" name="languages" value="Chinese" /><label for="ad-lang-chinese">Chinese</label><select class="lang-level" name="language_level" data-language="Chinese" aria-label="Level in Chinese"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<span class="lang-pick"><input type="checkbox" id="ad-lang-zulu" name="languages" value="Zulu" /><label for="ad-lang-zulu">Zulu</label><select class="lang-level" name="language_level" data-language="Zulu" aria-label="Level in Zulu"><option value="">—</option><option value="native">native</option><option value="fluent">fluent</option><option value="advanced">advanced</option><option value="intermediate">intermediate</option><option value="beginner">beginner</option></select></span>
<!-- LANGUAGE-OPTIONS-END -->
</div>
<span class="hint">Each language is stored by its standard name, so the directory filter never gets a name like “Spanish - beginners”. A level is optional.</span>
</fieldset>
<div class="form-field">
<label for="adSpecialities">Specialities</label>
<input id="adSpecialities" name="specialities" type="text" />
<span class="hint">Separate with commas.</span>
</div>
<div class="form-field">
<label for="adGithub">GitHub URL</label>
<input id="adGithub" name="github" type="text" inputmode="url" />
</div>
<div class="form-field">
<label for="adOca">OCA profile URL</label>
<input id="adOca" name="oca_profile" type="text" inputmode="url" />
</div>
<div class="form-field">
<label for="adLinkedin">LinkedIn URL</label>
<input id="adLinkedin" name="Linkedin" type="text" inputmode="url" />
</div>
<div class="form-field">
<label for="adUpwork">Upwork URL</label>
<input id="adUpwork" name="upwork" type="text" inputmode="url" />
</div>
<div class="form-field">
<label for="adFiverr">Fiverr URL</label>
<input id="adFiverr" name="fiverr" type="text" inputmode="url" />
</div>
<div class="form-field">
<label for="adImage">Image URL</label>
<input id="adImage" name="Image" type="text" inputmode="url" />
<span class="hint">A link (https://…). A photo shows on the card only when this is a link — an inline/base64 image is not displayed.</span>
</div>
<label class="form-check">
<input type="checkbox" name="OCA_member" value="1" />
<span>OCA member.</span>
</label>
<label class="form-check">
<input type="checkbox" name="is_individual" value="1" />
<span>Works as an individual.</span>
</label>
<div class="form-actions">
<button type="submit" class="btn btn-brass">Save this record</button>
<span class="form-status" id="adminStatus" role="status"></span>
</div>
</div>
</form>
</div>
</main>
<script>
/* ============================================================================
Freelancer records — admin page script.
ONE webhook, for everything. The freelancer platform is a SEPARATE system from
the CRM: there is no /crm, no /crm_odoo and nothing from the CRM skill here.
Write contract (same URL, id decides):
POST with an id → UPDATE that record
POST without an id → CREATE a new record
Email confirmation applies to both paths.
READ-MODIFY-WRITE (the reason this page loads the record first): a boolean the
owner did not touch must keep the record's CURRENT value. Loading the record
syncs the checkboxes to it, so an untouched box sends what the record already
holds instead of flipping it to false. An edit is refused until the record has
been loaded and confirmed — a bare numeric id can no longer edit the wrong person.
============================================================================ */
var ODOO_EXPERTS_WEBHOOK =
"https://n8n.odoo4projects.com/webhook/ODOO4projects/odoo_experts";
var MULTI_FIELDS = ["odoo_versions"]; // comma-separated → array
// `languages` and `specialities` are COMMA-SEPARATED STRINGS on the wire
// ("English,German"), NOT arrays. `odoo_versions` stays an array.
var CSV_STRING_FIELDS = ["languages", "specialities"];
var BOOL_FIELDS = ["OCA_member", "is_individual"];
var NUM_FIELDS = ["odoo4projects_clients"];
// Country VALUE FORMAT — what the Country select sends to the webhook.
// "code" → the ISO 3166-1 alpha-2 code, e.g. "DE" (default; stable, exact-matchable)
// "name" → the full English name, e.g. "Germany" (switch to this line if the
// workflow expects names instead of codes)
// The select always DISPLAYS the English name; this only changes the SUBMITTED value.
var COUNTRY_VALUE_FORMAT = "code";
var TEXT_FIELDS = [
"Name", "email", "Focus", "description", "Country",
"Linkedin", "upwork", "fiverr", "github", "oca_profile", "Image"
];
var loadedRecord = null;
function fieldElements(form, name) {
return Array.prototype.slice.call(
form.querySelectorAll('[name="' + name.replace(/"/g, '\\"') + '"]')
);
}
/* Lower-case and strip accents ("Zürich" -> "zurich"). */
function fold(text) {
return String(text == null ? "" : text)
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase();
}
/* Comma-separated tokens from any shape of field: a text box split on commas, or the
`languages` checklist read as its checked values. One wire shape either way. */
function collectCsvTokens(els) {
var out = [];
els.forEach(function (el) {
if (el.type === "checkbox" || el.type === "radio") {
if (el.checked && el.value) out.push(el.value.trim());
} else {
(el.value || "").split(",").forEach(function (part) { out.push(part.trim()); });
}
});
return out.filter(Boolean);
}
/* The Country value to submit: the ISO code by default, the English name when
COUNTRY_VALUE_FORMAT is "name". Empty until the owner picks a country. */
function countryValue(form) {
var sel = form.querySelector('select[name="Country"]');
if (!sel || !sel.value) return "";
if (COUNTRY_VALUE_FORMAT === "name") {
var opt = sel.options[sel.selectedIndex];
return opt ? opt.textContent.trim() : "";
}
return sel.value.trim();
}
function setStatus(el, message, state) {
if (!el) return;
el.textContent = message;
if (state) el.setAttribute("data-state", state);
else el.removeAttribute("data-state");
}
/* The webhook casing is inconsistent; read a field's value by its known names. */
function recordField(rec, name) {
if (rec[name] !== undefined) return rec[name];
if (name === "Country" && rec.country !== undefined) return rec.country;
return undefined;
}
function joinMulti(value) {
if (value == null) return "";
if (Array.isArray(value)) return value.join(", ");
return String(value);
}
/* Copy a loaded record into the form, so an untouched field carries the record's
current value through to the write. */
function populate(rec) {
var form = document.getElementById("adminForm");
TEXT_FIELDS.forEach(function (name) {
var el = form.querySelector('[name="' + name + '"]');
if (!el) return;
var v = recordField(rec, name);
el.value = v == null ? "" : String(v);
});
MULTI_FIELDS.forEach(function (name) {
var el = form.querySelector('[name="' + name + '"]');
if (el) el.value = joinMulti(recordField(rec, name));
});
var clients = form.querySelector('[name="odoo4projects_clients"]');
if (clients) {
var n = rec.odoo4projects_clients;
clients.value = n == null ? "" : String(n);
}
BOOL_FIELDS.forEach(function (name) {
var box = form.querySelector('[name="' + name + '"]');
if (!box) return;
var val = rec[name] === true;
box.checked = val;
box.dataset.loaded = val ? "true" : "false";
});
// Languages: the standardised checklist. A stored token ticks a box when it IS a
// standard name or begins with one ("Spanish - beginners" -> Spanish); its level,
// where `language_levels` records one, goes in the level select beside it.
var langs = String(recordField(rec, "languages") || "").split(",")
.map(function (s) { return s.trim(); }).filter(Boolean);
var levels = {};
String(recordField(rec, "language_levels") || "").split(",").forEach(function (pair) {
var bits = pair.split(":");
if (bits.length === 2) levels[fold(bits[0])] = bits[1].trim();
});
Array.prototype.forEach.call(form.querySelectorAll('input[name="languages"]'), function (box) {
var fb = fold(box.value);
var on = langs.some(function (t) {
var ft = fold(t);
return ft === fb || ft.indexOf(fb + " ") === 0 || ft.indexOf(fb + "-") === 0 || ft.indexOf(fb + "(") === 0;
});
box.checked = on;
var row = box.closest(".lang-pick");
var sel = row ? row.querySelector("select") : null;
if (sel) sel.value = on && levels[fb] ? levels[fb] : "";
});
}
function loadRecord() {
var form = document.getElementById("adminForm");
var info = document.getElementById("adLoadInfo");
var idRaw = form.querySelector('[name="id"]').value.trim();
loadedRecord = null;
if (!idRaw) {
setStatus(info, "Enter a record id first — or leave it blank to create a new record.", "err");
return;
}
setStatus(info, "Loading record " + idRaw + "…", null);
fetch(ODOO_EXPERTS_WEBHOOK, { method: "GET" })
.then(function (r) {
return r.json();
})
.then(function (payload) {
var list = Array.isArray(payload) ? payload : (payload && payload.data) || [];
var rec = null;
list.forEach(function (r) {
if (String(r.id) === String(idRaw)) rec = r;
});
if (!rec) {
setStatus(info, "No record with id " + idRaw + ". Nothing was loaded.", "err");
return;
}
loadedRecord = rec;
populate(rec);
var who = rec.Name ? rec.Name : "(no name)";
setStatus(
info,
"Loaded: " + who + " — id " + rec.id + ". Check this is the right person, then edit and save. Fields you leave alone keep their current value.",
"ok"
);
})
.catch(function () {
setStatus(info, "Could not read the record list. Try again.", "err");
});
}
function buildPayload(form, withId) {
var payload = {};
var names = [];
Array.prototype.forEach.call(form.querySelectorAll("[name]"), function (el) {
if (names.indexOf(el.name) === -1) names.push(el.name);
});
names.forEach(function (name) {
var els = fieldElements(form, name);
if (!els.length) return;
if (name === "id") {
if (!withId) return;
var raw = els[0].value.trim();
if (raw) payload.id = Number(raw);
return;
}
if (name === "Country") {
var countryVal = countryValue(form);
if (countryVal !== "") payload[name] = countryVal;
return;
}
if (MULTI_FIELDS.indexOf(name) !== -1) {
var text = els[0].value || "";
var list = text
.split(",")
.map(function (s) {
return s.trim();
})
.filter(Boolean);
if (list.length) payload[name] = list; // an empty box must not wipe the field
return;
}
if (name === "language_level") {
// Proficiency is a SEPARATE AXIS: a level rides in `language_levels`
// ("Spanish:beginner"), never inside a language name.
var pairs = [];
Array.prototype.forEach.call(
form.querySelectorAll('input[name="languages"]:checked'),
function (box) {
var row = box.closest(".lang-pick");
var sel = row ? row.querySelector("select") : null;
if (sel && sel.value) pairs.push(box.value + ":" + sel.value);
}
);
if (pairs.length) payload.language_levels = pairs.join(",");
return;
}
if (CSV_STRING_FIELDS.indexOf(name) !== -1) {
// Normalise to the wire shape: trim each entry, drop blanks, re-join. A text
// box is split on commas; the `languages` checklist is read as its checked
// values. Nothing selected → the field is omitted, so a save cannot wipe it.
var norm = collectCsvTokens(els).join(",");
if (norm) payload[name] = norm;
return;
}
if (BOOL_FIELDS.indexOf(name) !== -1) {
var box = els[0];
if (withId && box.dataset.loaded !== undefined) {
// READ-MODIFY-WRITE: an untouched box keeps the record's current value.
var original = box.dataset.loaded === "true";
var now = !!box.checked;
payload[name] = now === original ? original : now;
} else {
payload[name] = !!box.checked;
}
return;
}
if (NUM_FIELDS.indexOf(name) !== -1) {
var n = els[0].value.trim();
if (n !== "") payload[name] = Number(n);
return;
}
var value = els[0].value.trim();
if (value !== "") payload[name] = value;
});
return payload;
}
function postToWebhook(payload) {
return fetch(ODOO_EXPERTS_WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
}
(function init() {
var form = document.getElementById("adminForm");
if (!form) return;
var info = document.getElementById("adLoadInfo");
var status = document.getElementById("adminStatus");
document.getElementById("adLoadBtn").addEventListener("click", loadRecord);
// Changing the id after a load invalidates the confirmation.
form.querySelector('[name="id"]').addEventListener("input", function () {
if (loadedRecord && String(loadedRecord.id) !== this.value.trim()) {
loadedRecord = null;
setStatus(info, "The id changed — load the record again to confirm who you are editing.", "err");
}
});
form.addEventListener("submit", function (event) {
event.preventDefault();
if (!form.reportValidity()) return;
var idRaw = form.querySelector('[name="id"]').value.trim();
var hasId = !!idRaw;
if (hasId && (!loadedRecord || String(loadedRecord.id) !== idRaw)) {
setStatus(
status,
"Load the record first (Load this record) and confirm it is the right person. Nothing was saved.",
"err"
);
return;
}
var submit = form.querySelector('button[type="submit"]');
submit.disabled = true;
setStatus(status, "Saving…", null);
postToWebhook(buildPayload(form, true))
.then(function () {
setStatus(
status,
hasId
? "Saved. Check your inbox to confirm the change before it is applied."
: "New record sent. Check your inbox to confirm it before it goes live.",
"ok"
);
})
.catch(function () {
setStatus(status, "That did not save. Please try again.", "err");
})
.then(function () {
submit.disabled = false;
});
});
})();
</script>
</body>
</html>