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

feature: Added services history section in device details page (10828) #10934

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
Prev Previous commit
Next Next commit
fixed some issues
  • Loading branch information
Yashgupta9330 committed Mar 2, 2025
commit 58847c4eaa18af8d905469ae496477f510ead736
5 changes: 0 additions & 5 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,6 @@
"booked_by": "Booked by",
"bradycardia": "Bradycardia",
"breathlessness_level": "Breathlessness level",
"button_cancel": "Cancel",
"button_save": "Save",
"button_saving": "Saving...",
"button_update": "Update",
"button_updating": "Updating...",
"by_name": "by <strong>{{by}}</strong>",
"camera": "Camera",
"camera_bed_link_success": "Camera linked to bed successfully.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useQuery } from "@tanstack/react-query";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { format } from "date-fns";
import { Edit, Plus } from "lucide-react";
import { useState } from "react";
Expand All @@ -17,7 +17,7 @@ import {
} from "@/components/ui/table";

import query from "@/Utils/request/query";
import { ServiceHistoryResponse } from "@/types/device/device";
import { ServiceHistory } from "@/types/device/device";
import deviceApi from "@/types/device/deviceApi";

import ServiceHistorySheet from "./ServiceHistorySheet";
Expand All @@ -34,18 +34,14 @@ export default function DeviceServiceHistory({
const { t } = useTranslation();
const [isServiceSheetOpen, setIsServiceSheetOpen] = useState(false);
const [selectedServiceHistory, setSelectedServiceHistory] =
useState<ServiceHistoryResponse | null>(null);

const {
data: serviceHistory,
isLoading,
refetch,
} = useQuery({
useState<ServiceHistory | null>(null);
const queryClient = useQueryClient();
const { data: serviceHistory, isLoading } = useQuery({
queryKey: ["device", facilityId, deviceId, "serviceHistory"],
queryFn: query(deviceApi.serviceHistory.list, {
pathParams: {
facility_external_id: facilityId,
device_external_id: deviceId,
facilityId: facilityId,
deviceId: deviceId,
},
}),
});
Expand All @@ -55,14 +51,16 @@ export default function DeviceServiceHistory({
setIsServiceSheetOpen(true);
};

const handleEditService = (service: ServiceHistoryResponse) => {
const handleEditService = (service: ServiceHistory) => {
setSelectedServiceHistory(service);
setIsServiceSheetOpen(true);
};

const handleServiceSheetClose = () => {
setIsServiceSheetOpen(false);
refetch();
queryClient.invalidateQueries({
queryKey: ["device", facilityId, deviceId, "serviceHistory"],
});
};

return (
Expand Down Expand Up @@ -93,7 +91,7 @@ export default function DeviceServiceHistory({
</TableRow>
</TableHeader>
<TableBody>
{serviceHistory.results.map((service: ServiceHistoryResponse) => (
{serviceHistory.results.map((service: ServiceHistory) => (
<TableRow key={service.id}>
<TableCell className="font-medium">
{format(new Date(service.serviced_on), "PPP")}
Expand All @@ -116,7 +114,7 @@ export default function DeviceServiceHistory({
</Table>
) : (
<div className="text-center py-6 text-muted-foreground">
{t("service_records_none", "No service history available")}
{t("service_records_none")}
</div>
)}
</CardContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { format } from "date-fns";
import { CalendarIcon } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { z } from "zod";

import { cn } from "@/lib/utils";

import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Textarea } from "@/components/ui/textarea";

import mutate from "@/Utils/request/mutate";
import { ServiceHistory } from "@/types/device/device";
import deviceApi from "@/types/device/deviceApi";

interface ServiceHistoryFormProps {
facilityId: string;
deviceId: string;
serviceRecord?: ServiceHistory | null;
onOpenChange: (open: boolean) => void;
}

const formSchema = z.object({
note: z.string().min(1, { message: "Notes are required" }),
serviced_on: z.date({ required_error: "Service date is required" }),
});

type FormValues = z.infer<typeof formSchema>;

export default function ServiceHistoryForm({
facilityId,
deviceId,
serviceRecord,
onOpenChange,
}: ServiceHistoryFormProps) {
const { t } = useTranslation();
const queryClient = useQueryClient();

const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
note: "",
serviced_on: new Date(),
},
});

useEffect(() => {
if (serviceRecord) {
form.reset({
note: serviceRecord.note,
serviced_on: new Date(serviceRecord.serviced_on),
});
} else {
form.reset({
note: "",
serviced_on: new Date(),
});
}
}, [serviceRecord, form]);

const createMutation = useMutation({
mutationFn: mutate(deviceApi.serviceHistory.create, {
pathParams: {
facility_external_id: facilityId,
device_external_id: deviceId,
},
}),
onError: (error) => {
console.error("Failed to create service record:", error);
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["device", facilityId, deviceId],
});
onOpenChange(false);
form.reset();
},
});

const updateMutation = useMutation({
mutationFn: mutate(deviceApi.serviceHistory.update, {
pathParams: {
facilityId,
deviceId,
externalId: serviceRecord?.id,
},
}),
onError: (error) => {
console.error("Failed to update service record:", error);
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["device", facilityId, deviceId],
});
onOpenChange(false);
form.reset();
},
});

const onSubmit = (values: FormValues) => {
const payload = {
...values,
serviced_on: values.serviced_on.toISOString(),
meta: serviceRecord?.meta || {},
};

if (serviceRecord) {
updateMutation.mutate(payload);
} else {
createMutation.mutate(payload);
}
};

const isPending = createMutation.isPending || updateMutation.isPending;

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="serviced_on"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>{t("service_date")}</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-full pl-3 text-left font-normal",
!field.value && "text-muted-foreground",
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>{t("select_date")}</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="note"
render={({ field }) => (
<FormItem>
<FormLabel>{t("service_notes")}</FormLabel>
<FormControl>
<Textarea
placeholder={t("service_notes_enter")}
{...field}
rows={5}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end space-x-2 pt-4">
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
>
{t("cancel")}
</Button>
<Button type="submit" disabled={isPending}>
{isPending
? serviceRecord
? t("updating")
: t("saving")
: serviceRecord
? t("update")
: t("save")}
</Button>
</div>
</form>
</Form>
);
}
Loading