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

Merge Develop to Staging 24.33.0 | Patch for fixing two edge case bugs | #8282

Merged
merged 2 commits into from
Aug 9, 2024
Merged
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
11 changes: 2 additions & 9 deletions src/Components/ABDM/ABDMFacilityRecords.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ export default function ABDMFacilityRecords({ facilityId }: IProps) {
consent.expiry,
) < new Date()
? "EXPIRED"
: consent.consent_artefacts?.[0]?.status ??
consent.status}
: (consent.consent_artefacts?.[0]?.status ??
consent.status)}
</td>

<td className="px-3 py-4 text-center text-sm">
Expand All @@ -102,13 +102,6 @@ export default function ABDMFacilityRecords({ facilityId }: IProps) {
: "-"}
</td>

{/* <td className="px-3 py-4 text-center text-sm">
{`${consent.requester?.first_name} ${consent.requester?.last_name}`.trim()}
<p className="text-secondary-600">
({consent.requester.username})
</p>
</td> */}

<td className="px-3 py-4 text-center text-sm">
{formatDateTime(
consent.consent_artefacts?.[0]?.from_time ??
Expand Down
4 changes: 2 additions & 2 deletions src/Components/ABDM/ABDMRecordsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import CareIcon from "../../CAREUI/icons/CareIcon";
import ButtonV2 from "../Common/components/ButtonV2";
import * as Notification from "../../Utils/Notifications.js";
import Loading from "../Common/Loading";
import { classNames } from "../../Utils/utils";
import { classNames, formatName } from "../../Utils/utils";
import { Link } from "raviger";
import routes from "../../Redux/api";
import request from "../../Utils/request/request";
Expand Down Expand Up @@ -75,7 +75,7 @@ function ConsentRequestCard({ consent }: IConsentRequestCardProps) {
}
</h5>
<h6 className="mt-1 leading-6 text-secondary-700">
{consent.requester.first_name} {consent.requester.last_name}
{formatName(consent.requester)}
</h6>
</div>
<div className="flex flex-col items-center">
Expand Down
5 changes: 2 additions & 3 deletions src/Components/Assets/AssetManage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Pagination from "../Common/Pagination";
import { navigate } from "raviger";
import QRCode from "qrcode.react";
import AssetWarrantyCard from "./AssetWarrantyCard";
import { formatDate, formatDateTime } from "../../Utils/utils";
import { formatDate, formatDateTime, formatName } from "../../Utils/utils";
import Chip from "../../CAREUI/display/Chip";
import CareIcon from "../../CAREUI/icons/CareIcon";
import ButtonV2 from "../Common/components/ButtonV2";
Expand Down Expand Up @@ -148,8 +148,7 @@ const AssetManage = (props: AssetManageProps) => {
</td>
<td className="whitespace-nowrap px-6 py-4 text-center text-sm leading-5 text-secondary-500">
<span className="font-medium text-secondary-900">
{transaction.performed_by.first_name}{" "}
{transaction.performed_by.last_name}
{formatName(transaction.performed_by)}
</span>
</td>
<td className="whitespace-nowrap px-6 py-4 text-right text-sm leading-5 text-secondary-500">
Expand Down
4 changes: 2 additions & 2 deletions src/Components/Common/RelativeDateUserMention.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import CareIcon from "../../CAREUI/icons/CareIcon";
import { formatDateTime, relativeDate } from "../../Utils/utils";
import { formatDateTime, formatName, relativeDate } from "../../Utils/utils";
import { PerformedByModel } from "../HCX/misc";

function RelativeDateUserMention(props: {
Expand Down Expand Up @@ -28,7 +28,7 @@ function RelativeDateUserMention(props: {
}`}
>
<div className="flex flex-col whitespace-normal text-sm font-semibold leading-5 text-white">
<p className="flex justify-center">{`${props.user.first_name} ${props.user.last_name}`}</p>
<p className="flex justify-center">{formatName(props.user)}</p>
<p className="flex justify-center">{`@${props.user.username}`}</p>
<p className="flex justify-center">{props.user.user_type}</p>
</div>
Expand Down
210 changes: 128 additions & 82 deletions src/Components/Common/UserAutocompleteFormField.tsx
Original file line number Diff line number Diff line change
@@ -1,112 +1,158 @@
import { useAsyncOptions } from "../../Common/hooks/useAsyncOptions";
import { getFacilityUsers, getUserList } from "../../Redux/actions";
import { Autocomplete } from "../Form/FormFields/Autocomplete";
import FormField from "../Form/FormFields/FormField";
import {
FormFieldBaseProps,
useFormFieldPropsResolver,
} from "../Form/FormFields/Utils";
import { UserModel } from "../Users/models";
import { isUserOnline } from "../../Utils/utils";
import {
classNames,
formatName,
isUserOnline,
mergeQueryOptions,
} from "../../Utils/utils";
import { UserRole } from "../../Common/constants";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import useQuery from "../../Utils/request/useQuery";
import routes from "../../Redux/api";
import { UserBareMinimum } from "../Users/models";

type Props = FormFieldBaseProps<UserModel> & {
type BaseProps = FormFieldBaseProps<UserBareMinimum> & {
placeholder?: string;
facilityId?: string;
homeFacility?: string;
userType?: UserRole;
showActiveStatus?: boolean;
noResultsError?: string;
};

export default function UserAutocompleteFormField(props: Props) {
type LinkedFacilitySearchProps = BaseProps & {
facilityId: string;
homeFacility?: undefined;
};

type UserSearchProps = BaseProps & {
facilityId?: undefined;
homeFacility?: string;
};

export default function UserAutocomplete(props: UserSearchProps) {
const field = useFormFieldPropsResolver(props);
const { fetchOptions, isLoading, options } = useAsyncOptions<UserModel>(
"id",
{ queryResponseExtractor: (data) => data.results },
);
const [query, setQuery] = useState("");
const [disabled, setDisabled] = useState(false);

let search_filter: {
limit: number;
offset: number;
home_facility?: string;
user_type?: string;
search_text?: string;
} = { limit: 50, offset: 0 };
const { data, loading } = useQuery(routes.userList, {
query: {
home_facility: props.homeFacility,
user_type: props.userType,
search_text: query,
limit: 50,
offset: 0,
},
});

if (props.showActiveStatus && props.userType) {
search_filter = { ...search_filter, user_type: props.userType };
}
useEffect(() => {
if (
loading ||
query ||
!field.required ||
!props.noResultsError ||
!data?.results
) {
return;
}

if (props.homeFacility) {
search_filter = { ...search_filter, home_facility: props.homeFacility };
}
if (data.results.length === 0) {
setDisabled(true);
field.handleChange(undefined as unknown as UserBareMinimum);
}
}, [loading, query, field.required, data?.results, props.noResultsError]);

const getStatusIcon = (option: UserModel) => {
if (!props.showActiveStatus) return null;
return (
<FormField field={field}>
<Autocomplete
id={field.id}
disabled={field.disabled || disabled}
required={field.required as true}
placeholder={(disabled && props.noResultsError) || props.placeholder}
value={field.value}
onChange={field.handleChange}
options={mergeQueryOptions(
field.value ? [field.value] : [],
data?.results ?? [],
(obj) => obj.username,
)}
optionLabel={formatName}
optionIcon={userOnlineDot}
optionDescription={(option) =>
`${option.user_type} - ${option.username}`
}
optionValue={(option) => option}
onQuery={setQuery}
isLoading={loading}
/>
</FormField>
);
}

return (
<div className="mr-6 mt-[2px]">
<svg
className={`h-3 w-3 ${
isUserOnline(option) ? "text-green-500" : "text-secondary-400"
}`}
fill="currentColor"
viewBox="0 0 8 8"
>
<circle cx="4" cy="4" r="4" />
</svg>
</div>
);
};
export const LinkedFacilityUsers = (props: LinkedFacilitySearchProps) => {
const field = useFormFieldPropsResolver(props);

const items = options(field.value && [field.value]);
const [query, setQuery] = useState("");

useEffect(() => {
if (props.required && !isLoading && !items.length && props.noResultsError) {
field.handleChange(undefined as unknown as UserModel);
}
}, [isLoading, items, props.required]);
const { data, loading } = useQuery(routes.getFacilityUsers, {
pathParams: { facility_id: props.facilityId },
query: {
user_type: props.userType,
search_text: query,
limit: 50,
offset: 0,
},
});

const noResultError =
(props.required && !isLoading && !items.length && props.noResultsError) ||
(!query &&
!loading &&
field.required &&
!data?.results?.length &&
props.noResultsError) ||
undefined;

useEffect(() => {
if (noResultError) {
field.handleChange(undefined as unknown as UserBareMinimum);
}
}, [noResultError]);

return (
<FormField field={field}>
<div className="relative">
<Autocomplete
id={field.id}
disabled={field.disabled || !!noResultError}
// Voluntarily casting type as true to ignore type errors.
required={field.required as true}
placeholder={noResultError || props.placeholder}
value={field.value}
onChange={field.handleChange}
options={items}
optionLabel={getUserFullName}
optionIcon={getStatusIcon}
optionDescription={(option) => `${option.user_type}`}
optionValue={(option) => option}
onQuery={(query) =>
fetchOptions(
props.facilityId
? getFacilityUsers(props.facilityId, {
...search_filter,
search_text: query,
})
: getUserList({ ...search_filter, search_text: query }),
)
}
isLoading={isLoading}
/>
</div>
<Autocomplete
id={field.id}
disabled={field.disabled || !!noResultError}
// Voluntarily casting type as true to ignore type errors.
required={field.required as true}
placeholder={noResultError || props.placeholder}
value={field.value}
onChange={field.handleChange}
options={mergeQueryOptions(
field.value ? [field.value] : [],
data?.results ?? [],
(obj) => obj.username,
)}
optionLabel={formatName}
optionIcon={userOnlineDot}
optionDescription={(option) =>
`${option.user_type} - ${option.username}`
}
optionValue={(option) => option}
onQuery={setQuery}
isLoading={loading}
/>
</FormField>
);
}

const getUserFullName = (user: UserModel) => {
const personName = user.first_name + " " + user.last_name;
return personName.trim().length > 0 ? personName : user.username || "";
};

const userOnlineDot = (user: UserBareMinimum) => (
<div
className={classNames(
"mr-4 size-2.5 rounded-full ",
isUserOnline(user) ? "bg-primary-500" : "bg-secondary-400",
)}
/>
);
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ const formatValue = (value: unknown, key?: string): ReactNode => {
return trimmed;
}

if (new Date(trimmed).toString() !== "Invalid Date") {
const dateTimeRegex =
/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?$/;

if (trimmed.match(dateTimeRegex)) {
return new Date(trimmed).toLocaleString();
}

Expand Down
Loading
Loading