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

Questionnaire List & Edit UI Redesign #10678

Merged
merged 20 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7a144e2
Valuesets WIP
gigincg Feb 12, 2025
f1e2be2
Merge branch 'develop' into valueset_ui
gigincg Feb 12, 2025
579275a
Value Set options in Answer
amjithtitus09 Feb 13, 2025
5885df1
Merge branch 'develop' of https://github.com/ohcnetwork/care_fe into …
amjithtitus09 Feb 13, 2025
b1a5735
Move Question Types to types file
gigincg Feb 13, 2025
2fcbe0f
Questionnaire Redesign
amjithtitus09 Feb 18, 2025
e68f897
Responsiveness
amjithtitus09 Feb 18, 2025
b6d44e3
Merge branch 'develop' of https://github.com/ohcnetwork/care_fe into …
amjithtitus09 Feb 18, 2025
2f9d956
Fix merge conflicts
amjithtitus09 Feb 18, 2025
b8ee52a
Remove unused imports
amjithtitus09 Feb 18, 2025
9fffc52
Merge branch 'develop' of https://github.com/ohcnetwork/care_fe into …
amjithtitus09 Feb 19, 2025
3391699
Fix width
amjithtitus09 Feb 19, 2025
0a50f3c
Organization required, i18n
amjithtitus09 Feb 19, 2025
e52aa6c
isPreview
amjithtitus09 Feb 20, 2025
3a65239
Merge branch 'develop' of https://github.com/ohcnetwork/care_fe into …
amjithtitus09 Feb 20, 2025
367a226
Update src/components/Questionnaire/QuestionnaireEditor.tsx
amjithtitus09 Feb 20, 2025
9709690
Merge branch 'develop' into quest-ui-redesign
amjithtitus09 Feb 20, 2025
4461967
Merge branch 'develop' of https://github.com/ohcnetwork/care_fe into…
amjithtitus09 Feb 21, 2025
70a93a2
Rmoved bg-background, reverted eslint config
amjithtitus09 Feb 21, 2025
0394f0f
Merge branch 'develop' of https://github.com/ohcnetwork/care_fe into …
amjithtitus09 Feb 21, 2025
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
4 changes: 2 additions & 2 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ export default [
rules: {
...tseslint.configs.recommended.rules,
"@typescript-eslint/no-unused-vars": [
"error",
"warn",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-unused-expressions": [
"error",
"warn",
{ allowShortCircuit: true, allowTernary: true },
],
"@typescript-eslint/no-explicit-any": "warn",
Expand Down
9 changes: 9 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@
"create_position_preset": "Create a new position preset",
"create_position_preset_description": "Creates a new position preset in Care from the current position of the camera for the given name",
"create_preset_prerequisite": "To create presets for this bed, you'll need to link the camera to the bed first.",
"create_questionnaire": "Create Questionnaire",
"create_resource_request": "Create Request",
"create_schedule_template": "Create Schedule Template",
"create_tag": "Create Tag",
Expand Down Expand Up @@ -1387,6 +1388,7 @@
"name_of_shifting_approving_facility": "Name of shifting approving facility",
"nationality": "Nationality",
"nationality_is_required": "Nationality is required",
"navigation": "Navigation",
"nearby_facilities": "Nearby Facilities",
"network_failure": "Network Failure. Please check your internet connectivity.",
"never": "never",
Expand Down Expand Up @@ -1745,6 +1747,7 @@
"professional_info_note_view": "View user's professional information",
"profile": "Profile",
"profile_picture_deleted": "Profile picture deleted",
"properties": "Properties",
"proposed": "Proposed",
"provisional": "Provisional",
"qualification": "Qualification",
Expand Down Expand Up @@ -1880,6 +1883,7 @@
"resume": "Resume",
"retake": "Retake",
"retake_recording": "Retake Recording",
"retired": "Retired",
"return_to_care": "Return to CARE",
"return_to_login": "Return to Login",
"return_to_password_reset": "Return to Password Reset",
Expand Down Expand Up @@ -1960,8 +1964,10 @@
"search_medication": "Search Medication",
"search_medications": "Search Medications",
"search_medicine": "Search Medicine",
"search_organizations": "Search Organizations",
"search_patient_page_text": "Search for existing patients using their phone number or create a new patient record",
"search_patients": "Search Patients",
"search_questionnaires": "Search Questionnaires",
"search_resource": "Search Resource",
"search_tags": "Search tags...",
"search_user": "Search User",
Expand Down Expand Up @@ -2018,6 +2024,7 @@
"select_skills": "Select and add some skills",
"select_status": "Select Status",
"select_sub_department": "Select sub-department",
"select_subject_type": "Select Subject Type",
"select_time": "Select time",
"select_time_slot": "Select time slot",
"select_user": "Select user",
Expand Down Expand Up @@ -2126,6 +2133,7 @@
"stream_uuid": "Stream UUID",
"sub_category": "Sub Category",
"subject": "Subject",
"subject_type": "Subject Type",
"submit": "Submit",
"submitting": "Submitting",
"subscribe": "Subscribe",
Expand Down Expand Up @@ -2362,6 +2370,7 @@
"verify_patient_identifier": "Please verify the patient identifier",
"verify_patient_identity": "Verify Patient Identity",
"verify_using": "Verify Using",
"version": "Version",
"video_call": "Video Call",
"video_conference_link": "Video Conference Link",
"view": "View",
Expand Down
16 changes: 9 additions & 7 deletions src/components/Questionnaire/CloneQuestionnaireSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useMutation, useQuery } from "@tanstack/react-query";
import { Building, Check, Loader2 } from "lucide-react";
import { useNavigate } from "raviger";
import { useState } from "react";
import { toast } from "sonner";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -67,12 +68,11 @@ export default function CloneQuestionnaireSheet({
navigate(`/admin/questionnaire/${data.slug}`);
setOpen(false);
},
onError: (error: any) => {
if (error.response?.status === 400) {
setError("This slug is already in use. Please choose a different one.");
} else {
setError("Failed to clone questionnaire. Please try again.");
}
onError: (error) => {
const errorData = error.cause as { errors: { msg: string }[] };
errorData.errors.forEach((er) => {
toast.error(er.msg);
});
},
});

Expand Down Expand Up @@ -212,7 +212,9 @@ export default function CloneQuestionnaireSheet({
</Button>
<Button
onClick={handleClone}
disabled={isCloning || !newSlug.trim()}
disabled={
isCloning || !newSlug.trim() || selectedIds.length === 0
}
>
{isCloning ? (
<>
Expand Down
244 changes: 124 additions & 120 deletions src/components/Questionnaire/ManageQuestionnaireTagsSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "@/components/ui/command";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import {
Sheet,
SheetContent,
Expand Down Expand Up @@ -69,7 +70,7 @@ export default function ManageQuestionnaireTagsSheet({
}),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["questionnaire", questionnaire.slug],
queryKey: ["questionnaireDetail", questionnaire.slug],
});
toast.success("Tags updated successfully");
setOpen(false);
Expand Down Expand Up @@ -150,133 +151,136 @@ export default function ManageQuestionnaireTagsSheet({
<SheetTitle>{t("manage_tags")}</SheetTitle>
<SheetDescription>{t("manage_tags_description")}</SheetDescription>
</SheetHeader>

<div className="space-y-6 py-4">
{/* Selected Tags */}
<div className="space-y-4">
<h3 className="text-sm font-medium">{t("selected_tags")}</h3>
<div className="flex flex-wrap gap-2">
{selectedTags?.map((tag) => (
<Badge
key={tag.slug}
variant="secondary"
className="flex items-center gap-1"
>
{tag.name}
<Button
variant="ghost"
size="icon"
className="h-4 w-4 p-0 hover:bg-transparent"
onClick={() => handleToggleTag(tag.slug)}
disabled={isUpdating}
<ScrollArea className="h-[calc(100vh-11rem)]">
{/* Selected Tags */}
<div className="space-y-4">
<h3 className="text-sm font-medium">{t("selected_tags")}</h3>
<div className="flex flex-wrap gap-2">
{selectedTags?.map((tag) => (
<Badge
key={tag.slug}
variant="secondary"
className="flex items-center gap-1"
>
<X className="h-3 w-3" />
</Button>
</Badge>
))}
{!isLoading && (!selectedTags || selectedTags.length === 0) && (
<p className="text-sm text-gray-500">{t("no_tags_selected")}</p>
)}
{tag.name}
<Button
variant="ghost"
size="icon"
className="h-4 w-4 p-0 hover:bg-transparent"
onClick={() => handleToggleTag(tag.slug)}
disabled={isUpdating}
>
<X className="h-3 w-3" />
</Button>
</Badge>
))}
{!isLoading && (!selectedTags || selectedTags.length === 0) && (
<p className="text-sm text-gray-500">
{t("no_tags_selected")}
</p>
)}
</div>
</div>
</div>

{/* Tag Selector */}
<div className="space-y-4">
<h3 className="text-sm font-medium">{t("add_tags")}</h3>
<Command className="rounded-lg border shadow-md">
<CommandInput
placeholder={t("search_tags")}
onValueChange={setSearchQuery}
/>
<CommandList>
<CommandEmpty>{t("no_tags_found")}</CommandEmpty>
<CommandGroup>
{isLoading ? (
<div className="flex items-center justify-center py-6">
<Loader2 className="h-6 w-6 animate-spin" />
</div>
) : (
filteredTags?.map((tag) => (
<CommandItem
key={tag.slug}
value={tag.slug}
onSelect={() => handleToggleTag(tag.slug)}
>
<div className="flex flex-1 items-center gap-2">
<Hash className="h-4 w-4" />
<span>{tag.name}</span>
</div>
{selectedSlugs.includes(tag.slug) && (
<Check className="h-4 w-4" />
)}
</CommandItem>
))
)}
</CommandGroup>
</CommandList>
</Command>
</div>
{/* Tag Selector */}
<div className="space-y-4">
<h3 className="text-sm font-medium">{t("add_tags")}</h3>
<Command className="rounded-lg border shadow-md">
<CommandInput
placeholder={t("search_tags")}
onValueChange={setSearchQuery}
/>
<CommandList>
<CommandEmpty>{t("no_tags_found")}</CommandEmpty>
<CommandGroup>
{isLoading ? (
<div className="flex items-center justify-center py-6">
<Loader2 className="h-6 w-6 animate-spin" />
</div>
) : (
filteredTags?.map((tag) => (
<CommandItem
key={tag.slug}
value={tag.slug}
onSelect={() => handleToggleTag(tag.slug)}
>
<div className="flex flex-1 items-center gap-2">
<Hash className="h-4 w-4" />
<span>{tag.name}</span>
</div>
{selectedSlugs.includes(tag.slug) && (
<Check className="h-4 w-4" />
)}
</CommandItem>
))
)}
</CommandGroup>
</CommandList>
</Command>
</div>

{/* Create New Tag */}
<Collapsible
open={isCreateOpen}
onOpenChange={setIsCreateOpen}
className="rounded-lg border p-4"
>
<CollapsibleTrigger asChild>
<Button
variant="outline"
size="sm"
className="flex w-full items-center justify-between"
>
<div className="flex items-center gap-2">
<Plus className="h-4 w-4" />
<span>{t("create_new_tag")}</span>
{/* Create New Tag */}
<Collapsible
open={isCreateOpen}
onOpenChange={setIsCreateOpen}
className="rounded-lg border p-4"
>
<CollapsibleTrigger asChild>
<Button
variant="outline"
size="sm"
className="flex w-full items-center justify-between"
>
<div className="flex items-center gap-2">
<Plus className="h-4 w-4" />
<span>{t("create_new_tag")}</span>
</div>
<CareIcon
icon={isCreateOpen ? "l-angle-up" : "l-angle-down"}
className="h-4 w-4"
/>
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="mt-4 space-y-4">
<div className="space-y-2">
<Label htmlFor="tag-name">{t("tag_name")}</Label>
<Input
id="tag-name"
value={newTagName}
onChange={(e) => setNewTagName(e.target.value)}
placeholder={t("enter_tag_name")}
/>
</div>
<CareIcon
icon={isCreateOpen ? "l-angle-up" : "l-angle-down"}
className="h-4 w-4"
/>
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="mt-4 space-y-4">
<div className="space-y-2">
<Label htmlFor="tag-name">{t("tag_name")}</Label>
<Input
id="tag-name"
value={newTagName}
onChange={(e) => setNewTagName(e.target.value)}
placeholder={t("enter_tag_name")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="tag-slug">{t("tag_slug")}</Label>
<Input
id="tag-slug"
value={newTagSlug}
onChange={(e) => setNewTagSlug(e.target.value)}
placeholder={t("enter_tag_slug")}
/>
</div>
<Button
onClick={handleCreateTag}
disabled={isCreating || !newTagName || !newTagSlug}
className="w-full"
>
{isCreating ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
{t("creating")}
</>
) : (
t("create_tag")
)}
</Button>
</CollapsibleContent>
</Collapsible>
<div className="space-y-2">
<Label htmlFor="tag-slug">{t("tag_slug")}</Label>
<Input
id="tag-slug"
value={newTagSlug}
onChange={(e) => setNewTagSlug(e.target.value)}
placeholder={t("enter_tag_slug")}
/>
</div>
<Button
onClick={handleCreateTag}
disabled={isCreating || !newTagName || !newTagSlug}
className="w-full"
>
{isCreating ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
{t("creating")}
</>
) : (
t("create_tag")
)}
</Button>
</CollapsibleContent>
</Collapsible>
</ScrollArea>
</div>

<SheetFooter className="absolute bottom-0 left-0 right-0 p-4 border-t bg-background">
<SheetFooter className=" bottom-0 left-0 right-0 p-4 border-t bg-background">
<div className="flex w-full justify-end gap-4">
<Button
variant="outline"
Expand Down
Loading
Loading