feat: Reorganize platform into member, board, and admin sections
Some checks failed
Build And Push Image / docker (push) Failing after 55s
Some checks failed
Build And Push Image / docker (push) Failing after 55s
Major platform reorganization implementing role-based portal sections: ## Infrastructure Changes - Created role-based middleware for member, board, and admin access - Updated main dashboard router to redirect based on highest privilege - Implemented access hierarchy: Admin > Board > Member ## New Layouts - Member layout: Simplified navigation for regular members - Board layout: Enhanced tools for board member management - Admin layout: Full system administration capabilities ## Member Portal (/member/*) - Dashboard: Profile overview, events, payments, activity tracking - Events: Browse, register, and manage event participation - Profile: Complete personal and professional information management - Resources: Access to documents, guides, FAQs, and quick links ## Board Portal (/board/*) - Dashboard: Statistics, dues management, board-specific tools - Members: Comprehensive member management with filtering ## Admin Portal (/admin/*) - Dashboard: System overview and administrative controls (existing) ## Design Implementation - Monaco red (#dc2626) as primary accent color - Modern card-based layouts with consistent spacing - Responsive design for all screen sizes - Glass morphism effects for enhanced visual appeal This reorganization provides clear separation of concerns based on user privileges while maintaining a cohesive user experience across all sections. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="profile-mockup">
|
||||
<!-- Profile Header -->
|
||||
<!-- Admin Header -->
|
||||
<div
|
||||
v-motion
|
||||
:initial="{ opacity: 0, y: -20 }"
|
||||
@@ -22,51 +22,64 @@
|
||||
<div v-else class="profile-avatar__placeholder">
|
||||
{{ initials }}
|
||||
</div>
|
||||
<button class="profile-avatar__edit">
|
||||
<span>📷</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="profile-status">
|
||||
<span class="profile-status__indicator"></span>
|
||||
<span class="profile-status__text">Online</span>
|
||||
<div class="member-status-badge">
|
||||
<span
|
||||
class="status-indicator"
|
||||
:class="`status-indicator--${profile.memberStatus}`"
|
||||
></span>
|
||||
<span class="status-text">{{ profile.memberStatus }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-header__info">
|
||||
<h1 class="profile-name">{{ profile.name }}</h1>
|
||||
<p class="profile-title">{{ profile.title }} at {{ profile.company }}</p>
|
||||
<p class="profile-bio">{{ profile.bio }}</p>
|
||||
<p class="profile-member-id">Member ID: #{{ profile.memberId }}</p>
|
||||
|
||||
<div class="profile-badges">
|
||||
<span class="badge badge--verified">✓ Verified Member</span>
|
||||
<span class="badge badge--board">Board Member</span>
|
||||
<span class="badge badge--year">Member Since {{ profile.memberSince }}</span>
|
||||
<span
|
||||
class="badge"
|
||||
:class="`badge--${profile.memberStatus}`"
|
||||
>
|
||||
{{ profile.memberStatus }} Member
|
||||
</span>
|
||||
<span
|
||||
class="badge"
|
||||
:class="`badge--${profile.duesStatus}`"
|
||||
>
|
||||
Dues: {{ profile.duesStatus }}
|
||||
</span>
|
||||
<span class="badge badge--year">Joined {{ profile.memberSince }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-header__actions">
|
||||
<MonacoButton variant="primary" icon="edit">
|
||||
Edit Profile
|
||||
Edit Member
|
||||
</MonacoButton>
|
||||
<MonacoButton variant="glass" icon="share">
|
||||
Share
|
||||
<MonacoButton variant="glass" icon="mail">
|
||||
Send Email
|
||||
</MonacoButton>
|
||||
<MonacoButton variant="ghost" icon="settings">
|
||||
Settings
|
||||
<MonacoButton variant="ghost" icon="ban">
|
||||
Suspend
|
||||
</MonacoButton>
|
||||
<MonacoButton variant="ghost" icon="trash">
|
||||
Remove
|
||||
</MonacoButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Overview -->
|
||||
<!-- Admin Stats Overview -->
|
||||
<div
|
||||
v-motion
|
||||
:initial="{ opacity: 0, y: 20 }"
|
||||
:enter="{ opacity: 1, y: 0, transition: { delay: 200 } }"
|
||||
class="profile-stats"
|
||||
>
|
||||
<div class="stat-card" v-for="stat in stats" :key="stat.label">
|
||||
<div class="stat-card" v-for="stat in adminStats" :key="stat.label">
|
||||
<span class="stat-card__value">{{ stat.value }}</span>
|
||||
<span class="stat-card__label">{{ stat.label }}</span>
|
||||
</div>
|
||||
@@ -116,63 +129,92 @@
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
<!-- Skills & Expertise -->
|
||||
<!-- Account Information -->
|
||||
<GlassCard
|
||||
title="Skills & Expertise"
|
||||
title="Account Information"
|
||||
variant="glass"
|
||||
:delay="400"
|
||||
>
|
||||
<div class="skills-cloud">
|
||||
<span
|
||||
v-for="skill in profile.skills"
|
||||
:key="skill"
|
||||
class="skill-tag"
|
||||
:class="`skill-tag--${getSkillLevel(skill)}`"
|
||||
>
|
||||
{{ skill }}
|
||||
</span>
|
||||
<div class="account-info">
|
||||
<div class="account-item">
|
||||
<span class="account-item__label">Portal Access</span>
|
||||
<span
|
||||
class="account-item__value"
|
||||
:class="{ 'account-item__value--active': profile.hasPortalAccess }"
|
||||
>
|
||||
{{ profile.hasPortalAccess ? 'Active' : 'No Access' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="account-item">
|
||||
<span class="account-item__label">Last Login</span>
|
||||
<span class="account-item__value">{{ profile.lastLogin }}</span>
|
||||
</div>
|
||||
<div class="account-item">
|
||||
<span class="account-item__label">Login Count</span>
|
||||
<span class="account-item__value">{{ profile.loginCount }}</span>
|
||||
</div>
|
||||
<div class="account-item">
|
||||
<span class="account-item__label">Account Created</span>
|
||||
<span class="account-item__value">{{ profile.accountCreated }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<MonacoButton
|
||||
v-if="!profile.hasPortalAccess"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
block
|
||||
>
|
||||
Send Portal Invitation
|
||||
</MonacoButton>
|
||||
<MonacoButton
|
||||
v-else
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
block
|
||||
>
|
||||
Reset Password
|
||||
</MonacoButton>
|
||||
</GlassCard>
|
||||
|
||||
<!-- Membership Details -->
|
||||
<!-- Payment History -->
|
||||
<GlassCard
|
||||
title="Membership"
|
||||
title="Payment History"
|
||||
variant="gradient"
|
||||
:delay="500"
|
||||
>
|
||||
<div class="membership-details">
|
||||
<div class="membership-item">
|
||||
<span class="membership-item__label">Status</span>
|
||||
<span class="membership-item__value membership-item__value--active">
|
||||
Active
|
||||
<div class="payment-history">
|
||||
<div
|
||||
v-for="payment in paymentHistory"
|
||||
:key="payment.id"
|
||||
class="payment-item"
|
||||
>
|
||||
<div class="payment-item__date">{{ payment.date }}</div>
|
||||
<div class="payment-item__info">
|
||||
<span class="payment-item__type">{{ payment.type }}</span>
|
||||
<span class="payment-item__amount">${{ payment.amount }}</span>
|
||||
</div>
|
||||
<span
|
||||
class="payment-item__status"
|
||||
:class="`payment-item__status--${payment.status}`"
|
||||
>
|
||||
{{ payment.status }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="membership-item">
|
||||
<span class="membership-item__label">Type</span>
|
||||
<span class="membership-item__value">Executive</span>
|
||||
</div>
|
||||
<div class="membership-item">
|
||||
<span class="membership-item__label">Dues</span>
|
||||
<span class="membership-item__value membership-item__value--paid">
|
||||
Paid through 2025
|
||||
</span>
|
||||
</div>
|
||||
<div class="membership-item">
|
||||
<span class="membership-item__label">Next Renewal</span>
|
||||
<span class="membership-item__value">January 2025</span>
|
||||
</div>
|
||||
</div>
|
||||
<MonacoButton variant="primary" size="sm" block>
|
||||
Manage Membership
|
||||
Record Payment
|
||||
</MonacoButton>
|
||||
<MonacoButton variant="ghost" size="sm" block>
|
||||
Send Invoice
|
||||
</MonacoButton>
|
||||
</GlassCard>
|
||||
</div>
|
||||
|
||||
<!-- Main Column -->
|
||||
<div class="profile-grid__main">
|
||||
<!-- Activity Timeline -->
|
||||
<!-- Administrative Actions Log -->
|
||||
<GlassCard
|
||||
title="Recent Activity"
|
||||
title="Administrative Actions"
|
||||
variant="glass"
|
||||
:delay="350"
|
||||
>
|
||||
@@ -203,22 +245,33 @@
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
<!-- Achievements -->
|
||||
<!-- Member Notes -->
|
||||
<GlassCard
|
||||
title="Achievements"
|
||||
title="Internal Notes"
|
||||
variant="glass"
|
||||
:delay="450"
|
||||
>
|
||||
<div class="achievements-grid">
|
||||
<div class="notes-section">
|
||||
<div
|
||||
v-for="achievement in achievements"
|
||||
:key="achievement.id"
|
||||
class="achievement-card"
|
||||
v-for="note in memberNotes"
|
||||
:key="note.id"
|
||||
class="note-item"
|
||||
>
|
||||
<div class="achievement-card__icon">{{ achievement.icon }}</div>
|
||||
<h4 class="achievement-card__title">{{ achievement.title }}</h4>
|
||||
<p class="achievement-card__description">{{ achievement.description }}</p>
|
||||
<span class="achievement-card__date">{{ achievement.date }}</span>
|
||||
<div class="note-item__header">
|
||||
<span class="note-item__author">{{ note.author }}</span>
|
||||
<span class="note-item__date">{{ note.date }}</span>
|
||||
</div>
|
||||
<p class="note-item__content">{{ note.content }}</p>
|
||||
</div>
|
||||
<div class="add-note">
|
||||
<textarea
|
||||
placeholder="Add a new note..."
|
||||
class="note-input"
|
||||
rows="3"
|
||||
></textarea>
|
||||
<MonacoButton variant="primary" size="sm">
|
||||
Add Note
|
||||
</MonacoButton>
|
||||
</div>
|
||||
</div>
|
||||
</GlassCard>
|
||||
@@ -253,60 +306,54 @@
|
||||
|
||||
<!-- Right Column -->
|
||||
<div class="profile-grid__aside">
|
||||
<!-- Quick Stats -->
|
||||
<!-- Admin Quick Actions -->
|
||||
<GlassCard
|
||||
variant="glass"
|
||||
:delay="400"
|
||||
>
|
||||
<h3 class="card-title">Network Stats</h3>
|
||||
<div class="quick-stats">
|
||||
<div class="quick-stat">
|
||||
<span class="quick-stat__icon">👥</span>
|
||||
<span class="quick-stat__value">234</span>
|
||||
<span class="quick-stat__label">Connections</span>
|
||||
</div>
|
||||
<div class="quick-stat">
|
||||
<span class="quick-stat__icon">📅</span>
|
||||
<span class="quick-stat__value">45</span>
|
||||
<span class="quick-stat__label">Events</span>
|
||||
</div>
|
||||
<div class="quick-stat">
|
||||
<span class="quick-stat__icon">🏆</span>
|
||||
<span class="quick-stat__value">12</span>
|
||||
<span class="quick-stat__label">Awards</span>
|
||||
</div>
|
||||
<h3 class="card-title">Quick Actions</h3>
|
||||
<div class="admin-actions">
|
||||
<MonacoButton variant="glass" size="sm" icon="mail" block>
|
||||
Send Email
|
||||
</MonacoButton>
|
||||
<MonacoButton variant="glass" size="sm" icon="dollar" block>
|
||||
Record Payment
|
||||
</MonacoButton>
|
||||
<MonacoButton variant="glass" size="sm" icon="refresh" block>
|
||||
Reset Password
|
||||
</MonacoButton>
|
||||
<MonacoButton variant="glass" size="sm" icon="user-plus" block>
|
||||
Add to Group
|
||||
</MonacoButton>
|
||||
<MonacoButton variant="primary" size="sm" icon="chart" block>
|
||||
View Reports
|
||||
</MonacoButton>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
<!-- Connected Members -->
|
||||
<!-- Member Flags & Warnings -->
|
||||
<GlassCard
|
||||
title="Connections"
|
||||
title="Flags & Warnings"
|
||||
variant="glass"
|
||||
:delay="500"
|
||||
>
|
||||
<div class="connections-list">
|
||||
<div class="warnings-list">
|
||||
<div
|
||||
v-for="connection in connections"
|
||||
:key="connection.id"
|
||||
class="connection-item"
|
||||
v-for="warning in memberWarnings"
|
||||
:key="warning.id"
|
||||
class="warning-item"
|
||||
:class="`warning-item--${warning.severity}`"
|
||||
>
|
||||
<img
|
||||
v-if="connection.avatar"
|
||||
:src="connection.avatar"
|
||||
:alt="connection.name"
|
||||
class="connection-item__avatar"
|
||||
/>
|
||||
<div v-else class="connection-item__avatar-placeholder">
|
||||
{{ connection.name.split(' ').map(n => n[0]).join('') }}
|
||||
</div>
|
||||
<div class="connection-item__info">
|
||||
<h5 class="connection-item__name">{{ connection.name }}</h5>
|
||||
<p class="connection-item__title">{{ connection.title }}</p>
|
||||
<span class="warning-item__icon">{{ warning.icon }}</span>
|
||||
<div class="warning-item__content">
|
||||
<h5 class="warning-item__title">{{ warning.title }}</h5>
|
||||
<p class="warning-item__description">{{ warning.description }}</p>
|
||||
<span class="warning-item__date">{{ warning.date }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MonacoButton variant="primary" size="sm" block>
|
||||
View All Connections
|
||||
<MonacoButton variant="ghost" size="sm" block>
|
||||
Add Flag
|
||||
</MonacoButton>
|
||||
</GlassCard>
|
||||
</div>
|
||||
@@ -323,82 +370,81 @@ const profile = ref({
|
||||
name: 'Alexandra Martin',
|
||||
title: 'CEO & Founder',
|
||||
company: 'Monaco Ventures',
|
||||
bio: 'Passionate about fostering Monaco-US business relationships and cultural exchange. Leading innovation in international commerce.',
|
||||
memberId: 'MCA2021-0234',
|
||||
avatar: '/api/placeholder/200/200',
|
||||
email: 'alexandra@monacoventures.com',
|
||||
phone: '+1 (555) 123-4567',
|
||||
linkedin: 'linkedin.com/in/alexandra-martin',
|
||||
location: 'Monaco & New York',
|
||||
memberSince: '2021',
|
||||
skills: [
|
||||
'Leadership', 'Strategy', 'Investment', 'International Business',
|
||||
'Networking', 'Public Speaking', 'Venture Capital', 'M&A'
|
||||
]
|
||||
memberSince: 'January 2021',
|
||||
memberStatus: 'active',
|
||||
duesStatus: 'current',
|
||||
hasPortalAccess: true,
|
||||
lastLogin: '2 hours ago',
|
||||
loginCount: 234,
|
||||
accountCreated: 'Jan 15, 2021'
|
||||
})
|
||||
|
||||
const initials = computed(() => {
|
||||
return profile.value.name.split(' ').map(n => n[0]).join('').toUpperCase()
|
||||
})
|
||||
|
||||
const stats = ref([
|
||||
{ label: 'Years Active', value: '3' },
|
||||
{ label: 'Events Hosted', value: '24' },
|
||||
{ label: 'Connections', value: '234' },
|
||||
{ label: 'Contributions', value: '156' }
|
||||
const adminStats = ref([
|
||||
{ label: 'Total Paid', value: '$12,500' },
|
||||
{ label: 'Portal Logins', value: '234' },
|
||||
{ label: 'Events Attended', value: '18' },
|
||||
{ label: 'Days Overdue', value: '0' }
|
||||
])
|
||||
|
||||
const activities = ref([
|
||||
{
|
||||
type: 'event',
|
||||
icon: '📅',
|
||||
title: 'Attended Monaco Winter Gala',
|
||||
description: 'Networked with 50+ members at the annual gala',
|
||||
type: 'admin',
|
||||
icon: '✏',
|
||||
title: 'Profile Updated',
|
||||
description: 'Admin Jane Doe updated contact information',
|
||||
time: '2 days ago'
|
||||
},
|
||||
{
|
||||
type: 'achievement',
|
||||
icon: '🏆',
|
||||
title: 'Earned Top Contributor Badge',
|
||||
description: 'Recognized for outstanding community engagement',
|
||||
time: '1 week ago'
|
||||
type: 'payment',
|
||||
icon: '💵',
|
||||
title: 'Annual Dues Paid',
|
||||
description: 'Payment of $2,500 processed successfully',
|
||||
time: '1 month ago'
|
||||
},
|
||||
{
|
||||
type: 'connection',
|
||||
icon: '🤝',
|
||||
title: 'Connected with 5 new members',
|
||||
description: 'Expanded network in the Tech sector',
|
||||
time: '2 weeks ago'
|
||||
type: 'email',
|
||||
icon: '✉',
|
||||
title: 'Portal Invitation Sent',
|
||||
description: 'Member accepted invitation and logged in',
|
||||
time: '2 months ago'
|
||||
},
|
||||
{
|
||||
type: 'post',
|
||||
icon: '📝',
|
||||
title: 'Published article on Monaco innovation',
|
||||
description: 'Shared insights on emerging markets',
|
||||
time: '3 weeks ago'
|
||||
type: 'status',
|
||||
icon: '✅',
|
||||
title: 'Status Changed to Active',
|
||||
description: 'Member activated after payment verification',
|
||||
time: '3 months ago'
|
||||
}
|
||||
])
|
||||
|
||||
const achievements = ref([
|
||||
const memberNotes = ref([
|
||||
{
|
||||
id: 1,
|
||||
icon: '🌟',
|
||||
title: 'Founding Member',
|
||||
description: 'One of the first 100 members',
|
||||
date: 'March 2021'
|
||||
author: 'Admin Jane Doe',
|
||||
date: 'Dec 1, 2024',
|
||||
content: 'VIP member - ensure priority support and invitations to exclusive events.'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: '🎯',
|
||||
title: 'Event Champion',
|
||||
description: 'Hosted 10+ successful events',
|
||||
date: 'June 2023'
|
||||
author: 'Admin John Smith',
|
||||
date: 'Nov 15, 2024',
|
||||
content: 'Requested paper invoices instead of electronic billing. Updated preferences.'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: '💎',
|
||||
title: 'Diamond Contributor',
|
||||
description: 'Top 1% activity level',
|
||||
date: 'December 2023'
|
||||
author: 'System',
|
||||
date: 'Oct 10, 2024',
|
||||
content: 'Automatic renewal scheduled for January 2025.'
|
||||
}
|
||||
])
|
||||
|
||||
@@ -408,21 +454,32 @@ const eventsAttended = ref([
|
||||
{ id: 3, day: '20', month: 'OCT', title: 'Tech Innovation Day', role: 'Panelist' }
|
||||
])
|
||||
|
||||
const connections = ref([
|
||||
{ id: 1, name: 'John Doe', title: 'CFO at TechCorp', avatar: '/api/placeholder/40/40' },
|
||||
{ id: 2, name: 'Sarah Smith', title: 'Director at Monaco Bank', avatar: '' },
|
||||
{ id: 3, name: 'Marc Blanc', title: 'Partner at Law Firm', avatar: '/api/placeholder/40/40' },
|
||||
{ id: 4, name: 'Lisa Chen', title: 'VP Marketing', avatar: '/api/placeholder/40/40' }
|
||||
const paymentHistory = ref([
|
||||
{ id: 1, date: 'Dec 2024', type: 'Annual Dues', amount: 2500, status: 'paid' },
|
||||
{ id: 2, date: 'Nov 2024', type: 'Event Ticket', amount: 150, status: 'paid' },
|
||||
{ id: 3, date: 'Oct 2024', type: 'Special Assessment', amount: 500, status: 'paid' },
|
||||
{ id: 4, date: 'Jan 2024', type: 'Annual Dues', amount: 2500, status: 'paid' }
|
||||
])
|
||||
|
||||
const memberWarnings = ref([
|
||||
{
|
||||
id: 1,
|
||||
severity: 'info',
|
||||
icon: 'ℹ',
|
||||
title: 'VIP Member',
|
||||
description: 'Priority support required',
|
||||
date: 'Active'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
severity: 'warning',
|
||||
icon: '⚠',
|
||||
title: 'Paper Invoices',
|
||||
description: 'Requires printed billing',
|
||||
date: 'Nov 2024'
|
||||
}
|
||||
])
|
||||
|
||||
const getSkillLevel = (skill: string) => {
|
||||
const expertSkills = ['Leadership', 'Strategy', 'Investment']
|
||||
const advancedSkills = ['International Business', 'Networking']
|
||||
|
||||
if (expertSkills.includes(skill)) return 'expert'
|
||||
if (advancedSkills.includes(skill)) return 'advanced'
|
||||
return 'intermediate'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -540,7 +597,7 @@ const getSkillLevel = (skill: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
.profile-status {
|
||||
.member-status-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
@@ -548,20 +605,36 @@ const getSkillLevel = (skill: string) => {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
|
||||
&__indicator {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
&--active {
|
||||
background: #10b981;
|
||||
border-radius: 50%;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #10b981;
|
||||
&--inactive {
|
||||
background: #6b7280;
|
||||
}
|
||||
|
||||
&--suspended {
|
||||
background: #ef4444;
|
||||
}
|
||||
|
||||
&--pending {
|
||||
background: #fb923c;
|
||||
}
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
color: #27272a;
|
||||
}
|
||||
|
||||
.profile-name {
|
||||
@@ -578,11 +651,11 @@ const getSkillLevel = (skill: string) => {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.profile-bio {
|
||||
.profile-member-id {
|
||||
margin: 0 0 1.5rem;
|
||||
font-size: 1rem;
|
||||
color: #27272a;
|
||||
max-width: 600px;
|
||||
color: #6b7280;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.profile-badges {
|
||||
@@ -597,14 +670,39 @@ const getSkillLevel = (skill: string) => {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
|
||||
&--verified {
|
||||
&--active {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
&--board {
|
||||
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
|
||||
color: white;
|
||||
&--inactive {
|
||||
background: rgba(107, 114, 128, 0.1);
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
&--suspended {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
&--pending {
|
||||
background: rgba(251, 146, 60, 0.1);
|
||||
color: #fb923c;
|
||||
}
|
||||
|
||||
&--current {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
&--overdue {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
&--exempt {
|
||||
background: rgba(107, 114, 128, 0.1);
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
&--year {
|
||||
@@ -710,38 +808,99 @@ const getSkillLevel = (skill: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
.skills-cloud {
|
||||
.account-info {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skill-tag {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
.account-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
|
||||
&--expert {
|
||||
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
&--advanced {
|
||||
background: rgba(220, 38, 38, 0.15);
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
&--intermediate {
|
||||
background: rgba(107, 114, 128, 0.1);
|
||||
&__label {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
&__value {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #27272a;
|
||||
|
||||
&--active {
|
||||
color: #10b981;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.payment-history {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.payment-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.75rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
|
||||
&__date {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
&__info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.125rem;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
&__type {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #27272a;
|
||||
}
|
||||
|
||||
&__amount {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
&__status {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
|
||||
&--paid {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
&--pending {
|
||||
background: rgba(251, 146, 60, 0.1);
|
||||
color: #fb923c;
|
||||
}
|
||||
|
||||
&--overdue {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,51 +1029,63 @@ const getSkillLevel = (skill: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
.achievements-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
.notes-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.achievement-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(220, 38, 38, 0.05) 0%,
|
||||
rgba(220, 38, 38, 0.02) 100%);
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
transition: all 0.2s;
|
||||
.note-item {
|
||||
padding: 1rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid #dc2626;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 2rem;
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0 0 0.25rem;
|
||||
&__author {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #27272a;
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
&__date {
|
||||
font-size: 0.75rem;
|
||||
color: #dc2626;
|
||||
font-weight: 500;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
&__content {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
color: #27272a;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.add-note {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.note-input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 2px solid rgba(220, 38, 38, 0.1);
|
||||
border-radius: 8px;
|
||||
font-size: 0.875rem;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: #dc2626;
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,36 +1158,78 @@ const getSkillLevel = (skill: string) => {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.quick-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
.admin-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.quick-stat {
|
||||
.warnings-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.warning-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid;
|
||||
|
||||
&--info {
|
||||
background: rgba(59, 130, 246, 0.05);
|
||||
border-color: #3b82f6;
|
||||
|
||||
.warning-item__icon {
|
||||
color: #3b82f6;
|
||||
}
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background: rgba(251, 146, 60, 0.05);
|
||||
border-color: #fb923c;
|
||||
|
||||
.warning-item__icon {
|
||||
color: #fb923c;
|
||||
}
|
||||
}
|
||||
|
||||
&--error {
|
||||
background: rgba(239, 68, 68, 0.05);
|
||||
border-color: #ef4444;
|
||||
|
||||
.warning-item__icon {
|
||||
color: #ef4444;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: #dc2626;
|
||||
margin-bottom: 0.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 0.625rem;
|
||||
&__content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0 0 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #27272a;
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin: 0 0 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__date {
|
||||
font-size: 0.625rem;
|
||||
color: #a3a3a3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user