Mockups for Designs
All checks were successful
Build And Push Image / docker (push) Successful in 1m55s

This commit is contained in:
2025-09-03 21:04:44 +02:00
parent e75de8b9f4
commit 4d24315103
12 changed files with 5425 additions and 0 deletions

View File

@@ -0,0 +1,814 @@
<template>
<div class="admin-dashboard">
<!-- Sidebar Navigation -->
<aside class="sidebar" :class="{ 'sidebar--collapsed': isSidebarCollapsed }">
<div class="sidebar-header">
<div class="sidebar-logo">
<img src="/MONACOUSA-Flags_376x376.png" alt="MonacoUSA" />
<span v-if="!isSidebarCollapsed" class="sidebar-title">Admin Portal</span>
</div>
<button @click="isSidebarCollapsed = !isSidebarCollapsed" class="sidebar-toggle">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<nav class="sidebar-nav">
<a
v-for="item in navItems"
:key="item.id"
:class="['sidebar-item', { 'sidebar-item--active': activeNav === item.id }]"
@click="activeNav = item.id"
>
<span class="sidebar-item-icon">
<component :is="item.icon" />
</span>
<span v-if="!isSidebarCollapsed" class="sidebar-item-label">{{ item.label }}</span>
<span v-if="!isSidebarCollapsed && item.badge" class="sidebar-item-badge">{{ item.badge }}</span>
</a>
</nav>
<div class="sidebar-footer">
<div class="sidebar-user">
<div class="sidebar-user-avatar">
<img src="https://via.placeholder.com/40" alt="Admin" />
</div>
<div v-if="!isSidebarCollapsed" class="sidebar-user-info">
<div class="sidebar-user-name">John Admin</div>
<div class="sidebar-user-role">System Admin</div>
</div>
</div>
</div>
</aside>
<!-- Main Content -->
<main class="main-content">
<!-- Top Bar -->
<header class="topbar">
<div class="topbar-left">
<h1 class="topbar-title">Dashboard Overview</h1>
<p class="topbar-subtitle">Welcome back, John. Here's what's happening today.</p>
</div>
<div class="topbar-right">
<button class="topbar-button">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</button>
<button class="topbar-button topbar-button--notification">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
<span class="notification-badge">3</span>
</button>
<div class="topbar-divider"></div>
<button class="topbar-user">
<img src="https://via.placeholder.com/32" alt="User" class="topbar-user-avatar" />
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
</header>
<!-- Stats Grid -->
<div class="stats-grid">
<StatCard
v-for="stat in stats"
:key="stat.title"
:title="stat.title"
:value="stat.value"
:change="stat.change"
:trend="stat.trend"
:icon="stat.icon"
/>
</div>
<!-- Content Grid -->
<div class="content-grid">
<!-- Chart Card -->
<NeumorphicCard class="chart-card">
<template #header>
<div class="card-header">
<h2 class="card-title">Revenue Overview</h2>
<div class="card-actions">
<button class="card-action">Day</button>
<button class="card-action card-action--active">Week</button>
<button class="card-action">Month</button>
<button class="card-action">Year</button>
</div>
</div>
</template>
<div class="chart-container">
<canvas ref="chartCanvas"></canvas>
</div>
</NeumorphicCard>
<!-- Activity Feed -->
<NeumorphicCard class="activity-card">
<template #header>
<div class="card-header">
<h2 class="card-title">Recent Activity</h2>
<button class="card-link">View All</button>
</div>
</template>
<div class="activity-list">
<div v-for="activity in activities" :key="activity.id" class="activity-item">
<div class="activity-icon" :class="`activity-icon--${activity.type}`">
<component :is="activity.icon" />
</div>
<div class="activity-content">
<p class="activity-description">{{ activity.description }}</p>
<span class="activity-time">{{ activity.time }}</span>
</div>
</div>
</div>
</NeumorphicCard>
<!-- Members Table -->
<NeumorphicCard class="table-card">
<template #header>
<div class="card-header">
<h2 class="card-title">Recent Members</h2>
<ProfessionalButton variant="outline" size="sm">
View All Members
</ProfessionalButton>
</div>
</template>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Member</th>
<th>Status</th>
<th>Joined</th>
<th>Last Active</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="member in recentMembers" :key="member.id">
<td>
<div class="member-cell">
<img :src="member.avatar" :alt="member.name" class="member-avatar" />
<div>
<div class="member-name">{{ member.name }}</div>
<div class="member-email">{{ member.email }}</div>
</div>
</div>
</td>
<td>
<span class="status-badge" :class="`status-badge--${member.status}`">
{{ member.status }}
</span>
</td>
<td>{{ member.joined }}</td>
<td>{{ member.lastActive }}</td>
<td>
<button class="table-action">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
</svg>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</NeumorphicCard>
</div>
</main>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import NeumorphicCard from '../../components/core/NeumorphicCard.vue';
import ProfessionalButton from '../../components/core/ProfessionalButton.vue';
import StatCard from '../../components/data/StatCard.vue';
import Chart from 'chart.js/auto';
// Icons (simplified for demo)
const HomeIcon = { template: '<svg fill="currentColor" viewBox="0 0 20 20"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/></svg>' };
const UsersIcon = { template: '<svg fill="currentColor" viewBox="0 0 20 20"><path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z"/></svg>' };
const CalendarIcon = { template: '<svg fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clip-rule="evenodd"/></svg>' };
const ChartIcon = { template: '<svg fill="currentColor" viewBox="0 0 20 20"><path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/></svg>' };
// Data
const isSidebarCollapsed = ref(false);
const activeNav = ref('dashboard');
const chartCanvas = ref<HTMLCanvasElement | null>(null);
const navItems = [
{ id: 'dashboard', label: 'Dashboard', icon: HomeIcon },
{ id: 'members', label: 'Members', icon: UsersIcon, badge: '127' },
{ id: 'events', label: 'Events', icon: CalendarIcon, badge: '5' },
{ id: 'analytics', label: 'Analytics', icon: ChartIcon },
];
const stats = [
{ title: 'Total Members', value: '1,247', change: '+12%', trend: 'up', icon: UsersIcon },
{ title: 'Active Events', value: '18', change: '+3', trend: 'up', icon: CalendarIcon },
{ title: 'Monthly Revenue', value: '$48,392', change: '+8%', trend: 'up', icon: ChartIcon },
{ title: 'Engagement Rate', value: '87%', change: '-2%', trend: 'down', icon: ChartIcon },
];
const activities = [
{ id: 1, type: 'user', icon: UsersIcon, description: 'New member registration: Sarah Johnson', time: '5 minutes ago' },
{ id: 2, type: 'event', icon: CalendarIcon, description: 'Annual Gala event updated', time: '1 hour ago' },
{ id: 3, type: 'payment', icon: ChartIcon, description: 'Payment received from Michael Brown', time: '2 hours ago' },
{ id: 4, type: 'user', icon: UsersIcon, description: 'Member profile updated: Robert Davis', time: '3 hours ago' },
];
const recentMembers = [
{ id: 1, name: 'Sarah Johnson', email: 'sarah@example.com', avatar: 'https://via.placeholder.com/40', status: 'active', joined: 'Jan 15, 2024', lastActive: '2 hours ago' },
{ id: 2, name: 'Michael Brown', email: 'michael@example.com', avatar: 'https://via.placeholder.com/40', status: 'active', joined: 'Jan 10, 2024', lastActive: '1 day ago' },
{ id: 3, name: 'Emma Wilson', email: 'emma@example.com', avatar: 'https://via.placeholder.com/40', status: 'pending', joined: 'Jan 8, 2024', lastActive: '3 days ago' },
{ id: 4, name: 'James Taylor', email: 'james@example.com', avatar: 'https://via.placeholder.com/40', status: 'inactive', joined: 'Dec 20, 2023', lastActive: '1 week ago' },
];
// Initialize chart
onMounted(() => {
if (chartCanvas.value) {
new Chart(chartCanvas.value, {
type: 'line',
data: {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
datasets: [{
label: 'Revenue',
data: [12000, 19000, 15000, 25000, 22000, 30000, 28000],
borderColor: '#DC2626',
backgroundColor: 'rgba(220, 38, 38, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
grid: {
display: true,
color: 'rgba(0, 0, 0, 0.05)'
}
},
x: {
grid: {
display: false
}
}
}
}
});
}
});
</script>
<style lang="scss" scoped>
@import '../../styles/neumorphic-system.scss';
.admin-dashboard {
display: flex;
min-height: 100vh;
background: $neutral-100;
}
// Sidebar
.sidebar {
width: $sidebar-width;
background: white;
box-shadow: $shadow-soft-md;
display: flex;
flex-direction: column;
transition: width $transition-base;
&--collapsed {
width: $sidebar-width-collapsed;
}
&-header {
padding: $space-6;
border-bottom: 1px solid $neutral-200;
display: flex;
align-items: center;
justify-content: space-between;
}
&-logo {
display: flex;
align-items: center;
gap: $space-3;
img {
width: 40px;
height: 40px;
border-radius: $radius-lg;
}
}
&-title {
font-weight: $font-semibold;
color: $neutral-800;
white-space: nowrap;
}
&-toggle {
background: none;
border: none;
padding: $space-2;
cursor: pointer;
color: $neutral-600;
border-radius: $radius-md;
transition: all $transition-base;
svg {
width: 20px;
height: 20px;
}
&:hover {
background: $neutral-100;
}
}
&-nav {
flex: 1;
padding: $space-4;
}
&-item {
display: flex;
align-items: center;
gap: $space-3;
padding: $space-3 $space-4;
margin-bottom: $space-2;
border-radius: $radius-lg;
color: $neutral-600;
text-decoration: none;
cursor: pointer;
transition: all $transition-base;
position: relative;
&:hover {
background: $neutral-100;
color: $neutral-800;
}
&--active {
background: linear-gradient(135deg, $primary-500, $primary-600);
color: white;
box-shadow: $shadow-soft-sm;
}
&-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
svg {
width: 100%;
height: 100%;
}
}
&-label {
font-size: $text-sm;
font-weight: $font-medium;
white-space: nowrap;
}
&-badge {
margin-left: auto;
padding: 2px 8px;
background: $primary-100;
color: $primary-700;
border-radius: $radius-full;
font-size: $text-xs;
font-weight: $font-semibold;
}
}
&-footer {
padding: $space-4;
border-top: 1px solid $neutral-200;
}
&-user {
display: flex;
align-items: center;
gap: $space-3;
padding: $space-3;
&-avatar {
width: 40px;
height: 40px;
border-radius: $radius-full;
overflow: hidden;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
&-info {
flex: 1;
min-width: 0;
}
&-name {
font-size: $text-sm;
font-weight: $font-semibold;
color: $neutral-800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&-role {
font-size: $text-xs;
color: $neutral-500;
}
}
}
// Main Content
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
// Topbar
.topbar {
background: white;
padding: $space-6;
box-shadow: $shadow-soft-sm;
display: flex;
align-items: center;
justify-content: space-between;
&-left {
flex: 1;
}
&-title {
font-size: $text-2xl;
font-weight: $font-bold;
color: $neutral-800;
margin-bottom: $space-1;
}
&-subtitle {
font-size: $text-sm;
color: $neutral-600;
}
&-right {
display: flex;
align-items: center;
gap: $space-3;
}
&-button {
position: relative;
padding: $space-2;
background: white;
border: none;
border-radius: $radius-lg;
cursor: pointer;
color: $neutral-600;
box-shadow: $shadow-soft-sm;
transition: all $transition-base;
svg {
width: 20px;
height: 20px;
}
&:hover {
box-shadow: $shadow-soft-md;
color: $neutral-800;
}
&--notification {
.notification-badge {
position: absolute;
top: -4px;
right: -4px;
width: 18px;
height: 18px;
background: $error-500;
color: white;
border-radius: $radius-full;
font-size: $text-xs;
display: flex;
align-items: center;
justify-content: center;
font-weight: $font-bold;
}
}
}
&-divider {
width: 1px;
height: 24px;
background: $neutral-200;
}
&-user {
display: flex;
align-items: center;
gap: $space-2;
padding: $space-2;
background: white;
border: none;
border-radius: $radius-lg;
cursor: pointer;
box-shadow: $shadow-soft-sm;
transition: all $transition-base;
&:hover {
box-shadow: $shadow-soft-md;
}
&-avatar {
width: 32px;
height: 32px;
border-radius: $radius-full;
}
}
}
// Stats Grid
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: $space-6;
padding: $space-6;
}
// Content Grid
.content-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: $space-6;
padding: 0 $space-6 $space-6;
@media (max-width: $breakpoint-lg) {
grid-template-columns: 1fr;
}
.chart-card {
grid-column: 1;
grid-row: 1;
}
.activity-card {
grid-column: 2;
grid-row: 1;
}
.table-card {
grid-column: 1 / -1;
}
}
// Card styles
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-title {
font-size: $text-lg;
font-weight: $font-semibold;
color: $neutral-800;
}
.card-actions {
display: flex;
gap: $space-2;
}
.card-action {
padding: $space-2 $space-3;
background: transparent;
border: none;
border-radius: $radius-md;
font-size: $text-sm;
color: $neutral-600;
cursor: pointer;
transition: all $transition-base;
&:hover {
background: $neutral-100;
color: $neutral-800;
}
&--active {
background: $primary-500;
color: white;
box-shadow: $shadow-soft-sm;
}
}
.card-link {
background: none;
border: none;
color: $primary-600;
font-size: $text-sm;
font-weight: $font-medium;
cursor: pointer;
&:hover {
color: $primary-700;
text-decoration: underline;
}
}
// Chart
.chart-container {
height: 300px;
padding: $space-4 0;
}
// Activity List
.activity-list {
display: flex;
flex-direction: column;
gap: $space-4;
}
.activity-item {
display: flex;
gap: $space-3;
&-icon {
width: 40px;
height: 40px;
border-radius: $radius-lg;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
svg {
width: 20px;
height: 20px;
}
&--user {
background: $info-100;
color: $info-500;
}
&--event {
background: $warning-100;
color: $warning-500;
}
&--payment {
background: $success-100;
color: $success-500;
}
}
&-content {
flex: 1;
min-width: 0;
}
&-description {
font-size: $text-sm;
color: $neutral-700;
margin-bottom: $space-1;
}
&-time {
font-size: $text-xs;
color: $neutral-500;
}
}
// Table
.table-wrapper {
overflow-x: auto;
}
.data-table {
width: 100%;
thead {
tr {
border-bottom: 2px solid $neutral-200;
}
th {
text-align: left;
padding: $space-3;
font-size: $text-xs;
font-weight: $font-semibold;
text-transform: uppercase;
letter-spacing: 0.05em;
color: $neutral-600;
}
}
tbody {
tr {
border-bottom: 1px solid $neutral-100;
transition: background $transition-base;
&:hover {
background: $neutral-50;
}
}
td {
padding: $space-3;
font-size: $text-sm;
color: $neutral-700;
}
}
}
.member-cell {
display: flex;
align-items: center;
gap: $space-3;
}
.member-avatar {
width: 40px;
height: 40px;
border-radius: $radius-full;
}
.member-name {
font-weight: $font-medium;
color: $neutral-800;
margin-bottom: 2px;
}
.member-email {
font-size: $text-xs;
color: $neutral-500;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: $radius-full;
font-size: $text-xs;
font-weight: $font-medium;
&--active {
background: $success-100;
color: $success-500;
}
&--pending {
background: $warning-100;
color: $warning-500;
}
&--inactive {
background: $neutral-100;
color: $neutral-500;
}
}
.table-action {
padding: $space-2;
background: none;
border: none;
cursor: pointer;
color: $neutral-500;
border-radius: $radius-md;
transition: all $transition-base;
&:hover {
background: $neutral-100;
color: $neutral-700;
}
}</style>

View File

@@ -0,0 +1,419 @@
<template>
<div class="login-page">
<div class="login-container">
<!-- Left Side - Branding -->
<div class="login-branding">
<div class="branding-content">
<div class="logo-container">
<img src="/MONACOUSA-Flags_376x376.png" alt="MonacoUSA" class="logo" />
</div>
<h1 class="brand-title">MonacoUSA Portal</h1>
<p class="brand-tagline">Excellence in Partnership</p>
<div class="feature-list">
<div class="feature-item" v-for="feature in features" :key="feature">
<svg class="feature-icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
<span>{{ feature }}</span>
</div>
</div>
</div>
<!-- Background pattern -->
<div class="branding-pattern"></div>
</div>
<!-- Right Side - Login Form -->
<div class="login-form-section">
<NeumorphicCard size="lg" elevation="lg" class="login-card">
<template #header>
<h2 class="login-title">Welcome Back</h2>
<p class="login-subtitle">Sign in to access your account</p>
</template>
<form @submit.prevent="handleLogin" class="login-form">
<div class="form-group">
<label for="email" class="form-label">Email Address</label>
<div class="input-wrapper">
<input
id="email"
v-model="credentials.email"
type="email"
class="form-input"
placeholder="you@example.com"
required
/>
<span class="input-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</span>
</div>
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<div class="input-wrapper">
<input
id="password"
v-model="credentials.password"
:type="showPassword ? 'text' : 'password'"
class="form-input"
placeholder="Enter your password"
required
/>
<button
type="button"
@click="showPassword = !showPassword"
class="input-icon clickable"
>
<svg v-if="!showPassword" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
<svg v-else fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
</svg>
</button>
</div>
</div>
<div class="form-options">
<label class="checkbox-label">
<input v-model="credentials.rememberMe" type="checkbox" class="checkbox-input" />
<span>Remember me</span>
</label>
<button type="button" class="forgot-password-link" @click="showForgotPassword = true">
Forgot password?
</button>
</div>
<ProfessionalButton
type="submit"
variant="primary"
size="lg"
block
:loading="loading"
class="login-button"
>
Sign In
</ProfessionalButton>
</form>
<template #footer>
<div class="login-footer">
<p class="signup-prompt">
Don't have an account?
<a href="/signup" class="signup-link">Create Account</a>
</p>
</div>
</template>
</NeumorphicCard>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import NeumorphicCard from '../../components/core/NeumorphicCard.vue';
import ProfessionalButton from '../../components/core/ProfessionalButton.vue';
// Data
const credentials = ref({
email: '',
password: '',
rememberMe: false
});
const showPassword = ref(false);
const loading = ref(false);
const showForgotPassword = ref(false);
const features = [
'Secure Member Access',
'Event Management',
'Document Library',
'Payment Processing'
];
// Methods
const handleLogin = async () => {
loading.value = true;
// Simulate API call
setTimeout(() => {
loading.value = false;
console.log('Login with:', credentials.value);
}, 2000);
};
</script>
<style lang="scss" scoped>
@import '../../styles/neumorphic-system.scss';
.login-page {
min-height: 100vh;
background: linear-gradient(135deg, $neutral-50 0%, $neutral-100 100%);
display: flex;
align-items: center;
justify-content: center;
}
.login-container {
width: 100%;
max-width: 1400px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
min-height: 100vh;
@media (max-width: $breakpoint-md) {
grid-template-columns: 1fr;
}
}
// Left side - Branding
.login-branding {
position: relative;
background: linear-gradient(135deg, $primary-600, $primary-800);
display: flex;
align-items: center;
justify-content: center;
padding: $space-12;
overflow: hidden;
@media (max-width: $breakpoint-md) {
display: none;
}
.branding-content {
position: relative;
z-index: 2;
text-align: center;
color: white;
}
.logo-container {
width: 120px;
height: 120px;
margin: 0 auto $space-6;
background: white;
border-radius: $radius-2xl;
padding: $space-4;
box-shadow: $shadow-soft-lg;
.logo {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.brand-title {
font-family: $font-heading;
font-size: $text-3xl;
font-weight: $font-bold;
margin-bottom: $space-2;
letter-spacing: -0.025em;
}
.brand-tagline {
font-size: $text-lg;
opacity: 0.9;
margin-bottom: $space-12;
}
.feature-list {
text-align: left;
max-width: 300px;
margin: 0 auto;
}
.feature-item {
display: flex;
align-items: center;
gap: $space-3;
margin-bottom: $space-4;
font-size: $text-base;
.feature-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
color: rgba(white, 0.9);
}
}
.branding-pattern {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
opacity: 0.5;
}
}
// Right side - Login form
.login-form-section {
display: flex;
align-items: center;
justify-content: center;
padding: $space-8;
.login-card {
width: 100%;
max-width: 480px;
}
.login-title {
font-family: $font-heading;
font-size: $text-2xl;
font-weight: $font-bold;
color: $neutral-800;
margin-bottom: $space-2;
}
.login-subtitle {
font-size: $text-base;
color: $neutral-600;
}
.login-form {
.form-group {
margin-bottom: $space-5;
}
.form-label {
display: block;
font-size: $text-sm;
font-weight: $font-medium;
color: $neutral-700;
margin-bottom: $space-2;
}
.input-wrapper {
position: relative;
}
.form-input {
width: 100%;
padding: $space-3 $space-4;
padding-right: $space-12;
background: $neutral-50;
border: none;
border-radius: $radius-lg;
box-shadow: $shadow-inset-sm;
font-size: $text-base;
color: $neutral-800;
transition: all $transition-base;
&::placeholder {
color: $neutral-400;
}
&:focus {
outline: none;
box-shadow: $shadow-inset-md, 0 0 0 3px rgba($primary-500, 0.1);
}
}
.input-icon {
position: absolute;
right: $space-3;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
color: $neutral-400;
&.clickable {
cursor: pointer;
background: none;
border: none;
padding: 0;
transition: color $transition-base;
&:hover {
color: $neutral-600;
}
}
}
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: $space-6;
}
.checkbox-label {
display: flex;
align-items: center;
gap: $space-2;
font-size: $text-sm;
color: $neutral-700;
cursor: pointer;
.checkbox-input {
width: 18px;
height: 18px;
border-radius: $radius-sm;
cursor: pointer;
}
}
.forgot-password-link {
background: none;
border: none;
color: $primary-600;
font-size: $text-sm;
font-weight: $font-medium;
cursor: pointer;
padding: 0;
transition: color $transition-base;
&:hover {
color: $primary-700;
text-decoration: underline;
}
}
.login-button {
margin-top: $space-4;
}
}
.login-footer {
text-align: center;
.signup-prompt {
font-size: $text-sm;
color: $neutral-600;
}
.signup-link {
color: $primary-600;
font-weight: $font-medium;
text-decoration: none;
margin-left: $space-1;
transition: color $transition-base;
&:hover {
color: $primary-700;
text-decoration: underline;
}
}
}
}
// Dark mode support
@include dark-mode {
.login-page {
background: linear-gradient(135deg, $neutral-900 0%, $neutral-800 100%);
}
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff