diff --git a/backend-services/comment-service/.env b/backend-services/comment-service/.env new file mode 100644 index 0000000000000000000000000000000000000000..cdcec15e5d17e18500a01e4034a48fd292f593e5 --- /dev/null +++ b/backend-services/comment-service/.env @@ -0,0 +1,8 @@ +MONGO_USERNAME = 'dailythoughtapp' +MONGO_PASSWORD = 'dailythoughtapp12345' + +SERVER_PORT = '8000' + +JWT_SECRET = "yB/uX5KdyjHN9P34IE49HxAcrlQ4gfvpVJEzGbo5E/I=" + + diff --git a/daily-thought-frontend/src/components/navigation/NavBar.tsx b/daily-thought-frontend/src/components/navigation/NavBar.tsx index 88b34623f1f5e50efd166372658c672bcce852b3..020d54de21c79a5c11af157e7004ff2e409e0d5b 100644 --- a/daily-thought-frontend/src/components/navigation/NavBar.tsx +++ b/daily-thought-frontend/src/components/navigation/NavBar.tsx @@ -1,21 +1,15 @@ import { FC, Fragment, PropsWithChildren, useEffect, useState } from 'react' import { Disclosure, Menu, Transition } from '@headlessui/react' import { Bars3Icon, BellIcon, XMarkIcon } from '@heroicons/react/24/outline' +import { PhotoIcon, UserCircleIcon } from '@heroicons/react/24/solid' import { User } from '@/types/user' -const user = { - Name: 'Tom Cook', - email: 'tom@example.com', - username: 'TomCook', - imageUrl: - 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', -} const navigation = [ { name: 'My Feed', href: '#', current: true } ] const userNavigation = [ - { name: 'Your Profile', href: '#' }, - { name: 'Sign out', href: '#' }, + { name: 'Your Profile', href: '/profile' }, + { name: 'Sign out', href: '/signOut' }, ] function classNames(...classes: any) { @@ -23,7 +17,7 @@ function classNames(...classes: any) { } type NavBarProps = { - user: User | null; + user: User | undefined; } const NavBar: FC<PropsWithChildren<NavBarProps>> = ({ @@ -39,7 +33,7 @@ const NavBar: FC<PropsWithChildren<NavBarProps>> = ({ <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> <div className={`flex h-16 items-center justify-between`}> - {user !== null && + {user !== undefined && <div className="flex items-center"> <div className="hidden md:block"> <div className="flex items-baseline space-x-4"> @@ -66,7 +60,7 @@ const NavBar: FC<PropsWithChildren<NavBarProps>> = ({ <h1 className="text-2xl font-bold tracking-tight text-c-green">Daily</h1> </div> - {user !== null && + {user !== undefined && <div className="hidden md:block"> <div className="flex items-center"> <button @@ -80,14 +74,14 @@ const NavBar: FC<PropsWithChildren<NavBarProps>> = ({ {/* Profile dropdown */} <Menu as="div" className="relative ml-3"> <div> - <Menu.Button className="flex max-w-xs items-center rounded-full bg-green-400 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-c-pink"> + <Menu.Button className="flex max-w-xs items-center rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-c-pink"> <span className="sr-only">Open user menu</span> - {user.profile.length > 0 && + {user && user.profile.length > 0 && <img className="h-8 w-8 rounded-full" src={user.profile} alt="" /> } - {user.profile.length === 0 && - <p className='text text-white h-8 w-8 rounded-full flex justify-center items-center'>{user.username[0]}</p> + {user && user.profile.length === 0 && + <UserCircleIcon className='h-8 w-8 rounded-full text-gray-300 flex items-center justify-center'>{user.username[0]}</UserCircleIcon> } </Menu.Button> @@ -102,6 +96,9 @@ const NavBar: FC<PropsWithChildren<NavBarProps>> = ({ leaveTo="transform opacity-0 scale-95" > <Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> + <div className='w-full border-b'> + <p className='block px-4 py-2 text-sm text-gray-700 font-bold'>{`Hi, ${user?.firstName || user?.username}`}</p> + </div> {userNavigation.map((item) => ( <Menu.Item key={item.name}> {({ active }) => ( @@ -124,7 +121,7 @@ const NavBar: FC<PropsWithChildren<NavBarProps>> = ({ </div> } - {user !== null && + {user !== undefined && <div className="-mr-2 flex md:hidden"> {/* Mobile menu button */} <Disclosure.Button className="inline-flex items-center justify-center rounded-md bg-white p-2 text-c-pink hover:bg-c-pink hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-c-pink"> @@ -165,12 +162,12 @@ const NavBar: FC<PropsWithChildren<NavBarProps>> = ({ } {user && user.profile.length === 0 && - <p className='h-10 w-10 rounded-full bg-c-green text-white flex items-center justify-center'>{user.username[0]}</p> + <UserCircleIcon className='h-10 w-10 rounded-full text-gray-300 flex items-center justify-center'>{user.username[0]}</UserCircleIcon> } </div> <div className="ml-3"> - <div className="text-base font-medium leading-none text-grey-600">{user?.name}</div> + {(user?.firstName || user?.lastName) && <div className="text-base font-medium leading-none text-grey-600">{`${user?.firstName || ""} ${user?.lastName || ""}`}</div>} <div className="text-sm font-medium leading-none text-gray-400">{`@${user?.username}`}</div> </div> <button diff --git a/daily-thought-frontend/src/pages/feed.tsx b/daily-thought-frontend/src/pages/feed.tsx index 4564b6b8179aea0b9bab32ca5998ed137b19d450..95c23d95fab42b5bf57963bdd1d09d8c070d9fc5 100644 --- a/daily-thought-frontend/src/pages/feed.tsx +++ b/daily-thought-frontend/src/pages/feed.tsx @@ -3,6 +3,7 @@ import AnswerCard from "@/components/post/AnswerCard"; import Post from "@/components/post/Post"; import Question from "@/components/question/Question"; import { User } from "@/types/user"; +import Router from "next/router"; import { useEffect, useState } from "react"; const userx = { @@ -24,11 +25,17 @@ const Feed = () => { } + useEffect(() => { + if(sessionStorage.length < 2){ + Router.push("/") + } + }) + useEffect(() => { if(!user){ fetchUser().then(res => { - const {_id, username, email, profile } = res - setUser({id: _id, email, username, profile, name: null}) + const {_id, username, email, profile, firstName, lastName } = res + setUser({id: _id, email, username, profile, firstName, lastName}) }) } }) diff --git a/daily-thought-frontend/src/pages/index.tsx b/daily-thought-frontend/src/pages/index.tsx index 013f431d1f4b224d15256cd0d1e6ea226ed55c6a..a794b71ceab215444c4aa17219a593347a1f0846 100644 --- a/daily-thought-frontend/src/pages/index.tsx +++ b/daily-thought-frontend/src/pages/index.tsx @@ -3,6 +3,8 @@ import Image from 'next/image' import { Inter } from 'next/font/google' import NavBar from '@/components/navigation/NavBar' import Hero from '@/components/hero/Hero' +import { useEffect } from 'react' +import Router from 'next/router' const inter = Inter({ subsets: ['latin'] }) @@ -16,6 +18,12 @@ const user = { } export default function Home() { + + useEffect(() => { + if(sessionStorage.getItem("token") !== null && sessionStorage.getItem("username") !== null){ + Router.push("/feed") + } + }) return ( <> <Head> @@ -25,7 +33,7 @@ export default function Home() { <link rel="icon" href="/favicon.ico" /> </Head> <main className='min-h-screen bg-white'> - <NavBar user={null}> + <NavBar user={undefined}> {user && <Hero /> } diff --git a/daily-thought-frontend/src/pages/profile.tsx b/daily-thought-frontend/src/pages/profile.tsx new file mode 100644 index 0000000000000000000000000000000000000000..713871abb4189864b2902714f3675577311a5880 --- /dev/null +++ b/daily-thought-frontend/src/pages/profile.tsx @@ -0,0 +1,189 @@ +import NavBar from "@/components/navigation/NavBar"; +import { User } from "@/types/user"; +import { PhotoIcon, UserCircleIcon } from "@heroicons/react/24/outline"; +import Router from "next/router"; +import { FormEvent, useEffect, useState } from "react"; + +const Profile = () => { + const [user, setUser] = useState<undefined | User>(undefined) + + const fetchUser = async () => { + const endpoint = `${process.env.NEXT_PUBLIC_USER_SERVICE_URL}api/user/${sessionStorage.getItem('username')}` + const response = await fetch(endpoint) + return await response.json() + + } + + useEffect(() => { + if(!user){ + fetchUser().then(res => { + const {_id, username, email, profile, firstName, lastName } = res + setUser({id: _id, email, username, profile, firstName, lastName}) + }) + } + }) + + const handleItemChange = (item: string, value: string) => { + if(user){ + const updated = {...user, ...{[item]: value}} + setUser(updated) + } + } + + const handleSubmit = async (event: FormEvent) => { + const JSONdata = JSON.stringify({ + username: user?.username, + email: user?.email, + profile: user?.profile, + firstName: user?.firstName, + lastName: user?.lastName + }) + const endpoint = `${process.env.NEXT_PUBLIC_USER_SERVICE_URL}api/updateuser` + const options = { + method: 'PUT', + headers: { + 'Authorization': `Bearer ${sessionStorage.getItem("token")}`, + 'Content-Type': 'application/json', + }, + body: JSONdata, + } + const response = await fetch(endpoint, options) + const result = await response.json() + + } + + return ( + <div className="w-full h-screen flex flex-col"> + <div className="w-full"> + <NavBar user={user} /> + </div> + + <form className="max-w-4xl mx-auto pt-10 p-3" onSubmit={(e) => handleSubmit(e)}> + <div className="h-16"></div> + <div className="space-y-12"> + <div className="border-b border-gray-900/10 pb-12"> + <h2 className="text-base font-semibold leading-7 text-gray-900">Profile</h2> + <p className="mt-1 text-sm leading-6 text-gray-600"> + This information will be displayed publicly so be careful what you share. + </p> + + <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div className="sm:col-span-3"> + <label htmlFor="first-name" className="block text-sm font-medium leading-6 text-gray-900"> + First name + </label> + <div className="mt-2"> + <input + type="text" + name="first-name" + id="first-name" + autoComplete="given-name" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + value={user?.firstName} + onChange={(e) => handleItemChange("firstName", e.target.value)} + /> + </div> + </div> + + <div className="sm:col-span-3"> + <label htmlFor="last-name" className="block text-sm font-medium leading-6 text-gray-900"> + Last name + </label> + <div className="mt-2"> + <input + type="text" + name="last-name" + id="last-name" + autoComplete="family-name" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + value={user?.lastName} + onChange={(e) => handleItemChange("lastName", e.target.value)} + /> + </div> + </div> + + + <div className="sm:col-span-4"> + <label htmlFor="username" className="block text-sm font-medium leading-6 text-gray-900"> + Username + </label> + <div className="mt-2"> + <div className="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md"> + <span className="flex select-none items-center pl-3 text-gray-500 sm:text-sm">dialy.com/</span> + <input + type="text" + name="username" + id="username" + autoComplete="username" + className="block flex-1 border-0 bg-transparent py-1.5 pl-0 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" + placeholder="janesmith" + value={user?.username} + onChange={(e) => handleItemChange("username", e.target.value)} + /> + </div> + </div> + </div> + + <div className="col-span-full"> + <label htmlFor="photo" className="block text-sm font-medium leading-6 text-gray-900"> + Photo + </label> + <div className="mt-2 flex items-center gap-x-3"> + <UserCircleIcon className="h-12 w-12 text-gray-300" aria-hidden="true" /> + <button + type="button" + className="rounded-md bg-white px-2.5 py-1.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50" + > + Change + </button> + </div> + </div> + + + </div> + </div> + + <div className="border-b border-gray-900/10 pb-12"> + <h2 className="text-base font-semibold leading-7 text-gray-900">Personal Information</h2> + <p className="mt-1 text-sm leading-6 text-gray-600">This won't be shared with anyone.</p> + + <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div className="sm:col-span-4"> + <label htmlFor="email" className="block text-sm font-medium leading-6 text-gray-900"> + Email address + </label> + <div className="mt-2"> + <input + id="email" + name="email" + type="email" + autoComplete="email" + className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + value={user?.email} + onChange={(e) => handleItemChange("email", e.target.value)} + /> + </div> + </div> + </div> + </div> + + + </div> + + <div className="mt-6 flex items-center justify-end gap-x-6"> + <button type="button" className="text-sm font-semibold leading-6 text-gray-900" onClick={() => {Router.push("/feed")}}> + Cancel + </button> + <button + type="submit" + className="rounded-md bg-c-pink px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-c-green focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + > + Save + </button> + </div> + </form> + </div> + ) +} + +export default Profile; \ No newline at end of file diff --git a/daily-thought-frontend/src/pages/signIn.tsx b/daily-thought-frontend/src/pages/signIn.tsx index da00b299df5fbcbe2cdc964350bb257e97fc7b98..459b208fa4f5947eea7877e9581e903677afe0b9 100644 --- a/daily-thought-frontend/src/pages/signIn.tsx +++ b/daily-thought-frontend/src/pages/signIn.tsx @@ -1,5 +1,5 @@ import Router from 'next/router' -import { FormEvent, useState } from 'react'; +import { FormEvent, useEffect, useState } from 'react'; const SignIn = () => { const [error, setError] = useState<string>(""); @@ -33,6 +33,12 @@ const SignIn = () => { } } + useEffect(() => { + if(sessionStorage.length === 2){ + Router.push("/feed") + } + }) + return ( <> <div className="flex min-h-full items-center justify-center px-4 py-12 sm:px-6 lg:px-8"> diff --git a/daily-thought-frontend/src/pages/signOut.tsx b/daily-thought-frontend/src/pages/signOut.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0a102430bd6509cb40e949f3c47c26ffd881126e --- /dev/null +++ b/daily-thought-frontend/src/pages/signOut.tsx @@ -0,0 +1,22 @@ +import Router from "next/router"; +import { useEffect } from "react" + +const SignOut = () => { + + useEffect(() => { + if(sessionStorage.length > 0){ + sessionStorage.clear() + } + + Router.push("/") + }) + + + return ( + <div className="w-screen h-screen flex justify-center items-center bg-c-pink"> + <p className="text-white text-xl">Bye!</p> + </div> + ) +} + +export default SignOut \ No newline at end of file diff --git a/daily-thought-frontend/src/types/user.ts b/daily-thought-frontend/src/types/user.ts index d22386103c6da872ed718b90c4fc334f1094bc54..4a25fbc4f122991e4b9d154e755474db368abc7b 100644 --- a/daily-thought-frontend/src/types/user.ts +++ b/daily-thought-frontend/src/types/user.ts @@ -3,5 +3,6 @@ export type User = { profile: string, username: string, id: string, - name: string | null + firstName?: string, + lastName?: string } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 98177f07f9505e98a287338c86ef75b6adbb335b..52ce39430abb560783528340ba58e0a0f6a1d3cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,14 @@ version: "3.9" services: - feed-service: - build: - context: './backend-services/feed-service' - dockerfile: Dockerfile - restart: unless-stopped - ports: - - "9001:9000" - environment: - - MONGO_URI=mongodb://feed-mongo:27017/ + # feed-service: + # build: + # context: './backend-services/feed-service' + # dockerfile: Dockerfile + # restart: unless-stopped + # ports: + # - "9001:9000" + # environment: + # - MONGO_URI=mongodb://feed-mongo:27017/ user-service: build: @@ -21,17 +21,17 @@ services: - MONGO_URI=mongodb://user-mongo:27017/userdb - feed-mongo: - image: mongo - container_name: feed-mongo - ports: - - "27017:27017" + # feed-mongo: + # image: mongo + # container_name: feed-mongo + # ports: + # - "27017:27017" user-mongo: image: mongo container_name: user-mongo volumes: - - "./mongo/db:/data/db" + - "./mongo/user:/data/user" ports: - "27018:27017"