feat: add 'can_be_assigned' field to users with updates to user creation, retrieval, and assignment queries
This commit is contained in:
@@ -97,6 +97,30 @@ export default function UserManagementPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleAssignable = async (userId, canBeAssigned) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/admin/users/${userId}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ can_be_assigned: !canBeAssigned }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to update user");
|
||||||
|
}
|
||||||
|
|
||||||
|
setUsers(users.map(user =>
|
||||||
|
user.id === userId
|
||||||
|
? { ...user, can_be_assigned: !canBeAssigned }
|
||||||
|
: user
|
||||||
|
));
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getRoleColor = (role) => {
|
const getRoleColor = (role) => {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case "admin":
|
case "admin":
|
||||||
@@ -207,6 +231,9 @@ export default function UserManagementPage() {
|
|||||||
<Badge color={user.is_active ? "green" : "red"}>
|
<Badge color={user.is_active ? "green" : "red"}>
|
||||||
{user.is_active ? "Active" : "Inactive"}
|
{user.is_active ? "Active" : "Inactive"}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
<Badge color={user.can_be_assigned ? "blue" : "gray"}>
|
||||||
|
{user.can_be_assigned ? "Assignable" : "Not Assignable"}
|
||||||
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@@ -237,6 +264,20 @@ export default function UserManagementPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id={`assignable-${user.id}`}
|
||||||
|
checked={user.can_be_assigned || false}
|
||||||
|
onChange={() => handleToggleAssignable(user.id, user.can_be_assigned)}
|
||||||
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
|
/>
|
||||||
|
<label htmlFor={`assignable-${user.id}`} className="text-sm text-gray-700">
|
||||||
|
Can be assigned to projects/tasks
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -290,7 +331,8 @@ function CreateUserModal({ onClose, onUserCreated }) {
|
|||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
role: "user",
|
role: "user",
|
||||||
is_active: true
|
is_active: true,
|
||||||
|
can_be_assigned: true
|
||||||
});
|
});
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
@@ -408,6 +450,19 @@ function CreateUserModal({ onClose, onUserCreated }) {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="can_be_assigned"
|
||||||
|
checked={formData.can_be_assigned}
|
||||||
|
onChange={(e) => setFormData({ ...formData, can_be_assigned: e.target.checked })}
|
||||||
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
|
/>
|
||||||
|
<label htmlFor="can_be_assigned" className="ml-2 block text-sm text-gray-900">
|
||||||
|
Can be assigned to projects/tasks
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex space-x-3 pt-4">
|
<div className="flex space-x-3 pt-4">
|
||||||
<Button type="submit" disabled={loading} className="flex-1">
|
<Button type="submit" disabled={loading} className="flex-1">
|
||||||
{loading ? "Creating..." : "Create User"}
|
{loading ? "Creating..." : "Create User"}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export function getAllUsersForAssignment() {
|
|||||||
`
|
`
|
||||||
SELECT id, name, username, role
|
SELECT id, name, username, role
|
||||||
FROM users
|
FROM users
|
||||||
WHERE is_active = 1 AND role != 'admin'
|
WHERE is_active = 1 AND can_be_assigned = 1
|
||||||
ORDER BY name
|
ORDER BY name
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ export function getAllUsersForTaskAssignment() {
|
|||||||
`
|
`
|
||||||
SELECT id, name, username, role
|
SELECT id, name, username, role
|
||||||
FROM users
|
FROM users
|
||||||
WHERE is_active = 1 AND role != 'admin'
|
WHERE is_active = 1 AND can_be_assigned = 1
|
||||||
ORDER BY name ASC
|
ORDER BY name ASC
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import bcrypt from "bcryptjs"
|
|||||||
import { randomBytes } from "crypto"
|
import { randomBytes } from "crypto"
|
||||||
|
|
||||||
// Create a new user
|
// Create a new user
|
||||||
export async function createUser({ name, username, password, role = 'user', is_active = true }) {
|
export async function createUser({ name, username, password, role = 'user', is_active = true, can_be_assigned = true }) {
|
||||||
const existingUser = db.prepare("SELECT id FROM users WHERE username = ?").get(username)
|
const existingUser = db.prepare("SELECT id FROM users WHERE username = ?").get(username)
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
throw new Error("User with this username already exists")
|
throw new Error("User with this username already exists")
|
||||||
@@ -13,13 +13,13 @@ export async function createUser({ name, username, password, role = 'user', is_a
|
|||||||
const userId = randomBytes(16).toString('hex')
|
const userId = randomBytes(16).toString('hex')
|
||||||
|
|
||||||
const result = db.prepare(`
|
const result = db.prepare(`
|
||||||
INSERT INTO users (id, name, username, password_hash, role, is_active)
|
INSERT INTO users (id, name, username, password_hash, role, is_active, can_be_assigned)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
`).run(userId, name, username, passwordHash, role, is_active ? 1 : 0)
|
`).run(userId, name, username, passwordHash, role, is_active ? 1 : 0, can_be_assigned ? 1 : 0)
|
||||||
|
|
||||||
return db.prepare(`
|
return db.prepare(`
|
||||||
SELECT id, name, username, role, created_at, updated_at, last_login,
|
SELECT id, name, username, role, created_at, updated_at, last_login,
|
||||||
is_active, failed_login_attempts, locked_until, initial
|
is_active, failed_login_attempts, locked_until, initial, can_be_assigned
|
||||||
FROM users WHERE id = ?
|
FROM users WHERE id = ?
|
||||||
`).get(userId)
|
`).get(userId)
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ export async function createUser({ name, username, password, role = 'user', is_a
|
|||||||
export function getUserById(id) {
|
export function getUserById(id) {
|
||||||
return db.prepare(`
|
return db.prepare(`
|
||||||
SELECT id, name, username, password_hash, role, created_at, updated_at, last_login,
|
SELECT id, name, username, password_hash, role, created_at, updated_at, last_login,
|
||||||
is_active, failed_login_attempts, locked_until, initial
|
is_active, failed_login_attempts, locked_until, initial, can_be_assigned
|
||||||
FROM users WHERE id = ?
|
FROM users WHERE id = ?
|
||||||
`).get(id)
|
`).get(id)
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ export function getUserByUsername(username) {
|
|||||||
export function getAllUsers() {
|
export function getAllUsers() {
|
||||||
return db.prepare(`
|
return db.prepare(`
|
||||||
SELECT id, name, username, password_hash, role, created_at, updated_at, last_login, is_active,
|
SELECT id, name, username, password_hash, role, created_at, updated_at, last_login, is_active,
|
||||||
failed_login_attempts, locked_until, initial
|
failed_login_attempts, locked_until, initial, can_be_assigned
|
||||||
FROM users
|
FROM users
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
`).all()
|
`).all()
|
||||||
@@ -172,6 +172,11 @@ export async function updateUser(userId, updates) {
|
|||||||
updateValues.push(updates.is_active ? 1 : 0);
|
updateValues.push(updates.is_active ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updates.can_be_assigned !== undefined) {
|
||||||
|
updateFields.push("can_be_assigned = ?");
|
||||||
|
updateValues.push(updates.can_be_assigned ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (updates.initial !== undefined) {
|
if (updates.initial !== undefined) {
|
||||||
updateFields.push("initial = ?");
|
updateFields.push("initial = ?");
|
||||||
updateValues.push(updates.initial);
|
updateValues.push(updates.initial);
|
||||||
@@ -204,7 +209,7 @@ export async function updateUser(userId, updates) {
|
|||||||
if (result.changes > 0) {
|
if (result.changes > 0) {
|
||||||
return db.prepare(`
|
return db.prepare(`
|
||||||
SELECT id, name, username, role, created_at, updated_at, last_login,
|
SELECT id, name, username, role, created_at, updated_at, last_login,
|
||||||
is_active, failed_login_attempts, locked_until, initial
|
is_active, failed_login_attempts, locked_until, initial, can_be_assigned
|
||||||
FROM users WHERE id = ?
|
FROM users WHERE id = ?
|
||||||
`).get(userId);
|
`).get(userId);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user