mirror of
https://github.com/Dadechin/XRoomDashboardFront.git
synced 2025-07-05 17:54:34 +00:00
add edit profile page & footer
This commit is contained in:
parent
56769fc1b8
commit
34324f447c
|
@ -2,17 +2,24 @@
|
|||
<div id="app">
|
||||
<!-- Main App Layout -->
|
||||
|
||||
<h1 class="app-title"> </h1>
|
||||
|
||||
|
||||
|
||||
<!-- The router-view here will display the active route's component -->
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
<Footer />
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Footer from '@/components/Footer.vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
Footer
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -58,14 +65,14 @@ router-view {
|
|||
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Yekan';
|
||||
src: url('@/assets/fonts/Yekan.ttf') format('truetype');
|
||||
font-family: 'IRANSans';
|
||||
src: url('@/assets/fonts/IRANSansXFaNum-Medium.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Apply the font globally */
|
||||
body {
|
||||
font-family: 'Yekan', sans-serif;
|
||||
* {
|
||||
font-family: 'IRANSans', sans-serif !important;
|
||||
}
|
||||
</style>
|
BIN
xroom-dashboard/src/assets/fonts/IRANSansXFaNum-Medium.ttf
Normal file
BIN
xroom-dashboard/src/assets/fonts/IRANSansXFaNum-Medium.ttf
Normal file
Binary file not shown.
BIN
xroom-dashboard/src/assets/fonts/IRANSansXFaNum-Medium.woff
Normal file
BIN
xroom-dashboard/src/assets/fonts/IRANSansXFaNum-Medium.woff
Normal file
Binary file not shown.
31
xroom-dashboard/src/components/Footer.vue
Normal file
31
xroom-dashboard/src/components/Footer.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<footer class="footer">
|
||||
<div class="text-wrapper-13">All Rights Reserved ©Dadechin</div>
|
||||
|
||||
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AppFooter'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.footer {
|
||||
/* margin-top: 48px; */
|
||||
text-align: center;
|
||||
color: #aaa;
|
||||
font-size: 12px;
|
||||
padding: 20px 0;
|
||||
background-color: #101010; /* Optional: Add a light background */
|
||||
}
|
||||
|
||||
|
||||
|
||||
.clip-path-group {
|
||||
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
|
@ -3,18 +3,28 @@
|
|||
<div class="sidebar">
|
||||
<div class="group">
|
||||
<!-- Profile Info -->
|
||||
<div class="overlap">
|
||||
<img class="profile" src="https://c.animaapp.com/m9nvumalUMfQbN/img/profile.png" />
|
||||
<div class="frame-2">
|
||||
<div class="text-wrapper-2">خوش آمدید...</div>
|
||||
<div class="text-wrapper-3">دانیال پژوهش کیا</div>
|
||||
<div class="logo-xroom">
|
||||
|
||||
<div class="logo">
|
||||
<div class="clip-path-group-wrapper">
|
||||
<img class="clip-path-group" src="https://c.animaapp.com/m9nvumalUMfQbN/img/clip-path-group.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notifications">
|
||||
<div class="overlap-group"><div class="text-wrapper-4">4</div></div>
|
||||
</div>
|
||||
<router-link to="/dashboard/edit-profile" class="profile-link">
|
||||
<div class="profile-container">
|
||||
<img class="profile" src="https://c.animaapp.com/m9nvumalUMfQbN/img/profile.png" />
|
||||
<div class="frame-2">
|
||||
<div class="text-wrapper-2">خوش آمدید...</div>
|
||||
<div class="text-wrapper-3">دانیال پژوهش کیا</div>
|
||||
</div>
|
||||
<div class="notifications">
|
||||
<div class="notification-badge">4</div>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
<div class="frame">
|
||||
<router-link to="/dashboard" class="nav-button" :class="{ active: isActive('/dashboard') }">
|
||||
|
@ -53,6 +63,9 @@
|
|||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -60,9 +73,9 @@ export default {
|
|||
name: 'SidebarMenu',
|
||||
methods: {
|
||||
isActive(path) {
|
||||
return this.$route.path === path
|
||||
return this.$route.path === path
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeMenu: 'dashboard'
|
||||
|
@ -72,6 +85,127 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.footer {
|
||||
margin-top: 48px;
|
||||
text-align: center;
|
||||
color: #aaa;
|
||||
font-size: 12px;
|
||||
}
|
||||
.sidebar {
|
||||
background-color: #101010;
|
||||
width: 360px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 30px 50px;
|
||||
direction: rtl;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.group {
|
||||
width: 228px;
|
||||
margin-bottom: 75px;
|
||||
}
|
||||
|
||||
.logo-xroom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.text-wrapper-13 {
|
||||
font-family: "IRANSansXFaNum-Medium", Helvetica;
|
||||
color: #e6e6e6;
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.clip-path-group-wrapper {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.clip-path-group {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.profile-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.profile-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.profile {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.frame-2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.text-wrapper-2 {
|
||||
font-family: "IRANSansXFaNum-Medium", Helvetica;
|
||||
font-weight: 500;
|
||||
color: #e6e6e6;
|
||||
font-size: 16px;
|
||||
line-height: 22.4px;
|
||||
}
|
||||
|
||||
.text-wrapper-3 {
|
||||
font-family: "IRANSansXFaNum-Medium", Helvetica;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
font-size: 19px;
|
||||
line-height: 26.6px;
|
||||
}
|
||||
|
||||
.notifications {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.notification-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-color: #dc3434;
|
||||
border-radius: 50%;
|
||||
font-family: "IRANSansXFaNum-DemiBold", Helvetica;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.frame {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 260px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
all: unset;
|
||||
|
@ -99,172 +233,17 @@ export default {
|
|||
background-color: #3a57e8;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.sidebar {
|
||||
background-color: #101010;
|
||||
width: 360px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 30px 50px;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.group {
|
||||
position: relative;
|
||||
width: 228px;
|
||||
height: 88px;
|
||||
margin-bottom: 75px;
|
||||
}
|
||||
|
||||
.overlap {
|
||||
position: absolute;
|
||||
width: 137px;
|
||||
height: 72px;
|
||||
top: 16px;
|
||||
right: 91px;
|
||||
}
|
||||
|
||||
.profile {
|
||||
position: absolute;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
top: 0;
|
||||
right: -95px;
|
||||
}
|
||||
|
||||
.frame-2 {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: right;
|
||||
gap: 8px;
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.text-wrapper-2 {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
margin-top: -1px;
|
||||
font-family: "IRANSansXFaNum-Medium", Helvetica;
|
||||
font-weight: 500;
|
||||
color: #e6e6e6;
|
||||
font-size: 16px;
|
||||
text-align: right;
|
||||
line-height: 22.4px;
|
||||
white-space: nowrap;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.text-wrapper-3 {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
font-family: "IRANSansXFaNum-Medium", Helvetica;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
font-size: 19px;
|
||||
text-align: right;
|
||||
line-height: 26.6px;
|
||||
white-space: nowrap;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.notifications {
|
||||
position: absolute;
|
||||
width: 31px;
|
||||
height: 29px;
|
||||
top: 0;
|
||||
right: 52px;
|
||||
}
|
||||
|
||||
.overlap-group {
|
||||
position: relative;
|
||||
height: 31px;
|
||||
top: 1px;
|
||||
right: -1px;
|
||||
background-color: #dc3434;
|
||||
border-radius: 15.5px;
|
||||
}
|
||||
|
||||
.text-wrapper-4 {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
right: 11px;
|
||||
font-family: "IRANSansXFaNum-DemiBold", Helvetica;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.22px;
|
||||
line-height: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.frame {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 260px;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.BTN {
|
||||
all: unset;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
width: 260px;
|
||||
height: 57px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
padding: 16px 24px;
|
||||
position: relative;
|
||||
background-color: #3a57e8;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.text-wrapper {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
margin-top: -2px;
|
||||
font-family: "IRANSansXFaNum-Medium", Helvetica;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
text-align: right;
|
||||
letter-spacing: 0;
|
||||
line-height: 25.2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.img {
|
||||
position: relative;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.button {
|
||||
all: unset;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
width: 260px;
|
||||
height: 57px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
padding: 16px 24px;
|
||||
position: relative;
|
||||
background-color: #101010;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #1e1e1e;
|
||||
}
|
||||
</style>
|
|
@ -79,10 +79,8 @@ export default {
|
|||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
/* Add your styles here */
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
|
321
xroom-dashboard/src/pages/dashboard/EditProfile.vue
Normal file
321
xroom-dashboard/src/pages/dashboard/EditProfile.vue
Normal file
|
@ -0,0 +1,321 @@
|
|||
<template>
|
||||
<SidebarMenu />
|
||||
|
||||
<div class="dashboard-page">
|
||||
<div class="content">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="header-row">
|
||||
<div class="right-actions">
|
||||
<button class="subscription-button">
|
||||
<img src="https://c.animaapp.com/m9nvumalUMfQbN/img/frame-6.svg" class="button-icon" />
|
||||
خرید اشتراک
|
||||
</button>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<span class="user-name">{{ userData.user.first_name }} {{ userData.user.last_name }}</span>
|
||||
<div class="avatar-box">
|
||||
<img class="avatar-icon" src="https://c.animaapp.com/m9nvumalUMfQbN/img/frame.svg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-title">ویرایش پروفایل</div>
|
||||
|
||||
<!-- Two-Column Form Layout -->
|
||||
<div class="profile-edit-container">
|
||||
|
||||
<!-- Left Column -->
|
||||
<div class="column">
|
||||
|
||||
<!-- VR Avatar Section -->
|
||||
<div class="form-section">
|
||||
<h3>آواتار واقعیت مجازی شما</h3>
|
||||
<p class="section-description">
|
||||
میتوانید با آپلود یک تصویر، به شخصیسازی آواتار خود، ظاهر خود را در محیط واقعیت مجازی ویرایش کنید.
|
||||
</p>
|
||||
<img :src="userAvatarUrl" class="avatar-image" />
|
||||
<div class="avatar-actions">
|
||||
<a @click="changeAvatar">تغییر آواتار</a> |
|
||||
<a @click="regenerateAvatar">ساخت مجدد آواتار</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Change Section -->
|
||||
<div class="form-section">
|
||||
<h3>تغییر رمز عبور</h3>
|
||||
<div class="form-group">
|
||||
<label for="currentPassword">رمز عبور فعلی</label>
|
||||
<input type="password" id="currentPassword" v-model="passwordForm.current_password" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newPassword">رمز عبور جدید</label>
|
||||
<input type="password" id="newPassword" v-model="passwordForm.new_password" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirmPassword">تأیید رمز عبور جدید</label>
|
||||
<input type="password" id="confirmPassword" v-model="passwordForm.confirm_password" />
|
||||
</div>
|
||||
<button class="save-btn" @click="saveProfile" :disabled="saving">
|
||||
{{ saving ? 'در حال ذخیره...' : 'ذخیره' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div class="column">
|
||||
|
||||
<!-- Profile Picture Section -->
|
||||
<div class="form-section">
|
||||
<h3>تصویر پروفایل</h3>
|
||||
<p class="section-description">
|
||||
این نماد در کنار نام شما و برای دیگران در واقعیت مجازی و در پلتفرم وب قابل مشاهده خواهد بود.
|
||||
</p>
|
||||
<img :src="userProfilePicUrl" class="profile-image" />
|
||||
<input type="file" @change="uploadProfileImage" class="upload-input" />
|
||||
</div>
|
||||
|
||||
<!-- User Info Section -->
|
||||
<div class="form-section">
|
||||
<h3>اطلاعات کاربر</h3>
|
||||
<div class="form-group">
|
||||
<label for="email">آدرس ایمیل</label>
|
||||
<input type="email" id="email" v-model="editForm.email" disabled />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="firstName">نام و نام خانوادگی</label>
|
||||
<input type="text" id="firstName" v-model="editForm.first_name" />
|
||||
<input type="text" id="lastName" v-model="editForm.last_name" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="position">جایگاه</label>
|
||||
<input type="text" id="position" placeholder="جایگاه شغلی (اختیاری)" />
|
||||
</div>
|
||||
<button class="save-btn" @click="saveProfile" :disabled="saving">
|
||||
{{ saving ? 'در حال ذخیره...' : 'ذخیره' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarMenu from '@/components/SidebarMenu.vue'
|
||||
import axios from '@/axios';
|
||||
|
||||
export default {
|
||||
name: 'EditProfile',
|
||||
components: {
|
||||
SidebarMenu
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedProfileImage: null,
|
||||
|
||||
userData: {
|
||||
user: { first_name: '', last_name: '', email: '' }
|
||||
},
|
||||
editForm: { first_name: '', last_name: '', email: '' },
|
||||
passwordForm: { current_password: '', new_password: '', confirm_password: '' },
|
||||
saving: false,
|
||||
userProfilePicUrl: 'https://i.imgur.com/QbXfV6C.png',
|
||||
userAvatarUrl: 'https://i.imgur.com/QbXfV6C.png',
|
||||
baseUrl: 'http://194.62.43.230:8000'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchUserData();
|
||||
},
|
||||
methods: {
|
||||
async fetchUserData() {
|
||||
try {
|
||||
const response = await axios.get('/getInfo');
|
||||
this.userData = response.data;
|
||||
this.editForm = {
|
||||
first_name: response.data.user.first_name,
|
||||
last_name: response.data.user.last_name,
|
||||
email: response.data.user.email,
|
||||
userAvatarUrl: this.baseUrl+ "/"+ response.data.customer.profile_img
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching user data:', error);
|
||||
}
|
||||
},
|
||||
async saveProfile() {
|
||||
this.saving = true;
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('first_name', this.editForm.first_name);
|
||||
formData.append('last_name', this.editForm.last_name);
|
||||
|
||||
if (this.selectedProfileImage) {
|
||||
formData.append('profile_img', this.selectedProfileImage);
|
||||
}
|
||||
|
||||
await axios.post(`${this.baseUrl}/editProfile/`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
|
||||
// Handle password change if filled
|
||||
if (this.passwordForm.new_password && this.passwordForm.current_password) {
|
||||
if (this.passwordForm.new_password !== this.passwordForm.confirm_password) {
|
||||
throw new Error('رمز عبور جدید و تکرار آن مطابقت ندارند');
|
||||
}
|
||||
|
||||
await axios.post(`${this.baseUrl}/resetPassword/`, {
|
||||
old_password: this.passwordForm.current_password,
|
||||
new_password: this.passwordForm.new_password
|
||||
});
|
||||
}
|
||||
|
||||
await this.fetchUserData();
|
||||
alert('تغییرات با موفقیت ذخیره شد');
|
||||
} catch (error) {
|
||||
alert(error.response?.data?.detail || error.message || 'خطا در ذخیره تغییرات');
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
changeAvatar() {
|
||||
alert('تغییر آواتار کلیک شد');
|
||||
},
|
||||
regenerateAvatar() {
|
||||
alert('ساخت مجدد آواتار کلیک شد');
|
||||
},
|
||||
uploadProfileImage(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
this.selectedProfileImage = file;
|
||||
this.userProfilePicUrl = URL.createObjectURL(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.dashboard-page {
|
||||
margin-right: 360px;
|
||||
padding: 20px;
|
||||
direction: rtl;
|
||||
font-family: IRANSansXFaNum, sans-serif;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 20px;
|
||||
padding: 35px 80px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
}
|
||||
.page-title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
margin: 24px 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.profile-edit-container {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.form-section h3 {
|
||||
margin-bottom: 8px;
|
||||
color: #222;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.section-description {
|
||||
color: #777;
|
||||
font-size: 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
color: #444;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
background: #f5f5f5;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.avatar-image,
|
||||
.profile-image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.avatar-actions a {
|
||||
color: #3a57e8;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
background: #3a57e8;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 10px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.save-btn:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.upload-input {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
|
@ -1,29 +1,38 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import SignupPage from '../pages/SignupPage.vue' // Renamed
|
||||
import LoginPage from '../pages/LoginPage.vue' // Renamed
|
||||
import SignupPage from '../pages/SignupPage.vue'
|
||||
import LoginPage from '../pages/LoginPage.vue'
|
||||
import DashboardPage from '../pages/dashboard/index.vue'
|
||||
import FilesPage from '@/pages/dashboard/files.vue'; // import the new page
|
||||
import FilesPage from '@/pages/dashboard/files.vue';
|
||||
import axios from '@/axios';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/signup',
|
||||
name: 'SignupPage', // Renamed
|
||||
name: 'SignupPage',
|
||||
component: SignupPage
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'LoginPage', // Renamed
|
||||
name: 'LoginPage',
|
||||
component: LoginPage
|
||||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'DashboardPage', // Renamed
|
||||
component: DashboardPage
|
||||
},{
|
||||
name: 'DashboardPage',
|
||||
component: DashboardPage,
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: '/dashboard/files',
|
||||
name: 'files',
|
||||
component: FilesPage, // link the files page
|
||||
component: FilesPage,
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: '/dashboard/edit-profile',
|
||||
name: 'EditProfile',
|
||||
component: () => import('@/pages/dashboard/EditProfile.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -32,32 +41,42 @@ const router = createRouter({
|
|||
routes
|
||||
})
|
||||
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
// No token, redirect to login if trying to access dashboard
|
||||
if (to.path === '/dashboard' && !token) {
|
||||
|
||||
// Check if the route requires authentication
|
||||
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
|
||||
|
||||
// If route doesn't require auth, continue
|
||||
if (!requiresAuth) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// If route requires auth but no token, redirect to login
|
||||
if (requiresAuth && !token) {
|
||||
return next('/login');
|
||||
}
|
||||
|
||||
|
||||
// If we have a token and it's an auth route, verify it
|
||||
if (token) {
|
||||
try {
|
||||
await axios.get('/getInfo');
|
||||
|
||||
// If trying to access login page while authenticated, redirect to dashboard
|
||||
if (to.path === '/login') {
|
||||
return next('/dashboard');
|
||||
}
|
||||
|
||||
return next();
|
||||
} catch (err) {
|
||||
// Invalid token, redirect to login
|
||||
// Invalid token, clear storage and redirect to login
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('user');
|
||||
return next('/login');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
export default router
|
||||
export default router
|
Loading…
Reference in New Issue
Block a user