fix some errors
This commit is contained in:
Binary file not shown.
1
forms.py
1
forms.py
@@ -13,7 +13,6 @@ class UserForm(FlaskForm):
|
|||||||
company = StringField('Company (Optional)', validators=[Optional(), Length(max=100)])
|
company = StringField('Company (Optional)', validators=[Optional(), Length(max=100)])
|
||||||
position = StringField('Position (Optional)', validators=[Optional(), Length(max=100)])
|
position = StringField('Position (Optional)', validators=[Optional(), Length(max=100)])
|
||||||
notes = TextAreaField('Notes (Optional)', validators=[Optional()])
|
notes = TextAreaField('Notes (Optional)', validators=[Optional()])
|
||||||
is_active = BooleanField('Active', default=True)
|
|
||||||
is_admin = BooleanField('Admin Role', default=False)
|
is_admin = BooleanField('Admin Role', default=False)
|
||||||
new_password = PasswordField('New Password (Optional)')
|
new_password = PasswordField('New Password (Optional)')
|
||||||
confirm_password = PasswordField('Confirm Password (Optional)')
|
confirm_password = PasswordField('Confirm Password (Optional)')
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ python-dotenv==1.0.1
|
|||||||
psycopg2-binary==2.9.9
|
psycopg2-binary==2.9.9
|
||||||
gunicorn==21.2.0
|
gunicorn==21.2.0
|
||||||
Flask-SocketIO==5.3.6
|
Flask-SocketIO==5.3.6
|
||||||
|
email_validator==2.1.0.post1
|
||||||
Binary file not shown.
@@ -110,7 +110,7 @@ def new_contact():
|
|||||||
company=form.company.data,
|
company=form.company.data,
|
||||||
position=form.position.data,
|
position=form.position.data,
|
||||||
notes=form.notes.data,
|
notes=form.notes.data,
|
||||||
is_active=form.is_active.data,
|
is_active=True, # Set default value
|
||||||
is_admin=form.is_admin.data,
|
is_admin=form.is_admin.data,
|
||||||
profile_picture=profile_picture
|
profile_picture=profile_picture
|
||||||
)
|
)
|
||||||
@@ -141,7 +141,6 @@ def edit_profile():
|
|||||||
current_user.company = form.company.data
|
current_user.company = form.company.data
|
||||||
current_user.position = form.position.data
|
current_user.position = form.position.data
|
||||||
current_user.notes = form.notes.data
|
current_user.notes = form.notes.data
|
||||||
current_user.is_active = form.is_active.data
|
|
||||||
current_user.is_admin = form.is_admin.data
|
current_user.is_admin = form.is_admin.data
|
||||||
# Set password if provided
|
# Set password if provided
|
||||||
if form.new_password.data:
|
if form.new_password.data:
|
||||||
@@ -159,7 +158,6 @@ def edit_profile():
|
|||||||
form.company.data = current_user.company
|
form.company.data = current_user.company
|
||||||
form.position.data = current_user.position
|
form.position.data = current_user.position
|
||||||
form.notes.data = current_user.notes
|
form.notes.data = current_user.notes
|
||||||
form.is_active.data = current_user.is_active
|
|
||||||
form.is_admin.data = current_user.is_admin
|
form.is_admin.data = current_user.is_admin
|
||||||
|
|
||||||
return render_template('contacts/form.html', form=form, title='Edit Profile', total_admins=total_admins)
|
return render_template('contacts/form.html', form=form, title='Edit Profile', total_admins=total_admins)
|
||||||
@@ -180,7 +178,6 @@ def edit_contact(id):
|
|||||||
form.company.data = user.company
|
form.company.data = user.company
|
||||||
form.position.data = user.position
|
form.position.data = user.position
|
||||||
form.notes.data = user.notes
|
form.notes.data = user.notes
|
||||||
form.is_active.data = user.is_active
|
|
||||||
form.is_admin.data = user.is_admin
|
form.is_admin.data = user.is_admin
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
# Handle profile picture removal
|
# Handle profile picture removal
|
||||||
@@ -225,7 +222,6 @@ def edit_contact(id):
|
|||||||
user.company = form.company.data
|
user.company = form.company.data
|
||||||
user.position = form.position.data
|
user.position = form.position.data
|
||||||
user.notes = form.notes.data
|
user.notes = form.notes.data
|
||||||
user.is_active = form.is_active.data
|
|
||||||
user.is_admin = form.is_admin.data
|
user.is_admin = form.is_admin.data
|
||||||
# Set password if provided
|
# Set password if provided
|
||||||
if form.new_password.data:
|
if form.new_password.data:
|
||||||
|
|||||||
@@ -145,10 +145,6 @@
|
|||||||
|
|
||||||
<div class="flex flex-col space-y-4">
|
<div class="flex flex-col space-y-4">
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<div class="flex items-center">
|
|
||||||
{{ form.is_active(class="h-4 w-4 focus:ring-blue-500 border-gray-300 rounded", style="accent-color: #16767b;") }}
|
|
||||||
{{ form.is_active.label(class="ml-2 block text-sm text-gray-900") }}
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center relative group">
|
<div class="flex items-center relative group">
|
||||||
{% set is_last_admin = current_user.is_admin and total_admins <= 1 %}
|
{% set is_last_admin = current_user.is_admin and total_admins <= 1 %}
|
||||||
{{ form.is_admin(
|
{{ form.is_admin(
|
||||||
|
|||||||
@@ -30,11 +30,6 @@
|
|||||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-200">
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-200">
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-4 items-center">
|
<div class="flex gap-4 items-center">
|
||||||
<select name="status" class="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-200">
|
|
||||||
<option value="">All Status</option>
|
|
||||||
<option value="active" {% if request.args.get('status') == 'active' %}selected{% endif %}>Active</option>
|
|
||||||
<option value="inactive" {% if request.args.get('status') == 'inactive' %}selected{% endif %}>Inactive</option>
|
|
||||||
</select>
|
|
||||||
<select name="role" class="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-200">
|
<select name="role" class="px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-200">
|
||||||
<option value="">All Roles</option>
|
<option value="">All Roles</option>
|
||||||
<option value="admin" {% if request.args.get('role') == 'admin' %}selected{% endif %}>Admin</option>
|
<option value="admin" {% if request.args.get('role') == 'admin' %}selected{% endif %}>Admin</option>
|
||||||
@@ -91,7 +86,6 @@
|
|||||||
// Clear button resets all filters and submits the form
|
// Clear button resets all filters and submits the form
|
||||||
document.getElementById('clearFilters').addEventListener('click', function() {
|
document.getElementById('clearFilters').addEventListener('click', function() {
|
||||||
document.querySelector('#filterForm input[name="search"]').value = '';
|
document.querySelector('#filterForm input[name="search"]').value = '';
|
||||||
document.querySelector('#filterForm select[name="status"]').selectedIndex = 0;
|
|
||||||
document.querySelector('#filterForm select[name="role"]').selectedIndex = 0;
|
document.querySelector('#filterForm select[name="role"]').selectedIndex = 0;
|
||||||
document.getElementById('filterForm').submit();
|
document.getElementById('filterForm').submit();
|
||||||
});
|
});
|
||||||
@@ -107,7 +101,6 @@
|
|||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Contact Info</th>
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Contact Info</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Company</th>
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Company</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
|
||||||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -149,26 +142,6 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="text-sm text-gray-900">{{ user.company or 'No company' }}</div>
|
<div class="text-sm text-gray-900">{{ user.company or 'No company' }}</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
|
||||||
<div class="flex flex-row flex-wrap gap-1.5">
|
|
||||||
{% if user.email != current_user.email and not user.is_admin %}
|
|
||||||
<form method="POST" action="{{ url_for('contacts.toggle_active', id=user.id) }}" class="inline">
|
|
||||||
<button type="submit"
|
|
||||||
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm font-medium transition-colors duration-200 cursor-pointer"
|
|
||||||
style="background-color: {% if user.is_active %}rgba(34,197,94,0.1){% else %}rgba(239,68,68,0.1){% endif %}; color: {% if user.is_active %}#15803d{% else %}#b91c1c{% endif %};">
|
|
||||||
<i class="fas fa-{% if user.is_active %}check-circle{% else %}times-circle{% endif %}" style="font-size: 0.85em; opacity: 0.7;"></i>
|
|
||||||
{{ 'Active' if user.is_active else 'Inactive' }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm font-medium"
|
|
||||||
style="background-color: {% if user.is_active %}rgba(34,197,94,0.1){% else %}rgba(239,68,68,0.1){% endif %}; color: {% if user.is_active %}#15803d{% else %}#b91c1c{% endif %};">
|
|
||||||
<i class="fas fa-{% if user.is_active %}check-circle{% else %}times-circle{% endif %}" style="font-size: 0.85em; opacity: 0.7;"></i>
|
|
||||||
{{ 'Active' if user.is_active else 'Inactive' }}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex flex-row flex-wrap gap-1.5">
|
<div class="flex flex-row flex-wrap gap-1.5">
|
||||||
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm font-medium"
|
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm font-medium"
|
||||||
@@ -187,20 +160,53 @@
|
|||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
{% if user.email != current_user.email %}
|
{% if user.email != current_user.email %}
|
||||||
<form method="POST" action="{{ url_for('contacts.delete_contact', id=user.id) }}" class="inline"
|
<button type="button"
|
||||||
onsubmit="return confirm('Are you sure you want to delete this contact?');">
|
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm no-underline transition-colors duration-200"
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
style="background-color: rgba(239,68,68,0.1); color: #b91c1c;"
|
||||||
<button type="submit"
|
data-bs-toggle="modal"
|
||||||
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-sm no-underline transition-colors duration-200"
|
data-bs-target="#deleteContactModal{{ user.id }}">
|
||||||
style="background-color: rgba(239,68,68,0.1); color: #b91c1c;">
|
<i class="fas fa-trash" style="font-size: 0.85em; opacity: 0.7;"></i>
|
||||||
<i class="fas fa-trash" style="font-size: 0.85em; opacity: 0.7;"></i>
|
Delete
|
||||||
Delete
|
</button>
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if user.email != current_user.email %}
|
||||||
|
<!-- Delete Contact Modal -->
|
||||||
|
<div class="modal fade" id="deleteContactModal{{ user.id }}" tabindex="-1" aria-labelledby="deleteContactModalLabel{{ user.id }}" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="deleteContactModalLabel{{ user.id }}">Delete Contact</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="d-flex align-items-center gap-3 mb-3">
|
||||||
|
<i class="fas fa-trash text-danger" style="font-size: 2rem;"></i>
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-1">Delete Contact</h6>
|
||||||
|
<p class="text-muted mb-0">{{ user.username }} {{ user.last_name }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||||
|
This action cannot be undone. The contact will be permanently deleted.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
<form action="{{ url_for('contacts.delete_contact', id=user.id) }}" method="POST" class="d-inline">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||||
|
<button type="submit" class="btn btn-danger">
|
||||||
|
<i class="fas fa-trash me-2"></i>Delete Contact
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user