Initial commit

This commit is contained in:
Edward Tirado Jr 2025-03-30 20:51:11 -05:00
commit 50c4133930
35 changed files with 12673 additions and 0 deletions

88
src/pages/admin/index.vue Normal file
View file

@ -0,0 +1,88 @@
<template>
<div class="p-5 sm:p-0">
<Modal>
<AddMovie v-if="modal_movie" :movie="modal_movie"></AddMovie>
</Modal>
<div class="text-center sm:text-left">
<ul class="inline-flex space-x-5 pb-3">
<li id="search-tab" class="hover-pointer me-3 underline" @click="toggleDisplay('search')">Search</li>
<li id="showings-tab" class="hover-pointer me-3" @click="toggleDisplay('showings')">Showings</li>
<li id="lists-tab" class="hover-pointer" @click="toggleDisplay('lists')">Lists</li>
<li id="logout" class="hover-pointer" @click="logout">Logout</li>
</ul>
</div>
<div id="search">
<search/>
</div>
<div id="showings" class="hidden">
<showings/>
</div>
<div id="lists" class="hidden">
<lists></lists>
</div>
</div>
</template>
<script>
import AddMovie from "~/components/modal-content/AddMovie.vue";
import Search from "~/components/admin/search.vue";
import Showings from "~/components/admin/showings.vue";
import Lists from "~/components/admin/lists.vue";
export default {
name: "index",
components: {Lists, Showings, Search, AddMovie},
data: () => ({
lists: [],
modal_movie: null,
}),
methods: {
showModal: function (movie) {
this.modal_movie = movie;
document.getElementById("movie-modal").classList.remove("hidden");
},
toggleDisplay: function (id) {
let tabs = ["search", "showings", "lists"];
tabs.forEach((value) => {
if (value === id) {
document.getElementById(id).classList.toggle("hidden");
document.getElementById(id + "-tab").classList.toggle("underline");
} else if (!document.getElementById(value).classList.contains("hidden")) {
document.getElementById(value).classList.toggle("hidden");
document.getElementById(value + "-tab").classList.toggle("underline");
}
})
},
logout: () => {
let config = useRuntimeConfig()
let token = useCookie("token").value;
fetch(`${config.public.apiURL}/logout`, {
method: "PUT",
headers: {"Content-type": "application/json", "token": token},
})
.then(response => response.json())
.then(_json => {
let token = useCookie("token");
token.value = null;
window.location = "/";
})
.catch(err => console.log(err))
}
},
mounted() {
const token = useCookie("token").value;
if (!token) {
navigateTo("/")
}
}
}
</script>
<style scoped>
</style>

84
src/pages/index.vue Normal file
View file

@ -0,0 +1,84 @@
<template>
<div>
<div class="p-5 movie-card neon-border">
<h3 class="bloodseeker mb-5">Login</h3>
<form class="grid p-1 p-sm-5" method="post" name="login-form" v-on:keyup.enter="login">
<div class="mx-auto">
<!-- USERNAME -->
<div class="row pb-5">
<label class="fw-bold pb-1 mx-0 px-0" for="username">Username</label><br/>
<input id="username" class="p-2 rounded" placeholder="username" type="text"/>
</div>
<!-- PASSWORD -->
<div class="row">
<label class="fw-bold pb-1 px-0" for="password">Password</label><br/>
<input id="password" class="p-2 rounded" placeholder="password" type="password"/>
</div>
</div>
<!-- SUBMIT BUTTON -->
<div class="mx-auto">
<button class="btn my-5 p-3 rounded" type="button" @click="login">Submit</button>
</div>
<div class="mx-auto pt-5">
<img id="password-incorrect" alt="password-incorrect" class="hidden"
src="https://i.imgur.com/6pXxxyZ.gif"/>
</div>
</form>
</div>
</div>
</template>
<script>
export default {
name: "index",
methods: {
login: async function (e) {
const config = useRuntimeConfig();
e.preventDefault()
document.getElementById("password-incorrect").classList.add("hidden")
let username = document.getElementById("username").value;
let password = document.getElementById("password").value;
let response = await fetch(`${config.public.apiURL}/auth/login`, {
method: "POST",
body: JSON.stringify({username: username, password: password}),
headers: {"Content-type": "application/json"}
})
.then(response => {
if (!response.ok) {
document.getElementById("password-incorrect").classList.remove("hidden")
return false;
}
return response.json();
//window.location = "/admin"
})
.catch(err => {
return false
})
if (response) {
let token = useCookie("token", {
sameSite: "lax",
});
token.value = response.token;
return navigateTo("/admin")
}
}
},
mounted() {
let token = useCookie("token");
if (token.value) {
navigateTo("/admin")
}
}
}
</script>
<style scoped>
</style>

144
src/pages/lists/[id].vue Normal file
View file

@ -0,0 +1,144 @@
<template>
<div v-if="list_id !== 0" class="p-5 sm:p-0">
<Modal>
<ShowMovie v-if="modal_movie" :movie="modal_movie"></ShowMovie>
</Modal>
<h2 class="text-xl font-bold pb-5">{{ list.name }}</h2>
<div class="grid grid-cols-2 rounded movie-card neon-border p-5">
<div>
<ul class="flex flex-row">
<li>
<label class="mr-2" for="hide_scheduled">Hide Scheduled</label>
<input
@change="hideScheduled"
v-model="hide_scheduled" id="hide_scheduled" type="checkbox"/>
</li>
</ul>
</div>
<input v-model="movie_query"
class="p-1 rounded"
placeholder="Filter Movies"
type="text"
@input="filterMovies"
/>
</div>
<!-- MOVIE LIST -->
<ul class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-2 mt-5">
<li v-for="movie in filtered_movies" :key="movie.id" class="rounded movie-card neon-border">
<!-- POSTER -->
<img
:data-src="movie.poster"
alt="movie poster"
class="lazyload p-3 movie-poster hover-pointer mx-auto"
@click="showModal(movie)"
/>
<div class="p-5 flex flex-col">
<!-- TITLE -->
<span class="font-bold text-center mb-1">{{ movie.title }}</span>
<span v-if="logged_in" class="text-center hover-pointer" @click="removeMovie(movie.id)">
X
</span>
</div>
</li>
</ul>
</div>
</template>
<script>
import ShowMovie from "~/components/modal-content/ShowMovie.vue";
import 'lazysizes';
export default {
name: "list",
components: {ShowMovie},
data: () => ({
list_id: 0,
list: [],
modal_movie: null,
movies: [],
filtered_movies: [],
movie_query: "",
logged_in: false,
hide_scheduled: false,
}),
methods: {
getList: function (list_id) {
let config = useRuntimeConfig()
fetch(`${config.public.apiURL}/lists/${list_id}`, {
method: "GET",
headers: {"Content-type": "application/json"}
})
.then(response => response.json())
.then(json => {
this.list = json.list;
this.movies = json.movies;
this.filtered_movies = this.movies;
})
.catch(err => console.log(err))
},
hideScheduled: function() {
if(this.hide_scheduled) {
this.filtered_movies = this.movies.filter(movie => {
return movie.last_watched === null
});
} else {
this.filtered_movies = this.movies;
}
},
removeMovie: function (movie_id) {
let config = useRuntimeConfig()
let confirmed = confirm("Remove movie from list?");
if (!confirmed) {
return false;
}
return fetch(`${config.public.apiURL}/movies/l/${this.list_id}/m/${movie_id}`, {
credentials: "include",
method: "DELETE",
headers: {
"Content-type": "application/json",
"token": useCookie("token").value,
}
})
.then(response => response.json())
.then(_json => {
this.filtered_movies = this.filtered_movies.filter((movie) => {
return movie.id !== movie_id
})
})
.catch(err => console.log(err));
},
filterMovies: function () {
if (!this.movie_query) {
this.filtered_movies = this.movies;
return;
}
this.filtered_movies = this.movies.filter(movie => {
return movie.title.toLowerCase()
.search(this.movie_query.toLowerCase()) > -1
});
},
showModal: function (movie) {
this.modal_movie = movie;
document.getElementById("movie-modal").classList.remove("hidden");
},
},
mounted() {
const route = useRoute();
this.list_id = route.params.id
this.getList(this.list_id)
const token = useCookie("token").value;
if (token) {
this.logged_in = true;
}
}
}
</script>
<style scoped>
</style>

42
src/pages/lists/index.vue Normal file
View file

@ -0,0 +1,42 @@
<template>
<div class="p-5 sm:p-0">
<ul class="grid grid-cols-2 gap-3 mt-5">
<li v-for="list in lists" class="movie-card neon-border p-5 rounded">
<div class="grid grid-rows-2 gap-3">
<NuxtLink :to="`/lists/${list.id}`" class="underline">
<h2 class="text-lg">{{ list.name }}</h2>
</NuxtLink>
<span>Movies: {{ list.movie_count }}</span>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "index",
data: () => ({
lists: [],
}),
methods: {
getLists: function () {
let config = useRuntimeConfig();
fetch(`${config.public.apiURL}/lists`, {
method: "GET",
headers: {"Content-type": "application/json"}
})
.then(response => response.json())
.then(json => this.lists = json)
.catch(err => console.log(err))
},
},
mounted() {
this.getLists()
}
}
</script>
<style scoped>
</style>

View file

@ -0,0 +1,101 @@
<template>
<div class="p-5 sm:p-0">
<ul class="flex flex-col gap-5">
<li v-for="showing in showings" class="p-5 movie-card neon-border">
<div class="sm:grid grid-cols-2 lg:grid-cols-3">
<img :src="showing.poster"
alt="Movie Poster"
class="mx-auto mb-5 sm:mb-0 sm:mx-0 neon-border bg-black schedule-poster"
/>
<div class="self-center text-left">
<h5 class="text-center sm:text-left mb-3 text-xl">{{ showing.title }}</h5>
<h5 class="text-center sm:text-left mb-3">{{ formatDate(showing.showtime) }}</h5>
<span class="">{{ showing.plot }}</span>
</div>
</div>
</li>
</ul>
<!-- PREVIOUS SHOWINGS -->
<div id="previous-showings" class="mt-5 list-group">
<span class="block mb-5 hover-pointer underline" @click="getShowings(true)">
Previous Showings
</span>
<span id="loader" class="hidden">Loading...</span>
<ul class="flex flex-col gap-5">
<li v-for="showing in previous_showings" class="p-5 movie-card neon-border">
<div class="sm:grid grid-cols-2 lg:grid-cols-3">
<img :src="showing.poster"
alt="Movie Poster"
class="mx-auto mb-5 sm:mb-0 sm:mx-0 neon-border bg-black schedule-poster"
/>
<div class="self-center text-left">
<h5 class="text-xl mb-3">{{ showing.title }}</h5>
<h5 class="mb-3">{{ formatDate(showing.showtime) }}</h5>
<span class="">{{ showing.plot }}</span>
</div>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: "index",
data: () => ({
showings: [],
previous_showings: [],
got_previous: false,
months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
}),
methods: {
formatDate: function (date_string) {
console.log(date_string)
let parsed_date = new Date(Date.parse(date_string));
let month = this.months[parsed_date.getMonth()];
return `${month} ${parsed_date.getDate()}, ${parsed_date.getFullYear()}`
},
getShowings: function (previous = false) {
let config = useRuntimeConfig()
if (this.got_previous) {
return false;
}
document.getElementById("loader").classList.toggle("hidden")
let params = "";
if (previous) params = "?previous=true";
return fetch(`${config.public.apiURL}/schedules/1${params}`, {
method: "GET",
headers: {"Content-type": "application/json"}
})
.then(response => response.json())
.then(showings => {
if (previous) {
this.got_previous = true;
this.previous_showings = showings;
} else {
this.showings = showings
}
document.getElementById("loader").classList.toggle("hidden")
})
.catch(err => console.log(err));
}
},
mounted() {
this.getShowings()
}
}
</script>
<style scoped>
</style>