mirror of
https://github.com/getnamingo/registry.git
synced 2025-07-31 23:06:10 +02:00
1160 lines
No EOL
67 KiB
Twig
1160 lines
No EOL
67 KiB
Twig
{% extends "layouts/app.twig" %}
|
|
|
|
{% block title %}{{ __('Create Domain') }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-wrapper">
|
|
<!-- Page header -->
|
|
<div class="page-header d-print-none">
|
|
<div class="container-xl">
|
|
<div class="row g-2 align-items-center">
|
|
<div class="col">
|
|
<!-- Page pre-title -->
|
|
<div class="page-pretitle">
|
|
{{ __('Overview') }}
|
|
</div>
|
|
<h2 class="page-title">
|
|
{{ __('Create Domain') }}
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Page body -->
|
|
<div class="page-body">
|
|
<div class="container-xl">
|
|
<div class="col-12">
|
|
{% include 'partials/flash.twig' %}
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form id="domainCreateForm" action="/domain/create" method="post">
|
|
{{ csrf.field | raw }}
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="domainName" class="form-label required">{{ __('Your Domain Name') }}</label>
|
|
<input type="text" class="form-control mb-2" placeholder="example.com" name="domainName" id="domainName" required="required" autocapitalize="none">
|
|
</div>
|
|
|
|
{% if registrars and not registrar %}
|
|
<div class="mb-3">
|
|
<label for="registrarDropdown" class="form-label required">{{ __('Select Registrar') }}</label>
|
|
<select id="registrarDropdown" name="registrar" class="form-select">
|
|
{% for registrar in registrars %}
|
|
<option value="{{ registrar.id }}">{{ registrar.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Slider for years -->
|
|
<div class="mb-3">
|
|
<label for="registrationYears" class="form-label">{{ __('Registration Years') }}</label>
|
|
<input type="range" class="form-range" min="1" max="10" step="1" id="registrationYears" name="registrationYears" value="1">
|
|
<span id="yearValue">1 Year</span>
|
|
</div>
|
|
|
|
<!-- Placeholder for displaying domain price -->
|
|
<div class="mb-3" id="domainPriceDisplay" style="display:none;">
|
|
<strong>{{ __('Estimated Price') }}: </strong><span id="domainPrice">{{ currency }} 0.00</span>
|
|
</div>
|
|
|
|
{% if minimum_data == 'false' %}<!-- Fields for 4 contacts with roles -->
|
|
<div class="mb-3">
|
|
<label for="contactRegistrant" class="form-label required">{{ __('Contacts') }}</label>
|
|
<div class="row g-2">
|
|
<div class="col">
|
|
<div class="input-group input-group-flat mb-2">
|
|
<input type="text" class="form-control" placeholder="{{ __('Registrant Contact') }}" name="contactRegistrant" id="contactRegistrant" required="required">
|
|
<span class="input-group-text">
|
|
<a href="#" class="link-secondary ms-2 open-modal" data-bs-toggle="modal" data-bs-target="#contactModal" data-input="contactRegistrant" title="{{ __('Assign Contact') }}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" /><path d="M21 21l-6 -6" /></svg>
|
|
</a>
|
|
<a href="#" class="link-secondary ms-2 open-add-modal" title="{{ __('Create Contact') }}" data-bs-toggle="modal" data-bs-target="#addContactModal" data-input="contactRegistrant">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5l0 14" /><path d="M5 12l14 0" /></svg>
|
|
</a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-auto align-self-center mb-2">
|
|
<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p class='mb-0'>{{ __('Enter a Contact ID if you know it. Use the magnifying glass to search or the plus icon to create a new contact (both open in new tabs). Then return and paste the Contact ID here.') }}</p>" data-bs-html="true">?</span>
|
|
</div>
|
|
</div>
|
|
<div class="row g-2">
|
|
<div class="col">
|
|
<div class="input-group input-group-flat mb-2">
|
|
<input type="text" class="form-control" placeholder="{{ __('Admin Contact') }}" name="contactAdmin" id="contactAdmin">
|
|
<span class="input-group-text">
|
|
<a href="#" class="link-secondary ms-2 open-modal" data-bs-toggle="modal" data-bs-target="#contactModal" data-input="contactAdmin" title="{{ __('Assign Contact') }}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" /><path d="M21 21l-6 -6" /></svg>
|
|
</a>
|
|
<a href="#" class="link-secondary ms-2 open-add-modal" title="{{ __('Create Contact') }}" data-bs-toggle="modal" data-bs-target="#addContactModal" data-input="contactAdmin">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5l0 14" /><path d="M5 12l14 0" /></svg>
|
|
</a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-auto align-self-center mb-2">
|
|
<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p class='mb-0'>{{ __('Enter a Contact ID if you know it. Use the magnifying glass to search or the plus icon to create a new contact (both open in new tabs). Then return and paste the Contact ID here.') }}</p>" data-bs-html="true">?</span>
|
|
</div>
|
|
</div>
|
|
<div class="row g-2">
|
|
<div class="col">
|
|
<div class="input-group input-group-flat mb-2">
|
|
<input type="text" class="form-control" placeholder="{{ __('Tech Contact') }}" name="contactTech" id="contactTech">
|
|
<span class="input-group-text">
|
|
<a href="#" class="link-secondary ms-2 open-modal" data-bs-toggle="modal" data-bs-target="#contactModal" data-input="contactTech" title="{{ __('Assign Contact') }}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" /><path d="M21 21l-6 -6" /></svg>
|
|
</a>
|
|
<a href="#" class="link-secondary ms-2 open-add-modal" title="{{ __('Create Contact') }}" data-bs-toggle="modal" data-bs-target="#addContactModal" data-input="contactTech">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5l0 14" /><path d="M5 12l14 0" /></svg>
|
|
</a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-auto align-self-center mb-2">
|
|
<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p class='mb-0'>{{ __('Enter a Contact ID if you know it. Use the magnifying glass to search or the plus icon to create a new contact (both open in new tabs). Then return and paste the Contact ID here.') }}</p>" data-bs-html="true">?</span>
|
|
</div>
|
|
</div>
|
|
<div class="row g-2">
|
|
<div class="col">
|
|
<div class="input-group input-group-flat mb-2">
|
|
<input type="text" class="form-control" placeholder="{{ __('Billing Contact') }}" name="contactBilling" id="contactBilling">
|
|
<span class="input-group-text">
|
|
<a href="#" class="link-secondary ms-2 open-modal" data-bs-toggle="modal" data-bs-target="#contactModal" data-input="contactBilling" title="{{ __('Assign Contact') }}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" /><path d="M21 21l-6 -6" /></svg>
|
|
</a>
|
|
<a href="#" class="link-secondary ms-2 open-add-modal" title="{{ __('Create Contact') }}" data-bs-toggle="modal" data-bs-target="#addContactModal" data-input="contactBilling">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5l0 14" /><path d="M5 12l14 0" /></svg>
|
|
</a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-auto align-self-center mb-2">
|
|
<span class="form-help" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="<p class='mb-0'>{{ __('Enter a Contact ID if you know it. Use the magnifying glass to search or the plus icon to create a new contact (both open in new tabs). Then return and paste the Contact ID here.') }}</p>" data-bs-html="true">?</span>
|
|
</div>
|
|
</div>
|
|
|
|
</div>{% endif %}
|
|
|
|
<!-- AuthInfo -->
|
|
<div class="mb-3">
|
|
<label for="authInfo" class="form-label required">{{ __('Auth Info') }}</label>
|
|
<input type="text" class="form-control" id="authInfo" name="authInfo" required>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="col-md-6">
|
|
|
|
<!-- Fields for nameservers -->
|
|
<div id="nameserverFields">
|
|
<div class="form-label">{{ __('Nameservers') }} <button type="button" id="addNameserver" class="btn btn-success btn-sm mb-2">+</button> <button type="button" id="removeNameserver" class="btn btn-danger btn-sm mb-2">-</button></div>
|
|
|
|
<div class="nameserver-group mb-1 row">
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 1" name="nameserver[]" autocapitalize="none">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 1 - IPv4" name="nameserver_ipv4[]">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 1 - IPv6" name="nameserver_ipv6[]" autocapitalize="none">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="nameserver-group mb-1 row">
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 2" name="nameserver[]" autocapitalize="none">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 2 - IPv4" name="nameserver_ipv4[]">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<input type="text" class="form-control mb-1" placeholder="{{ __('Nameserver') }} 2 - IPv6" name="nameserver_ipv6[]" autocapitalize="none">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3 mt-3">
|
|
<div class="form-label">{{ __('Statuses') }}</div>
|
|
<div>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientHold]">
|
|
<span class="form-check-label">clientHold</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientRenewProhibited]">
|
|
<span class="form-check-label">clientRenewProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientTransferProhibited]">
|
|
<span class="form-check-label">clientTransferProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientDeleteProhibited]">
|
|
<span class="form-check-label">clientDeleteProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="clientStatuses[clientUpdateProhibited]">
|
|
<span class="form-check-label">clientUpdateProhibited</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{% if roles == 0 %}
|
|
<div class="mb-3 mt-3">
|
|
<div class="form-label">{{ __('Server Statuses') }}</div>
|
|
<div>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverHold]">
|
|
<span class="form-check-label">serverHold</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverRenewProhibited]">
|
|
<span class="form-check-label">serverRenewProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverTransferProhibited]">
|
|
<span class="form-check-label">serverTransferProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverDeleteProhibited]">
|
|
<span class="form-check-label">serverDeleteProhibited</span>
|
|
</label>
|
|
<label class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" name="serverStatuses[serverUpdateProhibited]">
|
|
<span class="form-check-label">serverUpdateProhibited</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mb-3">
|
|
<label for="token" class="form-label">{{ __('Allocation Token') }}</label>
|
|
<input type="text" class="form-control" placeholder="{{ __('Allocation token') }}" name="token" autocapitalize="none">
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DNSSEC Data with checkbox -->
|
|
<div class="mb-3 mt-2 form-check">
|
|
<input type="checkbox" class="form-check-input" id="addDnssec" name="addDnssec">
|
|
<label class="form-check-label" for="addDnssec">{{ __('Add DNSSEC Data') }}</label>
|
|
</div>
|
|
<div id="dnssecData" style="display: none;">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="dsKeyTag" class="form-label">{{ __('DS Record') }}</label>
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Key Tag') }}" name="dsKeyTag" id="dsKeyTag">
|
|
<select class="form-select mb-2" name="dsAlg">
|
|
<option value="" disabled selected>{{ __('Select Algorithm') }}</option>
|
|
<option value="8">RSA/SHA-256</option>
|
|
<option value="13">ECDSA Curve P-256 with SHA-256</option>
|
|
<option value="14">ECDSA Curve P-384 with SHA-384</option>
|
|
<option value="15">Ed25519</option>
|
|
<option value="16">Ed448</option>
|
|
</select>
|
|
<select class="form-select mb-2" name="dsDigestType">
|
|
<option value="" disabled selected>{{ __('Select Digest Type') }}</option>
|
|
<option value="2">SHA-256</option>
|
|
<option value="4">SHA-384</option>
|
|
</select>
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Digest') }}" name="dsDigest">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="dnskeyFlags" class="form-label">{{ __('DNSKEY Record') }}</label>
|
|
<input type="number" class="form-control mb-2" placeholder="{{ __('Flags') }}" name="dnskeyFlags" id="dnskeyFlags">
|
|
<input type="number" class="form-control mb-2" placeholder="{{ __('Protocol') }}" name="dnskeyProtocol" value="3" readonly> <!-- Protocol is typically set to 3 -->
|
|
<select class="form-select mb-2" name="dnskeyAlg">
|
|
<option value="" disabled selected>{{ __('Select Algorithm') }}</option>
|
|
<option value="8">RSA/SHA-256</option>
|
|
<option value="13">ECDSA Curve P-256 with SHA-256</option>
|
|
<option value="14">ECDSA Curve P-384 with SHA-384</option>
|
|
<option value="15">Ed25519</option>
|
|
<option value="16">Ed448</option>
|
|
</select>
|
|
<input type="text" class="form-control mb-2" placeholder="{{ __('Public Key') }}" name="dnskeyPubKey">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% if launch_phases == 'on' %}<hr />{% endif %}
|
|
</div>
|
|
|
|
{% if launch_phases == 'on' %}
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="phaseType" class="form-label">{{ __('Phase Type') }}</label>
|
|
<select class="form-select" id="phaseType" name="phaseType">
|
|
<option value="none">N/A</option>
|
|
<option value="sunrise">Sunrise</option>
|
|
<option value="landrush">Landrush</option>
|
|
<option value="claims">Claims</option>
|
|
<option value="open">Open</option>
|
|
<option value="custom">Custom</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="phaseName" class="form-label">{{ __('Phase Name') }}</label>
|
|
<input type="text" class="form-control" id="phaseName" name="phaseName" placeholder="Enter phase name">
|
|
<small class="form-hint">The "Phase name" field is required only if the "Type" is set to "Custom".</small>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">{{ __('Signed Mark Information') }}</label>
|
|
<textarea class="form-control" data-bs-toggle="autosize" name="smd" placeholder="{{ __('Paste SMD contents') }}…"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
|
|
<div class="mb-3">
|
|
<label for="noticeid" class="form-label">{{ __('Notice ID') }}</label>
|
|
<input type="text" class="form-control" name="noticeid">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="notafter" class="form-label">{{ __('Not After Date') }}</label>
|
|
<input type="datetime-local" class="form-control" name="notafter">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="accepted" class="form-label">{{ __('Accepted Date') }}</label>
|
|
<input type="datetime-local" class="form-control" name="accepted">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="row align-items-center">
|
|
<div class="col-auto">
|
|
<button type="submit" class="btn btn-primary">{{ __('Create Domain') }}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% include 'partials/footer.twig' %}
|
|
</div>
|
|
<div class="modal modal-blur fade" id="contactModal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">{{ __('Choose Contact to Assign') }}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-4 text-secondary">
|
|
{{ __('Search') }}:
|
|
<div class="ms-2 d-inline-block">
|
|
<input id="search-input" type="text" class="form-control" aria-label="{{ __('Search contacts') }}">
|
|
</div>
|
|
</div>
|
|
<div id="contactTable"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal modal-blur fade" id="addContactModal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">{{ __('Create Contact') }}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="addContactForm">
|
|
{{ csrf.field | raw }}
|
|
<div class="row">
|
|
<div id="addContactError" class="alert alert-important alert-danger alert-dismissible d-none" role="alert">
|
|
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
|
|
</div>
|
|
|
|
<!-- First Column: General & Internationalized Info -->
|
|
<div class="col-md-6">
|
|
<h5 class="card-title mb-3">{{ __('General & Internationalized Info') }}</h5>
|
|
|
|
<!-- Internationalized Name -->
|
|
<div class="mb-3">
|
|
<label for="intName" class="form-label required">{{ __('Name') }}</label>
|
|
<input type="text" class="form-control" id="intName" name="intName">
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseNameInt" name="disclose_name_int">
|
|
<span class="form-check-label" for="discloseNameInt">{{ __('Disclose in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
{% if registrars and not registrar %}
|
|
<div class="form-group mb-3">
|
|
<label for="registrarDropdown2" class="form-label required">{{ __('Select Registrar') }}</label>
|
|
<select id="registrarDropdown2" name="registrar" class="form-select" required="required">
|
|
{% for registrar in registrars %}
|
|
<option value="{{ registrar.id }}">{{ registrar.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Internationalized Organization -->
|
|
<div class="mb-3">
|
|
<label for="intOrg" class="form-label">{{ __('Organization') }}</label>
|
|
<input type="text" class="form-control" id="intOrg" name="org">
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseOrgInt" name="disclose_org_int">
|
|
<span class="form-check-label" for="discloseOrgInt">{{ __('Disclose in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Internationalized Address: Street Details -->
|
|
<div class="mb-3">
|
|
<label for="street1" class="form-label required">{{ __('Street') }} 1</label>
|
|
<input type="text" class="form-control" id="street1" name="street1">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="street2" class="form-label">{{ __('Street') }} 2</label>
|
|
<input type="text" class="form-control" id="street2" name="street2">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="street3" class="form-label">{{ __('Street') }} 3</label>
|
|
<input type="text" class="form-control" id="street3" name="street3">
|
|
</div>
|
|
|
|
<!-- Internationalized Address: City, SP, PC, CC -->
|
|
<div class="mb-3">
|
|
<label for="city" class="form-label required">{{ __('City') }}</label>
|
|
<input type="text" class="form-control" id="city" name="city">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="sp" class="form-label">{{ __('State/Province') }}</label>
|
|
<input type="text" class="form-control" id="sp" name="sp">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="pc" class="form-label">{{ __('Postal Code') }}</label>
|
|
<input type="text" class="form-control" id="pc" name="pc">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="cc" class="form-label required">{{ __('Country') }}</label>
|
|
<select class="form-select" id="cc" name="cc">
|
|
{% for country in countries %}
|
|
<option value="{{ country.alpha2|lower }}">{{ country.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseAddrInt" name="disclose_addr_int">
|
|
<span class="form-check-label" for="discloseAddrInt">{{ __('Disclose Address in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Second Column: Voice, Fax, Email, and other details -->
|
|
<div class="col-md-6">
|
|
<h5 class="card-title mb-3">{{ __('Contact Details') }}</h5>
|
|
|
|
<!-- Contact ID -->
|
|
<div class="mb-3">
|
|
<label for="contactid" class="form-label required">{{ __('Contact ID') }}</label>
|
|
<input type="text" class="form-control" id="contactid" name="contactid" required="required">
|
|
<small class="form-text text-muted">{{ __('Auto-generated ID for the contact') }}.</small>
|
|
</div>
|
|
|
|
<!-- Voice -->
|
|
<div class="mb-3">
|
|
<label for="voice" class="form-label required">{{ __('Voice') }}</label>
|
|
<input type="tel" class="form-control" id="voice" name="voice" required="required">
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseVoice" name="disclose_voice">
|
|
<span class="form-check-label" for="discloseVoice">{{ __('Disclose in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Fax -->
|
|
<div class="mb-3">
|
|
<label for="fax" class="form-label">{{ __('Fax') }}</label>
|
|
<input type="tel" class="form-control" id="fax" name="fax">
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseFax" name="disclose_fax">
|
|
<span class="form-check-label" for="discloseFax">{{ __('Disclose in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Email -->
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label required">{{ __('Email') }}</label>
|
|
<input type="text" class="form-control" id="email" name="email" required="required" autocapitalize="none">
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseEmail" name="disclose_email">
|
|
<span class="form-check-label" for="discloseEmail">{{ __('Disclose in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- AuthInfo for Contact -->
|
|
<div class="mb-3">
|
|
<label for="authInfoc" class="form-label required">{{ __('Contact AuthInfo') }}</label>
|
|
<input type="text" class="form-control" id="authInfoc" name="authInfoc" readonly>
|
|
<small class="form-text text-muted">{{ __('Auto-generated authentication information for the contact') }}.</small>
|
|
</div>
|
|
|
|
<!-- NIN - National Identification Number -->
|
|
<div class="mb-3">
|
|
<label for="nin" class="form-label">{{ __('NIN - National Identification Number') }}</label>
|
|
<input type="text" class="form-control" id="nin" name="nin">
|
|
</div>
|
|
|
|
<!-- Personal or Business Checkbox -->
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input type="checkbox" class="form-check-input" id="isBusiness" name="isBusiness">
|
|
<label class="form-check-label" for="isBusiness">{{ __('This is a Business Contact') }}</label>
|
|
</div>
|
|
<!-- You can invert the logic if you prefer the default to be 'Personal' instead of 'Business' -->
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Localized Info Checkbox -->
|
|
<div class="mb-3">
|
|
<label class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" id="toggleLoc">
|
|
<span class="form-check-label" for="toggleLoc">{{ __('Include Localized Info') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Hidden Localized Info Rows -->
|
|
<div class="row" id="localizedInfo" style="display: none;">
|
|
<!-- Localized Postal Info: First Column -->
|
|
<div class="col-md-6">
|
|
<h5 class="card-title mb-3">{{ __('Localized Postal Info: Personal Details') }}</h5>
|
|
|
|
<!-- Localized Name -->
|
|
<div class="mb-3">
|
|
<label for="locName" class="form-label">{{ __('Name') }}</label>
|
|
<input type="text" class="form-control" id="locName" name="locName">
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseNameLoc" name="disclose_name_loc">
|
|
<span class="form-check-label" for="discloseNameLoc">{{ __('Disclose in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Localized Organization -->
|
|
<div class="mb-3">
|
|
<label for="locOrg" class="form-label">{{ __('Organization') }}</label>
|
|
<input type="text" class="form-control" id="locOrg" name="locOrg">
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseOrgLoc" name="disclose_org_loc">
|
|
<span class="form-check-label" for="discloseOrgLoc">{{ __('Disclose in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Localized Street Details -->
|
|
<div class="mb-3">
|
|
<label for="locStreet1" class="form-label">{{ __('Street') }} 1</label>
|
|
<input type="text" class="form-control" id="locStreet1" name="locStreet1">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="locStreet2" class="form-label">{{ __('Street') }} 2</label>
|
|
<input type="text" class="form-control" id="locStreet2" name="locStreet2">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Localized Postal Info: Second Column -->
|
|
<div class="col-md-6">
|
|
<h5 class="card-title mb-3">{{ __('Localized Postal Info: Address Details') }}</h5>
|
|
|
|
<!-- Continued Localized Street Detail -->
|
|
<div class="mb-3">
|
|
<label for="locStreet3" class="form-label">{{ __('Street') }} 3</label>
|
|
<input type="text" class="form-control" id="locStreet3" name="locStreet3">
|
|
</div>
|
|
|
|
<!-- Localized City, SP, PC, CC -->
|
|
<div class="mb-3">
|
|
<label for="locCity" class="form-label">{{ __('City') }}</label>
|
|
<input type="text" class="form-control" id="locCity" name="locCity">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="locSP" class="form-label">{{ __('State/Province') }}</label>
|
|
<input type="text" class="form-control" id="locSP" name="locSP">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="locPC" class="form-label">{{ __('Postal Code') }}</label>
|
|
<input type="text" class="form-control" id="locPC" name="locPC">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="locCC" class="form-label">{{ __('Country') }}</label>
|
|
<select class="form-select" id="locCC" name="locCC">
|
|
{% for country in countries %}
|
|
<option value="{{ country.alpha2|lower }}">{{ country.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<label class="form-check form-switch mt-1">
|
|
<input class="form-check-input" type="checkbox" id="discloseAddrLoc" name="disclose_addr_loc">
|
|
<span class="form-check-label" for="discloseAddrLoc">{{ __('Disclose Address in WHOIS') }}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<a href="#" class="btn btn-link link-secondary btn-3" data-bs-dismiss="modal" id="cancelButton">{{ __('Close') }}</a>
|
|
<button type="submit" class="btn btn-primary">{{ __('Create Contact') }}</button></form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<link href="/assets/css/tabulator.min.css" rel="stylesheet">
|
|
<script src="/assets/js/tabulator.min.js" defer></script>
|
|
<script>
|
|
let addContactTargetInputId = null;
|
|
|
|
// Capture the target input when opening "Add Contact" modal
|
|
document.addEventListener("click", function (e) {
|
|
let openBtn = e.target.closest('.open-add-modal');
|
|
if (openBtn) {
|
|
addContactTargetInputId = openBtn.getAttribute('data-input');
|
|
}
|
|
});
|
|
|
|
document.getElementById("addContactForm").addEventListener("submit", function (e) {
|
|
e.preventDefault(); // Prevent full-page reload
|
|
|
|
let form = this;
|
|
let formData = new FormData(form);
|
|
|
|
fetch("/contact/create-api", {
|
|
method: "POST",
|
|
body: formData,
|
|
headers: {
|
|
"Accept": "application/json"
|
|
}
|
|
})
|
|
.then(response => {
|
|
return response.json().then(data => {
|
|
if (!response.ok) {
|
|
showErrorMessage(data.error || `Server Error (${response.status})`);
|
|
|
|
// Keep the modal open when there's an error
|
|
let modalElement = document.getElementById("addContactModal");
|
|
let modalInstance = bootstrap.Modal.getInstance(modalElement);
|
|
if (modalInstance) {
|
|
modalInstance._isShown = true;
|
|
}
|
|
throw new Error(data.error || `Server Error (${response.status})`);
|
|
}
|
|
return data;
|
|
});
|
|
})
|
|
.then(data => {
|
|
console.log("✅ Contact Added:", data);
|
|
|
|
if (data.identifier && addContactTargetInputId) {
|
|
let targetInput = document.getElementById(addContactTargetInputId);
|
|
if (targetInput) {
|
|
targetInput.value = data.identifier;
|
|
}
|
|
}
|
|
|
|
// ✅ Only close modal if form submission is successful
|
|
if (data.identifier) {
|
|
let modalElement = document.getElementById("addContactModal");
|
|
let modalInstance = bootstrap.Modal.getInstance(modalElement);
|
|
if (modalInstance) {
|
|
modalInstance.hide();
|
|
}
|
|
|
|
// Reset the form and clear errors after success
|
|
form.reset();
|
|
addContactTargetInputId = null;
|
|
document.getElementById("addContactError").classList.add("d-none");
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("❌ Error adding contact:", error.message);
|
|
showErrorMessage(error.message);
|
|
|
|
refreshCsrfToken();
|
|
|
|
// ❌ Prevent modal from closing on error
|
|
let modalElement = document.getElementById("addContactModal");
|
|
let modalInstance = bootstrap.Modal.getInstance(modalElement);
|
|
if (modalInstance) {
|
|
modalInstance._isShown = true; // Keep modal open
|
|
}
|
|
});
|
|
});
|
|
|
|
// Function to show error messages inside the modal
|
|
function showErrorMessage(message) {
|
|
let errorContainer = document.getElementById("addContactError");
|
|
let submitButton = document.querySelector("#addContactModal button[type='submit']");
|
|
|
|
if (!errorContainer) {
|
|
let form = document.getElementById("addContactForm");
|
|
errorContainer = document.createElement("div");
|
|
errorContainer.id = "addContactError";
|
|
errorContainer.className = "alert alert-danger mt-2";
|
|
form.prepend(errorContainer);
|
|
}
|
|
|
|
errorContainer.innerText = message;
|
|
errorContainer.classList.remove("d-none"); // Ensure it's visible
|
|
|
|
// 🔹 Scroll to top of modal when error occurs
|
|
let modalBody = document.querySelector("#addContactModal .modal-body");
|
|
if (modalBody) {
|
|
modalBody.scrollTop = 0;
|
|
}
|
|
|
|
}
|
|
|
|
let targetInputId = null;
|
|
|
|
// Ensure target input is set correctly before the modal opens
|
|
document.addEventListener("mousedown", function(e) {
|
|
let openBtn = e.target.closest('.open-modal');
|
|
if (openBtn) {
|
|
targetInputId = openBtn.getAttribute('data-input');
|
|
}
|
|
});
|
|
|
|
// Handle click on "Add to Main" button
|
|
document.addEventListener("click", function(e) {
|
|
let addBtn = e.target.closest('.add-to-main');
|
|
if (!addBtn) return;
|
|
|
|
if (!targetInputId) {
|
|
return;
|
|
}
|
|
|
|
let identifier = addBtn.getAttribute('data-identifier');
|
|
|
|
let targetInput = document.getElementById(targetInputId);
|
|
if (targetInput) {
|
|
targetInput.value = identifier;
|
|
}
|
|
|
|
// Close the modal
|
|
let modalElement = document.getElementById("contactModal");
|
|
if (modalElement) {
|
|
let modalInstance = bootstrap.Modal.getInstance(modalElement);
|
|
if (modalInstance) {
|
|
modalInstance.hide();
|
|
}
|
|
}
|
|
|
|
// Reset input selection after choosing a contact
|
|
targetInputId = null;
|
|
});
|
|
|
|
function generateAuthInfoC() {
|
|
const length = 16;
|
|
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
let retVal = "";
|
|
let digitCount = 0;
|
|
|
|
// Generate initial random string
|
|
for (let i = 0; i < length; i++) {
|
|
const randomIndex = Math.floor(Math.random() * charset.length);
|
|
const char = charset.charAt(randomIndex);
|
|
retVal += char;
|
|
if (char >= '0' && char <= '9') {
|
|
digitCount++;
|
|
}
|
|
}
|
|
|
|
// Ensure there are at least two digits in the string
|
|
while (digitCount < 2) {
|
|
// Replace a non-digit character at a random position with a digit
|
|
const replacePosition = Math.floor(Math.random() * length);
|
|
if (!(retVal[replacePosition] >= '0' && retVal[replacePosition] <= '9')) {
|
|
const randomDigit = Math.floor(Math.random() * 10); // Generate a digit from 0 to 9
|
|
retVal = retVal.substring(0, replacePosition) + randomDigit + retVal.substring(replacePosition + 1);
|
|
digitCount++;
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
async function refreshCsrfToken() {
|
|
try {
|
|
let response = await fetch('/token-well', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error("Failed to fetch CSRF token:", response.status);
|
|
return;
|
|
}
|
|
|
|
let data = await response.json();
|
|
let csrfNameField = document.querySelector('#addContactModal input[name="csrf_name"]');
|
|
let csrfValueField = document.querySelector('#addContactModal input[name="csrf_value"]');
|
|
|
|
if (csrfNameField && csrfValueField) {
|
|
csrfNameField.value = Object.keys(data)[0]; // Set the token name
|
|
csrfValueField.value = Object.values(data)[0]; // Set the token value
|
|
}
|
|
} catch (error) {
|
|
console.error("Error refreshing CSRF token:", error);
|
|
}
|
|
}
|
|
|
|
document.getElementById("addContactModal").addEventListener("show.bs.modal", function () {
|
|
document.getElementById('contactid').value = generateAuthInfoC();
|
|
document.getElementById('authInfoc').value = generateAuthInfoC();
|
|
});
|
|
|
|
var table;
|
|
|
|
document.addEventListener("DOMContentLoaded", function(){
|
|
|
|
// Only refresh CSRF token when opening the "Add Contact" modal
|
|
let addContactModal = document.getElementById("addContactModal");
|
|
if (addContactModal) {
|
|
addContactModal.addEventListener("show.bs.modal", refreshCsrfToken);
|
|
}
|
|
|
|
const toggleLocCheckbox = document.getElementById('toggleLoc');
|
|
const localizedSection = document.getElementById('localizedInfo');
|
|
|
|
toggleLocCheckbox.addEventListener('change', function() {
|
|
if (toggleLocCheckbox.checked) {
|
|
localizedSection.style.display = "flex";
|
|
} else {
|
|
localizedSection.style.display = "none";
|
|
}
|
|
});
|
|
|
|
// Generate ID for Contact
|
|
const contactidInput = document.getElementById('contactid');
|
|
contactidInput.value = generateAuthInfoC();
|
|
|
|
// Generate authInfo for Contact
|
|
const authInfoInput = document.getElementById('authInfoc');
|
|
authInfoInput.value = generateAuthInfoC();
|
|
|
|
function contactLinkFormatter(cell){
|
|
var value = cell.getValue();
|
|
return `<a href="/contact/view/${value}" style="font-weight:bold;">${value}</a>`;
|
|
}
|
|
|
|
function actionsFormatter(cell, formatterParams, onRendered) {
|
|
return `
|
|
<button class="btn btn-outline-success btn-icon add-to-main" data-identifier="${cell.getRow().getData().identifier}" title="{{ __('Assign Contact') }}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
<path d="M12 5v14m-7 -7h14"></path>
|
|
</svg>
|
|
</button>
|
|
`;
|
|
}
|
|
|
|
var searchTerm = ""; // global variable to hold the search term
|
|
|
|
function updateSearchTerm(term) {
|
|
searchTerm = term;
|
|
table.replaceData();
|
|
}
|
|
|
|
table = new Tabulator("#contactTable", {
|
|
pagination: true,
|
|
paginationMode: "remote",
|
|
paginationSize: 10,
|
|
height:400,
|
|
paginationSizeSelector:[10, 25, 50, 100],
|
|
clipboard:true,
|
|
clipboardPasteAction:"replace",
|
|
sortMode: "remote",
|
|
ajaxURL: "/api/records/contact",
|
|
ajaxURLGenerator: function(url, config, params) {
|
|
var queryParts = [];
|
|
|
|
// Handle search term
|
|
if (searchTerm) {
|
|
queryParts.push("filter1=identifier,cs," + encodeURIComponent(searchTerm));
|
|
queryParts.push("filter2=email,cs," + encodeURIComponent(searchTerm));
|
|
queryParts.push("filter3=voice,cs," + encodeURIComponent(searchTerm));
|
|
queryParts.push("filter4=crdate,cs," + encodeURIComponent(searchTerm));
|
|
}
|
|
|
|
// Handle sorting from Tabulator
|
|
if (params.sort && params.sort.length > 0) {
|
|
var sorter = params.sort[0]; // single-column sorting
|
|
var sortField = encodeURIComponent(sorter.field);
|
|
var sortDir = (sorter.dir === "asc" ? "asc" : "desc");
|
|
queryParts.push("order=" + sortField + "," + sortDir);
|
|
} else {
|
|
// fallback default order if no sorters
|
|
queryParts.push("order=id,desc");
|
|
}
|
|
|
|
// Include pagination parameters
|
|
if (params.page) {
|
|
queryParts.push("page=" + params.page + "," + params.size);
|
|
}
|
|
|
|
return url + "?" + queryParts.join("&");
|
|
},
|
|
ajaxResponse: function(url, params, response) {
|
|
if (response && Array.isArray(response.records) && typeof response.results === 'number') {
|
|
var lastPage = Math.ceil(response.results / this.options.paginationSize);
|
|
return {
|
|
last_page: lastPage, // Calculated total number of pages
|
|
data: response.records, // Data for the current page
|
|
};
|
|
} else {
|
|
console.error('Unexpected response format', response);
|
|
return { last_page: 1, data: [] };
|
|
}
|
|
},
|
|
layout:"fitColumns",
|
|
responsiveLayout: "collapse",
|
|
responsiveLayoutCollapseStartOpen:false,
|
|
resizableColumns:false,
|
|
placeholder: "{{ __('No Data') }}",
|
|
columns:[
|
|
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
|
{title:"{{ __('Identifier') }}", field:"identifier", width:200, minWidth:150, resizable:false, headerSort:true, formatter: contactLinkFormatter, responsive:0},
|
|
{title:"{{ __('Email') }}", field:"email", minWidth:250, resizable:false, headerSort:true, responsive:2},
|
|
{title: "{{ __('Actions') }}", formatter: actionsFormatter, minWidth:80, resizable:false, headerSort:false, download:false, hozAlign: "center", responsive:0, cellClick: function(e, cell){
|
|
if (e.target.closest('.delete-btn')) {
|
|
e.preventDefault(); // Prevent the default link behavior
|
|
Swal.fire({
|
|
title: "{{ __('Are you sure you want to delete this contact?') }}",
|
|
showCancelButton: true,
|
|
confirmButtonText: "{{ __('Confirm') }}"
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
let deleteUrl = e.target.closest('.delete-btn').getAttribute('data-delete-url');
|
|
window.location.href = deleteUrl;
|
|
}
|
|
});
|
|
}
|
|
}},
|
|
]
|
|
});
|
|
var searchInput = document.getElementById("search-input");
|
|
let searchTimeout;
|
|
|
|
searchInput.addEventListener("input", function () {
|
|
clearTimeout(searchTimeout);
|
|
searchTimeout = setTimeout(() => {
|
|
updateSearchTerm(searchInput.value);
|
|
}, 300); // 300ms delay
|
|
});
|
|
});
|
|
</script>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
|
|
window.currencySymbol = "{{ currencySymbol }}";
|
|
window.currencyPosition = "{{ currencyPosition }}";
|
|
|
|
const yearSlider = document.getElementById('registrationYears');
|
|
const yearValueDisplay = document.getElementById('yearValue');
|
|
const addNameserverBtn = document.getElementById('addNameserver');
|
|
const removeNameserverBtn = document.getElementById('removeNameserver');
|
|
const nameserverFields = document.getElementById('nameserverFields');
|
|
const authInfoField = document.getElementById('authInfo');
|
|
const registrarDropdown = document.getElementById('registrarDropdown');
|
|
|
|
// Display year value from slider
|
|
yearSlider.addEventListener('input', function() {
|
|
yearValueDisplay.textContent = `${yearSlider.value} Year${yearSlider.value > 1 ? 's' : ''}`;
|
|
updatePrice(); // Call updatePrice() directly when slider moves
|
|
});
|
|
|
|
function createNameserverGroup(count) {
|
|
const group = document.createElement('div');
|
|
group.className = 'nameserver-group mb-1 row';
|
|
|
|
const nameserverCol = document.createElement('div');
|
|
nameserverCol.className = 'col-md-4';
|
|
const nameserverField = document.createElement('input');
|
|
nameserverField.type = 'text';
|
|
nameserverField.className = 'form-control mb-1';
|
|
nameserverField.placeholder = `{{ __('Nameserver') }} ${count}`;
|
|
nameserverField.name = `nameserver[]`;
|
|
nameserverCol.appendChild(nameserverField);
|
|
|
|
const ipv4Col = document.createElement('div');
|
|
ipv4Col.className = 'col-md-4';
|
|
const ipv4Field = document.createElement('input');
|
|
ipv4Field.type = 'text';
|
|
ipv4Field.className = 'form-control mb-1';
|
|
ipv4Field.placeholder = `{{ __('Nameserver') }} ${count} - IPv4`;
|
|
ipv4Field.name = `nameserver_ipv4[]`;
|
|
ipv4Col.appendChild(ipv4Field);
|
|
|
|
const ipv6Col = document.createElement('div');
|
|
ipv6Col.className = 'col-md-4';
|
|
const ipv6Field = document.createElement('input');
|
|
ipv6Field.type = 'text';
|
|
ipv6Field.className = 'form-control mb-1';
|
|
ipv6Field.placeholder = `{{ __('Nameserver') }} ${count} - IPv6`;
|
|
ipv6Field.name = `nameserver_ipv6[]`;
|
|
ipv6Col.appendChild(ipv6Field);
|
|
|
|
group.appendChild(nameserverCol);
|
|
group.appendChild(ipv4Col);
|
|
group.appendChild(ipv6Col);
|
|
|
|
return group;
|
|
}
|
|
|
|
// Add nameserver fields
|
|
let nameserverCount = 2;
|
|
addNameserverBtn.addEventListener('click', function() {
|
|
if (nameserverCount < 13) {
|
|
nameserverCount++;
|
|
const nameserverGroup = createNameserverGroup(nameserverCount);
|
|
nameserverFields.appendChild(nameserverGroup);
|
|
}
|
|
});
|
|
|
|
// Remove nameserver group
|
|
removeNameserverBtn.addEventListener('click', function() {
|
|
if (nameserverCount > 2) {
|
|
const lastGroup = nameserverFields.querySelector('.nameserver-group:last-child');
|
|
if (lastGroup) {
|
|
nameserverFields.removeChild(lastGroup);
|
|
nameserverCount--;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Generate random AuthInfo and set it to the field
|
|
function generateAuthInfo() {
|
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
let result = "";
|
|
for (let i = 0; i < 16; i++) {
|
|
result += charset.charAt(Math.floor(Math.random() * charset.length));
|
|
}
|
|
return result;
|
|
}
|
|
authInfoField.value = generateAuthInfo();
|
|
|
|
// Display DNSSEC data when the checkbox is ticked
|
|
document.getElementById('addDnssec').addEventListener('change', function() {
|
|
const dnssecData = document.getElementById('dnssecData');
|
|
if (this.checked) {
|
|
dnssecData.style.display = 'block';
|
|
} else {
|
|
dnssecData.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
const domainInput = document.getElementById('domainName');
|
|
const yearInput = document.getElementById('registrationYears');
|
|
const priceDisplay = document.getElementById('domainPriceDisplay');
|
|
const priceValue = document.getElementById('domainPrice');
|
|
|
|
function getDomainPrice(domain, years, registrarId) {
|
|
const currency = "{{ currency }}";
|
|
const apiUrl = `/dapi/domain/price?domain_name=${encodeURIComponent(domain)}&date_add=${years * 12}&command=create®istrar_id=${encodeURIComponent(registrarId)}¤cy=${encodeURIComponent(currency)}`;
|
|
|
|
return fetch(apiUrl)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// If the response is a raw number (e.g., 0.5), wrap it in an object
|
|
if (typeof data === "number") {
|
|
data = { price: data };
|
|
}
|
|
|
|
if (!data || typeof data !== "object" || !("price" in data)) {
|
|
console.error("Invalid API response structure:", data);
|
|
return Promise.reject("Invalid API response structure");
|
|
}
|
|
|
|
// Convert price to float safely
|
|
const price = parseFloat(data.price);
|
|
if (isNaN(price)) {
|
|
console.error("Invalid price received:", data.price);
|
|
return Promise.reject("Invalid price received");
|
|
}
|
|
|
|
return { price, type: data.type || "regular" };
|
|
})
|
|
.catch(error => {
|
|
console.error("Error fetching domain price:", error);
|
|
return Promise.reject("Error fetching domain price");
|
|
});
|
|
}
|
|
|
|
function formatPrice(price) {
|
|
switch(window.currencyPosition) {
|
|
case 'before':
|
|
return `${"{{ currency }}"} ${price.toFixed(2)}`;
|
|
case 'after':
|
|
return `${price.toFixed(2)} ${"{{ currency }}"} `;
|
|
default:
|
|
return price.toFixed(2);
|
|
}
|
|
}
|
|
|
|
function updatePrice() {
|
|
const domainValue = document.getElementById('domainName')?.value.trim() || "";
|
|
const registrarId = document.getElementById('registrarDropdown')?.value || "";
|
|
const years = parseInt(document.getElementById('registrationYears')?.value, 10) || 1;
|
|
|
|
if (domainValue) {
|
|
getDomainPrice(domainValue, years, registrarId).then(({ price, type }) => {
|
|
if (isNaN(price)) {
|
|
console.error("Invalid price received:", price);
|
|
priceValue.innerText = formatPrice(0.00);
|
|
return;
|
|
}
|
|
|
|
// Multiply price by years
|
|
const totalPrice = price * years;
|
|
|
|
priceValue.innerText = formatPrice(totalPrice);
|
|
priceDisplay.style.display = 'block';
|
|
|
|
// Remove existing color classes
|
|
priceValue.classList.remove('text-red', 'text-green', 'text-blue');
|
|
|
|
// Apply appropriate colors based on type
|
|
if (type === "promotion") {
|
|
priceValue.classList.add('text-green'); // Mark as promotion
|
|
priceDisplay.title = "Promotional Price";
|
|
} else if (type === "premium") {
|
|
priceValue.classList.add('text-red'); // Mark as premium
|
|
priceDisplay.title = "Premium Price";
|
|
} else {
|
|
priceValue.classList.add('text-blue'); // Default regular price
|
|
priceDisplay.title = "Regular Price";
|
|
}
|
|
|
|
}).catch(error => {
|
|
console.error("Error fetching price:", error);
|
|
priceDisplay.style.display = 'none';
|
|
});
|
|
} else {
|
|
priceDisplay.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
domainInput.addEventListener('input', updatePrice);
|
|
yearInput.addEventListener('input', updatePrice);
|
|
registrarDropdown.addEventListener('change', updatePrice);
|
|
yearSlider.addEventListener('input', updatePrice);
|
|
});
|
|
</script>
|
|
{% endblock %} |