diff --git a/src/assets/css/main.css b/src/assets/css/main.css index dc9639a..e1f4f52 100644 --- a/src/assets/css/main.css +++ b/src/assets/css/main.css @@ -94,6 +94,12 @@ input { width: 80%; /* Could be more or less, depending on screen size */ } +.page-header { + font-size: 1.5rem; + line-height: calc(2 / 1.5); + padding-bottom: 1rem; +} + .hover-pointer { cursor: pointer; } diff --git a/src/components/ProfileMenu.vue b/src/components/ProfileMenu.vue new file mode 100644 index 0000000..ac69e59 --- /dev/null +++ b/src/components/ProfileMenu.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/src/components/common/navigation/navbar.vue b/src/components/common/navigation/navbar.vue new file mode 100644 index 0000000..ff470d0 --- /dev/null +++ b/src/components/common/navigation/navbar.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/src/components/common/ui/FormButton.vue b/src/components/common/ui/FormButton.vue new file mode 100644 index 0000000..853caed --- /dev/null +++ b/src/components/common/ui/FormButton.vue @@ -0,0 +1,9 @@ + + + + + diff --git a/src/components/Modal.vue b/src/components/common/ui/Modal.vue similarity index 100% rename from src/components/Modal.vue rename to src/components/common/ui/Modal.vue diff --git a/src/components/forms/PasswordReset.vue b/src/components/forms/PasswordReset.vue new file mode 100644 index 0000000..1e8ff9d --- /dev/null +++ b/src/components/forms/PasswordReset.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/src/components/navbar.vue b/src/components/navbar.vue deleted file mode 100644 index 9d1af19..0000000 --- a/src/components/navbar.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/composables/hasToken.ts b/src/composables/hasToken.ts new file mode 100644 index 0000000..7b8d7c2 --- /dev/null +++ b/src/composables/hasToken.ts @@ -0,0 +1,6 @@ +import { useCookie } from "#app"; + +export function hasToken() { + const token = useCookie("token").value; + return token !== null && token !== undefined && token !== ""; +} diff --git a/src/composables/logout.ts b/src/composables/logout.ts new file mode 100644 index 0000000..6349738 --- /dev/null +++ b/src/composables/logout.ts @@ -0,0 +1,19 @@ +import { useCookie } from "#app"; + +export function logout() { + let config = useRuntimeConfig(); + fetch(`${config.public.apiURL}/auth/logout/`, { + method: "POST", + headers: { + "Content-type": "application/json", + Authorization: `Token ${useCookie("token").value}`, + }, + }) + .then((response) => response) + .then((_json) => { + let token = useCookie("token"); + token.value = null; + navigateTo("/"); + }) + .catch((err) => console.log(err)); +} diff --git a/src/layouts/default.vue b/src/layouts/default.vue index f2cccac..cf1d587 100644 --- a/src/layouts/default.vue +++ b/src/layouts/default.vue @@ -6,8 +6,11 @@ diff --git a/src/nuxt.config.ts b/src/nuxt.config.ts index 17d32de..0f767ca 100644 --- a/src/nuxt.config.ts +++ b/src/nuxt.config.ts @@ -9,7 +9,12 @@ export default defineNuxtConfig({ }, }, - modules: ["@nuxtjs/tailwindcss"], + modules: ["@nuxtjs/tailwindcss", "@vesp/nuxt-fontawesome"], + fontawesome: { + icons: { + solid: ["user"], + }, + }, css: ["@/assets/css/main.css"], compatibilityDate: "2025-04-05", diff --git a/src/package-lock.json b/src/package-lock.json index 4dbf0c1..d4f7f39 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -9,8 +9,12 @@ "lazysizes": "^5.3.2" }, "devDependencies": { + "@fortawesome/free-brands-svg-icons": "^6.7.2", + "@fortawesome/free-regular-svg-icons": "^6.7.2", + "@fortawesome/free-solid-svg-icons": "^6.7.2", "@nuxtjs/tailwindcss": "^6.2.0", "@types/node": "^22.14.0", + "@vesp/nuxt-fontawesome": "^1.2.1", "nuxt": "3.x", "prettier": "3.x", "typescript": "^5.8.3", @@ -971,6 +975,68 @@ "node": ">=18" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", + "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", + "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.2.tgz", + "integrity": "sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==", + "dev": true, + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz", + "integrity": "sha512-7Z/ur0gvCMW8G93dXIQOkQqHo2M5HLhYrRVC0//fakJXxcF1VmMPsxnG6Ee8qEylA8b8Q3peQXWMNZ62lYF28g==", + "dev": true, + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz", + "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==", + "dev": true, + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@ioredis/commands": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", @@ -2827,6 +2893,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@vesp/nuxt-fontawesome": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vesp/nuxt-fontawesome/-/nuxt-fontawesome-1.2.1.tgz", + "integrity": "sha512-W7gaCQ8szFmOsMwBcxq22vyAV7wARQ8TK5wsd1we8Gt3KPFVQHj9ZYi738b4ePoeFxYGBEndh/uMLY6sIc+9HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.5.1", + "@nuxt/kit": "^3.13.0" + } + }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz", diff --git a/src/package.json b/src/package.json index 109baeb..925eb83 100644 --- a/src/package.json +++ b/src/package.json @@ -8,8 +8,12 @@ "postinstall": "nuxt prepare" }, "devDependencies": { + "@fortawesome/free-brands-svg-icons": "^6.7.2", + "@fortawesome/free-regular-svg-icons": "^6.7.2", + "@fortawesome/free-solid-svg-icons": "^6.7.2", "@nuxtjs/tailwindcss": "^6.2.0", "@types/node": "^22.14.0", + "@vesp/nuxt-fontawesome": "^1.2.1", "nuxt": "3.x", "prettier": "3.x", "typescript": "^5.8.3", diff --git a/src/pages/admin/index.vue b/src/pages/admin/index.vue index ae83bc0..8672afc 100644 --- a/src/pages/admin/index.vue +++ b/src/pages/admin/index.vue @@ -54,9 +54,9 @@ 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"; -import { useCookie } from "#app"; import type { Movie } from "~/types/movie"; -import Modal from "~/components/Modal.vue"; +import Modal from "~/components/common/ui/Modal.vue"; +import { logout } from "~/composables/logout"; const modal_movie = defineModel("#movie-modal"); @@ -87,30 +87,6 @@ const toggleDisplay = function (element_id: string) { } }); }; -const logout = () => { - let config = useRuntimeConfig(); - fetch(`${config.public.apiURL}/auth/logout/`, { - method: "POST", - headers: { - "Content-type": "application/json", - Authorization: `Token ${useCookie("token").value}`, - }, - }) - .then((response) => response) - .then((_json) => { - let token = useCookie("token"); - token.value = null; - navigateTo("/"); - }) - .catch((err) => console.log(err)); -}; - -onMounted(() => { - const token = useCookie("token").value; - if (!token) { - navigateTo("/"); - } -}); diff --git a/src/pages/lists/[id].vue b/src/pages/lists/[id].vue index 0d3d2ff..270ebc6 100644 --- a/src/pages/lists/[id].vue +++ b/src/pages/lists/[id].vue @@ -78,7 +78,7 @@ import ShowMovie from "~/components/modal-content/ShowMovie.vue"; import "lazysizes"; import type { MovieList } from "~/types/movielist"; import type { Movie } from "~/types/movie"; -import Modal from "~/components/Modal.vue"; +import Modal from "~/components/common/ui/Modal.vue"; import { useCookie } from "#app"; import { $fetch } from "ofetch"; import MoviePoster from "~/components/MoviePoster.vue"; diff --git a/src/pages/user/profile.vue b/src/pages/user/profile.vue new file mode 100644 index 0000000..b73974d --- /dev/null +++ b/src/pages/user/profile.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/src/pages/user/settings.vue b/src/pages/user/settings.vue new file mode 100644 index 0000000..6357aa7 --- /dev/null +++ b/src/pages/user/settings.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/src/types/userProfile.ts b/src/types/userProfile.ts new file mode 100644 index 0000000..887835b --- /dev/null +++ b/src/types/userProfile.ts @@ -0,0 +1,8 @@ +import type { MovieList } from "~/types/movielist"; + +export type UserProfile = { + name: string; + username: string; + date_joined: string; + lists: MovieList[]; +};