640 lines
20 KiB
Vue
640 lines
20 KiB
Vue
<template>
|
|
<div>
|
|
<!-- Header -->
|
|
<div class="mb-6">
|
|
<h1 class="text-h4 font-weight-bold mb-2">My Profile</h1>
|
|
<p class="text-body-1 text-medium-emphasis">Manage your personal information and preferences</p>
|
|
</div>
|
|
|
|
<!-- Profile Completion Alert -->
|
|
<v-alert
|
|
v-if="profileCompletion < 100"
|
|
type="info"
|
|
variant="tonal"
|
|
class="mb-6"
|
|
closable
|
|
>
|
|
<v-alert-title>Complete Your Profile</v-alert-title>
|
|
Your profile is {{ profileCompletion }}% complete. Add more information to help other members connect with you.
|
|
<v-progress-linear
|
|
:model-value="profileCompletion"
|
|
color="info"
|
|
class="mt-2"
|
|
height="6"
|
|
rounded
|
|
/>
|
|
</v-alert>
|
|
|
|
<v-row>
|
|
<!-- Left Column - Profile Card -->
|
|
<v-col cols="12" lg="4">
|
|
<v-card elevation="2" class="mb-6">
|
|
<v-card-text class="text-center pa-6">
|
|
<!-- Avatar -->
|
|
<div class="mb-4">
|
|
<ProfileAvatar
|
|
:member-id="profile.memberId"
|
|
:first-name="profile.firstName"
|
|
:last-name="profile.lastName"
|
|
size="x-large"
|
|
:show-badge="false"
|
|
/>
|
|
<v-btn
|
|
color="error"
|
|
variant="text"
|
|
size="small"
|
|
class="mt-2"
|
|
@click="changeAvatar"
|
|
>
|
|
<v-icon start>mdi-camera</v-icon>
|
|
Change Photo
|
|
</v-btn>
|
|
</div>
|
|
|
|
<!-- Basic Info -->
|
|
<h2 class="text-h5 font-weight-bold mb-1">{{ profile.firstName }} {{ profile.lastName }}</h2>
|
|
<p class="text-body-2 text-medium-emphasis mb-3">{{ profile.title }}</p>
|
|
|
|
<!-- Member Badge -->
|
|
<v-chip
|
|
color="error"
|
|
variant="tonal"
|
|
class="mb-4"
|
|
>
|
|
<v-icon start>mdi-shield-star</v-icon>
|
|
{{ profile.memberType }} Member
|
|
</v-chip>
|
|
|
|
<!-- Stats -->
|
|
<v-row class="mt-4">
|
|
<v-col cols="4">
|
|
<div class="text-h6 font-weight-bold">{{ profile.eventsAttended }}</div>
|
|
<div class="text-caption text-medium-emphasis">Events</div>
|
|
</v-col>
|
|
<v-col cols="4">
|
|
<div class="text-h6 font-weight-bold">{{ profile.connections }}</div>
|
|
<div class="text-caption text-medium-emphasis">Connections</div>
|
|
</v-col>
|
|
<v-col cols="4">
|
|
<div class="text-h6 font-weight-bold">{{ profile.yearJoined }}</div>
|
|
<div class="text-caption text-medium-emphasis">Joined</div>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<!-- Quick Actions -->
|
|
<v-card elevation="1">
|
|
<v-card-title class="text-body-1">Quick Actions</v-card-title>
|
|
<v-list density="compact">
|
|
<v-list-item @click="downloadMemberCard">
|
|
<template v-slot:prepend>
|
|
<v-icon color="error">mdi-card-account-details</v-icon>
|
|
</template>
|
|
<v-list-item-title>Download Member Card</v-list-item-title>
|
|
</v-list-item>
|
|
<v-list-item @click="exportData">
|
|
<template v-slot:prepend>
|
|
<v-icon color="error">mdi-download</v-icon>
|
|
</template>
|
|
<v-list-item-title>Export My Data</v-list-item-title>
|
|
</v-list-item>
|
|
<v-list-item @click="privacySettings">
|
|
<template v-slot:prepend>
|
|
<v-icon color="error">mdi-shield-lock</v-icon>
|
|
</template>
|
|
<v-list-item-title>Privacy Settings</v-list-item-title>
|
|
</v-list-item>
|
|
</v-list>
|
|
</v-card>
|
|
</v-col>
|
|
|
|
<!-- Right Column - Profile Details -->
|
|
<v-col cols="12" lg="8">
|
|
<!-- Tab Navigation -->
|
|
<v-tabs
|
|
v-model="activeTab"
|
|
color="error"
|
|
class="mb-6"
|
|
>
|
|
<v-tab value="personal">
|
|
<v-icon start>mdi-account</v-icon>
|
|
Personal Info
|
|
</v-tab>
|
|
<v-tab value="contact">
|
|
<v-icon start>mdi-phone</v-icon>
|
|
Contact
|
|
</v-tab>
|
|
<v-tab value="professional">
|
|
<v-icon start>mdi-briefcase</v-icon>
|
|
Professional
|
|
</v-tab>
|
|
<v-tab value="preferences">
|
|
<v-icon start>mdi-cog</v-icon>
|
|
Preferences
|
|
</v-tab>
|
|
</v-tabs>
|
|
|
|
<!-- Tab Content -->
|
|
<v-window v-model="activeTab">
|
|
<!-- Personal Info Tab -->
|
|
<v-window-item value="personal">
|
|
<v-card elevation="1">
|
|
<v-card-title>
|
|
Personal Information
|
|
<v-spacer />
|
|
<v-btn
|
|
v-if="!editingPersonal"
|
|
variant="text"
|
|
color="error"
|
|
@click="editingPersonal = true"
|
|
>
|
|
<v-icon start>mdi-pencil</v-icon>
|
|
Edit
|
|
</v-btn>
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<v-form v-model="personalFormValid">
|
|
<v-row>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.firstName"
|
|
label="First Name"
|
|
variant="outlined"
|
|
:readonly="!editingPersonal"
|
|
:rules="editingPersonal ? [v => !!v || 'Required'] : []"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.lastName"
|
|
label="Last Name"
|
|
variant="outlined"
|
|
:readonly="!editingPersonal"
|
|
:rules="editingPersonal ? [v => !!v || 'Required'] : []"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.dateOfBirth"
|
|
label="Date of Birth"
|
|
type="date"
|
|
variant="outlined"
|
|
:readonly="!editingPersonal"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-select
|
|
v-model="profile.nationality"
|
|
label="Nationality"
|
|
:items="nationalities"
|
|
variant="outlined"
|
|
:readonly="!editingPersonal"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12">
|
|
<v-textarea
|
|
v-model="profile.bio"
|
|
label="Bio"
|
|
variant="outlined"
|
|
rows="3"
|
|
:readonly="!editingPersonal"
|
|
placeholder="Tell us about yourself..."
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</v-form>
|
|
</v-card-text>
|
|
<v-card-actions v-if="editingPersonal">
|
|
<v-spacer />
|
|
<v-btn variant="text" @click="cancelEditPersonal">Cancel</v-btn>
|
|
<v-btn
|
|
color="error"
|
|
variant="flat"
|
|
:disabled="!personalFormValid"
|
|
@click="savePersonal"
|
|
>
|
|
Save Changes
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-window-item>
|
|
|
|
<!-- Contact Tab -->
|
|
<v-window-item value="contact">
|
|
<v-card elevation="1">
|
|
<v-card-title>
|
|
Contact Information
|
|
<v-spacer />
|
|
<v-btn
|
|
v-if="!editingContact"
|
|
variant="text"
|
|
color="error"
|
|
@click="editingContact = true"
|
|
>
|
|
<v-icon start>mdi-pencil</v-icon>
|
|
Edit
|
|
</v-btn>
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<v-form v-model="contactFormValid">
|
|
<v-row>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.email"
|
|
label="Email"
|
|
type="email"
|
|
variant="outlined"
|
|
:readonly="!editingContact"
|
|
:rules="editingContact ? [v => !!v || 'Required', v => /.+@.+/.test(v) || 'Invalid email'] : []"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.phone"
|
|
label="Phone"
|
|
variant="outlined"
|
|
:readonly="!editingContact"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12">
|
|
<v-text-field
|
|
v-model="profile.address"
|
|
label="Address"
|
|
variant="outlined"
|
|
:readonly="!editingContact"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.city"
|
|
label="City"
|
|
variant="outlined"
|
|
:readonly="!editingContact"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="3">
|
|
<v-text-field
|
|
v-model="profile.state"
|
|
label="State"
|
|
variant="outlined"
|
|
:readonly="!editingContact"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="3">
|
|
<v-text-field
|
|
v-model="profile.zipCode"
|
|
label="ZIP Code"
|
|
variant="outlined"
|
|
:readonly="!editingContact"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</v-form>
|
|
</v-card-text>
|
|
<v-card-actions v-if="editingContact">
|
|
<v-spacer />
|
|
<v-btn variant="text" @click="cancelEditContact">Cancel</v-btn>
|
|
<v-btn
|
|
color="error"
|
|
variant="flat"
|
|
:disabled="!contactFormValid"
|
|
@click="saveContact"
|
|
>
|
|
Save Changes
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-window-item>
|
|
|
|
<!-- Professional Tab -->
|
|
<v-window-item value="professional">
|
|
<v-card elevation="1">
|
|
<v-card-title>
|
|
Professional Information
|
|
<v-spacer />
|
|
<v-btn
|
|
v-if="!editingProfessional"
|
|
variant="text"
|
|
color="error"
|
|
@click="editingProfessional = true"
|
|
>
|
|
<v-icon start>mdi-pencil</v-icon>
|
|
Edit
|
|
</v-btn>
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<v-form v-model="professionalFormValid">
|
|
<v-row>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.company"
|
|
label="Company"
|
|
variant="outlined"
|
|
:readonly="!editingProfessional"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.title"
|
|
label="Job Title"
|
|
variant="outlined"
|
|
:readonly="!editingProfessional"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-select
|
|
v-model="profile.industry"
|
|
label="Industry"
|
|
:items="industries"
|
|
variant="outlined"
|
|
:readonly="!editingProfessional"
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="profile.linkedin"
|
|
label="LinkedIn Profile"
|
|
variant="outlined"
|
|
:readonly="!editingProfessional"
|
|
placeholder="https://linkedin.com/in/..."
|
|
/>
|
|
</v-col>
|
|
<v-col cols="12">
|
|
<v-textarea
|
|
v-model="profile.expertise"
|
|
label="Areas of Expertise"
|
|
variant="outlined"
|
|
rows="2"
|
|
:readonly="!editingProfessional"
|
|
placeholder="e.g., Finance, Marketing, Technology..."
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</v-form>
|
|
</v-card-text>
|
|
<v-card-actions v-if="editingProfessional">
|
|
<v-spacer />
|
|
<v-btn variant="text" @click="cancelEditProfessional">Cancel</v-btn>
|
|
<v-btn
|
|
color="error"
|
|
variant="flat"
|
|
:disabled="!professionalFormValid"
|
|
@click="saveProfessional"
|
|
>
|
|
Save Changes
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-window-item>
|
|
|
|
<!-- Preferences Tab -->
|
|
<v-window-item value="preferences">
|
|
<v-card elevation="1" class="mb-4">
|
|
<v-card-title>Communication Preferences</v-card-title>
|
|
<v-card-text>
|
|
<v-switch
|
|
v-model="preferences.emailNotifications"
|
|
label="Email Notifications"
|
|
color="error"
|
|
hide-details
|
|
class="mb-3"
|
|
/>
|
|
<v-switch
|
|
v-model="preferences.eventReminders"
|
|
label="Event Reminders"
|
|
color="error"
|
|
hide-details
|
|
class="mb-3"
|
|
/>
|
|
<v-switch
|
|
v-model="preferences.newsletter"
|
|
label="Monthly Newsletter"
|
|
color="error"
|
|
hide-details
|
|
class="mb-3"
|
|
/>
|
|
<v-switch
|
|
v-model="preferences.memberUpdates"
|
|
label="Member Updates"
|
|
color="error"
|
|
hide-details
|
|
/>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<v-card elevation="1">
|
|
<v-card-title>Privacy Settings</v-card-title>
|
|
<v-card-text>
|
|
<v-switch
|
|
v-model="preferences.profileVisible"
|
|
label="Profile visible to other members"
|
|
color="error"
|
|
hide-details
|
|
class="mb-3"
|
|
/>
|
|
<v-switch
|
|
v-model="preferences.showEmail"
|
|
label="Show email in member directory"
|
|
color="error"
|
|
hide-details
|
|
class="mb-3"
|
|
/>
|
|
<v-switch
|
|
v-model="preferences.showPhone"
|
|
label="Show phone in member directory"
|
|
color="error"
|
|
hide-details
|
|
/>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer />
|
|
<v-btn
|
|
color="error"
|
|
variant="flat"
|
|
@click="savePreferences"
|
|
>
|
|
Save Preferences
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-window-item>
|
|
</v-window>
|
|
</v-col>
|
|
</v-row>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { Member } from '~/utils/types';
|
|
|
|
definePageMeta({
|
|
layout: 'member',
|
|
middleware: 'member'
|
|
});
|
|
|
|
const { user } = useAuth();
|
|
|
|
// State
|
|
const activeTab = ref('personal');
|
|
const editingPersonal = ref(false);
|
|
const editingContact = ref(false);
|
|
const editingProfessional = ref(false);
|
|
const personalFormValid = ref(true);
|
|
const contactFormValid = ref(true);
|
|
const professionalFormValid = ref(true);
|
|
|
|
// Profile data
|
|
const profile = ref({
|
|
memberId: 'MUSA-0001',
|
|
firstName: 'John',
|
|
lastName: 'Doe',
|
|
email: 'john.doe@example.com',
|
|
phone: '+1 234 567 8900',
|
|
dateOfBirth: '1985-06-15',
|
|
nationality: 'United States',
|
|
bio: 'Passionate about business and innovation. Active member of the Monaco business community.',
|
|
address: '123 Main Street',
|
|
city: 'Monaco',
|
|
state: 'MC',
|
|
zipCode: '98000',
|
|
company: 'Tech Innovations Inc.',
|
|
title: 'CEO & Founder',
|
|
industry: 'Technology',
|
|
linkedin: 'https://linkedin.com/in/johndoe',
|
|
expertise: 'Technology, Innovation, Business Strategy',
|
|
memberType: 'Premium',
|
|
eventsAttended: 24,
|
|
connections: 156,
|
|
yearJoined: '2021'
|
|
});
|
|
|
|
// Preferences
|
|
const preferences = ref({
|
|
emailNotifications: true,
|
|
eventReminders: true,
|
|
newsletter: true,
|
|
memberUpdates: false,
|
|
profileVisible: true,
|
|
showEmail: false,
|
|
showPhone: false
|
|
});
|
|
|
|
// Options
|
|
const nationalities = ref([
|
|
'United States',
|
|
'Monaco',
|
|
'France',
|
|
'Italy',
|
|
'United Kingdom',
|
|
'Germany',
|
|
'Spain',
|
|
'Other'
|
|
]);
|
|
|
|
const industries = ref([
|
|
'Technology',
|
|
'Finance',
|
|
'Healthcare',
|
|
'Real Estate',
|
|
'Hospitality',
|
|
'Manufacturing',
|
|
'Retail',
|
|
'Education',
|
|
'Other'
|
|
]);
|
|
|
|
// Computed
|
|
const profileCompletion = computed(() => {
|
|
let completed = 0;
|
|
const fields = [
|
|
profile.value.firstName,
|
|
profile.value.lastName,
|
|
profile.value.email,
|
|
profile.value.phone,
|
|
profile.value.dateOfBirth,
|
|
profile.value.nationality,
|
|
profile.value.bio,
|
|
profile.value.address,
|
|
profile.value.company,
|
|
profile.value.title
|
|
];
|
|
|
|
fields.forEach(field => {
|
|
if (field) completed += 10;
|
|
});
|
|
|
|
return completed;
|
|
});
|
|
|
|
// Methods
|
|
const changeAvatar = () => {
|
|
console.log('Change avatar');
|
|
};
|
|
|
|
const downloadMemberCard = () => {
|
|
console.log('Download member card');
|
|
};
|
|
|
|
const exportData = () => {
|
|
console.log('Export user data');
|
|
};
|
|
|
|
const privacySettings = () => {
|
|
activeTab.value = 'preferences';
|
|
};
|
|
|
|
const cancelEditPersonal = () => {
|
|
editingPersonal.value = false;
|
|
// Reset form if needed
|
|
};
|
|
|
|
const savePersonal = () => {
|
|
console.log('Saving personal info');
|
|
editingPersonal.value = false;
|
|
};
|
|
|
|
const cancelEditContact = () => {
|
|
editingContact.value = false;
|
|
};
|
|
|
|
const saveContact = () => {
|
|
console.log('Saving contact info');
|
|
editingContact.value = false;
|
|
};
|
|
|
|
const cancelEditProfessional = () => {
|
|
editingProfessional.value = false;
|
|
};
|
|
|
|
const saveProfessional = () => {
|
|
console.log('Saving professional info');
|
|
editingProfessional.value = false;
|
|
};
|
|
|
|
const savePreferences = () => {
|
|
console.log('Saving preferences', preferences.value);
|
|
};
|
|
|
|
// Load real member data on mount
|
|
onMounted(async () => {
|
|
try {
|
|
const { data: sessionData } = await $fetch<{ success: boolean; member: Member | null }>('/api/auth/session');
|
|
if (sessionData?.member) {
|
|
// Map real data to profile
|
|
profile.value.firstName = sessionData.member.first_name || profile.value.firstName;
|
|
profile.value.lastName = sessionData.member.last_name || profile.value.lastName;
|
|
profile.value.email = sessionData.member.email || profile.value.email;
|
|
profile.value.phone = sessionData.member.phone || profile.value.phone;
|
|
profile.value.nationality = sessionData.member.nationality || profile.value.nationality;
|
|
profile.value.memberId = sessionData.member.member_id || profile.value.memberId;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading member data:', error);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Custom styles if needed */
|
|
</style> |