fix teacher, parent and admin pages
This commit is contained in:
@@ -0,0 +1,719 @@
|
||||
import { type FormEvent, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { fetchUsers, saveUser } from '../../api/session'
|
||||
import type { UserListRow } from '../../api/types'
|
||||
|
||||
function dash(v: unknown): string {
|
||||
const s = (v ?? '').toString().trim()
|
||||
return s === '' ? '—' : s
|
||||
}
|
||||
|
||||
function formatDateTimeLocal(value: unknown): string {
|
||||
if (value == null || value === '') return ''
|
||||
const normalized = String(value).trim().replace(' ', 'T')
|
||||
const date = new Date(normalized)
|
||||
if (Number.isNaN(date.valueOf())) {
|
||||
const match = normalized.match(/^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2})/)
|
||||
return match ? `${match[1]}T${match[2]}` : ''
|
||||
}
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`
|
||||
}
|
||||
|
||||
function roleLabels(row: UserListRow): string {
|
||||
const roles = row.roles
|
||||
if (!roles?.length) return 'No Role Assigned'
|
||||
const parts = roles
|
||||
.map((r) => {
|
||||
if (typeof r === 'string') return r.trim()
|
||||
return String(r.name ?? r.slug ?? '').trim()
|
||||
})
|
||||
.filter(Boolean)
|
||||
return parts.length ? parts.join(', ') : 'No Role Assigned'
|
||||
}
|
||||
|
||||
function isParentRole(row: UserListRow): boolean {
|
||||
return (row.roles ?? []).some((r) => {
|
||||
const s =
|
||||
typeof r === 'string' ? r.toLowerCase() : `${r.name ?? ''} ${r.slug ?? ''}`.toLowerCase()
|
||||
return s.includes('parent')
|
||||
})
|
||||
}
|
||||
|
||||
type EditFormState = {
|
||||
id: number
|
||||
account_id: string
|
||||
firstname: string
|
||||
lastname: string
|
||||
gender: string
|
||||
cellphone: string
|
||||
email: string
|
||||
address_street: string
|
||||
apt: string
|
||||
city: string
|
||||
state: string
|
||||
zip: string
|
||||
accept_school_policy: boolean
|
||||
user_type: string
|
||||
rfid_tag: string
|
||||
school_id: string
|
||||
failed_attempts: string
|
||||
last_failed_at: string
|
||||
semester: string
|
||||
school_year: string
|
||||
status: string
|
||||
is_suspended: boolean
|
||||
is_verified: boolean
|
||||
token: string
|
||||
updated_at: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
function userToForm(user: Record<string, unknown>, id: number): EditFormState {
|
||||
const g = (user.gender ?? '').toString().toLowerCase()
|
||||
return {
|
||||
id,
|
||||
account_id: String(user.account_id ?? ''),
|
||||
firstname: String(user.firstname ?? ''),
|
||||
lastname: String(user.lastname ?? ''),
|
||||
gender: g === 'male' || g === 'female' ? g : '',
|
||||
cellphone: String(user.cellphone ?? ''),
|
||||
email: String(user.email ?? ''),
|
||||
address_street: String(user.address_street ?? ''),
|
||||
apt: String(user.apt ?? ''),
|
||||
city: String(user.city ?? ''),
|
||||
state: String(user.state ?? ''),
|
||||
zip: String(user.zip ?? ''),
|
||||
accept_school_policy: Boolean(
|
||||
user.accept_school_policy === true ||
|
||||
user.accept_school_policy === 1 ||
|
||||
user.accept_school_policy === '1',
|
||||
),
|
||||
user_type: String(user.user_type ?? ''),
|
||||
rfid_tag: String(user.rfid_tag ?? ''),
|
||||
school_id: String(user.school_id ?? ''),
|
||||
failed_attempts: String(user.failed_attempts ?? ''),
|
||||
last_failed_at: formatDateTimeLocal(user.last_failed_at),
|
||||
semester: String(user.semester ?? ''),
|
||||
school_year: String(user.school_year ?? ''),
|
||||
status: String(user.status ?? ''),
|
||||
is_suspended: Boolean(
|
||||
user.is_suspended === true || user.is_suspended === 1 || user.is_suspended === '1',
|
||||
),
|
||||
is_verified: Boolean(
|
||||
user.is_verified === true || user.is_verified === 1 || user.is_verified === '1',
|
||||
),
|
||||
token: String(user.token ?? ''),
|
||||
updated_at: formatDateTimeLocal(user.updated_at),
|
||||
created_at: formatDateTimeLocal(user.created_at),
|
||||
}
|
||||
}
|
||||
|
||||
function formToPayload(f: EditFormState): Record<string, unknown> {
|
||||
const payload: Record<string, unknown> = {
|
||||
firstname: f.firstname.trim(),
|
||||
lastname: f.lastname.trim(),
|
||||
accept_school_policy: f.accept_school_policy ? 1 : 0,
|
||||
is_suspended: f.is_suspended ? 1 : 0,
|
||||
is_verified: f.is_verified ? 1 : 0,
|
||||
}
|
||||
const normalizedGender = f.gender.trim().toLowerCase()
|
||||
const normalizedUserType = f.user_type.trim().toLowerCase()
|
||||
const normalizedStatus = f.status.trim().toLowerCase()
|
||||
|
||||
const assignIfPresent = (key: string, value: string, opts?: { nullable?: boolean }) => {
|
||||
const trimmed = value.trim()
|
||||
if (trimmed !== '') {
|
||||
payload[key] = trimmed
|
||||
return
|
||||
}
|
||||
if (opts?.nullable) {
|
||||
payload[key] = null
|
||||
}
|
||||
}
|
||||
|
||||
assignIfPresent('account_id', f.account_id, { nullable: true })
|
||||
assignIfPresent('cellphone', f.cellphone)
|
||||
assignIfPresent('email', f.email)
|
||||
assignIfPresent('address_street', f.address_street)
|
||||
assignIfPresent('apt', f.apt, { nullable: true })
|
||||
assignIfPresent('city', f.city)
|
||||
assignIfPresent('state', f.state)
|
||||
assignIfPresent('zip', f.zip)
|
||||
assignIfPresent('rfid_tag', f.rfid_tag, { nullable: true })
|
||||
assignIfPresent('last_failed_at', f.last_failed_at, { nullable: true })
|
||||
assignIfPresent('semester', f.semester)
|
||||
assignIfPresent('school_year', f.school_year, { nullable: true })
|
||||
assignIfPresent('token', f.token, { nullable: true })
|
||||
|
||||
if (normalizedGender === 'male' || normalizedGender === 'female') {
|
||||
payload.gender = normalizedGender
|
||||
} else if (normalizedGender === '') {
|
||||
payload.gender = null
|
||||
}
|
||||
|
||||
if (normalizedUserType === 'primary' || normalizedUserType === 'secondary' || normalizedUserType === 'tertiary') {
|
||||
payload.user_type = normalizedUserType
|
||||
}
|
||||
|
||||
if (normalizedStatus === 'active' || normalizedStatus === 'inactive') {
|
||||
payload.status = normalizedStatus === 'active' ? 'Active' : 'Inactive'
|
||||
}
|
||||
|
||||
const schoolId = f.school_id.trim()
|
||||
if (schoolId !== '') {
|
||||
if (/^\d+$/.test(schoolId)) {
|
||||
payload.school_id = Number(schoolId)
|
||||
}
|
||||
} else {
|
||||
payload.school_id = null
|
||||
}
|
||||
|
||||
const failedAttempts = f.failed_attempts.trim()
|
||||
payload.failed_attempts = failedAttempts === '' ? null : Number(failedAttempts)
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
function buildChangedPayload(
|
||||
current: EditFormState,
|
||||
original: EditFormState | null,
|
||||
): Record<string, unknown> {
|
||||
const currentPayload = formToPayload(current)
|
||||
if (!original) return currentPayload
|
||||
|
||||
const originalPayload = formToPayload(original)
|
||||
const changed: Record<string, unknown> = {}
|
||||
|
||||
for (const [key, value] of Object.entries(currentPayload)) {
|
||||
if (!Object.is(value, originalPayload[key])) {
|
||||
changed[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
/** CI `user/user_list.php` — DataTable-style list + full edit modal (`saveUser`). */
|
||||
export function UserListPage() {
|
||||
const [rows, setRows] = useState<UserListRow[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [saveError, setSaveError] = useState<string | null>(null)
|
||||
const [form, setForm] = useState<EditFormState | null>(null)
|
||||
const [originalForm, setOriginalForm] = useState<EditFormState | null>(null)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const modalRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
async function load() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const data = await fetchUsers({ sort: 'lastname', order: 'asc' })
|
||||
setRows(data.users ?? [])
|
||||
setError(null)
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : 'Unable to load users.')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
void load()
|
||||
}, [])
|
||||
|
||||
const modalTitle = useMemo(() => {
|
||||
if (!form) return 'Edit User'
|
||||
const name = [form.firstname, form.lastname].filter(Boolean).join(' ').trim()
|
||||
return name ? `Edit User — ${name}` : 'Edit User'
|
||||
}, [form])
|
||||
|
||||
useEffect(() => {
|
||||
if (!form) return
|
||||
const el = modalRef.current
|
||||
const B = (
|
||||
window as unknown as {
|
||||
bootstrap?: { Modal: { getOrCreateInstance: (n: HTMLElement) => { show: () => void } } }
|
||||
}
|
||||
).bootstrap
|
||||
if (el && B?.Modal?.getOrCreateInstance) {
|
||||
B.Modal.getOrCreateInstance(el).show()
|
||||
}
|
||||
}, [form])
|
||||
|
||||
function openEdit(entry: UserListRow) {
|
||||
const user = entry.user ?? {}
|
||||
const id = Number(user.id ?? 0)
|
||||
if (!id) return
|
||||
const nextForm = userToForm(user as Record<string, unknown>, id)
|
||||
setSaveError(null)
|
||||
setOriginalForm(nextForm)
|
||||
setForm(nextForm)
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
const el = modalRef.current
|
||||
const Inst = (
|
||||
window as unknown as {
|
||||
bootstrap?: { Modal: { getInstance: (n: HTMLElement) => { hide: () => void } | null } }
|
||||
}
|
||||
).bootstrap?.Modal
|
||||
if (el && Inst?.getInstance) {
|
||||
Inst.getInstance(el)?.hide()
|
||||
}
|
||||
setForm(null)
|
||||
setOriginalForm(null)
|
||||
}
|
||||
|
||||
async function onSubmit(e: FormEvent) {
|
||||
e.preventDefault()
|
||||
if (!form) return
|
||||
const payload = buildChangedPayload(form, originalForm)
|
||||
if (Object.keys(payload).length === 0) {
|
||||
setSaveError(null)
|
||||
closeModal()
|
||||
return
|
||||
}
|
||||
setSaving(true)
|
||||
setSaveError(null)
|
||||
try {
|
||||
await saveUser(form.id, payload)
|
||||
await load()
|
||||
closeModal()
|
||||
} catch (err) {
|
||||
setSaveError(err instanceof Error ? err.message : 'Save failed.')
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container-fluid py-3">
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
|
||||
<h2 className="text-center mt-2 mb-0 flex-grow-1">User List</h2>
|
||||
<Link className="btn btn-success btn-sm" to="/app/user/create">
|
||||
Create user
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{error ? (
|
||||
<div id="userListAlert" className="alert alert-danger" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="table-responsive">
|
||||
<table className="display table table-striped table-bordered align-middle" style={{ width: '100%' }}>
|
||||
<thead className="table-dark">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Email</th>
|
||||
<th>Phone</th>
|
||||
<th>User Type</th>
|
||||
<th>Roles</th>
|
||||
<th style={{ width: 140 }}>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading ? (
|
||||
<tr>
|
||||
<td colSpan={8} className="text-center text-muted">
|
||||
Loading users…
|
||||
</td>
|
||||
</tr>
|
||||
) : rows.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={8} className="text-center text-muted">
|
||||
No users found.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
rows.map((entry) => {
|
||||
const user = entry.user ?? {}
|
||||
const uid = Number(user.id ?? 0)
|
||||
const idDisplay =
|
||||
user.school_id != null && String(user.school_id).trim() !== ''
|
||||
? String(user.school_id)
|
||||
: String(user.id ?? '')
|
||||
const parent = isParentRole(entry) && uid > 0
|
||||
const familyHref = `/app/administrator/family?guardian_id=${encodeURIComponent(String(uid))}`
|
||||
|
||||
return (
|
||||
<tr key={uid || idDisplay}>
|
||||
<td>{dash(idDisplay)}</td>
|
||||
<td>
|
||||
{parent ? (
|
||||
<Link className="text-decoration-none" to={familyHref}>
|
||||
{dash(user.firstname)}
|
||||
</Link>
|
||||
) : (
|
||||
dash(user.firstname)
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{parent ? (
|
||||
<Link className="text-decoration-none" to={familyHref}>
|
||||
{dash(user.lastname)}
|
||||
</Link>
|
||||
) : (
|
||||
dash(user.lastname)
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{user.email ? (
|
||||
<a className="text-decoration-none" href={`mailto:${String(user.email)}`}>
|
||||
{String(user.email)}
|
||||
</a>
|
||||
) : (
|
||||
'—'
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{user.cellphone ? (
|
||||
<a className="text-decoration-none" href={`tel:${String(user.cellphone)}`}>
|
||||
{String(user.cellphone)}
|
||||
</a>
|
||||
) : (
|
||||
'—'
|
||||
)}
|
||||
</td>
|
||||
<td>{dash(user.user_type)}</td>
|
||||
<td>{roleLabels(entry)}</td>
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={!uid}
|
||||
onClick={() => openEdit(entry)}
|
||||
>
|
||||
<i className="fas fa-edit" aria-hidden /> Edit
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{form ? (
|
||||
<div
|
||||
className="modal fade"
|
||||
id="editUserModal"
|
||||
tabIndex={-1}
|
||||
ref={modalRef}
|
||||
aria-labelledby="editUserModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
|
||||
<form className="modal-content" onSubmit={(e) => void onSubmit(e)}>
|
||||
<div className="modal-header bg-primary text-white">
|
||||
<h5 className="modal-title" id="editUserModalLabel">
|
||||
{modalTitle}
|
||||
</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close btn-close-white"
|
||||
aria-label="Close"
|
||||
onClick={() => closeModal()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="modal-body">
|
||||
{saveError ? <div className="alert alert-danger">{saveError}</div> : null}
|
||||
<div className="row g-3">
|
||||
<div className="col-md-6">
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Account ID</label>
|
||||
<input
|
||||
type="text"
|
||||
name="account_id"
|
||||
className="form-control"
|
||||
value={form.account_id}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, account_id: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">First Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="firstname"
|
||||
className="form-control"
|
||||
required
|
||||
value={form.firstname}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, firstname: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Last Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="lastname"
|
||||
className="form-control"
|
||||
required
|
||||
value={form.lastname}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, lastname: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Gender</label>
|
||||
<select
|
||||
name="gender"
|
||||
className="form-select"
|
||||
value={form.gender}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, gender: e.target.value } : c))}
|
||||
>
|
||||
<option value="">—</option>
|
||||
<option value="male">Male</option>
|
||||
<option value="female">Female</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Phone</label>
|
||||
<input
|
||||
type="text"
|
||||
name="cellphone"
|
||||
className="form-control"
|
||||
value={form.cellphone}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, cellphone: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
className="form-control"
|
||||
required
|
||||
value={form.email}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, email: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Address Street</label>
|
||||
<input
|
||||
type="text"
|
||||
name="address_street"
|
||||
className="form-control"
|
||||
value={form.address_street}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, address_street: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Apt</label>
|
||||
<input
|
||||
type="text"
|
||||
name="apt"
|
||||
className="form-control"
|
||||
value={form.apt}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, apt: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">City</label>
|
||||
<input
|
||||
type="text"
|
||||
name="city"
|
||||
className="form-control"
|
||||
value={form.city}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, city: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">State</label>
|
||||
<input
|
||||
type="text"
|
||||
name="state"
|
||||
maxLength={2}
|
||||
className="form-control"
|
||||
value={form.state}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, state: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">ZIP</label>
|
||||
<input
|
||||
type="text"
|
||||
name="zip"
|
||||
className="form-control"
|
||||
value={form.zip}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, zip: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<div className="mb-3">
|
||||
<label className="form-label d-block">Accept School Policy</label>
|
||||
<div className="form-check form-switch">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={form.accept_school_policy}
|
||||
onChange={(e) =>
|
||||
setForm((c) => (c ? { ...c, accept_school_policy: e.target.checked } : c))
|
||||
}
|
||||
/>
|
||||
<label className="form-check-label">Accepted</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">User Type</label>
|
||||
<input
|
||||
type="text"
|
||||
name="user_type"
|
||||
className="form-control"
|
||||
value={form.user_type}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, user_type: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">RFID Tag</label>
|
||||
<input
|
||||
type="text"
|
||||
name="rfid_tag"
|
||||
className="form-control"
|
||||
value={form.rfid_tag}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, rfid_tag: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">School ID</label>
|
||||
<input
|
||||
type="text"
|
||||
name="school_id"
|
||||
className="form-control"
|
||||
value={form.school_id}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, school_id: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Failed Attempts</label>
|
||||
<input
|
||||
type="number"
|
||||
name="failed_attempts"
|
||||
className="form-control"
|
||||
value={form.failed_attempts}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, failed_attempts: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Last Failed At</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
name="last_failed_at"
|
||||
className="form-control"
|
||||
value={form.last_failed_at}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, last_failed_at: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Semester</label>
|
||||
<input
|
||||
type="text"
|
||||
name="semester"
|
||||
className="form-control"
|
||||
value={form.semester}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, semester: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">School Year</label>
|
||||
<input
|
||||
type="text"
|
||||
name="school_year"
|
||||
className="form-control"
|
||||
value={form.school_year}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, school_year: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Status</label>
|
||||
<input
|
||||
type="text"
|
||||
name="status"
|
||||
className="form-control"
|
||||
value={form.status}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, status: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label d-block">Is Suspended</label>
|
||||
<div className="form-check form-switch">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={form.is_suspended}
|
||||
onChange={(e) =>
|
||||
setForm((c) => (c ? { ...c, is_suspended: e.target.checked } : c))
|
||||
}
|
||||
/>
|
||||
<label className="form-check-label">Suspended</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label d-block">Is Verified</label>
|
||||
<div className="form-check form-switch">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={form.is_verified}
|
||||
onChange={(e) =>
|
||||
setForm((c) => (c ? { ...c, is_verified: e.target.checked } : c))
|
||||
}
|
||||
/>
|
||||
<label className="form-check-label">Verified</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Token</label>
|
||||
<input
|
||||
type="text"
|
||||
name="token"
|
||||
className="form-control"
|
||||
value={form.token}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, token: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Updated At</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
name="updated_at"
|
||||
className="form-control"
|
||||
value={form.updated_at}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, updated_at: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Created At</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
name="created_at"
|
||||
className="form-control"
|
||||
value={form.created_at}
|
||||
onChange={(e) => setForm((c) => (c ? { ...c, created_at: e.target.value } : c))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" onClick={() => closeModal()}>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" className="btn btn-primary" disabled={saving}>
|
||||
{saving ? 'Saving…' : 'Save Changes'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user