sync: updated admin, assets, new images, composer config, customer email API

This commit is contained in:
2026-06-21 03:46:45 +00:00
parent 3d9b648f74
commit 145397ab81
17 changed files with 539 additions and 15 deletions
+1
View File
@@ -4,3 +4,4 @@
config/database.php
uploads/
vendor/
+27
View File
@@ -0,0 +1,27 @@
<?php
/**
* Tom's Java Jive - Admin API: Customer Email History
*/
require_once dirname(__DIR__, 2) . '/includes/auth.php';
AdminAuth::require();
header('Content-Type: application/json');
$customerId = trim($_GET['customer_id'] ?? '');
if (!$customerId) {
echo json_encode(['emails' => []]);
exit;
}
$emails = db()->fetchAll(
"SELECT id, subject, status, opened, open_count, opened_at, tags,
DATE_FORMAT(sent_at, '%b %e, %Y %l:%i %p') as sent_at,
message_id, error_message
FROM email_log
WHERE customer_id = :cid
ORDER BY sent_at DESC
LIMIT 50",
['cid' => $customerId]
);
echo json_encode(['emails' => $emails ?: []]);
+75 -1
View File
@@ -290,6 +290,11 @@ $totalWalletBalance = (float)(db()->fetch("SELECT COALESCE(SUM(wallet_balance),0
<i class="fas fa-shopping-bag"></i> Orders
<span id="cOrderBadge" style="background:var(--color-border);border-radius:10px;padding:1px 7px;font-size:.75rem;margin-left:3px"></span>
</button>
<button type="button" onclick="switchCTab('emails')" id="ctab-emails"
style="padding:.6rem 1rem;border:none;border-bottom:2px solid transparent;background:none;cursor:pointer;font-weight:600;color:var(--color-text-muted);margin-bottom:-1px">
<i class="fas fa-envelope-open-text"></i> Emails
<span id="cEmailBadge" style="background:var(--color-border);border-radius:10px;padding:1px 7px;font-size:.75rem;margin-left:3px"></span>
</button>
</div>
<div id="cpanel-details">
<form method="POST" id="customerForm">
@@ -360,6 +365,37 @@ $totalWalletBalance = (float)(db()->fetch("SELECT COALESCE(SUM(wallet_balance),0
<a id="cOrdViewAll" href="/admin/orders.php" class="btn btn-primary" target="_blank">View All Orders</a>
</div>
</div>
<!-- Emails Panel -->
<div id="cpanel-emails" style="display:none">
<div class="modal-body" style="padding-top:.5rem">
<div id="cEmailLoading" style="text-align:center;padding:2rem;color:var(--color-text-muted)">
<i class="fas fa-spinner fa-spin"></i> Loading emails...
</div>
<div id="cEmailContent" style="display:none">
<div id="cEmailEmpty" style="display:none;text-align:center;padding:2rem;color:var(--color-text-muted)">
<i class="fas fa-envelope" style="font-size:2rem;margin-bottom:.5rem;display:block"></i>
No emails sent to this customer yet.
</div>
<table id="cEmailTable" style="width:100%;border-collapse:collapse;display:none;font-size:.85rem">
<thead>
<tr style="border-bottom:2px solid var(--color-border)">
<th style="padding:.5rem;text-align:left">Subject</th>
<th style="padding:.5rem;text-align:left">Status</th>
<th style="padding:.5rem;text-align:left">Opened</th>
<th style="padding:.5rem;text-align:left">Sent</th>
</tr>
</thead>
<tbody id="cEmailBody"></tbody>
</table>
</div>
</div>
<div class="modal-footer">
<a id="cEmailViewAll" href="#" class="btn btn-primary" target="_blank">
<i class="fas fa-external-link-alt"></i> View All Emails
</a>
</div>
</div>
</div>
</div>
<style>
@@ -372,7 +408,7 @@ $totalWalletBalance = (float)(db()->fetch("SELECT COALESCE(SUM(wallet_balance),0
<script>
// switchCTab defined above
function _switchCTabDummy(tab){
['details','orders'].forEach(function(t){
['details','orders','emails'].forEach(function(t){
document.getElementById('ctab-'+t).style.borderBottomColor=t===tab?'var(--color-primary)':'transparent';
document.getElementById('ctab-'+t).style.color=t===tab?'var(--color-primary)':'var(--color-text-muted)';
document.getElementById('cpanel-'+t).style.display=t===tab?'':'none';
@@ -499,6 +535,44 @@ function openCustomerModal(customer = null) {
const isEdit = !!customer;
_cCustId = isEdit ? customer.customer_id : null;
_cLoaded = false;
// Email history loader
function loadCustomerEmails(customerId){
if(!customerId) return;
document.getElementById('cEmailLoading').style.display='block';
document.getElementById('cEmailContent').style.display='none';
fetch('/admin/api/customer-emails.php?customer_id='+encodeURIComponent(customerId))
.then(r=>r.json()).then(function(data){
document.getElementById('cEmailLoading').style.display='none';
document.getElementById('cEmailContent').style.display='block';
var emails=data.emails||[];
document.getElementById('cEmailBadge').textContent=emails.length;
document.getElementById('cEmailViewAll').href='/admin/email-log.php?customer='+encodeURIComponent(customerId);
if(!emails.length){
document.getElementById('cEmailEmpty').style.display='block';
document.getElementById('cEmailTable').style.display='none';
return;
}
document.getElementById('cEmailEmpty').style.display='none';
document.getElementById('cEmailTable').style.display='table';
var colors={"sent":"#3B82F6","delivered":"#10B981","bounced":"#EF4444","failed":"#EF4444","unknown":"#9CA3AF"};
var html='';
emails.forEach(function(e){
var color=colors[e.status]||'#9CA3AF';
var badge='<span style="background:'+color+';color:white;padding:2px 7px;border-radius:3px;font-size:.75rem;">'+e.status+'</span>';
var opened=e.opened=='1'?'<span style="color:#10B981">✓'+(e.open_count>1?' ('+e.open_count+')':'')+'</span>':'<span style="color:#9CA3AF">—</span>';
html+='<tr style="border-bottom:1px solid var(--color-border)">'
+'<td style="padding:.5rem;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="'+e.subject+'">'+e.subject+'</td>'
+'<td style="padding:.5rem">'+badge+'</td>'
+'<td style="padding:.5rem">'+opened+'</td>'
+'<td style="padding:.5rem;white-space:nowrap;color:#666">'+e.sent_at+'</td>'
+'</tr>';
});
document.getElementById('cEmailBody').innerHTML=html;
}).catch(function(){
document.getElementById('cEmailLoading').innerHTML='<div style="color:var(--color-error)"><i class="fas fa-exclamation-circle"></i> Failed to load emails</div>';
});
}
document.getElementById('customerModalTitle').textContent = isEdit ? 'Edit Customer' : 'Add Customer';
document.getElementById('customerSubmitBtn').textContent = isEdit ? 'Update Customer' : 'Add Customer';
document.getElementById('customerAction').value = isEdit ? 'update' : 'create';
Binary file not shown.

Before

Width:  |  Height:  |  Size: 692 B

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

+5
View File
@@ -0,0 +1,5 @@
{
"require": {
"phpmailer/phpmailer": "^7.1"
}
}
Generated
+101
View File
@@ -0,0 +1,101 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c7173d6a700b02b111e51b703acd8c8e",
"packages": [
{
"name": "phpmailer/phpmailer",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "1bc1716a507a65e039d4ac9d9adebbbd0d346e15"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/1bc1716a507a65e039d4ac9d9adebbbd0d346e15",
"reference": "1bc1716a507a65e039d4ac9d9adebbbd0d346e15",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-filter": "*",
"ext-hash": "*",
"php": ">=5.5.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
"doctrine/annotations": "^1.2.6 || ^1.13.3",
"php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^10.0.0@dev",
"squizlabs/php_codesniffer": "^3.13.5",
"yoast/phpunit-polyfills": "^1.0.4"
},
"suggest": {
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
"directorytree/imapengine": "For uploading sent messages via IMAP, see gmail example",
"ext-imap": "Needed to support advanced email address parsing according to RFC822",
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
"psr/log": "For optional PSR-3 debug logging",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
},
"type": "library",
"autoload": {
"psr-4": {
"PHPMailer\\PHPMailer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-only"
],
"authors": [
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.1.1"
},
"funding": [
{
"url": "https://github.com/Synchro",
"type": "github"
}
],
"time": "2026-05-18T08:06:14+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.9.0"
}
+18 -14
View File
@@ -1,35 +1,39 @@
<?php
/**
* Tom's Java Jive - Main Configuration
*
* Edit these values for your deployment
*/
// Site Configuration
if (!defined('SITE_NAME')) { define('SITE_NAME', "Tom's Java Jive"); }
if (!defined('SITE_URL')) { define('SITE_URL', 'https://tomsjavajive.com'); } // Change to your domain
if (!defined('SITE_URL')) { define('SITE_URL', 'https://tomsjavajive.com'); }
if (!defined('SITE_EMAIL')) { define('SITE_EMAIL', 'support@tomsjavajive.com'); }
// Environment
if (!defined('ENVIRONMENT')) { define('ENVIRONMENT', 'production'); } // development or production
if (!defined('ENVIRONMENT')) { define('ENVIRONMENT', 'production'); }
if (!defined('DEBUG_MODE')) { define('DEBUG_MODE', false); }
// Session Configuration
if (!defined('SESSION_NAME')) { define('SESSION_NAME', 'tjj_session'); }
if (!defined('SESSION_LIFETIME')) { define('SESSION_LIFETIME', 86400 * 7); } // 7 days
if (!defined('SESSION_LIFETIME')) { define('SESSION_LIFETIME', 86400 * 7); }
// Security
if (!defined('HASH_COST')) { define('HASH_COST', 12); } // bcrypt cost factor
if (!defined('HASH_COST')) { define('HASH_COST', 12); }
if (!defined('CSRF_TOKEN_NAME')) { define('CSRF_TOKEN_NAME', 'csrf_token'); }
// API Keys - UPDATE THESE
if (!defined('STRIPE_SECRET_KEY')) { define('STRIPE_SECRET_KEY', 'sk_test_your_stripe_key'); }
if (!defined('STRIPE_PUBLISHABLE_KEY')) { define('STRIPE_PUBLISHABLE_KEY', 'pk_test_your_stripe_key'); }
if (!defined('STRIPE_WEBHOOK_SECRET')) { define('STRIPE_WEBHOOK_SECRET', 'whsec_your_webhook_secret'); }
// Stripe
if (!defined('STRIPE_SECRET_KEY')) { define('STRIPE_SECRET_KEY', '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'); }
if (!defined('STRIPE_PUBLISHABLE_KEY')) { define('STRIPE_PUBLISHABLE_KEY', 'pk_live_51TcSte42hN9FJ8ht6QMzoTJwji8q1me1cFvEX5Y3mES1cJnA6LQ0ROvqkoiiPBMGf73jszIHZPv6Q37qUgLYdCeG00W4uehVba'); }
if (!defined('STRIPE_WEBHOOK_SECRET')) { define('STRIPE_WEBHOOK_SECRET', 'whsec_esEudSGg3zch1zoHaYD3b9Bj2CfZerO9'); }
if (!defined('SENDGRID_API_KEY')) { define('SENDGRID_API_KEY', 'SG.your_sendgrid_key'); }
if (!defined('SENDER_EMAIL')) { define('SENDER_EMAIL', 'noreply@tomsjavajive.com'); }
if (!defined('SENDER_NAME')) { define('SENDER_NAME', "Tom's Java Jive"); }
// CyberMail SMTP
if (!defined('SMTP_HOST')) { define('SMTP_HOST', 'mail.cyberpersons.com'); }
if (!defined('SMTP_PORT')) { define('SMTP_PORT', 587); }
if (!defined('SMTP_USER')) { define('SMTP_USER', 'smtp_49a1fa9c0f15d2d7'); }
if (!defined('SMTP_PASS')) { define('SMTP_PASS', 'T3mOFSMK1SG1l4D1d7N8NefRd8xypwMy'); }
if (!defined('SMTP_SECURE')) { define('SMTP_SECURE', 'tls'); } // STARTTLS
if (!defined('SENDER_EMAIL')) { define('SENDER_EMAIL', 'noreply@tomsjavajive.com'); }
if (!defined('SENDER_NAME')) { define('SENDER_NAME', "Tom's Java Jive"); }
if (!defined('CYBERMAIL_API_KEY')) { define('CYBERMAIL_API_KEY', 'sk_live_d52bf062797105aeaafac9954c21ff988e9b41b77315807d'); }
// Twilio (optional)
if (!defined('TWILIO_SID')) { define('TWILIO_SID', ''); }
@@ -38,7 +42,7 @@ if (!defined('TWILIO_PHONE')) { define('TWILIO_PHONE', ''); }
// File Uploads
if (!defined('UPLOAD_DIR')) { define('UPLOAD_DIR', __DIR__ . '/../uploads/'); }
if (!defined('MAX_UPLOAD_SIZE')) { define('MAX_UPLOAD_SIZE', 5 * 1024 * 1024); } // 5MB
if (!defined('MAX_UPLOAD_SIZE')) { define('MAX_UPLOAD_SIZE', 5 * 1024 * 1024); }
if (!defined('ALLOWED_IMAGE_TYPES')) { define('ALLOWED_IMAGE_TYPES', ['image/jpeg', 'image/png', 'image/gif', 'image/webp']); }
// Pagination
+312
View File
@@ -0,0 +1,312 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5" />
<meta name="theme-color" content="#8B4513" />
<!-- Primary SEO Meta Tags -->
<title>Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee | Weatherford TX</title>
<meta name="title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee | Weatherford TX" />
<meta name="description" content="Shop premium coffee beans and freshly roasted coffee grounds at Tom's Java Jive. Hand-selected, expertly roasted, delivered fresh to your door. Located in Weatherford, Texas. Free shipping on orders over $50!" />
<meta name="keywords" content="coffee beans, fresh roasted coffee, premium coffee, coffee grounds, Weatherford Texas coffee, coffee shop, espresso beans, arabica coffee, single origin coffee, coffee subscription, gift cards, local coffee roaster" />
<meta name="author" content="Tom's Java Jive" />
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
<meta name="googlebot" content="index, follow" />
<meta name="bingbot" content="index, follow" />
<link rel="canonical" href="https://tomsjavaJive.com" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://tomsjavaJive.com/" />
<meta property="og:title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee" />
<meta property="og:description" content="Shop premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door. Free shipping on orders over $50!" />
<meta property="og:image" content="https://tomsjavaJive.com/og-image.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="Tom's Java Jive" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://tomsjavaJive.com/" />
<meta property="twitter:title" content="Tom's Java Jive | Premium Coffee Beans & Fresh Roasted Coffee" />
<meta property="twitter:description" content="Shop premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door." />
<meta property="twitter:image" content="https://tomsjavaJive.com/og-image.jpg" />
<!-- Additional SEO Meta Tags -->
<meta name="geo.region" content="US-TX" />
<meta name="geo.placename" content="Weatherford" />
<meta name="geo.position" content="32.7593;-97.7972" />
<meta name="ICBM" content="32.7593, -97.7972" />
<meta name="rating" content="General" />
<meta name="revisit-after" content="7 days" />
<meta name="distribution" content="global" />
<!-- PWA Meta Tags -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Java Jive" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="application-name" content="Tom's Java Jive" />
<meta name="msapplication-TileColor" content="#8B4513" />
<meta name="msapplication-config" content="/browserconfig.xml" />
<link rel="apple-touch-icon" href="/icons/icon-192x192.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/icons/icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180x180.png" />
<link rel="apple-touch-icon" sizes="167x167" href="/icons/icon-167x167.png" />
<link rel="manifest" href="/manifest.json" />
<!-- Preconnect for Performance -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
<link rel="dns-prefetch" href="https://js.stripe.com" />
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@400;500;600;700&display=swap" rel="stylesheet" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/icons/icon-16x16.png" />
<!-- Structured Data - Local Business -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "CoffeeStore",
"name": "Tom's Java Jive",
"description": "Premium coffee beans and freshly roasted coffee grounds. Hand-selected, expertly roasted, delivered fresh to your door.",
"url": "https://tomsjavaJive.com",
"telephone": "+1-817-266-2022",
"email": "hello@tomsjavaJive.com",
"address": {
"@type": "PostalAddress",
"streetAddress": "",
"addressLocality": "Weatherford",
"addressRegion": "TX",
"postalCode": "",
"addressCountry": "US"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 32.7593,
"longitude": -97.7972
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "07:00",
"closes": "18:00"
},
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Saturday"],
"opens": "08:00",
"closes": "16:00"
}
],
"priceRange": "$$",
"servesCuisine": "Coffee",
"paymentAccepted": "Cash, Credit Card, Debit Card, Apple Pay, Google Pay",
"currenciesAccepted": "USD",
"hasMenu": "https://tomsjavaJive.com/shop",
"sameAs": [
"https://facebook.com/tomsjavaJive",
"https://instagram.com/tomsjavaJive",
"https://twitter.com/tomsjavaJive"
],
"image": "https://tomsjavaJive.com/og-image.jpg",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "127"
}
}
</script>
<!-- Structured Data - Website -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Tom's Java Jive",
"url": "https://tomsjavaJive.com",
"potentialAction": {
"@type": "SearchAction",
"target": "https://tomsjavaJive.com/shop?search={search_term_string}",
"query-input": "required name=search_term_string"
}
}
</script>
<!-- Structured Data - Organization -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Tom's Java Jive",
"url": "https://tomsjavaJive.com",
"logo": "https://tomsjavaJive.com/icons/icon-512x512.png",
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+1-817-266-2022",
"contactType": "customer service",
"availableLanguage": ["English", "Spanish"]
}
}
</script>
<script>window.addEventListener("error",function(e){if(e.error instanceof DOMException&&e.error.name==="DataCloneError"&&e.message&&e.message.includes("PerformanceServerTiming")){e.stopImmediatePropagation();e.preventDefault()}},true);</script>
<script src="https://assets.emergent.sh/scripts/emergent-main.js"></script>
<!-- Register Service Worker -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(reg => console.log('SW registered'))
.catch(err => console.log('SW registration failed:', err));
});
}
</script>
<script defer src="/static/js/bundle.js"></script><script>
if(window.self!==window.top){
var s=document.createElement("script");s.src="/visual-edit-overlay.js";document.head.appendChild(s);
window.tailwind=window.tailwind||{};tailwind.config={corePlugins:{preflight:false}};var t=document.createElement("script");t.src="https://cdn.tailwindcss.com";document.head.appendChild(t);
}
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<a
id="emergent-badge"
target="_blank"
href="https://app.emergent.sh/?utm_source=emergent-badge"
style="
display: inline-flex !important;
box-sizing: border-box;
width: 178px;
height: 40px;
padding: 8px 12px 8px 12px;
align-items: center !important;
gap: 8px;
border-radius: 50px !important;
background: #000 !important;
position: fixed !important;
bottom: 16px;
right: 16px;
text-decoration: none;
font-family: -apple-system, BlinkMacSystemFont,
&quot;Segoe UI&quot;, Roboto, Oxygen, Ubuntu, Cantarell,
&quot;Open Sans&quot;, &quot;Helvetica Neue&quot;,
sans-serif !important;
font-size: 12px !important;
z-index: 9999 !important;
"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M15.5702 8.13142C15.7729 8.0412 16.0007 8.18878 15.9892 8.4103C15.8374 11.3192 14.0965 14.0405 11.2531 15.3065C8.40964 16.5725 5.2224 16.0453 2.95912 14.2117C2.78676 14.072 2.82955 13.804 3.03219 13.7137L4.95677 12.8568C5.04866 12.8159 5.15446 12.823 5.24204 12.8725C6.73377 13.7153 8.59176 13.8649 10.2772 13.1145C11.9626 12.3641 13.0947 10.8833 13.4665 9.21075C13.4883 9.11256 13.5539 9.02918 13.6457 8.98827L15.5702 8.13142Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.3066 4.74698L15.5067 5.19653C15.5759 5.35178 15.5061 5.53366 15.3508 5.60278L1.29992 11.8586C1.14467 11.9278 0.962794 11.8579 0.893675 11.7027L0.701732 11.2716L0.693457 11.2531C-1.10317 7.21778 0.711626 2.49007 4.74692 0.693443C8.78221 -1.10318 13.51 0.711693 15.3066 4.74698ZM2.82356 8.55367C2.63552 8.63739 2.41991 8.51617 2.40853 8.31065C2.28373 6.05724 3.53858 3.85787 5.72286 2.88536C7.90715 1.91286 10.3813 2.45199 11.9724 4.05256C12.1175 4.19854 12.0633 4.43988 11.8753 4.5236L2.82356 8.55367Z" fill="white"/>
</svg>
<p
style="
color: #FFF !important;
font-family: 'Inter', sans-serif !important;
font-size: 13px !important;
font-style: normal !important;
font-weight: 600 !important;
line-height: 20px !important;
margin: 0 !important;
white-space: nowrap !important;
"
>
Made with Emergent
</p>
</a>
<script>
!(function (t, e) {
var o, n, p, r;
e.__SV ||
((window.posthog = e),
(e._i = []),
(e.init = function (i, s, a) {
function g(t, e) {
var o = e.split(".");
2 == o.length && ((t = t[o[0]]), (e = o[1])),
(t[e] = function () {
t.push(
[e].concat(
Array.prototype.slice.call(
arguments,
0,
),
),
);
});
}
((p = t.createElement("script")).type =
"text/javascript"),
(p.crossOrigin = "anonymous"),
(p.async = !0),
(p.src =
s.api_host.replace(
".i.posthog.com",
"-assets.i.posthog.com",
) + "/static/array.js"),
(r =
t.getElementsByTagName(
"script",
)[0]).parentNode.insertBefore(p, r);
var u = e;
for (
void 0 !== a ? (u = e[a] = []) : (a = "posthog"),
u.people = u.people || [],
u.toString = function (t) {
var e = "posthog";
return (
"posthog" !== a && (e += "." + a),
t || (e += " (stub)"),
e
);
},
u.people.toString = function () {
return u.toString(1) + ".people (stub)";
},
o =
"init me ws ys ps bs capture je Di ks register register_once register_for_session unregister unregister_for_session Ps getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSurveysLoaded onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey canRenderSurveyAsync identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty Es $s createPersonProfile Is opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing Ss debug xs getPageViewId captureTraceFeedback captureTraceMetric".split(
" ",
),
n = 0;
n < o.length;
n++
)
g(u, o[n]);
e._i.push([i, s, a]);
}),
(e.__SV = 1));
})(document, window.posthog || []);
posthog.init("phc_xAvL2Iq4tFmANRE7kzbKwaSqp1HJjN7x48s3vr0CMjs", {
api_host: "https://us.i.posthog.com",
person_profiles: "identified_only", // or 'always' to create profiles for anonymous users as well,
session_recording: {
recordCrossOriginIframes: true,
capturePerformance: false,
},
});
</script>
</body>
</html>