Google auth provided

This commit is contained in:
aneuhmanh 2025-04-20 23:35:38 +03:00
parent c9e8e5386f
commit 00ad7ddef1
12 changed files with 1394 additions and 1225 deletions

1
.gitignore vendored
View File

@ -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
View File

@ -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
View File

@ -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'))

View File

@ -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

View File

@ -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'

View File

@ -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;
}

View 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
View 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

View File

@ -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;

View File

@ -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>

View File

@ -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="Имя пользователя") }}

View File

@ -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>