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
	 Rodolfo Ruiz
					Rodolfo Ruiz