first commit
This commit is contained in:
143
frontend/src/components/Footer.vue
Normal file
143
frontend/src/components/Footer.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<script setup>
|
||||
import { Github, Twitter, Mail, MessageCircle } from 'lucide-vue-next'
|
||||
|
||||
const currentYear = new Date().getFullYear()
|
||||
|
||||
const footerLinks = {
|
||||
marketplace: [
|
||||
{ name: 'Browse Market', path: '/market' },
|
||||
{ name: 'Sell Items', path: '/sell' },
|
||||
{ name: 'Recent Sales', path: '/market?tab=recent' },
|
||||
{ name: 'Featured Items', path: '/market?tab=featured' },
|
||||
],
|
||||
support: [
|
||||
{ name: 'FAQ', path: '/faq' },
|
||||
{ name: 'Support Center', path: '/support' },
|
||||
{ name: 'Contact Us', path: '/support' },
|
||||
{ name: 'Report Issue', path: '/support' },
|
||||
],
|
||||
legal: [
|
||||
{ name: 'Terms of Service', path: '/terms' },
|
||||
{ name: 'Privacy Policy', path: '/privacy' },
|
||||
{ name: 'Refund Policy', path: '/terms#refunds' },
|
||||
{ name: 'Cookie Policy', path: '/privacy#cookies' },
|
||||
],
|
||||
}
|
||||
|
||||
const socialLinks = [
|
||||
{ name: 'Discord', icon: MessageCircle, url: '#' },
|
||||
{ name: 'Twitter', icon: Twitter, url: '#' },
|
||||
{ name: 'GitHub', icon: Github, url: '#' },
|
||||
{ name: 'Email', icon: Mail, url: 'mailto:support@turbotrades.com' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer class="bg-surface-dark border-t border-surface-lighter mt-auto">
|
||||
<div class="container-custom py-12">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-8">
|
||||
<!-- Brand Column -->
|
||||
<div class="lg:col-span-2">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<div class="w-10 h-10 bg-gradient-to-br from-primary-500 to-primary-700 rounded-lg flex items-center justify-center">
|
||||
<span class="text-white font-bold">TT</span>
|
||||
</div>
|
||||
<span class="text-xl font-display font-bold text-white">TurboTrades</span>
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm mb-6 max-w-md">
|
||||
The premier marketplace for CS2 and Rust skins. Buy, sell, and trade with confidence. Fast transactions, secure trading, and competitive prices.
|
||||
</p>
|
||||
|
||||
<!-- Social Links -->
|
||||
<div class="flex items-center gap-3">
|
||||
<a
|
||||
v-for="social in socialLinks"
|
||||
:key="social.name"
|
||||
:href="social.url"
|
||||
:aria-label="social.name"
|
||||
class="w-10 h-10 flex items-center justify-center rounded-lg bg-surface-light hover:bg-surface-lighter border border-surface-lighter hover:border-primary-500/50 text-gray-400 hover:text-primary-500 transition-all"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<component :is="social.icon" class="w-5 h-5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Marketplace Links -->
|
||||
<div>
|
||||
<h3 class="text-white font-semibold mb-4">Marketplace</h3>
|
||||
<ul class="space-y-2">
|
||||
<li v-for="link in footerLinks.marketplace" :key="link.path">
|
||||
<router-link
|
||||
:to="link.path"
|
||||
class="text-gray-400 hover:text-primary-500 text-sm transition-colors"
|
||||
>
|
||||
{{ link.name }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Support Links -->
|
||||
<div>
|
||||
<h3 class="text-white font-semibold mb-4">Support</h3>
|
||||
<ul class="space-y-2">
|
||||
<li v-for="link in footerLinks.support" :key="link.path">
|
||||
<router-link
|
||||
:to="link.path"
|
||||
class="text-gray-400 hover:text-primary-500 text-sm transition-colors"
|
||||
>
|
||||
{{ link.name }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Legal Links -->
|
||||
<div>
|
||||
<h3 class="text-white font-semibold mb-4">Legal</h3>
|
||||
<ul class="space-y-2">
|
||||
<li v-for="link in footerLinks.legal" :key="link.path">
|
||||
<router-link
|
||||
:to="link.path"
|
||||
class="text-gray-400 hover:text-primary-500 text-sm transition-colors"
|
||||
>
|
||||
{{ link.name }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Bar -->
|
||||
<div class="mt-12 pt-8 border-t border-surface-lighter">
|
||||
<div class="flex flex-col md:flex-row items-center justify-between gap-4">
|
||||
<p class="text-gray-500 text-sm">
|
||||
© {{ currentYear }} TurboTrades. All rights reserved.
|
||||
</p>
|
||||
|
||||
<div class="flex items-center gap-6">
|
||||
<span class="text-gray-500 text-sm">
|
||||
Made with ❤️ for the gaming community
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Disclaimer -->
|
||||
<div class="mt-4 text-center md:text-left">
|
||||
<p class="text-gray-600 text-xs">
|
||||
TurboTrades is not affiliated with Valve Corporation or Facepunch Studios.
|
||||
CS2, Counter-Strike, and Rust are trademarks of their respective owners.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
footer {
|
||||
background: linear-gradient(180deg, rgba(15, 25, 35, 0.8) 0%, rgba(10, 15, 20, 0.95) 100%);
|
||||
}
|
||||
</style>
|
||||
323
frontend/src/components/NavBar.vue
Normal file
323
frontend/src/components/NavBar.vue
Normal file
@@ -0,0 +1,323 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import {
|
||||
Menu,
|
||||
User,
|
||||
LogOut,
|
||||
Settings,
|
||||
Package,
|
||||
CreditCard,
|
||||
History,
|
||||
ShoppingCart,
|
||||
Wallet,
|
||||
TrendingUp,
|
||||
Shield,
|
||||
X,
|
||||
ChevronDown,
|
||||
Plus,
|
||||
} from "lucide-vue-next";
|
||||
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const showMobileMenu = ref(false);
|
||||
const showUserMenu = ref(false);
|
||||
const showBalanceMenu = ref(false);
|
||||
const userMenuRef = ref(null);
|
||||
const balanceMenuRef = ref(null);
|
||||
|
||||
const navigationLinks = [
|
||||
{ name: "Market", path: "/market", icon: ShoppingCart },
|
||||
{ name: "Sell", path: "/sell", icon: TrendingUp, requiresAuth: true },
|
||||
{ name: "FAQ", path: "/faq", icon: null },
|
||||
];
|
||||
|
||||
const userMenuLinks = computed(() => [
|
||||
{ name: "Profile", path: "/profile", icon: User },
|
||||
{ name: "Inventory", path: "/inventory", icon: Package },
|
||||
{ name: "Transactions", path: "/transactions", icon: History },
|
||||
{ name: "Withdraw", path: "/withdraw", icon: CreditCard },
|
||||
...(authStore.isAdmin
|
||||
? [{ name: "Admin", path: "/admin", icon: Shield }]
|
||||
: []),
|
||||
]);
|
||||
|
||||
const formattedBalance = computed(() => {
|
||||
return new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
}).format(authStore.balance);
|
||||
});
|
||||
|
||||
const handleLogin = () => {
|
||||
authStore.login();
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
showUserMenu.value = false;
|
||||
await authStore.logout();
|
||||
};
|
||||
|
||||
const handleDeposit = () => {
|
||||
showBalanceMenu.value = false;
|
||||
router.push("/deposit");
|
||||
};
|
||||
|
||||
const toggleMobileMenu = () => {
|
||||
showMobileMenu.value = !showMobileMenu.value;
|
||||
};
|
||||
|
||||
const toggleUserMenu = () => {
|
||||
showUserMenu.value = !showUserMenu.value;
|
||||
};
|
||||
|
||||
const toggleBalanceMenu = () => {
|
||||
showBalanceMenu.value = !showBalanceMenu.value;
|
||||
};
|
||||
|
||||
const closeMenus = () => {
|
||||
showMobileMenu.value = false;
|
||||
showUserMenu.value = false;
|
||||
showBalanceMenu.value = false;
|
||||
};
|
||||
|
||||
const handleClickOutside = (event) => {
|
||||
if (userMenuRef.value && !userMenuRef.value.contains(event.target)) {
|
||||
showUserMenu.value = false;
|
||||
}
|
||||
if (balanceMenuRef.value && !balanceMenuRef.value.contains(event.target)) {
|
||||
showBalanceMenu.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav
|
||||
class="sticky top-0 z-40 bg-surface/95 backdrop-blur-md border-b border-surface-lighter"
|
||||
>
|
||||
<div class="w-full px-6">
|
||||
<div class="flex items-center justify-between h-16">
|
||||
<!-- Logo -->
|
||||
<router-link
|
||||
to="/"
|
||||
class="flex items-center gap-2 text-xl font-display font-bold text-white hover:text-primary-500 transition-colors"
|
||||
@click="closeMenus"
|
||||
>
|
||||
<div
|
||||
class="w-8 h-8 bg-gradient-to-br from-primary-500 to-primary-700 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<span class="text-white text-sm font-bold">TT</span>
|
||||
</div>
|
||||
<span class="hidden sm:inline">TurboTrades</span>
|
||||
</router-link>
|
||||
|
||||
<!-- Desktop Navigation - Centered -->
|
||||
<div
|
||||
class="hidden lg:flex items-center gap-8 absolute left-1/2 transform -translate-x-1/2"
|
||||
>
|
||||
<template v-for="link in navigationLinks" :key="link.path">
|
||||
<router-link
|
||||
v-if="!link.requiresAuth || authStore.isAuthenticated"
|
||||
:to="link.path"
|
||||
class="nav-link flex items-center gap-2"
|
||||
active-class="nav-link-active"
|
||||
>
|
||||
<component :is="link.icon" v-if="link.icon" class="w-4 h-4" />
|
||||
{{ link.name }}
|
||||
</router-link>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Right Side Actions -->
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- Balance with Inline Deposit Button (when authenticated) -->
|
||||
<div
|
||||
v-if="authStore.isAuthenticated"
|
||||
class="hidden sm:flex items-center bg-surface-light rounded-lg border border-surface-lighter overflow-hidden h-10"
|
||||
>
|
||||
<!-- Balance Display -->
|
||||
<div class="flex items-center gap-2 px-4 py-2">
|
||||
<Wallet class="w-5 h-5 text-primary-500" />
|
||||
<span class="text-base font-semibold text-white">{{
|
||||
formattedBalance
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<!-- Deposit Button -->
|
||||
<button
|
||||
@click="handleDeposit"
|
||||
class="h-full px-4 bg-primary-500 hover:bg-primary-600 transition-colors flex items-center justify-center"
|
||||
title="Deposit"
|
||||
>
|
||||
<Plus class="w-5 h-5 text-white" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- User Menu / Login Button -->
|
||||
<div
|
||||
v-if="authStore.isAuthenticated"
|
||||
class="relative"
|
||||
ref="userMenuRef"
|
||||
>
|
||||
<button
|
||||
@click.stop="toggleUserMenu"
|
||||
class="flex items-center gap-2 px-3 py-2 bg-surface-light hover:bg-surface-lighter rounded-lg border border-surface-lighter transition-colors"
|
||||
>
|
||||
<img
|
||||
v-if="authStore.avatar"
|
||||
:src="authStore.avatar"
|
||||
:alt="authStore.username"
|
||||
class="w-8 h-8 rounded-full"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="w-8 h-8 rounded-full bg-primary-500 flex items-center justify-center"
|
||||
>
|
||||
<User class="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span class="hidden lg:inline text-sm font-medium text-white">
|
||||
{{ authStore.username }}
|
||||
</span>
|
||||
<ChevronDown class="w-4 h-4 text-gray-400" />
|
||||
</button>
|
||||
|
||||
<!-- User Dropdown Menu -->
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="showUserMenu"
|
||||
class="absolute right-0 mt-2 w-56 bg-surface-light border border-surface-lighter rounded-lg shadow-xl overflow-hidden"
|
||||
>
|
||||
<!-- Balance (Mobile) -->
|
||||
<div
|
||||
class="sm:hidden px-4 py-3 bg-surface-dark border-b border-surface-lighter"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-sm text-gray-400">Balance</span>
|
||||
<span class="text-sm font-semibold text-primary-500">{{
|
||||
formattedBalance
|
||||
}}</span>
|
||||
</div>
|
||||
<button
|
||||
@click="handleDeposit"
|
||||
class="w-full px-3 py-1.5 bg-primary-500 hover:bg-primary-600 text-surface-dark text-sm font-medium rounded transition-colors flex items-center justify-center gap-1.5"
|
||||
>
|
||||
<Plus class="w-3.5 h-3.5" />
|
||||
Deposit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Menu Items -->
|
||||
<div class="py-2">
|
||||
<router-link
|
||||
v-for="link in userMenuLinks"
|
||||
:key="link.path"
|
||||
:to="link.path"
|
||||
:class="[
|
||||
'flex items-center gap-3 px-4 py-2.5 text-sm transition-colors',
|
||||
link.name === 'Admin'
|
||||
? 'bg-gradient-to-r from-yellow-900/40 to-yellow-800/40 text-yellow-400 hover:from-yellow-900/60 hover:to-yellow-800/60 hover:text-yellow-300 border-l-2 border-yellow-500'
|
||||
: 'text-gray-300 hover:text-white hover:bg-surface',
|
||||
]"
|
||||
@click="closeMenus"
|
||||
>
|
||||
<component :is="link.icon" class="w-4 h-4" />
|
||||
{{ link.name }}
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<!-- Logout -->
|
||||
<div class="border-t border-surface-lighter">
|
||||
<button
|
||||
@click="handleLogout"
|
||||
class="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-accent-red hover:bg-surface transition-colors"
|
||||
>
|
||||
<LogOut class="w-4 h-4" />
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- Login Button -->
|
||||
<button v-else @click="handleLogin" class="btn btn-primary">
|
||||
<img
|
||||
src="https://community.cloudflare.steamstatic.com/public/images/signinthroughsteam/sits_01.png"
|
||||
alt="Sign in through Steam"
|
||||
class="h-6"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<!-- Mobile Menu Toggle -->
|
||||
<button
|
||||
@click="toggleMobileMenu"
|
||||
class="lg:hidden p-2 text-gray-400 hover:text-white transition-colors"
|
||||
>
|
||||
<Menu v-if="!showMobileMenu" class="w-6 h-6" />
|
||||
<X v-else class="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<Transition name="slide-down">
|
||||
<div
|
||||
v-if="showMobileMenu"
|
||||
class="lg:hidden border-t border-surface-lighter bg-surface"
|
||||
>
|
||||
<div class="container-custom py-4 space-y-2">
|
||||
<template v-for="link in navigationLinks" :key="link.path">
|
||||
<router-link
|
||||
v-if="!link.requiresAuth || authStore.isAuthenticated"
|
||||
:to="link.path"
|
||||
class="flex items-center gap-3 px-4 py-3 text-gray-300 hover:text-white hover:bg-surface-light rounded-lg transition-colors"
|
||||
active-class="text-primary-500 bg-surface-light"
|
||||
@click="closeMenus"
|
||||
>
|
||||
<component :is="link.icon" v-if="link.icon" class="w-5 h-5" />
|
||||
{{ link.name }}
|
||||
</router-link>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-down-enter-active,
|
||||
.slide-down-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-down-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
.slide-down-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user