diff --git a/.gitignore b/.gitignore
index 6708349..0d2fdc0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,4 +54,9 @@ apps/**/next-env.d.ts
/sls/layers/**/python
/sls/**/targetsls/**/*.jar
-/sls/**/*.dev
\ No newline at end of file
+/sls/**/*.dev
+/.idea/.gitignore
+/.idea/kl-rmh-coding-challenge.iml
+/.idea/modules.xml
+/.idea/inspectionProfiles/Project_Default.xml
+/.idea/vcs.xml
diff --git a/apps/rmh-web/app/globals.css b/apps/rmh-web/app/globals.css
index 86005d0..f529a81 100644
--- a/apps/rmh-web/app/globals.css
+++ b/apps/rmh-web/app/globals.css
@@ -17,13 +17,7 @@
}
body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(
- to bottom,
- transparent,
- rgb(var(--background-end-rgb))
- )
- rgb(var(--background-start-rgb));
+ background-color: lightgray;
}
.column {
diff --git a/apps/rmh-web/app/layout.tsx b/apps/rmh-web/app/layout.tsx
index ae84562..7e01a2f 100644
--- a/apps/rmh-web/app/layout.tsx
+++ b/apps/rmh-web/app/layout.tsx
@@ -5,8 +5,8 @@ import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
- title: 'Create Next App',
- description: 'Generated by create next app',
+ title: 'My Portal',
+ description: 'A portal to view patient data',
}
export default function RootLayout({
diff --git a/apps/rmh-web/app/pt-portal/dummyData.tsx b/apps/rmh-web/app/pt-portal/dummyData.tsx
new file mode 100644
index 0000000..a258e6d
--- /dev/null
+++ b/apps/rmh-web/app/pt-portal/dummyData.tsx
@@ -0,0 +1,22 @@
+export const dummyAppointmentData = [{
+ ptId: "6b87d552-a2fe-465a-998c-1b288fee212f",
+ appointmentDate: "2024-02-01T10:00:00",
+ createdOn: "2024-01-31T15:30:00",
+ duration: 30,
+ patientName: "John Doe",
+ zoomUrl: "https://example.com/zoom-meeting"
+}, {
+ ptId: "6b87d552-a2fe-465a-998c-1b288fee212f",
+ appointmentDate: "2024-01-01T10:00:00",
+ createdOn: "2024-01-31T15:30:00",
+ duration: 30,
+ patientName: "Jane Doe",
+ zoomUrl: "https://example.com/zoom-meeting"
+}, {
+ ptId: "6b87d552-a2fe-465a-998c-1b288fee212f",
+ appointmentDate: "2024-02-10T10:00:00",
+ createdOn: "2024-01-31T15:30:00",
+ duration: 30,
+ patientName: "Doe Doe",
+ zoomUrl: "https://example.com/zoom-meeting"
+}];
diff --git a/apps/rmh-web/app/pt-portal/getUsers.tsx b/apps/rmh-web/app/pt-portal/getUsers.tsx
new file mode 100644
index 0000000..60263e4
--- /dev/null
+++ b/apps/rmh-web/app/pt-portal/getUsers.tsx
@@ -0,0 +1,54 @@
+'use server'
+
+import '@aws-amplify/ui-react/styles.css';
+import '../globals.css';
+import {User} from "@/app/pt-portal/models";
+import axios from "axios";
+import gql from 'graphql-tag';
+import { print } from 'graphql/language/printer'
+
+export async function getUsers(ptID: string) {
+ const GRAPHQL_API_URL_API_KEY = process.env.GRAPHQL_API_URL_API_KEY ? process.env.GRAPHQL_API_URL_API_KEY : '';
+ const GRAPHQL_API_URL = process.env.GRAPHQL_API_URL ? process.env.GRAPHQL_API_URL : '';
+ const query = gql`
+ query MyQuery {
+ getAppointments(ptId: "${ptID}") {
+ items {
+ appointmentDate
+ createdOn
+ duration
+ patientName
+ ptId
+ zoomUrl
+ }
+ }
+ }
+`
+ try {
+ const data: User[] = await axios.post(GRAPHQL_API_URL, { query: print(query) },
+ {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'x-api-key': GRAPHQL_API_URL_API_KEY
+ }
+ })
+ .then(response => {
+ const items: User[] = response.data.data.getAppointments.items;
+ console.log('Response:', items);
+ return items
+ })
+ .catch(error => {
+ console.error('Error:', error.response ? error.response.data : error.message);
+ return []
+ });
+ return data.sort(
+ (a, b) =>
+ new Date(a.appointmentDate).getTime() -
+ new Date(b.appointmentDate).getTime()
+ )
+ } catch (err) {
+ console.log('Formatting Error');
+ return []
+
+ }
+}
\ No newline at end of file
diff --git a/apps/rmh-web/app/pt-portal/models.ts b/apps/rmh-web/app/pt-portal/models.ts
index c277fec..0d6b945 100644
--- a/apps/rmh-web/app/pt-portal/models.ts
+++ b/apps/rmh-web/app/pt-portal/models.ts
@@ -4,6 +4,10 @@ export interface Client {
}
export interface User {
- id: string;
- name: string;
+ ptId: string;
+ appointmentDate: string;
+ createdOn: string;
+ duration: number;
+ patientName: string;
+ zoomUrl: string;
}
diff --git a/apps/rmh-web/app/pt-portal/page.tsx b/apps/rmh-web/app/pt-portal/page.tsx
index 0c7211f..92e569d 100644
--- a/apps/rmh-web/app/pt-portal/page.tsx
+++ b/apps/rmh-web/app/pt-portal/page.tsx
@@ -1,13 +1,14 @@
-'use client'
-
import '@aws-amplify/ui-react/styles.css';
import '../globals.css';
import Head from 'next/head';
import TopBar from '@/components/TopBar';
+import SideBar from "@/components/SideBar";
+import {User} from "@/app/pt-portal/models";
+import {getUsers} from "@/app/pt-portal/getUsers";
-
-export default function PTPortal() {
-
+export default async function PTPortal() {
+ const ptID: string = '6b87d552-a2fe-465a-998c-1b288fee212f';
+ const users: User[] = await getUsers(ptID);
return (
@@ -15,7 +16,8 @@ export default function PTPortal() {
-
+
+
diff --git a/apps/rmh-web/components/SideBar.tsx b/apps/rmh-web/components/SideBar.tsx
new file mode 100644
index 0000000..a001d1f
--- /dev/null
+++ b/apps/rmh-web/components/SideBar.tsx
@@ -0,0 +1,62 @@
+"use client";
+import { User } from "@/app/pt-portal/models";
+import Image from "next/image";
+import { useMemo, useState } from "react";
+import UsersList from "./UsersList";
+import Tab from "./Tab";
+const tabs = ["Upcoming Appointments", "Past Appointments"];
+
+const useFinalUsers = (search: string, users: User[]) =>
+ useMemo(() => {
+ return search.trim().length > 0
+ ? users.filter((user) =>
+ user.patientName.toLowerCase().startsWith(search)
+ )
+ : users;
+ }, [search, users]);
+
+const SideBar = ({
+ users,
+}: {
+ users: User[];
+}) => {
+ const [search, setSearch] = useState("");
+ const [activeIndex, setActiveIndex] = useState(0);
+ const finalUsers = useFinalUsers(search, users);
+
+ return (
+
+
+ My Appointments
+
+
+ {tabs.map((tab, index) => (
+ setActiveIndex(index)}
+ text={tab}
+ />
+ ))}
+
+
+
+ setSearch(e.target.value)}
+ />
+
+
+
+ );
+};
+
+export default SideBar;
diff --git a/apps/rmh-web/components/Tab.tsx b/apps/rmh-web/components/Tab.tsx
new file mode 100644
index 0000000..f74513e
--- /dev/null
+++ b/apps/rmh-web/components/Tab.tsx
@@ -0,0 +1,28 @@
+import clsx from "clsx";
+
+const Tab = ({
+ isActive,
+ text,
+ setActive,
+}: {
+ isActive: boolean;
+ text: string;
+ setActive: () => void;
+}) => {
+ const color = isActive
+ ? "border-[#2B478B] text-[#2B478B]"
+ : "border-[#C3C6CC] text-[343741] transition-opacity hover:opacity-50";
+
+ const className = clsx(
+ "cursor-pointer border-b-[2px] font-bold text-[16px] pb-2",
+ color
+ );
+
+ return (
+
+ {text}
+
+ );
+};
+
+export default Tab;
diff --git a/apps/rmh-web/components/TopBar.tsx b/apps/rmh-web/components/TopBar.tsx
index ed9aa41..1a22933 100644
--- a/apps/rmh-web/components/TopBar.tsx
+++ b/apps/rmh-web/components/TopBar.tsx
@@ -1,18 +1,18 @@
import React from 'react';
-import { useSelector } from 'react-redux';
-import { selectIsAuthenticated } from '@/features/authSlice';
-import { Auth } from 'aws-amplify';
+// import { useSelector } from 'react-redux';
+// import { selectIsAuthenticated } from '@/features/authSlice';
+// import { Auth } from 'aws-amplify';
const TopBar = () => {
- const isAuthenticated = useSelector(selectIsAuthenticated);
-
- const handleSignOut = async () => {
- try {
- await Auth.signOut();
- } catch (error) {
- console.error('Error signing out:', error);
- }
- };
+ // const isAuthenticated = useSelector(selectIsAuthenticated);
+ //
+ // const handleSignOut = async () => {
+ // try {
+ // await Auth.signOut();
+ // } catch (error) {
+ // console.error('Error signing out:', error);
+ // }
+ // };
return (
);
};
diff --git a/apps/rmh-web/components/UsersList.tsx b/apps/rmh-web/components/UsersList.tsx
new file mode 100644
index 0000000..4de51ff
--- /dev/null
+++ b/apps/rmh-web/components/UsersList.tsx
@@ -0,0 +1,44 @@
+import { User } from "@/app/pt-portal/models";
+import Image from "next/image";
+// import { isSelected, setUsers } from "@/features/usersSlice";
+// import { useDispatch } from "react-redux";
+// import { useSelector } from "react-redux";
+import clsx from "clsx";
+
+const UserItem = ({ user }: { user: User }) => {
+ // const dispatch = useDispatch();
+ // const userSelected = useSelector(isSelected)(user);
+ const selectedStyles = clsx(
+ `flex items-center justify-between p-4 hover:bg-[#EFF3FC] transition-all duration-250`
+ );
+//onClick={() => dispatch(setUsers(user))}
+ return (
+
+
+
+
+ );
+};
+
+const UsersList = ({ users }: { users: User[] }) => (
+ <>
+ {users.map((user) => (
+
+ ))}
+ >
+);
+
+export default UsersList;
diff --git a/apps/rmh-web/next.config.js b/apps/rmh-web/next.config.js
index 767719f..5feaa51 100644
--- a/apps/rmh-web/next.config.js
+++ b/apps/rmh-web/next.config.js
@@ -1,4 +1,9 @@
/** @type {import('next').NextConfig} */
-const nextConfig = {}
-module.exports = nextConfig
+module.exports = {
+ experimental: {
+ serverActions: {
+ allowedOrigins: ['my-proxy.com', '*.my-proxy.com'],
+ },
+ },
+}
\ No newline at end of file
diff --git a/apps/rmh-web/package-lock.json b/apps/rmh-web/package-lock.json
index 36c639a..78c463f 100644
--- a/apps/rmh-web/package-lock.json
+++ b/apps/rmh-web/package-lock.json
@@ -17,6 +17,7 @@
"autoprefixer": "10.4.15",
"aws-amplify": "^5.3.10",
"axios": "^1.4.0",
+ "clsx": "^2.1.0",
"eslint": "8.47.0",
"eslint-config-next": "13.4.19",
"graphql": "^16.8.0",
@@ -10598,6 +10599,14 @@
"node": ">=6"
}
},
+ "node_modules/clsx": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
+ "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
diff --git a/apps/rmh-web/package.json b/apps/rmh-web/package.json
index 2d7c35d..ffaeca0 100644
--- a/apps/rmh-web/package.json
+++ b/apps/rmh-web/package.json
@@ -18,6 +18,7 @@
"autoprefixer": "10.4.15",
"aws-amplify": "^5.3.10",
"axios": "^1.4.0",
+ "clsx": "^2.1.0",
"eslint": "8.47.0",
"eslint-config-next": "13.4.19",
"graphql": "^16.8.0",
diff --git a/apps/rmh-web/public/chat-icon.svg b/apps/rmh-web/public/chat-icon.svg
new file mode 100644
index 0000000..0cdd05d
--- /dev/null
+++ b/apps/rmh-web/public/chat-icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/apps/rmh-web/public/profile-icon.svg b/apps/rmh-web/public/profile-icon.svg
new file mode 100644
index 0000000..7b9aaee
--- /dev/null
+++ b/apps/rmh-web/public/profile-icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/apps/rmh-web/public/search-icon.svg b/apps/rmh-web/public/search-icon.svg
new file mode 100644
index 0000000..e766534
--- /dev/null
+++ b/apps/rmh-web/public/search-icon.svg
@@ -0,0 +1,3 @@
+