chore: add edit form
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// src/api/userApi.js
|
||||
export default class UserApi {
|
||||
constructor(token) {
|
||||
this.baseUrl = 'https://thalos-bff.dream-views.com/api/v1/User';
|
||||
@@ -32,4 +31,40 @@ export default class UserApi {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// === CREATE a user ===
|
||||
async createUser(userData) {
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}/Create`, {
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
body: JSON.stringify(userData),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to create user: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (err) {
|
||||
console.error('Error creating user:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// === UPDATE a user ===
|
||||
async updateUser(userData) {
|
||||
try {
|
||||
const response = await fetch(`${this.baseUrl}/Update`, {
|
||||
method: 'PUT',
|
||||
headers: this.getHeaders(),
|
||||
body: JSON.stringify(userData),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to update user: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (err) {
|
||||
console.error('Error updating user:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,102 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { Box, Button, TextField, MenuItem } from '@mui/material';
|
||||
import { createExternalData, updateExternalData } from '../../api/mongo/actions';
|
||||
import UserApi from '../../api/userApi';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
|
||||
export default function AddOrEditUserForm({ onAdd, initialData, onCancel }) {
|
||||
const { user } = useAuth();
|
||||
const thalosToken = user?.thalosToken || localStorage.getItem('thalosToken');
|
||||
const api = useMemo(() => (thalosToken ? new UserApi(thalosToken) : null), [thalosToken]);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
username: '',
|
||||
fullName: '',
|
||||
_Id: '',
|
||||
email: '',
|
||||
role: 'User',
|
||||
status: 'Active'
|
||||
name: '',
|
||||
middleName: '',
|
||||
lastName: '',
|
||||
tenantId: '',
|
||||
roleId: '',
|
||||
status: 'Active',
|
||||
companies: [],
|
||||
projects: [],
|
||||
sendInvitation: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
setFormData({ ...initialData });
|
||||
setFormData({
|
||||
_Id: initialData._id || initialData._Id || '',
|
||||
email: initialData.email ?? '',
|
||||
name: initialData.name ?? '',
|
||||
middleName: initialData.middleName ?? '',
|
||||
lastName: initialData.lastName ?? '',
|
||||
tenantId: initialData.tenantId ?? '',
|
||||
roleId: initialData.roleId ?? '',
|
||||
status: initialData.status ?? 'Active',
|
||||
companies: Array.isArray(initialData.companies) ? initialData.companies : [],
|
||||
projects: Array.isArray(initialData.projects) ? initialData.projects : [],
|
||||
sendInvitation: true,
|
||||
});
|
||||
} else {
|
||||
setFormData({
|
||||
username: '',
|
||||
fullName: '',
|
||||
_Id: '',
|
||||
email: '',
|
||||
role: 'User',
|
||||
status: 'Active'
|
||||
name: '',
|
||||
middleName: '',
|
||||
lastName: '',
|
||||
tenantId: '',
|
||||
roleId: '',
|
||||
status: 'Active',
|
||||
companies: [],
|
||||
projects: [],
|
||||
sendInvitation: true,
|
||||
});
|
||||
}
|
||||
}, [initialData]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
if (name === 'companies' || name === 'projects') {
|
||||
const arr = value.split(',').map(s => s.trim()).filter(s => s.length > 0);
|
||||
setFormData(prev => ({ ...prev, [name]: arr }));
|
||||
} else {
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
if (!api) throw new Error('Missing Thalos token');
|
||||
|
||||
if (initialData) {
|
||||
await updateExternalData(formData);
|
||||
// UPDATE (PUT /User/Update) — API requires _Id, remove Id, displayName, tenantId
|
||||
const payload = {
|
||||
_Id: formData._Id,
|
||||
email: formData.email,
|
||||
name: formData.name,
|
||||
middleName: formData.middleName,
|
||||
lastName: formData.lastName,
|
||||
tenantId: formData.tenantId,
|
||||
roleId: formData.roleId,
|
||||
companies: formData.companies,
|
||||
projects: formData.projects,
|
||||
status: formData.status || 'Active',
|
||||
};
|
||||
await api.updateUser(payload);
|
||||
} else {
|
||||
await createExternalData(formData);
|
||||
// CREATE (POST /User/Create)
|
||||
const payload = {
|
||||
email: formData.email,
|
||||
name: formData.name,
|
||||
middleName: formData.middleName,
|
||||
lastName: formData.lastName,
|
||||
roleId: formData.roleId,
|
||||
sendInvitation: !!formData.sendInvitation,
|
||||
};
|
||||
await api.createUser(payload);
|
||||
}
|
||||
if (onAdd) onAdd();
|
||||
|
||||
onAdd?.();
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
}
|
||||
@@ -45,22 +104,6 @@ export default function AddOrEditUserForm({ onAdd, initialData, onCancel }) {
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 2 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Username"
|
||||
name="username"
|
||||
value={formData.username}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Full Name"
|
||||
name="fullName"
|
||||
value={formData.fullName}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Email"
|
||||
@@ -71,17 +114,62 @@ export default function AddOrEditUserForm({ onAdd, initialData, onCancel }) {
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
select
|
||||
label="Role"
|
||||
name="role"
|
||||
value={formData.role}
|
||||
label="Name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
>
|
||||
<MenuItem value="Admin">Admin</MenuItem>
|
||||
<MenuItem value="User">User</MenuItem>
|
||||
<MenuItem value="Manager">Manager</MenuItem>
|
||||
</TextField>
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Middle Name"
|
||||
name="middleName"
|
||||
value={formData.middleName}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Last Name"
|
||||
name="lastName"
|
||||
value={formData.lastName}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Tenant Id"
|
||||
name="tenantId"
|
||||
value={formData.tenantId}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Role Id"
|
||||
name="roleId"
|
||||
value={formData.roleId}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Companies"
|
||||
name="companies"
|
||||
value={formData.companies.join(', ')}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
helperText="Comma-separated list"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Projects"
|
||||
name="projects"
|
||||
value={formData.projects.join(', ')}
|
||||
onChange={handleChange}
|
||||
margin="normal"
|
||||
helperText="Comma-separated list"
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
select
|
||||
@@ -94,6 +182,7 @@ export default function AddOrEditUserForm({ onAdd, initialData, onCancel }) {
|
||||
<MenuItem value="Active">Active</MenuItem>
|
||||
<MenuItem value="Inactive">Inactive</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<Box display="flex" justifyContent="flex-end" mt={3} gap={1}>
|
||||
<Button onClick={onCancel} className="button-transparent">Cancel</Button>
|
||||
<Button variant="contained" onClick={handleSubmit} className="button-gold">Save</Button>
|
||||
|
||||
@@ -7,8 +7,8 @@ import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
|
||||
import AddOrEditUserForm from './AddOrEditUserForm';
|
||||
import UserApi from '../../api/userApi';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { deleteExternalData } from '../../api/mongo/actions';
|
||||
import useApiToast from '../../hooks/useApiToast';
|
||||
import '../../App.css';
|
||||
|
||||
const columnsBase = [
|
||||
{ field: 'email', headerName: 'Email', width: 260 },
|
||||
@@ -88,7 +88,22 @@ export default function UserManagement() {
|
||||
|
||||
const handleEditClick = (params) => {
|
||||
if (!params || !params.row) return;
|
||||
setEditingData(params.row);
|
||||
const r = params.row;
|
||||
const normalized = {
|
||||
_id: r._id || r._Id || '',
|
||||
id: r.id || r.Id || '',
|
||||
email: r.email ?? '',
|
||||
name: r.name ?? '',
|
||||
middleName: r.middleName ?? '',
|
||||
lastName: r.lastName ?? '',
|
||||
displayName: r.displayName ?? '',
|
||||
tenantId: r.tenantId ?? '',
|
||||
roleId: r.roleId ?? '',
|
||||
status: r.status ?? 'Active',
|
||||
companies: Array.isArray(r.companies) ? r.companies : [],
|
||||
projects: Array.isArray(r.projects) ? r.projects : [],
|
||||
};
|
||||
setEditingData(normalized);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
@@ -100,7 +115,22 @@ export default function UserManagement() {
|
||||
|
||||
const handleConfirmDelete = async () => {
|
||||
try {
|
||||
await deleteExternalData(rowToDelete?._id);
|
||||
if (!apiRef.current || !rowToDelete?._id) throw new Error('Missing API or user id');
|
||||
|
||||
const payload = {
|
||||
_Id: rowToDelete._id || rowToDelete._Id,
|
||||
Id: rowToDelete.id || rowToDelete.Id,
|
||||
email: rowToDelete.email ?? '',
|
||||
name: rowToDelete.name ?? '',
|
||||
middleName: rowToDelete.middleName ?? '',
|
||||
lastName: rowToDelete.lastName ?? '',
|
||||
roleId: rowToDelete.roleId ?? '',
|
||||
companies: Array.isArray(rowToDelete.companies) ? rowToDelete.companies : [],
|
||||
projects: Array.isArray(rowToDelete.projects) ? rowToDelete.projects : [],
|
||||
status: 'Inactive',
|
||||
};
|
||||
|
||||
await apiRef.current.updateUser(payload);
|
||||
await loadData();
|
||||
} catch (error) {
|
||||
console.error('Delete failed:', error);
|
||||
@@ -164,12 +194,11 @@ export default function UserManagement() {
|
||||
|
||||
return (
|
||||
<SectionContainer sx={{ width: '100%' }}>
|
||||
<Typography variant="h4" gutterBottom color='#26201AFF'>User Management</Typography>
|
||||
|
||||
<Dialog open={open} onClose={() => { setOpen(false); setEditingData(null); }} maxWidth="md" fullWidth>
|
||||
<DialogTitle>{editingData ? 'Edit User' : 'Add User'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<AddOrEditUserForm
|
||||
key={editingData?._id || editingData?.id || (open ? 'editing' : 'new')}
|
||||
onAdd={async () => {
|
||||
await loadData();
|
||||
setOpen(false);
|
||||
@@ -212,18 +241,18 @@ export default function UserManagement() {
|
||||
sx={{
|
||||
'& .MuiDataGrid-cell': {
|
||||
display: 'flex',
|
||||
alignItems: 'center', // vertical centering
|
||||
alignItems: 'center',
|
||||
},
|
||||
'& .MuiDataGrid-columnHeader': {
|
||||
display: 'flex',
|
||||
alignItems: 'center', // vertical centering headers too
|
||||
alignItems: 'center',
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" justifyContent="flex-end" mt={2}>
|
||||
<Button variant="contained" onClick={() => setOpen(true)} className="button-gold">
|
||||
<Button variant="contained" className="button-gold" onClick={() => setOpen(true)} >
|
||||
Add User
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user