UI: секретный вход в админку — удержание кнопки меню 10с; убраны видимые кнопки входа админа
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,12 @@
|
|||||||
import { Menu, Plus, Users } from "lucide-react";
|
import { Menu, Plus, Users } from "lucide-react";
|
||||||
|
import { useRef } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { useToast } from "../context/ToastContext";
|
import { useToast } from "../context/ToastContext";
|
||||||
import { useMe } from "../hooks/auth";
|
import { useMe } from "../hooks/auth";
|
||||||
|
|
||||||
|
const ADMIN_HOLD_MS = 10000; // секретный вход в админку — удержание кнопки меню 10с
|
||||||
|
|
||||||
export function BottomBar({ onMenu }: { onMenu: () => void }) {
|
export function BottomBar({ onMenu }: { onMenu: () => void }) {
|
||||||
const { data: me } = useMe();
|
const { data: me } = useMe();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -11,6 +14,28 @@ export function BottomBar({ onMenu }: { onMenu: () => void }) {
|
|||||||
const hasGroup = me?.active_group_id != null;
|
const hasGroup = me?.active_group_id != null;
|
||||||
const activeGroup = me?.groups.find((g) => g.id === me?.active_group_id);
|
const activeGroup = me?.groups.find((g) => g.id === me?.active_group_id);
|
||||||
|
|
||||||
|
// Долгое удержание кнопки меню → /admin/login; короткий тап → открыть боковое меню.
|
||||||
|
const holdTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
const holdFired = useRef(false);
|
||||||
|
|
||||||
|
const startHold = () => {
|
||||||
|
holdFired.current = false;
|
||||||
|
holdTimer.current = setTimeout(() => {
|
||||||
|
holdFired.current = true;
|
||||||
|
navigate("/admin/login");
|
||||||
|
}, ADMIN_HOLD_MS);
|
||||||
|
};
|
||||||
|
const clearHold = () => {
|
||||||
|
if (holdTimer.current) {
|
||||||
|
clearTimeout(holdTimer.current);
|
||||||
|
holdTimer.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const endHold = () => {
|
||||||
|
clearHold();
|
||||||
|
if (!holdFired.current) onMenu(); // обычный тап
|
||||||
|
};
|
||||||
|
|
||||||
const onCreate = () => {
|
const onCreate = () => {
|
||||||
if (!hasGroup) {
|
if (!hasGroup) {
|
||||||
toast.show("Вы не состоите в группе. Создайте или вступите в группу.");
|
toast.show("Вы не состоите в группе. Создайте или вступите в группу.");
|
||||||
@@ -29,7 +54,14 @@ export function BottomBar({ onMenu }: { onMenu: () => void }) {
|
|||||||
|
|
||||||
<div className="spacer" />
|
<div className="spacer" />
|
||||||
|
|
||||||
<button className="slot" onClick={onMenu} aria-label="Меню">
|
<button
|
||||||
|
className="slot"
|
||||||
|
aria-label="Меню"
|
||||||
|
onPointerDown={startHold}
|
||||||
|
onPointerUp={endHold}
|
||||||
|
onPointerLeave={clearHold}
|
||||||
|
onPointerCancel={clearHold}
|
||||||
|
>
|
||||||
<Menu size={20} />
|
<Menu size={20} />
|
||||||
<span>Меню</span>
|
<span>Меню</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -158,12 +158,6 @@ export function AccountPage() {
|
|||||||
<button className="btn btn-danger" style={{ width: "100%" }} onClick={doLogout}>
|
<button className="btn btn-danger" style={{ width: "100%" }} onClick={doLogout}>
|
||||||
Выйти
|
Выйти
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p className="center" style={{ marginTop: 24 }}>
|
|
||||||
<button className="btn btn-ghost small muted" onClick={() => navigate("/admin/login")}>
|
|
||||||
Вход для администратора
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,16 +93,6 @@ export function LoginPage() {
|
|||||||
{showStub && !pendingTg && <DevLogin onError={setError} />}
|
{showStub && !pendingTg && <DevLogin onError={setError} />}
|
||||||
|
|
||||||
{error && <p className="error-text center">{error}</p>}
|
{error && <p className="error-text center">{error}</p>}
|
||||||
|
|
||||||
<p className="center" style={{ marginTop: 40 }}>
|
|
||||||
<button
|
|
||||||
className="btn btn-ghost small"
|
|
||||||
onClick={() => navigate("/admin/login")}
|
|
||||||
style={{ opacity: 0.4 }}
|
|
||||||
>
|
|
||||||
·
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user