diff --git a/web/static/custom/bountyhub.js b/web/static/custom/bountyhub.js index 287c26ab0..3545e91c7 100644 --- a/web/static/custom/bountyhub.js +++ b/web/static/custom/bountyhub.js @@ -129,16 +129,10 @@ document.addEventListener('DOMContentLoaded', function() {
Since ${new Date(attributes.started_accepting_at).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
${attributes.currency.toUpperCase()}
- + `; } - function isProgramNew(startedAcceptingAt) { - const threeMonthsAgo = new Date(); - threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3); - return new Date(startedAcceptingAt) > threeMonthsAgo; - } - function initializeFilter() { const filterAndSearchCards = debounce(() => { const selectedFilter = filterSelect.value; @@ -266,4 +260,199 @@ document.addEventListener('DOMContentLoaded', function() { } fetchPrograms(false, false); -}); \ No newline at end of file +}); + +function isProgramNew(startedAcceptingAt) { + const threeMonthsAgo = new Date(); + threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3); + return new Date(startedAcceptingAt) > threeMonthsAgo; +} + +// see detail function call +function see_detail(handle) { + Swal.fire({ + title: 'Loading...', + html: 'Fetching program details', + allowOutsideClick: false, + allowEscapeKey: false, + showConfirmButton: false, + willOpen: () => { + Swal.showLoading(); + } + }); + + fetch(`/api/hackerone-programs/${handle}/program_details/`) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + Swal.close(); + + populateModal(data); + }) + .catch(error => { + console.error('Error:', error); + + Swal.fire({ + icon: 'error', + title: 'Oops...', + text: 'There was an error fetching the program details. Please try again.', + }); + }); +} + + +function populateModal(data) { + const attributes = data.attributes; + + const modalHTML = ` + + `; + + // Remove any existing modal + const existingModal = document.getElementById('programDetailModal'); + if (existingModal) { + existingModal.remove(); + } + + document.body.insertAdjacentHTML('beforeend', modalHTML); + + populateBadges(attributes); + + populateAssetAccordion(data); + + const modal = new bootstrap.Modal(document.getElementById('programDetailModal')); + modal.show(); +} +function populateBadges(attributes) { + const badgeContainer = document.getElementById('badgeContainer'); + const badges = [ + { + condition: true, + classes: `${attributes.submission_state === 'open' ? 'bg-success' : 'bg-danger'} text-white`, + text: attributes.submission_state === 'open' ? 'Open' : 'Closed' + }, + { + condition: true, + classes: 'bg-primary text-white', + text: attributes.state === 'public_mode' ? 'Public' : 'Private' + }, + { + condition: true, + classes: attributes.offers_bounties ? 'badge bg-info bg-opacity-10 text-info' : 'badge bg-danger bg-opacity-10 text-danger', + text: attributes.offers_bounties ? 'Bounty' : 'VDP' + }, + { + condition: attributes.open_scope, + classes: 'bg-success text-white', + text: 'Open Scope' + }, + { + condition: isProgramNew(attributes.started_accepting_at), + classes: 'bg-primary text-white', + text: 'New', + icon: 'fe-zap' + } + ]; + + badges.forEach(badge => { + if (badge.condition) { + badgeContainer.innerHTML += ` + + ${badge.icon ? `` : ''}${badge.text} + + `; + } + }); +} + +function populateAssetAccordion(data) { + const accordion = document.getElementById('assetAccordion'); + const assetTypes = { + WILDCARD: [], DOMAIN: [], IP_ADDRESS: [], CIDR: [], URL: [] + }; + + data.relationships.structured_scopes.data.forEach(scope => { + const type = scope.attributes.asset_type; + if (assetTypes.hasOwnProperty(type)) { + assetTypes[type].push(scope.attributes.asset_identifier); + } + }); + + Object.entries(assetTypes).forEach(([type, assets], index) => { + if (assets.length > 0) { + const item = createAccordionItem(type, assets, index); + accordion.appendChild(item); + } + }); +} + +function createAccordionItem(type, assets, index) { + const item = document.createElement('div'); + item.className = 'accordion-item border-0 mb-2'; + item.innerHTML = ` +

+ +

+
+
+ +
+
+ `; + return item; +} \ No newline at end of file diff --git a/web/static/custom/custom.css b/web/static/custom/custom.css index 309f64324..dac8b4e5e 100644 --- a/web/static/custom/custom.css +++ b/web/static/custom/custom.css @@ -773,4 +773,56 @@ mark{ .import-programs-btn.button-updated { animation: gentlePulse 1.5s infinite, colorTransition 0.5s ease; +} + + + +.program-profile-picture { + width: 60px; + height: 60px; + object-fit: cover; + border-radius: 50%; + border: 2px solid #e9ecef; +} + +.program-stat-card { + background-color: #f8f9fa; + border-radius: 8px; + padding: 1.25rem; + transition: all 0.2s ease; +} + +.program-stat-card:hover { + background-color: #e9ecef; +} + +.custom-program-accordion .accordion-button { + background-color: #f8f9fa; + color: #333; + font-weight: 500; + padding: 0.75rem 1rem; + border-radius: 4px; + margin-bottom: 0.5rem; +} + +.custom-program-accordion .accordion-button:not(.collapsed) { + background-color: #e9ecef; + color: #0d6efd; +} + +.custom-program-accordion .accordion-body { + background-color: #ffffff; + border-radius: 0 0 4px 4px; + border: 1px solid #e9ecef; + padding: 0.75rem; +} + +.custom-program-accordion .accordion-body ul { + list-style-type: none; + padding-left: 0; + margin-bottom: 0; +} + +.custom-program-accordion .accordion-body li { + padding: 0.25rem 0; } \ No newline at end of file