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} + /> + ))} +
+
+ search icon + 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 (
@@ -20,10 +20,9 @@ const TopBar = () => { Logo

Right Move Health

- {isAuthenticated ? ( + {
- ) : null} + }
); }; 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 ( +
+
+ profile icon +
{user.patientName}
+
+ +
+ ); +}; + +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 @@ + + +