Delete SPA service files, add index.html stabfile, set the docker and docker-compose.yaml files.

This commit is contained in:
valitovgaziz
2025-05-07 15:38:53 +05:00
parent edaaaead2e
commit e81cd6a358
48 changed files with 140 additions and 5435 deletions
+1 -1
View File
@@ -16,6 +16,6 @@ GOOSE_DRIVER=postgres
GOOSE_DBSTRING='user=postgres dbname=postgres sslmode=disable' GOOSE_DBSTRING='user=postgres dbname=postgres sslmode=disable'
GOOSE_MIGRATION_DIR=migrations GOOSE_MIGRATION_DIR=migrations
# SPA on VUE.js # FRONTEND SPA
INNERPORT=80 INNERPORT=80
OUTERPORT=8088 OUTERPORT=8088
-22
View File
@@ -1,22 +0,0 @@
# Postgres
PGHOST=888
PGPORT=888
PGUSER=888
PGPASSWORD=888
PGDATABASE=888
SSLmode=disable
PGURL='888'
# SERVER
SERVER_PORT=888
SECRET_KEY=888
# MIGRATOR
MIGRATOR_PORT=888
GOOSE_DRIVER=888
GOOSE_DBSTRING='user=888 dbname=888 sslmode=888'
GOOSE_MIGRATION_DIR=migrations
# SPA on VUE.js
INNERPORT=888
OUTERPORT=888
+7 -28
View File
@@ -1,32 +1,11 @@
# Используем официальный образ Node.js на основе Alpine Linux # Используем официальный образ Nginx
FROM node:16-alpine as build-stage FROM nginx:alpine
# Устанавливаем рабочую директорию внутри контейнера # Копируем index.html в папку Nginx
WORKDIR /app COPY index.html /usr/share/nginx/html/
# Копируем package.json и package-lock.json (если есть) # (Опционально) Можно заменить конфиг Nginx
COPY package*.json ./ # COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
# Устанавливаем зависимости # Порт, который будет слушать Nginx
RUN npm install
# Копируем все файлы проекта в рабочую директорию
COPY . .
# Собираем проект
RUN npm run build
# Используем легковесный образ Nginx для serving статики
FROM nginx:stable-alpine as production-stage
# Копируем собранные файлы из предыдущего этапа в директорию Nginx
COPY --from=build-stage /app/dist /usr/share/nginx/html
# Копируем конфигурацию Nginx (если нужно)
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# Открываем порт 80
EXPOSE 80 EXPOSE 80
# Запускаем Nginx
CMD ["nginx", "-g", "daemon off;"]
-29
View File
@@ -1,29 +0,0 @@
# spa
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Compile and Minify for Production
```sh
npm run build
```
+130 -10
View File
@@ -1,14 +1,134 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang=""> <html lang="ru">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/src/components/images/logo150x150.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/src/assets/main.css"> <title>ЯлАрба - Туристический агрегатор</title>
<title>YalArba</title> <style>
</head> body {
<body> font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
<div id="app"></div> line-height: 1.6;
<script type="module" src="/src/main.js"></script> color: #333;
</body> max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f7fa;
}
header {
background-color: #1e88e5;
color: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
text-align: center;
}
h1 {
margin: 0;
font-size: 2.2em;
}
h2 {
color: #1e88e5;
border-bottom: 2px solid #1e88e5;
padding-bottom: 5px;
margin-top: 30px;
}
.developer {
background-color: #e3f2fd;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
}
.roadmap {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.phase {
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px dashed #ccc;
}
.phase:last-child {
border-bottom: none;
}
footer {
text-align: center;
margin-top: 40px;
color: #666;
font-size: 0.9em;
}
</style>
</head>
<body>
<header>
<h1>ЯлАрба</h1>
<p>Инновационный агрегатор мест отдыха с ИИ-планировщиком маршрутов</p>
</header>
<section>
<h2>О проекте</h2>
<p>ЯлАрба — это супер-приложение для путешественников, объединяющее в себе:</p>
<ul>
<li>Агрегатор мест отдыха (отели, экскурсии, рестораны)</li>
<li>Маркетплейс туров и локальных сувениров</li>
<li>ИИ-планировщик маршрутов с учетом здоровья пользователя</li>
<li>Систему бронирования и покупки билетов</li>
</ul>
<p>Наша миссия — сделать планирование путешествий простым, персонализированным и доступным.</p>
</section>
<div class="developer">
<h2>Разработчик</h2>
<p><strong>Валитов Газиз</strong></p>
<p>Full-stack разработчик с опытом в travel-tech проектах. Специализация: Python/Django, React, системы рекомендаций на основе ИИ.</p>
</div>
<section class="roadmap">
<h2>План разработки</h2>
<div class="phase">
<h3>Этап 1: MVP (0-6 месяцев)</h3>
<ul>
<li>База мест отдыха (1000+ позиций)</li>
<li>Интеграция с API бронирования (Booking.com, Ostrovok)</li>
<li>Прототип ИИ-рекомендаций</li>
<li>Мобильная версия (PWA)</li>
</ul>
</div>
<div class="phase">
<h3>Этап 2: Монетизация (6-12 месяцев)</h3>
<ul>
<li>Система комиссий (10-15% с бронирований)</li>
<li>Платные подписки (PRO-аккаунт)</li>
<li>Партнерская программа для гидов</li>
</ul>
</div>
<div class="phase">
<h3>Этап 3: Масштабирование (12-24 месяца)</h3>
<ul>
<li>Выход на рынки СНГ (Казахстан, Узбекистан)</li>
<li>Полноценный ИИ-ассистент с рекомендациями по здоровью</li>
<li>Интеграция с сервисами аренды авто</li>
</ul>
</div>
</section>
<section>
<h2>Технологический стек</h2>
<ul>
<li><strong>Frontend:</strong> React.js, TypeScript, PWA</li>
<li><strong>Backend:</strong> Python/Django, PostgreSQL</li>
<li><strong>AI:</strong> NLP-модели для обработки запросов, рекомендательные системы</li>
<li><strong>DevOps:</strong> Docker, Kubernetes, Yandex Cloud</li>
</ul>
</section>
<footer>
<p>© 2024 ЯлАрба. Все права защищены.</p>
<p>Контакты: gaziz.valitov@yalarba.travel</p>
</footer>
</body>
</html> </html>
-8
View File
@@ -1,8 +0,0 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
-3897
View File
File diff suppressed because it is too large Load Diff
-27
View File
@@ -1,27 +0,0 @@
{
"name": "spa",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.9",
"body-parser": "^1.20.3",
"cors": "^2.8.5",
"express": "^4.21.2",
"pg": "^8.13.1",
"spa": "file:",
"vue": "^3.5.13",
"vue-material-design-icons": "^5.3.1",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.0.5",
"vite-plugin-vue-devtools": "^7.6.8"
}
}
-12
View File
@@ -1,12 +0,0 @@
<script setup>
import { RouterLink, RouterView } from 'vue-router'
</script>
<template>
<div class="main">
<RouterView />
</div>
</template>
<style>
</style>
-42
View File
@@ -1,42 +0,0 @@
/* Общие стили */
html,
body {
margin: 0;
padding: 0;
height: fit-content;
width: 100%;
font-family: Arial, sans-serif;
}
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.main {
margin: 2rem;
min-height: 90vh;
min-width: 320px;
max-width: 1024px;
height: 100%;
width: 100%;
border-radius: 1rem;
display: flex;
align-items: center;
flex-direction: column;
justify-content: space-between;
}
/* Адаптивные стили для мобильных устройств */
@media only screen and (max-width: 600px) {
.main {
margin: 0.5rem;
min-height: 80vh;
min-width: auto;
max-width: none;
width: 95%; /* Можно сделать еще меньше, если требуется */
border-radius: 0.5rem;
}
}
-53
View File
@@ -1,53 +0,0 @@
<template>
<div class="footer">
<div class="navigation" v-on:click="goBack">
<img src="./images/icons/arrow_back_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="back">
</div>
<router-link to="/">
<img src="./images/icons/home_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="home">
</router-link>
<div class="navigation" v-on:click="goForward">
<img src="./images/icons/arrow_forward_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="forward">
</div>
</div>
</template>
<style scoped>
.footer {
height: 3rem;
background-color: darkmagenta;
border-radius: 1rem;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 2rem;
margin: 0 0 0.5rem 0;
}
.navigation {
padding: 0 1rem;
margin: 0 1rem;
}
.navigation:hover {
cursor: pointer;
background-color: rgb(112, 48, 171);
}
</style>
<script>
import MenuIcon from 'vue-material-design-icons/Menu.vue';
export default {
components: {
MenuIcon,
},
name: 'footer',
methods: {
goBack() {
this.$router.go(-1);
},
goForward() {
this.$router.go(1);
}
}
};
</script>
-55
View File
@@ -1,55 +0,0 @@
<template>
<div class="about">
<h1>Freedom & Adventure</h1>
<p>Приложение для туристов</p>
<p>Прошу потдержать проект</p>
<button v-on:click="donat" class="donat-coffee">Донат на кофе</button>
<img src="../images/photo_2025-01-25_05-57-24.jpg" alt="Донат QRcod" class="QRcod">
</div>
</template>
<style scoped>
.about {
background-color: #8B4513; /* коричневый цвет */
height: fit-content;
display: flex;
flex-direction: column;
align-items: center;
filter: none;
padding: 1rem;
border-radius: 1rem;
margin: 1rem;
}
.donat-coffee {
margin: 1rem;
background-color: rgb(89, 219, 109);
box-shadow: 1px 4px 8px 0 rgba(27, 45, 37, 0.2);
border-radius: 1rem;
}
.donat-coffee:hover {
cursor: pointer;
background-color: rgb(10, 150, 50);
color: white;
box-shadow: 1px 4px 8px rgb(10, 150, 50);
}
.QRcod {
height: 200px;
display: flexbox;
border-radius: 0.5rem;
}
</style>
<script>
export default {
methods: {
donat() {
window.open("https://www.tinkoff.ru/rm/r_fFXNVwPxmH.VMEQXkfWxO/Ho4MN83486", "_blank");
}
}
}
</script>
-58
View File
@@ -1,58 +0,0 @@
<template>
<div v-if="loading">Загрузка...</div>
<ul v-else-if="error">
Произошла ошибка: {{ error }}
</ul>
<ul v-else>
<li v-for="commit in commits" :key="commit.sha">
<strong>{{ commit.commit.author.date.slice(0, 11) }}:</strong> {{ commit.commit.message }} <i>{{ commit.author.login }}</i>
</li>
</ul>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
loading: true,
commits: [],
error: null,
};
},
mounted() {
this.fetchCommits();
},
methods: {
async fetchCommits() {
try {
const response = await axios.get('https://api.github.com/repos/valitovgaziz/spa_/commits', {
headers: {
Accept: 'application/vnd.github.v3+json',
},
});
this.commits = response.data;
} catch (err) {
this.error = err.message || 'Произошла неизвестная ошибка';
} finally {
this.loading = false;
}
},
},
};
</script>
<style scoped>
ul {
max-width: 1024px;
list-style-type: none;
padding: 0;
}
li {
margin: 0.5rem 0;
white-space: pre-wrap;
width: 100%;
}
</style>x``
-61
View File
@@ -1,61 +0,0 @@
<template>
<div class="developers">
<!-- Заголовок -->
<h1 class="title">Разработчики</h1>
<!-- Список разработчиков -->
<ul class="developer-list">
<li v-for="dev in developers" :key="dev.name" class="developer-item">
<a :href="dev.profileUrl" target="_blank" rel="noopener noreferrer">
{{ dev.nickname }} ({{ dev.name }})
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'DevelopersList',
data() {
return {
developers: [
{ nickname: 'valitovgaziz', name: 'Валитов Газиз', profileUrl: 'https://github.com/valitovgaziz' }
// Добавьте больше разработчиков по аналогии...
]
}
}
}
</script>
<style scoped>
.developers {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.title {
text-align: center;
font-size: 24px;
margin-bottom: 20px;
}
.developer-list {
list-style-type: none;
padding-left: 0;
}
.developer-item {
margin-bottom: 10px;
}
.developer-item a {
color: #007bff;
text-decoration: none;
}
.developer-item a:hover {
text-decoration: underline;
}
</style>
-118
View File
@@ -1,118 +0,0 @@
<template>
<div class="reviews-container">
<h2>Отзывы о приложении</h2>
<!-- Форма для добавления отзыва -->
<div class="add-review">
<textarea v-model="newReview" placeholder="Напишите ваш отзыв..." rows="4"></textarea>
<button @click="submitReview" :disabled="!newReview.trim()">Оставить отзыв</button>
</div>
<!-- Список последних отзывов -->
<div class="reviews-list">
<div v-for="review in reviews" :key="review.id" class="review-item">
<p>{{ review.text }}</p>
<small>{{ formatDate(review.created_at) }}</small>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
reviews: [], // Список отзывов
newReview: '', // Текст нового отзыва
};
},
mounted() {
this.loadReviews(); // Загружаем отзывы при монтировании компонента
},
methods: {
// Загрузка отзывов из базы данных
async loadReviews() {
try {
const response = await axios.get('http://213.108.4.63:3000/api/reviews');
this.reviews = response.data.slice(0, 5); // Показываем только последние 5 отзывов
} catch (error) {
console.error('Ошибка при загрузке отзывов:', error);
}
},
// Отправка нового отзыва
async submitReview() {
if (!this.newReview.trim()) return;
try {
const response = await axios.post('http://213.108.4.63:3000/api/reviews', {
text: this.newReview,
});
this.reviews.unshift(response.data); // Добавляем новый отзыв в начало списка
this.newReview = ''; // Очищаем текстовое поле
if (this.reviews.length > 5) {
this.reviews.pop(); // Удаляем самый старый отзыв, если их больше 5
}
} catch (error) {
console.error('Ошибка при отправке отзыва:', error);
}
},
// Форматирование даты
formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString(); // Форматируем дату в удобный формат
},
},
};
</script>
<style scoped>
.reviews-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.add-review textarea {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid #ccc;
}
.add-review button {
padding: 10px 20px;
background-color: #42b983;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.add-review button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.reviews-list {
margin-top: 20px;
}
.review-item {
background: #f9f9f9;
padding: 15px;
border-radius: 5px;
margin-bottom: 10px;
}
.review-item p {
margin: 0;
}
.review-item small {
color: #666;
font-size: 0.9em;
}
</style>
-50
View File
@@ -1,50 +0,0 @@
<template>
<div class="container">
<!-- Заголовок -->
<h1>Trip & Freedom & Adventure</h1>
<!-- Основной контент -->
<p>Разработка приложения Trip&Freedom&Adventure представляет собой вклад в создание полезного продукта для общества. Данная работа играет важную роль в профессиональном росте и развитии. Миссия этого приложения заключается в помощи пользователям в открытии новых горизонтов, планировании путешествий и наслаждении приключениями без излишних хлопот. Приложение принесет пользу как пользователям, так и мне лично, позволяя углубить мои знания в программировании, дизайне интерфейсов и разработке мобильных приложений. Участие в этом проекте также предоставляет возможность проявить креативность и воплотить идеи, способные улучшить качество жизни людей. Ежедневная работа над приложением приближает нас к созданию удобного и функционального продукта, который станет незаменимым инструментом для путешественников. Этот проект вдохновляет на постоянное движение вперёд, обучение новым навыкам и внесение вклада в технологическое и общественное развитие.</p>
</div>
</template>
<script setup lang="ts">
// Здесь может быть логика вашего компонента, если требуется
</script>
<style scoped>
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
text-align: center;
font-size: 32px;
margin-bottom: 30px;
}
p {
line-height: 1.6;
font-size: 18px;
}
.additional-content {
margin-top: 40px;
}
@media screen and (max-width: 600px) {
.container {
padding: 10px;
}
h1 {
font-size: 24px;
}
p {
font-size: 16px;
}
}
</style>
-70
View File
@@ -1,70 +0,0 @@
<template>
<div class="header">
<router-link to="/about" id="rl-about"><img src="../images/logo150x150.png" alt="logo"></router-link>
<router-link to="/filosofy" id="rl-filosofy">Trip*Freedom*Adventure</router-link>
<Menu />
</div>
</template>
<style scoped>
.header {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border-radius: 1rem;
background-color: aquamarine;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 3rem;
}
#rl-about {
display: flex;
flex-direction: row;
align-items: center;
justify-items: center;
border-radius: 50%;
padding: 1rem;
height: 1rem;
width: 1rem;
}
#rl-about:hover {
cursor: pointer;
background-color: rgb(85, 117, 117);
}
img {
height: 1.5rem;
padding: 0 1rem 0 0;
}
#rl-filosofy {
all: unset;
box-shadow: 1px 2px 3px rgb(63, 145, 130);
border-radius: 1rem;
padding: 0.5rem;
}
#rl-filosofy:hover {
cursor: pointer;
background-color: rgb(143, 202, 202);
box-shadow: 2px 3px 4px rgb(64,146, 132);
}
</style>
<script>
import Menu from './menu.vue';
export default {
components: {
Menu,
},
name: 'header',
}
</script>
-85
View File
@@ -1,85 +0,0 @@
<template>
<div class="menu" v-on:click="OpenClose">
<img src="../images/icons/menu_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="menu" id="img-m">
<ul id="ul-m">
<li v-on:click="$refs.profile.$el.click()"><router-link to="/profile" ref="profile">Профиль</router-link></li>
<li v-on:click="$refs.settings.$el.click()"><router-link to="/settings" ref="settings">Настройки</router-link></li>
<li v-on:click="$refs.about.$el.click()"><router-link to="/about" ref="about">О нас</router-link></li>
<li v-on:click="$refs.support.$el.click()"><router-link to="/support" ref="support">Написать в техпотдержку</router-link></li>
<li v-on:click="$refs.feetback.$el.click()"><router-link to="/feetback" ref="feetback">Оставить отзыв</router-link></li>
<li v-on:click="$refs.registration.$el.click()"><router-link to="/registration" ref="registration">Регистрация</router-link></li>
<li v-on:click="$refs.login.$el.click()"><router-link to="/login" ref="login">Войти</router-link></li>
<li v-on:click="$refs.logout.$el.click()"><router-link to="/logout" ref="logout">Выйти</router-link></li>
</ul>
</div>
</template>
<style scoped>
.menu {
height: 100%;
position: relative;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
}
#img-m {
height: 2rem;
width: 2rem;
border: solid 1px rgb(33, 159, 181);
border-radius: 1rem;
background-color: rgb(37, 78, 78);
margin: 0 1rem 0 0;
}
#img-m:hover {
cursor: pointer;
background-color: rgb(71, 135, 105);
}
#ul-m {
position: absolute;
top: 3rem;
right: 1rem;
width: fit-content;
visibility: hidden;
margin: auto;
border: 1px solid;
}
li {
margin: 3px 0;
border: solid 1px black;
border-radius: 1rem;
padding: 0 0.5rem;
}
li:hover {
cursor: pointer;
background-color: rgb(153, 204, 204);
border-radius: 1rem;
}
</style>
<script>
export default {
name: 'menu',
methods: {
OpenClose: OpenCloseM
}
}
function OpenCloseM() {
var ul = document.querySelector('ul');
if (ul.style.visibility === 'hidden') {
ul.style.visibility = 'visible';
} else {
ul.style.visibility = 'hidden';
}
}
</script>
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/></svg>

Before

Width:  |  Height:  |  Size: 189 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M647-440H160v-80h487L423-744l57-56 320 320-320 320-57-56 224-224Z"/></svg>

Before

Width:  |  Height:  |  Size: 190 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg>

Before

Width:  |  Height:  |  Size: 222 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>

Before

Width:  |  Height:  |  Size: 243 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/></svg>

Before

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

-116
View File
@@ -1,116 +0,0 @@
<template>
<div class="login-form">
<h1>Логин</h1>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label for="email">Email:</label>
<input v-model.trim="email" type="email" id="email" required />
</div>
<div class="form-group">
<label for="password">Пароль:</label>
<input v-model.trim="password" type="password" id="password" required />
</div>
<button type="submit">Войти</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
email: '',
password: ''
};
},
methods: {
handleSubmit() {
if (!this.isValid(this.email, this.password)) {
alert("Пожалуйста, заполните все поля корректно.");
return;
}
this.loginUser({
email: this.email,
password: this.password
}).then(() => {
this.$router.push('/'); // Переход на главную страницу
}).catch((error) => {
console.error(error);
alert('Неверный email или пароль. Попробуйте снова.');
});
},
isValid(email, password) {
if (email.length === 0 || password.length === 0) {
return false;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return false;
}
if (password.length < 6) {
return false;
}
return true;
},
async loginUser(data) {
console.log("Login by this data: ", data);
// try {
// const response = await fetch('/login', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify(data)
// });
// if (!response.ok) {
// throw new Error('Неверный email или пароль');
// }
// } catch (error) {
// throw error;
// }
}
}
};
</script>
<style scoped>
.login-form {
max-width: 400px;
margin: 50px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
-17
View File
@@ -1,17 +0,0 @@
<template>
<div class="logout">
<h2>выход</h2>
</div>
</template>
<style>
.logout {
background-color: #c68686;
}
</style>
<script>
export default {
name: 'logout',
}
</script>
-123
View File
@@ -1,123 +0,0 @@
<template>
<div class="register-form">
<h1>Регистрация</h1>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label for="username">Имя пользователя:</label>
<input v-model.trim="username" type="text" id="username" required />
</div>
<div class="form-group">
<label for="email">Email:</label>
<input v-model.trim="email" type="email" id="email" required />
</div>
<div class="form-group">
<label for="password">Пароль:</label>
<input v-model.trim="password" type="password" id="password" required />
</div>
<button type="submit">Зарегистрироваться</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
username: '',
email: '',
password: ''
};
},
methods: {
handleSubmit() {
if (!this.isValid(this.username, this.email, this.password)) {
alert("Пожалуйста, заполните все поля корректно.");
return;
}
this.sendRegistrationData({
username: this.username,
email: this.email,
password: this.password
}).then(() => {
this.$router.push('/login'); // Переход на страницу логина
}).catch((error) => {
console.error(error);
alert('Что-то пошло не так. Попробуйте еще раз.');
});
},
isValid(username, email, password) {
if (username.length === 0 || email.length === 0 || password.length === 0) {
return false;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return false;
}
if (password.length < 6) {
return false;
}
return true;
},
async sendRegistrationData(data) {
console.log("Register by this data: ", data);
// try {
// const response = await fetch('/register', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify(data)
// });
// if (!response.ok) {
// throw new Error('Ошибка при регистрации');
// }
// } catch (error) {
// throw error;
// }
}
}
};
</script>
<style scoped>
.register-form {
max-width: 400px;
margin: 50px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
-13
View File
@@ -1,13 +0,0 @@
<template>
<div class="profile">
<h3>This is an profile</h3>
</div>
</template>
<style></style>
<script>
export default {
name: 'profile',
}
</script>
@@ -1,14 +0,0 @@
<template>
<div class="results">
<h1>Results</h1>
</div>
</template>
<style>
.results {
background-color: darkslategrey;
}
</style>
<script>
</script>
@@ -1,120 +0,0 @@
<template>
<div class="search-container">
<div class="search-input-container">
<!-- Иконка для вызова меню параметров -->
<div class="menu-icon" @click="toggleMenu">
<svg viewBox="0 0 24 24" width="24" height="24">
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" fill="currentColor"/>
</svg>
</div>
<!-- Поле ввода -->
<input
v-model="searchQuery"
@keyup.enter="performSearch"
class="search-input"
placeholder="Введите место для поиска"
/>
<!-- Кнопка поиска -->
<button @click="performSearch" class="search-button">
<svg viewBox="0 0 24 24" width="24" height="24">
<path d="M10 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm8-8a7.95 7.95 0 0 1-1.757 4.95l4.95 4.95-1.414 1.414-4.95-4.95A7.95 7.95 0 0 1 10 18z" fill="currentColor"/>
</svg>
</button>
</div>
<!-- Выпадающее меню для параметров поиска -->
<div v-if="isMenuVisible" class="search-menu">
<div class="menu-item" v-for="option in searchOptions" :key="option" @click="selectOption(option)">
{{ option }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
searchQuery: '',
isMenuVisible: false,
searchOptions: ['Пляжи', 'Горы', 'Отели', 'Рестораны']
};
},
methods: {
performSearch() {
if (this.searchQuery.trim()) {
// Переход на страницу результатов поиска с передачей запроса
this.$router.push({
name: 'results', // Имя маршрута для страницы результатов
query: { q: this.searchQuery } // Передача поискового запроса через query-параметры
});
}
},
toggleMenu() {
this.isMenuVisible = !this.isMenuVisible;
},
selectOption(option) {
this.searchQuery = option;
this.isMenuVisible = false;
this.performSearch();
}
}
};
</script>
<style scoped>
.search-container {
position: relative;
width: 100%;
max-width: 600px;
margin: auto;
}
.search-input-container {
display: flex;
align-items: center;
background: #fff;
border-radius: 25px;
padding: 10px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.menu-icon {
cursor: pointer;
margin-right: 10px;
}
.search-input {
flex-grow: 1;
border: none;
outline: none;
padding: 10px;
border-radius: 20px;
}
.search-button {
background: none;
border: none;
cursor: pointer;
padding: 10px;
}
.search-menu {
position: absolute;
top: 60px;
left: 0;
right: 0;
background: #fff;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
z-index: 1000;
}
.menu-item {
padding: 10px;
cursor: pointer;
}
.menu-item:hover {
background-color: #f0f0f0;
}
</style>
-17
View File
@@ -1,17 +0,0 @@
<template>
<div class="settings">
<h1>Settings</h1>
</div>
</template>
<style>
.settings {
background-color: #856565;
}
</style>
<script>
export default {
name: 'Settings',
}
</script>
-17
View File
@@ -1,17 +0,0 @@
<template>
<div class="support">
<h3>This is an message send to support</h3>
</div>
</template>
<style scoped>
.support {
background-color: darkkhaki;
}
</style>
<script>
export default {
name: 'support',
}
</script>
-11
View File
@@ -1,11 +0,0 @@
import './assets/main.css';
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
-67
View File
@@ -1,67 +0,0 @@
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: () => import('../views/HomeView.vue'),
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
{
path: '/profile',
name: 'profile',
component: () => import('../views/ProfileView.vue'),
},
{
path: '/support',
name: 'support',
component: () => import('../views/SupportView.vue'),
},
{
path: '/feetback',
name: 'feetback',
component: () => import('../views/FeetbackView.vue'),
},
{
path: '/results',
name: 'results',
component: () => import('../views/ResultsView.vue'),
},
{
path: '/settings',
name: 'settings',
component: () => import('../views/SettingsView.vue'),
},
{
path: '/registration',
name: 'registration',
component: () => import('../views/RegistrationView.vue')
},
{
path: '/login',
name: 'login',
component: () => import('../views/LogInView.vue')
},
{
path: '/logout',
name: 'logout',
component: () => import('../views/LogOutView.vue')
},
{
path: '/filosofy',
name: 'filosofy',
component: () => import('../views/FilosofyView.vue')
}
],
})
export default router
-29
View File
@@ -1,29 +0,0 @@
<template>
<Header />
<About />
<Developers />
<Commits />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import About from '../components/about/about.vue';
import Commits from '../components/about/commits.vue';
import Developers from '../components/about/developers.vue';
export default {
components: {
Header,
Footer,
About,
Developers,
Commits,
},
};
</script>
-23
View File
@@ -1,23 +0,0 @@
<template>
<Header />
<Feetback />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Feetback from '../components/feeback/feetback.vue';
export default {
components: {
Header,
Footer,
Feetback,
},
};
</script>
-23
View File
@@ -1,23 +0,0 @@
<template>
<Header />
<Filosofy />
<Footer />
</template>
<style>
</style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Filosofy from '../components/filosofy.vue';
export default {
components: {
Footer,
Header,
Filosofy,
},
}
</script>
-22
View File
@@ -1,22 +0,0 @@
<template>
<Header />
<SearchLine />
<Footer />
</template>
<style src="../assets/main.css"></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import SearchLine from '../components/searchLine/searchLine.vue';
export default {
components: {
Header,
Footer,
SearchLine,
},
};
</script>
-23
View File
@@ -1,23 +0,0 @@
<template>
<Header />
<LogIn />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import LogIn from '../components/inout/login.vue';
export default {
components: {
Header,
Footer,
LogIn,
},
};
</script>
-23
View File
@@ -1,23 +0,0 @@
<template>
<Header />
<LogOut />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import LogOut from '../components/inout/logout.vue';
export default {
components: {
Header,
Footer,
LogOut,
},
};
</script>
-22
View File
@@ -1,22 +0,0 @@
<template>
<Header />
<Profile />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Profile from '../components/profile.vue';
export default {
components: {
Header,
Footer,
Profile,
},
};
</script>
-23
View File
@@ -1,23 +0,0 @@
<template>
<Header />
<Registration />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Registration from '../components/inout/registration.vue';
export default {
components: {
Header,
Footer,
Registration,
},
};
</script>
-27
View File
@@ -1,27 +0,0 @@
<template>
<Header />
<SearchLine />
<Results />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Results from '../components/saerch_results/results.vue';
import searchLine from '@/components/searchLine/searchLine.vue';
import SearchLine from '@/components/searchLine/searchLine.vue';
export default {
components: {
Header,
Footer,
Results,
SearchLine,
},
};
</script>
-22
View File
@@ -1,22 +0,0 @@
<template>
<Header />
<Settings />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Settings from '../components/settings.vue';
export default {
components: {
Header,
Footer,
Settings,
},
};
</script>
-23
View File
@@ -1,23 +0,0 @@
<template>
<Header />
<Support />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Support from '../components/support.vue';
export default {
components: {
Header,
Footer,
Support,
},
};
</script>
-9
View File
@@ -1,9 +0,0 @@
module.exports = {
content: [
'./src/**/*.{html,js,vue}',
],
theme: {
extend: {},
},
plugins: [],
}
-18
View File
@@ -1,18 +0,0 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})