Skip to content

Commit

Permalink
Merge pull request #14 from trevorpfiz/franklinjaradev/ttp-22-patient…
Browse files Browse the repository at this point in the history
…-can-pay-bills

Franklinjaradev/ttp 22 patient can pay bills
  • Loading branch information
trevorpfiz authored Dec 7, 2023
2 parents 6d64fe1 + 4f806e7 commit 4166c5d
Show file tree
Hide file tree
Showing 11 changed files with 649 additions and 28 deletions.
123 changes: 102 additions & 21 deletions apps/nextjs/src/app/(authenticated)/patient/[patientId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Button } from "@acme/ui/button";
import {
Card,
CardContent,
Expand All @@ -16,7 +17,9 @@ import {
} from "@acme/ui/table";

import CreateMessage from "~/components/communication/create-message";
import { formatDateTime } from "~/lib/utils";
import { api } from "~/trpc/server";
import CreatePayment from "../_components/create-payment";

export const runtime = "edge";

Expand All @@ -39,6 +42,18 @@ const PatientIdPage = async ({ params }: { params: { patientId: string } }) => {
},
});

const billDocuments = await api.document.searchBillDocument.query({
query: {
subject: `Patient/${params.patientId}`,
},
});

const payments = await api.payment.searchPayments.query({
query: {
request: `Patient/${params.patientId}`,
},
});

return (
<Card className="w-full">
<CardHeader>
Expand All @@ -63,16 +78,19 @@ const PatientIdPage = async ({ params }: { params: { patientId: string } }) => {
</TableRow>
</TableHeader>
<TableBody>
{listReceivedMsgs.entry?.map((msg, i) => (
<TableRow key={i}>
<TableCell>{msg.resource.sent}</TableCell>
<TableCell>
{msg.resource.received ?? "No received yet"}
</TableCell>
<TableCell>{msg.resource.recipient[0]?.reference}</TableCell>
<TableCell>{msg.resource.payload[0]?.contentString}</TableCell>
</TableRow>
))}
{listReceivedMsgs.total > 0 &&
listReceivedMsgs.entry.map((msg, i) => (
<TableRow key={i}>
<TableCell>{msg.resource.sent}</TableCell>
<TableCell>
{msg.resource.received ?? "No received yet"}
</TableCell>
<TableCell>{msg.resource.recipient[0]?.reference}</TableCell>
<TableCell>
{msg.resource.payload[0]?.contentString}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
Expand All @@ -88,20 +106,83 @@ const PatientIdPage = async ({ params }: { params: { patientId: string } }) => {
<TableHead>Received At</TableHead>
<TableHead>Recipient</TableHead>
<TableHead>Payload</TableHead>
<TableHead>Read</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{listSendMsgs.entry?.map((msg, i) => (
<TableRow key={i}>
<TableCell>{msg.resource.sent}</TableCell>
<TableCell>
{msg.resource.received ?? "No received yet"}
</TableCell>
<TableCell>{msg.resource.recipient[0]?.reference}</TableCell>
<TableCell>{msg.resource.payload[0]?.contentString}</TableCell>
</TableRow>
))}
{listSendMsgs.total > 0 &&
listSendMsgs.entry.map((msg, i) => (
<TableRow key={i}>
<TableCell>{msg.resource.sent}</TableCell>
<TableCell>
{msg.resource.received ?? "No received yet"}
</TableCell>
<TableCell>{msg.resource.recipient[0]?.reference}</TableCell>
<TableCell>
{msg.resource.payload[0]?.contentString}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
<CardContent>
<CardHeader>
<CardTitle>Bills</CardTitle>
</CardHeader>
<Table>
<TableCaption>A list of your bills</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Created At</TableHead>
<TableHead>Bill</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{billDocuments.total > 0 &&
billDocuments?.entry?.map((doc, i) => (
<TableRow key={i}>
<TableCell>
{/* @ts-ignore */}
{formatDateTime(new Date(doc?.resource?.date!))}{" "}
</TableCell>
<TableCell>
<a
// @ts-expect-error
href={doc?.resource?.content[0]!.attachment.url}
target="_blank"
rel="noopener noreferrer"
>
<Button variant={"link"}>View Bill</Button>
</a>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
<CardContent>
<CardHeader>
<CardTitle>Payments</CardTitle>
</CardHeader>
<CreatePayment reference={`Patient/${params.patientId}`} />
<Table>
<TableCaption>A list of your payments</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Created At</TableHead>
<TableHead>Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{payments.total > 0 &&
payments.entry.map((bill, i) => (
<TableRow key={i}>
<TableCell>
{formatDateTime(new Date(bill.resource.created))}{" "}
</TableCell>
<TableCell>{bill.resource.amount.value.toFixed(2)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"use client";

import { useRouter } from "next/navigation";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";

import { Button } from "@acme/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@acme/ui/form";
import { Input } from "@acme/ui/input";
import { Popover, PopoverContent, PopoverTrigger } from "@acme/ui/popover";
import { toast } from "@acme/ui/use-toast";

import { api } from "~/trpc/react";

const CreatePayment = ({ reference }: { reference: string }) => {
const router = useRouter();

const CreatePaymentFormSchema = z.object({
amount: z.coerce.number().gt(0, {
message: "Amount must be greater than 0",
}),
});

const createPayment = api.payment.createPayment.useMutation({
onSuccess: () => {
toast({
title: "Payment created",
description: "Payment created successfully",
});
router.refresh();
},
onError: (error) => {
toast({
title: "Payment creation failed",
description: error.message,
variant: "destructive",
});
},
});

const createPaymentForm = useForm<z.infer<typeof CreatePaymentFormSchema>>({
resolver: zodResolver(CreatePaymentFormSchema),
defaultValues: {
amount: 0,
},
});

const onSubmitCreatePayment = async (
data: z.infer<typeof CreatePaymentFormSchema>,
) => {
try {
await createPayment.mutateAsync({
body: {
resourceType: "PaymentNotice",
status: "active",
recipient: {},
request: {
reference: reference,
},
payment: {},
created: new Date().toISOString(),
amount: {
value: data.amount,
currency: "USD",
},
},
});

createPaymentForm.reset();
} catch (e) {
console.log(e);
}
};

return (
<Popover>
<PopoverTrigger asChild>
<Button>Create Payment</Button>
</PopoverTrigger>
<PopoverContent>
<Form {...createPaymentForm}>
<form
onSubmit={createPaymentForm.handleSubmit(onSubmitCreatePayment)}
className="w-full space-y-6"
>
<FormField
control={createPaymentForm.control}
name="amount"
render={({ field }) => (
<FormItem>
<FormLabel>Amount</FormLabel>
<FormControl>
<Input type="number" placeholder="10.00" {...field} />
</FormControl>
<FormDescription>
The amount of the payment to be made.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full">
Submit Payment
</Button>
</form>
</Form>
</PopoverContent>
</Popover>
);
};

export default CreatePayment;
11 changes: 7 additions & 4 deletions apps/nextjs/src/components/practitioner/search-practitioner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ const SearchPractitioner = ({
const [value, setValue] = useState("");

const { data } = api.practitioner.searchPractitioners.useQuery({
query: {},
query: {
name: "",
},
});

const practitioners = data?.entry.map((entry) => entry.resource) ?? [];

return (
Expand All @@ -41,9 +44,9 @@ const SearchPractitioner = ({
>
{value
? practitioners?.find(
(practitioner) =>
practitioner.name[0]!.text.toLowerCase() === value,
)?.name[0]!.text
(practitioner) =>
practitioner.name[0]!.text.toLowerCase() === value,
)?.name[0]!.text
: "Select practitioner..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
Expand Down
15 changes: 12 additions & 3 deletions packages/api/src/canvas/canvas-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ import {
BundleSchema,
bundleSchema,
careTeamSchema,
contentSchema,
documentReferenceSchema,
entryResourceSchema,
linkSchema,
resourceSchema,
ResourceSchema,
scheduleBundleSchema,
slotBundleSchema,
} from "../validators";
import { searchPaymentNoticeBundleSchema } from "../validators/payment";
import { searchDocumentNoticeBundleSchema } from "../validators/document-reference";

export type post_GetAnOauthToken = typeof post_GetAnOauthToken;
export const post_GetAnOauthToken = {
Expand Down Expand Up @@ -1479,7 +1486,7 @@ export const get_ReadDocumentreference = {
document_reference_id: z.string(),
}),
}),
response: z.unknown(),
response: documentReferenceSchema,
};

export type get_SearchDocumentreference = typeof get_SearchDocumentreference;
Expand All @@ -1496,7 +1503,7 @@ export const get_SearchDocumentreference = {
category: z.string().optional(),
}),
}),
response: z.unknown(),
response: searchDocumentNoticeBundleSchema,
};

export type get_ReadEncounter = typeof get_ReadEncounter;
Expand Down Expand Up @@ -2707,7 +2714,7 @@ export const get_SearchPaymentnotice = {
request: z.string().optional(),
}),
}),
response: z.unknown(),
response: searchPaymentNoticeBundleSchema,
};

export type post_CreatePaymentnotice = typeof post_CreatePaymentnotice;
Expand All @@ -2723,6 +2730,8 @@ export const post_CreatePaymentnotice = {
})
.optional(),
created: z.string().optional(),
payment: z.object({}),
recipient: z.object({}),
request: z
.object({
reference: z.string().optional(),
Expand Down
4 changes: 4 additions & 0 deletions packages/api/src/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { careTeamRouter } from "./router/care-team";
import { communicationRouter } from "./router/communication";
import { consentRouter } from "./router/consent";
import { coverageRouter } from "./router/coverage";
import { documentRouter } from "./router/document";
import { patientRouter } from "./router/patient";
import { patientMedicalHistoryRouter } from "./router/patient-medical-history";
import { paymentRouter } from "./router/payment";
import { postRouter } from "./router/post";
import { practitionerRouter } from "./router/practitioner";
import { questionnaireRouter } from "./router/questionnaire";
Expand All @@ -22,12 +24,14 @@ export const appRouter = createTRPCRouter({
careTeam: careTeamRouter,
practitioner: practitionerRouter,
communication: communicationRouter,
document: documentRouter,
patient: patientRouter,
questionnaire: questionnaireRouter,
consent: consentRouter,
coverage: coverageRouter,
patientMedicalHistory: patientMedicalHistoryRouter,
scheduling: schedulingRouter,
payment: paymentRouter,
});

// export type definition of API
Expand Down
2 changes: 2 additions & 0 deletions packages/api/src/router/communication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
get_SearchCommunicationSender,
} from "../canvas/canvas-client";
import { createTRPCRouter, protectedCanvasProcedure } from "../trpc";
import { env } from "../env.mjs";
import { BundleSchema } from "../validators";

export const communicationRouter = createTRPCRouter({
createMsg: protectedCanvasProcedure
Expand Down
Loading

0 comments on commit 4166c5d

Please sign in to comment.