1
1
{% extends " layouts/app.twig" %}
2
2
3
- {% block title %}{{ __(' Create New Order' ) }}{% endblock %}
3
+ {% block title %}{{ __(' Create Order' ) }}{% endblock %}
4
4
5
5
{% block content %}
6
- <style >
7
- .card.cursor-pointer :hover {
8
- border-color : #206bc4 ;
9
- box-shadow : 0 0 0 2px rgba (32 , 107 , 196 , 0.25 );
10
- }
11
- </style >
12
6
<div class =" page-wrapper" >
13
7
<!-- Page header -->
14
8
<div class =" page-header d-print-none" >
24
18
<a href =" {{route(' listOrders' )}}" >{{ __(' Orders' ) }}</a >
25
19
</li >
26
20
<li class =" breadcrumb-item active" >
27
- {{ __(' Create New Order' ) }}
21
+ {{ __(' Create Order' ) }}
28
22
</li >
29
23
</ol >
30
24
</div >
31
25
<h2 class =" page-title" >
32
- {{ __(' Create New Order' ) }}
26
+ {{ __(' Create Order' ) }}
33
27
</h2 >
34
28
</div >
35
29
</div >
38
32
<!-- Page body -->
39
33
<div class =" page-body" >
40
34
<div class =" container-xl" >
41
- <div class =" col-12" >
42
35
{% include ' partials/flash.twig' %}
43
- <div class =" card" >
44
- <div class =" card-body" >
45
- <form method =" post" action =" {{route(' createOrder' )}}" id =" createOrderForm" >
46
- {{ csrf .field | raw }}
47
-
48
- <div class =" row" >
49
- {% for provider in providers %}
50
- {% for code , product in provider .products %}
51
- {% if product .type == ' domain' %}
52
- <div class =" col-md-4 mb-4" >
53
- {% set safe_id = ' card-' ~ provider .id ~ ' -' ~ code | replace ({' .' : ' -' , ' :' : ' -' }) %}
54
- <label class =" card h-100 cursor-pointer position-relative" id =" {{ safe_id }}" onclick =" selectProduct('{{ provider .id }}', '{{ code }}', '{{ safe_id }}')" >
55
- <input type =" radio" name =" product_choice" class =" form-check-input position-absolute top-0 end-0 m-3 z-index-3" value =" {{ provider .id }}::{{ code }}" required >
56
- <div class =" card-status-start bg-primary d-none" ></div >
57
- <div class =" card-body" >
58
- <div class =" d-flex align-items-center justify-content-between mb-2" >
59
- <span class =" badge bg-blue text-blue-fg" >from {{ currency }} {{ product .register [' 1' ]| default (0 ) }}/{{ product .billing }}</span >
60
- </div >
61
- <h4 class =" card-title mb-0" >{{ product .label }}</h4 >
62
- <p class =" text-muted small mb-2" >{{ provider .name }} — {{ product .description }}</p >
63
- <div class =" text-end" >
64
- <span class =" badge bg-light text-light-fg" >Click to configure</span >
65
- </div >
66
- </div >
67
- </label >
68
- </div >
69
- {% endif %}
70
- {% endfor %}
71
- {% endfor %}
72
- </div >
73
36
74
- <div id =" productFields" class =" d-none" >
75
- <h4 >Order Details</h4 >
76
- <div class =" row g-3" id =" dynamicFields" >
77
- <!-- Dynamic domain/contact fields here -->
78
- </div >
79
- </div >
80
- </div >
81
- <div class =" card-footer" >
82
- <div class =" row align-items-center" >
83
- <div class =" col-auto" >
84
- <button type =" submit" class =" btn btn-primary" >{{ __(' Create Order' ) }}</button ></form >
37
+ {% if domainProducts | length > 0 %}
38
+ <div class =" card mb-4" >
39
+ <div class =" card-body" >
40
+ <h3 class =" card-title" >{{ __(' Find Your Perfect Domain' ) }}</h3 >
41
+ <p class =" text-muted" >{{ __(' Start your online journey by searching for an available domain.' ) }}</p >
42
+ <form class =" row g-2 align-items-stretch" onsubmit =" checkDomain(event)" >
43
+ <div class =" col-md-9" >
44
+ <input type =" text" id =" domainInput" class =" form-control form-control-lg rounded" placeholder =" example.com" required >
45
+ </div >
46
+ <div class =" col-md-3 d-grid" >
47
+ <button class =" btn btn-primary btn-lg w-100 rounded" type =" submit" >{{ __(' Check' ) }}</button >
48
+ </div >
49
+ </form >
50
+ <div id =" resultContainer" class =" mt-4" ></div >
51
+ </div >
52
+ </div >
53
+ <style >
54
+ input [name = " domain" ],
55
+ button [type = " submit" ] {
56
+ height : calc (2.875rem + 2px );
57
+ border-radius : 0.5rem ;
58
+ }
59
+ </style >
60
+ {% endif %}
61
+
62
+ {% if otherProducts | length > 0 %}
63
+ <h3 class =" mb-3" >Other Services</h3 >
64
+ <div class =" row" >
65
+ {% for item in otherProducts %}
66
+ {% set provider = item .provider %}
67
+ {% set product = item .product %}
68
+ <div class =" col-md-4 mb-4" >
69
+ <div class =" card h-100 cursor-pointer" onclick =" showProductOptions('{{ provider .id }}', '{{ product .label }}')" >
70
+ <div class =" card-body" >
71
+ <h4 class =" card-title" >{{ product .label }}</h4 >
72
+ <p class =" text-muted small" >{{ provider .name }} — {{ product .description }}</p >
73
+ <span class =" badge bg-blue text-blue-fg" >from {{ currency }} {{ product .price }}/{{ product .billing }}</span >
85
74
</div >
86
75
</div >
87
76
</div >
77
+ {% endfor %}
78
+ </div >
79
+ {% endif %}
80
+
81
+ <div id =" productOptions" class =" card mt-4 d-none" >
82
+ <div class =" card-body" >
83
+ <h4 >{{ __(' Configure Product' ) }}</h4 >
84
+ <form method =" post" action =" {{ route(' createOrder' ) }}" >
85
+ {{ csrf .field | raw }}
86
+ <input type =" hidden" name =" product_choice" id =" selectedProductChoice" >
87
+ <div class =" row g-3" id =" dynamicFields" ></div >
88
+ <div class =" mt-3" ><button class =" btn btn-primary" >{{ __(' Submit Order' ) }}</button ></div >
89
+ </form >
90
+ </div >
88
91
</div >
92
+
89
93
</div >
90
94
</div >
91
95
{% include ' partials/footer.twig' %}
92
96
</div >
93
97
<script >
94
- const productData = {{ providers | json_encode | raw }};
98
+ const productData = {{ otherProducts | json_encode | raw }};
95
99
const currency = {{ currency | json_encode | raw }};
96
100
97
- function selectProduct (providerId , productCode , cardId ) {
98
- document .querySelectorAll (' .card .card-status-start' ).forEach (el => el .classList .add (' d-none' ));
99
-
100
- const selectedCard = document .getElementById (cardId);
101
- if (selectedCard) {
102
- const statusEl = selectedCard .querySelector (' .card-status-start' );
103
- if (statusEl) statusEl .classList .remove (' d-none' );
104
- }
105
-
106
- document .querySelectorAll (' input[name="product_choice"]' ).forEach (r => r .checked = false );
107
- const inputEl = document .querySelector (` input[value="${ providerId} ::${ productCode} "]` );
108
- if (inputEl) inputEl .checked = true ;
109
-
110
- const provider = productData .find (p => p .id == providerId);
111
- const product = provider .products [productCode];
112
-
113
- document .getElementById (' productFields' ).classList .remove (' d-none' );
114
-
115
- const extra = document .getElementById (' dynamicFields' );
116
-
117
- if (product .type === ' domain' ) {
118
- const requiredFields = provider .credentials ? .required_fields || {};
119
-
120
- extra .innerHTML = `
121
- <div class =" col-md-8" >
122
- <label class =" form-label" >Domain Name</label >
123
- <div class =" input-group" >
124
- <input type =" text" name =" config[domain]" class =" form-control" placeholder =" yourname" >
125
- <span class =" input-group-text" >.com</span >
126
- </div >
127
- <small class =" form-hint" >Enter the domain name without TLD.</small >
128
- </div >
129
-
130
- <div class =" col-md-4" >
131
- <label class =" form-label" >Registration Period</label >
132
- <select name =" config[years]" class =" form-select" required >
133
- ${ Object .entries (product .register || {}).map (([year , price ]) =>
134
- ` <option value="${ year} ">${ year} year(s) (${ currency} ${ parseFloat (price).toFixed (2 )} )</option>` ).join (' ' )}
135
- </select >
136
- </div >
137
-
138
- <div class =" col-12" >
139
- <label class =" form-label" >Nameservers</label >
140
- <div class =" row g-2" >
141
- <div class =" col-md-4" ><input type =" text" class =" form-control" name =" config[nameservers][]" placeholder =" ns1.example.com" required ></div >
142
- <div class =" col-md-4" ><input type =" text" class =" form-control" name =" config[nameservers][]" placeholder =" ns2.example.com" required ></div >
143
- <div class =" col-md-4" ><input type =" text" class =" form-control" name =" config[nameservers][]" placeholder =" ns3.example.com (optional)" ></div >
144
- </div >
145
- </div >
146
-
147
- <div class =" col-12 mt-4" >
148
- <label class =" form-label" >Registrant Contact</label >
149
- <div class =" form-selectgroup" >
150
- <label class =" form-selectgroup-item" >
151
- <input type =" radio" name =" contact_mode" value =" default" class =" form-selectgroup-input" checked onchange =" toggleContactFields(false)" >
152
- <span class =" form-selectgroup-label" >Use Default Contact</span >
153
- </label >
154
- <label class =" form-selectgroup-item" >
155
- <input type =" radio" name =" contact_mode" value =" custom" class =" form-selectgroup-input" onchange =" toggleContactFields(true)" >
156
- <span class =" form-selectgroup-label" >Enter New Contact</span >
157
- </label >
158
- </div >
159
- </div >
160
-
161
- <div id =" contactFields" class =" row g-3 mt-3 d-none" >
162
- <div class =" col-md-6" >
163
- <label class =" form-label" >Full Name</label >
164
- <input type =" text" name =" config[contact][name]" class =" form-control" >
165
- </div >
166
- <div class =" col-md-6" >
167
- <label class =" form-label" >Email</label >
168
- <input type =" email" name =" config[contact][email]" class =" form-control" >
169
- </div >
101
+ function showProductOptions (providerId , productLabel ) {
102
+ const target = productData .find (p => p .provider .id == providerId && p .product .label == productLabel);
103
+ if (! target) return ;
104
+
105
+ const product = target .product ;
106
+ const fieldsDiv = document .getElementById (' dynamicFields' );
107
+ const choiceInput = document .getElementById (' selectedProductChoice' );
108
+
109
+ fieldsDiv .innerHTML = ' ' ;
110
+ choiceInput .value = ` ${ providerId} ::${ productLabel} ` ;
111
+ document .getElementById (' productOptions' ).classList .remove (' d-none' );
112
+
113
+ fieldsDiv .innerHTML += `
114
+ <div class =" col-md-6" >
115
+ <label class =" form-label" >Service Name</label >
116
+ <input type =" text" name =" config[name]" class =" form-control" required >
117
+ </div >
118
+ <div class =" col-md-6" >
119
+ <label class =" form-label" >Billing Cycle</label >
120
+ <select name =" config[cycle]" class =" form-select" required >
121
+ <option value =" monthly" >Monthly</option >
122
+ <option value =" yearly" selected >Yearly</option >
123
+ </select >
124
+ </div >
125
+ ` ;
126
+
127
+ if (product .fields ) {
128
+ for (const [field , def ] of Object .entries (product .fields )) {
129
+ fieldsDiv .innerHTML += `
170
130
<div class =" col-md-6" >
171
- <label class =" form-label" >Organization (optional)</label >
172
- <input type =" text" name =" config[contact][org]" class =" form-control" >
173
- </div >
174
- <div class =" col-md-6" >
175
- <label class =" form-label" >Phone</label >
176
- <input type =" text" name =" config[contact][phone]" class =" form-control" placeholder =" +1.1234567890" >
177
- </div >
178
- <div class =" col-12" >
179
- <label class =" form-label" >Address</label >
180
- <input type =" text" name =" config[contact][address]" class =" form-control" placeholder =" Street, City, Country" >
181
- </div >
182
- </div >
183
- ` ;
184
-
185
- Object .entries (requiredFields).forEach (([fieldName , fieldDef ]) => {
186
- const fieldType = fieldDef .type || ' text' ;
187
- extra .innerHTML += `
188
- <div class =" col-md-6" >
189
- <label class =" form-label" >${ fieldDef .label || fieldName} </label >
190
- <input type =" ${fieldType}" name =" config[${fieldName}]" class =" form-control" placeholder =" ${fieldDef.hint || ''}" ${fieldDef.required ? ' required' : ' ' } >
131
+ <label class =" form-label" >${ def .label || field} </label >
132
+ <input type =" ${def.type || 'text'}" name =" config[${field}]" class =" form-control" placeholder =" ${def.hint || ''}" ${def.required ? ' required' : ' ' } >
191
133
</div >
192
134
` ;
193
- });
194
- } else {
195
- extra .innerHTML = ' ' ;
135
+ }
196
136
}
197
137
}
198
138
199
- function toggleContactFields (show ) {
200
- const el = document .getElementById (' contactFields' );
201
- if (el) {
202
- if (show) {
203
- el .classList .remove (' d-none' );
204
- } else {
205
- el .classList .add (' d-none' );
139
+ async function checkDomain (event ) {
140
+ event .preventDefault ();
141
+ const domainInput = document .getElementById (" domainInput" );
142
+ const resultContainer = document .getElementById (" resultContainer" );
143
+
144
+ const domain = domainInput .value .trim ();
145
+ if (! domain) {
146
+ resultContainer .innerHTML = ' <div class="alert alert-warning">Please enter a domain name.</div>' ;
147
+ return ;
206
148
}
207
- }
149
+
150
+ // Send POST request
151
+ try {
152
+ const response = await fetch (' /dapi/domain/check' , {
153
+ method: ' POST' ,
154
+ headers: {
155
+ ' Content-Type' : ' application/json'
156
+ },
157
+ body: JSON .stringify ({ domain: [domain] }) // Sending domain array
158
+ });
159
+
160
+ const data = await response .json ();
161
+ resultContainer .innerHTML = formatResult (data);
162
+ } catch (error) {
163
+ resultContainer .innerHTML = ' <div class="alert alert-danger">An error occurred. Please try again.</div>' ;
164
+ }
165
+ }
166
+
167
+ function formatResult (data ) {
168
+ if (! data .domains || data .domains .length === 0 ) {
169
+ return ' <div class="alert alert-info">No results found.</div>' ;
170
+ }
171
+
172
+ return data .domains .map (domain => `
173
+ <div class="card border-${ domain .available ? ' success' : ' danger' } mt-3">
174
+ <div class="card-body">
175
+ <h5 class="card-title">${ domain .name } </h5>
176
+ <p class="card-text">
177
+ <strong>Status:</strong>
178
+ <span class="badge bg-${ domain .available ? ' success' : ' danger' } text-${ domain .available ? ' success' : ' danger' } -fg">
179
+ ${ domain .available ? ' Available' : ' Not Available' }
180
+ </span>
181
+ </p>
182
+ ${ domain .reason ? ` <p class="text-muted"><strong>Reason:</strong> ${ domain .reason } </p>` : ' ' }
183
+ ${ domain .available ? `
184
+ <a href="/register?domain=${ encodeURIComponent (domain .name )} " class="btn btn-success">
185
+ Register
186
+ </a>
187
+ ` : ' ' }
188
+ </div>
189
+ </div>
190
+ ` ).join (' ' );
208
191
}
209
192
</script >
210
193
{% endblock %}
0 commit comments