feat!: switch to Nuxt, complete overhaul

This commit is contained in:
2025-07-16 12:47:33 -05:00
parent f5ec1d2264
commit 00c0770388
49 changed files with 7414 additions and 1673 deletions
Vendored
+19 -28
View File
@@ -1,35 +1,26 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
# Misc
.DS_Store
dist
dist-ssr
coverage
*.local
.fleet
.idea
/cypress/videos/
/cypress/screenshots/
# Local env files
.env
.env.*
!.env.example
# Editor directories and files
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
# Repository specific
*.scss.css
*.scss.css.map
build
*.cache
# .env files
.env
build/
+1 -1
View File
@@ -11,7 +11,7 @@ const toggleSidebar = () => {
};
const headings = [
{ name: 'Home', href: '/' },
{ href: '/' },
{ name: 'Episodes', href: '/episodes' },
{ name: 'Characters', href: '/characters' },
{ name: 'Seasons', href: '/seasons' },

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

+2 -2
View File
@@ -46,8 +46,6 @@
}
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
@@ -79,6 +77,8 @@
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
}
.dark {
@@ -9,14 +9,12 @@ import {
import type { HTMLAttributes } from 'vue';
import { cn } from '@/lib/utils';
import { computed } from 'vue';
import.meta.glob('/public/json/*.json');
console.log();
const lastIndex = computed(() => props.items.length - 1);
const props = defineProps<
{
items: { text: string; to: { name: string; params?: Record<string, string> } }[];
items: { text: string; to?: string }[];
} & { class?: HTMLAttributes['class'] }
>();
</script>
@@ -27,10 +25,10 @@ const props = defineProps<
<template v-for="(item, index) in items" :key="item.text">
<BreadcrumbSeparator v-if="index !== 0" />
<BreadcrumbItem>
<BreadcrumbLink class="text-gray-600" :href="item.to.name" as-child>
<RouterLink :to="item.to" v-if="index !== lastIndex">
<BreadcrumbLink class="text-gray-600" as-child>
<NuxtLink :to="item.to" v-if="index !== lastIndex">
{{ item.text }}
</RouterLink>
</NuxtLink>
<span v-else>{{ item.text }}</span>
</BreadcrumbLink>
</BreadcrumbItem>
+6
View File
@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
+10 -2
View File
@@ -1,4 +1,4 @@
<template>
<!-- <template>
<div>
<BBreadcrumb :items="breadcrumbs" />
<BCard>
@@ -52,4 +52,12 @@ export default defineComponent({
})
</script>
<style scoped></style>
<style scoped></style> -->
<script setup lang="ts"></script>
<template>
<div>
<h1>About</h1>
</div>
</template>
@@ -1,4 +1,4 @@
<template>
<!-- <template>
<div>
<BBreadcrumb v-if="ready" :items="breadcrumbs" />
<BCard v-else class="breadcrumb-skeleton mb-3">
@@ -104,4 +104,12 @@ export default defineComponent({
},
},
});
</script>
</script> -->
<script setup lang="ts"></script>
<template>
<div>
<h1>Character</h1>
</div>
</template>
@@ -1,4 +1,4 @@
<template>
<!-- <template>
<div>
<template v-if="ready">
<BBreadcrumb v-if="ready" :items="breadcrumbs" />
@@ -25,7 +25,7 @@
class="no-link"
:to="{ name: 'Character', params: { character: id } }"
>
<!-- <b-icon class="h6" icon="caret-right-fill" /> -->
<b-icon class="h6" icon="caret-right-fill" />
</RouterLink>
<span class="h6 font-italic" style="opacity: 50%">
{{ characters[id].actor }}
@@ -135,3 +135,12 @@ export default defineComponent({
},
});
</script>
-->
<script setup lang="ts"></script>
<template>
<div>
<h1>About</h1>
</div>
</template>
@@ -1,27 +1,21 @@
<script setup lang="ts">
import QuoteList from '@/components/features/QuoteList.vue';
import CharacterBadges from '@/components/features/CharacterBadges.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 = useRoute();
const route = {
params: {
season: '1',
episode: '1',
},
};
const params = { season: 1, episode: route.params.id };
const breadcrumbs = computed(() => {
return [
{ text: 'Home', to: { name: 'Home' } },
{ text: `Season ${route.params.season}`, to: { name: 'Season', season: route.params.season } },
{ text: 'Home', to: '/' },
{ text: `Season ${params.season}`, to: `/season/${params.season}` },
{
text: `Episode ${route.params.episode}`,
to: { name: 'Episode', season: route.params.season, episode: route.params.episode },
text: `Episode ${params.episode}`,
to: `/episode/${params.episode}`,
},
];
});
@@ -117,7 +111,7 @@ const breadcrumbs = computed(() => {
<!-- <BCard v-else class="breadcrumb-skeleton mb-3">
<Skeleton style="width: 40%" />
</BCard> -->
<BCard class="mb-4">
<!-- <BCard class="mb-4">
<template v-if="ready">
<h3 class="card-title">"{{ episode.title }}"</h3>
<span>{{ episode.description }}</span>
@@ -129,8 +123,8 @@ const breadcrumbs = computed(() => {
<Skeleton style="width: 45%; height: 60%" />
<Skeleton style="width: 69%; height: 40%" />
</template>
</BCard>
<div v-if="ready">
</BCard> -->
<!-- <div v-if="ready">
<BCard
v-for="(scene, sceneIndex) in episode.scenes"
:key="`scene-${sceneIndex}`"
@@ -148,7 +142,7 @@ const breadcrumbs = computed(() => {
</span>
</BCardText>
</BCard>
</div>
</div> -->
</div>
</template>
+37 -40
View File
@@ -1,5 +1,5 @@
<template>
<BCard>
<!-- <BCard>
<template v-if="ready">
<h4>The Office Quotes</h4>
<BCardText>
@@ -23,54 +23,51 @@
<Skeleton style="width: 60%" />
<Skeleton style="width: 60%" />
</BCardText>
</BCard>
</BCard> -->
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import axios from 'axios';
import Skeleton from '@/components/common/Skeleton.vue';
import { BCardText, BCard } from 'bootstrap-vue-next';
export default defineComponent({
name: 'HomeComponent',
// export default defineComponent({
// name: 'HomeComponent',
components: {
Skeleton,
BCardText,
BCard,
},
// components: {
// Skeleton,
// BCardText,
// BCard,
// },
data() {
return {
stats: null,
};
},
// data() {
// return {
// stats: null,
// };
// },
computed: {
ready() {
return true;
// return this.stats != null;
},
},
// computed: {
// ready() {
// return true;
// // return this.stats != null;
// },
// },
created() {
// this.getStats();
},
// created() {
// // this.getStats();
// },
methods: {
getStats() {
const path = `${import.meta.env.VUE_APP_API_URL}/api/stats/`;
axios
.get(path)
.then((res) => {
this.stats = res.data;
})
.catch((error) => {
console.error(error);
});
},
},
});
// methods: {
// getStats() {
// const path = `${import.meta.env.VUE_APP_API_URL}/api/stats/`;
// axios
// .get(path)
// .then((res) => {
// this.stats = res.data;
// })
// .catch((error) => {
// console.error(error);
// });
// },
// },
// });
</script>
@@ -1,4 +1,4 @@
<template>
<!-- <template>
<div>
<BBreadcrumb :items="breadcrumbs" />
<BCard v-if="ready">
@@ -102,4 +102,12 @@ export default defineComponent({
},
},
})
</script>
</script> -->
<script setup lang="ts"></script>
<template>
<div>
<h1>About</h1>
</div>
</template>
+31 -32
View File
@@ -1,5 +1,4 @@
import { defineStore } from 'pinia';
import axios from 'axios';
export interface Character {
name: string;
@@ -127,49 +126,49 @@ const useStore = defineStore('main', {
}
const path = `/json/${payload.season.toString().padStart(2, '0')}/${payload.episode.toString().padStart(2, '0')}.json`;
axios
.get(path)
.then((res) => {
// Push episode data
this.mergeEpisode({
season: payload.season,
episode: payload.episode,
episodeData: res.data,
});
resolve();
})
.catch((error) => {
console.error(error);
reject(error);
});
// axios
// .get(path)
// .then((res) => {
// // Push episode data
// this.mergeEpisode({
// season: payload.season,
// episode: payload.episode,
// episodeData: res.data,
// });
// resolve();
// })
// .catch((error) => {
// console.error(error);
// reject(error);
// });
});
},
preloadEpisodes(): void {
const path = `/json/episodes.json`;
axios
.get(path)
.then((res) => {
this.mergeEpisodes(res.data as Episode[][]);
this.setPreloaded({ type: 'episodes', status: true });
})
.catch((error) => {
console.error(error);
});
// axios
// .get(path)
// .then((res) => {
// this.mergeEpisodes(res.data as Episode[][]);
// this.setPreloaded({ type: 'episodes', status: true });
// })
// .catch((error) => {
// console.error(error);
// });
},
async preloadCharacters(): Promise<void> {
if (this.checkPreloaded('characters')) return;
const path = `/json/characters.json`;
let res = null;
try {
res = await axios.get(path);
} catch (error) {
console.error(error);
throw error;
}
// try {
// res = await axios.get(path);
// } catch (error) {
// console.error(error);
// throw error;
// }
this.mergeCharacters({ characters: res.data });
// this.mergeCharacters({ characters: res.data });
this.setPreloaded({ type: 'characters', status: true });
},
},
+1 -1
View File
@@ -4,7 +4,7 @@
"typescript": true,
"tailwind": {
"config": "",
"css": "src/index.css",
"css": "app/assets/tailwind.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
+36
View File
@@ -0,0 +1,36 @@
import tailwindcss from "@tailwindcss/vite";
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
css: ['~/assets/tailwind.css', '@fontsource-variable/roboto-slab', '@fontsource/open-sans'],
vite: {
plugins: [tailwindcss()],
},
modules: [
'@nuxt/test-utils',
'@nuxt/ui',
'@nuxt/eslint',
'@nuxt/image',
'shadcn-nuxt',
'@pinia/nuxt'
],
shadcn: {
prefix: '',
componentDir: './app/components/ui',
},
typescript: {
typeCheck: true,
tsConfig: {
compilerOptions: {
allowSyntheticDefaultImports: true,
allowArbitraryExtensions: true,
baseUrl: '.',
paths: {
'@/*': ['./src/*'],
},
},
}
}
})
+18 -57
View File
@@ -4,77 +4,38 @@
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest",
"build-only": "vite build",
"type-check": "vue-tsc --build",
"lint": "eslint . --fix",
"format": "prettier --write src/"
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@fontsource-variable/roboto-slab": "^5.2.6",
"@fontsource/open-sans": "^5.2.6",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/vue-fontawesome": "^3.0.8",
"@nuxt/eslint": "1.5.2",
"@nuxt/image": "1.10.0",
"@nuxt/test-utils": "3.19.2",
"@nuxt/ui": "3.2.0",
"@pinia/nuxt": "0.11.1",
"@tailwindcss/vite": "^4.1.11",
"@vueuse/core": "^13.5.0",
"algoliasearch": "^5.32.0",
"axios": "^1.10.0",
"bootstrap": "^5.3.7",
"bootstrap-vue-next": "^0.30.4",
"browserslist": "^4.25.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"ejs": "^3.1.10",
"instantsearch.css": "^8.5.1",
"eslint": "^9.0.0",
"lucide-vue-next": "^0.525.0",
"moment": "^2.30.1",
"node-forge": "1.3.0",
"nuxt": "^4.0.0",
"pinia": "^3.0.3",
"postcss": "^8",
"reka-ui": "^2.3.2",
"sass": "^1.89.2",
"shadcn-nuxt": "2.2.0",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.11",
"tw-animate-css": "^1.3.5",
"vue": "^3.5.17",
"vue-autosuggest": "^2.2.0",
"vue-router": "^4.5.1",
"ws": "^6.2.3"
"vue-router": "^4.5.1"
},
"packageManager": "pnpm@9.15.1+sha512.1acb565e6193efbebda772702950469150cf12bcc764262e7587e71d19dc98a423dff9536e57ea44c49bdf790ff694e83c27be5faa23d67e0c033b583be4bfcf",
"devDependencies": {
"@iconify-json/radix-icons": "^1.2.2",
"@iconify/vue": "^5.0.0",
"@tsconfig/node22": "^22.0.2",
"@types/jsdom": "^21.1.7",
"@types/node": "^24.0.14",
"@vitejs/plugin-vue": "^6.0.0",
"@vitejs/plugin-vue-jsx": "^5.0.1",
"@vitest/eslint-plugin": "^1.3.4",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.31.0",
"eslint-plugin-vue": "~10.2.0",
"jiti": "^2.4.2",
"jsdom": "^26.1.0",
"npm-run-all2": "^8.0.4",
"prettier": "3.5.3",
"prettier-plugin-tailwindcss": "^0.6.14",
"typescript": "~5.8.3",
"vite": "^7.0.4",
"vite-plugin-vue-devtools": "^7.7.7",
"vitest": "^3.2.4",
"vue-tsc": "^2.2.12"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"packageManager": "pnpm@9.15.1+sha512.1acb565e6193efbebda772702950469150cf12bcc764262e7587e71d19dc98a423dff9536e57ea44c49bdf790ff694e83c27be5faa23d67e0c033b583be4bfcf"
"typescript": "^5.8.3",
"vue-tsc": "^3.0.1"
}
}
+7197 -1367
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
User-Agent: *
Disallow:
-26
View File
@@ -1,26 +0,0 @@
import '@fontsource/open-sans';
import '@fontsource-variable/roboto-slab';
import './index.css';
import { createApp } from 'vue';
import App from '@/App.vue';
import router from '@/router.ts';
import { createPinia } from 'pinia';
// import { createBootstrap, vBToggle } from 'bootstrap-vue-next';
// Add the necessary CSS
// import 'bootstrap/dist/css/bootstrap.css';
// import 'bootstrap-vue-next/dist/bootstrap-vue-next.css';
// Prevent invalid episodes, seasons and characters from being accessed
router.beforeEach((to, from, next) => {
if (from.name !== null && to.name === 'Character' && false) {
} else next();
});
const pinia = createPinia();
const app = createApp(App);
app.use(router);
app.use(pinia);
app.mount('#app');
-66
View File
@@ -1,66 +0,0 @@
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';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about/',
name: 'About',
component: About,
},
{
path: '/characters/',
name: 'Characters',
component: Characters,
},
{
path: '/search_results',
name: 'SearchResults',
component: SearchResults,
},
{
path: '/character/:character',
name: 'Character',
component: Character,
},
{
path: '/season/:season',
name: 'Season',
component: Season,
},
{
path: '/episode/:episode',
name: 'Episode',
component: Episode,
},
{ path: '/:pathMatch(.*)*', name: 'NotFound', redirect: '/' }, // catch all
],
scrollBehavior(to, from, savedPosition) {
// https://router.vuejs.org/guide/advanced/scroll-behavior.html
if (to.hash) {
return { el: to.hash, behavior: 'smooth' };
}
if (savedPosition) {
return savedPosition;
}
return {
x: 0,
y: 0,
};
},
});
export default router;
+8 -4
View File
@@ -1,20 +1,24 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
"path": "./.nuxt/tsconfig.app.json"
},
{
"path": "./tsconfig.app.json"
"path": "./.nuxt/tsconfig.server.json"
},
{
"path": "./tsconfig.vitest.json"
"path": "./.nuxt/tsconfig.shared.json"
},
{
"path": "./.nuxt/tsconfig.node.json"
}
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
"@/*": ["./app/*"]
}
}
}
-18
View File
@@ -1,18 +0,0 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueDevTools from 'vite-plugin-vue-devtools';
import tailwindcss from '@tailwindcss/vite';
import path from 'node:path';
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), vueDevTools(), tailwindcss()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
build: {
outDir: './build',
},
});