From 8deec252ebc59a03fe3c053280286ac60de79b15 Mon Sep 17 00:00:00 2001 From: arhamathar Date: Tue, 7 Jan 2025 15:49:10 +0530 Subject: [PATCH 01/16] diet preference options with kebab case value --- .../Questionnaire/QuestionTypes/EncounterQuestion.tsx | 6 +++--- src/types/emr/encounter.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Questionnaire/QuestionTypes/EncounterQuestion.tsx b/src/components/Questionnaire/QuestionTypes/EncounterQuestion.tsx index 00ba6ce0536..82341545d43 100644 --- a/src/components/Questionnaire/QuestionTypes/EncounterQuestion.tsx +++ b/src/components/Questionnaire/QuestionTypes/EncounterQuestion.tsx @@ -392,9 +392,9 @@ export function EncounterQuestion({ Vegetarian - Dairy Free - Nut Free - Gluten Free + Dairy Free + Nut Free + Gluten Free Vegan Halal Kosher diff --git a/src/types/emr/encounter.ts b/src/types/emr/encounter.ts index 7b6fdb5bed6..afb13b6ffcc 100644 --- a/src/types/emr/encounter.ts +++ b/src/types/emr/encounter.ts @@ -43,9 +43,9 @@ export type EncounterDischargeDisposition = export type EncounterDietPreference = | "vegetarian" - | "diary_free" - | "nut_free" - | "gluten_free" + | "diary-free" + | "nut-free" + | "gluten-free" | "vegan" | "halal" | "kosher" From e4c4e71e612559938aa32a8e88b93a3419d6c0c6 Mon Sep 17 00:00:00 2001 From: Bodhish Thomas Date: Tue, 7 Jan 2025 17:32:03 +0530 Subject: [PATCH 02/16] feat: Add new fields to Question interface (#9824) --- package-lock.json | 6 +- src/Routers/routes/questionnaireRoutes.tsx | 2 + .../Questionnaire/QuestionnaireEditor.tsx | 1214 +++++++++++++++++ src/types/questionnaire/questionnaire.ts | 4 + src/types/questionnaire/questionnaireApi.ts | 4 +- 5 files changed, 1224 insertions(+), 6 deletions(-) create mode 100644 src/components/Questionnaire/QuestionnaireEditor.tsx diff --git a/package-lock.json b/package-lock.json index edfd7250ef1..a95d10cbac0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -146,8 +146,8 @@ "node": ">=22.8.0" }, "optionalDependencies": { - "@esbuild/linux-arm64": "*", - "@esbuild/linux-x64": "*", + "@esbuild/linux-arm64": "latest", + "@esbuild/linux-x64": "latest", "@rollup/rollup-linux-arm64-gnu": "4.29.1", "@rollup/rollup-linux-x64-gnu": "4.29.1" } @@ -3720,7 +3720,6 @@ "version": "17.0.0", "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-17.0.0.tgz", "integrity": "sha512-LDDPOix/5N0j5QZxubiW9T0M0+1PR0rTDWeZF5pu1Tz91UQnuVK4qQ/EjY83Qm2QeX0eM8qDXANfDh3VVqtR4Q==", - "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.25.6", "css-box-model": "^1.2.1", @@ -5379,7 +5378,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.4.tgz", "integrity": "sha512-Sch9idFJHJTMH9YNpxxESqABcAFweJG4tKv+0zo0m5XBvUSL8FM5xKcJLFLXononpePs8IclyX1KieL5SDUNgA==", - "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.1", diff --git a/src/Routers/routes/questionnaireRoutes.tsx b/src/Routers/routes/questionnaireRoutes.tsx index 69d3a114d5a..044f9fcc1d4 100644 --- a/src/Routers/routes/questionnaireRoutes.tsx +++ b/src/Routers/routes/questionnaireRoutes.tsx @@ -1,4 +1,5 @@ import { QuestionnaireList } from "@/components/Questionnaire"; +import QuestionnaireEditor from "@/components/Questionnaire/QuestionnaireEditor"; import { QuestionnaireShow } from "@/components/Questionnaire/show"; import { AppRoutes } from "../AppRouter"; @@ -6,6 +7,7 @@ import { AppRoutes } from "../AppRouter"; const QuestionnaireRoutes: AppRoutes = { "/questionnaire": () => , "/questionnaire/:id": ({ id }) => , + "/questionnaire/:id/edit": ({ id }) => , }; export default QuestionnaireRoutes; diff --git a/src/components/Questionnaire/QuestionnaireEditor.tsx b/src/components/Questionnaire/QuestionnaireEditor.tsx new file mode 100644 index 00000000000..8c7f0fa0854 --- /dev/null +++ b/src/components/Questionnaire/QuestionnaireEditor.tsx @@ -0,0 +1,1214 @@ +import { DragDropContext, Draggable, Droppable } from "@hello-pangea/dnd"; +import { useMutation } from "@tanstack/react-query"; +import { useNavigate } from "raviger"; +import { useEffect, useState } from "react"; +import { toast } from "sonner"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Textarea } from "@/components/ui/textarea"; + +import mutate from "@/Utils/request/mutate"; +import useQuery from "@/Utils/request/useQuery"; +import { + AnswerOption, + EnableWhen, + Question, + QuestionType, + StructuredQuestionType, +} from "@/types/questionnaire/question"; +import { + QuestionStatus, + QuestionnaireDetail, + QuestionnaireUpdate, + SubjectType, +} from "@/types/questionnaire/questionnaire"; +import questionnaireApi from "@/types/questionnaire/questionnaireApi"; + +import Loading from "../Common/Loading"; +import { QuestionnaireForm } from "./QuestionnaireForm"; + +interface QuestionnaireEditorProps { + id: string; +} + +export default function QuestionnaireEditor({ id }: QuestionnaireEditorProps) { + const navigate = useNavigate(); + const [activeTab, setActiveTab] = useState<"edit" | "preview">("edit"); + const [expandedQuestions, setExpandedQuestions] = useState>( + new Set(), + ); + + const { + data: initialQuestionnaire, + loading, + error, + } = useQuery(questionnaireApi.detail, { + pathParams: { id }, + }); + + const { mutate: updateQuestionnaire, isPending } = useMutation({ + mutationFn: mutate(questionnaireApi.update, { + pathParams: { id }, + }), + onSuccess: () => { + toast.success("Questionnaire updated successfully"); + }, + onError: (_error) => { + toast.error("Failed to update questionnaire"); + }, + }); + + const [questionnaire, setQuestionnaire] = + useState(null); + + useEffect(() => { + if (initialQuestionnaire) { + setQuestionnaire({ + ...initialQuestionnaire, + organizations: ["628b44da-3da0-4321-a75d-e53697b281bb"], + }); + } + }, [initialQuestionnaire]); + + if (loading) return ; + if (error) { + return ( + + + Error + + Failed to load questionnaire. Please try again later. + + + ); + } + if (!questionnaire) { + return ( + + + Not Found + + The requested questionnaire could not be found. + + + ); + } + + const updateQuestionnaireField = ( + field: keyof QuestionnaireDetail, + value: any, + ) => { + setQuestionnaire((prev) => (prev ? { ...prev, [field]: value } : null)); + }; + + const handleCancel = () => { + navigate(`/questionnaire/${id}`); + }; + + const handleDragEnd = (result: any) => { + if (!result.destination) return; + + const items = Array.from(questionnaire.questions); + const [reorderedItem] = items.splice(result.source.index, 1); + items.splice(result.destination.index, 0, reorderedItem); + + updateQuestionnaireField("questions", items); + }; + + const toggleQuestionExpanded = (questionId: string) => { + setExpandedQuestions((prev) => { + const next = new Set(prev); + if (next.has(questionId)) { + next.delete(questionId); + } else { + next.add(questionId); + } + return next; + }); + }; + + return ( +
+ {/* Top bar: Title + Buttons */} +
+
+

Edit Questionnaire

+

+ {questionnaire.description} +

+
+
+ + +
+
+ + setActiveTab(v as "edit" | "preview")} + > + + Edit + Preview + + + +
+ {/* Left Sidebar: Navigation */} +
+ + + Navigation + + + + + + + + + Properties + + +
+ + +
+ +
+ + + updateQuestionnaireField("version", e.target.value) + } + /> +
+ +
+ + +
+
+
+
+ + {/* Main Content */} +
+ + + Basic Information + + +
+ + + updateQuestionnaireField("title", e.target.value) + } + /> +
+ +
+ +