Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

patient can provide their subscriber id and payor id to get insurance… #6

Merged
merged 1 commit into from
Dec 3, 2023
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
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
Loading