How to Build a Hospital Record App with NextJs and Strapi

Strapi
18 min readApr 20, 2022

--

Introduction

Hospital records help to provide information on the background health conditions of prospective patients. There is a need to have an easily retrievable and accessible means of getting this record. We will solve this case study using the Strapi Content Management System to access and store patient records in an application.

Goal

In this article, we will build a Record application focusing on Patient Records. The app will focus on managing patient records in relations using the Strapi CMS and will provide all records to any patient when a request is made. These records will be fetched from an API and managed in Strapi.

Prerequisites

To follow up on this tutorial, prior knowledge of NextJs and APIs is required.

What is Strapi?

Strapi is an open-source, headless Content Management System that was developed using the Nodejs Javascript framework. Strapi provides users with features to enable them to store, manipulate, and manage content across their application workflows. It’s simple and easy to use, and with its administrative panel, users can easily monitor and manage their content. What’s more? With Strapi, we can integrate APIs that can provide and manage content stored on the Strapi CMS.

Setting up a Strapi Project

To set up our Strapi back-end, we will first create a directory for our project using the following code in CLI:

mkdir recordapp

This creates a folder for our project recordapp. Next, we move into this folder:

cd recordapp

Then, we install Strapi:

npx create-strapi-app@latest record-backend --quickstart

The command above sets up Strapi for our app with all required dependencies and creates a new folder record-backend for this.

Once the installation is complete, it will start up the server which you can view in your browser via the specified link. In your browser, you will have a result similar to the image below:

Here, simply fill in the required details and create a user account to access your dashboard.

Here we will set up the collection for our application.

Before we create our Strapi collection for our data, we have to understand how our hospital record app should operate. The application is to be able to facilitate identifying specific patient records on query. We will use MeiliSearch to make looking up of content in each record easier. With this, we can easily search for and get results from our Strapi collection showing content that have data related to the search keyword.

To set up our content, we will start by defining the content we wish to store on Strapi. To do this: first click on the Create your first Content Typebutton.

On the new page that opens up, click on Create new collection type.

On the next page, we get different content types we can create for our collection.

We will create fields for the patients. These fields will include: name, surname, age, blood_type, ailment, medicine, last_visit, allergies, next_of_kin, next_of_kin_contact, gender, and address. All of these fields will be text fields except the age and last visit which would be of type number and date respectively.

Above are all the fields for the patient record. After creation, click on the save button at the top right.

Building the Records App

With our content setup on Strapi CMS complete, the next thing we need to do is to build the front-end of our application. To do this, we will be using the NextJs framework which is an open-source Javascript framework developed on NodeJs to render applications on server-side as opposed to client-side rendering by ReactJs.

To install NextJs, enter the following command in your terminal:

npx create-next-app hospitalrecords

We will be using Tailwind CSS to scaffold our application. This can be installed in CLI with the following command:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

This creates a project folder hospitalrecords in your current directory. The created folder has the NextJs framework installed and we will use it to scaffold our application. The images below show what our finished app will look like.

Landing Section:

Search Section:

Patient Info section:

Here is the tree structure of our application:

...
┣ 📂pages
┃ ┣ 📂api
┃ ┃ ┗ 📜hello.js
┃ ┣ 📂Components
┃ ┃ ┣ 📜Cards.js
┃ ┃ ┣ 📜Data.js
┃ ┃ ┣ 📜Footer.js
┃ ┃ ┣ 📜Landing.js
┃ ┃ ┣ 📜Record.js
┃ ┃ ┗ 📜Topnav.js
┃ ┣ 📜index.js
┃ ┗ 📜_app.js
┣ 📂public
┃ ┣ 📜favicon.ico
┃ ┣ 📜hosrecords.png
┃ ┗ 📜vercel.svg
┣ 📂styles
┃ ┣ 📜globals.css
┃ ┗ 📜Home.module.css
┣ 📜.eslintrc.json
┣ 📜.gitignore
┣ 📜next.config.js
┣ 📜package-lock.json
┣ 📜package.json
┣ 📜postcss.config.js
┣ 📜README.md
┗ 📜tailwind.config.js

Here, the index.js file contains all the components for the web page. In this file, we have:

import styles from '../styles/Home.module.css'
import Topnav from './Components/Topnav'
import Landing from './Components/Landing'
import Record from './Components/Record'
import Cards from './Components/Cards'
import Footer from './Components/Footer'
export default function Home() {
return (
<div className="font-Roboto">
<Topnav/>
<Landing/>
<Record/>
<Cards/>
<Footer/>
</div>
)
}

Taking the components in order, the Topnav component contains the website’s navigation bar and has the following code:

import { React, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faBars } from "@fortawesome/free-solid-svg-icons";
import { faTimes } from "@fortawesome/free-solid-svg-icons";
function Topnav() {
const [show, setShow] = useState(false);
return (
<header>
<div
className=" w-screen fixed flex items-center justify-center px-2 top-2 z-20"

>
<nav className=" container flex items-start py-4 mt-4 sm:mt-12 backdrop-blur-md relative">
{/* <nav className=" z-20 fixed container flex items-start py-4 mt-4 sm:mt-12 backdrop-blur top-1 lg:left-20 lg:w-full md:left-0 sm:left-0"> */}
<div className=" relative">
<h1 className=" text-blue-600 font-bold text-2xl">
Hospital Records
</h1>
</div>
<ul className="hidden sm:flex flex-1 justify-end items-center gap-12 text-slate-900 uppercase text-base font-medium md:py-1">
<li className="cursor-pointer">
<a href="#Home">Home</a>
</li>
<li className=" cursor-pointer">
<a href="#Search">Search</a>
</li>
<li className=" cursor-pointer">
<a href="http://strapi.io" target="_blank" rel="noreferrer">About Strapi</a>
</li>
</ul>
{show ? (
<ul className="navigation sm:flex flex-1 md:hidden justify-end items-center gap-12 text-slate-900 uppercase text-xs md:py-1">
<li className="cursor-pointer">
<a href="#Home" onClick={() => setShow(!show)}>Home</a>
</li>
<li className=" cursor-pointer">
<a href="#Search" onClick={() => setShow(!show)}>Search</a>
</li>
<li className=" cursor-pointer">
<a href="http://strapi.io" target="_blank" rel="noreferrer" onClick={() => setShow(!show)}>About Strapi</a>
</li>
</ul>
) : null}
<div
className="flex sm:hidden flex-1 justify-end z-40"
onClick={() => setShow(!show)}
>
<FontAwesomeIcon
className=" text-2xl"
icon={show ? faTimes : faBars}
/>
</div>
</nav>
</div>
</header>
);
}
export default Topnav;

Next, Landing.js is the website’s landing section:

import React from "react";
import Image from "next/image";
function Landing() {
return (
<div>
<section className=" relative" id="Home">
{/* hero section */}
<div className=" container flex flex-col-reverse lg:flex-row items-center gap-12 mt-14 lg:mt-28">
{/* content */}
<div className="flex flex-1 flex-col items-center lg:items-start">
<h2 className=" text-3xl text-blue-600 md:text-4xl lg:text-5xl text-center lg:text-left mb-6">
Hospital Patient Records available on Search
</h2>
<p className=" text-lg text-center lg:text-left mb-6 text-gray-600">
A simple app mimicking digitalized hospital records. Built on
NextJs and powered by Strapi.
</p>
<div className=" flex justify-center flex-wrap gap-6">
<button className=" btn border-2 hover:bg-white hover:text-blue-600 hover:border-blue-600 hover:border-2">
<a href="#Search">Try it out</a>
</button>
</div>
</div>
{/* image */}
<div className=" flex justify-center flex-1 mb-10 md:mb-16 lg:mb-0 z-10">
<Image
src="/hosrecords.png"
height={500}
width={500}
className=" w-5/6 h-5/6 sm:h-3/4 md:w-full md:h-full"
alt="stethoscope and record"
/>
</div>
</div>
{/* container attachment shape */}
<div className="hidden md:flex items-center justify-center overflow-hidden absolute h-96 w-2/4 top-14 right-0 lg:-bottom-28 lg:right-36 lg:w-2/5">
<svg
viewBox="0 0 500 500"
xmlns="http://www.w3.org/2000/svg"
width="450px"
id="blobSvg"
>
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="rgb(37, 99, 235)"></stop>
<stop offset="100%" stop-color="rgb(196, 224, 229)"></stop>
</linearGradient>
</defs>
<path fill="url(#gradient)">
<animate
attributeName="d"
dur="8000ms"
repeatCount="indefinite"
values="M439.5,306Q444,362,396.5,392Q349,422,299.5,459.5Q250,497,211.5,440.5Q173,384,111,375Q49,366,54,308Q59,250,67.5,200Q76,150,106.5,102Q137,54,193.5,49.5Q250,45,300.5,60Q351,75,381.5,115.5Q412,156,423.5,203Q435,250,439.5,306Z;
M442.5,289.5Q387,329,378,393Q369,457,309.5,429.5Q250,402,188.5,432.5Q127,463,118.5,397Q110,331,103.5,290.5Q97,250,78,195Q59,140,100,100Q141,60,195.5,52Q250,44,293,72.5Q336,101,359,137.5Q382,174,440,212Q498,250,442.5,289.5Z;
M435.5,310Q457,370,412.5,412Q368,454,309,459Q250,464,188,464.5Q126,465,83,418Q40,371,56.5,310.5Q73,250,91,209.5Q109,169,134,130.5Q159,92,204.5,67Q250,42,310.5,41.5Q371,41,379.5,105.5Q388,170,401,210Q414,250,435.5,310Z;
M456,301Q426,352,388.5,388.5Q351,425,300.5,414Q250,403,200.5,412Q151,421,116.5,384Q82,347,79.5,298.5Q77,250,61.5,191Q46,132,105,117Q164,102,207,84.5Q250,67,288.5,92Q327,117,378,132Q429,147,457.5,198.5Q486,250,456,301Z;
M413,296.5Q411,343,388.5,397Q366,451,308,427Q250,403,194.5,422.5Q139,442,86.5,408.5Q34,375,47,312.5Q60,250,76,204.5Q92,159,119.5,115.5Q147,72,198.5,63.5Q250,55,292.5,79Q335,103,372.5,130Q410,157,412.5,203.5Q415,250,413,296.5Z;
M439.5,306Q444,362,396.5,392Q349,422,299.5,459.5Q250,497,211.5,440.5Q173,384,111,375Q49,366,54,308Q59,250,67.5,200Q76,150,106.5,102Q137,54,193.5,49.5Q250,45,300.5,60Q351,75,381.5,115.5Q412,156,423.5,203Q435,250,439.5,306Z
"
fill="url(#gradient)"
></animate>
</path>
</svg>
</div>
</section>
</div>
);
}
export default Landing;

The Records component contains the search bar for patient records.

import React from "react";

function Record(props) {
return (
<div>
<section className=" bg-gray-100 py-20 mt-20 lg:mt-60" id="Search">
<div className="sm:w-3/4 lg:w-5/12 mx-auto">
<h2 className=" text-3xl text-blue-600 text-center font-bold">
Search for Records
</h2>
<p className=" text-gray-600 text-center mt-4">
Enter a random name in the search box below and see what records pop
up....
</p>
<div className=" flex flex-col sm:flex-row gap-6 mt-8">
<input
type="text"
placeholder="Enter patient name"
className=" focus:outline-none flex-1 px-2 py-3 rounded-md text-black border-2 border-blue-600"
></input>
<button className=" btn flex-1 border-2 hover:bg-white hover:text-blue-600 hover:border-blue-600 hover:border-2">
Search
</button>
{/* grid collection */}
</div>
</div>
</section>
</div>
);
}
export default Record;

Card.js is where the fetched data from Strapi will be displayed to the user:

import {React, useState} from 'react';
import Data from './data';
function Cards() {
const [data, moreData] = useState(false)
const pull_data =(dat)=>{
moreData(dat)
console.log(dat)
}
return <div className=' relative'>
<div className=" container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-16 max-w-screen-lg mt-8 py-12">
<div className=' flex flex-col rounded-md shadow-2xl'>
<div className=' p-6 flex flex-col items-center'>
<h1 className=' text-4xl font-black text-blue-600 uppercase'>M</h1>
<h3 className=' mt-6 mb-2 text-xl'>Samuel Harrison</h3>
<hr className=' w-2/5 border-b border-blue-600'></hr>
<div className=' flex p-6'>
<button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2">
More Info
</button>
</div>
</div>
</div>
<div className=' flex flex-col rounded-md shadow-2xl'>
<div className=' p-6 flex flex-col items-center'>
<h1 className=' text-4xl font-black text-blue-600 uppercase'>F</h1>
<h3 className=' mt-6 mb-2 text-xl'>Anita Florence</h3>
<hr className=' w-2/5 border-b border-blue-600'></hr>
<div className=' flex p-6'>
<button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2">
More Info
</button>
</div>
</div>
</div>
<div className=' flex flex-col rounded-md shadow-2xl'>
<div className=' p-6 flex flex-col items-center'>
<h1 className=' text-4xl font-black text-blue-600 uppercase'>M</h1>
<h3 className=' mt-6 mb-2 text-xl'>James Micheal</h3>
<hr className=' w-2/5 border-b border-blue-600'></hr>
<div className=' flex p-6'>
<button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2"
onClick={()=> moreData(true)}
>
More Info
</button>
</div>
</div>
</div>
</div>
{/* More info on Patient Data */}
<Data open= {data} func={pull_data}/>
</div>
}
export default Cards;

It contains a child component Data that completely displays a selected patient’s data. Data.js contains the following lines of code:

import { React, useState } from "react";
function Data(props) {
// const [newData, setNewData] = useState(null);
// props.func(newData)
const handleclick = () => {
props.func(false)
};
console.log(props.open);
return (
<div>
{props.open ? (
<div className=" absolute h-full flex justify-center items-center w-full top-0 backdrop-blur-md flex-col">
<button
className="btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2 mb-1"
onClick={() => handleclick()}
>
Close
</button>
<div className=" relative py-3 px-6 bg-white font-Roboto font-medium shadow-2xl">
<h1 className=" text-center text-3xl font-extrabold mb-5">M</h1>
<h3 className=" mb-1">Name: John Doe</h3>
<p className=" mb-1">Age: 25</p>
<p className=" mb-1">Blood Type: O</p>
<p className=" mb-1">Ailment: Cancer</p>
<p className=" mb-1">Medicine: Aspirin</p>
<p className=" mb-1">Last visit: 12/12/12</p>
<p className=" mb-1">Allergies: None</p>
<p className=" mb-1">Next of Kin: Isaac Doe</p>
<p className=" mb-1">Next of Kin Contact: 0700000</p>
<p className=" mb-1">1, Main Street, Dublin, Ireland</p>
</div>
</div>
) : null}
</div>
);
}
export default Data;

Currently, the Card and Data components contain hard-coded values but these will be replaced as we proceed with building the application.

And finally, there’s the Footer.js Component with the following code:

import React from 'react';
function Footer() {
return <footer className=' bg-blue-600 py-8 flex items-center justify-center mt-10'>
<div>
<h3 className=' text-white font-medium text-xl'>Made with StrapiCMS and NextJs</h3>
</div>
</footer>;
}
export default Footer;

Next in the tree above are the style sheets. The global.module.css file has been slightly modified to make use of Tailwind styles.

@tailwind base;
@tailwind components;
@tailwind utilities;
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');
.btn{
@apply shadow-md py-3 px-6 rounded-md transition duration-300 text-white bg-blue-600 outline-none
}
.navigation{
text-transform: uppercase;
opacity: 0;
}
@media screen and (max-width: 640px){
.navigation{
@apply absolute h-screen bg-white z-30 w-screen left-0 flex items-center justify-center flex-col gap-44 uppercase font-bold text-2xl top-0 transition-opacity ease-in opacity-100
}
}

html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
scroll-behavior: smooth;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
There is also a custom font defined here which was used in the application. Also, I’ve made changes to the properties of the `container`Tailwind class, and added the font imported above in the `tailwind.config.js` file: module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
fontFamily: {
Roboto: ["Roboto, sans-serif"]
},
container: {
center: true,
padding: "1rem",
screens: {
lg: "1124px",
xl: "1124px",
"2xl": "1124px",
},
},
},
plugins: [],
}

You can run the application with the npm run dev command in CLI to get a result similar to the images above.

Building the Patients API

For the purpose of this tutorial, we will be building a simple API to handle our patient’s data. For this, we will be making use of Node.js, express.js and a JSON file. First, we will create a new folder called api handler . This file will be where we will build and set up our API. Next we will create a JSON file containing our data. The file will be called patients.json and will contain the following:

[
{
"name": "John",
"surname": "Doe",
"age": "25",
"bloodtype": "O",
"ailment": "Cancer",
"medicine": "Aspirin",
"last_visit": "2020-06-01",
"allergies": "None",
"next_of_kin": "Isaac Doe",
"next_of_kin_contact": "07000000",
"gender": "male",
"address": "1, Main Street, Dublin, Ireland"
},
{
"name": "Newton",
"surname": "Dannet",
"age": "46",
"bloodtype": "AB",
"ailment": "none",
"medicine": "Aspirin",
"last_visit": "2020-03-12",
"allergies": "None",
"next_of_kin": "Clover Dannet",
"next_of_kin_contact": "071111111",
"gender": "male",
"address": "1, Main Street, Dublin, Ireland"
},
{
"name": "Grace",
"surname": "Dommy",
"age": "64",
"bloodtype": "A",
"ailment": "Diabetes",
"medicine": "Aspirin",
"last_visit": "2020-01-01",
"allergies": "None",
"next_of_kin": "Anny Dommy",
"next_of_kin_contact": "0722222",
"gender": "female",
"address": "1, Main Street, Dublin, Ireland"
},
{
"name": "Henry",
"surname": "Derry",
"age": "19",
"bloodtype": "AB",
"ailment": "none",
"medicine": "none",
"last_visit": "2021-09-01",
"allergies": "None",
"next_of_kin": "Ben Derry",
"next_of_kin_contact": "07333333",
"gender": "male",
"address": "1, Main Street, Dublin, Ireland"
},
{
"name": "Fred",
"surname": "Eddy",
"age": "28",
"bloodtype": "B",
"ailment": "none",
"medicine": "Aspirin",
"last_visit": "2022-01-011",
"allergies": "None",
"next_of_kin": "Eddy Edd",
"next_of_kin_contact": "07444444",
"gender": "male",
"address": "1, Main Street, Dublin, Ireland"
},
{
"name": "Stella",
"surname": "Morico",
"age": "14",
"bloodtype": "O",
"ailment": "none",
"medicine": "none",
"last_visit": "2021-05-23",
"allergies": "Peanuts",
"next_of_kin": "Ella Morico",
"next_of_kin_contact": "07555555",
"gender": "female",
"address": "1, Main Street, Dublin, Ireland"
}
]

Above is the structure of our sample patient data. Here, we have six patients in total with their corresponding records. For our API, we will require the Express.js framework. This can installed via CLI with the following bash code:

npm install express --save

The above command installs Express.js and adds it to the dependencies. With this installed, we can now set up the server. Create a new file called server.js in the working directory and add the following code to it:

var express = require('express');
var app = express();
var fs = require('fs');

app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.header("Access-Control-Allow-Headers", "x-access-token, Origin, X-Requested-With, Content-Type, Accept");
next();
});

app.get("/fetchpatients", function(req, res) {
fs.readFile(__dirname + "/" + "patients.json", 'utf8', function(err, data) {
console.log(data);
res.end(data);
})
})

var server = app.listen(8081, function() {
var host = server.address().address;
var port = server.address().port;
console.log("Listening at http://%s:%s", host, port);
});

The above code sets up our server. It makes use of the file systemmodule to access the JSON file we earlier created for the patient data. The server is set to listen on port 8081. We can run this with the following command:

node server.js

You will get a message saying: Listening at http://:::8081. You can navigate to the port 8081 in your browser via http://localhost:8081/fetchpatients to view the JSON response.

With this, our API is complete and we can now link it to Strapi to manage the relationship between the data.

Integrating the App with Strapi

With the API setup complete, we will proceed by first adding data from the API to our Strapi collection. Then, we will integrate milisearch into the application to fetch data and add search functionality.

First, we will install and make use of Axios to perform our fetch and post requests. we can install this with the following command in CLI

npm install axios

After installation we can use this to fetch our JSON response from our API. Back in the index.js file, add the following code:

...
import axios from 'axios';
import { useEffect } from 'react'

export default function Home() {
const url = "http://localhost:8081/fetchpatients"
useEffect(() => {
const fetchData = async () => {
const result = await axios.get(url);
console.log(result)
};
fetchData();
}, []);
...

The code above uses axios to fetch the data from our API running on port 8081. It then runs an asynchronous function that awaits the fetch request and logs it in the browser console.

Adding Data to Strapi To allow access to the Strapi collection we have to make changes to the user roles. To do this, navigate in the dashboard to the settings pane on the left navigation menu. Select Roles under Users and Permissions. Click on Public, select the patient-name collection and check all checkboxes.

Finally, click on the Save button at the top-right to save these changes.

Back within our index.js file, we will make modifications to send our API data to our Strapi Collection.

...
import { useEffect, useState } from "react";
...
export default function Home() {
...
const url2 = "http://localhost:1337/api/patient-names";
var response;
const [sent,dataSent] = useState(false);
useEffect(() => {
const fetchData = async () => {
const result = await axios.get(url);
response = result.data;
console.log(response);
sendData();
};
fetchData();
}, []);

const sendData = async () => {
if (sent==false) {
try {
response.forEach((response) => {
// console.log(response.name);
axios.post(url2, {
data: {
name: response.name,
surname: response.surname,
age: response.age,
blood_type: response.bloodtype,
ailment: response.ailment,
medicine: response.medicine,
last_visit: response.last_visit,
allergies: response.allergies,
next_of_kin: response.next_of_kin,
next_of_kin_contact: response.next_of_kin_contact,
gender: response.gender,
address: response.address,
},
});
});
} catch (error) {
console.log(error);
}
dataSent(true)
} else {
console.log("data already uploadedd")
}

};

The code above stores our API response in a variable called response. A function sendData() is then called. This function maps through the response from the API and for each response, it sends the corresponding data to Strapi. At the end, if we view our Strapi Content-manager we will see six entries in our Patient name collection.

We can view the content of each entry by clicking on them. We get a window displaying each of the fields and their values.

Integrating Meilisearch To use Meilisearch locally, we will download and run an instance of it. This can be downloaded from Meilisearch Local. Opening the downloaded application shows a terminal with the Meilisearch instance hosted on local host:

If we navigate in the browser to the specified URL, we can see the Meilisearch interface:

To make use of Meilisearch from within our application, we would need to install it via CLI with the following command:

npm install meilisearch

Back in the records.js file, we will add an import for the meilisearchmodule.

import MeiliSearch from "meilisearch";

Then we create a client and set the host to the Meilisearch instance URL

const [initialValue, setInitialValue] = useState("");
const [query, setQuery] = useState([])
const client = new MeiliSearch({
host: "http://127.0.0.1:7700/",
apiKey: "masterKey",
});
const index = client.index("patient");
const search = async () => {
const documents = await axios.get(
"http://localhost:1337/api/patient-names"
);
var result = documents.data.data;
let response = await index.addDocuments(result);
console.log(response);
};
const handlesearch = async () => {
const search = await index.search(initialValue);
console.log(search);
console.log(search.hits);
};
search();

The code above instantiates a client for our data. An index is created using this client and data is fetched from the Strapi collection and stored in it. The results are added to the index and displayed in the console. The index.search method is used to find strings in the data and it’s what we will implement in our search functionality. In this case, it is searching for the string “Stella”. Note that if the index.search method takes an empty string "" then all data will be displayed as results.

Next, we will need to pass the results of our search query to the Cardscomponents to display only the patients that match the search query. In the handlesearch function, we add:

...
props.func(search.hits)

Then, we will get this props in the parent component Index.js with the following code:

const [getQuery, setQuery] = useState([])
const pull_data =(dat)=>{
setQuery(dat)
}

While also passing the function as a props to the Records component.

<Record func={pull_data}/>

With this we get the results of the search query from the Recordscomponent. We can then pass these results to the Cards component and display them.

<Cards results={getQuery}/>

Next, we iterate over the number of results and display the corresponding data. To do this, add the following to the Cards.js file

function Cards({results}) {
...
return (
<div className=" relative">
<div className=" container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-16 max-w-screen-lg mt-8 py-12">
{results.map((item, index) => (
<div className=" flex flex-col rounded-md shadow-2xl" key={index}>
<div className=" p-6 flex flex-col items-center">
<h1 className=" text-4xl font-black text-blue-600 uppercase">
{item.attributes.gender == "male" ? "M" : "F" }
</h1>
<h3 className=" mt-6 mb-2 text-xl">{item.attributes.name}</h3>
<hr className=" w-2/5 border-b border-blue-600"></hr>
<div className=" flex p-6">
<button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2" onClick={()=> moreData(true)}>
More Info
</button>
</div>
</div>
</div>
))}
...

The above code displays the cards with the corresponding data, and the number of displayed cards changes based on the search input.

If we were to search for a particular name, you only get that card displayed.

With Meilisearch, we can search for any data pertaining to a patient. This could be age, next of kin or even a health condition.

Finally, we need to display the entire patient data when the user clicks on the more info button. We will need to pass the data from the clicked card to the Data.js component. We will first create a variable for this:

const [fullData, getFullData] = useState([]);

Then, use the More info button to change the value of this state by modifying its onClick event-listener as follows:

onClick={() => {
moreData(true)
getFullData(item)
}}

We can now pass the fullData as props to the Data component.

<Data open={data} func={pull_data} info={fullData}/>

To display this data in the Data.js file, we will make the following modifications:

...
<div className=" relative py-3 px-6 bg-white font-Roboto font-medium shadow-2xl">
<h1 className=" text-center text-3xl font-extrabold mb-5">M</h1>
<h3 className=" mb-1">Name: {props.info.attributes.name}</h3>
<p className=" mb-1">Age: {props.info.attributes.age}</p>
<p className=" mb-1">Blood Type: {props.info.attributes.blood_type}</p>
<p className=" mb-1">Ailment: {props.info.attributes.ailment}</p>
<p className=" mb-1">Medicine: {props.info.attributes.medicine}</p>
<p className=" mb-1">Last visit: {props.info.attributes.last_visit}</p>
<p className=" mb-1">Allergies: {props.info.attributes.allergies}</p>
<p className=" mb-1">Next of Kin: {props.info.attributes.next_of_kin}</p>
<p className=" mb-1">Next of Kin Contact: {props.info.attributes.next_of_kin_contact}</p>
<p className=" mb-1">Address: {props.info.attributes.address}</p>
</div>
...

With this, we have the correct data displayed when we click on the More info button.

Conclusion

In this tutorial, we learned about the Strapi CMS and how we can use it to build a hospital records application. We also learned how to make use of a search functionality to manage our data.

Resources

The repository containing all the code used in this tutorial can be found here

--

--

Strapi

The open source Headless CMS Front-End Developers love.