api js
@@ -0,0 +1,32 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 15b87c1584bf
|
||||
Revises: 2e39b25a3b28
|
||||
Create Date: 2026-03-13 22:53:40.498042
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '15b87c1584bf'
|
||||
down_revision: Union[str, Sequence[str], None] = 'dd476c0dcf61'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'food')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('food', sa.BOOLEAN(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,32 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 1fa13c2c4df4
|
||||
Revises: 15b87c1584bf
|
||||
Create Date: 2026-03-13 22:56:36.115487
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '1fa13c2c4df4'
|
||||
down_revision: Union[str, Sequence[str], None] = '15b87c1584bf'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('type_of_food', sa.String(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'type_of_food')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,32 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 55f24e794643
|
||||
Revises: 1fa13c2c4df4
|
||||
Create Date: 2026-03-13 23:03:47.236864
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '55f24e794643'
|
||||
down_revision: Union[str, Sequence[str], None] = '1fa13c2c4df4'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('middlename', sa.String(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'middlename')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,32 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: dd476c0dcf61
|
||||
Revises: 1e2bd98e74a5
|
||||
Create Date: 2026-03-13 22:38:32.065614
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'dd476c0dcf61'
|
||||
down_revision: Union[str, Sequence[str], None] = '1e2bd98e74a5'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'alco')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('alco', sa.BOOLEAN(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
@@ -23,11 +23,11 @@ class User(Base):
|
||||
code = Column(String, unique=True, nullable=True)
|
||||
|
||||
name = Column(String, nullable=True)
|
||||
middlename=Column(String, nullable=True)
|
||||
surname = Column(String, nullable=True)
|
||||
text_field = Column(String, nullable=True)
|
||||
food = Column(Boolean)
|
||||
alco = Column(Boolean)
|
||||
types_of_alco = Column(String, default="Nothing")
|
||||
type_of_food = Column(String, nullable=True)
|
||||
types_of_alco = Column(String, default="Nothing", nullable=True)
|
||||
|
||||
activated = Column(Boolean)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
@@ -2,6 +2,7 @@ from pydantic import BaseModel, Field, field_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from pydantic.types import StringConstraints
|
||||
from typing_extensions import Annotated
|
||||
from typing import Optional
|
||||
import re
|
||||
|
||||
NameStr = Annotated[
|
||||
@@ -23,16 +24,18 @@ class UserOut(BaseModel):
|
||||
name: NameStr = Field(..., description="Name of the guest")
|
||||
surname: NameStr = Field(..., description="Surname of the guest")
|
||||
|
||||
class UserUpdate(UserAccess):
|
||||
name: NameStr = Field(..., description="Name of the guest")
|
||||
surname: NameStr = Field(..., description="Surname of the guest")
|
||||
text_field: str = Field("", max_length=500, description="what the guest wants")
|
||||
activated: bool = Field(False, description="activation of the guest")
|
||||
food: bool = Field(False, description="Options meat or fish")
|
||||
alco: bool = Field(False, description="if the guest will drink alco or not")
|
||||
types_of_alco: str = Field("", description="types of alco")
|
||||
class UserUpdate(BaseModel):
|
||||
code: Optional[str] = Field(None, min_length=6, max_length=6, description="Code of the guest")
|
||||
name: Optional[NameStr] = Field(None, description="Name of the guest")
|
||||
middlename: Optional[NameStr] = Field(None, description="Middlename of the guest")
|
||||
surname: Optional[NameStr] = Field(None, description="Surname of the guest")
|
||||
text_field: Optional[str] = Field(None, max_length=500, description="what the guest wants")
|
||||
activated: Optional[bool] = Field(None, description="activation of the guest")
|
||||
type_of_food: Optional[str] = Field(None, description="meat or fish")
|
||||
types_of_alco: Optional[str] = Field(None, description="types of alco")
|
||||
|
||||
class UserCreate(UserUpdate):
|
||||
code: str = Field(..., min_length=6, max_length=6, description="Code of the guest")
|
||||
admin:bool = Field(False, description="Admin privilegies")
|
||||
class Settings(BaseSettings):
|
||||
DIR:str
|
||||
|
||||
0
server/frontend/auth/login.css
Normal file
21
server/frontend/auth/login.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login</title>
|
||||
<link rel="stylesheet" href="login.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="glass-container">
|
||||
<div class="login-box">
|
||||
<h2>Login</h2>
|
||||
<form id="loginForm">
|
||||
<input type="password" id="password" name="password" required placeholder="Code">
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script src="login.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
74
server/frontend/auth/login.js
Normal file
@@ -0,0 +1,74 @@
|
||||
function getToken() {
|
||||
return localStorage.getItem("token") || sessionStorage.getItem("token");
|
||||
}
|
||||
function tokenCheck(){
|
||||
const token = getToken();
|
||||
if (token) {
|
||||
window.location.href = "http://localhost:5500/server/frontend/main/index.html";
|
||||
}
|
||||
}
|
||||
document.getElementById('loginForm').addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
const password = document.getElementById('password').value;
|
||||
const userData = {
|
||||
password
|
||||
};
|
||||
try {
|
||||
const response = await fetch("http://localhost:8000/api/auth", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
password: userData.password
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json(); // читаем только один раз
|
||||
if (response.ok) { // сохраняем только при успехе
|
||||
// в sessionstorage до перезахода в браузер(
|
||||
sessionStorage.setItem("token", data.access_token);
|
||||
|
||||
window.location.href = 'http://localhost:5500/server/frontend/main/index.html';
|
||||
} else { //парсинг и вывод ошибок, если есть
|
||||
if (Array.isArray(data.detail)) {
|
||||
const messages = data.detail.map(e => {
|
||||
const field = e.loc.filter(locPart => locPart !== 'body').join(' -> ');
|
||||
return `${field}: ${e.msg}`;
|
||||
});
|
||||
showError(messages);
|
||||
} else if (typeof data.detail === 'string') {
|
||||
showError([data.detail]);
|
||||
} else {
|
||||
showError(['Unknown error']);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
showError(['Connection error: ' + err.message]);
|
||||
}
|
||||
});
|
||||
function showError(messages){ //Добавление их на form со стилями
|
||||
let errorElem = document.getElementById('formError');
|
||||
let container = document.getElementById('glass-container');
|
||||
if (!errorElem){
|
||||
errorElem = document.createElement('div');
|
||||
errorElem.style.transition="3s";
|
||||
errorElem.id = 'formError';
|
||||
errorElem.style.color = 'red';
|
||||
errorElem.style.marginTop = '20px';
|
||||
errorElem.style.fontSize = '14px';
|
||||
errorElem.style.fontWeight = '100';
|
||||
errorElem.style.marginBottom = '20px';
|
||||
errorElem.style.lineHeight="120%";
|
||||
errorElem.style.height = 'auto';
|
||||
const form = document.getElementById('loginForm');
|
||||
form.insertAdjacentElement('afterend', errorElem);
|
||||
};
|
||||
errorElem.innerHTML = '';
|
||||
messages.forEach(msg => {
|
||||
const li = document.createElement('li');
|
||||
li.style.listStyleType="none";
|
||||
li.textContent = msg;
|
||||
errorElem.appendChild(li);
|
||||
});
|
||||
}
|
||||
69
server/frontend/main/api.js
Normal file
@@ -0,0 +1,69 @@
|
||||
function getToken() {
|
||||
return localStorage.getItem("token") || sessionStorage.getItem("token");
|
||||
}
|
||||
function tokenCheck(){
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
window.location.href = "http://localhost:5500/server/frontend/auth/login.html";
|
||||
}
|
||||
}
|
||||
|
||||
tokenCheck()
|
||||
document.getElementById('logoutForm').addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
localStorage.removeItem("token");
|
||||
sessionStorage.removeItem("token");
|
||||
tokenCheck();
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const form = document.querySelector(".form-info");
|
||||
|
||||
form.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
// try {
|
||||
// собираем данные из формы
|
||||
const name = document.getElementById('ffname').value || "";
|
||||
const middlename = document.getElementById('fmname').value || "";
|
||||
const surname = document.getElementById('flname').value || "";
|
||||
const text_field = document.getElementById('text_field')?.value || "";
|
||||
const food = document.querySelector('input[name="food"]:checked')?.value || "";
|
||||
const types_of_alco = Array.from(document.querySelectorAll('input[name="drink"]:checked'))
|
||||
.map(el => el.value)
|
||||
.join(', ');
|
||||
|
||||
const guestData = {
|
||||
name: name,
|
||||
middlename: middlename,
|
||||
surname: surname,
|
||||
text_field: text_field,
|
||||
activated: true,
|
||||
types_of_food: food,
|
||||
types_of_alco: types_of_alco
|
||||
};
|
||||
console.log(guestData)
|
||||
|
||||
// отправка на /api/update с Authorization
|
||||
// const updateResponse = await fetch('/api/update', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// 'Authorization': `Bearer ${token}`
|
||||
// },
|
||||
// body: JSON.stringify(guestData)
|
||||
// });
|
||||
|
||||
// if (!updateResponse.ok) {
|
||||
// throw new Error('Ошибка при отправке формы');
|
||||
// }
|
||||
|
||||
// const updateData = await updateResponse.json();
|
||||
// console.log('Форма успешно отправлена:', updateData);
|
||||
|
||||
// } catch (err) {
|
||||
// console.error(err);
|
||||
// alert('Ошибка: ' + err.message);
|
||||
// }
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 288 KiB After Width: | Height: | Size: 288 KiB |
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 2.8 MiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 221 KiB |
|
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 175 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 362 KiB After Width: | Height: | Size: 362 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 525 KiB After Width: | Height: | Size: 525 KiB |
|
Before Width: | Height: | Size: 920 B After Width: | Height: | Size: 920 B |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 2.9 MiB After Width: | Height: | Size: 2.9 MiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
@@ -64,7 +64,7 @@
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<content class="content">
|
||||
<main class="content">
|
||||
<section class="heading" id="heading">
|
||||
<article class="heading_content">
|
||||
<div class="heading_cont">
|
||||
@@ -300,38 +300,27 @@
|
||||
</h4>
|
||||
<div class="transfer_inner">
|
||||
<div class="transfer_map">
|
||||
<map class="map">
|
||||
<div class="map">
|
||||
<div style="position:relative;overflow:hidden; border-radius: 33px;">
|
||||
<a href="https://yandex.com/maps/org/glavnoye_upravleniye_zapisi_aktov_grazhdanskogo_sostoyaniya/104116805635/?utm_medium=mapframe&utm_source=maps"
|
||||
<a href="https://www.google.com/maps/place/Glavnoye+Upravleniye+Zapisi+Aktov+Grazhdanskogo+Sostoyaniya+Tverskoy+Oblasti/@56.8571039,35.901453,17z/data=!3m1!4b1!4m6!3m5!1s0x46b687a4d4edacad:0x15df9a38c874eb5d!8m2!3d56.857101!4d35.9040279!16s%2Fg%2F11ls6j2lvj?entry=ttu&g_ep=EgoyMDI2MDMxMS4wIKXMDSoASAFQAw%3D%3D"
|
||||
style="color:#eee;font-size:12px;position:absolute;top:0px;">
|
||||
Главное управление записи актов гражданского состояния</a>
|
||||
<iframe
|
||||
src="https://yandex.com/map-widget/v1/?ll=35.903981%2C56.857281&mode=search&oid=104116805635&ol=biz&sctx=ZAAAAAgBEAAaKAoSCY3PZP88XSFAEVrW%2FWMhDklAEhIJzjgNUYU%2Fsz8RXD6Skh6Gtj8iBgABAgMEBSgKOABAgqANSAFiOnJlYXJyPXNjaGVtZV9Mb2NhbC9HZW91cHBlci9BZHZlcnRzL0N1c3RvbU1heGFkdi9FbmFibGVkPTFiOnJlYXJyPXNjaGVtZV9Mb2NhbC9HZW91cHBlci9BZHZlcnRzL0N1c3RvbU1heGFkdi9NYXhhZHY9MTViRHJlYXJyPXNjaGVtZV9Mb2NhbC9HZW91cHBlci9BZHZlcnRzL0N1c3RvbU1heGFkdi9SZWdpb25JZHM9WzEsMTAxNzRdYkByZWFycj1zY2hlbWVfTG9jYWwvR2VvdXBwZXIvQWR2ZXJ0cy9NYXhhZHZUb3BNaXgvTWF4YWR2Rm9yTWl4PTEwagJkZZ0BzczMPaABAKgBAL0BByG7v8IBBoPA4e6DA4ICE9C30LDQs9GBINGC0LLQtdGA0YyKAgCSAgIxNJoCDGRlc2t0b3AtbWFwcw%3D%3D&sll=35.903981%2C56.857281&sspn=0.001002%2C0.000999&text=%D0%B7%D0%B0%D0%B3%D1%81%20%D1%82%D0%B2%D0%B5%D1%80%D1%8C&z=19.23"
|
||||
width="760" height="500" frameborder="1" allowfullscreen="true"
|
||||
style="position:relative;"></iframe>
|
||||
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2181.349338718406!2d35.901452977352925!3d56.85710390639812!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x46b687a4d4edacad%3A0x15df9a38c874eb5d!2sGlavnoye%20Upravleniye%20Zapisi%20Aktov%20Grazhdanskogo%20Sostoyaniya%20Tverskoy%20Oblasti!5e0!3m2!1sen!2s!4v1773428540512!5m2!1sen!2s" width="760" height="500" style="border:1px;" allowfullscreen="true" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
|
||||
</div>
|
||||
<p class="map_adress">Тверь, Свободный переулок, 5</p>
|
||||
<h6 class="map_info">Начало в</h6>
|
||||
<h6 class="map_time">10:30</h6>
|
||||
</map>
|
||||
<map class="map">
|
||||
</div>
|
||||
<div class="map">
|
||||
<div style="position:relative;overflow:hidden; border-radius: 33px;">
|
||||
<a href="https://yandex.com/maps/org/loft_1870/90344521327/?utm_medium=mapframe&utm_source=maps"
|
||||
style="color:#eee;font-size:12px;position:absolute;top:0px;">Лофт 1870</a><a
|
||||
href="https://yandex.com/maps/14/tver/category/banquet_hall/184108315/?utm_medium=mapframe&utm_source=maps"
|
||||
style="color:#eee;font-size:12px;position:absolute;top:14px;">Банкетный зал в
|
||||
Твери</a><a
|
||||
href="https://yandex.com/maps/14/tver/category/organization_of_events/184108329/?utm_medium=mapframe&utm_source=maps"
|
||||
style="color:#eee;font-size:12px;position:absolute;top:28px;">Организация мероприятий в
|
||||
Твери</a><iframe
|
||||
src="https://yandex.com/map-widget/v1/?filter=alternate_vertical%3AWhatWhere&ll=35.862462%2C56.851043&mode=search&oid=90344521327&ol=biz&sctx=ZAAAAAgCEAAaKAoSCUqaP6a180FAEbg9QWK7bUxAEhIJ8wTCTrFqUD8RgXwJFRxeUD8iBgABAgMEBSgKOABAgaANSAFiOnJlYXJyPXNjaGVtZV9Mb2NhbC9HZW91cHBlci9BZHZlcnRzL0N1c3RvbU1heGFkdi9FbmFibGVkPTFiOnJlYXJyPXNjaGVtZV9Mb2NhbC9HZW91cHBlci9BZHZlcnRzL0N1c3RvbU1heGFkdi9NYXhhZHY9MTViRHJlYXJyPXNjaGVtZV9Mb2NhbC9HZW91cHBlci9BZHZlcnRzL0N1c3RvbU1heGFkdi9SZWdpb25JZHM9WzEsMTAxNzRdYkByZWFycj1zY2hlbWVfTG9jYWwvR2VvdXBwZXIvQWR2ZXJ0cy9NYXhhZHZUb3BNaXgvTWF4YWR2Rm9yTWl4PTEwagJydZ0BzczMPaABAKgBAL0BvACOKsIBKO%2F8z8fQAtiGgZHvBbCG7O6vBo79quPrAY6e17vSBOOAnfSN%2BvnKggGCAhPQu9C%2B0YTRgiDRgtCy0LXRgNGMigIAkgICMTSaAgxkZXNrdG9wLW1hcHM%3D&sll=35.862462%2C56.851043&sspn=0.007794%2C0.007771&text=%D0%BB%D0%BE%D1%84%D1%82%20%D1%82%D0%B2%D0%B5%D1%80%D1%8C&z=16.27"
|
||||
width="760" height="500" frameborder="1" allowfullscreen="true"
|
||||
style="position:relative;"></iframe>
|
||||
<a href="https://www.google.com/maps/place/Loft+1870+%7C+%D0%9F%D0%BB%D0%BE%D1%89%D0%B0%D0%B4%D0%BA%D0%B0+%D0%B4%D0%BB%D1%8F+%D0%BC%D0%B5%D1%80%D0%BE%D0%BF%D1%80%D0%B8%D1%8F%D1%82%D0%B8%D0%B9+%D0%A2%D0%B2%D0%B5%D1%80%D1%8C+%7C+%D0%BA%D0%B5%D0%B9%D1%82%D0%B5%D1%80%D0%B8%D0%BD%D0%B3+%D0%BD%D0%B0+%D1%81%D0%B2%D0%B0%D0%B4%D1%8C%D0%B1%D1%83,+%D0%B1%D0%B0%D0%BD%D0%BA%D0%B5%D1%82%D0%BD%D1%8B%D0%B9+%D0%B7%D0%B0%D0%BB/@56.850656,35.8603082,17.32z/data=!3m1!5s0x46b687a53bac0dcf:0x5c270d07d710d5a!4m6!3m5!1s0x46b68764eb0c1387:0x7cb135f5414bc1e0!8m2!3d56.8508612!4d35.8650531!16s%2Fg%2F11p0wc5v7q?entry=ttu&g_ep=EgoyMDI2MDMxMS4wIKXMDSoASAFQAw%3D%3D"
|
||||
style="color:#eee;font-size:12px;position:absolute;top:0px;">Лофт 1870</a>
|
||||
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d1749.675516895515!2d35.86030818806184!3d56.850656040536194!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x46b68764eb0c1387%3A0x7cb135f5414bc1e0!2zTG9mdCAxODcwIHwg0J_Qu9C-0YnQsNC00LrQsCDQtNC70Y8g0LzQtdGA0L7Qv9GA0LjRj9GC0LjQuSDQotCy0LXRgNGMIHwg0LrQtdC50YLQtdGA0LjQvdCzINC90LAg0YHQstCw0LTRjNCx0YMsINCx0LDQvdC60LXRgtC90YvQuSDQt9Cw0Ls!5e0!3m2!1sen!2s!4v1773428705434!5m2!1sen!2s" width="760" height="500" style="border:1px;" allowfullscreen="true" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
|
||||
</div>
|
||||
<p class="map_adress">Тверь, улица Двор Пролетарки, 16</p>
|
||||
<h6 class="map_info">Начало в</h6>
|
||||
<h6 class="map_time">14:00</h6>
|
||||
</map>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -434,20 +423,20 @@
|
||||
Просим Вас заполнить форму и подтвердить своё участие
|
||||
</h6>
|
||||
|
||||
<form class="form-example">
|
||||
<form class="form-info">
|
||||
|
||||
<!-- Левая колонка -->
|
||||
<div class="form_name">
|
||||
<label>Имя
|
||||
<input type="text" name="firstName" required>
|
||||
<input type="text" name="firstName" id="ffname" required>
|
||||
</label>
|
||||
|
||||
<label>Отчество
|
||||
<input type="text" name="middleName" required>
|
||||
<input type="text" name="middleName" id="fmname" required>
|
||||
</label>
|
||||
|
||||
<label>Фамилия
|
||||
<input type="text" name="lastName" required>
|
||||
<input type="text" name="lastName" id="flname" required>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -456,12 +445,12 @@
|
||||
<p class="block_title">Горячее блюдо</p>
|
||||
|
||||
<label class="option">
|
||||
<input type="radio" name="food" value="meat">
|
||||
<input type="radio" name="food" id="rmeat" value="meat" required>
|
||||
Мясо
|
||||
</label>
|
||||
|
||||
<label class="option">
|
||||
<input type="radio" name="food" value="fish">
|
||||
<input type="radio" name="food" id="rfish" value="fish">
|
||||
Рыба
|
||||
</label>
|
||||
</div>
|
||||
@@ -470,21 +459,21 @@
|
||||
<div class="form_drink">
|
||||
<p class="block_title">Напитки</p>
|
||||
|
||||
<label class="option"><input type="checkbox" name="drink" value="champagne"> Шампанское</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="wine"> Вино</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="vodka"> Водка</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="whiskey"> Виски</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="tequila"> Текила</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="champagne" id="cchampagne"> Шампанское</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="wine" id="cwine"> Вино</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="vodka" id="cvodka"> Водка</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="whiskey" id="cwhiskey"> Виски</label>
|
||||
<label class="option"><input type="checkbox" name="drink" value="tequila" id="ctequila"> Текила</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="answer_btn">
|
||||
<button type="submit" class="answer_btn" id="bsubmit">
|
||||
Подтвердить участие
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</content>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="footer_inner">
|
||||
@@ -534,6 +523,7 @@
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js"></script>
|
||||
<script src="main.js"></script>
|
||||
<script src="api.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -455,7 +455,7 @@ h4 {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.form-example {
|
||||
.form-info {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 50px;
|
||||