You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

457 lines
13 KiB

{% extends "base.html" %}
{% block title %}Payment Search - Plutus{% endblock %}
{% block head %}
<style>
/* Background styling */
body {
background-color: #3a3a3a !important;
background-image: url("{{ url_for('static', filename='images/plutus3.JPG') }}") !important;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
position: relative;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(58, 58, 58, 0.85);
z-index: -1;
}
.container-fluid, .container {
position: relative;
z-index: 1;
}
/* Ensure navbar and footer are above background */
.navbar, .footer, main {
position: relative;
z-index: 1;
}
/* Page title and breadcrumb styling */
h1, h2, h3, h4, h5, h6 {
color: #faf8f0 !important;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
}
.breadcrumb-item a {
color: #d4af37 !important;
}
.breadcrumb-item.active {
color: #faf8f0 !important;
}
.card {
background-color: rgba(250, 248, 240, 0.98) !important;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
}
.alert {
background-color: rgba(250, 248, 240, 0.98);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.search-container {
max-width: 1200px;
margin: 0 auto;
}
.search-box {
background: white;
border-radius: 8px;
padding: 2rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 2rem;
}
.search-input {
font-size: 1.2rem;
padding: 1rem;
border: 2px solid #dee2e6;
border-radius: 6px;
transition: border-color 0.3s;
}
.search-input:focus {
border-color: #0d6efd;
outline: none;
box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.25);
}
.search-results {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.result-item {
padding: 1rem;
border-bottom: 1px solid #f5f5f5;
transition: background-color 0.3s;
}
.result-item:hover {
background-color: #fafafa;
}
.result-item:last-child {
border-bottom: none;
}
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.result-type {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: bold;
text-transform: uppercase;
}
.result-type.single {
background-color: #48c774;
color: white;
}
.result-type.batch {
background-color: #0d6efd;
color: white;
}
.result-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin: 0.5rem 0;
}
.result-field {
font-size: 0.9rem;
}
.result-field strong {
color: #363636;
}
.result-actions {
margin-top: 1rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.loading {
text-align: center;
padding: 2rem;
}
.no-results {
text-align: center;
padding: 3rem;
color: #666;
}
.search-tips {
background-color: #f5f5f5;
border-radius: 4px;
padding: 1rem;
margin-top: 1rem;
}
.search-tips ul {
margin: 0;
padding-left: 1.5rem;
}
.search-error {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 4px;
padding: 1rem;
margin: 1rem 0;
color: #721c24;
}
</style>
{% endblock %}
{% block content %}
<div class="search-container">
<div class="search-box">
<h1 class="h2 mb-2">🔍 Payment Search</h1>
<p class="text-muted mb-4">Search across all payment records by Splynx ID or Payment Intent</p>
<div class="input-group input-group-lg mb-3">
<input
class="form-control search-input"
type="text"
id="searchQuery"
placeholder="Enter Splynx ID (e.g. 123456) or Payment Intent (e.g. pi_1234567890)"
autocomplete="off"
>
<button class="btn btn-primary" type="button" onclick="performSearch()">
<i class="fas fa-search"></i> Search
</button>
</div>
<div class="row g-3 mb-3">
<div class="col-md-4">
<label class="form-label form-label-sm">Search Type:</label>
<select class="form-select form-select-sm" id="searchType">
<option value="all">Auto-detect</option>
<option value="splynx_id">Splynx ID</option>
<option value="payment_intent">Payment Intent</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label form-label-sm">Results Limit:</label>
<select class="form-select form-select-sm" id="resultsLimit">
<option value="25">25 results</option>
<option value="50" selected>50 results</option>
<option value="100">100 results</option>
</select>
</div>
<div class="col-md-4 d-flex align-items-end">
<button class="btn btn-sm btn-outline-info" onclick="clearSearch()">
<i class="fas fa-times"></i> Clear
</button>
</div>
</div>
<div class="search-tips">
<h5 class="h6 mb-2">💡 Search Tips:</h5>
<ul class="mb-0">
<li><strong>Splynx ID:</strong> Enter customer ID number (e.g., 123456)</li>
<li><strong>Payment Intent:</strong> Enter full Stripe Payment Intent ID (e.g., pi_1234567890)</li>
<li><strong>Auto-detect:</strong> System automatically detects search type based on format</li>
<li><strong>Results:</strong> Searches both Single Payments and Batch Payments simultaneously</li>
</ul>
</div>
</div>
<div id="searchResults" style="display: none;"></div>
</div>
<script>
let searchInProgress = false;
// Perform search on Enter key
document.getElementById('searchQuery').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
performSearch();
}
});
// Auto-focus search input
document.getElementById('searchQuery').focus();
function performSearch() {
const query = document.getElementById('searchQuery').value.trim();
const searchType = document.getElementById('searchType').value;
const limit = document.getElementById('resultsLimit').value;
if (!query) {
showError('Please enter a search query');
return;
}
if (searchInProgress) {
return; // Prevent multiple simultaneous searches
}
searchInProgress = true;
showLoading();
const params = new URLSearchParams({
q: query,
type: searchType,
limit: limit
});
fetch(`/search/api?${params}`)
.then(response => response.json())
.then(data => {
searchInProgress = false;
if (data.success) {
displayResults(data);
} else {
showError(data.error || 'Search failed');
}
})
.catch(error => {
searchInProgress = false;
console.error('Search error:', error);
showError('Network error occurred while searching');
});
}
function showLoading() {
const resultsDiv = document.getElementById('searchResults');
resultsDiv.style.display = 'block';
resultsDiv.innerHTML = `
<div class="search-results">
<div class="loading">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Searching payments...</p>
</div>
</div>
`;
}
function showError(message) {
const resultsDiv = document.getElementById('searchResults');
resultsDiv.style.display = 'block';
resultsDiv.innerHTML = `
<div class="alert alert-danger" role="alert">
<strong>Search Error:</strong> ${message}
</div>
`;
}
function displayResults(data) {
const resultsDiv = document.getElementById('searchResults');
if (data.results.length === 0) {
resultsDiv.innerHTML = `
<div class="search-results">
<div class="no-results">
<h3 class="h4 mb-3">No Results Found</h3>
<p>No payments found matching "${data.search_query}"</p>
<p class="text-muted">Try searching with a different Splynx ID or Payment Intent</p>
</div>
</div>
`;
return;
}
let resultsHtml = `
<div class="search-results">
<div class="p-3 border-bottom bg-light">
<h3 class="h4 mb-2">Search Results</h3>
<p class="mb-0">Found ${data.total_found} payment(s) for "${data.search_query}" (${data.search_type})</p>
</div>
`;
data.results.forEach(result => {
resultsHtml += createResultItem(result);
});
resultsHtml += '</div>';
resultsDiv.innerHTML = resultsHtml;
}
function createResultItem(result) {
const statusClass = result.success === true ? 'bg-success bg-opacity-10' :
result.success === false ? 'bg-danger bg-opacity-10' :
'bg-warning bg-opacity-10';
const statusText = result.success === true ? 'Success' :
result.success === false ? 'Failed' :
'Pending';
const statusIcon = result.success === true ? 'fa-check' :
result.success === false ? 'fa-times' :
'fa-clock';
return `
<div class="result-item ${statusClass}">
<div class="result-header">
<div>
<span class="result-type ${result.type}">${result.type}</span>
<strong class="ms-2">Payment #${result.id}</strong>
${result.batch_id ? `<span class="badge bg-info ms-2">Batch #${result.batch_id}</span>` : ''}
</div>
<div>
<span class="badge ${result.success === true ? 'bg-success' : result.success === false ? 'bg-danger' : 'bg-warning'}">
<i class="fas ${statusIcon}"></i> ${statusText}
</span>
${result.refund ? '<span class="badge bg-purple ms-1">Refunded</span>' : ''}
${result.pi_followup ? '<span class="badge bg-warning ms-1">PI Follow-up</span>' : ''}
${result.refund_followup ? '<span class="badge bg-info ms-1">Refund Follow-up</span>' : ''}
</div>
</div>
<div class="result-details">
<div class="result-field">
<strong>Splynx ID:</strong>
${result.splynx_id ? `<a href="${result.splynx_url}" target="_blank">${result.splynx_id}</a>` : 'N/A'}
</div>
<div class="result-field">
<strong>Amount:</strong> $${Math.abs(result.amount).toFixed(2)} AUD
</div>
<div class="result-field">
<strong>Payment Method:</strong> ${result.payment_method || 'N/A'}
</div>
<div class="result-field">
<strong>Payment Intent:</strong>
<code class="small">${result.payment_intent || 'N/A'}</code>
</div>
<div class="result-field">
<strong>Created:</strong> ${new Date(result.created).toLocaleDateString()} ${new Date(result.created).toLocaleTimeString()}
</div>
<div class="result-field">
<strong>Processed By:</strong> ${result.processed_by}
</div>
</div>
${result.error ? `
<div class="alert alert-danger alert-sm mt-2">
<strong>Error:</strong> ${result.error.substring(0, 200)}${result.error.length > 200 ? '...' : ''}
</div>
` : ''}
<div class="result-actions">
<a href="${result.detail_url}" class="btn btn-sm btn-primary">
<i class="fas fa-eye"></i> View Details
</a>
${result.batch_url ? `
<a href="${result.batch_url}" class="btn btn-sm btn-info">
<i class="fas fa-layer-group"></i> View Batch
</a>
` : ''}
${result.splynx_url ? `
<a href="${result.splynx_url}" target="_blank" class="btn btn-sm btn-link">
<i class="fas fa-external-link-alt"></i> Splynx Customer
</a>
` : ''}
</div>
</div>
`;
}
function clearSearch() {
document.getElementById('searchQuery').value = '';
document.getElementById('searchType').value = 'all';
document.getElementById('resultsLimit').value = '50';
document.getElementById('searchResults').style.display = 'none';
document.getElementById('searchQuery').focus();
}
// Purple badge custom style
const style = document.createElement('style');
style.textContent = `
.bg-purple {
background-color: #9370db !important;
color: white;
}
`;
document.head.appendChild(style);
</script>
{% endblock %}