feat: Enhance proposal creation and display

- Improve proposal creation form with input validation and
  default date settings for a better user experience.
- Add context variables to the proposals template for
  consistent display across governance pages.
- Enhance proposal detail page with visual improvements,
  voting results display, and user voting functionality.
- Add styles for better visual presentation of proposal details
  and voting information.
This commit is contained in:
Mahmoud-Emad
2025-05-22 16:31:11 +03:00
parent fad288f67d
commit 52fbc77e3e
3 changed files with 459 additions and 149 deletions

View File

@@ -23,21 +23,24 @@
</ul>
</div>
</div>
<!-- Info Alert -->
<div class="row">
<div class="col-12">
<div class="alert alert-info alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<h5><i class="bi bi-info-circle"></i> About Creating Proposals</h5>
<p>Creating a proposal is an important step in our community governance process. Well-crafted proposals clearly state the problem, solution, and implementation details. The community will review and vote on your proposal, so be thorough and thoughtful in your submission.</p>
<p>Creating a proposal is an important step in our community governance process. Well-crafted proposals
clearly state the problem, solution, and implementation details. The community will review and vote
on your proposal, so be thorough and thoughtful in your submission.</p>
<div class="mt-2">
<a href="/governance/proposal-templates" class="btn btn-sm btn-outline-primary"><i class="bi bi-file-earmark-text"></i> Proposal Templates</a>
<a href="/governance/proposal-templates" class="btn btn-sm btn-outline-primary"><i
class="bi bi-file-earmark-text"></i> Proposal Templates</a>
</div>
</div>
</div>
</div>
<!-- Proposal Form and Guidelines in Flex Layout -->
<div class="row mb-4">
<!-- Proposal Form Column -->
@@ -47,34 +50,42 @@
<h5 class="mb-0">New Proposal</h5>
</div>
<div class="card-body">
<form action="/governance/create" method="post">
<form action="/governance/create" method="post" id="proposalForm" novalidate>
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" name="title" required
placeholder="Enter a clear, concise title for your proposal">
<input type="text" class="form-control" id="title" name="title" required minlength="5"
maxlength="100" placeholder="Enter a clear, concise title for your proposal">
<div class="invalid-feedback">Please provide a title (5-100 characters).</div>
<div class="form-text">Make it descriptive and specific</div>
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description" rows="8" required
placeholder="Provide a detailed description of your proposal..."></textarea>
minlength="50" maxlength="5000"
placeholder="Provide a detailed description of your proposal..."></textarea>
<div class="invalid-feedback">Please provide a detailed description (at least 50
characters).</div>
<div class="form-text">Explain the purpose, benefits, and implementation details</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="voting_start_date" class="form-label">Voting Start Date</label>
<input type="date" class="form-control" id="voting_start_date" name="voting_start_date">
<div class="invalid-feedback" id="start_date_feedback">Please select a valid start date.
</div>
<div class="form-text">When should voting begin?</div>
</div>
<div class="col-md-6">
<label for="voting_end_date" class="form-label">Voting End Date</label>
<input type="date" class="form-control" id="voting_end_date" name="voting_end_date">
<div class="invalid-feedback" id="end_date_feedback">End date must be after start date.
</div>
<div class="form-text">When should voting end?</div>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="draft" name="draft" value="true">
@@ -83,7 +94,7 @@
</label>
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Submit Proposal</button>
<a href="/governance" class="btn btn-outline-secondary">Cancel</a>
@@ -92,7 +103,7 @@
</div>
</div>
</div>
<!-- Guidelines Column -->
<div class="col-lg-4">
<div class="card bg-light h-100">
@@ -122,4 +133,111 @@
</div>
</div>
</div>
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('proposalForm');
const startDateInput = document.getElementById('voting_start_date');
const endDateInput = document.getElementById('voting_end_date');
const startDateFeedback = document.getElementById('start_date_feedback');
const endDateFeedback = document.getElementById('end_date_feedback');
// Set default dates
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const nextWeek = new Date(today);
nextWeek.setDate(nextWeek.getDate() + 7);
// Format dates for input fields
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
// Set default values
startDateInput.value = formatDate(tomorrow);
endDateInput.value = formatDate(nextWeek);
// Validate dates when they change
function validateDates() {
const startDate = new Date(startDateInput.value);
const endDate = new Date(endDateInput.value);
const currentDate = new Date();
currentDate.setHours(0, 0, 0, 0); // Reset time to start of day
let startValid = true;
let endValid = true;
// Validate start date is not in the past
if (startDate < currentDate) {
startDateInput.classList.add('is-invalid');
startDateFeedback.textContent = 'Start date cannot be in the past.';
startValid = false;
} else {
startDateInput.classList.remove('is-invalid');
}
// Validate end date is after start date
if (endDate < startDate) {
endDateInput.classList.add('is-invalid');
endDateFeedback.textContent = 'End date must be after start date.';
endValid = false;
} else {
endDateInput.classList.remove('is-invalid');
}
return startValid && endValid;
}
// Validate on input
startDateInput.addEventListener('change', validateDates);
endDateInput.addEventListener('change', validateDates);
// Form submission validation
form.addEventListener('submit', function (event) {
let formValid = true;
// Validate required fields
const requiredFields = form.querySelectorAll('[required]');
requiredFields.forEach(field => {
if (!field.value.trim()) {
field.classList.add('is-invalid');
formValid = false;
} else {
field.classList.remove('is-invalid');
}
// Check minlength if specified
if (field.minLength && field.value.length < field.minLength) {
field.classList.add('is-invalid');
formValid = false;
}
});
// Validate dates
const datesValid = validateDates();
formValid = formValid && datesValid;
// If form is not valid, prevent submission
if (!formValid) {
event.preventDefault();
// Scroll to the first invalid element
const firstInvalid = form.querySelector('.is-invalid');
if (firstInvalid) {
firstInvalid.scrollIntoView({ behavior: 'smooth', block: 'center' });
firstInvalid.focus();
}
}
});
// Initial validation
validateDates();
});
</script>
{% endblock %}
{% endblock %}