mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-08-17 19:35:42 +02:00
Added clickable cards, added import button on any card selection, fix card designs
This commit is contained in:
@@ -60,60 +60,13 @@
|
||||
</div>
|
||||
|
||||
<div class="row" id="program_cards">
|
||||
<div class="col-md-6 col-lg-4 col-xl-3 mb-4">
|
||||
<div class="card h-100 shadow-sm position-relative overflow-hidden bbp-card">
|
||||
<div class="position-absolute top-0 end-0 p-2 bbp-bookmark-container">
|
||||
<input type="checkbox" id="bookmark-22746" class="bbp-bookmark-checkbox d-none" checked>
|
||||
<label for="bookmark-22746" class="bbp-bookmark-label" data-bs-toggle="tooltip" data-bs-placement="left"
|
||||
title="Bookmarked Program">
|
||||
<i class="fe-star fs-4"></i>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img
|
||||
src="https://profile-photos.hackerone-user-content.com/variants/gx1qb7440tyefirbxv1tkl1ty186/a11a5f547ea25bb14fce3951b07a50f6288859555c2028feb0c3aad3e1aea36d"
|
||||
alt="Grammarly" class="rounded-circle me-3" width="48" height="48">
|
||||
<div>
|
||||
<h5 class="card-title mb-0">Grammarly</h5>
|
||||
<small class="text-muted">@grammarly</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<span class="badge border border-success text-success me-1 mt-1 bbp-badge">Open for Submission</span>
|
||||
<span class="badge border border-primary text-primary me-1 mt-1 bbp-badge">Public Program</span>
|
||||
<span class="badge border border-info text-info me-1 mt-1 bbp-badge">Bounty $$$</span>
|
||||
<span class="badge border border-warning text-warning mt-1 bbp-badge">Open Scope</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mb-3 small">
|
||||
<div><i class="fe-file-text me-1"></i> My Reports: 0</div>
|
||||
<div><i class="fe-dollar-sign me-1"></i> My Earnings: $0.00</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="d-flex justify-content-between text-muted small mb-3">
|
||||
<div><i class="fe-calendar me-1"></i> Since Sep 2017</div>
|
||||
<div><i class="fe-globe me-1"></i> USD</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer bg-transparent border-0 pt-0">
|
||||
<a href="#" class="btn btn-outline-primary w-100">See details</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button id="importProgramsBtn" class="import-programs-btn" disabled>
|
||||
<i class="fe-download-cloud"></i> Import Programs
|
||||
</button>
|
||||
{% endblock main_content %}
|
||||
|
||||
|
||||
{% block page_level_script %}
|
||||
<script type="text/javascript">
|
||||
|
||||
</script>
|
||||
<script src="{% static 'custom/bountyhub.js' %}"></script>
|
||||
{% endblock page_level_script %}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// this js file will be used to do everything js related to the bountyhub page
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const api_url = '/api/hackerone-programs/';
|
||||
|
||||
Swal.fire({
|
||||
title: "Loading HackerOne Programs",
|
||||
text: "Fetching the latest data...",
|
||||
icon: "info",
|
||||
allowOutsideClick: false,
|
||||
allowEscapeKey: false,
|
||||
showConfirmButton: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
fetch(api_url, {
|
||||
method: "GET",
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
"X-CSRFToken": getCookie("csrftoken"),
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
Swal.close();
|
||||
|
||||
displayPrograms(data);
|
||||
})
|
||||
.catch(error => {
|
||||
Swal.close();
|
||||
displayErrorMessage("An error occurred while fetching the data. Please try again later. Make sure you have hackerone api key set in your API Vault.");
|
||||
console.error('Error:', error);
|
||||
});
|
||||
|
||||
function displayPrograms(programs) {
|
||||
const container = document.getElementById('program_cards');
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!programs || programs.length === 0) {
|
||||
displayErrorMessage("No programs available at the moment.");
|
||||
return;
|
||||
}
|
||||
|
||||
programs.forEach(program => {
|
||||
const { id, attributes } = program;
|
||||
const card = document.createElement('div');
|
||||
card.className = 'col-md-6 col-lg-4 col-xl-3 mb-3';
|
||||
card.innerHTML = `
|
||||
<div class="card h-100 shadow-sm position-relative overflow-hidden bbp-card card-selectable" onclick="toggleCardSelection(this)">
|
||||
|
||||
<div class="card-body py-2 px-3">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<img src="${attributes.profile_picture}" alt="${attributes.name}" class="rounded-circle me-3" width="40" height="40">
|
||||
<div>
|
||||
<h5 class="card-title mb-0">${attributes.name}
|
||||
${attributes.bookmarked ? '<i class="text-warning mdi mdi-bookmark-check" data-bs-toggle="tooltip" data-bs-placement="top" title="Bookmarked Program"></i>' : ''}
|
||||
</h5>
|
||||
<small class="text-muted">@${attributes.handle}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<span class="badge bg-success bg-opacity-10 text-success me-1 mb-1">${attributes.submission_state === 'open' ? 'Open for Submission' : 'Closed'}</span>
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary me-1 mb-1">${attributes.state === 'public_mode' ? 'Public Program' : 'Private Program'}</span>
|
||||
${attributes.offers_bounties ? '<span class="badge bg-info bg-opacity-10 text-info me-1 mb-1">Bounty $$$</span>' : ''}
|
||||
${attributes.open_scope ? '<span class="badge bg-warning bg-opacity-10 text-warning mb-1">Open Scope</span>' : ''}
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mb-2 small">
|
||||
<div><i class="bi bi-flag me-1"></i> My Reports: ${attributes.number_of_reports_for_user}</div>
|
||||
<div><i class="bi bi-currency-dollar me-1"></i> My Earnings: $${attributes.bounty_earned_for_user.toFixed(2)}</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="d-flex justify-content-between text-muted small mb-2">
|
||||
<div><i class="bi bi-calendar me-1"></i> Since ${new Date(attributes.started_accepting_at).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}</div>
|
||||
<div><i class="bi bi-globe me-1"></i> ${attributes.currency.toUpperCase()}</div>
|
||||
</div>
|
||||
<a href="#" class="btn btn-outline-primary btn-sm w-100 mt-2">See details</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Initialize tooltips esp for bookmarked programs
|
||||
const tooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
tooltips.forEach((tooltip) => {
|
||||
new bootstrap.Tooltip(tooltip);
|
||||
});
|
||||
container.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function displayErrorMessage(message) {
|
||||
const container = document.getElementById('program_cards');
|
||||
container.innerHTML = `
|
||||
<div class="col-12">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<i class="fe-alert-triangle me-2"></i>
|
||||
${message}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// below has everything to do with card selection and import button
|
||||
|
||||
function toggleCardSelection(card) {
|
||||
card.classList.toggle('card-selected');
|
||||
updateImportButton();
|
||||
}
|
||||
|
||||
// lets create func and listeners if cards are selected
|
||||
const importBtn = document.getElementById('importProgramsBtn');
|
||||
const container = document.getElementById('program_cards');
|
||||
|
||||
function updateImportButton() {
|
||||
const selectedCards = container.querySelectorAll('.card-selected');
|
||||
importBtn.disabled = selectedCards.length === 0;
|
||||
}
|
||||
|
||||
window.toggleCardSelection = toggleCardSelection;
|
||||
|
||||
});
|
||||
@@ -609,4 +609,97 @@ mark{
|
||||
}
|
||||
.bbp-badge:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.card-selectable {
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
.card-selectable:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
.card-selected {
|
||||
border: 2px solid #007bff;
|
||||
background-color: rgba(0,123,255,0.05);
|
||||
}
|
||||
|
||||
.bbp-bookmark-label {
|
||||
cursor: pointer;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
.bbp-bookmark-label:hover {
|
||||
color: #ffc107;
|
||||
}
|
||||
.bbp-bookmark-checkbox:checked + .bbp-bookmark-label {
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
|
||||
.import-programs-btn {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 12px 24px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.import-programs-btn:not(:disabled) {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
animation: enableButton 0.5s ease;
|
||||
}
|
||||
|
||||
.import-programs-btn:hover {
|
||||
transform: translateX(-50%) translateY(-5px);
|
||||
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.import-programs-btn:active {
|
||||
transform: translateX(-50%) translateY(-2px);
|
||||
box-shadow: 0 5px 7px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.import-programs-btn i {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@keyframes enableButton {
|
||||
0% {
|
||||
transform: translateX(-50%) scale(0.9);
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-50%) scale(1.05);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes disableButton {
|
||||
0% {
|
||||
transform: translateX(-50%) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%) scale(0.9);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.import-programs-btn:disabled {
|
||||
animation: disableButton 0.3s ease forwards;
|
||||
}
|
||||
Reference in New Issue
Block a user