Skip to content

Commit

Permalink
Merge branch 'develop' into patient-status-description
Browse files Browse the repository at this point in the history
  • Loading branch information
shivankacker authored Jun 10, 2024
2 parents b31fcc2 + fc714f4 commit d6a87bc
Show file tree
Hide file tree
Showing 37 changed files with 852 additions and 426 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ Authenticate to staging API with any of the following credentials
- Once the code review is done, the PR will be marked with a "Needs Testing" label where it'll be queued for QA testing.
- Once tested, the PR would be marked with a "Tested" label and would be queued for merge.

### Testing

To ensure the quality of our pull requests, we use a variety of tools:

- **Automated E2E Testing:** We use Cypress for end-to-end testing to automatically verify the functionality and performance of our code.
- **Manual Real Device Testing:** We use BrowserStack to manually test our code on real devices, ensuring compatibility and functionality across different platforms and browsers.

#### 🧪 Run cypress tests

To run cypress tests locally, you'll need to setup the backend to run locally and load dummy data required for cypress to the database. See [docs](https://github.com/coronasafe/care#self-hosting).
Expand Down Expand Up @@ -110,6 +117,7 @@ npm run cypress:open # To debug and run tests individually.
- [CARE Documentation](https://docs.coronasafe.network/coronasafe-care-documentation/)
- [Swagger API Documentation](https://careapi.ohc.network/swagger/)
- [Storybook component library](https://careui.coronasafe.in/)
- [Testing Documentation](https://docs.coronasafe.network/care-testing-documentation/)

## 🚀 Production

Expand Down
2 changes: 1 addition & 1 deletion cypress/pageobject/Patient/PatientInvestigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class PatientInvestigation {
}

selectInvestigation(investigation: string) {
cy.get("#search-patient-investigation").click();
cy.get("#search-patient-investigation").type(investigation);
cy.verifyAndClickElement("#investigation-group", investigation);
cy.verifyAndClickElement("#investigation", "Investigation No. 1");
}
Expand Down
2 changes: 1 addition & 1 deletion src/CAREUI/interactive/FiltersSlideover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const AdvancedFilterButton = ({ onClick }: { onClick: () => void }) => {
<ButtonV2
ghost
border
className="w-full bg-white sm:w-auto"
className="w-full bg-white md:w-auto"
onClick={onClick}
id="advanced-filter"
>
Expand Down
15 changes: 12 additions & 3 deletions src/CAREUI/misc/Fullscreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@ interface Props {
fullscreenClassName?: string;
children: React.ReactNode;
fullscreen: boolean;
onExit: () => void;
onExit: (reason?: "DEVICE_UNSUPPORTED") => void;
}

export default function Fullscreen(props: Props) {
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
if (!ref.current) {
return;
}

if (props.fullscreen) {
ref.current?.requestFullscreen();
if (ref.current.requestFullscreen) {
ref.current.requestFullscreen();
} else {
props.onExit("DEVICE_UNSUPPORTED");
}
} else {
document.exitFullscreen();
document.exitFullscreen?.();
}
}, [props.fullscreen]);

Expand All @@ -27,6 +35,7 @@ export default function Fullscreen(props: Props) {
};

document.addEventListener("fullscreenchange", listener);

return () => {
document.removeEventListener("fullscreenchange", listener);
};
Expand Down
15 changes: 15 additions & 0 deletions src/Common/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1391,3 +1391,18 @@ export const PATIENT_NOTES_THREADS = {
} as const;

export const RATION_CARD_CATEGORY = ["BPL", "APL", "NO_CARD"] as const;

export const DEFAULT_ALLOWED_EXTENSIONS = [
"image/*",
"video/*",
"audio/*",
"text/plain",
"text/csv",
"application/rtf",
"application/msword",
"application/vnd.oasis.opendocument.text",
"application/pdf",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.oasis.opendocument.spreadsheet,application/pdf",
];
74 changes: 48 additions & 26 deletions src/Components/Assets/AssetsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,40 +105,63 @@ const AssetsList = () => {
prefetch: !!(qParams.facility && qParams.location),
});

const getAssetIdFromQR = async (assetUrl: string) => {
function isValidURL(url: string) {
try {
new URL(url);
return true;
} catch (_) {
return false;
}
}

const accessAssetIdFromQR = async (assetURL: string) => {
try {
setIsLoading(true);
setIsScannerActive(false);
const params = parseQueryParams(assetUrl);
if (!isValidURL(assetURL)) {
setIsLoading(false);
Notification.Error({
msg: "Invalid QR code scanned !!!",
});
return;
}
const params = parseQueryParams(assetURL);
// QR Maybe searchParams "asset" or "assetQR"
// If no params found, then use assetText
const assetId = params.asset || params.assetQR;

if (assetId) {
const { data } = await request(routes.listAssets, {
query: { qr_code_id: assetId },
const { data } = await request(routes.listAssetQR, {
pathParams: { qr_code_id: assetId },
});
if (!data) {
setIsLoading(false);
Notification.Error({
msg: "Invalid QR code scanned !!!",
});
return;
}
const { data: assetData } = await request(routes.listAssets, {
query: { qr_code_id: assetId, limit: 1 },
});
if (assetData?.results.length === 1) {
navigate(
`/facility/${assetData.results[0].location_object.facility?.id}/assets/${assetData.results[0].id}`,
);
} else {
setIsLoading(false);
Notification.Error({
msg: "Asset not found !!!",
});
}
} else {
setIsLoading(false);
Notification.Error({
msg: "Invalid QR code scanned !!!",
});
return data?.results[0].id;
}
} catch (err) {
console.log(err);
}
};

const checkValidAssetId = async (assetId: string) => {
const { data: assetData } = await request(routes.getAsset, {
pathParams: { external_id: assetId },
});
try {
if (assetData) {
navigate(
`/facility/${assetData.location_object.facility?.id}/assets/${assetId}`,
);
}
} catch (err) {
console.log(err);
setIsLoading(false);
Notification.Error({
msg: "Invalid QR code scanned !!!",
});
}
};

Expand All @@ -159,8 +182,7 @@ const AssetsList = () => {
<Scanner
onResult={async (text) => {
if (text) {
const assetId = await getAssetIdFromQR(text);
checkValidAssetId(assetId ?? text);
await accessAssetIdFromQR(text);
}
}}
onError={(e) => {
Expand Down
19 changes: 15 additions & 4 deletions src/Components/CameraFeed/AssetBedSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,35 @@ export default function CameraPresetSelect(props: Props) {
{props.options.length > 5 && (
<CameraPresetDropdown
{...props}
placeholder="More preset"
options={props.options.slice(4)}
value={props.options.slice(4).find((o) => o.id === props.value?.id)}
/>
)}
</div>
<div className="w-full lg:hidden">
{/* Mobile View */}
<CameraPresetDropdown {...props} />
<CameraPresetDropdown {...props} placeholder="Select preset" />
</div>
</>
);
}

export const CameraPresetDropdown = (props: Props) => {
export const CameraPresetDropdown = (
props: Props & { placeholder: string },
) => {
const selected = props.value;

const options = props.options.filter(({ meta }) => meta.type !== "boundary");

const label = props.label ?? defaultLabel;

return (
<Listbox value={selected} onChange={props.onChange}>
<Listbox
value={selected}
onChange={props.onChange}
disabled={options.length === 0}
>
<div className="relative flex-1">
<Listbox.Button
className={classNames(
Expand All @@ -67,7 +74,11 @@ export const CameraPresetDropdown = (props: Props) => {
)}
>
<span className="block truncate">
{selected ? label(selected) : "Select preset"}
{options.length === 0
? "No presets"
: selected
? label(selected)
: props.placeholder}
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 mr-1 mt-1 flex items-center">
<CareIcon icon="l-angle-down" className="text-xl text-zinc-400" />
Expand Down
41 changes: 30 additions & 11 deletions src/Components/CameraFeed/CameraFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import useOperateCamera, { PTZPayload } from "./useOperateCamera";
import usePlayer from "./usePlayer";
import { getStreamUrl } from "./utils";
import ReactPlayer from "react-player";
import { classNames, isIOS } from "../../Utils/utils";
import { classNames, isAppleDevice, isIOS } from "../../Utils/utils";
import FeedAlert, { FeedAlertState } from "./FeedAlert";
import FeedNetworkSignal from "./FeedNetworkSignal";
import NoFeedAvailable from "./NoFeedAvailable";
import FeedControls from "./FeedControls";
import Fullscreen from "../../CAREUI/misc/Fullscreen";
import FeedWatermark from "./FeedWatermark";
import CareIcon from "../../CAREUI/icons/CareIcon";
import { Error } from "../../Utils/Notifications";

interface Props {
children?: React.ReactNode;
Expand All @@ -27,6 +28,7 @@ interface Props {
constrolsDisabled?: boolean;
shortcutsDisabled?: boolean;
onMove?: () => void;
onReset?: () => void;
}

export default function CameraFeed(props: Props) {
Expand Down Expand Up @@ -86,14 +88,29 @@ export default function CameraFeed(props: Props) {

const resetStream = () => {
setState("loading");
props.onReset?.();
initializeStream();
};
return (
<Fullscreen fullscreen={isFullscreen} onExit={() => setFullscreen(false)}>
<Fullscreen
fullscreen={isFullscreen}
onExit={(reason) => {
setFullscreen(false);

if (reason === "DEVICE_UNSUPPORTED") {
// iOS webkit allows only video/iframe elements to call full-screen
// APIs. But we need to show controls too, not just the video element.
Error({
msg: "This device does not support viewing this content in full-screen.",
});
}
}}
>
<div
className={classNames(
"flex max-h-screen flex-col overflow-clip rounded-xl bg-black",
"flex flex-col overflow-clip rounded-xl bg-black md:max-h-screen",
props.className,
isAppleDevice && isFullscreen && "px-20",
)}
>
<div className="flex items-center justify-between bg-zinc-900 px-4 py-1.5 md:py-2">
Expand All @@ -106,14 +123,16 @@ export default function CameraFeed(props: Props) {
/>
{props.asset.name}
</span>
<div className={state === "loading" ? "animate-pulse" : ""}>
<FeedNetworkSignal
playerRef={playerRef as any}
playedOn={player.playedOn}
status={player.status}
onReset={resetStream}
/>
</div>
{!isIOS && (
<div className={state === "loading" ? "animate-pulse" : ""}>
<FeedNetworkSignal
playerRef={playerRef as any}
playedOn={player.playedOn}
status={player.status}
onReset={resetStream}
/>
</div>
)}
</div>
</div>

Expand Down
1 change: 1 addition & 0 deletions src/Components/CameraFeed/CameraFeedWithBedPresets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default function LocationFeedTile(props: Props) {
options={data?.results ?? []}
value={preset}
onChange={setPreset}
placeholder="Select preset"
/>
)}
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/Components/CameraFeed/FeedAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ interface Props {
state?: FeedAlertState;
}

const ALERT_ICON_MAP: Record<FeedAlertState, IconName> = {
const ALERT_ICON_MAP: Partial<Record<FeedAlertState, IconName>> = {
playing: "l-play-circle",
stop: "l-stop-circle",
offline: "l-exclamation-triangle",
loading: "l-spinner",
moving: "l-expand-from-corner",
// moving: "l-expand-from-corner",
zooming: "l-search",
saving_preset: "l-save",
host_unreachable: "l-exclamation-triangle",
Expand Down Expand Up @@ -53,14 +53,14 @@ export default function FeedAlert({ state }: Props) {
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 -translate-y-5"
>
<div className="absolute right-8 top-6 flex items-center gap-1.5 rounded bg-white/20 px-2 py-1 text-white">
{state && (
<div className="absolute right-8 top-4 z-20 flex items-center gap-1.5 rounded bg-white/20 px-2 py-1 text-white">
{state && ALERT_ICON_MAP[state] && (
<CareIcon
className={classNames(
"text-base",
state === "loading" && "animate-spin",
)}
icon={ALERT_ICON_MAP[state]}
icon={ALERT_ICON_MAP[state]!}
/>
)}
<span className="text-xs font-medium capitalize">
Expand Down
Loading

0 comments on commit d6a87bc

Please sign in to comment.