Changeset 3395869
- Timestamp:
- 11/14/2025 04:48:32 PM (3 months ago)
- File:
-
- 1 edited
-
askeet/trunk/askeet.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
askeet/trunk/askeet.php
r3395647 r3395869 1242 1242 </div> 1243 1243 1244 <!-- Modern Modal for subscription actions --> 1245 <div id="askeet-modal-overlay" class="askeet-modal-overlay"> 1246 <div class="askeet-modal"> 1247 <div class="askeet-modal-icon" id="askeet-modal-icon"> 1248 <!-- Icon will be injected here --> 1249 </div> 1250 <h2 class="askeet-modal-title" id="askeet-modal-title">Title</h2> 1251 <p class="askeet-modal-message" id="askeet-modal-message">Message</p> 1252 <div class="askeet-modal-actions" id="askeet-modal-actions"> 1253 <button class="askeet-modal-btn askeet-modal-btn-primary" id="askeet-modal-btn-primary">OK</button> 1254 </div> 1255 </div> 1256 </div> 1257 1258 <style> 1259 /* Modern Modal Styles */ 1260 .askeet-modal-overlay { 1261 display: none; 1262 position: fixed; 1263 top: 0; 1264 left: 0; 1265 width: 100%; 1266 height: 100%; 1267 background: rgba(0, 0, 0, 0.6); 1268 backdrop-filter: blur(8px); 1269 z-index: 999999; 1270 opacity: 0; 1271 transition: opacity 0.3s ease; 1272 } 1273 1274 .askeet-modal-overlay.active { 1275 display: flex; 1276 align-items: center; 1277 justify-content: center; 1278 opacity: 1; 1279 } 1280 1281 .askeet-modal { 1282 background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); 1283 border-radius: 20px; 1284 padding: 40px 30px; 1285 max-width: 480px; 1286 width: 90%; 1287 box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); 1288 transform: scale(0.7); 1289 opacity: 0; 1290 transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); 1291 text-align: center; 1292 position: relative; 1293 overflow: hidden; 1294 } 1295 1296 .askeet-modal::before { 1297 content: ''; 1298 position: absolute; 1299 top: 0; 1300 left: 0; 1301 width: 100%; 1302 height: 4px; 1303 background: linear-gradient(90deg, #005a9e 0%, #0088cc 100%); 1304 } 1305 1306 .askeet-modal-overlay.active .askeet-modal { 1307 transform: scale(1); 1308 opacity: 1; 1309 } 1310 1311 .askeet-modal-icon { 1312 width: 80px; 1313 height: 80px; 1314 margin: 0 auto 20px; 1315 border-radius: 50%; 1316 display: flex; 1317 align-items: center; 1318 justify-content: center; 1319 font-size: 40px; 1320 animation: iconPop 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.2s both; 1321 } 1322 1323 @keyframes iconPop { 1324 0% { 1325 transform: scale(0); 1326 opacity: 0; 1327 } 1328 50% { 1329 transform: scale(1.2); 1330 } 1331 100% { 1332 transform: scale(1); 1333 opacity: 1; 1334 } 1335 } 1336 1337 .askeet-modal-icon.success { 1338 background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); 1339 box-shadow: 0 8px 24px rgba(76, 175, 80, 0.3); 1340 } 1341 1342 .askeet-modal-icon.error { 1343 background: linear-gradient(135deg, #f44336 0%, #d32f2f 100%); 1344 box-shadow: 0 8px 24px rgba(244, 67, 54, 0.3); 1345 } 1346 1347 .askeet-modal-icon.loading { 1348 background: linear-gradient(135deg, #2196f3 0%, #1976d2 100%); 1349 box-shadow: 0 8px 24px rgba(33, 150, 243, 0.3); 1350 } 1351 1352 .askeet-modal-icon.loading::after { 1353 content: ''; 1354 width: 30px; 1355 height: 30px; 1356 border: 3px solid #ffffff; 1357 border-top-color: transparent; 1358 border-radius: 50%; 1359 animation: spin 0.8s linear infinite; 1360 } 1361 1362 @keyframes spin { 1363 to { transform: rotate(360deg); } 1364 } 1365 1366 .askeet-modal-title { 1367 font-size: 24px; 1368 font-weight: 700; 1369 color: #1a1a1a; 1370 margin: 0 0 12px 0; 1371 animation: slideDown 0.4s ease 0.3s both; 1372 } 1373 1374 .askeet-modal-message { 1375 font-size: 16px; 1376 color: #666; 1377 line-height: 1.6; 1378 margin: 0 0 30px 0; 1379 animation: slideDown 0.4s ease 0.4s both; 1380 } 1381 1382 @keyframes slideDown { 1383 from { 1384 opacity: 0; 1385 transform: translateY(-10px); 1386 } 1387 to { 1388 opacity: 1; 1389 transform: translateY(0); 1390 } 1391 } 1392 1393 .askeet-modal-actions { 1394 display: flex; 1395 gap: 12px; 1396 justify-content: center; 1397 animation: slideDown 0.4s ease 0.5s both; 1398 } 1399 1400 .askeet-modal-btn { 1401 padding: 14px 32px; 1402 border: none; 1403 border-radius: 12px; 1404 font-size: 16px; 1405 font-weight: 600; 1406 cursor: pointer; 1407 transition: all 0.3s ease; 1408 min-width: 120px; 1409 } 1410 1411 .askeet-modal-btn-primary { 1412 background: linear-gradient(135deg, #005a9e 0%, #0088cc 100%); 1413 color: white; 1414 box-shadow: 0 4px 12px rgba(0, 90, 158, 0.3); 1415 } 1416 1417 .askeet-modal-btn-primary:hover { 1418 transform: translateY(-2px); 1419 box-shadow: 0 6px 20px rgba(0, 90, 158, 0.4); 1420 } 1421 1422 .askeet-modal-btn-secondary { 1423 background: #f5f5f5; 1424 color: #666; 1425 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 1426 } 1427 1428 .askeet-modal-btn-secondary:hover { 1429 background: #e0e0e0; 1430 transform: translateY(-2px); 1431 } 1432 </style> 1433 1244 1434 <script> 1245 1435 jQuery(document).ready(function($) { 1246 1436 // Track current billing mode (monthly or yearly) 1247 1437 let currentBillingMode = 'monthly'; 1438 1439 // ======================================== 1440 // MODERN MODAL SYSTEM 1441 // ======================================== 1442 1443 /** 1444 * Show modern modal with different types 1445 * @param {string} type - 'success', 'error', or 'loading' 1446 * @param {string} title - Modal title 1447 * @param {string} message - Modal message 1448 * @param {Object} options - Additional options (buttons, callbacks) 1449 */ 1450 function showModal(type, title, message, options = {}) { 1451 const overlay = $('#askeet-modal-overlay'); 1452 const modal = $('.askeet-modal'); 1453 const icon = $('#askeet-modal-icon'); 1454 const titleEl = $('#askeet-modal-title'); 1455 const messageEl = $('#askeet-modal-message'); 1456 const actionsEl = $('#askeet-modal-actions'); 1457 1458 // Reset modal classes and content 1459 icon.removeClass('success error loading').addClass(type); 1460 titleEl.text(title); 1461 messageEl.text(message); 1462 1463 // Set icon content based on type 1464 if (type === 'success') { 1465 icon.html('✓'); 1466 } else if (type === 'error') { 1467 icon.html('✕'); 1468 } else if (type === 'loading') { 1469 icon.html(''); // Spinner is CSS-based 1470 } 1471 1472 // Configure buttons 1473 actionsEl.empty(); 1474 1475 if (type === 'loading') { 1476 // No buttons for loading state 1477 } else { 1478 // Primary button 1479 const primaryBtn = $('<button>') 1480 .addClass('askeet-modal-btn askeet-modal-btn-primary') 1481 .text(options.primaryText || 'OK') 1482 .on('click', function() { 1483 hideModal(); 1484 if (options.onPrimary) options.onPrimary(); 1485 }); 1486 actionsEl.append(primaryBtn); 1487 1488 // Secondary button (optional) 1489 if (options.secondaryText) { 1490 const secondaryBtn = $('<button>') 1491 .addClass('askeet-modal-btn askeet-modal-btn-secondary') 1492 .text(options.secondaryText) 1493 .on('click', function() { 1494 hideModal(); 1495 if (options.onSecondary) options.onSecondary(); 1496 }); 1497 actionsEl.prepend(secondaryBtn); 1498 } 1499 } 1500 1501 // Show modal with animation 1502 overlay.addClass('active'); 1503 1504 // Close on overlay click (except for loading) 1505 if (type !== 'loading') { 1506 overlay.off('click').on('click', function(e) { 1507 if ($(e.target).is('#askeet-modal-overlay')) { 1508 hideModal(); 1509 } 1510 }); 1511 } 1512 } 1513 1514 /** 1515 * Hide modal with animation 1516 */ 1517 function hideModal() { 1518 const overlay = $('#askeet-modal-overlay'); 1519 overlay.removeClass('active'); 1520 } 1521 1522 /** 1523 * Show success modal 1524 */ 1525 function showSuccess(title, message, callback) { 1526 showModal('success', title, message, { 1527 primaryText: '<?php echo esc_js(__('Got it!', 'askeet')); ?>', 1528 onPrimary: callback 1529 }); 1530 } 1531 1532 /** 1533 * Show error modal 1534 */ 1535 function showError(title, message, callback) { 1536 showModal('error', title, message, { 1537 primaryText: '<?php echo esc_js(__('Try Again', 'askeet')); ?>', 1538 onPrimary: callback 1539 }); 1540 } 1541 1542 /** 1543 * Show loading modal 1544 */ 1545 function showLoading(message = '<?php echo esc_js(__('Processing...', 'askeet')); ?>') { 1546 showModal('loading', '<?php echo esc_js(__('Please Wait', 'askeet')); ?>', message); 1547 } 1548 1549 // ======================================== 1550 // END MODAL SYSTEM 1551 // ======================================== 1248 1552 1249 1553 // Handle billing toggle (monthly/yearly) … … 1280 1584 const btn = $(this); 1281 1585 const originalText = btn.html(); 1586 const currentPlan = '<?php echo esc_js($plan_code); ?>'; 1282 1587 1283 1588 // Get the correct plan based on billing mode … … 1289 1594 } 1290 1595 1596 console.log('Current plan:', currentPlan); 1291 1597 console.log('Selected plan:', plan, 'Billing mode:', currentBillingMode); 1292 1598 1599 // Disable button 1293 1600 btn.prop('disabled', true).html('<?php echo esc_js(__('Processing...', 'askeet')); ?>'); 1294 1601 1295 $.ajax({ 1296 url: '<?php echo esc_js(askeet_get_api_url()); ?>/create-checkout-session', 1297 type: 'POST', 1298 contentType: 'application/json', 1299 data: JSON.stringify({ 1602 // Determine if user has an active paid subscription 1603 const hasPaidSubscription = currentPlan !== 'free' && currentPlan !== ''; 1604 1605 // Show loading modal 1606 if (hasPaidSubscription) { 1607 showLoading('<?php echo esc_js(__('Updating your subscription... Please wait.', 'askeet')); ?>'); 1608 } else { 1609 showLoading('<?php echo esc_js(__('Creating checkout session... Redirecting to Stripe.', 'askeet')); ?>'); 1610 } 1611 1612 // Choose the correct endpoint 1613 let apiEndpoint, requestData; 1614 1615 if (hasPaidSubscription) { 1616 // User has active subscription → UPGRADE existing subscription 1617 console.log('Using /upgrade-subscription endpoint (modifying existing subscription)'); 1618 apiEndpoint = '<?php echo esc_js(askeet_get_api_url()); ?>/upgrade-subscription'; 1619 requestData = { 1620 install_id: '<?php echo esc_js($install_id); ?>', 1621 plan: plan.toString() 1622 }; 1623 } else { 1624 // User is on free plan → CREATE new subscription 1625 console.log('Using /create-checkout-session endpoint (creating new subscription)'); 1626 apiEndpoint = '<?php echo esc_js(askeet_get_api_url()); ?>/create-checkout-session'; 1627 requestData = { 1300 1628 install_id: '<?php echo esc_js($install_id); ?>', 1301 1629 plan: plan.toString(), 1302 1630 success_url: '<?php echo esc_js($success_url); ?>', 1303 1631 cancel_url: '<?php echo esc_js($cancel_url); ?>' 1304 }), 1632 }; 1633 } 1634 1635 $.ajax({ 1636 url: apiEndpoint, 1637 type: 'POST', 1638 contentType: 'application/json', 1639 data: JSON.stringify(requestData), 1305 1640 success: function(response) { 1306 if (response.checkout_url) { 1307 window.location.href = response.checkout_url; 1641 if (hasPaidSubscription) { 1642 // Upgrade response - reload page to show new plan 1643 if (response.success) { 1644 hideModal(); 1645 showSuccess( 1646 '<?php echo esc_js(__('Success!', 'askeet')); ?>', 1647 '<?php echo esc_js(__('Your subscription has been updated successfully. The page will reload to show your new plan.', 'askeet')); ?>', 1648 function() { 1649 window.location.reload(); 1650 } 1651 ); 1652 // Auto reload after 2 seconds 1653 setTimeout(function() { 1654 window.location.reload(); 1655 }, 2000); 1656 } else { 1657 hideModal(); 1658 showError( 1659 '<?php echo esc_js(__('Upgrade Failed', 'askeet')); ?>', 1660 (response.error || '<?php echo esc_js(__('An unknown error occurred. Please try again.', 'askeet')); ?>'), 1661 function() { 1662 btn.prop('disabled', false).html(originalText); 1663 } 1664 ); 1665 btn.prop('disabled', false).html(originalText); 1666 } 1308 1667 } else { 1309 alert('<?php echo esc_js(__('Error: No checkout URL received.', 'askeet')); ?>'); 1310 btn.prop('disabled', false).html(originalText); 1668 // Checkout session response - redirect to Stripe 1669 if (response.checkout_url) { 1670 hideModal(); 1671 showSuccess( 1672 '<?php echo esc_js(__('Redirecting to Checkout', 'askeet')); ?>', 1673 '<?php echo esc_js(__('Your checkout session is ready. Redirecting you to Stripe...', 'askeet')); ?>', 1674 function() { 1675 window.location.href = response.checkout_url; 1676 } 1677 ); 1678 // Auto redirect after 1 second 1679 setTimeout(function() { 1680 window.location.href = response.checkout_url; 1681 }, 1000); 1682 } else { 1683 hideModal(); 1684 showError( 1685 '<?php echo esc_js(__('Checkout Failed', 'askeet')); ?>', 1686 '<?php echo esc_js(__('Could not create checkout session. Please try again or contact support.', 'askeet')); ?>', 1687 function() { 1688 btn.prop('disabled', false).html(originalText); 1689 } 1690 ); 1691 btn.prop('disabled', false).html(originalText); 1692 } 1311 1693 } 1312 1694 }, 1313 error: function() { 1314 alert('<?php echo esc_js(__('Error creating checkout session. Please try again.', 'askeet')); ?>'); 1695 error: function(xhr) { 1696 hideModal(); 1697 const errorMsg = xhr.responseJSON && xhr.responseJSON.error ? xhr.responseJSON.error : '<?php echo esc_js(__('Network error. Please check your connection and try again.', 'askeet')); ?>'; 1698 showError( 1699 '<?php echo esc_js(__('Request Failed', 'askeet')); ?>', 1700 errorMsg, 1701 function() { 1702 btn.prop('disabled', false).html(originalText); 1703 } 1704 ); 1315 1705 btn.prop('disabled', false).html(originalText); 1316 1706 }
Note: See TracChangeset
for help on using the changeset viewer.