Skip to content

Commit 710dfb9

Browse files
authored
Merge pull request #126 from modx-pro/dev/lk-update
refactor(lk): integrate standalone modules, add confirm dialog
2 parents a0dfe2d + c1c9d44 commit 710dfb9

File tree

20 files changed

+617
-386
lines changed

20 files changed

+617
-386
lines changed

_build/elements/settings.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@
233233
"[[+jsUrl]]web\/lib\/izitoast\/iziToast.js",
234234
"[[+jsUrl]]web\/modules\/hooks.js",
235235
"[[+jsUrl]]web\/modules\/message.js",
236+
"[[+jsUrl]]web\/modules\/confirm.js",
236237
"[[+jsUrl]]web\/core\/Selectors.js",
237238
"[[+jsUrl]]web\/core\/ApiClient.js",
238239
"[[+jsUrl]]web\/core\/TokenManager.js",
@@ -244,6 +245,7 @@
244245
"[[+jsUrl]]web\/ui\/CustomerUI.js",
245246
"[[+jsUrl]]web\/ui\/QuantityUI.js",
246247
"[[+jsUrl]]web\/ui\/ProductCardUI.js",
248+
"[[+jsUrl]]web\/ui\/AuthUI.js",
247249
"[[+jsUrl]]web\/ms3.js"
248250
]',
249251
'xtype' => 'textarea',

assets/components/minishop3/js/web/core/CustomerAPI.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,53 @@ class CustomerAPI {
110110
async deleteAddress (id) {
111111
return this.api.delete(`/api/v1/customer/addresses/${id}`)
112112
}
113+
114+
/**
115+
* Login customer
116+
*
117+
* POST /api/v1/customer/login
118+
*
119+
* @param {string} email - Email
120+
* @param {string} password - Password
121+
* @returns {Promise<Object>}
122+
*/
123+
async login (email, password) {
124+
return this.api.post('/api/v1/customer/login', { email, password })
125+
}
126+
127+
/**
128+
* Register customer
129+
*
130+
* POST /api/v1/customer/register
131+
*
132+
* @param {Object} data - Registration data
133+
* @returns {Promise<Object>}
134+
*/
135+
async register (data) {
136+
return this.api.post('/api/v1/customer/register', data)
137+
}
138+
139+
/**
140+
* Set default address
141+
*
142+
* PUT /api/v1/customer/addresses/{id}/set-default
143+
*
144+
* @param {number} id - Address ID
145+
* @returns {Promise<Object>}
146+
*/
147+
async setDefaultAddress (id) {
148+
return this.api.put(`/api/v1/customer/addresses/${id}/set-default`)
149+
}
150+
151+
/**
152+
* Cancel order
153+
*
154+
* POST /api/v1/customer/orders/{orderId}/cancel
155+
*
156+
* @param {number} orderId - Order ID
157+
* @returns {Promise<Object>}
158+
*/
159+
async cancelOrder (orderId) {
160+
return this.api.post(`/api/v1/customer/orders/${orderId}/cancel`)
161+
}
113162
}

assets/components/minishop3/js/web/core/Selectors.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ const defaultSelectors = {
1818
orderCost: '#ms3_order_cost',
1919
orderCartCost: '#ms3_order_cart_cost',
2020
orderDeliveryCost: '#ms3_order_delivery_cost',
21-
link: '.ms3_link'
21+
link: '.ms3_link',
22+
orderCancel: '.ms3-order-cancel',
23+
addressSetDefault: '.set-default-address',
24+
addressDelete: '.delete-address',
25+
authLoginForm: '#ms3-login-form',
26+
authRegisterForm: '#ms3-register-form',
27+
authForgotPassword: '#forgot-password-link'
2228
}
2329

2430
/**
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* ConfirmDialog - Promise-based confirmation dialog
3+
*
4+
* Uses Bootstrap Modal when available, falls back to native confirm().
5+
* Auto-binds to elements with [data-ms3-confirm] attribute.
6+
*
7+
* @example
8+
* const confirmed = await ms3Confirm('Delete this item?')
9+
*
10+
* @example
11+
* await ms3Confirm('Cancel order?', {
12+
* confirmText: 'Yes, cancel',
13+
* confirmClass: 'btn-danger'
14+
* })
15+
*
16+
* @example HTML auto-bind
17+
* <a href="/logout" data-ms3-confirm="Are you sure?">Logout</a>
18+
*/
19+
const ms3Confirm = (function () {
20+
let modalElement = null
21+
let pendingResolve = null
22+
23+
function getOrCreateModal () {
24+
if (modalElement) return modalElement
25+
26+
modalElement = document.createElement('div')
27+
modalElement.className = 'modal fade'
28+
modalElement.setAttribute('tabindex', '-1')
29+
modalElement.innerHTML = `
30+
<div class="modal-dialog modal-dialog-centered modal-sm">
31+
<div class="modal-content">
32+
<div class="modal-body text-center py-4">
33+
<p class="mb-0 ms3-confirm-message"></p>
34+
</div>
35+
<div class="modal-footer justify-content-center border-top-0 pt-0">
36+
<button type="button" class="btn btn-secondary ms3-confirm-cancel" data-bs-dismiss="modal"></button>
37+
<button type="button" class="btn ms3-confirm-ok"></button>
38+
</div>
39+
</div>
40+
</div>
41+
`
42+
document.body.appendChild(modalElement)
43+
return modalElement
44+
}
45+
46+
/**
47+
* Show confirmation dialog
48+
*
49+
* @param {string} message - Confirmation message
50+
* @param {Object} [options] - Options
51+
* @param {string} [options.confirmText] - Confirm button text
52+
* @param {string} [options.cancelText] - Cancel button text
53+
* @param {string} [options.confirmClass] - Confirm button CSS class
54+
* @returns {Promise<boolean>} - true if confirmed, false if cancelled
55+
*/
56+
async function confirm (message, options = {}) {
57+
// Fallback to native confirm if Bootstrap is not available
58+
if (typeof bootstrap === 'undefined' || !bootstrap.Modal) {
59+
return window.confirm(message)
60+
}
61+
62+
// Dismiss previous dialog if still open
63+
if (pendingResolve) {
64+
pendingResolve(false)
65+
pendingResolve = null
66+
}
67+
68+
const lexicon = (typeof window !== 'undefined' && window.ms3Lexicon) || {}
69+
const lang = (document.documentElement.lang || 'en').slice(0, 2)
70+
const i18n = { ru: { ok: 'Подтвердить', cancel: 'Отмена' }, en: { ok: 'Confirm', cancel: 'Cancel' } }
71+
const t = i18n[lang] || i18n.en
72+
const opts = {
73+
confirmText: options.confirmText || lexicon.ms3_confirm_ok || t.ok,
74+
cancelText: options.cancelText || lexicon.ms3_confirm_cancel || t.cancel,
75+
confirmClass: options.confirmClass || 'btn-primary'
76+
}
77+
78+
const el = getOrCreateModal()
79+
const modalInstance = bootstrap.Modal.getOrCreateInstance(el)
80+
81+
el.querySelector('.ms3-confirm-message').textContent = message
82+
83+
const okBtn = el.querySelector('.ms3-confirm-ok')
84+
okBtn.textContent = opts.confirmText
85+
okBtn.className = `btn ${opts.confirmClass} ms3-confirm-ok`
86+
87+
el.querySelector('.ms3-confirm-cancel').textContent = opts.cancelText
88+
89+
return new Promise((resolve) => {
90+
let resolved = false
91+
pendingResolve = resolve
92+
93+
function cleanup () {
94+
okBtn.removeEventListener('click', handleConfirm)
95+
el.removeEventListener('hidden.bs.modal', handleHidden)
96+
if (pendingResolve === resolve) {
97+
pendingResolve = null
98+
}
99+
}
100+
101+
function handleConfirm () {
102+
if (resolved) return
103+
resolved = true
104+
cleanup()
105+
modalInstance.hide()
106+
resolve(true)
107+
}
108+
109+
function handleHidden () {
110+
if (resolved) return
111+
resolved = true
112+
cleanup()
113+
resolve(false)
114+
}
115+
116+
okBtn.addEventListener('click', handleConfirm)
117+
el.addEventListener('hidden.bs.modal', handleHidden)
118+
119+
modalInstance.show()
120+
})
121+
}
122+
123+
// Auto-bind: elements with [data-ms3-confirm] get a confirm dialog on click
124+
document.addEventListener('click', async (e) => {
125+
const el = e.target.closest('[data-ms3-confirm]')
126+
if (!el) return
127+
128+
e.preventDefault()
129+
130+
const message = el.dataset.ms3Confirm
131+
const confirmed = await confirm(message)
132+
133+
if (confirmed) {
134+
if (el.tagName === 'A' && el.href) {
135+
window.location.href = el.href
136+
}
137+
}
138+
})
139+
140+
return confirm
141+
})()
142+
143+
window.ms3Confirm = ms3Confirm

assets/components/minishop3/js/web/modules/customer-addresses.js

Lines changed: 0 additions & 128 deletions
This file was deleted.

assets/components/minishop3/js/web/modules/order-cancel.js

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)