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:
@@ -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 %}
|
Reference in New Issue
Block a user