refactor: restructure all components into separate folders/views

This commit is contained in:
2025-07-16 11:11:40 -05:00
parent ccd975d181
commit d5211ef24b
20 changed files with 123 additions and 123 deletions
Vendored
+3 -3
View File
@@ -1,7 +1,7 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>
export default component
import type { DefineComponent } from 'vue';
const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>;
export default component;
}
+13 -13
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import SeasonList from '@/components/SeasonList.vue';
import SeasonList from '@/components/layout/SeasonList.vue';
import { ref } from 'vue';
import logoSrc from '@/assets/logo.svg';
@@ -21,14 +21,14 @@ const headings = [
<div class="min-h-screen bg-gray-50">
<!-- Header -->
<header
class="bg-white px-4 py-3 border-b border-gray-200 fixed top-0 left-0 right-0 z-40 flex items-center h-24"
class="fixed top-0 right-0 left-0 z-40 flex h-24 items-center border-b border-gray-200 bg-white px-4 py-3"
>
<div class="flex items-center w-full justify-between">
<div class="flex w-full items-center justify-between">
<div class="flex items-center space-x-4">
<!-- Mobile menu button -->
<button
@click="toggleSidebar"
class="lg:hidden p-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500"
class="rounded-md p-2 text-gray-600 hover:bg-gray-100 hover:text-gray-900 focus:ring-2 focus:ring-blue-500 focus:outline-none focus:ring-inset lg:hidden"
>
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
@@ -42,33 +42,33 @@ const headings = [
<!-- Logo/Brand -->
<div class="flex">
<img :src="logoSrc" alt="The Office Logo" class="h-full max-w-[225px] py-4 mr-6" />
<img :src="logoSrc" alt="The Office Logo" class="mr-6 h-full max-w-[225px] py-4" />
</div>
</div>
<!-- Header Navigation -->
<nav
class="hidden text-gray-800 md:flex items-center space-x-2 font-display text-2xl tracking-widest lowercase"
class="font-display hidden items-center space-x-2 text-2xl tracking-widest text-gray-800 lowercase md:flex"
>
<RouterLink
v-for="heading in headings"
:key="heading.name"
:to="heading.href"
class="hover:text-blue-600 px-3 py-2 transition-[color]"
class="px-3 py-2 transition-[color] hover:text-blue-600"
>
{{ heading.name }}
</RouterLink>
</nav>
<!-- Search bar -->
<div class="hidden md:flex items-center">
<div class="hidden items-center md:flex">
<div class="relative">
<input
type="text"
placeholder="Search..."
class="w-64 pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
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="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<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"
@@ -88,7 +88,7 @@ const headings = [
</div>
</header>
<div class="flex mt-24">
<div class="mt-24 flex">
<!-- Sidebar -->
<div class="pl-8">
<SeasonList />
@@ -98,13 +98,13 @@ const headings = [
<div
v-if="sidebarOpen"
@click="toggleSidebar"
class="fixed inset-0 z-20 bg-black bg-opacity-50 lg:hidden"
class="bg-opacity-50 fixed inset-0 z-20 bg-black lg:hidden"
></div>
<!-- Main Content -->
<main class="col-span-8 lg:ml-0">
<div class="p-6">
<h2 class="text-2xl text-gray-900 mb-6">Welcome to The Office</h2>
<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>
-25
View File
@@ -1,25 +0,0 @@
<template>
<div>
<ais-hits>
<template v-slot="{ items }">
<div>
<SearchResult v-for="item in items" :key="item.objectID" :item="item" />
</div>
</template>
</ais-hits>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import SearchResult from "@/components/SearchResult.vue";
export default defineComponent({
name: "SearchResults",
components: {
SearchResult,
},
});
</script>
@@ -1,5 +1,5 @@
<template>
<table class="quote-list px-3 w-100">
<table class="quote-list w-100 px-3">
<tr
v-for="(quote, index) in quotes"
:id="`${sceneIndex}-${index}`"
@@ -53,9 +53,9 @@
</style>
<script lang="ts">
import { defineComponent } from 'vue'
import { defineComponent } from 'vue';
import DynamicSpeaker from '@/components/DynamicSpeaker.vue'
import DynamicSpeaker from '@/components/features/DynamicSpeaker.vue';
export default defineComponent({
components: {
@@ -76,16 +76,16 @@ export default defineComponent({
methods: {
transform(quoteText) {
if (quoteText.includes('[')) {
return quoteText.replace(/\[([^\]]+)]/g, ' <i>[$1]</i> ')
return quoteText.replace(/\[([^\]]+)]/g, ' <i>[$1]</i> ');
}
return quoteText
return quoteText;
},
quote_link(quoteIndex) {
return `/${this.$route.params.season}/${this.$route.params.episode}#${this.sceneIndex}-${quoteIndex}`
return `/${this.$route.params.season}/${this.$route.params.episode}#${this.sceneIndex}-${quoteIndex}`;
},
copy(quoteIndex) {
this.$copyText(import.meta.env.VUE_APP_BASE_URL + this.quote_link(quoteIndex))
this.$copyText(import.meta.env.VUE_APP_BASE_URL + this.quote_link(quoteIndex));
},
},
})
});
</script>
@@ -1,5 +1,5 @@
<script setup lang="ts">
import SeasonListItem from '@/components/SeasonListItem.vue';
import SeasonListItem from '@/components/layout/SeasonListItem.vue';
import {
Accordion,
AccordionContent,
+13 -13
View File
@@ -1,12 +1,12 @@
import Home from '@/components/Home.vue'
import Episode from '@/components/Episode.vue'
import SearchResults from '@/components/SearchResults.vue'
import Character from '@/components/Character.vue'
import Season from '@/components/Season.vue'
import Characters from '@/components/Characters.vue'
import About from '@/components/About.vue'
import Home from '@/views/Home.vue';
import Episode from '@/views/Episode.vue';
import SearchResults from '@/views/SearchResults.vue';
import Character from '@/views/Character.vue';
import Season from '@/views/Season.vue';
import Characters from '@/views/Characters.vue';
import About from '@/views/About.vue';
import { createRouter, createWebHistory } from 'vue-router'
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
@@ -51,16 +51,16 @@ const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// https://router.vuejs.org/guide/advanced/scroll-behavior.html
if (to.hash) {
return { el: to.hash, behavior: 'smooth' }
return { el: to.hash, behavior: 'smooth' };
}
if (savedPosition) {
return savedPosition
return savedPosition;
}
return {
x: 0,
y: 0,
}
};
},
})
});
export default router
export default router;
@@ -30,15 +30,15 @@
</style>
<script lang="ts">
import { defineComponent, nextTick } from 'vue'
import Skeleton from './Skeleton.vue'
import { BBreadcrumb } from 'bootstrap-vue-next'
import useStore from '@/store'
import { defineComponent, nextTick } from 'vue';
import Skeleton from '@/components/common/Skeleton.vue';
import { BBreadcrumb } from 'bootstrap-vue-next';
import useStore from '@/store';
interface BreadcrumbItem {
text: string
to?: { name: string }
active?: boolean
text: string;
to?: { name: string };
active?: boolean;
}
export default defineComponent({
@@ -50,17 +50,17 @@ export default defineComponent({
},
setup() {
const store = useStore()
const store = useStore();
return {
store,
}
};
},
computed: {
character() {
return this.store.characters[this.$route.params.character as string]
return this.store.characters[this.$route.params.character as string];
},
ready(): boolean {
return this.character !== undefined
return this.character !== undefined;
},
breadcrumbs(): BreadcrumbItem[] {
@@ -77,31 +77,31 @@ export default defineComponent({
text: this.character?.name || (this.$route.params.character as string),
active: true,
},
]
];
},
},
watch: {
'$route.params.character'() {
nextTick(() => {
this.fetchCharacter()
})
this.fetchCharacter();
});
},
},
mounted() {
this.fetchCharacter()
this.fetchCharacter();
},
methods: {
async fetchCharacter(): Promise<void> {
try {
await this.store.preloadCharacters()
this.character = this.store.characters[this.$route.params.character as string]
await this.store.preloadCharacters();
this.character = this.store.characters[this.$route.params.character as string];
} catch (error) {
console.error('Error fetching character:', error)
console.error('Error fetching character:', error);
}
},
},
})
});
</script>
@@ -87,11 +87,11 @@ h4 {
</style>
<script lang="ts">
import { defineComponent } from 'vue'
import { defineComponent } from 'vue';
import Skeleton from '@/components/Skeleton.vue'
import ImageSkeleton from '@/components/ImageSkeleton.vue'
import { BBreadcrumb, BImg } from 'bootstrap-vue-next'
import Skeleton from '@/components/common/Skeleton.vue';
import ImageSkeleton from '@/components/common/ImageSkeleton.vue';
import { BBreadcrumb, BImg } from 'bootstrap-vue-next';
export default defineComponent({
name: 'CharactersComponent',
@@ -105,24 +105,24 @@ export default defineComponent({
computed: {
ready() {
return this.$store.getters.checkPreloaded('characters')
return this.$store.getters.checkPreloaded('characters');
},
sorted_character_ids() {
return this.$store.getters.getSortedCharacters()
return this.$store.getters.getSortedCharacters();
},
characters() {
return this.$store.state.characters
return this.$store.state.characters;
},
breadcrumbs() {
return [
{ text: 'Home', to: { name: 'Home' } },
{ text: 'Characters', active: true },
]
];
},
},
async mounted() {
await this.$store.dispatch(types.PRELOAD_CHARACTERS)
await this.$store.dispatch(types.PRELOAD_CHARACTERS);
// Re-compute computed properties since Vuex won't do it
// this.$forceUpdate();
@@ -130,8 +130,8 @@ export default defineComponent({
methods: {
faceURL(character, thumbnail = false) {
return `/img/${character}/` + (thumbnail ? 'face_thumb' : 'face') + '.jpeg'
return `/img/${character}/` + (thumbnail ? 'face_thumb' : 'face') + '.jpeg';
},
},
})
});
</script>
@@ -28,7 +28,7 @@
<QuoteList :quotes="scene.quotes" :scene-index="sceneIndex" />
<span
v-if="scene.deleted"
class="mt-n2 mb-4 text-muted deleted-scene pl-2"
class="mt-n2 text-muted deleted-scene mb-4 pl-2"
:footer="`Deleted Scene ${scene.deleted}`"
>
Deleted Scene {{ scene.deleted }}
@@ -52,12 +52,12 @@
</style>
<script lang="ts">
import { defineComponent, nextTick } from 'vue'
import { defineComponent, nextTick } from 'vue';
import QuoteList from '@/components/QuoteList.vue'
import CharacterBadges from '@/components/CharacterBadges.vue'
import Skeleton from '@/components/Skeleton.vue'
import { BBreadcrumb } from 'bootstrap-vue-next'
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',
@@ -71,14 +71,14 @@ export default defineComponent({
computed: {
episode() {
return this.$store.getters.getEpisode(this.params.season, this.params.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
return this.$route.params;
},
ready() {
return this.$store.getters.isFetched(this.params.season, this.params.episode)
return this.$store.getters.isFetched(this.params.season, this.params.episode);
},
breadcrumbs() {
return [
@@ -104,7 +104,7 @@ export default defineComponent({
},
active: true,
},
]
];
},
},
@@ -112,14 +112,14 @@ export default defineComponent({
// When route changes, fetch data for current Episode route
$route() {
nextTick(() => {
this.fetch()
})
this.fetch();
});
},
},
created() {
// When page loads directly on this Episode initially, fetch data
this.fetch()
this.fetch();
},
methods: {
@@ -129,17 +129,17 @@ export default defineComponent({
.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()
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' })
})
const section = document.getElementById(this.$route.hash.substring(1));
this.$scrollTo(section, 500, { easing: 'ease-in' });
});
}
})
});
},
},
})
});
</script>
+11 -11
View File
@@ -27,11 +27,11 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { defineComponent } from 'vue';
import axios from 'axios'
import Skeleton from './Skeleton.vue'
import { BCardText, BCard } from 'bootstrap-vue-next'
import axios from 'axios';
import Skeleton from '@/components/common/Skeleton.vue';
import { BCardText, BCard } from 'bootstrap-vue-next';
export default defineComponent({
name: 'HomeComponent',
@@ -45,12 +45,12 @@ export default defineComponent({
data() {
return {
stats: null,
}
};
},
computed: {
ready() {
return true
return true;
// return this.stats != null;
},
},
@@ -61,16 +61,16 @@ export default defineComponent({
methods: {
getStats() {
const path = `${import.meta.env.VUE_APP_API_URL}/api/stats/`
const path = `${import.meta.env.VUE_APP_API_URL}/api/stats/`;
axios
.get(path)
.then((res) => {
this.stats = res.data
this.stats = res.data;
})
.catch((error) => {
console.error(error)
})
console.error(error);
});
},
},
})
});
</script>
+25
View File
@@ -0,0 +1,25 @@
<template>
<div>
<ais-hits>
<template v-slot="{ items }">
<div>
<SearchResult v-for="item in items" :key="item.objectID" :item="item" />
</div>
</template>
</ais-hits>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import SearchResult from '@/components/features/SearchResult.vue';
export default defineComponent({
name: 'SearchResults',
components: {
SearchResult,
},
});
</script>