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

Separate benchmark assignment modal from trpc #489

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"type-css:check": "npx tcm src --listDifferent",
"test": "next build --no-lint && ava",
"test-storybook": "test-storybook --maxWorkers=2",
"docker-reup": "docker compose -f supporting_services/docker-compose.yml up -d",
"db:migrate": "npx tsx src/backend/scripts/migrate-database.ts",
"db:reset": "npx tsx src/backend/scripts/reset-database.ts",
"db:seed": "npx tsx src/backend/scripts/seed-database.ts",
Expand Down
2 changes: 1 addition & 1 deletion src/backend/routers/iep.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ test("basic flow - add/get goals, benchmarks, tasks", async (t) => {
const gotBenchmark = await trpc.iep.getBenchmark.query({
benchmark_id: benchmark2Id,
});
t.is(gotBenchmark[0].description, "benchmark 2");
t.is(gotBenchmark.description, "benchmark 2");

// TODO: Don't query db directly and use an API method instead. Possibly create a getTasks method later
t.truthy(
Expand Down
77 changes: 75 additions & 2 deletions src/backend/routers/iep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,14 +363,87 @@ export const iep = router({
.query(async (req) => {
const { benchmark_id } = req.input;

// NOTE: existing code
const result = await req.ctx.db
.selectFrom("benchmark")
.innerJoin("task", "benchmark.benchmark_id", "task.benchmark_id")
.innerJoin("user", "task.assignee_id", "user.user_id")
.where("benchmark.benchmark_id", "=", benchmark_id)
.selectAll()
.execute();
.select((eb) => [
"benchmark.description",
"benchmark.instructions",
"benchmark.frequency",
"benchmark.number_of_trials",
"benchmark.benchmark_id",
jsonArrayFrom(
eb
.selectFrom("user")
.selectAll()
.whereRef("task.assignee_id", "=", "user.user_id")
.orderBy("user.first_name")
).as("assignees"),
])
.executeTakeFirstOrThrow();
return result;
}),

//.innerJoin("task", "benchmark.benchmark_id", "task.benchmark_id")
// .innerJoin("goal", "benchmark.goal_id", "goal.goal_id")
// .innerJoin("iep", "goal.iep_id", "iep.iep_id")
// .innerJoin("student", "iep.student_id", "student.student_id")

// "task.task_id",
// "student.first_name",
// "student.last_name",
// "goal.category",
// "benchmark.description",
// "benchmark.instructions",
// "benchmark.frequency",
// "benchmark.number_of_trials",
// "benchmark.benchmark_id",
// "task.due_date",
// "task.seen",
// "task.trial_count",

// .select([
// "benchmark.description",
// "benchmark.instructions",
// "benchmark.frequency",
// "benchmark.number_of_trials",
// "benchmark.benchmark_id",
// ])

// {
// benchmark: {
// id,
// title,
// desc,
// assignees: [
// { id:, name }
// ]
// }
// }

// jsonArrayFrom(
// eb
// .selectFrom("trial_data")
// .select([
// "trial_data.trial_data_id",
// "trial_data.success",
// "trial_data.unsuccess",
// "trial_data.submitted",
// "trial_data.notes",
// "trial_data.created_at",
// ])
// .whereRef("trial_data.task_id", "=", "task.task_id")
// .whereRef(
// "trial_data.created_by_user_id",
// "=",
// "task.assignee_id"
// )
// .orderBy("trial_data.created_at")
// ).as("assignees"),

getBenchmarkByAssignee: hasCaseManager
.input(
z.object({
Expand Down
10 changes: 9 additions & 1 deletion src/client/lib/trpc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { createTRPCReact } from "@trpc/react-query";
import {
createTRPCReact,
type inferReactQueryProcedureOptions,
} from "@trpc/react-query";
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
import { AppRouter } from "@/backend/routers/_app";

export type ReactQueryOptions = inferReactQueryProcedureOptions<AppRouter>;
export type RouterInputs = inferRouterInputs<AppRouter>;
export type RouterOutputs = inferRouterOutputs<AppRouter>;

export const trpc = createTRPCReact<AppRouter>();
110 changes: 110 additions & 0 deletions src/components/benchmarks/BenchmarkAssignment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { trpc } from "@/client/lib/trpc";
import { useState, useRef } from "react";

import { AssignmentDuration } from "./Duration-Selection-Step";
import { BenchmarkAssignmentModal } from "@/components/benchmarks/BenchmarkAssignmentModal";

interface BenchmarkAssignmentProps {
isOpen: boolean;
onClose: () => void;
benchmark_id: string;
}

export const STEPS = ["PARA_SELECTION", "DURATION_SELECTION"];
export type Step = (typeof STEPS)[number];

export const BenchmarkAssignment = (props: BenchmarkAssignmentProps) => {
const [selectedParaIds, setSelectedParaIds] = useState<string[]>([]);
const nextButtonRef = useRef<HTMLButtonElement>(null);
const [assignmentDuration, setAssignmentDuration] =
useState<AssignmentDuration>({ type: "forever" });
const [currentModalSelection, setCurrentModalSelection] =
useState<Step>("PARA_SELECTION");
const { data: myParas } = trpc.case_manager.getMyParas.useQuery();
const { data: benchmark } = trpc.iep.getBenchmark.useQuery({
benchmark_id: props.benchmark_id,
}); // maybe it should include assignments, or have a flag to include assignments

const [errorMessage, setErrorMessage] = useState<string>("");

const assignTaskToPara = trpc.iep.assignTaskToParas.useMutation();

const handleParaToggle = (paraId: string) => () => {
setErrorMessage("");
setSelectedParaIds((prev) => {
if (prev.includes(paraId)) {
return prev.filter((id) => id !== paraId);
} else {
return [...prev, paraId];
}
});
};

const handleClose = () => {
props.onClose();
setSelectedParaIds([]);
setErrorMessage("");
setCurrentModalSelection("PARA_SELECTION");
};

const handleBack = () => {
const currentStepIndex = STEPS.indexOf(currentModalSelection);
const previousStep = STEPS[currentStepIndex - 1];
if (previousStep) {
setCurrentModalSelection(previousStep);
}
};

const handleNext = async () => {
if (nextButtonRef.current) {
nextButtonRef.current.blur();
}
const currentStepIndex = STEPS.indexOf(currentModalSelection);
const nextStep = STEPS[currentStepIndex + 1];
if (nextStep) {
setCurrentModalSelection(nextStep);
} else {
// Reached end, save
try {
await assignTaskToPara.mutateAsync({
benchmark_id: props.benchmark_id,
para_ids: selectedParaIds,
due_date:
assignmentDuration.type === "until_date"
? assignmentDuration.date
: undefined,
trial_count:
assignmentDuration.type === "minimum_number_of_collections"
? assignmentDuration.minimumNumberOfCollections
: undefined,
});
handleClose();
} catch (err) {
// TODO: issue #450
console.log(err);
if (err instanceof Error) {
setErrorMessage(err.message);
}
}
}
};

return (
<BenchmarkAssignmentModal
isOpen={props.isOpen}
handleClose={handleClose}
benchmark={benchmark}
myParas={myParas}
currentModalSelection={currentModalSelection}
errorMessage={errorMessage}
selectedParaIds={selectedParaIds}
handleParaToggle={handleParaToggle}
assignmentDuration={assignmentDuration}
setAssignmentDuration={setAssignmentDuration}
isAssignTaskToParaLoading={assignTaskToPara.isLoading}
handleBack={handleBack}
handleNext={handleNext}
nextButtonRef={nextButtonRef}
/>
);
};
Loading
Loading