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

Main dashboard search #203

Closed
wants to merge 5 commits into from
Closed
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
9 changes: 9 additions & 0 deletions backend/python/app/rest/intake_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,12 @@

except Exception as error:
return jsonify(str(error)), 400


@blueprint.route("/<string:family_name>", methods=["GET"], strict_slashes=False)
def search_intake(family_name):
try:
search = intake_service.search_intake(family_name=family_name)
return jsonify(list(map(lambda intake: intake.__dict__, search))), 200
except Exception as error:
return jsonify(str(error)), 400

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.
23 changes: 23 additions & 0 deletions backend/python/app/services/implementations/intake_service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@

import logging
from sqlalchemy import inspect

from ...models import db
Expand Down Expand Up @@ -91,3 +93,24 @@ def update_intake(self, intake_id: int, updated_data):
except Exception as error:
db.session.rollback()
raise error

def search_intake(self, family_name: str):
try:
if not family_name:
raise Exception("Empty Family Name passed to search_intake function")
if not isinstance(family_name, str):
raise Exception("Family name passed is not of str type")

intake = Intake.query.filter_by(family_name=family_name)
if not intake:
return []
intake_dto = [IntakeDTO(**i.to_dict()) for i in intake]
self.logger.debug(intake)



return intake_dto

except Exception as error:
db.session.rollback()
raise error
6 changes: 6 additions & 0 deletions backend/python/app/services/interfaces/intake_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,9 @@ def update_intake(self, intake_id, updated_data):
:raises Exception: if intake_id or updated_data is not valid or if an error occurs during update
"""
pass

@abstractmethod
def search_intake(self, family_name):
"""
Searches for all cases attributed to a Family Name
"""
13 changes: 7 additions & 6 deletions frontend/src/APIClients/IntakeAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { Case } from "../types/CasesContextTypes";
import CaseStatus from "../types/CaseStatus";

interface Intake {

Check warning on line 7 in frontend/src/APIClients/IntakeAPIClient.ts

View workflow job for this annotation

GitHub Actions / run-lint

'Intake' is defined but never used
user_id: number;
id: number;
referring_worker_name: string;
Expand Down Expand Up @@ -37,17 +37,18 @@
}
};

const get = async (
intakeStatus: CaseStatus,
page: number,
limit: number,
): Promise<Case[]> => {
const get = async (parameter?: string): Promise<IntakeResponse[]> => {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
"access_token",
)}`;

let url = "/intake";
if (parameter) {
url += `/${parameter}`;
}
try {
const { data } = await baseAPIClient.get<Intake[]>("/intake", {
const { data } = await baseAPIClient.get(url, {
headers: { Authorization: bearerToken },
params: {
intake_status: intakeStatus,
Expand Down
159 changes: 144 additions & 15 deletions frontend/src/components/pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,47 @@ import CustomInput from "../common/CustomInput";
import IntakeHeader from "../intake/IntakeHeader";
import CaseStatus from "../../types/CaseStatus";
import FilteredSection from "../dashboard/FilteredSection";
import { CaseCardProps } from "../dashboard/CaseCard";
import IntakeApiClient from "../../APIClients/IntakeAPIClient";
import StatusModal from "../dashboard/StatusModal";
import PermanentDeleteModal from "../dashboard/PermanentDeleteModal";
import CaseCard, { CaseCardProps } from "../dashboard/CaseCard";
import IntakeAPIClient from "../../APIClients/IntakeAPIClient";
import CasesContext from "../../contexts/CasesContext";
import { Case } from "../../types/CasesContextTypes";

const SecondaryHeader = (): React.ReactElement => {
const SecondaryHeader = ({
searchValue,
setSearchValue,
handleSearch,
}: {
searchValue: string;
setSearchValue: React.Dispatch<React.SetStateAction<string>>;
handleSearch: () => void;
}): React.ReactElement => {
const history = useHistory();

function goToIntake() {
history.push("/intake");
}


const {
onOpen: onOpenPermanentDelete,
isOpen: isOpenPermanentDelete,
onClose: onClosePermanentDelete,
} = useDisclosure();

const {
onOpen: onOpenStatusModal,
isOpen: isOpenStatusModal,
onClose: onCloseStatusModal,
} = useDisclosure();

const handleEnter = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === "Enter") {
handleSearch();
}
};

return (
<Box>
<Text textStyle="header-large">Intake Cases</Text>
Expand All @@ -34,6 +63,9 @@ const SecondaryHeader = (): React.ReactElement => {
<CustomInput
placeholder="Search By Family Name"
icon={<Icon as={Search} />}
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
onKeyPress={handleEnter}
/>
</Box>
<Spacer />
Expand All @@ -51,8 +83,88 @@ const SecondaryHeader = (): React.ReactElement => {
</Box>
);
};

const Home = (): React.ReactElement => {
const [searchValue, setSearchValue] = useState("");
const [searchResults, setSearchResults] = useState<CaseCardProps[]>([]);

const handleSearch = () => {
if (searchValue.trim() !== "") {
IntakeAPIClient.get(searchValue).then((data) => {
// Assuming the API response is an array of objects as shown in the example data
// Map through the response and transform it into an array of CaseCardProps objects
const caseCards: CaseCardProps[] = data.map((caseData: any) => ({
caseTitle: caseData.cpin_number, // Use appropriate properties here
caseLead: caseData.referring_worker_name,
date: caseData.referral_date,
familyName: caseData.family_name,
caseTag: caseData.intake_status as CaseStatus,
}));
setSearchResults(caseCards);
});
} else {
setSearchResults([]);
}
};

const cases: { [key: string]: CaseCardProps[] } = {
active: [
{
caseTitle: "Case 1",
caseLead: "Case Lead",
date: "11/06/2023",
familyName: "Family Name",
caseTag: CaseStatus.ACTIVE,
},
{
caseTitle: "Case 1",
caseLead: "Case Lead",
date: "11/06/2023",
familyName: "Family Name",
caseTag: CaseStatus.ACTIVE,
},
{
caseTitle: "Case 1",
caseLead: "Case Lead",
date: "11/06/2023",
familyName: "Family Name",
caseTag: CaseStatus.ACTIVE,
},
{
caseTitle: "Case 1",
caseLead: "Case Lead",
date: "11/06/2023",
familyName: "Family Name",
caseTag: CaseStatus.ACTIVE,
},
],
submitted: [
{
caseTitle: "Case 1",
caseLead: "Case Lead",
date: "11/06/2023",
familyName: "Family Name",
caseTag: CaseStatus.SUBMITTED,
},
],
pending: [
{
caseTitle: "Case 1",
caseLead: "Case Lead",
date: "11/06/2023",
familyName: "Family Name",
caseTag: CaseStatus.PENDING,
},
],
archived: [
{
caseTitle: "Case 1",
caseLead: "Case Lead",
date: "11/06/2023",
familyName: "Family Name",
caseTag: CaseStatus.ARCHIVED,
},
],

// TODO: remove console log and use context instead of state
const casesFromContext = useContext(CasesContext);
// eslint-disable-next-line
Expand Down Expand Up @@ -115,18 +227,35 @@ const Home = (): React.ReactElement => {
hasLogout
/>
<Box px="100px" py="60px">
<SecondaryHeader />
<SecondaryHeader
searchValue={searchValue}
setSearchValue={setSearchValue}
handleSearch={handleSearch}
/>

<VStack spacing={15} align="stretch" my={12}>
<FilteredSection status={CaseStatus.ACTIVE} cases={cases.active} />
<FilteredSection
status={CaseStatus.SUBMITTED}
cases={cases.submitted}
/>
<FilteredSection status={CaseStatus.PENDING} cases={cases.pending} />
<FilteredSection
status={CaseStatus.ARCHIVED}
cases={cases.archived}
/>
{searchResults.length > 0 ? (
<FilteredSection status="Search Results" cases={searchResults} />
) : (
<>
<FilteredSection
status={CaseStatus.ACTIVE}
cases={cases.active}
/>
<FilteredSection
status={CaseStatus.SUBMITTED}
cases={cases.submitted}
/>
<FilteredSection
status={CaseStatus.PENDING}
cases={cases.pending}
/>
<FilteredSection
status={CaseStatus.ARCHIVED}
cases={cases.archived}
/>
</>
)}
</VStack>
</Box>
</Box>
Expand Down
Loading