feat: separate SearchBar in App.vue, start work on Episode view

This commit is contained in:
2025-07-16 11:57:36 -05:00
parent cf7cb09687
commit fa924a26be
4 changed files with 142 additions and 127 deletions
+3 -28
View File
@@ -2,6 +2,7 @@
import SeasonList from '@/components/layout/SeasonList.vue';
import { ref } from 'vue';
import logoSrc from '@/assets/logo.svg';
import SearchBar from '@/components/layout/SearchBar.vue';
const sidebarOpen = ref(false);
@@ -62,28 +63,7 @@ const headings = [
<!-- Search bar -->
<div class="hidden items-center md:flex">
<div class="relative">
<input
type="text"
placeholder="Search..."
class="w-64 rounded-lg border border-gray-300 py-2 pr-4 pl-10 outline-none focus:border-transparent focus:ring-2 focus:ring-blue-500"
/>
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<svg
class="h-5 w-5 text-gray-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
</div>
</div>
<SearchBar />
</div>
</div>
</header>
@@ -103,12 +83,7 @@ const headings = [
<!-- Main Content -->
<main class="col-span-8 lg:ml-0">
<div class="p-6">
<h2 class="mb-6 text-2xl text-gray-900">Welcome to The Office</h2>
<p class="text-gray-600">
This is your main content area. You can add your router-view or other components here.
</p>
</div>
<RouterView />
</main>
</div>
</div>
+20
View File
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { ref } from 'vue';
import { SearchIcon } from 'lucide-vue-next';
const searchQuery = ref('');
</script>
<template>
<div class="relative">
<input
v-model="searchQuery"
type="text"
placeholder="Quotes, characters, episodes..."
class="w-72 rounded-lg border border-gray-400 py-2 pr-4 pl-10 outline-none focus:border-transparent focus:ring-2 focus:ring-blue-500"
/>
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<SearchIcon class="size-5 text-gray-500" />
</div>
</div>
</template>
+2 -2
View File
@@ -37,12 +37,12 @@ const router = createRouter({
component: Character,
},
{
path: '/:season/',
path: '/season/:season',
name: 'Season',
component: Season,
},
{
path: '/:season/:episode',
path: '/episode/:episode',
name: 'Episode',
component: Episode,
},
+117 -97
View File
@@ -1,9 +1,122 @@
<script setup lang="ts">
import QuoteList from '@/components/features/QuoteList.vue';
import CharacterBadges from '@/components/features/CharacterBadges.vue';
import Skeleton from '@/components/common/Skeleton.vue';
import Breadcrumb from '@/components/common/Breadcrumb.vue';
import { computed } from 'vue';
// import { useRoute } from 'vue-router';
// const route = useRoute();
const route = {
params: {
season: '1',
episode: '1',
},
};
const breadcrumbs = computed(() => {
return [
{ text: 'Home', to: { name: 'Home' } },
{ text: `Season ${route.params.season}`, to: { name: 'Season', season: route.params.season } },
{
text: `Episode ${route.params.episode}`,
to: { name: 'Episode', season: route.params.season, episode: route.params.episode },
},
];
});
// export default defineComponent({
// name: 'EpisodeComponent',
// components: {
// QuoteList,
// CharacterBadges,
// Skeleton,
// BBreadcrumb,
// },
// computed: {
// episode() {
// return this.$store.getters.getEpisode(this.params.season, this.params.episode);
// },
// // Shorthand - literally useless, why does everything to have such long prefixes in dot notation
// params() {
// return this.$route.params;
// },
// ready() {
// return this.$store.getters.isFetched(this.params.season, this.params.episode);
// },
// breadcrumbs() {
// return [
// {
// text: 'Home',
// to: {
// name: 'Home',
// },
// },
// {
// text: `Season ${this.$route.params.season}`,
// to: {
// name: 'Season',
// season: this.$route.params.season,
// },
// },
// {
// text: `Episode ${this.$route.params.episode}`,
// to: {
// name: 'Episode',
// season: this.$route.params.season,
// episode: this.$route.params.episode,
// },
// active: true,
// },
// ];
// },
// },
// watch: {
// // When route changes, fetch data for current Episode route
// $route() {
// nextTick(() => {
// this.fetch();
// });
// },
// },
// created() {
// // When page loads directly on this Episode initially, fetch data
// this.fetch();
// },
// methods: {
// async fetch() {
// // Fetch the episode, then scroll - already fetched episode should scroll immediately
// this.$store
// .dispatch(types.FETCH_EPISODE, { season: this.params.season, episode: this.params.episode })
// .then(() => {
// // Force update, as for some reason it doesn't update naturally. I hate it too.
// this.$forceUpdate();
// // Scroll down to quote
// if (this.$route.hash) {
// nextTick(() => {
// const section = document.getElementById(this.$route.hash.substring(1));
// this.$scrollTo(section, 500, { easing: 'ease-in' });
// });
// }
// });
// },
// },
// });
</script>
<template>
<div>
<BBreadcrumb v-if="ready" :items="breadcrumbs" />
<BCard v-else class="breadcrumb-skeleton mb-3">
<div class="h-full w-full p-4">
<Breadcrumb :items="breadcrumbs" />
<!-- <BCard v-else class="breadcrumb-skeleton mb-3">
<Skeleton style="width: 40%" />
</BCard>
</BCard> -->
<BCard class="mb-4">
<template v-if="ready">
<h3 class="card-title">"{{ episode.title }}"</h3>
@@ -50,96 +163,3 @@
line-height: 12px;
}
</style>
<script lang="ts">
import { defineComponent, nextTick } from 'vue';
import QuoteList from '@/components/features/QuoteList.vue';
import CharacterBadges from '@/components/features/CharacterBadges.vue';
import Skeleton from '@/components/common/Skeleton.vue';
import { BBreadcrumb } from 'bootstrap-vue-next';
export default defineComponent({
name: 'EpisodeComponent',
components: {
QuoteList,
CharacterBadges,
Skeleton,
BBreadcrumb,
},
computed: {
episode() {
return this.$store.getters.getEpisode(this.params.season, this.params.episode);
},
// Shorthand - literally useless, why does everything to have such long prefixes in dot notation
params() {
return this.$route.params;
},
ready() {
return this.$store.getters.isFetched(this.params.season, this.params.episode);
},
breadcrumbs() {
return [
{
text: 'Home',
to: {
name: 'Home',
},
},
{
text: `Season ${this.$route.params.season}`,
to: {
name: 'Season',
season: this.$route.params.season,
},
},
{
text: `Episode ${this.$route.params.episode}`,
to: {
name: 'Episode',
season: this.$route.params.season,
episode: this.$route.params.episode,
},
active: true,
},
];
},
},
watch: {
// When route changes, fetch data for current Episode route
$route() {
nextTick(() => {
this.fetch();
});
},
},
created() {
// When page loads directly on this Episode initially, fetch data
this.fetch();
},
methods: {
async fetch() {
// Fetch the episode, then scroll - already fetched episode should scroll immediately
this.$store
.dispatch(types.FETCH_EPISODE, { season: this.params.season, episode: this.params.episode })
.then(() => {
// Force update, as for some reason it doesn't update naturally. I hate it too.
this.$forceUpdate();
// Scroll down to quote
if (this.$route.hash) {
nextTick(() => {
const section = document.getElementById(this.$route.hash.substring(1));
this.$scrollTo(section, 500, { easing: 'ease-in' });
});
}
});
},
},
});
</script>