Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 147 additions & 15 deletions src/pages/users/CreateUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import type { AxiosError } from 'axios'

import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
Expand All @@ -21,12 +22,41 @@ import {
UsersApi,
type GetUsersTemplateResponse,
type StaffData,
type PostUsersRequest,
} from '@/fineract-api'
import { getConfiguration } from '@/lib/fineract-openapi'

const userApi = new UsersApi(getConfiguration())
const staffApi = new StaffApi(getConfiguration())

type UserApiErrorResponse = {
defaultUserMessage?: string
developerMessage?: string
errors?: Array<{
defaultUserMessage?: string
developerMessage?: string
}>
}

const parseId = (value: string) => {
const parsed = parseInt(value, 10)
return Number.isNaN(parsed) ? undefined : parsed
}

const getUserErrorMessage = (error: unknown) => {
const axiosError = error as AxiosError<UserApiErrorResponse>
const responseData = axiosError.response?.data

return (
responseData?.errors?.[0]?.defaultUserMessage ||
responseData?.errors?.[0]?.developerMessage ||
responseData?.defaultUserMessage ||
responseData?.developerMessage ||
axiosError.message ||
'Failed to create user'
)
}

const CreateUsers = () => {
const [users, setUsers] = useState<GetUsersTemplateResponse>()
const [staff, setStaff] = useState<StaffData[] | null>(null)
Expand All @@ -37,7 +67,7 @@ const CreateUsers = () => {
firstName: '',
lastName: '',
passwordNeverExpiers: false,
sendPasswordToEmail: true,
sendPasswordToEmail: false,
office: '',
staff: '',
roles: '',
Expand Down Expand Up @@ -83,7 +113,73 @@ const CreateUsers = () => {
fetchStaff()
}, [formData.office])

const staffOptions = (staff || []).filter(
option => option.officeId?.toString() === formData.office
)

const navigate = useNavigate()

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()

const requiresStaff = staffOptions.length > 0

if (
!formData.username.trim() ||
!formData.email.trim() ||
!formData.firstName.trim() ||
!formData.lastName.trim() ||
!formData.office ||
!formData.roles ||
(requiresStaff && !formData.staff)
) {
alert('Please fill all required fields.')
return
}

if (showPassword) {
if (!formData.password || !formData.repeatPassword) {
alert('Please enter and confirm the password.')
return
}
if (passwordShort || passwordsDontMatch) {
alert('Please fix the password errors.')
return
}
}

const officeId = parseId(formData.office)
const roleId = parseId(formData.roles)
const staffId = formData.staff ? parseId(formData.staff) : undefined

if (!officeId || !roleId || (formData.staff && !staffId)) {
alert('Please select valid office, role, and staff values.')
return
}

const payload: PostUsersRequest = {
username: formData.username.trim(),
email: formData.email.trim(),
firstname: formData.firstName.trim(),
lastname: formData.lastName.trim(),
officeId,
roles: [roleId],
staffId,
passwordNeverExpires: formData.passwordNeverExpiers,
sendPasswordToEmail: formData.sendPasswordToEmail,
password: showPassword ? formData.password : undefined,
repeatPassword: showPassword ? formData.repeatPassword : undefined,
}

try {
await userApi.create15(payload)
alert('User created successfully!')
navigate('/appusers')
} catch (err) {
console.error('Failed to create user', err)
alert(getUserErrorMessage(err))
}
}
return (
<div className="min-h-screen px-4 py-6 bg-gray-50 dark:bg-zinc-900">
<AppBreadCrumbs
Expand All @@ -96,26 +192,66 @@ const CreateUsers = () => {

<div className="p-8 bg-white dark:bg-zinc-900 rounded-md shadow border max-w-5xl mx-auto">
<h2 className="text-2xl font-semibold mb-6">Create User</h2>
<form className="space-y-6">
<form className="space-y-6" onSubmit={handleSubmit}>
<div className="flex flex-wrap gap-6">
<div className="w-full md:w-[48%] space-y-2">
<Label>Username *</Label>
<Input placeholder="Enter username" className="w-full" />
<Input
placeholder="Enter username"
className="w-full"
value={formData.username}
onChange={e =>
setFormData(prev => ({
...prev,
username: e.target.value,
}))
}
/>
</div>
<div className="w-full md:w-[48%] space-y-2">
<Label>Email *</Label>
<Input placeholder="Enter email" className="w-full" />
<Input
placeholder="Enter email"
className="w-full"
value={formData.email}
onChange={e =>
setFormData(prev => ({
...prev,
email: e.target.value,
}))
}
/>
</div>
</div>

<div className="flex flex-wrap gap-6">
<div className="w-full md:w-[48%] space-y-2">
<Label>First Name *</Label>
<Input placeholder="Enter First Name" className="w-full" />
<Input
placeholder="Enter First Name"
className="w-full"
value={formData.firstName}
onChange={e =>
setFormData(prev => ({
...prev,
firstName: e.target.value,
}))
}
/>
</div>
<div className="w-full md:w-[48%] space-y-2">
<Label>Last Name *</Label>
<Input placeholder="Enter Last Name" className="w-full" />
<Input
placeholder="Enter Last Name"
className="w-full"
value={formData.lastName}
onChange={e =>
setFormData(prev => ({
...prev,
lastName: e.target.value,
}))
}
/>
</div>
</div>

Expand Down Expand Up @@ -200,7 +336,7 @@ const CreateUsers = () => {
selectLabel="Office *"
selectValue={formData.office}
selectOnChange={value =>
setFormData(prev => ({ ...prev, office: value }))
setFormData(prev => ({ ...prev, office: value, staff: '' }))
}
selectPlaceholder="Select office"
selectOptions={(users?.allowedOffices || [])
Expand All @@ -217,14 +353,10 @@ const CreateUsers = () => {
setFormData(prev => ({ ...prev, staff: value }))
}
selectPlaceholder="Select staff"
selectOptions={(staff || [])
.filter(
option => option.officeId?.toString() === formData.office
)
.map(option => ({
id: option.id!,
name: option.displayName!,
}))}
selectOptions={staffOptions.map(option => ({
id: option.id!,
name: option.displayName!,
}))}
/>
</div>

Expand Down
Loading
Loading