Skip to content

Commit

Permalink
feat: add test filters (#90)
Browse files Browse the repository at this point in the history
- adds mode and status filter to test screen

Closes #89
  • Loading branch information
PupoSDC authored Jan 20, 2024
1 parent ec4a1ae commit d5691de
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,38 +74,36 @@ export const QuestionSearch = container<Props, Params, Data>(
placeholder="search Questions..."
/>

<FormProvider {...form}>
<SearchFilters
activeFilters={numberOfFilters}
fallback={
<>
<Select size="sm" />
<Select size="sm" />
</>
}
filters={
<>
<HookFormSelect size="sm" {...form.register("searchField")}>
<Option value={"all"}>All Fields</Option>
<Option value={"text"}>Question</Option>
<Option value={"questionId"}>Id</Option>
<Option value={"learningObjectives"}>
Learning Objectives
<SearchFilters
activeFilters={numberOfFilters}
fallback={
<>
<Select size="sm" />
<Select size="sm" />
</>
}
filters={
<FormProvider {...form}>
<HookFormSelect size="sm" {...form.register("searchField")}>
<Option value={"all"}>All Fields</Option>
<Option value={"text"}>Question</Option>
<Option value={"questionId"}>Id</Option>
<Option value={"learningObjectives"}>
Learning Objectives
</Option>
<Option value={"externalIds"}>External Ids</Option>
</HookFormSelect>
<HookFormSelect size="sm" {...form.register("subject")}>
<Option value={"all"}>All Subjects</Option>
{subjects.map(({ id, shortName }) => (
<Option value={id} key={id}>
{shortName}
</Option>
<Option value={"externalIds"}>External Ids</Option>
</HookFormSelect>
<HookFormSelect size="sm" {...form.register("subject")}>
<Option value={"all"}>All Subjects</Option>
{subjects.map(({ id, shortName }) => (
<Option value={id} key={id}>
{shortName}
</Option>
))}
</HookFormSelect>
</>
}
/>
</FormProvider>
))}
</HookFormSelect>
</FormProvider>
}
/>
</Stack>

<QuestionList
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createUsePersistenceHook } from "../../hooks/use-persistence";
import type { QuestionBankName } from "@chair-flight/base/types";
import type { UseFormReturn } from "react-hook-form";

const searchConfigSchema = z.object({
mode: z.enum(["all", "study", "exam"]),
status: z.enum(["all", "created", "started", "finished"]),
});

type SearchConfig = z.infer<typeof searchConfigSchema>;

const defaultSearchConfig: z.infer<typeof searchConfigSchema> = {
mode: "all",
status: "all",
};

const searchPersistence = {
"cf-test-search-atpl": createUsePersistenceHook<SearchConfig>(
"cf-test-search-atpl",
),
"cf-test-search-type": createUsePersistenceHook<SearchConfig>(
"cf-test-search-type",
),
"cf-test-search-prep": createUsePersistenceHook<SearchConfig>(
"cf-test-search-prep",
),
};

const resolver = zodResolver(searchConfigSchema);

export const useSearchConfig = (
questionBank: QuestionBankName,
): [SearchConfig, UseFormReturn<SearchConfig>] => {
const key = `cf-test-search-${questionBank}` as const;
const useSearchPersistence = searchPersistence[key];
const { persistedData, setPersistedData } = useSearchPersistence();
const defaultValues = persistedData ?? defaultSearchConfig;
const form = useForm({ defaultValues, resolver });
const mode = form.watch("mode");
const status = form.watch("status");

useEffect(() => {
setPersistedData({ mode, status });
}, [mode, status, setPersistedData]);

return [{ mode, status }, form];
};
76 changes: 69 additions & 7 deletions libs/react/containers/src/tests/test-search/test-search.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FormProvider } from "react-hook-form";
import { default as DeleteIcon } from "@mui/icons-material/DeleteOutlineOutlined";
import { default as PlayIcon } from "@mui/icons-material/PlayArrowOutlined";
import { default as EyeIcon } from "@mui/icons-material/VisibilityOutlined";
Expand All @@ -8,14 +9,22 @@ import {
IconButton,
Link,
ListItemContent,
Select,
Option,
Stack,
Tooltip,
Typography,
selectClasses,
} from "@mui/joy";
import { processTest } from "@chair-flight/core/app";
import { SearchList } from "@chair-flight/react/components";
import {
HookFormSelect,
SearchFilters,
SearchList,
} from "@chair-flight/react/components";
import { container } from "../../wraper/container";
import { useTestProgress } from "../hooks/use-test-progress";
import { useSearchConfig } from "./test-search-config-schema";
import type { QuestionBankName } from "@chair-flight/base/types";

type Props = {
Expand All @@ -24,18 +33,62 @@ type Props = {

export const TestSearch = container<Props>(
({ questionBank, sx, component = "section" }) => {
const [{ mode, status }, form] = useSearchConfig(questionBank);

const tests = useTestProgress((s) => s.tests);
const deleteTest = useTestProgress((s) => s.deleteTest);

const testsAsList = Object.values(tests)
.sort((a, b) => b.createdAtEpochMs - a.createdAtEpochMs)
.filter((test) => test.questionBank === questionBank)
.filter((test) => {
if (test.questionBank !== questionBank) return false;
if (status !== "all" && status !== test.status) return false;
if (mode !== "all" && mode !== test.mode) return false;
return true;
})
.map((test) => ({ ...test, ...processTest(test) }));

const numberOfFilters = Number(mode !== "all") + Number(status !== "all");

return (
<Stack component={component} sx={sx}>
<Stack sx={{ pb: { xs: 1, md: 2 } }}>
<Stack
direction={"row"}
sx={{
gap: 1,
justifyContent: "flex-end",
mb: { xs: 1, sm: 2 },
[`& .${selectClasses.root}`]: {
width: "13em",
},
}}
>
<SearchFilters
activeFilters={numberOfFilters}
filters={
<FormProvider {...form}>
<HookFormSelect size="sm" {...form.register("mode")}>
<Option value={"all"}>All Modes</Option>
<Option value={"study"}>Study</Option>
<Option value={"exam"}>Exam</Option>
</HookFormSelect>
<HookFormSelect size="sm" {...form.register("status")}>
<Option value={"all"}>All States</Option>
<Option value={"started"}>Started</Option>
<Option value={"finished"}>Finished</Option>
</HookFormSelect>
</FormProvider>
}
fallback={
<>
<Select size="sm" />
<Select size="sm" />
</>
}
/>

<Button
sx={{ ml: "auto" }}
size="sm"
component={Link}
href={`/modules/${questionBank}/tests/create`}
>
Expand All @@ -51,8 +104,10 @@ export const TestSearch = container<Props>(
<thead>
<tr>
<th style={{ width: "4em" }}>Score</th>
<th style={{ width: "4em" }}>Type</th>
<th style={{ width: "5em" }}>State</th>
<th>Title</th>
<th style={{ width: "12em" }}>Type</th>

<th style={{ textAlign: "center", width: "8em" }}>
No. Questions
</th>
Expand Down Expand Up @@ -93,9 +148,16 @@ export const TestSearch = container<Props>(
component="td"
sx={{ color: `${test.color}.500`, fontWeight: 700 }}
>
{test.title}
{test.mode}
</Box>
<td>{test.mode}</td>
<Box
component="td"
sx={{ color: `${test.color}.500`, fontWeight: 700 }}
>
{test.status}
</Box>
<Box component="td">{test.title}</Box>

<Box component="td" sx={{ textAlign: "center" }}>
{test.questions.length}
</Box>
Expand Down

1 comment on commit d5691de

@vercel
Copy link

@vercel vercel bot commented on d5691de Jan 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.