Google auth provided
This commit is contained in:
parent
c9e8e5386f
commit
00ad7ddef1
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@
|
|||||||
static/css/*.css
|
static/css/*.css
|
||||||
static/css/*.css.map
|
static/css/*.css.map
|
||||||
.env
|
.env
|
||||||
|
/flask_session/
|
22
app.py
22
app.py
@ -7,12 +7,16 @@ import random
|
|||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import requests
|
import requests
|
||||||
|
from authlib.integrations.flask_client import OAuth
|
||||||
|
from auth import auth_bp, init_oauth
|
||||||
|
from config import Config, configure_oauth
|
||||||
|
from flask_session import Session
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
from werkzeug.exceptions import BadRequest
|
from werkzeug.exceptions import BadRequest
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from flask import Flask, abort, render_template, redirect, url_for, request, flash, session, jsonify
|
from flask import Flask, abort, render_template, redirect, url_for, request, session, jsonify
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
||||||
@ -34,9 +38,12 @@ from auth import auth_bp
|
|||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
csrf = CSRFProtect(app)
|
csrf = CSRFProtect(app)
|
||||||
|
|
||||||
app.config.from_object(Config)
|
app.config.from_object(Config)
|
||||||
|
|
||||||
|
oauth = configure_oauth(app)
|
||||||
|
|
||||||
|
init_oauth(oauth)
|
||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
bcrypt.init_app(app)
|
bcrypt.init_app(app)
|
||||||
login_manager = LoginManager(app)
|
login_manager = LoginManager(app)
|
||||||
@ -44,7 +51,16 @@ login_manager.login_view = 'auth.login'
|
|||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
return User.query.get(int(user_id))
|
return db.session.get(User, int(user_id))
|
||||||
|
|
||||||
|
from flask_session import Session
|
||||||
|
|
||||||
|
app.config['SESSION_TYPE'] = 'filesystem'
|
||||||
|
app.config['SESSION_FILE_DIR'] = './flask_session'
|
||||||
|
app.config['SESSION_PERMANENT'] = False
|
||||||
|
app.config['SESSION_USE_SIGNER'] = True
|
||||||
|
app.config['SECRET_KEY'] = Config.SECRET_KEY
|
||||||
|
Session(app)
|
||||||
|
|
||||||
register_admin_routes(app)
|
register_admin_routes(app)
|
||||||
app.register_blueprint(upload_bp)
|
app.register_blueprint(upload_bp)
|
||||||
|
75
auth.py
75
auth.py
@ -1,15 +1,24 @@
|
|||||||
from flask import Blueprint, render_template, redirect, url_for, request
|
from flask import Blueprint, render_template, redirect, url_for, request, session
|
||||||
from flask_login import login_user, logout_user, login_required, current_user
|
from flask_login import login_user, logout_user, login_required, current_user
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
from models import db, User
|
from models import db, User
|
||||||
|
import uuid
|
||||||
from utils import get_client_ip
|
from utils import get_client_ip
|
||||||
from models import RegistrationForm, LoginForm, PasswordField, RecaptchaField, SubmitField
|
from models import RegistrationForm, LoginForm, PasswordField, RecaptchaField, SubmitField
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
from wtforms.validators import DataRequired, Length, EqualTo
|
from wtforms.validators import DataRequired, Length, EqualTo
|
||||||
from config import Config
|
from config import Config
|
||||||
|
from authlib.integrations.flask_client import OAuth # type: ignore
|
||||||
auth_bp = Blueprint('auth', __name__)
|
auth_bp = Blueprint('auth', __name__)
|
||||||
bcrypt = Bcrypt()
|
bcrypt = Bcrypt()
|
||||||
|
|
||||||
|
|
||||||
|
google = None
|
||||||
|
|
||||||
|
def init_oauth(oauth):
|
||||||
|
global google
|
||||||
|
google = oauth.google
|
||||||
|
|
||||||
password = PasswordField('Password', validators=[DataRequired(), Length(min=6)])
|
password = PasswordField('Password', validators=[DataRequired(), Length(min=6)])
|
||||||
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
|
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
|
||||||
recaptcha = RecaptchaField()
|
recaptcha = RecaptchaField()
|
||||||
@ -20,11 +29,15 @@ def register():
|
|||||||
form = RegistrationForm()
|
form = RegistrationForm()
|
||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
|
|
||||||
ip_address = get_client_ip()
|
ip_address = get_client_ip()
|
||||||
|
existing_user = User.query.filter_by(ip_address=ip_address).first()
|
||||||
|
|
||||||
|
if existing_user:
|
||||||
|
return render_template('register.html', form=form, recaptcha_key=Config.RECAPTCHA_PUBLIC_KEY)
|
||||||
|
|
||||||
|
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
|
||||||
username = form.username.data.lower()
|
username = form.username.data.lower()
|
||||||
|
|
||||||
existing_user = User.query.filter_by(ip_address=ip_address).first()
|
|
||||||
user = User(username=username, encrypted_password=hashed_password, ip_address=ip_address)
|
user = User(username=username, encrypted_password=hashed_password, ip_address=ip_address)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -46,7 +59,12 @@ def login():
|
|||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
user = User.query.filter_by(username=form.username.data).first()
|
user = User.query.filter_by(username=form.username.data).first()
|
||||||
if user and user.check_password(form.password.data):
|
if user:
|
||||||
|
|
||||||
|
if user.google_id:
|
||||||
|
return redirect(url_for('auth.login_google'))
|
||||||
|
|
||||||
|
if user.check_password(form.password.data):
|
||||||
login_user(user)
|
login_user(user)
|
||||||
if user.ip_address is None:
|
if user.ip_address is None:
|
||||||
ip_address = get_client_ip()
|
ip_address = get_client_ip()
|
||||||
@ -59,8 +77,6 @@ def login():
|
|||||||
|
|
||||||
return render_template('login.html', form=form, recaptcha_key=Config.RECAPTCHA_PUBLIC_KEY)
|
return render_template('login.html', form=form, recaptcha_key=Config.RECAPTCHA_PUBLIC_KEY)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@auth_bp.route('/register-modal')
|
@auth_bp.route('/register-modal')
|
||||||
def register_modal():
|
def register_modal():
|
||||||
form = RegistrationForm()
|
form = RegistrationForm()
|
||||||
@ -76,3 +92,50 @@ def login_modal():
|
|||||||
def logout():
|
def logout():
|
||||||
logout_user()
|
logout_user()
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
@auth_bp.route('/login/google')
|
||||||
|
def login_google():
|
||||||
|
|
||||||
|
base_domain = request.host_url.rstrip('/')
|
||||||
|
redirect_uri = f"{base_domain}/auth/google/callback"
|
||||||
|
|
||||||
|
state = str(uuid.uuid4())
|
||||||
|
nonce = str(uuid.uuid4())
|
||||||
|
session['oauth_state'] = state
|
||||||
|
session['oauth_nonce'] = nonce
|
||||||
|
|
||||||
|
return google.authorize_redirect(redirect_uri, state=state, nonce=nonce)
|
||||||
|
|
||||||
|
@auth_bp.route('/auth/google/callback')
|
||||||
|
def authorize_google():
|
||||||
|
state = session.pop('oauth_state', None)
|
||||||
|
nonce = session.pop('oauth_nonce', None)
|
||||||
|
|
||||||
|
if not state or state != request.args.get('state'):
|
||||||
|
return "Error: Invalid state parameter", 400
|
||||||
|
|
||||||
|
token = google.authorize_access_token()
|
||||||
|
|
||||||
|
try:
|
||||||
|
user_info = google.parse_id_token(token, nonce=nonce)
|
||||||
|
except Exception as e:
|
||||||
|
return "Error: Invalid ID token", 400
|
||||||
|
|
||||||
|
google_id = user_info['sub']
|
||||||
|
user = User.query.filter_by(google_id=google_id).first()
|
||||||
|
if user:
|
||||||
|
|
||||||
|
login_user(user)
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
username = f"user_{uuid.uuid4().hex[:8]}"
|
||||||
|
user = User(
|
||||||
|
username=username,
|
||||||
|
google_id=google_id,
|
||||||
|
ip_address=get_client_ip()
|
||||||
|
)
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
login_user(user)
|
||||||
|
return redirect(url_for('index'))
|
21
config.py
21
config.py
@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv, find_dotenv
|
from dotenv import load_dotenv, find_dotenv
|
||||||
|
from authlib.integrations.flask_client import OAuth
|
||||||
|
|
||||||
dotenv_path = find_dotenv()
|
dotenv_path = find_dotenv()
|
||||||
load_dotenv(dotenv_path, override=True)
|
load_dotenv(dotenv_path, override=True)
|
||||||
@ -10,6 +11,9 @@ class Config:
|
|||||||
RECAPTCHA_PUBLIC_KEY = os.getenv('RECAPTCHA_PUBLIC_KEY')
|
RECAPTCHA_PUBLIC_KEY = os.getenv('RECAPTCHA_PUBLIC_KEY')
|
||||||
RECAPTCHA_PRIVATE_KEY = os.getenv('RECAPTCHA_PRIVATE_KEY')
|
RECAPTCHA_PRIVATE_KEY = os.getenv('RECAPTCHA_PRIVATE_KEY')
|
||||||
SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI')
|
SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI')
|
||||||
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
|
BASE_DOMAIN = os.getenv('BASE_DOMAIN', 'http://localhost:5000')
|
||||||
|
GOOGLE_REDIRECT_URI = f"{BASE_DOMAIN}/auth/google/callback"
|
||||||
UPLOAD_FOLDER = {
|
UPLOAD_FOLDER = {
|
||||||
'images': 'static/arts/',
|
'images': 'static/arts/',
|
||||||
'arts': 'static/arts/',
|
'arts': 'static/arts/',
|
||||||
@ -25,3 +29,20 @@ class Config:
|
|||||||
ALLOWED_VIDEO_EXTENSIONS = {'mp4', 'avi', 'mov'}
|
ALLOWED_VIDEO_EXTENSIONS = {'mp4', 'avi', 'mov'}
|
||||||
MAX_IMAGE_SIZE = 15 * 1024 * 1024
|
MAX_IMAGE_SIZE = 15 * 1024 * 1024
|
||||||
MAX_VIDEO_SIZE = 10 * 1024 * 1024 * 1024
|
MAX_VIDEO_SIZE = 10 * 1024 * 1024 * 1024
|
||||||
|
|
||||||
|
GOOGLE_CLIENT_ID = os.getenv('GOOGLE_CLIENT_ID')
|
||||||
|
GOOGLE_CLIENT_SECRET = os.getenv('GOOGLE_CLIENT_SECRET')
|
||||||
|
GOOGLE_REDIRECT_URI = os.getenv('GOOGLE_REDIRECT_URI', 'http://localhost:5000/auth/google/callback')
|
||||||
|
|
||||||
|
def configure_oauth(app):
|
||||||
|
oauth = OAuth(app)
|
||||||
|
oauth.register(
|
||||||
|
name='google',
|
||||||
|
client_id=Config.GOOGLE_CLIENT_ID,
|
||||||
|
client_secret=Config.GOOGLE_CLIENT_SECRET,
|
||||||
|
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
|
||||||
|
client_kwargs={
|
||||||
|
'scope': 'openid profile'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return oauth
|
@ -54,7 +54,8 @@ class Comments(db.Model):
|
|||||||
class User(db.Model, UserMixin):
|
class User(db.Model, UserMixin):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
username = db.Column(db.String(20), unique=True, nullable=False)
|
username = db.Column(db.String(20), unique=True, nullable=False)
|
||||||
encrypted_password = db.Column(db.String(60), nullable=False)
|
encrypted_password = db.Column(db.String(60), nullable=True)
|
||||||
|
google_id = db.Column(db.String(255), unique=True, nullable=True)
|
||||||
ip_address = db.Column(db.String(15), nullable=True)
|
ip_address = db.Column(db.String(15), nullable=True)
|
||||||
avatar_file = db.Column(db.String(50), nullable=True)
|
avatar_file = db.Column(db.String(50), nullable=True)
|
||||||
banner_file = db.Column(db.String(50), nullable=True)
|
banner_file = db.Column(db.String(50), nullable=True)
|
||||||
@ -65,7 +66,9 @@ class User(db.Model, UserMixin):
|
|||||||
return f'<User {self.username}>'
|
return f'<User {self.username}>'
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password):
|
||||||
|
if self.encrypted_password:
|
||||||
return bcrypt.check_password_hash(self.encrypted_password, password)
|
return bcrypt.check_password_hash(self.encrypted_password, password)
|
||||||
|
return False
|
||||||
|
|
||||||
class Image(db.Model):
|
class Image(db.Model):
|
||||||
__tablename__ = 'image'
|
__tablename__ = 'image'
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
$dark-violet: #0D0C1C;
|
@import url("https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Playwrite+IT+Moderna:wght@100..400&display=swap");
|
||||||
$violet: #3C3882;
|
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap");
|
||||||
$light-violet: #8784C9;
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Playwrite+IT+Moderna:wght@100..400&display=swap');
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
|
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #05040A;
|
background-color: #05040A;
|
||||||
font-family: Nunito, sans-serif;
|
font-family: Nunito, sans-serif;
|
||||||
@ -21,7 +16,6 @@ html, body {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -81,11 +75,12 @@ html, body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding: 0px;
|
padding: 0;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
width: 1502px;
|
width: 1502px;
|
||||||
height: 817px;
|
height: 817px;
|
||||||
left: 209px;
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 740px;
|
top: 740px;
|
||||||
}
|
}
|
||||||
@ -109,11 +104,12 @@ html, body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding: 0px;
|
padding: 0;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
width: 1502px;
|
width: 1502px;
|
||||||
height: 631px;
|
height: 631px;
|
||||||
left: 209px;
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1617px;
|
top: 1617px;
|
||||||
}
|
}
|
||||||
@ -143,7 +139,8 @@ html, body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2494px;
|
top: 2494px;
|
||||||
left: 210px;
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +153,7 @@ html, body {
|
|||||||
letter-spacing: 0%;
|
letter-spacing: 0%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popular-content-text {
|
.popular-content-text {
|
||||||
@ -168,7 +165,7 @@ html, body {
|
|||||||
letter-spacing: 0%;
|
letter-spacing: 0%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewed-content-text {
|
.viewed-content-text {
|
||||||
@ -180,7 +177,7 @@ html, body {
|
|||||||
letter-spacing: 0%;
|
letter-spacing: 0%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popular-categories-text {
|
.popular-categories-text {
|
||||||
@ -192,7 +189,7 @@ html, body {
|
|||||||
letter-spacing: 0%;
|
letter-spacing: 0%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-more-button {
|
.view-more-button {
|
||||||
@ -204,7 +201,7 @@ html, body {
|
|||||||
padding-right: 679px;
|
padding-right: 679px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
padding-left: 679px;
|
padding-left: 679px;
|
||||||
background-color: $light-violet;
|
background-color: #8784C9;
|
||||||
border: none;
|
border: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -218,14 +215,13 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.view-more-button:hover {
|
.view-more-button:hover {
|
||||||
background-color: $violet;
|
background-color: #3C3882;
|
||||||
transition: background-color 0.3s ease, color 0.3s ease;
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-more-button:hover .new-context-button-text {
|
.view-more-button:hover .new-context-button-text {
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-view-more-button {
|
.img-view-more-button {
|
||||||
@ -237,7 +233,7 @@ html, body {
|
|||||||
padding-right: 679px;
|
padding-right: 679px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
padding-left: 679px;
|
padding-left: 679px;
|
||||||
background-color: $light-violet;
|
background-color: #8784C9;
|
||||||
border: none;
|
border: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -251,12 +247,12 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.img-view-more-button:hover {
|
.img-view-more-button:hover {
|
||||||
background-color: $violet;
|
background-color: #3C3882;
|
||||||
transition: background-color 0.3s ease, color 0.3s ease;
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-view-more-button:hover .new-context-button-text {
|
.img-view-more-button:hover .new-context-button-text {
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,16 +263,15 @@ html, body {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
letter-spacing: 0%;
|
letter-spacing: 0%;
|
||||||
color: $violet;
|
color: #3C3882;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* navbar */
|
/* navbar */
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1500px;
|
max-width: 1500px;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
background: $dark-violet;
|
background: #0D0C1C;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
@ -285,7 +280,7 @@ html, body {
|
|||||||
|
|
||||||
.navbar-wrapper {
|
.navbar-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: $dark-violet;
|
background: #0D0C1C;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
@ -300,10 +295,10 @@ html, body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
width: 452.5px;
|
width: 452.5px;
|
||||||
height: 27px;
|
height: 27px;
|
||||||
border: 1px solid $light-violet;
|
border: 1px solid #8784C9;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
background: $dark-violet;
|
background: #0D0C1C;
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: border-color 0.3s ease;
|
transition: border-color 0.3s ease;
|
||||||
}
|
}
|
||||||
@ -345,16 +340,16 @@ html, body {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
background-color: $dark-violet;
|
background-color: #0D0C1C;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container:hover {
|
.search-container:hover {
|
||||||
border-color: $violet;
|
border-color: #3C3882;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container:focus-within {
|
.search-container:focus-within {
|
||||||
border-color: $violet;
|
border-color: #3C3882;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-container {
|
.icon-container {
|
||||||
@ -379,12 +374,12 @@ html, body {
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 23px;
|
width: 23px;
|
||||||
height: 27px;
|
height: 27px;
|
||||||
border: 1px solid $light-violet;
|
border: 1px solid #8784C9;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: $dark-violet;
|
background: #0D0C1C;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
transition: border-color 0.3s ease, transform 0.3s ease, opacity 0.3s ease;
|
transition: border-color 0.3s ease, transform 0.3s ease, opacity 0.3s ease;
|
||||||
@ -414,7 +409,7 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.translate-btn:hover {
|
.translate-btn:hover {
|
||||||
border-color: $violet;
|
border-color: #3C3882;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-icon {
|
.overlay-icon {
|
||||||
@ -439,13 +434,13 @@ html, body {
|
|||||||
|
|
||||||
.menu a {
|
.menu a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu a:hover {
|
.menu a:hover {
|
||||||
color: $violet;
|
color: #3C3882;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-container {
|
.auth-container {
|
||||||
@ -486,31 +481,31 @@ html, body {
|
|||||||
width: 87px;
|
width: 87px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background: $light-violet;
|
background: #8784C9;
|
||||||
border: 1px solid $light-violet;
|
border: 1px solid #8784C9;
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 21.82px;
|
line-height: 21.82px;
|
||||||
letter-spacing: 0%;
|
letter-spacing: 0%;
|
||||||
color: $dark-violet;
|
color: #0D0C1C;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
|
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-btn:hover {
|
.login-btn:hover {
|
||||||
background-color: $violet;
|
background-color: #3C3882;
|
||||||
color: $dark-violet;
|
color: #0D0C1C;
|
||||||
border-color: $violet;
|
border-color: #3C3882;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
right: 0;
|
right: 0;
|
||||||
background: $dark-violet;
|
background: #0D0C1C;
|
||||||
border: 1px solid $violet;
|
border: 1px solid #3C3882;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
display: none;
|
display: none;
|
||||||
@ -523,18 +518,17 @@ html, body {
|
|||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.3s ease, color 0.3s ease;
|
transition: background 0.3s ease, color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item:hover {
|
.dropdown-item:hover {
|
||||||
background: $violet;
|
background: #3C3882;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tags-list */
|
/* tags-list */
|
||||||
|
|
||||||
.tags-container {
|
.tags-container {
|
||||||
width: 1500px;
|
width: 1500px;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
@ -549,13 +543,13 @@ html, body {
|
|||||||
border: none;
|
border: none;
|
||||||
background-color: #05040A;
|
background-color: #05040A;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
> .list-button {
|
.tags-container > .list-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
height: 35px;
|
height: 35px;
|
||||||
@ -564,9 +558,9 @@ html, body {
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: $violet;
|
background-color: #3C3882;
|
||||||
border: none;
|
border: none;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transition: visibility 0.3s ease;
|
transition: visibility 0.3s ease;
|
||||||
@ -580,9 +574,9 @@ html, body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 2px solid $light-violet;
|
border: 2px solid #8784C9;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: $violet;
|
color: #3C3882;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -598,7 +592,6 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* cards */
|
/* cards */
|
||||||
|
|
||||||
.img-cards-grid {
|
.img-cards-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(6, 1fr);
|
grid-template-columns: repeat(6, 1fr);
|
||||||
@ -695,7 +688,7 @@ html, body {
|
|||||||
|
|
||||||
.card-text {
|
.card-text {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-small-cards-grid {
|
.img-small-cards-grid {
|
||||||
@ -712,7 +705,6 @@ html, body {
|
|||||||
background: #1D1C2E;
|
background: #1D1C2E;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.img-small-card {
|
.img-small-card {
|
||||||
width: 189px;
|
width: 189px;
|
||||||
height: 314px;
|
height: 314px;
|
||||||
@ -793,7 +785,7 @@ html, body {
|
|||||||
|
|
||||||
.small-card-text {
|
.small-card-text {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pc-card {
|
.pc-card {
|
||||||
@ -825,7 +817,7 @@ html, body {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
letter-spacing: 0%;
|
letter-spacing: 0%;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
margin-left: -2px;
|
margin-left: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -836,7 +828,7 @@ html, body {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
letter-spacing: 0%;
|
letter-spacing: 0%;
|
||||||
color: $violet;
|
color: #3C3882;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
position: relative;
|
position: relative;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
@ -891,7 +883,7 @@ html, body {
|
|||||||
|
|
||||||
.ct-small-card-text {
|
.ct-small-card-text {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $light-violet;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ac-img-small-cards-grid {
|
.ac-img-small-cards-grid {
|
||||||
@ -906,7 +898,7 @@ html, body {
|
|||||||
width: 276px;
|
width: 276px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: $violet;
|
background-color: #3C3882;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -927,7 +919,6 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* pagination */
|
/* pagination */
|
||||||
|
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
width: 626px;
|
width: 626px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
@ -938,34 +929,35 @@ html, body {
|
|||||||
bottom: -180%;
|
bottom: -180%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
.swipe-button {
|
.pagination-container .swipe-button {
|
||||||
width: 128px;
|
width: 128px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 7px 45px;
|
padding: 7px 45px;
|
||||||
background-color: $violet;
|
background-color: #3C3882;
|
||||||
border: none;
|
border: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-buttons-container {
|
.pagination-container .page-buttons-container {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.page-button {
|
.pagination-container .page-buttons-container .page-button {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 1px 6px;
|
padding: 1px 6px;
|
||||||
background-color: $violet;
|
background-color: #3C3882;
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -973,8 +965,6 @@ html, body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: Inter;
|
font-family: Inter;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#modal-container {
|
#modal-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -990,44 +980,49 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
width: 312px;
|
width: 100%;
|
||||||
height: 400px;
|
max-width: 380px;
|
||||||
|
height: auto;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border-width: thin;
|
border: 1px solid #3C3882;
|
||||||
border-color: #3C3882;
|
|
||||||
border-style: solid;
|
|
||||||
padding: 30px 40px;
|
padding: 30px 40px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background-color: #0D0C1C;
|
background-color: #0D0C1C;
|
||||||
display: none;
|
display: none;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal.active {
|
.modal.active {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-inner-container {
|
.form-inner-container {
|
||||||
width: 312px;
|
width: 100%;
|
||||||
height: 400px;
|
max-width: 312px;
|
||||||
gap: 10px;
|
gap: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.reg-form-title {
|
.reg-form-title {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: "Nunito", sans-serif;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-register-input-container {
|
.modal-register-input-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 39px;
|
height: 39px;
|
||||||
@ -1037,6 +1032,7 @@ html, body {
|
|||||||
position: relative;
|
position: relative;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-register-text-input {
|
.modal-register-text-input {
|
||||||
width: calc(100% - 31px);
|
width: calc(100% - 31px);
|
||||||
height: 16px;
|
height: 16px;
|
||||||
@ -1047,16 +1043,10 @@ html, body {
|
|||||||
outline: none;
|
outline: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: "Nunito", sans-serif;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
.modal-text-input::placeholder {
|
|
||||||
color: #8784C9;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
.password-input {
|
|
||||||
-webkit-text-security: disc;
|
|
||||||
}
|
|
||||||
.modal-register-button {
|
.modal-register-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 38px;
|
height: 38px;
|
||||||
@ -1069,20 +1059,24 @@ html, body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-register-button:hover {
|
.modal-register-button:hover {
|
||||||
background-color: #3C3882;
|
background-color: #3C3882;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-register-button-text {
|
.modal-register-button-text {
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: "Nunito", sans-serif;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #3C3882;
|
color: #3C3882;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-register-button:hover .modal-register-button-text {
|
.modal-register-button:hover .modal-register-button-text {
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-link-container {
|
.login-link-container {
|
||||||
width: 312px;
|
width: 312px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
@ -1093,7 +1087,7 @@ margin-bottom: 10px;
|
|||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: "Nunito", sans-serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
@ -1111,13 +1105,14 @@ color: #8784C9;
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: "Nunito", sans-serif;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-login-input-container {
|
.modal-login-input-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 39px;
|
height: 39px;
|
||||||
@ -1127,6 +1122,7 @@ color: #8784C9;
|
|||||||
position: relative;
|
position: relative;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-login-text-input {
|
.modal-login-text-input {
|
||||||
width: calc(100% - 31px);
|
width: calc(100% - 31px);
|
||||||
height: 16px;
|
height: 16px;
|
||||||
@ -1137,16 +1133,19 @@ color: #8784C9;
|
|||||||
outline: none;
|
outline: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: "Nunito", sans-serif;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-text-input::placeholder {
|
.modal-text-input::placeholder {
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-input {
|
.password-input {
|
||||||
-webkit-text-security: disc;
|
-webkit-text-security: disc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-login-button {
|
.modal-login-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 38px;
|
height: 38px;
|
||||||
@ -1159,37 +1158,74 @@ color: #8784C9;
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-login-button:hover {
|
.modal-login-button:hover {
|
||||||
background-color: #9d9ad4;
|
background-color: #3C3882;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-login-button-text {
|
.modal-login-button-text {
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: "Nunito", sans-serif;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #3C3882;
|
color: #3C3882;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-link-container {
|
.modal-login-button:hover .modal-login-button-text {
|
||||||
width: 312px;
|
|
||||||
height: 16px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-prompt {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 16px;
|
|
||||||
font-family: 'Nunito', sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 30px;
|
|
||||||
letter-spacing: 0;
|
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-login-button-text {
|
||||||
|
font-family: "Nunito", sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #3C3882;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.login-link {
|
.login-link {
|
||||||
text-decoration: underline solid 0px;
|
text-decoration: underline solid 0px;
|
||||||
text-decoration-skip-ink: auto;
|
text-decoration-skip-ink: auto;
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.social-login-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.google-login-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 312px;
|
||||||
|
height: 39px;
|
||||||
|
gap: 5px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #222;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.google-button-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.google-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.google-button-text {
|
||||||
|
font-family: "Nunito", sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 100%;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
1
static/css/styles.scss.map
Normal file
1
static/css/styles.scss.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":["styles.css"],"names":[],"mappings":"AAAQ;AACA;AACR;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF","file":"styles.scss"}
|
14
static/icons/google.svg
Normal file
14
static/icons/google.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_4_95)">
|
||||||
|
<path d="M5.64829 0.937973C4.10051 1.4991 2.76572 2.56415 1.83996 3.97667C0.9142 5.38919 0.446273 7.07474 0.504909 8.78572C0.563545 10.4967 1.14565 12.143 2.16573 13.4826C3.18581 14.8223 4.59009 15.7849 6.17231 16.2288C7.45505 16.5747 8.79898 16.5899 10.0886 16.2731C11.2568 15.9989 12.3369 15.4123 13.223 14.5707C14.1453 13.6682 14.8148 12.5199 15.1594 11.2495C15.534 9.86798 15.6006 8.41511 15.3542 7.00247H8.15102V10.1251H12.3226C12.2393 10.6232 12.0606 11.0985 11.7973 11.5227C11.5341 11.9469 11.1916 12.3113 10.7905 12.5939C10.2811 12.9461 9.70687 13.183 9.10467 13.2896C8.5007 13.4069 7.88121 13.4069 7.27724 13.2896C6.6651 13.1573 6.08602 12.8933 5.57688 12.5143C4.75895 11.9092 4.1448 11.0496 3.82207 10.0581C3.49387 9.04807 3.49387 7.95306 3.82207 6.94302C4.0518 6.23503 4.43157 5.59041 4.93305 5.05727C5.50692 4.43596 6.23346 3.99184 7.03296 3.77365C7.83246 3.55545 8.67402 3.57161 9.46531 3.82035C10.0835 4.01865 10.6487 4.36512 11.1161 4.83215C11.5864 4.34311 12.056 3.85281 12.5247 3.36124C12.7668 3.09691 13.0306 2.84522 13.269 2.57456C12.5557 1.88082 11.7184 1.341 10.805 0.986033C9.14183 0.354909 7.32199 0.337948 5.64829 0.937973Z" fill="black"/>
|
||||||
|
<path d="M5.85226 0.908712C7.53202 0.349179 9.3586 0.364569 11.0281 0.95232C11.945 1.28535 12.785 1.79082 13.5 2.43972C13.2571 2.69194 13.0008 2.92766 12.753 3.17281C12.2817 3.62932 11.8108 4.08426 11.3403 4.53763C10.8712 4.10241 10.3038 3.77954 9.68342 3.59475C8.88946 3.36214 8.04481 3.34621 7.24214 3.54871C6.43947 3.75121 5.70979 4.16432 5.13316 4.7427C4.62983 5.23953 4.24865 5.84024 4.01807 6.5L1.5 4.60834C2.40132 2.8741 3.96189 1.54754 5.85226 0.908712Z" fill="#E33629"/>
|
||||||
|
<path d="M0.635417 6.45951C0.757101 5.77897 0.95912 5.11993 1.23607 4.5L3.5 6.48292C3.20384 7.46712 3.20384 8.53411 3.5 9.51831C2.74572 10.1756 1.99108 10.8362 1.23607 11.5C0.542753 9.94264 0.331303 8.16822 0.635417 6.45951Z" fill="#F8BD00"/>
|
||||||
|
<path d="M8.50059 7.5H16.3413C16.6095 8.80657 16.537 10.1503 16.1292 11.4281C15.7541 12.6032 15.0254 13.6652 14.0215 14.5C13.1402 13.8894 12.255 13.2834 11.3737 12.6728C11.8106 12.4111 12.1835 12.0737 12.4701 11.681C12.7567 11.2882 12.951 10.8481 13.0414 10.387H8.50059C8.49927 9.42547 8.50059 8.46273 8.50059 7.5Z" fill="#587DBD"/>
|
||||||
|
<path d="M1.5 12.3961C2.34258 11.7672 3.18474 11.1352 4.02651 10.5C4.35217 11.4247 4.97155 12.2262 5.79616 12.7899C6.31049 13.1416 6.89492 13.3858 7.51219 13.5068C8.12042 13.6163 8.74429 13.6163 9.35253 13.5068C9.95898 13.4075 10.5373 13.1866 11.0503 12.8583C11.8656 13.4738 12.6846 14.0846 13.5 14.7001C12.6077 15.4851 11.52 16.0324 10.3434 16.2885C9.04468 16.5839 7.69125 16.5697 6.39945 16.2472C5.37776 15.9833 4.42343 15.518 3.59628 14.8806C2.72079 14.2081 2.00573 13.3606 1.5 12.3961Z" fill="#319F43"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_4_95">
|
||||||
|
<rect width="16" height="16" fill="white" transform="translate(0.5 0.5)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
@ -15,7 +15,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Делегируем клики для открытия логина/регистрации внутри любых модалок
|
|
||||||
document.addEventListener('click', function (e) {
|
document.addEventListener('click', function (e) {
|
||||||
const target = e.target.closest('[data-action]');
|
const target = e.target.closest('[data-action]');
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
@ -3,7 +3,14 @@
|
|||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
<div class="form-inner-container">
|
<div class="form-inner-container">
|
||||||
<p class="login-form-title">ВХОД</p>
|
<p class="login-form-title">ВХОД</p>
|
||||||
|
<div class="social-login-container">
|
||||||
|
<a href="{{ url_for('auth.login_google') }}" class="google-login-button">
|
||||||
|
<div class="google-button-content">
|
||||||
|
<img src="/static/icons/google.svg" alt="Google Icon" class="google-icon">
|
||||||
|
<span class="google-button-text">Войти с аккаунтом Google</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div class="modal-login-input-container">
|
<div class="modal-login-input-container">
|
||||||
{{ form.username(class_="modal-login-text-input", placeholder="Имя пользователя") }}
|
{{ form.username(class_="modal-login-text-input", placeholder="Имя пользователя") }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
<div class="form-inner-container">
|
<div class="form-inner-container">
|
||||||
<p class="reg-form-title">РЕГИСТРАЦИЯ</p>
|
<p class="reg-form-title">РЕГИСТРАЦИЯ</p>
|
||||||
|
<div class="social-login-container">
|
||||||
|
<a href="{{ url_for('auth.login_google') }}" class="google-login-button">
|
||||||
|
<div class="google-button-content">
|
||||||
|
<img src="/static/icons/google.svg" alt="Google Icon" class="google-icon">
|
||||||
|
<span class="google-button-text">Войти с аккаунтом Google</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal-register-input-container">
|
<div class="modal-register-input-container">
|
||||||
{{ form.username(class_="modal-register-text-input", placeholder="Имя пользователя") }}
|
{{ form.username(class_="modal-register-text-input", placeholder="Имя пользователя") }}
|
||||||
|
@ -12,31 +12,31 @@
|
|||||||
|
|
||||||
.subnav-container {
|
.subnav-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 3rem; /* Используем rem вместо пикселей */
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #3C3882;
|
background-color: #3C3882;
|
||||||
padding: 0 20px;
|
padding: 0 1.25rem; /* Относительные отступы */
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 112px;
|
top: 7rem; /* Используем rem для позиции */
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 50px;
|
height: 100%; /* Высота кнопки равна высоте контейнера */
|
||||||
background-color: #3C3882;
|
background-color: #3C3882;
|
||||||
border: none;
|
border: none;
|
||||||
font-size: 16px;
|
font-size: 1rem; /* Относительный размер шрифта */
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #8784C9;
|
color: #8784C9;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 15px 0;
|
padding: 0.9375rem 0; /* Относительные отступы */
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:not(:last-child) {
|
.button:not(:last-child) {
|
||||||
margin-right: 20px;
|
margin-right: 1.25rem; /* Относительный отступ между кнопками */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user