Skip to content

Commit

Permalink
patient can provide their subscriber id and payor id to get insurance…
Browse files Browse the repository at this point in the history
… in canvas
  • Loading branch information
trevorpfiz committed Dec 3, 2023
1 parent 419603e commit cc11bf8
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import { api } from "~/trpc/react";
import { uploadTestPdf } from "./upload-test";

export function ConsentForm(props: { onSuccess?: () => void }) {
const router = useRouter();
const toaster = useToast();

const mutation = api.canvas.submitConsent.useMutation({
onSuccess: (data) => {
toaster.toast({
Expand All @@ -49,9 +52,6 @@ export function ConsentForm(props: { onSuccess?: () => void }) {
},
});

const router = useRouter();
const toaster = useToast();

const form = useForm<ConsentForm>({
resolver: zodResolver(consentFormSchema),
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"use client";

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

import { coverageFormSchema } from "@acme/api/src/validators";
import type { CoverageForm } from "@acme/api/src/validators";
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 { useToast } from "@acme/ui/use-toast";

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

export function CoverageForm(props: { onSuccess?: () => void }) {
const router = useRouter();
const toaster = useToast();

const mutation = api.canvas.submitCoverage.useMutation({
onSuccess: (data) => {
toaster.toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
});

// Call the passed onSuccess prop if it exists
if (props.onSuccess) {
props.onSuccess();
}
},
onError: (error) => {
// Show an error toast
toaster.toast({
title: "Error submitting consent",
description: "An issue occurred while submitting. Please try again.",
variant: "destructive",
});
},
});

const form = useForm<CoverageForm>({
resolver: zodResolver(coverageFormSchema),
});

function onSubmit(data: CoverageForm) {
const requestBody = {
status: "active",
subscriber: {
reference: `Patient/b685d0d97f604e1fb60f9ed089abc410`,
},
subscriberId: `${data.subscriberId}`,
beneficiary: {
reference: `Patient/b685d0d97f604e1fb60f9ed089abc410`,
},
relationship: {
coding: [
{
system: "http://hl7.org/fhir/ValueSet/subscriber-relationship",
code: "self",
},
],
},
payor: [
{
identifier: {
system: "https://www.claim.md/services/era/",
value: `${data.payorId}`,
},
display: "Insurer company name",
},
],
order: 1,
};

// Submit coverage
mutation.mutate({
query: {},
body: requestBody,
});

toaster.toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
});
}

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="subscriberId"
render={({ field }) => (
<FormItem>
<FormLabel>{`Subscriber ID`}</FormLabel>
<FormControl>
<Input placeholder="" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="payorId"
render={({ field }) => (
<FormItem>
<FormLabel>{`Payor ID`}</FormLabel>
<FormControl>
<Input placeholder="" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"use client";

import { useRouter } from "next/navigation";
import { motion } from "framer-motion";
import { Balancer } from "react-wrap-balancer";

import { CoverageForm } from "./coverage-form";

export default function Coverage() {
const router = useRouter();

return (
<motion.div
className="my-auto flex h-full w-full flex-col items-center justify-center"
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3, type: "spring" }}
>
<motion.div
variants={{
show: {
transition: {
staggerChildren: 0.2,
},
},
}}
initial="hidden"
animate="show"
className="flex flex-col rounded-xl bg-background/60 p-8"
>
<motion.h1
className="mb-4 font-cal text-2xl font-bold transition-colors sm:text-3xl"
variants={{
hidden: { opacity: 0, x: 250 },
show: {
opacity: 1,
x: 0,
transition: { duration: 0.4, type: "spring" },
},
}}
>
<Balancer>{`Converage`}</Balancer>
</motion.h1>
<motion.div
variants={{
hidden: { opacity: 0, x: 100 },
show: {
opacity: 1,
x: 0,
transition: { duration: 0.4, type: "spring" },
},
}}
>
<CoverageForm
onSuccess={() => router.push("/onboarding?step=consent")}
/>
</motion.div>
</motion.div>
</motion.div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,39 @@ interface QuestionnaireProps {
export function QuestionnaireForm(props: QuestionnaireProps) {
const { questionnaireId, onSuccess } = props;

const router = useRouter();
const toaster = useToast();

const { isLoading, isError, data, error } =
api.canvas.getQuestionnaire.useQuery({
id: questionnaireId,
});

const mutation = api.canvas.submitQuestionnaireResponse.useMutation();
const mutation = api.canvas.submitQuestionnaireResponse.useMutation({
onSuccess: (data) => {
toaster.toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
});

const router = useRouter();
const toaster = useToast();
// Call the passed onSuccess prop if it exists
if (onSuccess) {
onSuccess();
}
},
onError: (error) => {
// Show an error toast
toaster.toast({
title: "Error submitting consent",
description: "An issue occurred while submitting. Please try again.",
variant: "destructive",
});
},
});

const [dynamicSchema, setDynamicSchema] = useState<ZodSchema | null>(null);

Expand Down Expand Up @@ -94,35 +118,9 @@ export function QuestionnaireForm(props: QuestionnaireProps) {
item: transformedItems,
};

try {
mutation.mutate({
body: requestBody,
});

if (mutation.isSuccess && onSuccess) {
toaster.toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">
{JSON.stringify(data, null, 2)}
</code>
</pre>
),
});

onSuccess();
} else {
// router.push(`/onboarding`);
}
} catch (error) {
toaster.toast({
title: "Error submitting answer",
variant: "destructive",
description:
"An issue occurred while submitting answer. Please try again.",
});
}
mutation.mutate({
body: requestBody,
});
}

if (isLoading) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useSearchParams } from "next/navigation";
import { AnimatePresence } from "framer-motion";

import Consent from "./_components/consent";
import Coverage from "./_components/coverage";
import { Done } from "./_components/done";
import Questionnaire from "./_components/questionnaire";
import Welcome from "./_components/welcome";
Expand All @@ -18,6 +19,7 @@ export function Onboarding(props: { templateId: string }) {
<div className="mx-auto flex h-[calc(100vh-8rem)] w-full max-w-4xl flex-col items-center">
<AnimatePresence mode="wait">
{!step && <Welcome key="welcome" />}
{step === "coverage" && <Coverage key="coverage" />}
{step === "consent" && <Consent key="consent" />}
{step === "questionnaire" && ( // can increment query param for each section
<div key="questionnaire">
Expand Down
34 changes: 33 additions & 1 deletion packages/api/src/router/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { TRPCError } from "@trpc/server";
import { z } from "zod";

import {
get_ReadConsent,
get_ReadPatient,
get_SearchPatient,
post_CreateConsent,
post_CreateCoverage,
post_CreatePatient,
} from "../canvas/canvas-client";
import { createTRPCRouter, protectedCanvasProcedure } from "../trpc";
Expand Down Expand Up @@ -169,7 +171,8 @@ export const canvasRouter = createTRPCRouter({
const consentData = await api.get("/Consent/{consent_id}", {
path: { consent_id: id },
});
return consentData;
const validatedData = get_ReadConsent.response.parse(consentData);
return validatedData;
} catch (error) {
// Handle any other errors
throw new TRPCError({
Expand Down Expand Up @@ -204,4 +207,33 @@ export const canvasRouter = createTRPCRouter({
});
}
}),

// Coverage procedures
submitCoverage: protectedCanvasProcedure
.input(post_CreateCoverage.parameters)
.mutation(async ({ ctx, input }) => {
const { api, canvasToken } = ctx;
const { query, body } = input;

if (!canvasToken) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Canvas token is missing",
});
}

try {
const coverageData = await api.post("/Coverage", {
query,
body,
});
return coverageData;
} catch (error) {
// Handle any other errors
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "An error occurred while fetching coverage data",
});
}
}),
});
11 changes: 11 additions & 0 deletions packages/api/src/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ export const consentFormSchema = z.object({
});
export type ConsentForm = z.infer<typeof consentFormSchema>;

// Coverage
export const coverageFormSchema = z.object({
subscriberId: z.string().refine((value) => value.length > 0, {
message: "Can't be blank.",
}),
payorId: z.string().refine((value) => value.length > 0, {
message: "Can't be blank.",
}),
});
export type CoverageForm = z.infer<typeof coverageFormSchema>;

// Questionnaire
export const valueCodingSchema = z.object({
code: z.string(),
Expand Down

0 comments on commit cc11bf8

Please sign in to comment.