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

Patient Files #9480

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
373 changes: 373 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.2.0",
"@radix-ui/react-slider": "^1.2.2",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-tooltip": "^1.1.4",
"@sentry/browser": "^8.45.1",
Expand Down
12 changes: 12 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@
"action_irreversible": "This action is irreversible",
"actions": "Actions",
"active": "Active",
"active_files": "Active Files",
"active_prescriptions": "Active Prescriptions",
"add": "Add",
"add_as": "Add as",
Expand All @@ -289,6 +290,7 @@
"add_consultation_update": "Add Consultation Update",
"add_details_of_patient": "Add Details of Patient",
"add_facility": "Add Facility",
"add_files": "Add Files",
"add_insurance_details": "Add Insurance Details",
"add_location": "Add Location",
"add_new_beds": "Add New Bed(s)",
Expand Down Expand Up @@ -333,6 +335,7 @@
"approving_facility": "Name of Approving Facility",
"archive": "Archive",
"archived": "Archived",
"archived_files": "Archived Files",
"are_non_editable_fields": "are non-editable fields",
"are_you_still_watching": "Are you still watching?",
"are_you_sure_want_to_delete": "Are you sure you want to delete {{name}}?",
Expand Down Expand Up @@ -471,6 +474,7 @@
"claims": "Claims",
"clear": "Clear",
"clear_all_filters": "Clear All Filters",
"clear_filter": "Clear Filter",
"clear_home_facility": "Clear Home Facility",
"clear_home_facility_confirm": "Are you sure you want to clear the home facility",
"clear_home_facility_error": "Error while clearing home facility. Try again later.",
Expand Down Expand Up @@ -579,6 +583,7 @@
"cylinders": "Cylinders",
"cylinders_per_day": "Cylinders/day",
"daily_rounds": "Daily Rounds",
"date": "Date",
"date_and_time": "Date and Time",
"date_declared_positive": "Date of declaring positive",
"date_of_admission": "Date of Admission",
Expand Down Expand Up @@ -758,8 +763,10 @@
"file_list_headings__patient": "Patient Files",
"file_list_headings__sample_report": "Sample Report",
"file_list_headings__supporting_info": "Supporting Info",
"file_name": "File Name",
"file_preview": "File Preview",
"file_preview_not_supported": "Can't preview this file. Try downloading it.",
"file_type": "File Type",
"file_uploaded": "File Uploaded Successfully",
"filter": "Filter",
"filter_by": "Filter By",
Expand Down Expand Up @@ -1089,6 +1096,7 @@
"password_uppercase_validation": "Password must contain at least one uppercase letter (A-Z)",
"password_validation": "Password must contain at least: 8 characters, 1 uppercase letter (A-Z), 1 lowercase letter (a-z), and 1 number (0-9)",
"patient": "Patient",
"patient-files": "Files",
"patient-notes": "Notes",
"patient__general-info": "General Info",
"patient__insurance-details": "Insurance Details",
Expand Down Expand Up @@ -1142,6 +1150,8 @@
"phone_number": "Phone Number",
"phone_number_at_current_facility": "Phone Number of Contact person at current Facility",
"pincode": "Pincode",
"play": "Play",
"play_audio": "Play Audio",
"please_assign_bed_to_patient": "Please assign a bed to this patient",
"please_confirm_password": "Please confirm your new password.",
"please_enter_a_reason_for_the_shift": "Please enter a reason for the shift.",
Expand Down Expand Up @@ -1342,6 +1352,7 @@
"settings_and_filters": "Settings and Filters",
"severity_of_breathlessness": "Severity of Breathlessness",
"sex": "Sex",
"shared_by": "Shared By",
"shift": "Shift Patient",
"shift_request_updated_successfully": "Shift request updated successfully",
"shifting": "Shifting",
Expand Down Expand Up @@ -1483,6 +1494,7 @@
"updating": "Updating",
"upload": "Upload",
"upload_an_image": "Upload an image",
"upload_file": "Upload File",
"upload_headings__consultation": "Upload New Consultation File",
"upload_headings__patient": "Upload New Patient File",
"upload_headings__sample_report": "Upload Sample Report",
Expand Down
148 changes: 148 additions & 0 deletions src/components/Common/AudioPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { useCallback, useEffect, useRef, useState } from "react";

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

import CareIcon from "@/CAREUI/icons/CareIcon";

import { Button } from "@/components/ui/button";
import { Slider } from "@/components/ui/slider";

interface AudioPlayerProps {
src: string;
className?: string;
}

function AudioPlayer({ src, className }: AudioPlayerProps) {
const [isPlaying, setIsPlaying] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [isMuted, setIsMuted] = useState(false);
const audioRef = useRef<HTMLAudioElement | null>(null);

const formatTime = (time: number) => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
};

const handleTimeUpdate = useCallback(() => {
if (audioRef.current) {
setCurrentTime(audioRef.current.currentTime);
}
}, []);

const handleLoadedMetadata = useCallback(() => {
if (audioRef.current) {
audioRef.current.currentTime = 24 * 60 * 60; // Seek to 24 hours
}
}, []);

const handleDurationChange = useCallback(() => {
if (audioRef.current && isFinite(audioRef.current.duration)) {
setDuration(audioRef.current.duration);
setIsLoading(false);
audioRef.current.currentTime = 0;
setCurrentTime(0);
}
}, []);

useEffect(() => {
// Create the audio element
const audio = new Audio(src);
audio.preload = "metadata";

// Add event listeners
audio.addEventListener("loadedmetadata", handleLoadedMetadata);
audio.addEventListener("durationchange", handleDurationChange);
audio.addEventListener("timeupdate", handleTimeUpdate);
audio.addEventListener("ended", () => setIsPlaying(false));

// Set the ref
audioRef.current = audio;

// Cleanup
return () => {
audio.removeEventListener("loadedmetadata", handleLoadedMetadata);
audio.removeEventListener("durationchange", handleDurationChange);
audio.removeEventListener("timeupdate", handleTimeUpdate);
audio.removeEventListener("ended", () => setIsPlaying(false));
audio.remove();
};
}, [src, handleLoadedMetadata, handleDurationChange, handleTimeUpdate]);

const togglePlay = useCallback(() => {
if (audioRef.current) {
if (isPlaying) {
audioRef.current.pause();
} else {
audioRef.current.play();
}
setIsPlaying(!isPlaying);
}
}, [isPlaying]);

const toggleMute = useCallback(() => {
if (audioRef.current) {
audioRef.current.muted = !audioRef.current.muted;
setIsMuted(!isMuted);
}
}, [isMuted]);

const handleSliderChange = useCallback((value: number[]) => {
if (audioRef.current) {
audioRef.current.currentTime = value[0];
setCurrentTime(value[0]);
}
}, []);

const stopPlayback = () => {
if (audioRef.current) {
audioRef.current.pause();
audioRef.current.currentTime = 0;
setIsPlaying(false);
}
};

const Player = () => (
<div
className={cn(
"flex flex-row items-center p-2 rounded-md bg-secondary-300 border border-secondary-200 m-3",
className,
)}
>
<Button
variant="ghost"
size="icon"
onClick={togglePlay}
className="h-6 w-6"
>
<CareIcon icon={isPlaying ? "l-pause" : "l-play"} />
</Button>
<div className="flex flex-1 items-center gap-2">
<span className="text-sm text-gray-600">
{formatTime(currentTime)} / {formatTime(duration)}
</span>
<Slider
defaultValue={[0]}
value={[currentTime]}
min={0}
max={duration || 100}
step={0.1}
onValueChange={handleSliderChange}
disabled={isLoading}
className="flex-1"
/>
<CareIcon
icon={isMuted ? "l-volume-mute" : "l-volume"}
onClick={toggleMute}
className="cursor-pointer"
/>
</div>
</div>
);

return { Player, isPlaying, isLoading, stopPlayback };
}

export default AudioPlayer;
Loading
Loading