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 = `
+
+
+
+
+ ${assets.map(asset => `- ${asset}
`).join('')}
+
+
+
+ `;
+ 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