mirror of
https://github.com/wasrusgen/zov-tech.git
synced 2026-06-03 15:44:47 +00:00
feat: one-tap role buttons (WebApp directly, no intermediate step) + role param in URL/backend
This commit is contained in:
parent
017d179746
commit
af7dc07720
@ -147,7 +147,8 @@ function handleMe(body) {
|
||||
|
||||
// Регистрируем пользователя если первый раз, обновляем last_seen_at
|
||||
const startParam = body.startParam || auth.start_param;
|
||||
const user = getOrCreateUser(auth.user, startParam);
|
||||
const explicitRole = (body.role === "manager" || body.role === "client") ? body.role : null;
|
||||
const user = getOrCreateUser(auth.user, startParam, explicitRole);
|
||||
|
||||
if (user.role === "manager") {
|
||||
const m = getManagerProfile(tgId) || synthesizeManagerFromUser(user);
|
||||
@ -301,7 +302,7 @@ function findUser(tgId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getOrCreateUser(tgUser, startParam) {
|
||||
function getOrCreateUser(tgUser, startParam, explicitRole) {
|
||||
const tgId = tgUser.id;
|
||||
const props = PropertiesService.getScriptProperties();
|
||||
const adminId = parseInt(props.getProperty("ADMIN_TG_ID") || "0", 10);
|
||||
@ -309,22 +310,26 @@ function getOrCreateUser(tgUser, startParam) {
|
||||
const existing = findUser(tgId);
|
||||
if (existing) {
|
||||
updateColumnByKey("Users", "tg_id", tgId, "last_seen_at", new Date());
|
||||
// Если это админ и роль ещё не manager — повышаем + автозавод в Managers
|
||||
// Админ всегда manager
|
||||
if (tgId === adminId && existing.role !== "manager") {
|
||||
updateColumnByKey("Users", "tg_id", tgId, "role", "manager");
|
||||
ensureAdminManager(tgUser);
|
||||
existing.role = "manager";
|
||||
}
|
||||
// Не-админ может явно сменить роль через URL ?role=
|
||||
else if (explicitRole && tgId !== adminId && existing.role !== explicitRole) {
|
||||
updateColumnByKey("Users", "tg_id", tgId, "role", explicitRole);
|
||||
existing.role = explicitRole;
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
// Определяем роль:
|
||||
// - админ → manager (автозавод в Managers как ZOV-employee)
|
||||
// - invite-код менеджера → клиент с привязкой
|
||||
// - иначе → client
|
||||
// Новый пользователь — определяем роль
|
||||
let role = "client";
|
||||
let inviteCode = "";
|
||||
if (tgId === adminId) {
|
||||
role = "manager";
|
||||
} else if (explicitRole) {
|
||||
role = explicitRole;
|
||||
} else if (startParam && startParam.indexOf("client_inv_") === 0) {
|
||||
role = "client";
|
||||
inviteCode = startParam;
|
||||
@ -337,7 +342,7 @@ function getOrCreateUser(tgUser, startParam) {
|
||||
if (tgId === adminId) {
|
||||
ensureAdminManager(tgUser);
|
||||
}
|
||||
log("user_registered", tgId, { role, startParam });
|
||||
log("user_registered", tgId, { role, startParam, explicitRole });
|
||||
return findUser(tgId);
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
from aiogram import Router, F
|
||||
import time
|
||||
|
||||
from aiogram import Router
|
||||
from aiogram.filters import CommandStart
|
||||
from aiogram.types import (
|
||||
Message,
|
||||
InlineKeyboardMarkup,
|
||||
InlineKeyboardButton,
|
||||
WebAppInfo,
|
||||
CallbackQuery,
|
||||
)
|
||||
|
||||
from config import Config
|
||||
@ -13,25 +14,25 @@ from config import Config
|
||||
router = Router(name="start")
|
||||
|
||||
|
||||
def role_choice_kb() -> InlineKeyboardMarkup:
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(text="👤 Менеджер", callback_data="role:manager"),
|
||||
InlineKeyboardButton(text="🏠 Клиент", callback_data="role:client"),
|
||||
]
|
||||
]
|
||||
)
|
||||
def _bust_cache(url: str) -> str:
|
||||
"""Append unique timestamp to MiniApp URL so Telegram WebView can't cache between sessions."""
|
||||
sep = "&" if "?" in url else "?"
|
||||
return f"{url}{sep}t={int(time.time())}"
|
||||
|
||||
|
||||
def open_app_kb(miniapp_url: str) -> InlineKeyboardMarkup:
|
||||
def role_choice_kb(miniapp_url: str) -> InlineKeyboardMarkup:
|
||||
"""Two WebApp buttons — one tap opens the cabinet directly, no intermediate step."""
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="🚀 Открыть кабинет",
|
||||
web_app=WebAppInfo(url=miniapp_url),
|
||||
)
|
||||
text="👤 Менеджер",
|
||||
web_app=WebAppInfo(url=_bust_cache(f"{miniapp_url}?role=manager")),
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text="🏠 Клиент",
|
||||
web_app=WebAppInfo(url=_bust_cache(f"{miniapp_url}?role=client")),
|
||||
),
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -39,25 +40,8 @@ def open_app_kb(miniapp_url: str) -> InlineKeyboardMarkup:
|
||||
|
||||
@router.message(CommandStart())
|
||||
async def cmd_start(message: Message, config: Config) -> None:
|
||||
# TODO: проверить, есть ли пользователь в БД (Google Sheet → users).
|
||||
# Если есть → сразу показывать "Открыть кабинет".
|
||||
# Если нет → спрашивать роль.
|
||||
await message.answer(
|
||||
"👋 Здравствуйте, я бот-помощник от Руслана ВАСИЛЬЕВА.\n\n"
|
||||
"Кто вы?",
|
||||
reply_markup=role_choice_kb(),
|
||||
reply_markup=role_choice_kb(config.miniapp_url),
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("role:"))
|
||||
async def on_role_chosen(callback: CallbackQuery, config: Config) -> None:
|
||||
role = callback.data.split(":", 1)[1]
|
||||
# TODO: сохранить роль в БД (Google Sheet → users)
|
||||
|
||||
text = {
|
||||
"manager": "Отлично, открываю кабинет менеджера 👇",
|
||||
"client": "Спасибо! Открываю ваш кабинет 👇",
|
||||
}.get(role, "Открываю кабинет 👇")
|
||||
|
||||
await callback.message.edit_text(text, reply_markup=open_app_kb(config.miniapp_url))
|
||||
await callback.answer()
|
||||
|
||||
@ -53,11 +53,16 @@ async function fetchMe() {
|
||||
// Заголовок Content-Type НЕ ставим — иначе браузер шлёт CORS preflight,
|
||||
// который Apps Script не обрабатывает. Без заголовка fetch использует
|
||||
// text/plain — Apps Script всё равно парсит body как JSON.
|
||||
// Роль приходит в URL (?role=manager|client) — её бот подставляет в WebApp-кнопку
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const explicitRole = urlParams.get("role");
|
||||
|
||||
const res = await fetch(`${BACKEND_URL}?path=me`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
initData: tg?.initData || "",
|
||||
startParam: tg?.initDataUnsafe?.start_param || null,
|
||||
role: explicitRole,
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error("backend HTTP " + res.status);
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Geist:wght@400;500;600&family=Newsreader:ital,wght@0,400..600;1,400..600&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap">
|
||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||
<link rel="stylesheet" href="assets/styles.css?v=20260509h">
|
||||
<link rel="stylesheet" href="assets/styles.css?v=20260509i">
|
||||
</head>
|
||||
<body>
|
||||
<main id="app">
|
||||
@ -20,7 +20,7 @@
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="assets/icons.js?v=20260509h"></script>
|
||||
<script src="assets/app.js?v=20260509h"></script>
|
||||
<script src="assets/icons.js?v=20260509i"></script>
|
||||
<script src="assets/app.js?v=20260509i"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user