Merge pull request #2 from calcorum/supabase-auth

Supabase auth
This commit is contained in:
Cal Corum 2025-05-08 21:12:00 -05:00 committed by GitHub
commit 7940f817d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1044 additions and 237 deletions

View File

@ -1,20 +1,182 @@
<script setup lang="ts">
const user = useSupabaseUser()
const client = useSupabaseClient()
const router = useRouter()
const isLoggingOut = ref(false)
async function logout() {
isLoggingOut.value = true
const { error } = await client.auth.signOut()
isLoggingOut.value = false
if (error) {
console.error('Logout failed:', error)
} else {
router.push('/')
}
}
const menuOpen = ref(false)
function toggleMenu() {
menuOpen.value = !menuOpen.value
}
</script>
<template>
<nav class="container">
<ul>
<li>
<NuxtLink to="/">
<strong>Paper Dynasty</strong>
</NuxtLink>
</li>
</ul>
<ul>
<li>
<NuxtLink to='/players/random'>Random Card</NuxtLink>
</li>
<li>
<NuxtLink to='/players/69'>Card 69</NuxtLink>
</li>
<li><a href="#">Shop</a></li>
</ul>
</nav>
</template>
<nav class="container">
<!-- Left side -->
<ul>
<li>
<NuxtLink to="/">
<strong>Paper Dynasty</strong>
</NuxtLink>
</li>
</ul>
<!-- Hamburger (mobile only) -->
<button class="hamburger" @click="toggleMenu" aria-label="Toggle menu" :class="{ open: menuOpen }">
<span class="bar"></span>
<span class="bar"></span>
<span class="bar"></span>
</button>
<!-- Menu -->
<ul class="menu" :class="{ open: menuOpen }">
<li>
<NuxtLink to="/players/random">Random Cards</NuxtLink>
</li>
<li>
<NuxtLink to="/players/69">Card 69</NuxtLink>
</li>
<li>
<a href="#">Shop</a>
</li>
<li v-if="user?.id" class="user-info">
<NuxtLink to='/my-team'>
<img
:src='user.user_metadata?.avatar_url || "/default-avatar.png"'
alt="My Team"
class="profile-pic"
/>
</NuxtLink>
<button
@click="logout"
:disabled="isLoggingOut"
class="logout-btn"
>
<template v-if="isLoggingOut">
<svg class="animate-spin h-4 w-4 text-red-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"></path>
</svg>
<span>Logging out...</span>
</template>
<template v-else>
<span>Logout</span>
</template>
</button>
</li>
<li v-else>
<NuxtLink to="/login/auto">Login with Discord</NuxtLink>
</li>
</ul>
</nav>
</template>
<style scoped>
/* Basic layout */
nav.container {
display: flex;
justify-content: space-between;
align-items: center;
padding-block: 1rem;
position: relative;
}
/* Menu */
.menu {
display: flex;
list-style: none;
gap: 0.25rem;
align-items: center;
}
/* Set a fixed size for the profile image */
.profile-pic {
width: 32px; /* Shrinks to 24px */
height: 32px;
border-radius: 50%;
object-fit: cover; /* Ensures image maintains aspect ratio */
margin-right: 10px; /* Adds space between the image and other elements */
}
/* Styling for the logout button */
.logout-btn {
font-size: 0.8rem;
color: grey;
background: none;
border: none;
cursor: pointer;
}
/* Hamburger button */
.hamburger {
display: none;
flex-direction: column;
gap: 5px;
cursor: pointer;
background: none;
border: none;
padding: 0;
position: relative;
z-index: 100; /* stays above the menu */
}
.hamburger .bar {
width: 24px;
height: 3px;
background-color: currentColor;
border-radius: 2px;
transition: 0.3s ease;
}
/* Animate hamburger to X */
.hamburger.open .bar:nth-child(1) {
transform: translateY(8px) rotate(45deg);
}
.hamburger.open .bar:nth-child(2) {
opacity: 0;
}
.hamburger.open .bar:nth-child(3) {
transform: translateY(-8px) rotate(-45deg);
}
/* Mobile styles */
@media (max-width: 768px) {
.hamburger {
display: flex;
}
.menu {
display: none;
flex-direction: column;
position: absolute;
top: 100%;
right: 1rem;
background: var(--pico-background-color, #fff);
padding: 1rem;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.menu.open {
display: flex;
}
}
</style>

View File

@ -18,13 +18,12 @@
</div>
</template>
<script setup>
defineProps({
player: {
type: Object,
required: true
}
})
<script setup lang="ts">
import type { Player } from '~/types/Player';
const props = defineProps<{
player: Player
}>()
</script>
<style scoped>

View File

@ -10,9 +10,7 @@ export default defineNuxtConfig({
},
},
modules: [
// '@nuxtjs/supabase'
],
modules: ['@nuxtjs/supabase'],
css: ['@picocss/pico'],
@ -31,12 +29,17 @@ export default defineNuxtConfig({
runtimeConfig: {
public: {
supabaseKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNucGhwbnV2aGp2cXprY2J3emRrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDU4MTE3ODQsImV4cCI6MjA2MTM4Nzc4NH0.k3V9c2oiG8kufPa3_a4v6UdiGI6ML6-5lH2oifStB3I',
supabaseUrl: 'https://cnphpnuvhjvqzkcbwzdk.supabase.co/rest/v1/'
supabaseUrl: 'https://cnphpnuvhjvqzkcbwzdk.supabase.co',
supabaseRestURL: 'https://cnphpnuvhjvqzkcbwzdk.supabase.co/rest/v1'
}
},
// supabase: {
// url: 'https://cnphpnuvhjvqzkcbwzdk.supabase.co',
// key: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNucGhwbnV2aGp2cXprY2J3emRrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDU4MTE3ODQsImV4cCI6MjA2MTM4Nzc4NH0.k3V9c2oiG8kufPa3_a4v6UdiGI6ML6-5lH2oifStB3I',
// },
supabase: {
redirectOptions: {
login: '/login',
callback: '/confirm',
exclude: ['/', '/login/auto'],
saveRedirectToCookie: true,
},
},
})

274
package-lock.json generated
View File

@ -7,12 +7,15 @@
"name": "nuxt-app",
"hasInstallScript": true,
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/supabase": "^1.5.0",
"@picocss/pico": "^2.1.1",
"nuxt": "^3.17.1",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
},
"devDependencies": {
"prettier": "^3.5.3",
"supabase": "^2.22.12"
}
},
"node_modules/@ampproject/remapping": {
@ -371,15 +374,6 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/runtime": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz",
@ -2515,40 +2509,6 @@
"vue": "^3.3.4"
}
},
"node_modules/@nuxtjs/axios": {
"version": "5.13.6",
"resolved": "https://registry.npmjs.org/@nuxtjs/axios/-/axios-5.13.6.tgz",
"integrity": "sha512-XS+pOE0xsDODs1zAIbo95A0LKlilvJi8YW0NoXYuq3/jjxGgWDxizZ6Yx0AIIjZOoGsXJOPc0/BcnSEUQ2mFBA==",
"license": "MIT",
"dependencies": {
"@nuxtjs/proxy": "^2.1.0",
"axios": "^0.21.1",
"axios-retry": "^3.1.9",
"consola": "^2.15.3",
"defu": "^5.0.0"
}
},
"node_modules/@nuxtjs/axios/node_modules/consola": {
"version": "2.15.3",
"resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
"integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==",
"license": "MIT"
},
"node_modules/@nuxtjs/axios/node_modules/defu": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/defu/-/defu-5.0.1.tgz",
"integrity": "sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==",
"license": "MIT"
},
"node_modules/@nuxtjs/proxy": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@nuxtjs/proxy/-/proxy-2.1.0.tgz",
"integrity": "sha512-/qtoeqXgZ4Mg6LRg/gDUZQrFpOlOdHrol/vQYMnKu3aN3bP90UfOUB3QSDghUUK7OISAJ0xp8Ld78aHyCTcKCQ==",
"license": "MIT",
"dependencies": {
"http-proxy-middleware": "^1.0.6"
}
},
"node_modules/@nuxtjs/supabase": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@nuxtjs/supabase/-/supabase-1.5.0.tgz",
@ -3734,15 +3694,6 @@
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"license": "MIT"
},
"node_modules/@types/http-proxy": {
"version": "1.17.16",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz",
"integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "22.15.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz",
@ -4625,25 +4576,6 @@
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/axios-retry": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.9.1.tgz",
"integrity": "sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.15.4",
"is-retry-allowed": "^2.2.0"
}
},
"node_modules/b4a": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
@ -4683,6 +4615,23 @@
],
"license": "MIT"
},
"node_modules/bin-links": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/bin-links/-/bin-links-5.0.0.tgz",
"integrity": "sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==",
"dev": true,
"license": "ISC",
"dependencies": {
"cmd-shim": "^7.0.0",
"npm-normalize-package-bin": "^4.0.0",
"proc-log": "^5.0.0",
"read-cmd-shim": "^5.0.0",
"write-file-atomic": "^6.0.0"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@ -5075,6 +5024,16 @@
"node": ">=0.10.0"
}
},
"node_modules/cmd-shim": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-7.0.0.tgz",
"integrity": "sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
@ -6264,12 +6223,6 @@
"node": ">=6"
}
},
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"license": "MIT"
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@ -6530,26 +6483,6 @@
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
"license": "MIT"
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
@ -7073,48 +7006,6 @@
"node": ">= 0.8"
}
},
"node_modules/http-proxy": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"license": "MIT",
"dependencies": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
"requires-port": "^1.0.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/http-proxy-middleware": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz",
"integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==",
"license": "MIT",
"dependencies": {
"@types/http-proxy": "^1.17.5",
"http-proxy": "^1.18.1",
"is-glob": "^4.0.1",
"is-plain-obj": "^3.0.0",
"micromatch": "^4.0.2"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/http-proxy-middleware/node_modules/is-plain-obj": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
"integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/http-shutdown": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz",
@ -7441,18 +7332,6 @@
"@types/estree": "*"
}
},
"node_modules/is-retry-allowed": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz",
"integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-ssh": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.1.tgz",
@ -8518,6 +8397,16 @@
"node": ">=0.10.0"
}
},
"node_modules/npm-normalize-package-bin": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz",
"integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/npm-run-path": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
@ -9691,6 +9580,22 @@
"node": "^14.14.0 || >=16.0.0"
}
},
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pretty-bytes": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
@ -9703,6 +9608,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/proc-log": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
"integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -9838,6 +9753,16 @@
"destr": "^2.0.3"
}
},
"node_modules/read-cmd-shim": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-5.0.0.tgz",
"integrity": "sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/read-package-up": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
@ -9978,12 +9903,6 @@
"integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==",
"license": "MIT"
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"license": "MIT"
},
"node_modules/resolve": {
"version": "2.0.0-next.5",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
@ -10785,6 +10704,45 @@
"node": ">=4"
}
},
"node_modules/supabase": {
"version": "2.22.12",
"resolved": "https://registry.npmjs.org/supabase/-/supabase-2.22.12.tgz",
"integrity": "sha512-PWQT+uzwAXcamM/FK60CaWRjVwsX2SGW5vF7edbiTQC6vsNvTBnSIvd1yiXsIpq32uzQFu+iOrayxaTQytNiTw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bin-links": "^5.0.0",
"https-proxy-agent": "^7.0.2",
"node-fetch": "^3.3.2",
"tar": "7.4.3"
},
"bin": {
"supabase": "bin/supabase"
},
"engines": {
"npm": ">=8"
}
},
"node_modules/supabase/node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"dev": true,
"license": "MIT",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/superjson": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz",

View File

@ -7,12 +7,18 @@
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
"postinstall": "nuxt prepare",
"gen:types": "npx supabase gen types typescript --project-id cnphpnuvhjvqzkcbwzdk > types/supabase.d.ts && prettier --write types/supabase.d.ts"
},
"dependencies": {
"@nuxtjs/supabase": "^1.5.0",
"@picocss/pico": "^2.1.1",
"nuxt": "^3.17.1",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
},
"devDependencies": {
"prettier": "^3.5.3",
"supabase": "^2.22.12"
}
}

43
pages/confirm.vue Normal file
View File

@ -0,0 +1,43 @@
<script setup lang="ts">
import type { Database } from '~/types/supabase'
const user = useSupabaseUser()
const client = useSupabaseClient<Database>()
const redirectInfo = useSupabaseCookieRedirect()
watch(user, async (newUser) => {
if (newUser) {
console.log('Logged-in user: ', newUser)
// Update profile with Discord username and avatar
const { error } = await client.from('profiles')
.upsert({
id: newUser.id,
discord_username: newUser.user_metadata?.full_name || 'Unknown',
avatar_url: newUser.user_metadata?.avatar_url || '',
discord_id: newUser.user_metadata?.provider_id || '',
})
.eq('id', newUser.id)
if (error) {
console.error('Error updating profile with Discord info:', error)
}
else {
console.log('Profile updated with Discord username and avatar')
}
}
if (user.value) {
// Get redirect path, and clear it from the cookie
let path = redirectInfo.pluck()
// Redirect to the saved path, or fallback to home
if (path?.includes('login')) path = '/'
return navigateTo(path || '/')
}},
{ immediate: true }
)
</script>
<template>
<div>Waiting for login...</div>
</template>

View File

@ -0,0 +1,33 @@
<script setup lang="ts">
const supabase = useSupabaseClient()
const client = useSupabaseClient()
onMounted(async () => {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'discord',
options: {
redirectTo: 'http://localhost:3000/confirm'
}
})
if (error) {
console.error('OAuth login failed:', error)
}
})
</script>
<template>
<div class="flex justify-center items-center min-h-screen">
<Transition name="fade">
<p class="text-lg">Redirecting to Discord...</p>
</Transition>
</div>
</template>
<style scoped>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>

22
pages/login/index.vue Normal file
View File

@ -0,0 +1,22 @@
<script setup lang="ts">
const supabase = useSupabaseClient()
const email = ref('')
const signInWithDiscord = async () => {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'discord',
options: {
redirectTo: 'http://localhost:3000/confirm'
}
})
if (error) console.log('OAuth login failed:', error)
}
</script>
<template>
<div>
<button @click="signInWithDiscord">
Login with Discord
</button>
</div>
</template>

View File

@ -2,27 +2,30 @@
import type { Player } from '~/types/Player'
const route = useRoute()
const config = useRuntimeConfig()
const playerId = route.params.id
const { data, pending, error } = await useFetch<Player[]>(
`${config.public.supabaseUrl}/players?select=*,rarity!inner(*)&id=eq.${playerId}`,
const user = useSupabaseUser()
const client = useSupabaseClient()
console.log('current user:', user)
const { data: player, pending, error } = await useAsyncData<Player | null>(
'players',
async () => {
const { data } = await client.from('players')
.select('*,rarity!inner(*)')
.eq('id', playerId)
.single()
console.log(`player:`, data)
return data
},
{
method: 'GET',
headers: {
apikey: config.public.supabaseKey
},
lazy: true,
server: false
}
)
const player = computed(() => data.value?.[0] ?? null)
console.log('data:')
console.log(data)
console.log('player: ')
console.log(player)
</script>
<template>
@ -30,6 +33,7 @@
<div>
<div v-if="pending">Loading...</div>
<div v-else-if="error">Failed to load player</div>
<div v-else-if="!player">No playler found</div>
<div v-else>
<Player :player="player" />
</div>

View File

@ -1,33 +1,30 @@
<script setup lang='ts'>
import type { Player } from '~/types/Player'
const config = useRuntimeConfig()
// const config = useRuntimeConfig()
const user = useSupabaseUser()
const client = useSupabaseClient()
const { data, pending, error } = await useFetch<Player[]>(
`${config.public.supabaseUrl}/random_player`,
console.log('current user:', user)
const { data: playerList, pending, error } = await useAsyncData<Player[] | null>(
'random_player',
async () => {
const { data } = await client.from('random_player')
.select('*,rarity!inner(*),mlb_player!inner(*)')
.eq('franchise', 'Baltimore Orioles')
.eq('mlb_player.offense_col', 1)
.limit(3)
console.log('players from supabase: ', data)
return data
},
{
method: 'GET',
headers: {
apikey: config.public.supabaseKey
},
server: false,
query: {
select: '*,rarity!inner(*),mlb_player!inner(*)',
// 'rarity.name': 'eq.All-Star',
// 'mlb_player.offense_col': 'eq.1',
franchise: 'eq.Baltimore Orioles',
limit: 3
}
lazy: true,
server: false
}
)
// const player = computed(() => data.value?.[0] ?? null)
const players = computed(() => data.value ?? [])
console.log('data:')
console.log(data)
console.log('player: ')
console.log(players)
</script>
<template>
@ -35,8 +32,9 @@
<div >
<div v-if="pending">Loading...</div>
<div v-else-if="error">Failed to load player</div>
<div v-else-if="playerList?.length == 0">No players found</div>
<div v-else class="grid">
<div v-for="player in players" :key="player.id">
<div v-for="player in playerList" :key="player.id">
<Player :player="player" />
</div>
</div>

14
types/Player.d.ts vendored
View File

@ -1,3 +1,10 @@
export interface Rarity {
id: number
name: string
color: string
value: number
}
export interface Player {
id: number
name: string
@ -6,10 +13,5 @@ export interface Player {
headshot: string
description: string
franchise: string
rarity: {
id: number
name: string
color: string
value: number
}
rarity: Rarity
}

577
types/supabase.d.ts vendored Normal file
View File

@ -0,0 +1,577 @@
export type Json =
| string
| number
| boolean
| null
| { [key: string]: Json | undefined }
| Json[];
export type Database = {
public: {
Tables: {
cardsets: {
Row: {
created_at: string;
description: string | null;
for_purchase: boolean;
id: number;
in_packs: boolean;
name: string;
ranked_legal: boolean;
total_cards: number | null;
};
Insert: {
created_at?: string;
description?: string | null;
for_purchase?: boolean;
id?: number;
in_packs?: boolean;
name: string;
ranked_legal?: boolean;
total_cards?: number | null;
};
Update: {
created_at?: string;
description?: string | null;
for_purchase?: boolean;
id?: number;
in_packs?: boolean;
name?: string;
ranked_legal?: boolean;
total_cards?: number | null;
};
Relationships: [];
};
mlb_player: {
Row: {
first_name: string | null;
id: number;
key_bbref: string | null;
key_fangraphs: number | null;
key_mlbam: number | null;
key_retro: string | null;
last_name: string | null;
offense_col: number | null;
};
Insert: {
first_name?: string | null;
id?: number;
key_bbref?: string | null;
key_fangraphs?: number | null;
key_mlbam?: number | null;
key_retro?: string | null;
last_name?: string | null;
offense_col?: number | null;
};
Update: {
first_name?: string | null;
id?: number;
key_bbref?: string | null;
key_fangraphs?: number | null;
key_mlbam?: number | null;
key_retro?: string | null;
last_name?: string | null;
offense_col?: number | null;
};
Relationships: [];
};
players: {
Row: {
bbref_id: string | null;
cardset_id: number;
cost: number;
created_at: string;
description: string;
fangr_id: string | null;
franchise: Database["public"]["Enums"]["franchise"] | null;
headshot: string | null;
id: number;
image: string;
image2: string | null;
mlbclub: Database["public"]["Enums"]["franchise"] | null;
mlbplayer_id: number | null;
name: string;
positions: string[] | null;
quantity: number;
rarity_id: number;
set_num: number | null;
strat_code: string | null;
vanity_card: string | null;
};
Insert: {
bbref_id?: string | null;
cardset_id: number;
cost: number;
created_at?: string;
description: string;
fangr_id?: string | null;
franchise?: Database["public"]["Enums"]["franchise"] | null;
headshot?: string | null;
id?: number;
image: string;
image2?: string | null;
mlbclub?: Database["public"]["Enums"]["franchise"] | null;
mlbplayer_id?: number | null;
name: string;
positions?: string[] | null;
quantity?: number;
rarity_id: number;
set_num?: number | null;
strat_code?: string | null;
vanity_card?: string | null;
};
Update: {
bbref_id?: string | null;
cardset_id?: number;
cost?: number;
created_at?: string;
description?: string;
fangr_id?: string | null;
franchise?: Database["public"]["Enums"]["franchise"] | null;
headshot?: string | null;
id?: number;
image?: string;
image2?: string | null;
mlbclub?: Database["public"]["Enums"]["franchise"] | null;
mlbplayer_id?: number | null;
name?: string;
positions?: string[] | null;
quantity?: number;
rarity_id?: number;
set_num?: number | null;
strat_code?: string | null;
vanity_card?: string | null;
};
Relationships: [
{
foreignKeyName: "players_cardset_id_fkey";
columns: ["cardset_id"];
isOneToOne: false;
referencedRelation: "cardsets";
referencedColumns: ["id"];
},
{
foreignKeyName: "players_mlbplayer_id_fkey";
columns: ["mlbplayer_id"];
isOneToOne: false;
referencedRelation: "mlb_player";
referencedColumns: ["id"];
},
{
foreignKeyName: "players_rarity_id_fkey";
columns: ["rarity_id"];
isOneToOne: false;
referencedRelation: "rarity";
referencedColumns: ["id"];
},
];
};
profiles: {
Row: {
avatar_url: string | null;
created_at: string | null;
discord_id: string | null;
discord_username: string | null;
email: string | null;
id: string;
};
Insert: {
avatar_url?: string | null;
created_at?: string | null;
discord_id?: string | null;
discord_username?: string | null;
email?: string | null;
id: string;
};
Update: {
avatar_url?: string | null;
created_at?: string | null;
discord_id?: string | null;
discord_username?: string | null;
email?: string | null;
id?: string;
};
Relationships: [];
};
rarity: {
Row: {
color: string;
created_at: string;
id: number;
name: string;
value: number;
};
Insert: {
color: string;
created_at?: string;
id?: number;
name: string;
value: number;
};
Update: {
color?: string;
created_at?: string;
id?: number;
name?: string;
value?: number;
};
Relationships: [];
};
teams: {
Row: {
abbrev: string | null;
career: string | null;
collection_value: string | null;
color: string | null;
event_id: string | null;
gmid: number | null;
gmname: string | null;
gsheet: string | null;
has_guide: string | null;
id: number;
is_ai: number | null;
lname: string | null;
logo: string | null;
ranking: number | null;
season: number | null;
sname: string | null;
team_value: string | null;
wallet: number | null;
};
Insert: {
abbrev?: string | null;
career?: string | null;
collection_value?: string | null;
color?: string | null;
event_id?: string | null;
gmid?: number | null;
gmname?: string | null;
gsheet?: string | null;
has_guide?: string | null;
id: number;
is_ai?: number | null;
lname?: string | null;
logo?: string | null;
ranking?: number | null;
season?: number | null;
sname?: string | null;
team_value?: string | null;
wallet?: number | null;
};
Update: {
abbrev?: string | null;
career?: string | null;
collection_value?: string | null;
color?: string | null;
event_id?: string | null;
gmid?: number | null;
gmname?: string | null;
gsheet?: string | null;
has_guide?: string | null;
id?: number;
is_ai?: number | null;
lname?: string | null;
logo?: string | null;
ranking?: number | null;
season?: number | null;
sname?: string | null;
team_value?: string | null;
wallet?: number | null;
};
Relationships: [];
};
};
Views: {
random_player: {
Row: {
bbref_id: string | null;
cardset_id: number | null;
cost: number | null;
created_at: string | null;
description: string | null;
fangr_id: string | null;
franchise: Database["public"]["Enums"]["franchise"] | null;
headshot: string | null;
id: number | null;
image: string | null;
image2: string | null;
mlbclub: Database["public"]["Enums"]["franchise"] | null;
mlbplayer_id: number | null;
name: string | null;
positions: string[] | null;
quantity: number | null;
rarity_id: number | null;
set_num: number | null;
strat_code: string | null;
vanity_card: string | null;
};
Insert: {
bbref_id?: string | null;
cardset_id?: number | null;
cost?: number | null;
created_at?: string | null;
description?: string | null;
fangr_id?: string | null;
franchise?: Database["public"]["Enums"]["franchise"] | null;
headshot?: string | null;
id?: number | null;
image?: string | null;
image2?: string | null;
mlbclub?: Database["public"]["Enums"]["franchise"] | null;
mlbplayer_id?: number | null;
name?: string | null;
positions?: string[] | null;
quantity?: number | null;
rarity_id?: number | null;
set_num?: number | null;
strat_code?: string | null;
vanity_card?: string | null;
};
Update: {
bbref_id?: string | null;
cardset_id?: number | null;
cost?: number | null;
created_at?: string | null;
description?: string | null;
fangr_id?: string | null;
franchise?: Database["public"]["Enums"]["franchise"] | null;
headshot?: string | null;
id?: number | null;
image?: string | null;
image2?: string | null;
mlbclub?: Database["public"]["Enums"]["franchise"] | null;
mlbplayer_id?: number | null;
name?: string | null;
positions?: string[] | null;
quantity?: number | null;
rarity_id?: number | null;
set_num?: number | null;
strat_code?: string | null;
vanity_card?: string | null;
};
Relationships: [
{
foreignKeyName: "players_cardset_id_fkey";
columns: ["cardset_id"];
isOneToOne: false;
referencedRelation: "cardsets";
referencedColumns: ["id"];
},
{
foreignKeyName: "players_mlbplayer_id_fkey";
columns: ["mlbplayer_id"];
isOneToOne: false;
referencedRelation: "mlb_player";
referencedColumns: ["id"];
},
{
foreignKeyName: "players_rarity_id_fkey";
columns: ["rarity_id"];
isOneToOne: false;
referencedRelation: "rarity";
referencedColumns: ["id"];
},
];
};
};
Functions: {
[_ in never]: never;
};
Enums: {
franchise:
| "Arizona Diamondbacks"
| "Atlanta Braves"
| "Baltimore Orioles"
| "Boston Red Sox"
| "Chicago Cubs"
| "Chicago White Sox"
| "Cincinnati Reds"
| "Cleveland Guardians"
| "Colorado Rockies"
| "Detroit Tigers"
| "Houston Astros"
| "Kansas City Royals"
| "Los Angeles Angels"
| "Los Angeles Dodgers"
| "Miami Marlins"
| "Milwaukee Brewers"
| "Minnesota Twins"
| "New York Mets"
| "New York Yankees"
| "Oakland Athletics"
| "Philadelphia Phillies"
| "Pittsburgh Pirates"
| "San Diego Padres"
| "San Francisco Giants"
| "Seattle Mariners"
| "St Louis Cardinals"
| "Tampa Bay Rays"
| "Texas Rangers"
| "Toronto Blue Jays"
| "Washington Nationals"
| "Anaheim Angels"
| "Brilliant Stars"
| "Cleveland Indians"
| "Custom Ballplayers"
| "Junior All Stars"
| "Mario Super Sluggers"
| "Montreal Expos"
| "Tampa Bay Devil Rays";
};
CompositeTypes: {
[_ in never]: never;
};
};
};
type DefaultSchema = Database[Extract<keyof Database, "public">];
export type Tables<
DefaultSchemaTableNameOrOptions extends
| keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
| { schema: keyof Database },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof Database;
}
? keyof (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"])
: never = never,
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
? (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends {
Row: infer R;
}
? R
: never
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] &
DefaultSchema["Views"])
? (DefaultSchema["Tables"] &
DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
Row: infer R;
}
? R
: never
: never;
export type TablesInsert<
DefaultSchemaTableNameOrOptions extends
| keyof DefaultSchema["Tables"]
| { schema: keyof Database },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof Database;
}
? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
: never = never,
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
Insert: infer I;
}
? I
: never
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
Insert: infer I;
}
? I
: never
: never;
export type TablesUpdate<
DefaultSchemaTableNameOrOptions extends
| keyof DefaultSchema["Tables"]
| { schema: keyof Database },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof Database;
}
? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
: never = never,
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
Update: infer U;
}
? U
: never
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
Update: infer U;
}
? U
: never
: never;
export type Enums<
DefaultSchemaEnumNameOrOptions extends
| keyof DefaultSchema["Enums"]
| { schema: keyof Database },
EnumName extends DefaultSchemaEnumNameOrOptions extends {
schema: keyof Database;
}
? keyof Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"]
: never = never,
> = DefaultSchemaEnumNameOrOptions extends { schema: keyof Database }
? Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName]
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"]
? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
: never;
export type CompositeTypes<
PublicCompositeTypeNameOrOptions extends
| keyof DefaultSchema["CompositeTypes"]
| { schema: keyof Database },
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
schema: keyof Database;
}
? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
: never = never,
> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database }
? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
: never;
export const Constants = {
public: {
Enums: {
franchise: [
"Arizona Diamondbacks",
"Atlanta Braves",
"Baltimore Orioles",
"Boston Red Sox",
"Chicago Cubs",
"Chicago White Sox",
"Cincinnati Reds",
"Cleveland Guardians",
"Colorado Rockies",
"Detroit Tigers",
"Houston Astros",
"Kansas City Royals",
"Los Angeles Angels",
"Los Angeles Dodgers",
"Miami Marlins",
"Milwaukee Brewers",
"Minnesota Twins",
"New York Mets",
"New York Yankees",
"Oakland Athletics",
"Philadelphia Phillies",
"Pittsburgh Pirates",
"San Diego Padres",
"San Francisco Giants",
"Seattle Mariners",
"St Louis Cardinals",
"Tampa Bay Rays",
"Texas Rangers",
"Toronto Blue Jays",
"Washington Nationals",
"Anaheim Angels",
"Brilliant Stars",
"Cleveland Indians",
"Custom Ballplayers",
"Junior All Stars",
"Mario Super Sluggers",
"Montreal Expos",
"Tampa Bay Devil Rays",
],
},
},
} as const;