diff --git a/movie-group-8/src/App.vue b/movie-group-8/src/App.vue index 0e77656b5126c785a7671287d90b550002604365..8ac8675a22a61a63f7ad986615b5a2bcf1ac0517 100644 --- a/movie-group-8/src/App.vue +++ b/movie-group-8/src/App.vue @@ -5,7 +5,7 @@ import Navbar from './components/Navbar.vue'; <template> <Navbar /> - <div class="pt-20"> + <div class="pt-20 z-10 bg-neutral-900"> <RouterView /> </div> </template> diff --git a/movie-group-8/src/components/Navbar.vue b/movie-group-8/src/components/Navbar.vue index 8a2479e8e6322b92f2b137f4f6d6ba99f5d25c4b..50e2353bc8aee589ef65ef36c9254be32039e9cc 100644 --- a/movie-group-8/src/components/Navbar.vue +++ b/movie-group-8/src/components/Navbar.vue @@ -1,100 +1,120 @@ <template> - <nav class="fixed top-0 left-0 w-full bg-gray-800 text-white shadow-md pr-4 pl-4 flex justify-between items-center"> - <!-- Logo --> - <div class="flex items-center space-x-3"> - <img class="h-20 w-auto" src="../assets/Dark_Mode.png" alt="Logo" /> + <nav class="fixed top-0 left-0 right-0 min-w-11/12 mx-6 bg-neutral-800 text-white shadow-md px-4 flex justify-between items-center z-50 rounded-2xl mt-3"> + <!-- Logo --> + <div class="flex items-center"> + <RouterLink to="/"> + <img class="h-14 w-auto" src="../assets/Dark_Mode.png" alt="Logo" /> + </RouterLink> + </div> + <!-- Search Bar (Perfectly Centered) --> + <div class="absolute left-1/2 transform -translate-x-1/2 w-full max-w-md"> + <input + v-model="searchQuery" + type="text" + placeholder="Search..." + class="w-full px-4 py-2 pr-10 rounded-full bg-neutral-700 text-white placeholder-gray-400 focus:ring-2 focus:ring-white focus:outline-none" + /> + <!-- Search Icon inside input --> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" + class="size-6 absolute right-3 top-1/2 -translate-y-1/2 text-gray-400"> + <path stroke-linecap="round" stroke-linejoin="round" + d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /> + </svg> + </div> + + <!-- Navigation & Profile Section --> + <div class="flex items-center space-x-4 ml-auto"> + <!-- Navigation Links --> + <div class="flex space-x-3"> <RouterLink v-for="item in filteredNavigation" :key="item.name" :to="item.href" - class="px-3 py-2 rounded-md text-sm font-medium transition hover:bg-gray-700" - :class="{ 'bg-gray-900': item.href === currentRoute }"> + class="px-3 py-2 rounded-md text-sm font-medium transition hover:bg-neutral-700" + :class="{ 'bg-neutral-900': item.href === currentRoute }"> {{ item.name }} </RouterLink> + + <RouterLink + v-if="!isLoggedIn" + to="/login" + class="px-4 py-2 rounded-xl bg-blue-600 text-white font-semibold text-sm transition hover:bg-blue-700 shadow-md"> + Login + </RouterLink> </div> - - <!-- Profile & Notifications --> - <div class="flex items-center space-x-4"> - <!-- Notifications --> - <!-- <button class="relative p-2 rounded-full text-gray-400 hover:text-white hover:bg-gray-700"> - <BellIcon class="h-6 w-6" aria-hidden="true" /> - </button> --> - - <!-- Profile Dropdown (Only show if logged in) --> - <Menu as="div" class="relative" v-if="isLoggedIn"> - <div> - <MenuButton class="flex rounded-full bg-gray-800 text-sm focus:ring-2 focus:ring-white focus:ring-offset-2"> - <img class="h-8 w-8 rounded-full" src="https://randomuser.me/api/portraits/men/1.jpg" alt="User Avatar" /> - </MenuButton> - </div> - <transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95"> - <MenuItems class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg ring-1 ring-black/5 py-1"> - <MenuItem v-slot="{ active }"> - <RouterLink to="/profile" :class="[active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700']">Your Profile</RouterLink> - </MenuItem> - <MenuItem v-slot="{ active }"> - <RouterLink to="/settings" :class="[active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700']">Settings</RouterLink> - </MenuItem> - <MenuItem @click="handleSignOut" v-slot="{ active }"> - <a href="#" @click.prevent="handleSignOut" :class="[active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700']">Sign out</a> - </MenuItem> - </MenuItems> - </transition> - </Menu> - </div> - </nav> - </template> - - <script setup> - import { ref, computed, onMounted } from 'vue'; - import { useRoute, useRouter } from 'vue-router'; - import { getAuth, onAuthStateChanged, signOut } from 'firebase/auth'; - import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'; - import { BellIcon } from '@heroicons/vue/24/outline'; - - // Firebase Authentication instance - const auth = getAuth(); - - const route = useRoute(); - const router = useRouter(); - const isLoggedIn = ref(false); - - const navigation = ref([ - { name: 'Home', href: '/' }, - { name: 'Feed', href: '/feed', authRequired: true }, - { name: 'Register', href: '/register', guestOnly: true }, - { name: 'Login', href: '/login', guestOnly: true }, - ]); - - // Compute the current route to match against the navigation links - const currentRoute = computed(() => route.path); - // Filter navigation based on auth state - const filteredNavigation = computed(() => { - return navigation.value.filter(item => { - if (item.authRequired && !isLoggedIn.value) return false; // Hide if user is not logged in - if (item.guestOnly && isLoggedIn.value) return false; // Hide if user is logged in - return true; - }); + <!-- Profile Dropdown (Only show if logged in) --> + <Menu as="div" class="relative" v-if="isLoggedIn"> + <div> + <MenuButton class="flex rounded-full bg-gray-800 text-sm focus:ring-1 focus:ring-white focus:ring-offset-2"> + <img class="h-8 w-8 rounded-full" src="https://randomuser.me/api/portraits/men/1.jpg" alt="User Avatar" /> + </MenuButton> + </div> + <transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95"> + <MenuItems class="absolute right-0 mt-2 w-48 bg-neutral-800 rounded-md shadow-lg ring-1 ring-black/5 py-1"> + <MenuItem v-slot="{ active }"> + <RouterLink to="/profile" :class="[active ? 'bg-neutral-700' : '', 'block px-4 py-2 text-sm text-white']">Your Profile</RouterLink> + </MenuItem> + <MenuItem v-slot="{ active }"> + <RouterLink to="/settings" :class="[active ? 'bg-neutral-700' : '', 'block px-4 py-2 text-sm text-white']">Settings</RouterLink> + </MenuItem> + <MenuItem @click="handleSignOut" v-slot="{ active }"> + <a href="#" @click.prevent="handleSignOut" :class="[active ? 'bg-neutral-700' : '', 'block px-4 py-2 text-sm text-white']">Sign out</a> + </MenuItem> + </MenuItems> + </transition> + </Menu> + </div> + </nav> +</template> + +<script setup> +import { ref, computed, onMounted } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import { getAuth, onAuthStateChanged, signOut } from 'firebase/auth'; +import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'; + +// Firebase Authentication instance +const auth = getAuth(); + +const route = useRoute(); +const router = useRouter(); +const isLoggedIn = ref(false); + +const navigation = ref([ + { name: 'Films', href: '/films', authRequired: true }, + { name: 'Watchlist', href: '/watchlist', authRequired: true }, +]); + +// Compute the current route to match against the navigation links +const currentRoute = computed(() => route.path); + +// Filter navigation based on auth state +const filteredNavigation = computed(() => { + return navigation.value.filter(item => { + if (item.authRequired && !isLoggedIn.value) return false; // Hide if user is not logged in + if (item.guestOnly && isLoggedIn.value) return false; // Hide if user is logged in + return true; }); - - // Check authentication state - onMounted(() => { - onAuthStateChanged(auth, (user) => { - isLoggedIn.value = !!user; // Sets to true if user exists, false otherwise - }); +}); + +// Check authentication state +onMounted(() => { + onAuthStateChanged(auth, (user) => { + isLoggedIn.value = !!user; // Sets to true if user exists, false otherwise }); - - // Sign-out function - const handleSignOut = () => { - signOut(auth) - .then(() => { - isLoggedIn.value = false; // Ensure UI updates correctly - router.push('/'); // Redirect to home - }) - .catch((error) => { - console.error("Error signing out:", error); - }); - }; - </script> +}); + +// Sign-out function +const handleSignOut = () => { + signOut(auth) + .then(() => { + isLoggedIn.value = false; // Ensure UI updates correctly + router.push('/'); // Redirect to home + }) + .catch((error) => { + console.error("Error signing out:", error); + }); +}; +</script> diff --git a/movie-group-8/src/router/index.js b/movie-group-8/src/router/index.js index 9be1126aeb8f7615a342126d0774d0ab7b7aa99b..328c5b5e33222a84cdf8a5169d72d431deb9059a 100644 --- a/movie-group-8/src/router/index.js +++ b/movie-group-8/src/router/index.js @@ -8,10 +8,15 @@ const router = createRouter({ { path: '/register', component: () => import('../views/Register.vue') }, { path: '/login', component: () => import('../views/Login.vue') }, { - path: '/feed', - component: () => import('../views/Feed.vue'), + path: '/films', + component: () => import('../views/Films.vue'), meta: { requiresAuth: true }, }, + { + path: '/watchlist', + component: () => import('../views/Watchlist.vue'), + meta: { requiresAuth: true }, + }, ], }); diff --git a/movie-group-8/src/views/Feed.vue b/movie-group-8/src/views/Feed.vue deleted file mode 100644 index ecd9bbcc8b759d60a8703f92c97522789d5b7a4a..0000000000000000000000000000000000000000 --- a/movie-group-8/src/views/Feed.vue +++ /dev/null @@ -1 +0,0 @@ -<template>Feed</template> \ No newline at end of file diff --git a/movie-group-8/src/views/Films.vue b/movie-group-8/src/views/Films.vue new file mode 100644 index 0000000000000000000000000000000000000000..072b643deb3df336d3dd52bd1cccf947486db309 --- /dev/null +++ b/movie-group-8/src/views/Films.vue @@ -0,0 +1 @@ +<template>Films</template> \ No newline at end of file diff --git a/movie-group-8/src/views/Home.vue b/movie-group-8/src/views/Home.vue index 852363e602be996ae93595e688b71a6fe3bc7fa7..85aff70b5ac278ad47fd69ac1f75abc9209c20a4 100644 --- a/movie-group-8/src/views/Home.vue +++ b/movie-group-8/src/views/Home.vue @@ -1 +1,77 @@ -<template>Home</template> \ No newline at end of file +<template> + <div class="h-full bg-neutral-900"> + <!-- Features --> + <div class="max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 mx-auto dark"> + <div class="relative p-6 md:p-16"> + <!-- Grid --> + <div class="relative z-10 lg:grid lg:grid-cols-12 lg:gap-16 lg:items-center"> + <div class="mb-10 lg:mb-0 lg:col-span-6 lg:col-start-8 lg:order-2"> + <h2 class="text-2xl text-neutral-200 font-bold sm:text-3xl"> + Fully customizable rules to match your unique needs + </h2> + + <!-- Tab Navs --> + <nav class="grid gap-4 mt-5 md:mt-10" aria-label="Tabs" role="tablist" aria-orientation="vertical"> + <button type="button" class="hs-tab-active:bg-neutral-700 hs-tab-active:shadow-md text-start hover:bg-neutral-700 focus:bg-neutral-700 p-4 md:p-5 rounded-xl active" id="tabs-with-card-item-1" aria-selected="true" data-hs-tab="#tabs-with-card-1" aria-controls="tabs-with-card-1" role="tab"> + <span class="flex gap-x-6"> + <svg class="shrink-0 mt-2 size-6 md:size-7 text-blue-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5.5A3.5 3.5 0 0 1 8.5 2H12v7H8.5A3.5 3.5 0 0 1 5 5.5z"/><path d="M12 2h3.5a3.5 3.5 0 1 1 0 7H12V2z"/><path d="M12 12.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 1 1-7 0z"/><path d="M5 19.5A3.5 3.5 0 0 1 8.5 16H12v3.5a3.5 3.5 0 1 1-7 0z"/><path d="M5 12.5A3.5 3.5 0 0 1 8.5 9H12v7H8.5A3.5 3.5 0 0 1 5 12.5z"/></svg> + <span class="grow"> + <span class="block text-lg font-semibold text-blue-500">Advanced tools</span> + <span class="block mt-1 text-neutral-200">Use Preline's automated libraries to manage your business.</span> + </span> + </span> + </button> + + <button type="button" class="hs-tab-active:bg-neutral-700 hs-tab-active:shadow-md text-start hover:bg-neutral-700 focus:bg-neutral-700 p-4 md:p-5 rounded-xl" id="tabs-with-card-item-2" aria-selected="false" data-hs-tab="#tabs-with-card-2" aria-controls="tabs-with-card-2" role="tab"> + <span class="flex gap-x-6"> + <svg class="shrink-0 mt-2 size-6 md:size-7 text-blue-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/></svg> + <span class="grow"> + <span class="block text-lg font-semibold text-blue-500">Smart dashboards</span> + <span class="block mt-1 text-neutral-200">Quickly use Preline sample components.</span> + </span> + </span> + </button> + + <button type="button" class="hs-tab-active:bg-neutral-700 hs-tab-active:shadow-md text-start hover:bg-neutral-700 focus:bg-neutral-700 p-4 md:p-5 rounded-xl" id="tabs-with-card-item-3" aria-selected="false" data-hs-tab="#tabs-with-card-3" aria-controls="tabs-with-card-3" role="tab"> + <span class="flex gap-x-6"> + <svg class="shrink-0 mt-2 size-6 md:size-7 text-blue-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/></svg> + <span class="grow"> + <span class="block text-lg font-semibold text-blue-500">Powerful features</span> + <span class="block mt-1 text-neutral-200">Save time with Preline's modern designs.</span> + </span> + </span> + </button> + </nav> + </div> + <!-- End Col --> + + <div class="lg:col-span-6"> + <div class="relative"> + <!-- Tab Content --> + <div> + <div id="tabs-with-card-1" role="tabpanel" aria-labelledby="tabs-with-card-item-1"> + <img class="shadow-xl shadow-gray-900/20 rounded-xl" src="https://images.unsplash.com/photo-1605629921711-2f6b00c6bbf4?ixlib=rb-4.0.3&auto=format&fit=crop&w=560&h=720&q=80" alt="Features Image"> + </div> + + <div id="tabs-with-card-2" class="hidden" role="tabpanel" aria-labelledby="tabs-with-card-item-2"> + <img class="shadow-xl shadow-gray-900/20 rounded-xl" src="https://images.unsplash.com/photo-1665686306574-1ace09918530?ixlib=rb-4.0.3&auto=format&fit=crop&w=560&h=720&q=80" alt="Features Image"> + </div> + + <div id="tabs-with-card-3" class="hidden" role="tabpanel" aria-labelledby="tabs-with-card-item-3"> + <img class="shadow-xl shadow-gray-900/20 rounded-xl" src="https://images.unsplash.com/photo-1598929213452-52d72f63e307?ixlib=rb-4.0.3&auto=format&fit=crop&w=560&h=720&q=80" alt="Features Image"> + </div> + </div> + <!-- End Tab Content --> + </div> + </div> + <!-- End Col --> + </div> + <!-- End Grid --> + + <!-- Background Color --> + <div class="absolute inset-0 bg-neutral-800 rounded-xl"></div> + </div> + </div> + <!-- End Features --> + </div> +</template> \ No newline at end of file diff --git a/movie-group-8/src/views/Login.vue b/movie-group-8/src/views/Login.vue index 01c5b0fd09732554a326774fb53310e9910d8e21..6039cde7d6f3a886bc58c4c92fff10ea5f90fa2d 100644 --- a/movie-group-8/src/views/Login.vue +++ b/movie-group-8/src/views/Login.vue @@ -79,7 +79,7 @@ const auth = getAuth(); await signInWithEmailAndPassword(auth, email.value, password.value); console.log('Successfully registered!'); - router.push('/feed'); + router.push('/films'); } catch (error) { console.error(error); alert(error.message); @@ -95,7 +95,7 @@ const provider = new GoogleAuthProvider(); await signInWithPopup(auth, provider); console.log('Successfully signed in with Google!'); - router.push('/feed'); + router.push('/films'); } catch (error) { console.error(error); alert(error.message); diff --git a/movie-group-8/src/views/Register.vue b/movie-group-8/src/views/Register.vue index 6f7f360399a4c19c4a2e12732f0a9259c6798cc6..2880887871e12186c4e92abcc2b2d1f83fff625f 100644 --- a/movie-group-8/src/views/Register.vue +++ b/movie-group-8/src/views/Register.vue @@ -95,7 +95,7 @@ loading.value = true; try { const auth = getAuth(); await createUserWithEmailAndPassword(auth, email.value, password.value); - router.push('/feed'); + router.push('/films'); } catch (error) { alert(error.message); } finally { diff --git a/movie-group-8/src/views/Watchlist.vue b/movie-group-8/src/views/Watchlist.vue new file mode 100644 index 0000000000000000000000000000000000000000..881fa1732ebc44f101dee618914366c413913c24 --- /dev/null +++ b/movie-group-8/src/views/Watchlist.vue @@ -0,0 +1 @@ +<template>Watchlist</template> \ No newline at end of file