mirror of
				https://github.com/creativetimofficial/vue-material-kit.git
				synced 2025-11-01 05:35:39 +08:00 
			
		
		
		
	
						commit
						8d7afcf5e0
					
				| @ -5,7 +5,7 @@ import { useWindowsWidth } from "../../assets/js/useWindowsWidth"; | ||||
| 
 | ||||
| // images | ||||
| import ArrDark from "@/assets/img/down-arrow-dark.svg"; | ||||
| import downArrow from "@/assets/img/down-arrow.svg"; | ||||
| 
 | ||||
| import DownArrWhite from "@/assets/img/down-arrow-white.svg"; | ||||
| 
 | ||||
| const isAuthenticated = computed(() => !!sessionStorage.getItem('access_token')); // Computed property to check if the user is authenticated | ||||
| @ -260,6 +260,22 @@ watch( | ||||
|                       </RouterLink> | ||||
| 
 | ||||
|                     </div> | ||||
| 
 | ||||
|                     <div v-if="isAuthenticated" class="position-relative"> | ||||
|                       <div | ||||
|                         class="dropdown-header text-dark font-weight-bolder d-flex align-items-center px-1" | ||||
|                       > | ||||
|                         Мои проекты | ||||
|                       </div> | ||||
|                       <RouterLink | ||||
|                         :to="{ name: 'myprojects' }" | ||||
|                         class="dropdown-item border-radius-md" | ||||
|                       > | ||||
|                         <span>Мои проекты</span> | ||||
|                       </RouterLink> | ||||
| 
 | ||||
|                     </div> | ||||
| 
 | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
| @ -312,9 +328,9 @@ watch( | ||||
|                       class="dropdown-item py-2 ps-3 border-radius-md" | ||||
|                       href="/ViewMyProfile" | ||||
|                     > | ||||
|                       <h6 class="dropdown-header text-dark font-weight-bolder d-flex justify-content-cente align-items-center p-0"> Мой Профиль </h6> | ||||
|                       <span class="text-sm">Рассказ о том, какой я классный</span> | ||||
| 
 | ||||
|                     </a> | ||||
| 
 | ||||
|                     <a | ||||
|                       class="dropdown-item py-2 ps-3 border-radius-md" | ||||
|                       href="/CreateProject" | ||||
| @ -325,7 +341,7 @@ watch( | ||||
|                         Создать проект | ||||
|                       </h6> | ||||
|                       <span class="text-sm" | ||||
|                         >Чтобы стать ещё более классным</span | ||||
|                         >Страница добавления проекта</span | ||||
|                       > | ||||
|                     </a> | ||||
|                   </li> | ||||
| @ -339,9 +355,10 @@ watch( | ||||
|                     <h6 class="dropdown-header text-dark font-weight-bolder d-flex justify-content-cente align-items-center p-0"> | ||||
|                       Мой профиль | ||||
|                     </h6> | ||||
|                     <span class="text-sm">Рассказ о том, какой я классный</span> | ||||
| 
 | ||||
|                   </a> | ||||
|                 </div> | ||||
|                  | ||||
|               </div> | ||||
|                | ||||
|             </div> | ||||
|  | ||||
| @ -15,6 +15,7 @@ import ViewMyProfile from "../views/LandingPages/Profile/AdmireProfile.vue"; | ||||
| import EditMyProfile from "../views/LandingPages/Profile/EditProfile.vue"; | ||||
| import CreateProject from "../views/LandingPages/Project/AddProject.vue"; | ||||
| import EditProject from "../views/LandingPages/Project/EditProject.vue"; | ||||
| import MyProjects from "../views/LandingPages/Project/MyProjects.vue"; | ||||
| 
 | ||||
| 
 | ||||
| const router = createRouter({ | ||||
| @ -38,6 +39,12 @@ const router = createRouter({ | ||||
|       component: Projects | ||||
|     }, | ||||
| 
 | ||||
|     { | ||||
|       path: '/myprojects', | ||||
|       name: 'myprojects', | ||||
|       component: MyProjects | ||||
|     }, | ||||
| 
 | ||||
|     { | ||||
|       path: '/CreateProject', | ||||
|       name: 'createproject', | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| import { onMounted, onUnmounted } from "vue"; | ||||
| import axios from 'axios'; | ||||
| import { ref } from "vue"; | ||||
| import NavbarDefault from "../../../examples/navbars/NavbarDefault.vue"; | ||||
| 
 | ||||
| 
 | ||||
| const searchQuery = ref(''); | ||||
| @ -29,7 +30,7 @@ onMounted(() => { | ||||
| 
 | ||||
| 
 | ||||
| <template> | ||||
| 
 | ||||
| <NavbarDefault /> | ||||
|   <div> | ||||
|      | ||||
|     <h2 class="result-header">Найдено людей: {{ searchResultUsers.length}} </h2> | ||||
| @ -37,7 +38,7 @@ onMounted(() => { | ||||
|       <div class="result-card" v-for="user in searchResultUsers" :key="user.id"> | ||||
|         <h3>{{ user.username }} with id {{ user.id }}</h3> | ||||
|         <p>{{ user.email }}</p> | ||||
|         <a :href="`http://somebodyhire.me/profile/${user.id}`">Страница пользователя</a> | ||||
|         <a :href="`/profile/${user.id}`">Страница пользователя</a> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
| @ -4,9 +4,6 @@ import { onMounted, ref, computed } from "vue"; | ||||
| import NavbarDefault from "../../../examples/navbars/NavbarDefault.vue"; | ||||
| import { useRouter } from "vue-router"; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| const isAuthenticated = computed(() => !!sessionStorage.getItem('access_token')); | ||||
| const userId = computed(() => sessionStorage.getItem('user_id')); | ||||
| const loggedUserName = computed(() => sessionStorage.getItem('username')); | ||||
| @ -14,9 +11,8 @@ const token = computed(() => sessionStorage.getItem('access_token')); | ||||
| 
 | ||||
| const profileData = ref([]); | ||||
| const router = useRouter(); | ||||
| 
 | ||||
| const debugText = ref(''); | ||||
| 
 | ||||
| const selectedImage = ref(null); | ||||
| 
 | ||||
| const getProfile = async () => { | ||||
|     const profileDataRecieved = await axios.get(`http://somebodyhire.me/api/profile/${userId.value}/`); | ||||
| @ -25,21 +21,20 @@ const getProfile = async () => { | ||||
| 
 | ||||
| const processProfileData = (data) => { | ||||
|     return { | ||||
|         ...data, | ||||
|         name: data.name || '🤷 No Name Provided', | ||||
|         location: data.location || '🌍 No Location Provided', | ||||
|         short_intro: data.short_intro || '📝 No Short Intro Provided', | ||||
|         bio: data.bio || '📘 No Bio Provided', | ||||
|         profile_image: data.profile_image || '📷 No Image Provided', | ||||
|         social_github: data.social_github || '👨💻 No Github Provided', | ||||
|         social_twitter: data.social_twitter || '🐦 No Twitter Provided', | ||||
|         social_vk: data.social_vk || '🔵 No VK Provided', | ||||
|         social_youtube: data.social_youtube || '▶️ No YouTube Provided', | ||||
|         social_website: data.social_website || '🌐 No Website Provided', | ||||
|         name: data.name || '', | ||||
|         email: data.email || '', | ||||
|         username: data.username || '', | ||||
|         location: data.location || '', | ||||
|         short_intro: data.short_intro || '', | ||||
|         bio: data.bio || '', | ||||
|         social_github: data.social_github || '', | ||||
|         social_twitter: data.social_twitter || '', | ||||
|         social_vk: data.social_vk || '', | ||||
|         social_youtube: data.social_youtube || '', | ||||
|         social_website: data.social_website || '', | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| // Axios request and response interceptors | ||||
| axios.interceptors.request.use((request) => { | ||||
|   debugText.value += '\n\nRequest:\n' + JSON.stringify(request, null, 2); | ||||
|   return request; | ||||
| @ -53,19 +48,34 @@ axios.interceptors.response.use((response) => { | ||||
|   return Promise.reject(error); | ||||
| }); | ||||
| 
 | ||||
| const updateProfile = async () => { | ||||
|     try { | ||||
|         const token = computed(() => sessionStorage.getItem('access_token')); | ||||
|         debugText.value = `Type of token: ${typeof token.value}, Value of token: ${token.value}`; | ||||
|         const headers = { 'Authorization': `Bearer ${token.value}` }; | ||||
|         await axios.put(`http://somebodyhire.me/api/profile/${userId.value}/`, profileData.value, { headers }); | ||||
|         router.push('/ViewMyProfile'); | ||||
|     } catch (error) { | ||||
|         debugText.value = `Error: ${JSON.stringify(error, null, 2)}`; | ||||
|         console.error(error); | ||||
|     } | ||||
| const onFileChange = (event) => { | ||||
|     selectedImage.value = event.target.files[0]; | ||||
|     debugText.value = `Selected image: ${selectedImage.value.name}`; | ||||
| }; | ||||
| 
 | ||||
| const updateProfile = async () => { | ||||
|   try { | ||||
|     const tokenValue = token.value; | ||||
|     const headers = { 'Authorization': `Bearer ${tokenValue}` }; | ||||
| 
 | ||||
|     // Create a new FormData object | ||||
|     const formData = new FormData(); | ||||
| 
 | ||||
|     // Append the profile data | ||||
|     Object.entries(profileData.value).forEach(([key, value]) => { | ||||
|       formData.append(key, value); | ||||
|     }); | ||||
| 
 | ||||
|     // Append the image file | ||||
|     formData.append('image', selectedImage.value); | ||||
| 
 | ||||
|     await axios.patch(`http://somebodyhire.me/api/profile/${userId.value}/`, formData, { headers }); | ||||
|     // router.push('/ViewMyProfile'); | ||||
|   } catch (error) { | ||||
|     debugText.value = `Error: ${JSON.stringify(error, null, 2)}`; | ||||
|     console.error(error); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const cancelUpdate = () => { | ||||
|     router.push('/ViewMyProfile'); | ||||
| @ -74,42 +84,30 @@ const cancelUpdate = () => { | ||||
| onMounted(async() => { | ||||
|     await getProfile(); | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <script> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <template> | ||||
|     <NavbarDefault /> | ||||
|     <div class="profile-container"> | ||||
|       <h1>Профиль пользователя {{ loggedUserName }}</h1> | ||||
|         <h1>User Profile: {{ loggedUserName }}</h1> | ||||
|         <textarea readonly v-model="debugText"></textarea> | ||||
|         <input type="file" accept="image/*" @change="onFileChange"> | ||||
|         <input type="text" v-model="profileData.username" placeholder="Username"> | ||||
|         <input type="email" v-model="profileData.email" placeholder="Email"> | ||||
|         <input type="text" v-model="profileData.name" placeholder="Имя"> | ||||
|         <input type="text" v-model="profileData.short_intro" placeholder="Краткое описание"> | ||||
|         <textarea v-model="profileData.bio" placeholder="Биография"></textarea> | ||||
|         <textarea v-model="profileData.profile_image" placeholder="Ссылка на изображение"></textarea> | ||||
|         <textarea v-model="profileData.social_github" placeholder="Ссылка на GitHub"></textarea> | ||||
|         <textarea v-model="profileData.social_twitter" placeholder="Ссылка на Twitter"></textarea> | ||||
|         <textarea v-model="profileData.social_vk" placeholder="Ссылка на VK"></textarea> | ||||
|         <textarea v-model="profileData.social_youtube" placeholder="Ссылка на YouTube"></textarea> | ||||
|         <textarea v-model="profileData.social_website" placeholder="Ссылка на сайт"></textarea> | ||||
|         <input type="text" v-model="profileData.name" placeholder="Name"> | ||||
|         <input type="text" v-model="profileData.short_intro" placeholder="Short Introduction"> | ||||
|         <textarea v-model="profileData.bio" placeholder="Biography"></textarea> | ||||
|         <textarea v-model="profileData.social_github" placeholder="GitHub Link"></textarea> | ||||
|         <textarea v-model="profileData.social_twitter" placeholder="Twitter Link"></textarea> | ||||
|         <textarea v-model="profileData.social_vk" placeholder="VK Link"></textarea> | ||||
|         <textarea v-model="profileData.social_youtube" placeholder="YouTube Link"></textarea> | ||||
|         <textarea v-model="profileData.social_website" placeholder="Website Link"></textarea> | ||||
|         <button @click="updateProfile" class="btn-submit">Submit</button> | ||||
|         <button @click="cancelUpdate" class="btn-cancel">Cancel</button> | ||||
|     </div> | ||||
| 
 | ||||
| 
 | ||||
| </template> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <style scoped> | ||||
| .profile-container { | ||||
|   display: flex; | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| import axios from 'axios'; | ||||
| import { onMounted, ref } from "vue"; | ||||
| import { useRoute } from "vue-router"; | ||||
| 
 | ||||
| import NavbarDefault from '../../../examples/navbars/NavbarDefault.vue'; | ||||
| 
 | ||||
| const profileId = ref(null); | ||||
| const route = useRoute(); | ||||
| @ -27,17 +27,45 @@ const getProfile = async () => { | ||||
| 
 | ||||
| 
 | ||||
| <template> | ||||
|     <div> | ||||
|       <h1>Профиль пользователя номер: {{ profileData.id }}</h1> | ||||
|   <NavbarDefault /> | ||||
|   <div class="profile-container"> | ||||
|     <h1>Профиль пользователя {{ loggedUserName }}</h1> | ||||
|       <h2>{{ profileData.username }}</h2> | ||||
|         <p>{{ profileData.email }}</p> | ||||
|       <p>{{ profileData.email }}</p> | ||||
|       <P>Имя: {{ profileData.name }}</P> | ||||
|       <p>Местоположение: {{ profileData.location }}</p> | ||||
|       <p>Краткое описание: {{ profileData.short_intro }}</p> | ||||
|       <p>Биография: {{ profileData.bio }}</p> | ||||
|       <p>Ссылка на изображение: {{ profileData.profile_image }}</p> | ||||
|       <p>Ссылка на GitHub: {{ profileData.social_github }}</p> | ||||
|       <p>Ссылка на Twitter: {{ profileData.social_twitter }}</p> | ||||
|       <p>Ссылка на VK: {{ profileData.social_vk }}</p> | ||||
|       <p>Ссылка на YouTube: {{ profileData.social_youtube }}</p> | ||||
|       <p>Ссылка на сайт: {{ profileData.social_website }}</p> | ||||
|        | ||||
| 
 | ||||
|     </div> | ||||
|   </template>  | ||||
|      | ||||
|   </div> | ||||
| </template>  | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <style scoped> | ||||
| </style> | ||||
| .profile-container { | ||||
| display: flex; | ||||
| flex-direction: column; | ||||
| align-items: center; | ||||
| width: 80%; | ||||
| margin: auto; | ||||
| padding: 20px; | ||||
| box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1); | ||||
| } | ||||
| 
 | ||||
| .profile-container img { | ||||
| width: 100px; | ||||
| height: 100px; | ||||
| border-radius: 50%; | ||||
| object-fit: cover; | ||||
| margin-bottom: 20px; | ||||
| } | ||||
| </style> | ||||
| @ -2,7 +2,7 @@ | ||||
| import { onMounted, onUnmounted } from "vue"; | ||||
| import axios from 'axios'; | ||||
| import { ref } from "vue"; | ||||
| 
 | ||||
| import NavbarDefault from "../../../examples/navbars/NavbarDefault.vue"; | ||||
| 
 | ||||
| const searchQuery = ref(''); | ||||
| const searchResultProjects = ref([]); | ||||
| @ -29,14 +29,14 @@ onMounted(() => { | ||||
| 
 | ||||
| 
 | ||||
| <template> | ||||
| 
 | ||||
| <NavbarDefault  /> | ||||
|   <div> | ||||
|     <h2 class="result-header">Найдено проектов: {{ searchResultProjects.length}} </h2> | ||||
|     <div class="result-grid"> | ||||
|       <div class="result-card" v-for="project in searchResultProjects" :key="project.id"> | ||||
|         <h3>{{ project.title }} with ID {{ project.id }}</h3> | ||||
|         <p>{{ project.description }}</p> | ||||
|         <a :href="`http://somebodyhire.me/project/${project.id}`">Страница проекта</a> | ||||
|         <a :href="`/project/${project.id}`">Страница проекта</a> | ||||
|       </div> | ||||
|     </div> | ||||
|      | ||||
|  | ||||
| @ -0,0 +1,181 @@ | ||||
| <script setup> | ||||
| import axios from 'axios'; | ||||
| import { onMounted, ref, computed } from "vue"; | ||||
| import NavbarDefault from "../../../examples/navbars/NavbarDefault.vue"; | ||||
| import { useRouter } from "vue-router"; | ||||
| import { useRoute } from 'vue-router'; | ||||
| 
 | ||||
| const isAuthenticated = computed(() => !!sessionStorage.getItem('access_token')); | ||||
| const userId = computed(() => sessionStorage.getItem('user_id')); | ||||
| const loggedUserName = computed(() => sessionStorage.getItem('username')); | ||||
| const token = computed(() => sessionStorage.getItem('access_token')); | ||||
| 
 | ||||
| const projectData = ref([]); | ||||
| const router = useRouter(); | ||||
| const debugText = ref(''); | ||||
| const projectId = ref(null); | ||||
| const route = useRoute(); | ||||
| 
 | ||||
| 
 | ||||
| // const getProfile = async () => { | ||||
| //     const profileDataRecieved = await axios.get(`http://somebodyhire.me/api/profile/${userId.value}/`); | ||||
| //     profileData.value = processProfileData(profileDataRecieved.data); | ||||
| // }; | ||||
| 
 | ||||
| 
 | ||||
| axios.interceptors.request.use((request) => { | ||||
|   debugText.value += '\n\nRequest:\n' + JSON.stringify(request, null, 2); | ||||
|   return request; | ||||
| }); | ||||
| 
 | ||||
| axios.interceptors.response.use((response) => { | ||||
|   debugText.value += '\n\nResponse:\n' + JSON.stringify(response, null, 2); | ||||
|   return response; | ||||
| }, (error) => { | ||||
|   debugText.value += '\n\nResponse Error:\n' + JSON.stringify(error.toJSON(), null, 2); | ||||
|   return Promise.reject(error); | ||||
| }); | ||||
| 
 | ||||
| const getProject = async () => { | ||||
|     try { | ||||
|         const projectDataRecieved = await axios.get(`http://somebodyhire.me/api/projects/${projectId.value}/`); | ||||
|         projectData.value = projectDataRecieved.data; | ||||
|     } catch (error) { | ||||
|         console.error('There was an error fetching the project data', error); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // const updateProfile = async () => { | ||||
| //     try { | ||||
| //         const token = computed(() => sessionStorage.getItem('access_token')); | ||||
| //         debugText.value = `Type of token: ${typeof token.value}, Value of token: ${token.value}`; | ||||
| //         const headers = { 'Authorization': `Bearer ${token.value}` }; | ||||
| //         await axios.patch(`http://somebodyhire.me/api/profile/${userId.value}/`, profileData.value, { headers }); | ||||
| //         router.push('/ViewMyProfile'); | ||||
| //     } catch (error) { | ||||
| //         debugText.value = `Error: ${JSON.stringify(error, null, 2)}`; | ||||
| //         console.error(error); | ||||
| //     } | ||||
| // }; | ||||
| 
 | ||||
| const updateProject = async () => { | ||||
|     try { | ||||
|         const headers = { 'Authorization': `Bearer ${token.value}` }; | ||||
|         const data = { | ||||
|             title: projectData.value.title, | ||||
|             description: projectData.value.description, | ||||
|             demo_link: projectData.value.demo_link, | ||||
|             source_link: projectData.value.source_link, | ||||
|             tags: projectData.value.tags, | ||||
| 
 | ||||
|         }; | ||||
|         const response = await axios.patch(`http://somebodyhire.me/api/projects/${projectId.value}/`, data, { headers }); | ||||
|         router.push(`/project/${response.data.id}`); | ||||
|     } catch (error) { | ||||
|         debugText.value = `Error: ${JSON.stringify(error, null, 2)}`; | ||||
|         console.error(error); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| const cancelUpdate = () => { | ||||
|     router.push('/myprojects'); | ||||
| }; | ||||
| 
 | ||||
| onMounted(async() => { | ||||
|     projectId.value = route.params.id; | ||||
|     await getProject(); | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <NavbarDefault /> | ||||
|     <div class="profile-container"> | ||||
|         <H1> Страница Редактирования Проекта {{ projectId }}</H1> | ||||
|         <div v-if="!isAuthenticated"> | ||||
|             <h1>Вы не авторизованы</h1> | ||||
|         </div> | ||||
|         <div v-else> | ||||
|             <div v-if = "userId == projectData.owner"> | ||||
|         <h1>User Profile: {{ loggedUserName }}</h1> | ||||
|         <textarea readonly v-model="debugText"></textarea> | ||||
|         <input type="text" v-model="projectData.title" placeholder="Title"> | ||||
|         <input type="text" v-model="projectData.description" placeholder="Description"> | ||||
|         <textarea v-model="projectData.demo_link" placeholder="Demo link"></textarea> | ||||
|         <textarea v-model="projectData.source_link" placeholder="Source code link"></textarea> | ||||
|         <textarea v-model="projectData.tags" placeholder="Tags"></textarea> | ||||
|         <button @click="updateProject" class="btn-submit">Update</button> | ||||
|         <button @click="cancelUpdate" class="btn-cancel">Cancel</button> | ||||
|             </div> | ||||
|             <div v-else> | ||||
|                 <h1>Вы не являетесь владельцем проекта</h1> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| 
 | ||||
| <style scoped> | ||||
| .profile-container { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   width: 80%; | ||||
|   margin: auto; | ||||
|   padding: 20px; | ||||
|   box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1); | ||||
| } | ||||
| 
 | ||||
| .profile-container img { | ||||
|   width: 100px; | ||||
|   height: 100px; | ||||
|   border-radius: 50%; | ||||
|   object-fit: cover; | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| .profile-container input, .profile-container textarea { | ||||
|   width: 100%; /* Make inputs and textareas take up the full width of the container */ | ||||
|   padding: 10px; /* Add some padding */ | ||||
|   margin-bottom: 15px; /* Add some margin */ | ||||
|   box-sizing: border-box; /* Ensure padding doesn't affect final dimensions */ | ||||
|   border: 1px solid #ccc; /* Add a border */ | ||||
|   border-radius: 5px; /* Add rounded corners */ | ||||
| } | ||||
| 
 | ||||
| /* Style for smaller screens */ | ||||
| @media (max-width: 768px) { | ||||
|   .profile-container { | ||||
|     width: 95%; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| .btn-submit { | ||||
|     color: #fff; | ||||
|     background-color: #4CAF50; | ||||
|     border: none; | ||||
|     padding: 15px 32px; | ||||
|     text-align: center; | ||||
|     text-decoration: none; | ||||
|     display: inline-block; | ||||
|     font-size: 16px; | ||||
|     margin: 4px 2px; | ||||
|     cursor: pointer; | ||||
|     border-radius: 5px; | ||||
| } | ||||
| 
 | ||||
| .btn-cancel { | ||||
|     color: #fff; | ||||
|     background-color: #f44336; | ||||
|     border: none; | ||||
|     padding: 15px 32px; | ||||
|     text-align: center; | ||||
|     text-decoration: none; | ||||
|     display: inline-block; | ||||
|     font-size: 16px; | ||||
|     margin: 4px 2px; | ||||
|     cursor: pointer; | ||||
|     border-radius: 5px; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										102
									
								
								src/views/LandingPages/Project/MyProjects.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/views/LandingPages/Project/MyProjects.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | ||||
| <script setup> | ||||
| import { onMounted, onUnmounted, computed } from "vue"; | ||||
| import axios from 'axios'; | ||||
| import { ref } from "vue"; | ||||
| import NavbarDefault from "../../../examples/navbars/NavbarDefault.vue"; | ||||
| 
 | ||||
| const searchQuery = ref(''); | ||||
| const searchResultProjects = ref([]); | ||||
| const searchResultUsers = ref([]); | ||||
| 
 | ||||
| const userId = computed(() => sessionStorage.getItem('user_id')); | ||||
| const username = computed(() => sessionStorage.getItem('username')); | ||||
| 
 | ||||
| const search = async () => { | ||||
|   try { | ||||
|     const projectsResponse = await axios.get(`http://somebodyhire.me/api/search/projects/?search_query=${searchQuery.value}`); | ||||
|     searchResultProjects.value = projectsResponse.data; | ||||
| 
 | ||||
|     const usersResponse = await axios.get(`http://somebodyhire.me/api/search/profiles/?search_query=${searchQuery.value}`); | ||||
|     searchResultUsers.value = usersResponse.data; | ||||
|   } catch (error) { | ||||
|     console.error('There was an error fetching the search results', error); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   search(); | ||||
| }); | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <template> | ||||
| <NavbarDefault  /> | ||||
|   <div> | ||||
|     <h2 class="result-header">Проекты пользователя {{ username }}</h2> | ||||
|     <div class="result-grid"> | ||||
|       <div class="result-card" v-for="project in searchResultProjects" :key="project.id"> | ||||
|         <div v-if = "project.owner == userId" class="project-owner-note"> | ||||
|         <h3>{{ project.title }} with ID {{ project.id }}</h3> | ||||
|         <p>{{ project.description }}</p> | ||||
|         <p>Создатель: {{ project.owner }} </p> | ||||
|         <a :href="`/project/${project.id}`">Страница проекта</a> | ||||
|         <p></p> | ||||
|         <a :href="`/editproject/${project.id}`">Редактирование проекта</a> | ||||
| 
 | ||||
|     </div>   | ||||
|     </div> | ||||
|     </div> | ||||
|      | ||||
|   </div> | ||||
| 
 | ||||
| 
 | ||||
|   </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| .searchBar { | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| .result-header { | ||||
|   color: #fff; | ||||
|   background-color: #333; | ||||
|   padding: 10px; | ||||
|   text-align: center; | ||||
|   margin-top: 20px; | ||||
| } | ||||
| 
 | ||||
| .result-grid { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
|   justify-content: space-between; | ||||
| } | ||||
| 
 | ||||
| .result-card { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   background-color: rgba(92, 90, 90, 0.5); | ||||
|   padding: 10px; | ||||
|   margin: 10px; | ||||
|   border-radius: 10px; | ||||
|   width: calc(100% / 3 - 20px); | ||||
|   box-sizing: border-box; | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 992px) { | ||||
|   .result-card { | ||||
|     width: calc(100% / 2 - 20px); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 600px) { | ||||
|   .result-card { | ||||
|     width: 100%; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| </style> | ||||
| @ -1,13 +1,19 @@ | ||||
| <script setup> | ||||
| import axios from 'axios'; | ||||
| import { onMounted, ref } from "vue"; | ||||
| import { onMounted, ref, computed } from "vue"; | ||||
| import { useRoute } from "vue-router"; | ||||
| import NavbarDefault from '../../../examples/navbars/NavbarDefault.vue'; | ||||
| 
 | ||||
| 
 | ||||
| const projectId = ref(null); | ||||
| const route = useRoute(); | ||||
| const projectData = ref([]); | ||||
| 
 | ||||
| const isAuthenticated = computed(() => !!sessionStorage.getItem('access_token')); | ||||
| const userId = computed(() => sessionStorage.getItem('user_id')); | ||||
| const loggedUserName = computed(() => sessionStorage.getItem('username')); | ||||
| const token = computed(() => sessionStorage.getItem('access_token')); | ||||
| 
 | ||||
| onMounted(async() => { | ||||
|     projectId.value = route.params.id; | ||||
|     await getProject(); | ||||
| @ -30,27 +36,89 @@ const getProject = async () => { | ||||
| 
 | ||||
| 
 | ||||
| <template> | ||||
|     <div v-if="projectData"> | ||||
|       <h1>Проект номер: {{ projectData.id }}</h1> | ||||
|       <h2>{{ projectData.title }}</h2> | ||||
|       <p>{{ projectData.description }}</p> | ||||
|       <img :src="projectData.featured_image" alt="Featured image"> | ||||
|       <p v-if="projectData.demo_link">Demo Link: <a :href="projectData.demo_link">{{ projectData.demo_link }}</a></p> | ||||
|       <p v-if="projectData.source_link">Source Link: <a :href="projectData.source_link">{{ projectData.source_link }}</a></p> | ||||
|       <p>Total Votes: {{ projectData.vote_total }}</p> | ||||
|       <p>Vote Ratio: {{ projectData.vote_ratio }}</p> | ||||
|       <p>Created On: {{ new Date(projectData.created).toLocaleDateString() }}</p> | ||||
|       <p>Owner ID: {{ projectData.owner }}</p> | ||||
|       <p>Tags:  | ||||
|         <span v-for="(tag, index) in projectData.tags" :key="index"> | ||||
|   <NavbarDefault /> | ||||
|     <div v-if="projectData" class="project-container"> | ||||
|       <h1 class="project-title">Проект номер: {{ projectData.id }}</h1> | ||||
|       <h2 class="project-subtitle">{{ projectData.title }}</h2> | ||||
|       <div v-if = "projectData.owner == userId" class="project-owner-note"> | ||||
|         <a :href="`/editproject/${projectData.id}`">Редактирование проекта</a> | ||||
|       </div> | ||||
|       <p class="project-description">{{ projectData.description }}</p> | ||||
|       <img class="project-image" :src="projectData.featured_image" alt="Featured image"> | ||||
|       <p v-if="projectData.demo_link" class="project-demo-link">Demo Link: <a :href="projectData.demo_link">{{ projectData.demo_link }}</a></p> | ||||
|       <p v-if="projectData.source_link" class="project-source-link">Source Link: <a :href="projectData.source_link">{{ projectData.source_link }}</a></p> | ||||
|       <p class="project-votes">Total Votes: {{ projectData.vote_total }}</p> | ||||
|       <p class="project-vote-ratio">Vote Ratio: {{ projectData.vote_ratio }}</p> | ||||
|       <p class="project-created">Created On: {{ new Date(projectData.created).toLocaleDateString() }}</p> | ||||
|       <p class="project-owner-id">Owner ID: {{ projectData.owner }}</p> | ||||
|       <p class="project-tags">Tags:  | ||||
|         <span v-for="(tag, index) in projectData.tags" :key="index" class="project-tag"> | ||||
|           {{ tag }}<span v-if="index < projectData.tags.length - 1">, </span> | ||||
|         </span> | ||||
|       </p> | ||||
|     </div> | ||||
|   </template>  | ||||
| 
 | ||||
| </template>  | ||||
| 
 | ||||
| 
 | ||||
| <style scoped> | ||||
| .project-container { | ||||
|   margin: 20px; | ||||
|   padding: 20px; | ||||
|   border: 1px solid #ddd; | ||||
|   border-radius: 5px; | ||||
|   background-color: #f9f9f9; | ||||
| } | ||||
| 
 | ||||
| .project-title { | ||||
|   font-size: 24px; | ||||
|   font-weight: bold; | ||||
|   color: #333; | ||||
| } | ||||
| 
 | ||||
| .project-subtitle { | ||||
|   font-size: 18px; | ||||
|   color: #555; | ||||
| } | ||||
| 
 | ||||
| .project-owner-note { | ||||
|   font-size: 16px; | ||||
|   color: #777; | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
| 
 | ||||
| .project-description { | ||||
|   font-size: 16px; | ||||
|   color: #333; | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
| 
 | ||||
| .project-image { | ||||
|   width: 100%; | ||||
|   height: auto; | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
| 
 | ||||
| .project-demo-link, .project-source-link { | ||||
|   font-size: 16px; | ||||
|   color: #337ab7; | ||||
|   text-decoration: none; | ||||
| } | ||||
| 
 | ||||
| .project-votes, .project-vote-ratio, .project-created, .project-owner-id { | ||||
|   font-size: 14px; | ||||
|   color: #777; | ||||
| } | ||||
| 
 | ||||
| .project-tags { | ||||
|   font-size: 14px; | ||||
|   color: #333; | ||||
| } | ||||
| 
 | ||||
| .project-tag { | ||||
|   background-color: #f0f0f0; | ||||
|   border-radius: 5px; | ||||
|   padding: 2px 5px; | ||||
|   margin: 2px; | ||||
| } | ||||
| </style> | ||||
|    | ||||
| @ -15,9 +15,12 @@ const username = ref(''); | ||||
| const password = ref(''); | ||||
| const errorMessage = ref(''); | ||||
| 
 | ||||
| const isAuthenticated = computed(() => !!sessionStorage.getItem('access_token')); // Computed property to check if the user is authenticated | ||||
| //Это блок для работы с хранилищем сессии | ||||
| const isAuthenticated = computed(() => !!sessionStorage.getItem('access_token'));  | ||||
| const userId = computed(() => sessionStorage.getItem('user_id')); | ||||
| const loggedUserName = computed(() => sessionStorage.getItem('username')); | ||||
| const isStaff = computed(() => sessionStorage.getItem('is_staff')); | ||||
| const token = computed(() => sessionStorage.getItem('token')); | ||||
| 
 | ||||
| const login = async () => { | ||||
|   if (!username.value || !password.value) { | ||||
| @ -34,10 +37,11 @@ const login = async () => { | ||||
| 
 | ||||
|     try { | ||||
|       const response = await axios.post(url, body, { headers }); | ||||
|       // Removed debug information from output | ||||
|       sessionStorage.setItem('access_token', response.data.access);  | ||||
|       sessionStorage.setItem('username', username.value); // Save username in sessionStorage | ||||
|       sessionStorage.setItem('user_id', response.data.id); // Save the user id in sessionStorage | ||||
|       sessionStorage.setItem('username', username.value);  | ||||
|       sessionStorage.setItem('user_id', response.data.id);  | ||||
|       sessionStorage.setItem('is_staff', response.data.is_staff);  | ||||
|       sessionStorage.setItem('token', response.data.token); | ||||
|       location.reload(); // Refresh page | ||||
|     } catch (error) { | ||||
|       if (error.response) { | ||||
| @ -55,6 +59,8 @@ const logout = () => { | ||||
|   sessionStorage.removeItem('access_token'); | ||||
|   sessionStorage.removeItem('username'); // Also clear the username from sessionStorage | ||||
|   sessionStorage.removeItem('user_id'); | ||||
|   sessionStorage.setItem('is_staff', false); | ||||
|   sessionStorage.removeItem('token'); | ||||
|   location.reload(); // Refresh page after logout | ||||
| }; | ||||
| 
 | ||||
| @ -120,7 +126,16 @@ export default { | ||||
|                   <div> | ||||
|                     <div v-if="isAuthenticated"> | ||||
|                         <!-- This will only be displayed if the user is authenticated --> | ||||
|                         <p>Вы вошли в аккаунт {{ loggedUserName }}, ваш ID {{ userId }}</p> | ||||
|                         <p>Вы вошли в аккаунт {{ loggedUserName }}</p> | ||||
|                         <p> | ||||
|                           <a href="/ViewMyProfile">Перейти в профиль.</a> | ||||
|                         </p> | ||||
|                         <!-- Это должно быть видно только админам --> | ||||
|                         <div v-if="isStaff"> | ||||
|                             <p> | ||||
|                             <a href="/admin">Перейти в панель администратора.</a> | ||||
|                             </p> | ||||
|                           </div> | ||||
|                         <button @click="logout">Выход</button> | ||||
|                     </div> | ||||
| 
 | ||||
| @ -128,9 +143,6 @@ export default { | ||||
|                         <!-- This will be displayed if the user is not authenticated --> | ||||
|                         <p>Пожалуйста, введите логин и пароль</p> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|                       <div> | ||||
|                         <input v-model="username" type="text" placeholder="Имя пользователя" /> | ||||
|                       </div> | ||||
| @ -151,16 +163,7 @@ export default { | ||||
| 
 | ||||
|                     </div> | ||||
| 
 | ||||
|                     </div> | ||||
| 
 | ||||
| 
 | ||||
|               <div v-if="errorMessage"> | ||||
|                       <p>{{ errorMessage }}</p> | ||||
|               </div> | ||||
|   </div> | ||||
|                    | ||||
| 
 | ||||
|                   <p class="mt-4 text-sm text-center"> | ||||
|                     <p class="mt-4 text-sm text-center"> | ||||
|                     Нет аккаунта? | ||||
|                     <a | ||||
|                       href="/register" | ||||
| @ -170,12 +173,24 @@ export default { | ||||
|                   </p> | ||||
|                   <p class="mt-4 text-sm text-center"> | ||||
|                     | ||||
|                     <a | ||||
|                       href="/forgot" | ||||
|                       class="text-success text-gradient font-weight-bold" | ||||
|                       >Забыли пароль</a | ||||
|                     > | ||||
|                   </p> | ||||
|                    <a | ||||
|                      href="/forgot" | ||||
|                      class="text-success text-gradient font-weight-bold" | ||||
|                      >Забыли пароль</a | ||||
|                    > | ||||
|                  </p> | ||||
|                        | ||||
|                     </div> | ||||
| 
 | ||||
| 
 | ||||
|               <div v-if="errorMessage"> | ||||
|                       <p>{{ errorMessage }}</p> | ||||
|               </div> | ||||
|   </div> | ||||
|                    | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|                 </form> | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
| @ -1,18 +1,3 @@ | ||||
| <style scoped> | ||||
| .project-container { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
|   justify-content: space-between; | ||||
| } | ||||
| .project-card { | ||||
|   flex-basis: calc(33.33% - 1em); /* 1em is for margin */ | ||||
|   margin: 0.5em; | ||||
|   box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); | ||||
|   padding: 1em; | ||||
|   box-sizing: border-box; | ||||
| } | ||||
| </style> | ||||
| 
 | ||||
| <script setup> | ||||
| import { onMounted, onUnmounted, computed, } from "vue"; | ||||
| 
 | ||||
| @ -124,4 +109,17 @@ export default { | ||||
| </template> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <style scoped> | ||||
| .project-container { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
|   justify-content: space-between; | ||||
| } | ||||
| .project-card { | ||||
|   flex-basis: calc(33.33% - 1em); /* 1em is for margin */ | ||||
|   margin: 0.5em; | ||||
|   box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); | ||||
|   padding: 1em; | ||||
|   box-sizing: border-box; | ||||
| } | ||||
| </style> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 lazyseal
						lazyseal