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.
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.
To follow up on this tutorial, prior knowledge of NextJs and APIs is required.
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.
To set up our Strapi back-end, we will first create a directory for our project using the following code in CLI:
1 mkdir recordapp
This creates a folder for our project recordapp
. Next, we move into this folder:
1 cd recordapp
Then, we install Strapi:
1 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 Type
button.
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.
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:
1 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:
1 npm install -D tailwindcss postcss autoprefixer
2 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:
1...
2 ┣ 📂pages
3 ┃ ┣ 📂api
4 ┃ ┃ ┗ 📜hello.js
5 ┃ ┣ 📂Components
6 ┃ ┃ ┣ 📜Cards.js
7 ┃ ┃ ┣ 📜Data.js
8 ┃ ┃ ┣ 📜Footer.js
9 ┃ ┃ ┣ 📜Landing.js
10 ┃ ┃ ┣ 📜Record.js
11 ┃ ┃ ┗ 📜Topnav.js
12 ┃ ┣ 📜index.js
13 ┃ ┗ 📜_app.js
14 ┣ 📂public
15 ┃ ┣ 📜favicon.ico
16 ┃ ┣ 📜hosrecords.png
17 ┃ ┗ 📜vercel.svg
18 ┣ 📂styles
19 ┃ ┣ 📜globals.css
20 ┃ ┗ 📜Home.module.css
21 ┣ 📜.eslintrc.json
22 ┣ 📜.gitignore
23 ┣ 📜next.config.js
24 ┣ 📜package-lock.json
25 ┣ 📜package.json
26 ┣ 📜postcss.config.js
27 ┣ 📜README.md
28 ┗ 📜tailwind.config.js
Here, the index.js
file contains all the components for the web page. In this file, we have:
1 import styles from '../styles/Home.module.css'
2 import Topnav from './Components/Topnav'
3 import Landing from './Components/Landing'
4 import Record from './Components/Record'
5 import Cards from './Components/Cards'
6 import Footer from './Components/Footer'
7 export default function Home() {
8 return (
9 <div className="font-Roboto">
10 <Topnav/>
11 <Landing/>
12 <Record/>
13 <Cards/>
14 <Footer/>
15 </div>
16 )
17 }
Taking the components in order, the Topnav
component contains the website’s navigation bar and has the following code:
1 import { React, useState } from "react";
2 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3 import { faBars } from "@fortawesome/free-solid-svg-icons";
4 import { faTimes } from "@fortawesome/free-solid-svg-icons";
5 function Topnav() {
6 const [show, setShow] = useState(false);
7 return (
8 <header>
9 <div
10 className=" w-screen fixed flex items-center justify-center px-2 top-2 z-20"
11
12 >
13 <nav className=" container flex items-start py-4 mt-4 sm:mt-12 backdrop-blur-md relative">
14 {/* <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"> */}
15 <div className=" relative">
16 <h1 className=" text-blue-600 font-bold text-2xl">
17 Hospital Records
18 </h1>
19 </div>
20 <ul className="hidden sm:flex flex-1 justify-end items-center gap-12 text-slate-900 uppercase text-base font-medium md:py-1">
21 <li className="cursor-pointer">
22 <a href="#Home">Home</a>
23 </li>
24 <li className=" cursor-pointer">
25 <a href="#Search">Search</a>
26 </li>
27 <li className=" cursor-pointer">
28 <a href="http://strapi.io" target="_blank" rel="noreferrer">About Strapi</a>
29 </li>
30 </ul>
31 {show ? (
32 <ul className="navigation sm:flex flex-1 md:hidden justify-end items-center gap-12 text-slate-900 uppercase text-xs md:py-1">
33 <li className="cursor-pointer">
34 <a href="#Home" onClick={() => setShow(!show)}>Home</a>
35 </li>
36 <li className=" cursor-pointer">
37 <a href="#Search" onClick={() => setShow(!show)}>Search</a>
38 </li>
39 <li className=" cursor-pointer">
40 <a href="http://strapi.io" target="_blank" rel="noreferrer" onClick={() => setShow(!show)}>About Strapi</a>
41 </li>
42 </ul>
43 ) : null}
44 <div
45 className="flex sm:hidden flex-1 justify-end z-40"
46 onClick={() => setShow(!show)}
47 >
48 <FontAwesomeIcon
49 className=" text-2xl"
50 icon={show ? faTimes : faBars}
51 />
52 </div>
53 </nav>
54 </div>
55 </header>
56 );
57 }
58 export default Topnav;
Next, Landing.js
is the website’s landing section:
1 import React from "react";
2 import Image from "next/image";
3 function Landing() {
4 return (
5 <div>
6 <section className=" relative" id="Home">
7 {/* hero section */}
8 <div className=" container flex flex-col-reverse lg:flex-row items-center gap-12 mt-14 lg:mt-28">
9 {/* content */}
10 <div className="flex flex-1 flex-col items-center lg:items-start">
11 <h2 className=" text-3xl text-blue-600 md:text-4xl lg:text-5xl text-center lg:text-left mb-6">
12 Hospital Patient Records available on Search
13 </h2>
14 <p className=" text-lg text-center lg:text-left mb-6 text-gray-600">
15 A simple app mimicking digitalized hospital records. Built on
16 NextJs and powered by Strapi.
17 </p>
18 <div className=" flex justify-center flex-wrap gap-6">
19 <button className=" btn border-2 hover:bg-white hover:text-blue-600 hover:border-blue-600 hover:border-2">
20 <a href="#Search">Try it out</a>
21 </button>
22 </div>
23 </div>
24 {/* image */}
25 <div className=" flex justify-center flex-1 mb-10 md:mb-16 lg:mb-0 z-10">
26 <Image
27 src="/hosrecords.png"
28 height={500}
29 width={500}
30 className=" w-5/6 h-5/6 sm:h-3/4 md:w-full md:h-full"
31 alt="stethoscope and record"
32 />
33 </div>
34 </div>
35 {/* container attachment shape */}
36 <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">
37 <svg
38 viewBox="0 0 500 500"
39 xmlns="http://www.w3.org/2000/svg"
40 width="450px"
41 id="blobSvg"
42 >
43 <defs>
44 <linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
45 <stop offset="0%" stop-color="rgb(37, 99, 235)"></stop>
46 <stop offset="100%" stop-color="rgb(196, 224, 229)"></stop>
47 </linearGradient>
48 </defs>
49 <path fill="url(#gradient)">
50 <animate
51 attributeName="d"
52 dur="8000ms"
53 repeatCount="indefinite"
54 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;
55 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;
56 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;
57 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;
58 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;
59 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
60 "
61 fill="url(#gradient)"
62 ></animate>
63 </path>
64 </svg>
65 </div>
66 </section>
67 </div>
68 );
69 }
70 export default Landing;
The Records
component contains the search bar for patient records.
1 import React from "react";
2
3 function Record(props) {
4 return (
5 <div>
6 <section className=" bg-gray-100 py-20 mt-20 lg:mt-60" id="Search">
7 <div className="sm:w-3/4 lg:w-5/12 mx-auto">
8 <h2 className=" text-3xl text-blue-600 text-center font-bold">
9 Search for Records
10 </h2>
11 <p className=" text-gray-600 text-center mt-4">
12 Enter a random name in the search box below and see what records pop
13 up....
14 </p>
15 <div className=" flex flex-col sm:flex-row gap-6 mt-8">
16 <input
17 type="text"
18 placeholder="Enter patient name"
19 className=" focus:outline-none flex-1 px-2 py-3 rounded-md text-black border-2 border-blue-600"
20 ></input>
21 <button className=" btn flex-1 border-2 hover:bg-white hover:text-blue-600 hover:border-blue-600 hover:border-2">
22 Search
23 </button>
24 {/* grid collection */}
25 </div>
26 </div>
27 </section>
28 </div>
29 );
30 }
31 export default Record;
Card.js
is where the fetched data from Strapi will be displayed to the user:
1 import {React, useState} from 'react';
2 import Data from './data';
3 function Cards() {
4 const [data, moreData] = useState(false)
5 const pull_data =(dat)=>{
6 moreData(dat)
7 console.log(dat)
8 }
9 return <div className=' relative'>
10 <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">
11 <div className=' flex flex-col rounded-md shadow-2xl'>
12 <div className=' p-6 flex flex-col items-center'>
13 <h1 className=' text-4xl font-black text-blue-600 uppercase'>M</h1>
14 <h3 className=' mt-6 mb-2 text-xl'>Samuel Harrison</h3>
15 <hr className=' w-2/5 border-b border-blue-600'></hr>
16 <div className=' flex p-6'>
17 <button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2">
18 More Info
19 </button>
20 </div>
21 </div>
22 </div>
23 <div className=' flex flex-col rounded-md shadow-2xl'>
24 <div className=' p-6 flex flex-col items-center'>
25 <h1 className=' text-4xl font-black text-blue-600 uppercase'>F</h1>
26 <h3 className=' mt-6 mb-2 text-xl'>Anita Florence</h3>
27 <hr className=' w-2/5 border-b border-blue-600'></hr>
28 <div className=' flex p-6'>
29 <button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2">
30 More Info
31 </button>
32 </div>
33 </div>
34 </div>
35 <div className=' flex flex-col rounded-md shadow-2xl'>
36 <div className=' p-6 flex flex-col items-center'>
37 <h1 className=' text-4xl font-black text-blue-600 uppercase'>M</h1>
38 <h3 className=' mt-6 mb-2 text-xl'>James Micheal</h3>
39 <hr className=' w-2/5 border-b border-blue-600'></hr>
40 <div className=' flex p-6'>
41 <button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2"
42 onClick={()=> moreData(true)}
43 >
44 More Info
45 </button>
46 </div>
47 </div>
48 </div>
49 </div>
50 {/* More info on Patient Data */}
51 <Data open= {data} func={pull_data}/>
52 </div>
53 }
54 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:
1 import { React, useState } from "react";
2 function Data(props) {
3 // const [newData, setNewData] = useState(null);
4 // props.func(newData)
5 const handleclick = () => {
6 props.func(false)
7 };
8 console.log(props.open);
9 return (
10 <div>
11 {props.open ? (
12 <div className=" absolute h-full flex justify-center items-center w-full top-0 backdrop-blur-md flex-col">
13 <button
14 className="btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2 mb-1"
15 onClick={() => handleclick()}
16 >
17 Close
18 </button>
19 <div className=" relative py-3 px-6 bg-white font-Roboto font-medium shadow-2xl">
20 <h1 className=" text-center text-3xl font-extrabold mb-5">M</h1>
21 <h3 className=" mb-1">Name: John Doe</h3>
22 <p className=" mb-1">Age: 25</p>
23 <p className=" mb-1">Blood Type: O</p>
24 <p className=" mb-1">Ailment: Cancer</p>
25 <p className=" mb-1">Medicine: Aspirin</p>
26 <p className=" mb-1">Last visit: 12/12/12</p>
27 <p className=" mb-1">Allergies: None</p>
28 <p className=" mb-1">Next of Kin: Isaac Doe</p>
29 <p className=" mb-1">Next of Kin Contact: 0700000</p>
30 <p className=" mb-1">1, Main Street, Dublin, Ireland</p>
31 </div>
32 </div>
33 ) : null}
34 </div>
35 );
36 }
37 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:
1 import React from 'react';
2 function Footer() {
3 return <footer className=' bg-blue-600 py-8 flex items-center justify-center mt-10'>
4 <div>
5 <h3 className=' text-white font-medium text-xl'>Made with StrapiCMS and NextJs</h3>
6 </div>
7 </footer>;
8 }
9 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.
1 @tailwind base;
2 @tailwind components;
3 @tailwind utilities;
4 @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');
5 .btn{
6 @apply shadow-md py-3 px-6 rounded-md transition duration-300 text-white bg-blue-600 outline-none
7 }
8 .navigation{
9 text-transform: uppercase;
10 opacity: 0;
11 }
12 @media screen and (max-width: 640px){
13 .navigation{
14 @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
15 }
16 }
17
18 html,
19 body {
20 padding: 0;
21 margin: 0;
22 font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
23 Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
24 scroll-behavior: smooth;
25 }
26 a {
27 color: inherit;
28 text-decoration: none;
29 }
30 * {
31 box-sizing: border-box;
32 }
33
34There 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:
35
36 module.exports = {
37 content: [
38 "./pages/**/*.{js,ts,jsx,tsx}",
39 "./components/**/*.{js,ts,jsx,tsx}",
40 ],
41 theme: {
42 extend: {},
43 fontFamily: {
44 Roboto: ["Roboto, sans-serif"]
45 },
46 container: {
47 center: true,
48 padding: "1rem",
49 screens: {
50 lg: "1124px",
51 xl: "1124px",
52 "2xl": "1124px",
53 },
54 },
55 },
56 plugins: [],
57 }
You can run the application with the npm run dev
command in CLI to get a result similar to the images above.
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:
1 [
2 {
3 "name": "John",
4 "surname": "Doe",
5 "age": "25",
6 "bloodtype": "O",
7 "ailment": "Cancer",
8 "medicine": "Aspirin",
9 "last_visit": "2020-06-01",
10 "allergies": "None",
11 "next_of_kin": "Isaac Doe",
12 "next_of_kin_contact": "07000000",
13 "gender": "male",
14 "address": "1, Main Street, Dublin, Ireland"
15 },
16 {
17 "name": "Newton",
18 "surname": "Dannet",
19 "age": "46",
20 "bloodtype": "AB",
21 "ailment": "none",
22 "medicine": "Aspirin",
23 "last_visit": "2020-03-12",
24 "allergies": "None",
25 "next_of_kin": "Clover Dannet",
26 "next_of_kin_contact": "071111111",
27 "gender": "male",
28 "address": "1, Main Street, Dublin, Ireland"
29 },
30 {
31 "name": "Grace",
32 "surname": "Dommy",
33 "age": "64",
34 "bloodtype": "A",
35 "ailment": "Diabetes",
36 "medicine": "Aspirin",
37 "last_visit": "2020-01-01",
38 "allergies": "None",
39 "next_of_kin": "Anny Dommy",
40 "next_of_kin_contact": "0722222",
41 "gender": "female",
42 "address": "1, Main Street, Dublin, Ireland"
43 },
44 {
45 "name": "Henry",
46 "surname": "Derry",
47 "age": "19",
48 "bloodtype": "AB",
49 "ailment": "none",
50 "medicine": "none",
51 "last_visit": "2021-09-01",
52 "allergies": "None",
53 "next_of_kin": "Ben Derry",
54 "next_of_kin_contact": "07333333",
55 "gender": "male",
56 "address": "1, Main Street, Dublin, Ireland"
57 },
58 {
59 "name": "Fred",
60 "surname": "Eddy",
61 "age": "28",
62 "bloodtype": "B",
63 "ailment": "none",
64 "medicine": "Aspirin",
65 "last_visit": "2022-01-011",
66 "allergies": "None",
67 "next_of_kin": "Eddy Edd",
68 "next_of_kin_contact": "07444444",
69 "gender": "male",
70 "address": "1, Main Street, Dublin, Ireland"
71 },
72 {
73 "name": "Stella",
74 "surname": "Morico",
75 "age": "14",
76 "bloodtype": "O",
77 "ailment": "none",
78 "medicine": "none",
79 "last_visit": "2021-05-23",
80 "allergies": "Peanuts",
81 "next_of_kin": "Ella Morico",
82 "next_of_kin_contact": "07555555",
83 "gender": "female",
84 "address": "1, Main Street, Dublin, Ireland"
85 }
86 ]
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:
1 var express = require('express');
2 var app = express();
3 var fs = require('fs');
4
5 app.use(function(req, res, next) {
6 res.header("Access-Control-Allow-Origin", "*");
7 res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
8 res.header("Access-Control-Allow-Headers", "x-access-token, Origin, X-Requested-With, Content-Type, Accept");
9 next();
10 });
11
12 app.get("/fetchpatients", function(req, res) {
13 fs.readFile(__dirname + "/" + "patients.json", 'utf8', function(err, data) {
14 console.log(data);
15 res.end(data);
16 })
17 })
18
19 var server = app.listen(8081, function() {
20 var host = server.address().address;
21 var port = server.address().port;
22 console.log("Listening at http://%s:%s", host, port);
23 });
The above code sets up our server. It makes use of the file system
module 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:
1 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.
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:
1 ...
2 import axios from 'axios';
3 import { useEffect } from 'react'
4
5 export default function Home() {
6 const url = "http://localhost:8081/fetchpatients"
7 useEffect(() => {
8 const fetchData = async () => {
9 const result = await axios.get(url);
10 console.log(result)
11 };
12 fetchData();
13 }, []);
14 ...
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.
1 ...
2 import { useEffect, useState } from "react";
3 ...
4 export default function Home() {
5 ...
6 const url2 = "http://localhost:1337/api/patient-names";
7 var response;
8 const [sent,dataSent] = useState(false);
9 useEffect(() => {
10 const fetchData = async () => {
11 const result = await axios.get(url);
12 response = result.data;
13 console.log(response);
14 sendData();
15 };
16 fetchData();
17 }, []);
18
19 const sendData = async () => {
20 if (sent==false) {
21 try {
22 response.forEach((response) => {
23 // console.log(response.name);
24 axios.post(url2, {
25 data: {
26 name: response.name,
27 surname: response.surname,
28 age: response.age,
29 blood_type: response.bloodtype,
30 ailment: response.ailment,
31 medicine: response.medicine,
32 last_visit: response.last_visit,
33 allergies: response.allergies,
34 next_of_kin: response.next_of_kin,
35 next_of_kin_contact: response.next_of_kin_contact,
36 gender: response.gender,
37 address: response.address,
38 },
39 });
40 });
41 } catch (error) {
42 console.log(error);
43 }
44 dataSent(true)
45 } else {
46 console.log("data already uploadedd")
47 }
48
49 };
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 meilisearch
module.
1 import MeiliSearch from "meilisearch";
Then we create a client and set the host to the Meilisearch instance URL
1 const [initialValue, setInitialValue] = useState("");
2 const [query, setQuery] = useState([])
3 const client = new MeiliSearch({
4 host: "http://127.0.0.1:7700/",
5 apiKey: "masterKey",
6 });
7 const index = client.index("patient");
8 const search = async () => {
9 const documents = await axios.get(
10 "http://localhost:1337/api/patient-names"
11 );
12 var result = documents.data.data;
13 let response = await index.addDocuments(result);
14 console.log(response);
15 };
16 const handlesearch = async () => {
17 const search = await index.search(initialValue);
18 console.log(search);
19 console.log(search.hits);
20 };
21 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 Cards
components to display only the patients that match the search query. In the handlesearch
function, we add:
1 ...
2 props.func(search.hits)
Then, we will get this props in the parent component Index.js
with the following code:
1 const [getQuery, setQuery] = useState([])
2 const pull_data =(dat)=>{
3 setQuery(dat)
4 }
While also passing the function as a props to the Records
component.
1 <Record func={pull_data}/>
With this we get the results of the search query from the Records
component. We can then pass these results to the Cards
component and display them.
1 <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
1 function Cards({results}) {
2 ...
3 return (
4 <div className=" relative">
5 <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">
6 {results.map((item, index) => (
7 <div className=" flex flex-col rounded-md shadow-2xl" key={index}>
8 <div className=" p-6 flex flex-col items-center">
9 <h1 className=" text-4xl font-black text-blue-600 uppercase">
10 {item.attributes.gender == "male" ? "M" : "F" }
11 </h1>
12 <h3 className=" mt-6 mb-2 text-xl">{item.attributes.name}</h3>
13 <hr className=" w-2/5 border-b border-blue-600"></hr>
14 <div className=" flex p-6">
15 <button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2" onClick={()=> moreData(true)}>
16 More Info
17 </button>
18 </div>
19 </div>
20 </div>
21 ))}
22 ...
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:
1 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:
1 onClick={() => {
2 moreData(true)
3 getFullData(item)
4 }}
We can now pass the fullData
as props to the Data
component.
1 <Data open={data} func={pull_data} info={fullData}/>
To display this data in the Data.js
file, we will make the following modifications:
1 ...
2 <div className=" relative py-3 px-6 bg-white font-Roboto font-medium shadow-2xl">
3 <h1 className=" text-center text-3xl font-extrabold mb-5">M</h1>
4 <h3 className=" mb-1">Name: {props.info.attributes.name}</h3>
5 <p className=" mb-1">Age: {props.info.attributes.age}</p>
6 <p className=" mb-1">Blood Type: {props.info.attributes.blood_type}</p>
7 <p className=" mb-1">Ailment: {props.info.attributes.ailment}</p>
8 <p className=" mb-1">Medicine: {props.info.attributes.medicine}</p>
9 <p className=" mb-1">Last visit: {props.info.attributes.last_visit}</p>
10 <p className=" mb-1">Allergies: {props.info.attributes.allergies}</p>
11 <p className=" mb-1">Next of Kin: {props.info.attributes.next_of_kin}</p>
12 <p className=" mb-1">Next of Kin Contact: {props.info.attributes.next_of_kin_contact}</p>
13 <p className=" mb-1">Address: {props.info.attributes.address}</p>
14 </div>
15 ...
With this, we have the correct data displayed when we click on the More info
button.
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.
The repository containing all the code used in this tutorial can be found here
I am a front-end react developer and technical writer. I build scalable web applications with different implementations.