нормальные руты

This commit is contained in:
Sevka 2023-09-05 20:30:36 +03:00
commit 350c394306
48 changed files with 25631 additions and 0 deletions

View File

@ -0,0 +1,71 @@
name: Create and publish a Docker image
on:
push:
branches: ["main"]
env:
REGISTRY: git.zetcraft.ru
jobs:
publish:
runs-on: ubuntu-latest
name: Publish image
container: catthehacker/ubuntu:act-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ gitea.actor }}
password: ${{ secrets.TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: https://github.com/docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ gitea.repository }}
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: ./
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
deploy:
needs: publish
name: Deploy image
runs-on: ubuntu-latest
steps:
- name: install ssh keys
# check this thread to understand why its needed:
# <https://stackoverflow.com/a/70447517>
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
- name: connect and pull
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.WORK_DIR }} && docker compose pull && docker compose up -d && docker image prune && exit"
- name: cleanup
run: rm -rf ~/.ssh
- name: send telegram message on deploy
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_TO }}
token: ${{ secrets.TELEGRAM_TOKEN }}
disable_web_page_preview: true
format: markdown
message: |
🚀ОБНОВА УЖЕ НА СЕРВЕРЕ🚀
${{ gitea.actor }} залил коммит: ${{ gitea.event.commits[0].message }}
Который успешно задеплоился на сервере!
[Открыть](${{ secrets.DEPLOYED_URL }})

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
FROM node:20-alpine3.17 AS builder
WORKDIR /app
COPY package.json package-lock.json ./
COPY public/ public/
COPY src/ src/
RUN npm ci
RUN npm run build
FROM nginx:1.25.2-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/build /usr/share/nginx/html
RUN touch /var/run/nginx.pid
RUN chown -R nginx:nginx /var/run/nginx.pid /usr/share/nginx/html /var/cache/nginx /var/log/nginx /etc/nginx/conf.d
USER nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

25
README.md Normal file
View File

@ -0,0 +1,25 @@
# FichaFrontend
# Docker
```bash
docker run -d -p 85:80 \
--name FichaFrontend \
--restart=always \
-e TZ=Europe/Moscow \
git.zetcraft.ru/fichahackaton/fichafrontend:main
```
# Docker Compose
```yml
services:
sistemafrontend:
ports:
- '85:80'
container_name: FichaFrontend
restart: always
environment:
- TZ=Europe/Moscow
image: 'git.zetcraft.ru/fichahackaton/fichafrontend:main'
```

9
nginx.conf Normal file
View File

@ -0,0 +1,9 @@
server_tokens off;
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri /index.html;
}
}

18026
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

62
package.json Normal file
View File

@ -0,0 +1,62 @@
{
"name": "hackathonficha",
"version": "0.1.0",
"private": true,
"dependencies": {
"@react-spring/web": "^9.5.5",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.4.0",
"framer": "^2.3.0",
"matchmedia": "^0.1.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.15.0",
"react-scripts": "5.0.1",
"react-swipeable": "^7.0.1",
"react-tinder-card": "^1.6.2",
"web-vitals": "^2.1.4",
"workbox-background-sync": "^6.6.0",
"workbox-broadcast-update": "^6.6.0",
"workbox-cacheable-response": "^6.6.0",
"workbox-core": "^6.6.0",
"workbox-expiration": "^6.6.0",
"workbox-google-analytics": "^6.6.0",
"workbox-navigation-preload": "^6.6.0",
"workbox-precaching": "^6.6.0",
"workbox-range-requests": "^6.6.0",
"workbox-routing": "^6.6.0",
"workbox-strategies": "^6.6.0",
"workbox-streams": "^6.6.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"main": "index.js",
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}

BIN
public/Dis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
public/Like.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

6114
public/circle.html Normal file

File diff suppressed because it is too large Load Diff

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
public/icon200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
public/image 2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

40
public/index.html Normal file
View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="ru" translate="no">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="EasyTravelApp" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>EasyTravel</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/logo5121.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

25
public/manifest.json Normal file
View File

@ -0,0 +1,25 @@
{
"short_name": "Путешествия просто!",
"name": "Агрегатор возможностей",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

BIN
public/menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

BIN
public/pngwing 7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

3
public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -0,0 +1,10 @@
.App {
position: relative;
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
.App.hidden {
opacity: 0;
pointer-events: none;
}

66
src/components/App/App.js Normal file
View File

@ -0,0 +1,66 @@
import React, { useState, useEffect } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Start from '../Start/Start';
import Second from '../Second/Second';
import Tinder from '../Tinder/Tinder';
import Main from '../Main/Main';
import City from '../City/City';
import axios from 'axios';
import './App.css';
function App() {
const [buttonValue, setButtonValue] = useState("");
const [isHidden, setIsHidden] = useState(false);
const [cardInfo, setCardInfo] = useState([]);
const [userData, setUserData] = useState({});
const handleButtonValue = (value) => {
setIsHidden(true);
setTimeout(() => {
setIsHidden(false);
setButtonValue(value);
}, 300);
};
useEffect(() => {
const fetchData = () => {
axios.get('https://easytravel.zetcraft.ru/v1/GetAllCards')
.then(response => {
setCardInfo(response.data);
})
.catch(error => {
console.error('Error fetching data:', error);
});
}
fetchData();
}, []);
useEffect(() => {
// Загрузка userData из localStorage при монтировании компонента
const savedUserData = JSON.parse(localStorage.getItem('userData'));
if (savedUserData) {
setUserData(savedUserData);
}
}, []);
useEffect(() => {
// Сохранение userData в localStorage при изменении
localStorage.setItem('userData', JSON.stringify(userData));
console.log(userData);
}, [userData]);
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Start getValue={handleButtonValue} userData={userData} setUserData={setUserData} />}/>
<Route path="second" element={<Second getValue={handleButtonValue} setUserData={setUserData} />} />
<Route path="tinder" element={<Tinder getValue={handleButtonValue} cardInfo={cardInfo} userData={userData} setUserData={setUserData} />} />
<Route path="city" element={<City getValue={handleButtonValue} setUserData={setUserData} />} />
<Route path="main" element={<Main userData={userData}/>} />
</Routes>
</BrowserRouter>
);
}
export default App;

View File

@ -0,0 +1,46 @@
/* .button-container {
display: flex;
flex-direction: column;
gap: 10px;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
} */
.animated-button {
width: 304px;
height: 54px;
font-size: 20px;
border-radius: 27px;
border: none;
background-color: #46A2E3;
font-family: 'Raleway';
color: #FFFFFF;
font-weight: 97px;
}
.animated-button:hover {
background-color: #398ac5;
}
.ab {
width: 304px;
height: 54px;
font-size: 20px;
border-radius: 27px;
border: none;
background-color: white;
font-family: 'Raleway';
color: #398ac5;
font-weight: 97px;
}
.ab:hover {
background-color: #f8f8f8;
}
/*
.button-container.visible {
opacity: 1;
transform: translateY(0);
} */

153
src/components/City/City.js Normal file
View File

@ -0,0 +1,153 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import './City.css'
const City = (props) => {
const route = props.getValue;
const setUserData = props.setUserData
const second = useNavigate();
const [cityOptions, setCityOptions] = useState([]);
const [filteredCityOptions, setFilteredCityOptions] = useState([]);
const [selectedCity, setSelectedCity] = useState('');
const [buttonsVisible, setButtonsVisible] = useState(false);
const cityHandler = (e) => {
setSelectedCity(e.target.value);
};
const options = {
method: 'GET',
url: 'https://easytravel.zetcraft.ru/v1/GetAllCity',
};
useEffect(() => {
axios.request(options)
.then(function (response) {
console.log(response.data);
setCityOptions(response.data);
setFilteredCityOptions(response.data);
})
.catch(function (error) {
console.error(error);
});
}, []);
useEffect(() => {
const filteredOptions = cityOptions.filter(option =>
option.name.toLowerCase().includes(selectedCity.toLowerCase())
);
setFilteredCityOptions(filteredOptions);
setTimeout(() => {
setButtonsVisible(selectedCity !== '');
}, 300);
}, [selectedCity, cityOptions]);
useEffect(() => {
const filteredOptions = cityOptions.filter(option =>
option.name.toLowerCase().includes(selectedCity.toLowerCase())
);
setFilteredCityOptions(filteredOptions);
}, [selectedCity, cityOptions]);
return (
<div style={{
display: 'flex',
flexDirection: 'column',
height: '100vh',
justifyContent: 'space-around',
alignItems: 'center',
background: 'linear-gradient(180deg, #7EAFE7 0.27%, rgba(41, 134, 242, 0.38) 27.08%, rgba(41, 134, 242, 0.35) 31.77%, rgba(152, 198, 253, 0.28) 46.35%, rgba(41, 134, 242, 0.00) 100%)',
}}>
<div style={{
position: 'fixed',
top: 100,
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'center',
gap: 80,
}}>
<div style={{
display: 'flex',
justifyContent: 'center',
width: 304,
}}>
<img src='./image 2.png' alt="#" draggable="false" style={{
width: 282,
height: 282,
}} />
</div>
<span style={{
fontWeight: 700,
fontSize: 30,
color: '#000'
}}></span>
<select onChange={e => cityHandler(e)} placeholder="Выберите город..." value={selectedCity} id="city" name="city" style={{
height: 40,
width: 304,
borderRadius: '20px',
border: 'none',
padding: '5px',
}}>
<option value="" style={{
maxWidth: 200,
}}>Выберите город...</option>
{filteredCityOptions.map(option => (
<option key={option.id} value={option.name} style={{
maxWidth: 200,
}}>{option.name}</option>
))}
</select>
</div>
{buttonsVisible ? (
<div className="button-container" style={{
display: 'flex',
flexDirection: 'column',
position: 'fixed',
bottom: 50,
gap: 10,
}}>
<button
className="animated-button"
onClick={() => {
second('/second')
setUserData({selectedCity: selectedCity});
console.log(selectedCity);
}}
>
Я в этом городе
</button>
<button
className="ab"
onClick={() => window.open('https://www.aviasales.ru/?marker=15468.ydof241309826304&yclid=18373991699987824639&params=TGK1')}
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: 42,
border: 'none',
}}
>
<img
src="./icon200.png"
alt="#"
style={{
fontSize: 20,
width: 30,
height: 30,
marginRight: 10,
}}
/>
Купить билет
</button>
</div>
) : null}
</div>
);
}
export default City;

View File

@ -0,0 +1,46 @@
const Card = ({city}) => {
return (
<div style={{
height: 257,
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundImage: '',
}}>
<div style={{
height: 179,
width: 371,
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-evenly',
alignItems: 'center',
}}>
<span style={{
color: '#000',
fontSize: 20,
fontWeight: 700
}}>Туристическая карта г.{city}</span>
<span style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>Приехали в город? <p>Узнай, где выгоднее остановиться, куда</p> <p>сходить и что посмотреть</p></span>
<button style={{
width: 240,
height: 46,
backgroundColor: '#4B60AE',
borderRadius: 10,
color: '#fff',
fontSize: 16,
fontWeight: 400,
border: 'none'
}}>Построить маршрут</button>
</div>
</div>
);
}
export default Card;

View File

@ -0,0 +1,33 @@
const Footer = () => {
return (
<div>
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
marginTop: 50,
marginBottom: 50,
}}>
<img src="./logo5121.png" alt="#" style={{
height: 53,
width: 53,
}}/>
<span style={{
color: '#F68C43',
width: '30%',
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
fontWeight: 700,
fontFamily: 'Raleway',
fontSize: '20px',
}}>Путешествия <p style={{color: '#4EB0F2'}} >Просто!</p></span>
</div>
</div>
);
}
export default Footer;

View File

@ -0,0 +1,57 @@
const Header = () => {
return (
<div style={{
height: '100%',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: '10px',
height: 79,
width: 390,
backgroundColor: '#ffffff',
}}>
<div style={{
display: 'flex',
alignItems: 'center',
}}>
<img src="./logo5121.png" alt="#" style={{
height: 53,
width: 53,
}}/>
<span style={{
color: '#F68C43',
width: '60%',
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
fontWeight: 700,
fontFamily: 'Raleway',
fontSize: '20px',
}}>Путешествия <p style={{color: '#4EB0F2'}} >Просто!</p></span>
</div>
<button style={{
backgroundColor: '#fff',
border: 'none',
}}>
<img src="./menu.png" alt="#" style={{
height: 20,
width: 32,
marginRight: 10,
}}/>
</button>
</div>
</div>
);
}
export default Header;

204
src/components/Main/Main.js Normal file
View File

@ -0,0 +1,204 @@
import React, { useEffect, useState } from "react";
import axios from "axios";
import Header from "./Header";
import Card from "./Card";
import Footer from "./Footer";
const Main = ({ userData }) => {
const [films, setFilms] = useState([]);
const [attract, setAttract] = useState([]);
const [hotel, setHotel] = useState([]);
const optionsFilms = {
method: 'GET',
url: `https://easytravel.zetcraft.ru/v1/GetAllFilmsInCity/${userData.selectedCity}`,
};
const optionsAttract = {
method: 'GET',
url: `https://easytravel.zetcraft.ru/v1/GetAllAttractionsInCity/${userData.selectedCity}`,
};
const optionsHotel = {
method: 'GET',
url: `https://easytravel.zetcraft.ru/v1/GetAllHotelsInCity/${userData.selectedCity}`,
};
useEffect(() => {
axios.request(optionsFilms).then(function (response) {
setFilms(response.data);
}).catch(function (error) {
console.error(error);
});
}, []);
useEffect(() => {
axios.request(optionsAttract).then(function (response) {
setAttract(response.data);
}).catch(function (error) {
console.error(error);
});
}, []);
useEffect(() => {
axios.request(optionsHotel).then(function (response) {
setHotel(response.data);
}).catch(function (error) {
console.error(error);
});
}, []);
return (
<div>
<Header />
<Card city={userData.selectedCity}/>
<div style={{
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginTop: 10
}}>
<h2>Кино по пушкинской карте</h2>
<div style={{
width: '100%',
overflowX: 'auto',
display: 'flex',
justifyContent: 'center',
marginTop: 20
}}>
<ul style={{
display: 'flex',
listStyle: 'none',
padding: 0,
}}>
{films.map((film, index) => (
<li key={index} style={{
padding: '10px',
marginRight: '30px',
}}>
<a href={film.url} style={{ textDecoration: 'none', color: 'inherit' }}>
<div key={index} style={{
height: 200,
width: 140,
display: 'flex',
alignContent: 'center',
flexDirection: 'column',
borderRadius: '10px',
boxShadow: '0px 0px 10px rgba(0, 0, 0, 0.5)'
}}>
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>
<img src={film.imageURL} alt="#" style={{
height: 110,
width: 79,
marginTop: 10,
borderRadius: 10
}}/>
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
width: 80,
}}>{film.filmName}</div>
</div>
</div>
</a>
</li>
))}
</ul>
</div>
<h2 style={{
marginTop: 20,
marginBottom: 20
}}>Достопримечательности</h2>
<ul style={{
display: 'flex',
flexDirection: 'column',
listStyle: 'none',
padding: 0,
marginTop: 20,
marginLeft: 55,
gap: 20,
}}>
{attract.map((attr, index) => (
<li key={index}>
<div style={{
display: 'flex',
alignItems: 'center',
gap: 20,
width: 400,
}}>
<img src={attr.imageURL} alt="#" style={{
height: '92px',
width: '107px',
borderRadius: '10px'
}}/>
<div style={{
width: 150
}}>{attr.name}</div>
<button style={{
height: 33,
width: 33,
backgroundColor: 'rgb(0,0,0,0)',
border: 'none'
}} onClick={() => {
window.open(`https://taxi.yandex.ru/?utm_source&utm_medium&gfrom=%2C&gto=47.213123%2C38.938103&ref&level&city&tariff&referrer=appmetrica_tracking_id%3D1178268795219780156%26ym_tracking_id%3D2217839191706395275`)
}}><img src="./pngwing 7.png" alt="#" /></button>
</div>
</li>
))}
</ul>
<h2 style={{
marginBottom: 20,
marginTop: 20,
}}>Отели</h2>
<ul style={{
display: 'flex',
flexDirection: 'column',
listStyle: 'none',
padding: 0,
marginTop: 20,
gap: 20,
}}>
{hotel.map((hotelItem, index) => (
<li key={index}>
<div style={{
display: 'flex',
alignItems: 'center',
gap: 20
}}>
<img src={hotelItem.imageURL} alt="#" style={{
height: 119,
width: 184,
borderRadius: 10,
}}/>
<div style={{
display: 'flex',
flexDirection: 'column',
height: 20,
width: 150,
alignItems: 'center',
}}>
<div>{hotelItem.name}</div>
<div>{hotelItem.price} </div>
</div>
</div>
</li>
))}
</ul>
</div>
<Footer />
</div>
);
}
export default Main;

View File

@ -0,0 +1,9 @@
const Map = () => {
return (
<div>
<span>Переход готов</span>
</div>
);
}
export default Map;

View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -0,0 +1,53 @@
import React from "react"
import { useNavigate } from "react-router-dom";
import './style.css'
import Icon from './images/Vector.svg'
const Second = (props) => {
const route = props.getValue;
const tinder = useNavigate();
const main = useNavigate();
return (
<div style={{
display: 'flex',
flexDirection: 'column',
alignContent: 'center',
flexWrap: 'wrap',
justifyContent: 'space-between',
height: '95vh' ,
background: 'linear-gradient(180deg, #7EAFE7 0.27%, rgba(41, 134, 242, 0.38) 27.08%, rgba(41, 134, 242, 0.35) 31.77%, rgba(152, 198, 253, 0.28) 46.35%, rgba(41, 134, 242, 0.00) 100%)',
filter: 'blur 2',
paddingBottom:10,
overflow: 'hidden',
}}>
<img src={Icon} alt='#'/>
<div style={{
display:'flex',
flexDirection:'column',
gap:20,
alignItems:'center',
}}>
<h1 className="main_text">Давайте<br/>знакомиться!</h1>
<p style={{
textAlign:'center',
color:'#6C6C6C',
}} className="text">Пройдите небольшой тест,<br/> чтобы мы подобрали интересные<br/>
для вас мероприятия</p>
</div>
<div style={{
display:'flex',
flexDirection:'column',
gap:15,
alignItems:'center',
}}>
<button className="btn_first" onClick={() => tinder('/tinder')}>Давайте!</button>
<button className="btn_second" onClick={() => main('/main')}>Пропустить</button>
</div>
</div>
)
}
export default Second;

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -0,0 +1,50 @@
@font-face {
font-family: "Raleway";
src: url("../Second/fonts/Raleway.ttf");
}
.main_text{
font-size: 45px;
text-align: center;
font-family: "Raleway";
}
.text{
font-size: 18px;
color: #6C6C6C;
text-align: center;
width: 332px;
font-family: "Raleway";
}
.btn_first{
width: 231px;
height: 61px;
background: #46A2E3;
color: #fff;
font-size: 24px;
border: none;
text-align: center;
border-radius: 53px;
font-weight: bold;
}
.btn_first:hover {
background-color: #398ac5;
}
.btn_second{
width: 169px;
height: 49px;
background: #7dc5f8;
border: #0094FF;
color: #ffffff;
font-size: 15px;
text-align: center;
border-radius: 53px;
}
.btn_second:hover {
background-color: #398ac5;
}

View File

@ -0,0 +1,137 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://cra.link/PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://cra.link/PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://cra.link/PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch((error) => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' },
})
.then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log('No internet connection found. App is running in offline mode.');
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready
.then((registration) => {
registration.unregister();
})
.catch((error) => {
console.error(error.message);
});
}
}

View File

View File

@ -0,0 +1,74 @@
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
const Start = (props) => {
const history = useNavigate();
const kek = './circle.html';
function hideAddressBar() {
if (document.documentElement.scrollHeight < window.outerHeight / window.devicePixelRatio)
document.documentElement.style.height = (window.outerHeight / window.devicePixelRatio) + 'px';
setTimeout(window.scrollTo(1, 1), 0);
}
window.addEventListener("load", function () { hideAddressBar(); });
window.addEventListener("orientationchange", function () { hideAddressBar(); });
const handleButtonClick = () => {
history('/city');
};
useEffect(() => {
const frame = document.querySelector("#start");
frame.addEventListener('load', () => {
frame.contentWindow.addEventListener('click', () => {
handleButtonClick();
});
});
return () => {
frame.removeEventListener('load', () => {
frame.contentWindow.removeEventListener('click', () => {
});
});
};
}, []);
return (
<div style={{
display: 'flex',
flexDirection: 'column',
alignContent: 'center',
alignItems: 'center',
flexWrap: 'wrap',
justifyContent: 'space-evenly',
height: '95vh',
overflow: 'hidden',
}}>
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>
<img src="./logo192.png" alt="logo" style={{ width: '181px', height: '181px' }} />
<span style={{
color: '#F68C43',
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
fontWeight: 700,
fontFamily: 'Raleway',
fontSize: '40px',
}}>Путешествия <p style={{ color: '#4EB0F2' }}>Просто!</p></span>
</div>
<iframe id="start" scrolling="no" src={kek} style={{
overflow: 'hidden',
border: 'none',
width: '440px',
height: '440px',
}}></iframe>
</div>
)
};
export default Start;

View File

@ -0,0 +1,97 @@
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@100&display=swap');
.tinder-container {
height: 85vh;
width: 100%;
display: flex;
justify-content: space-evenly;
align-items: center;
flex-direction: column;
overflow: hidden;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
.card-container {
position: relative;
display: flex;
height: 100%;
width: 300px;
flex-direction: column;
}
.text {
font-family: 'Raleway';
font-weight: bold;
text-align: center;
color: black;
font-size: 20px;
margin: 0;
}
.swipe {
margin-top: 15%;
display: flex;
gap: 5px;
flex-direction: column;
align-items: center;
position: absolute;
width: 100%;
/* max-width: 300px; */
/* max-height: 400px; */
cursor: pointer;
border-radius: 10px;
background-color: #fff;
transition: transform 0.05s ease-in-out, opacity 0.05s ease-in-out;
}
.swipe-left {
transform: translateX(-10%) rotate(-20deg);
opacity: 0;
}
.swipe-right {
transform: translateX(10%) rotate(20deg);
opacity: 0;
}
.card {
height: 500px;
width: 100%;
max-width: 300px;
max-height: 500px;
border-radius: 10px;
background-size: cover;
background-position: center;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
align-content: center;
flex-wrap: wrap;
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1);
background-color: #fff;
}
.card-content {
border-radius: 10px;
text-align: center;
align-items: center;
justify-content: space-between;
width: 300px;
height: 500px;
background-color: rgba(255, 255, 255, 0.8);
}
.infoText {
margin-top: 16px;
}

View File

@ -0,0 +1,138 @@
import React, { useState } from 'react';
import { useNavigate } from "react-router-dom";
import TinderCard from 'react-tinder-card';
import './Tinder.css';
const Tinder = (props) => {
const route = props.getValue;
const cardInfo = props.cardInfo;
const setUserData = props.setUserData;
const main = useNavigate();
const [lastDirection, setLastDirection] = useState();
const [cardId, setCardId] = useState([]);
const [allIds, setAllIds] = useState([]);
const swiped = (direction, id) => {
setLastDirection(direction);
allIds.push(id);
console.log(allIds.length);
if (direction === 'right' || direction === 'up') {
cardId.push(id);
};
};
const handleDislike = (id) => {
console.log('left');
swiped('left', id);
};
const handleLike = (id) => {
console.log('right');
swiped('right', id);
};
return (
<div style={{
display: 'flex',
flexDirection: 'column',
height: '100vh',
justifyContent: 'space-around',
alignItems: 'center',
background: 'linear-gradient(180deg, #7EAFE7 0.27%, rgba(41, 134, 242, 0.38) 27.08%, rgba(41, 134, 242, 0.35) 31.77%, rgba(152, 198, 253, 0.28) 46.35%, rgba(41, 134, 242, 0.00) 100%)',
}}>
<div className='tinder-container'>
<div className='card-container'>
{cardInfo.map((card) => (
<TinderCard
className='swipe'
key={card.id}
onSwipe={(dir) => {
swiped(dir, card.id);
var unique = [...new Set(cardId)]
var uniqueIds = [...new Set(allIds)];
if(uniqueIds.length === cardInfo.length) {
main('/main');
setUserData(prevUserData => ({ ...prevUserData, unique: unique }));
console.log(unique);
console.log(props.userData);
}
}}
>
<div className='card'>
<div
className='card-content'
style={{
width: '300px',
height: '500px',
borderRadius: '10px',
display: 'flex',
flexDirection: 'column',
paddingTop: '10px',
}}>
<div style={{
height: '300px',
width: '245px',
borderRadius: '10px',
backgroundImage: `url(${card.imageURL})`,
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}}/>
<div style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '150px',
}}>
<h3 className="noselect text">{card.question}</h3>
<div style={{
display: 'flex',
justifyContent: 'space-between',
marginLeft: '18px',
marginRight: '18px',
}}>
<button
className="noselect"
style={{
height: '69px',
width: '69px',
backgroundColor: 'rgb(0,0,0,0)',
border: 'none',
}}
onClick={() => handleDislike(card.id)}
>
<img draggable="false" src="./Dis.png" alt='#' style={{
height: '100%',
width: '100%',
}} onClick={() => handleDislike(card.id)}/>
</button>
<button
className="noselect"
style={{
height: '69px',
width: '69px',
backgroundColor: 'rgb(0,0,0,0)',
border: 'none',
}}
onClick={() => handleLike(card.id)}
>
<img draggable="false" src="./Like.png" alt='#' style={{
height: '100%',
width: '100%',
}} onClick={() => handleLike(card.id)}/>
</button>
</div>
</div>
</div>
</div>
</TinderCard>
))}
</div>
</div>
</div>
);
}
export default Tinder;

6
src/index.css Normal file
View File

@ -0,0 +1,6 @@
* {
margin: 0;
padding: 0;
/* max-width: 440px; */
/* max-height: 920px; */
}

15
src/index.js Normal file
View File

@ -0,0 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './components/App/App';
import * as serviceWorkerRegistration from './components/ServiceWorkerRegistration/serviceWorkerRegistration';
import reportWebVitals from './components/ReportWebVitals/reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
);
serviceWorkerRegistration.unregister();
reportWebVitals();