Initial commit
This commit is contained in:
commit
50c4133930
35 changed files with 12673 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
node_modules
|
||||||
|
*.log*
|
||||||
|
.nuxt
|
||||||
|
.nitro
|
||||||
|
.cache
|
||||||
|
.output
|
||||||
|
.env
|
||||||
|
dist
|
18
.idea/php.xml
generated
Normal file
18
.idea/php.xml
generated
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="MessDetectorOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PHPCSFixerOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PHPCodeSnifferOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PhpStanOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PsalmOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
122
.idea/workspace.xml
generated
Normal file
122
.idea/workspace.xml
generated
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="5e320804-68c9-4504-97d5-d421de3438b2" name="Changes" comment="">
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/pages/lists/[id].vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/lists/[id].vue" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="ComposerSettings">
|
||||||
|
<execution />
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="CSS File" />
|
||||||
|
<option value="Vue Single File Component" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="FormatOnSaveOptions">
|
||||||
|
<option name="myRunOnSave" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="MacroExpansionManager">
|
||||||
|
<option name="directoryName" value="cvh2jllu" />
|
||||||
|
</component>
|
||||||
|
<component name="MarkdownSettingsMigration">
|
||||||
|
<option name="stateVersion" value="1" />
|
||||||
|
</component>
|
||||||
|
<component name="OptimizeOnSaveOptions">
|
||||||
|
<option name="myRunOnSave" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectId" id="2K1xoABBc3o8XBi4orGtOrB9Dxn" />
|
||||||
|
<component name="ProjectLevelVcsManager">
|
||||||
|
<ConfirmationsSetting value="1" id="Add" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent">{
|
||||||
|
"keyToString": {
|
||||||
|
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||||
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"WebServerToolWindowFactoryState": "false",
|
||||||
|
"code.cleanup.on.save": "true",
|
||||||
|
"last_opened_file_path": "/home/tiradoe/Projects/movie-night/movie-night-web/src/package.json",
|
||||||
|
"list.type.of.created.stylesheet": "CSS",
|
||||||
|
"node.js.detected.package.eslint": "true",
|
||||||
|
"node.js.detected.package.standard": "true",
|
||||||
|
"node.js.detected.package.tslint": "true",
|
||||||
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
|
"node.js.selected.package.standard": "",
|
||||||
|
"node.js.selected.package.tslint": "(autodetect)",
|
||||||
|
"nodejs_package_manager_path": "npm",
|
||||||
|
"prettierjs.PrettierConfiguration.Package": "/home/tiradoe/Projects/movie-night/movie-night-web/node_modules/prettier",
|
||||||
|
"rearrange.code.on.save": "true",
|
||||||
|
"settings.editor.selected.configurable": "settings.javascript.prettier",
|
||||||
|
"ts.external.directory.path": "/home/tiradoe/.local/share/JetBrains/Toolbox/apps/PhpStorm/ch-0/223.8214.64/plugins/javascript-impl/jsLanguageServicesImpl/external",
|
||||||
|
"vue.rearranger.settings.migration": "true"
|
||||||
|
}
|
||||||
|
}</component>
|
||||||
|
<component name="RecentsManager">
|
||||||
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$/components" />
|
||||||
|
</key>
|
||||||
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$/src/public" />
|
||||||
|
<recent name="$PROJECT_DIR$" />
|
||||||
|
<recent name="$PROJECT_DIR$/components/modal-content" />
|
||||||
|
<recent name="$PROJECT_DIR$/assets/css" />
|
||||||
|
</key>
|
||||||
|
</component>
|
||||||
|
<component name="RunManager">
|
||||||
|
<configuration name="dev" type="js.build_tools.npm" nameIsGenerated="true">
|
||||||
|
<package-json value="$PROJECT_DIR$/src/package.json" />
|
||||||
|
<command value="run" />
|
||||||
|
<scripts>
|
||||||
|
<script value="dev" />
|
||||||
|
</scripts>
|
||||||
|
<node-interpreter value="project" />
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
|
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="Default task">
|
||||||
|
<changelist id="5e320804-68c9-4504-97d5-d421de3438b2" name="Changes" comment="" />
|
||||||
|
<created>1673156065550</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1673156065550</updated>
|
||||||
|
<workItem from="1673156066556" duration="5928000" />
|
||||||
|
<workItem from="1673164832861" duration="23180000" />
|
||||||
|
<workItem from="1673308542228" duration="9499000" />
|
||||||
|
<workItem from="1673325248718" duration="9321000" />
|
||||||
|
<workItem from="1673334572599" duration="6831000" />
|
||||||
|
<workItem from="1673385927574" duration="3502000" />
|
||||||
|
<workItem from="1673397538637" duration="47416000" />
|
||||||
|
<workItem from="1673508732689" duration="1316000" />
|
||||||
|
<workItem from="1673547794038" duration="2346000" />
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
<component name="UnknownFeatures">
|
||||||
|
<option featureType="com.intellij.fileTypeFactory" implementationName=".env" />
|
||||||
|
</component>
|
||||||
|
<component name="XSLT-Support.FileAssociations.UIState">
|
||||||
|
<expand />
|
||||||
|
<select />
|
||||||
|
</component>
|
||||||
|
</project>
|
42
README.md
Normal file
42
README.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Nuxt 3 Minimal Starter
|
||||||
|
|
||||||
|
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Make sure to install the dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# yarn
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
# npm
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# pnpm
|
||||||
|
pnpm install --shamefully-hoist
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Server
|
||||||
|
|
||||||
|
Start the development server on http://localhost:3000
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production
|
||||||
|
|
||||||
|
Build the application for production:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Locally preview production build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
8
src/.idea/.gitignore
generated
vendored
Normal file
8
src/.idea/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
8
src/.idea/modules.xml
generated
Normal file
8
src/.idea/modules.xml
generated
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/movie-night-web.iml" filepath="$PROJECT_DIR$/.idea/movie-night-web.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
8
src/.idea/movie-night-web.iml
generated
Normal file
8
src/.idea/movie-night-web.iml
generated
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
18
src/.idea/php.xml
generated
Normal file
18
src/.idea/php.xml
generated
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="MessDetectorOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PHPCSFixerOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PHPCodeSnifferOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PhpStanOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PsalmOptionsConfiguration">
|
||||||
|
<option name="transferred" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
src/.idea/prettier.xml
generated
Normal file
6
src/.idea/prettier.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PrettierConfiguration">
|
||||||
|
<option name="myRunOnSave" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
2
src/.prettierignore
Normal file
2
src/.prettierignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
build
|
||||||
|
coverage
|
1
src/.prettierrc.json
Normal file
1
src/.prettierrc.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
5
src/app.vue
Normal file
5
src/app.vue
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<NuxtLayout />
|
||||||
|
</div>
|
||||||
|
</template>
|
111
src/assets/css/main.css
Normal file
111
src/assets/css/main.css
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'BloodSeeker';
|
||||||
|
src: url('assets/fonts/bloodseeker.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Xolonium';
|
||||||
|
src: url('assets/fonts/xolonium-regular.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, .list-header {
|
||||||
|
background: #080809;
|
||||||
|
color: white;
|
||||||
|
font-family: "Xolonium";
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-card {
|
||||||
|
/*background: #0a0d40;*/
|
||||||
|
background: #1b1f24;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-poster {
|
||||||
|
width: 15em;
|
||||||
|
height: 15em;
|
||||||
|
object-fit: fill;
|
||||||
|
padding: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
color: white;
|
||||||
|
border: unset;
|
||||||
|
background: #6f0b51;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background: #a80f7a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.neon-border {
|
||||||
|
border: #6f0b51 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-title {
|
||||||
|
font-size: 20pt;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-title:hover {
|
||||||
|
color: #6f0b51 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
border-bottom: #6f0b51 solid 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bloodseeker {
|
||||||
|
font-family: "BloodSeeker";
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-link:hover {
|
||||||
|
color: #6f0b51 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-link {
|
||||||
|
color: #6f0b51 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-modal {
|
||||||
|
position: fixed; /* Stay in place */
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100vw; /* Full width */
|
||||||
|
height: 100vh; /* Full height */
|
||||||
|
overflow: auto; /* Enable scroll if needed */
|
||||||
|
background: #080809;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Content/Box */
|
||||||
|
.movie-modal-content {
|
||||||
|
background: #1b1f24;
|
||||||
|
min-height: 20rem;
|
||||||
|
margin: 15% auto; /* 15% from the top and centered */
|
||||||
|
padding: 20px;
|
||||||
|
width: 80%; /* Could be more or less, depending on screen size */
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-button:hover, .logout-button:hover, .close-button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > li {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > li:hover {
|
||||||
|
color: #6f0b51 !important;
|
||||||
|
}
|
BIN
src/assets/fonts/bloodseeker.ttf
Normal file
BIN
src/assets/fonts/bloodseeker.ttf
Normal file
Binary file not shown.
BIN
src/assets/fonts/xolonium-regular.ttf
Normal file
BIN
src/assets/fonts/xolonium-regular.ttf
Normal file
Binary file not shown.
23
src/components/Modal.vue
Normal file
23
src/components/Modal.vue
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<div id="movie-modal" class="movie-modal movie-card hidden p-5">
|
||||||
|
<span class="hover-pointer font-bold w-full block text-right sm:pr-5 pb-3 pt-5" @click="closeModal()">
|
||||||
|
X
|
||||||
|
</span>
|
||||||
|
<slot class=""></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Modal",
|
||||||
|
methods: {
|
||||||
|
closeModal: function () {
|
||||||
|
document.getElementById("movie-modal").classList.add("hidden")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
92
src/components/admin/lists.vue
Normal file
92
src/components/admin/lists.vue
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div id="add-list-container" class="my-5">
|
||||||
|
<label class="text-md font-bold" for="add-list">Add List</label>
|
||||||
|
<div class="flex">
|
||||||
|
<input id="add-list" class="p-1 rounded-l" placeholder="List Title" type="text" v-on:keyup.enter="addList"/>
|
||||||
|
|
||||||
|
<button class="btn p-1 rounded-r" @click="addList">Add</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul class="grid grid-rows gap-2">
|
||||||
|
<li v-for="list in lists" class="movie-card p-3 neon-border">
|
||||||
|
<span class="mb-2">{{ list.name }}</span> <br/>
|
||||||
|
<button class="btn mt-2 p-1 rounded" type="button" @click="deleteList(list.id)">Delete</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "lists",
|
||||||
|
data: () => ({
|
||||||
|
lists: [],
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
addList: async function () {
|
||||||
|
let config = useRuntimeConfig();
|
||||||
|
const list_name = document.getElementById("add-list").value;
|
||||||
|
if (!list_name) {
|
||||||
|
alert("Please add list name.");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let list_json = await fetch(`${config.public.apiURL}/lists`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: list_name,
|
||||||
|
public: false
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
"token": useCookie("token").value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => json)
|
||||||
|
.catch(err => console.log(err))
|
||||||
|
|
||||||
|
list_json.list.movie_count = 0;
|
||||||
|
this.lists.push(list_json.list);
|
||||||
|
},
|
||||||
|
deleteList: function (list_id) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
let confirmed = confirm("Delete list?");
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(`${config.public.apiURL}/lists/${list_id}`, {
|
||||||
|
credentials: "include",
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
"token": useCookie("token").value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(_json => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getLists: function () {
|
||||||
|
const 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>
|
51
src/components/admin/search.vue
Normal file
51
src/components/admin/search.vue
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<template>
|
||||||
|
<form class="py-3 p-sm-0 align-items-center" @submit="findMovies">
|
||||||
|
<label class="px-0 " for="search-field">Search</label>
|
||||||
|
<div class="px-0 mx-0">
|
||||||
|
<input id="search-field" class="p-1" name="search-field" type="text"/>
|
||||||
|
<button class="btn p-1" type="button" @click="findMovies">Submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<ul class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 gap-4">
|
||||||
|
<li v-for="movie in movies" class="p-1 movie-card">
|
||||||
|
<img :src="movie.Poster" alt="movie poster" class="neon-border hover-pointer" @click="$parent.showModal(movie)"/>
|
||||||
|
<div class="p-2">
|
||||||
|
<h5 class="text-center">{{ movie.Title }} {{ movie.year }}</h5>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "search",
|
||||||
|
data: () => ({
|
||||||
|
movies: [],
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
findMovies: function (e) {
|
||||||
|
let config = useRuntimeConfig();
|
||||||
|
e.preventDefault();
|
||||||
|
let searchTerm = document.getElementById('search-field').value
|
||||||
|
|
||||||
|
return fetch(`${config.public.apiURL}/movies/search/${searchTerm}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
"token": useCookie("token").value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
this.movies = json;
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
86
src/components/admin/showings.vue
Normal file
86
src/components/admin/showings.vue
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li v-for="showing in showings" class="movie-card p-3 neon-border mb-2">
|
||||||
|
<ul>
|
||||||
|
<li class="pb-2">
|
||||||
|
<span class="mb-3">{{ showing.title }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="pb-2">
|
||||||
|
<span class="mb-3">{{ formatDate(showing.showtime) }} </span>
|
||||||
|
</li>
|
||||||
|
<button class="btn p-1 rounded" type="button" @click="deleteShowing(showing.id)">Delete</button>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "showings",
|
||||||
|
data: () => ({
|
||||||
|
showings: [],
|
||||||
|
months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
||||||
|
}),
|
||||||
|
mounted() {
|
||||||
|
this.getShowings()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatDate: function (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()}`
|
||||||
|
},
|
||||||
|
deleteShowing: function (showing_id) {
|
||||||
|
let config = useRuntimeConfig();
|
||||||
|
let confirmed = confirm("Delete showing?");
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(`${config.public.apiURL}/schedules/${showing_id}`, {
|
||||||
|
credentials: "include",
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
"token": useCookie("token").value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
this.showings = this.showings.filter((showing) => {
|
||||||
|
return showing.id !== showing_id
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getShowings: function (previous = false) {
|
||||||
|
let config = useRuntimeConfig();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
58
src/components/forms/ScheduleMovie.vue
Normal file
58
src/components/forms/ScheduleMovie.vue
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<form id="schedule-form" class="visually-hidden" method="post" onsubmit="return false">
|
||||||
|
<!-- SCHEDULE -->
|
||||||
|
<label class="pb-1 text-start font-bold" for="schedule-date">Date</label><br/>
|
||||||
|
<input id="schedule-input" class="rounded-l p-1" name="schedule-date"
|
||||||
|
type="date"/>
|
||||||
|
<button class="btn mt-5 sm:mt-0 p-1 rounded sm:rounded-none sm:rounded-r" type="button" @click="schedule">Schedule
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ScheduleMovie",
|
||||||
|
methods: {
|
||||||
|
schedule: function (e) {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let showtime_input = document.getElementById("schedule-input").value;
|
||||||
|
if (!showtime_input) {
|
||||||
|
alert("Please set showtime.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let showtime = showtime_input + " " + "00:00:00";
|
||||||
|
|
||||||
|
fetch(`${config.public.apiURL}/schedules/movie`, {
|
||||||
|
credentials: "include",
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
"schedule_id": 1,
|
||||||
|
"movie_id": this.movie.id,
|
||||||
|
"showtime": showtime,
|
||||||
|
"owner": 1,
|
||||||
|
"public": false
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
"token": useCookie("token").value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(_json => {
|
||||||
|
this.$parent.$parent.closeModal();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch(err => alert("Unable to schedule movie. Error:\n" + err))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: ["movie"]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
75
src/components/modal-content/AddMovie.vue
Normal file
75
src/components/modal-content/AddMovie.vue
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="movie != null" class="sm:m-5 p-10 movie-card neon-border">
|
||||||
|
<div>
|
||||||
|
<h2 id="modal-title" class="row pb-3">
|
||||||
|
{{ movie.Title }} ({{ movie.Year }})
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="grid sm:grid-cols-2">
|
||||||
|
<!-- MODAL POSTER -->
|
||||||
|
<div class="text-end">
|
||||||
|
<img id="modal-poster" :src="movie.Poster" alt="poster" class="pt-5"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-5">
|
||||||
|
<label class="" for="list-picker">Add To List</label><br/>
|
||||||
|
<select id="list-picker" v-model="list_id" class="p-1 text-black">
|
||||||
|
<option v-for="list in lists" :value="list.id">{{ list.name }}</option>
|
||||||
|
</select>
|
||||||
|
<button class="modal-poster btn p-1" type="button" @click="addMovie(movie.imdbID)">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "AddMovie",
|
||||||
|
data: () => ({
|
||||||
|
list_id: 0,
|
||||||
|
lists: {}
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
addMovie: function (imdb_id) {
|
||||||
|
let config = useRuntimeConfig()
|
||||||
|
let list = parseInt(this.list_id)
|
||||||
|
|
||||||
|
return fetch(`${config.public.apiURL}/lists/movie`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({imdb_id: imdb_id, list_id: list}),
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
"token": useCookie("token").value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(_json => {
|
||||||
|
this.$parent.closeModal()
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err))
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
props: ['movie']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
40
src/components/modal-content/ShowMovie.vue
Normal file
40
src/components/modal-content/ShowMovie.vue
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<div class="sm:m-5 p-10 movie-card neon-border">
|
||||||
|
<div>
|
||||||
|
<h2 class="text-xl pb-3 text-center sm:text-left">
|
||||||
|
{{ movie.title }} ({{ movie.year }})
|
||||||
|
</h2>
|
||||||
|
<div class="sm:inline-flex sm:space-x-5">
|
||||||
|
<img :src="movie.poster" alt="movie poster" class="mx-auto sm:mx-0 neon-border"/>
|
||||||
|
<div class="pt-5 sm:pt-0">
|
||||||
|
<p>{{ movie.plot }}</p>
|
||||||
|
<ScheduleMovie v-if="logged_in" :movie="movie" class="mt-5"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ScheduleMovie from "~/components/forms/ScheduleMovie.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ShowMovie",
|
||||||
|
data: () => ({
|
||||||
|
logged_in: false,
|
||||||
|
}),
|
||||||
|
components: {ScheduleMovie},
|
||||||
|
props: ["movie"],
|
||||||
|
mounted() {
|
||||||
|
const token = useCookie("token").value;
|
||||||
|
if (token) {
|
||||||
|
this.logged_in = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
26
src/components/navbar.vue
Normal file
26
src/components/navbar.vue
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<template>
|
||||||
|
<div class="grid grid-rows-2 text-center sm:text-left sm:grid-rows-none sm:grid-cols-2 my-5 navbar w-full">
|
||||||
|
<NuxtLink class="block" to="/admin">
|
||||||
|
<h1 class="block site-title bloodseeker">Cinema Corona</h1>
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<ul class="mt-3 sm:mt-0 justify-self-center sm:justify-self-end inline-flex space-x-5 bloodseeker leading-10">
|
||||||
|
<li>
|
||||||
|
<NuxtLink class="text-xl header-link" to="/lists">Lists</NuxtLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<NuxtLink class="text-xl header-link" to="/schedule">Schedule</NuxtLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "navbar",
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
16
src/layouts/default.vue
Normal file
16
src/layouts/default.vue
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<template>
|
||||||
|
<div class="container mx-auto">
|
||||||
|
<Navbar/>
|
||||||
|
<NuxtPage/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "default",
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
28
src/nuxt.config.ts
Normal file
28
src/nuxt.config.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
app: {
|
||||||
|
head: {
|
||||||
|
charset: 'utf-8',
|
||||||
|
viewport: "width=device-width,initial-scale=1.0",
|
||||||
|
title: "Cinema Corona",
|
||||||
|
link: [
|
||||||
|
{rel: "icon", type: "image/png", href: "/favicon.png"}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
modules: ["@nuxtjs/tailwindcss"],
|
||||||
|
css: ["@/assets/css/main.css"],
|
||||||
|
components: {
|
||||||
|
dirs: [
|
||||||
|
'~/components',
|
||||||
|
'~/components/modal-content',
|
||||||
|
'~/components/forms',
|
||||||
|
'~/components/admin',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
runtimeConfig: {
|
||||||
|
public: {
|
||||||
|
apiURL: process.env.API_URL || "http://localhost:8000/api"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
11334
src/package-lock.json
generated
Normal file
11334
src/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
18
src/package.json
Normal file
18
src/package.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "nuxt build",
|
||||||
|
"dev": "nuxt dev",
|
||||||
|
"generate": "nuxt generate",
|
||||||
|
"preview": "nuxt preview",
|
||||||
|
"postinstall": "nuxt prepare"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nuxtjs/tailwindcss": "^6.2.0",
|
||||||
|
"nuxt": "3.x",
|
||||||
|
"prettier": "3.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lazysizes": "^5.3.2"
|
||||||
|
}
|
||||||
|
}
|
88
src/pages/admin/index.vue
Normal file
88
src/pages/admin/index.vue
Normal 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
84
src/pages/index.vue
Normal 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
144
src/pages/lists/[id].vue
Normal 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
42
src/pages/lists/index.vue
Normal 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>
|
101
src/pages/schedule/index.vue
Normal file
101
src/pages/schedule/index.vue
Normal 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>
|
BIN
src/public/favicon.png
Normal file
BIN
src/public/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 648 B |
4
src/tsconfig.json
Normal file
4
src/tsconfig.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue