completing user administration
This commit is contained in:
114
web/templates/admin/user.html
Normal file
114
web/templates/admin/user.html
Normal file
@@ -0,0 +1,114 @@
|
||||
{% extends 'base.html' %} {% block content %}
|
||||
<div id="user-details">
|
||||
{% if not user %}
|
||||
<h2>Create new user</h2>
|
||||
<form id="new-user-form" method="post" action="{{ url_for('admin_users') }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<div id="user-info">
|
||||
<p>
|
||||
<strong>Username:</strong>
|
||||
<input type="text" name="username" required />
|
||||
</p>
|
||||
<p>
|
||||
<strong>Password:</strong>
|
||||
<input type="password" name="password" required />
|
||||
</p>
|
||||
<p>
|
||||
<strong>Admin:</strong>
|
||||
<input type="checkbox" name="is_admin" />
|
||||
</p>
|
||||
<p>
|
||||
<strong>Active:</strong>
|
||||
<input type="checkbox" name="is_active" />
|
||||
</p>
|
||||
<button type="submit">Create User</button>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<h2>User {{ user.username }}</h2>
|
||||
<form
|
||||
id="user-form"
|
||||
method="post"
|
||||
action="{{ url_for('admin_user', user_id=user.user_id) }}"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="user_id" value="{{ user.user_id }}" />
|
||||
<input type="hidden" name="username" value="{{ user.username }}" />
|
||||
<div id="user-info">
|
||||
<p><strong>ID:</strong> {{ user.user_id }}</p>
|
||||
<p><strong>Username:</strong> {{ user.username }}</p>
|
||||
<p><strong>Created At:</strong> {{ user.created_at }}</p>
|
||||
<p><strong>Last Login:</strong> {{ user.last_login }}</p>
|
||||
<p>
|
||||
<strong>Admin:</strong>
|
||||
<input type="checkbox" name="is_admin" {{ 'checked' if user.is_admin
|
||||
else '' }} />
|
||||
</p>
|
||||
<p>
|
||||
<strong>Active:</strong>
|
||||
<input type="checkbox" name="is_active" {{ 'checked' if user.is_active
|
||||
else '' }} />
|
||||
</p>
|
||||
<p>
|
||||
<strong>Has Password:</strong> {{ '✅' if user.has_password else '❌' }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>New Password:</strong>
|
||||
<input type="password" id="new_password" name="new_password" />
|
||||
</p>
|
||||
<button type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
const userForm = document.getElementById("user-form");
|
||||
|
||||
userForm.addEventListener("submit", function (event) {
|
||||
const userId = document.getElementById("user_id").value;
|
||||
event.preventDefault(); // Prevent the default form submission
|
||||
updateUser(userId);
|
||||
});
|
||||
|
||||
function updateUser(userId) {
|
||||
const passwordInput = document.getElementById("new_password");
|
||||
const formData = userForm.elements;
|
||||
const username = formData.username.value;
|
||||
const password = passwordInput.value;
|
||||
const isAdmin = formData.is_admin.checked;
|
||||
const isActive = formData.is_active.checked;
|
||||
const hasPassword = passwordInput.value.trim() !== "";
|
||||
|
||||
fetch("/admin/user/" + userId, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": formData.csrf_token.value,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: userId,
|
||||
password: password,
|
||||
username: username,
|
||||
is_admin: isAdmin,
|
||||
is_active: isActive,
|
||||
}),
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
alert("User updated successfully");
|
||||
// Clear the password field after successful update
|
||||
passwordInput.value = "";
|
||||
// Set 'has_password' indicator
|
||||
userForm.querySelector('input[name="has_password"]').value =
|
||||
hasPassword ? "✅" : "❌";
|
||||
} else {
|
||||
alert("Error updating user");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
alert("Error updating user");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endif %} {% endblock %} {% block footer_scripts %} {% endblock %}
|
||||
@@ -1,139 +1,100 @@
|
||||
{% extends 'base.html' %} {% block content %}
|
||||
<div id="users">
|
||||
<h2>Users</h2>
|
||||
<form id="user-form" method="post" action="{{ url_for('admin_users') }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>Admin</th>
|
||||
<th>Active</th>
|
||||
<th colspan="2">Password</th>
|
||||
<th>Created</th>
|
||||
<th>Last Login</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for u in users %}
|
||||
<tr class="user-row" data-user-id="{{ u.user_id }}">
|
||||
<td>
|
||||
{{ u.user_id }}<input
|
||||
type="hidden"
|
||||
name="user_id"
|
||||
value="{{ u.user_id }}"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
value="{{ u.username }}"
|
||||
required
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="is_admin" {{ 'checked' if u.is_admin
|
||||
else '' }} />
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="is_active" {{ 'checked' if u.is_active
|
||||
else '' }} />
|
||||
</td>
|
||||
<td>{{ '✅' if u.has_password else '❌' }}</td>
|
||||
<td><input type="password" name="password" /></td>
|
||||
<td>{{ u.created_at }}</td>
|
||||
<td>{{ u.last_login or 'never' }}</td>
|
||||
<td>
|
||||
<button type="submit" data-user-id="{{ u.user_id }}">Save</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
<h3>Create / Update User</h3>
|
||||
<form
|
||||
id="create-update-user-form"
|
||||
method="post"
|
||||
action="{{ url_for('admin_users') }}"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<label>Username <input type="text" name="username" required /></label>
|
||||
<label>Password <input type="password" name="password" /></label>
|
||||
<label>Admin <input type="checkbox" name="is_admin" value="1" /></label>
|
||||
<label
|
||||
>Active <input type="checkbox" name="is_active" value="1" checked
|
||||
/></label>
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>Admin</th>
|
||||
<th>Active</th>
|
||||
<th>Password</th>
|
||||
<th>Created</th>
|
||||
<th>Last Login</th>
|
||||
|
||||
<th>Edit</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for u in users %}
|
||||
<tr class="user-row" data-user-id="{{ u.user_id }}">
|
||||
<td>{{ u.user_id }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('admin_user', user_id=u.user_id) }}"
|
||||
>{{ u.username }}</a
|
||||
>
|
||||
</td>
|
||||
<td>{{ '✅' if u.is_admin else '❌' }}</td>
|
||||
<td>{{ '✅' if u.is_active else '❌' }}</td>
|
||||
<td>{{ '✅' if u.has_password else '❌' }}</td>
|
||||
<td>{{ u.created_at }}</td>
|
||||
<td>{{ u.last_login or 'never' }}</td>
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
class="edit-user"
|
||||
data-user-id="{{ u.user_id }}"
|
||||
onclick="editUser({{ u.user_id }})"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
class="delete-user"
|
||||
data-user-id="{{ u.user_id }}"
|
||||
onclick="deleteUser({{ u.user_id }})"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2>Create New User</h2>
|
||||
<a href="{{ url_for('admin_user', user_id='new') }}">Create User</a>
|
||||
{% endblock %} {% block footer_scripts %}
|
||||
<script>
|
||||
function updateUser(userId) {
|
||||
const row = document.querySelector(`.user-row[data-user-id="${userId}"]`);
|
||||
const passwordInput = row.querySelector('input[name="password"]');
|
||||
const hasPassword =
|
||||
row.querySelector("td:nth-child(5)").textContent.trim() === "✅";
|
||||
const formData = row.querySelector("form").elements;
|
||||
const username = formData.username.value;
|
||||
const password = hasPassword ? passwordInput.value : undefined;
|
||||
const isAdmin = formData.is_admin.checked;
|
||||
const isActive = formData.is_active.checked;
|
||||
|
||||
fetch("/admin/users", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: userId,
|
||||
password: password,
|
||||
username: username,
|
||||
is_admin: isAdmin,
|
||||
is_active: isActive,
|
||||
csrf_token: formData.csrf_token.value,
|
||||
}),
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
alert("User updated successfully");
|
||||
// Clear the password field after successful update
|
||||
passwordInput.value = "";
|
||||
} else {
|
||||
alert("Error updating user");
|
||||
}
|
||||
function editUser(userId) {
|
||||
window.location.href = `/admin/user/${userId}`;
|
||||
}
|
||||
function deleteUser(userId) {
|
||||
if (
|
||||
confirm(
|
||||
"Are you sure you want to delete this user? This action cannot be undone."
|
||||
)
|
||||
) {
|
||||
fetch(`/admin/user/${userId}/delete`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRFToken": document.querySelector('input[name="csrf_token"]')
|
||||
.value,
|
||||
},
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
alert("Error updating user");
|
||||
});
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
// Remove the user row from the table
|
||||
const row = document.querySelector(
|
||||
`.user-row[data-user-id="${userId}"]`
|
||||
);
|
||||
if (row) {
|
||||
row.remove();
|
||||
}
|
||||
} else {
|
||||
alert("Error deleting user.");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
alert("Error deleting user.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initUserForm() {
|
||||
const form = document.getElementById("user-form");
|
||||
const createUpdateForm = document.getElementById("create-update-user-form");
|
||||
|
||||
form.addEventListener("submit", function (event) {
|
||||
const userId = event.target.querySelector('input[name="user_id"]').value;
|
||||
event.preventDefault(); // Prevent the default form submission
|
||||
updateUser(userId);
|
||||
});
|
||||
|
||||
form.addEventListener("click", function (event) {
|
||||
const userId = event.target.closest(".user-row").dataset.userId;
|
||||
updateUser(userId);
|
||||
});
|
||||
|
||||
createUpdateForm.addEventListener("submit", function (event) {
|
||||
const passwordInput = createUpdateForm.querySelector(
|
||||
'input[name="password"]'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
initUserForm();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
<div id="jobs">
|
||||
{% for job in jobs %}
|
||||
<div class="job">
|
||||
<h3><a href="{{ job['url'] }}" target="_blank">{{ job['title'] }}</a></h3>
|
||||
<!--<h3><a href="{{ job['url'] }}" target="_blank">{{ job['title'] }}</a></h3>-->
|
||||
<h3><a href="{{ url_for('job_by_id', job_id=job['id']) }}" target="_blank">{{ job['title'] }}</a></h3>
|
||||
<p class="job-posted-time">{{ job['posted_time'] }}</p>
|
||||
<span class="job-region region-{{ job['region'] }}">{{ job['region'] }}</span>
|
||||
<span class="job-keyword keyword-{{ job['keyword']|replace(' ', '')|lower }}">{{ job['keyword'] }}</span>
|
||||
|
||||
Reference in New Issue
Block a user