generated from StanfordBDHG/NextJSTemplate
-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Add user actions ## ♻️ Current situation & Problem Users need to be able to create/delete/update Users and Patients ### Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md).
- Loading branch information
1 parent
720a0df
commit 13821c1
Showing
66 changed files
with
4,718 additions
and
1,313 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// | ||
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
import { type ReactNode } from 'react' | ||
import { getAuthenticatedOnlyApp } from '@/modules/firebase/guards' | ||
import { UserContextProvider } from '@/modules/firebase/UserProvider' | ||
import { getDocDataOrThrow } from '@/modules/firebase/utils' | ||
import { getUserInfo } from '@/packages/design-system/src/modules/auth/user' | ||
|
||
interface DashboardLayoutProps { | ||
children?: ReactNode | ||
} | ||
|
||
export const dynamic = 'force-dynamic' | ||
|
||
const DashboardLayout = async ({ children }: DashboardLayoutProps) => { | ||
const { currentUser, docRefs } = await getAuthenticatedOnlyApp() | ||
const user = await getDocDataOrThrow(docRefs.user(currentUser.uid)) | ||
return ( | ||
<UserContextProvider | ||
user={{ | ||
auth: getUserInfo(currentUser), | ||
user, | ||
}} | ||
> | ||
{children} | ||
</UserContextProvider> | ||
) | ||
} | ||
|
||
export default DashboardLayout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// | ||
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
'use client' | ||
import { z } from 'zod' | ||
import { type User } from '@/modules/firebase/utils' | ||
import { Button } from '@/packages/design-system/src/components/Button' | ||
import { Input } from '@/packages/design-system/src/components/Input' | ||
import { | ||
Select, | ||
SelectContent, | ||
SelectItem, | ||
SelectTrigger, | ||
SelectValue, | ||
} from '@/packages/design-system/src/components/Select' | ||
import { Field } from '@/packages/design-system/src/forms/Field' | ||
import { useForm } from '@/packages/design-system/src/forms/useForm' | ||
import { | ||
getUserName, | ||
type UserInfo, | ||
} from '@/packages/design-system/src/modules/auth/user' | ||
|
||
export const patientFormSchema = z.object({ | ||
email: z.string().min(1, 'Email is required'), | ||
displayName: z.string(), | ||
invitationCode: z.string(), | ||
clinician: z.string(), | ||
}) | ||
|
||
export type PatientFormSchema = z.infer<typeof patientFormSchema> | ||
|
||
interface PatientFormProps { | ||
clinicians: Array<{ | ||
id: string | ||
displayName: string | null | ||
email: string | null | ||
}> | ||
userInfo?: Pick<UserInfo, 'email' | 'displayName' | 'uid'> | ||
user?: Pick<User, 'organization' | 'invitationCode' | 'clinician'> | ||
onSubmit: (data: PatientFormSchema) => Promise<void> | ||
clinicianPreselectId?: string | ||
} | ||
|
||
export const PatientForm = ({ | ||
user, | ||
clinicians, | ||
userInfo, | ||
onSubmit, | ||
clinicianPreselectId, | ||
}: PatientFormProps) => { | ||
const isEdit = !!user | ||
const form = useForm({ | ||
formSchema: patientFormSchema, | ||
defaultValues: { | ||
email: userInfo?.email ?? '', | ||
displayName: userInfo?.displayName ?? '', | ||
invitationCode: user?.invitationCode ?? '', | ||
clinician: user?.clinician ?? clinicianPreselectId ?? '', | ||
}, | ||
}) | ||
|
||
const handleSubmit = form.handleSubmit(async (data) => { | ||
await onSubmit(data) | ||
}) | ||
|
||
return ( | ||
<form onSubmit={handleSubmit} className="mx-auto w-full max-w-2xl"> | ||
<Field | ||
control={form.control} | ||
name="email" | ||
label="Email" | ||
render={({ field }) => <Input type="email" {...field} />} | ||
/> | ||
<Field | ||
control={form.control} | ||
name="displayName" | ||
label="Display name" | ||
render={({ field }) => <Input {...field} />} | ||
/> | ||
{isEdit && ( | ||
<Field | ||
control={form.control} | ||
name="invitationCode" | ||
label="Invitation code" | ||
render={({ field }) => <Input {...field} />} | ||
/> | ||
)} | ||
<Field | ||
control={form.control} | ||
name="clinician" | ||
label="Clinician" | ||
render={({ field }) => ( | ||
<Select onValueChange={field.onChange} {...field}> | ||
<SelectTrigger> | ||
<SelectValue placeholder="Clinician" /> | ||
</SelectTrigger> | ||
<SelectContent> | ||
{clinicians.map((clinician) => ( | ||
<SelectItem value={clinician.id} key={clinician.id}> | ||
{getUserName(clinician)} | ||
</SelectItem> | ||
))} | ||
</SelectContent> | ||
</Select> | ||
)} | ||
/> | ||
<Button type="submit" isPending={form.formState.isSubmitting}> | ||
{isEdit ? 'Edit' : 'Invite'} patient | ||
</Button> | ||
</form> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// | ||
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
'use client' | ||
import { Pencil, Trash } from 'lucide-react' | ||
import Link from 'next/link' | ||
import { | ||
deletePatient, | ||
deleteInvitation, | ||
} from '@/app/(dashboard)/patients/actions' | ||
import type { Patient } from '@/app/(dashboard)/patients/page' | ||
import { routes } from '@/modules/routes' | ||
import { RowDropdownMenu } from '@/packages/design-system/src/components/DataTable' | ||
import { DropdownMenuItem } from '@/packages/design-system/src/components/DropdownMenu' | ||
import { getUserName } from '@/packages/design-system/src/modules/auth/user' | ||
import { ConfirmDeleteDialog } from '@/packages/design-system/src/molecules/ConfirmDeleteDialog' | ||
import { useOpenState } from '@/packages/design-system/src/utils/useOpenState' | ||
|
||
interface PatientMenuProps { | ||
patient: Patient | ||
} | ||
|
||
export const PatientMenu = ({ patient }: PatientMenuProps) => { | ||
const deleteConfirm = useOpenState() | ||
|
||
const handleDelete = async () => { | ||
if (patient.resourceType === 'user') { | ||
await deletePatient({ userId: patient.resourceId }) | ||
} else { | ||
await deleteInvitation({ invitationId: patient.resourceId }) | ||
} | ||
deleteConfirm.close() | ||
} | ||
|
||
return ( | ||
<> | ||
<ConfirmDeleteDialog | ||
open={deleteConfirm.isOpen} | ||
onOpenChange={deleteConfirm.setIsOpen} | ||
entityName="patient" | ||
itemName={getUserName(patient)} | ||
onDelete={handleDelete} | ||
/> | ||
<RowDropdownMenu> | ||
{patient.resourceType === 'user' && ( | ||
<DropdownMenuItem asChild> | ||
<Link href={routes.patients.patient(patient.resourceId)}> | ||
<Pencil /> | ||
Edit | ||
</Link> | ||
</DropdownMenuItem> | ||
)} | ||
<DropdownMenuItem onClick={deleteConfirm.open}> | ||
<Trash /> | ||
Delete | ||
</DropdownMenuItem> | ||
</RowDropdownMenu> | ||
</> | ||
) | ||
} |
Oops, something went wrong.