feat: sync admin stars grant support

This commit is contained in:
A 2026-07-08 02:30:01 +08:00
parent 23fa1cad21
commit e0cabb4930
14 changed files with 286 additions and 11 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>telesrv admin</title>
<script type="module" crossorigin src="/assets/index-DLK0oYaN.js"></script>
<script type="module" crossorigin src="/assets/index-BHnkZ_za.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-W1-UPxwc.css">
</head>
<body>

View file

@ -113,6 +113,9 @@ const translations: Record<Language, Record<string, string>> = {
"account.notVerified": "Not verified",
"account.notPremium": "Not premium",
"account.premiumUntil": "Premium expires",
"account.starsBalance": "Stars balance",
"account.startingGrantApplied": "initial grant applied",
"account.startingGrantPending": "initial grant pending",
"account.activeSessions": "Authorized devices",
"account.accountFlags": "Account flags",
"account.restriction": "Restriction",
@ -137,6 +140,9 @@ const translations: Record<Language, Record<string, string>> = {
"account.premiumMonthsAria": "Set premium duration in months",
"account.setPremium": "Set premium",
"account.clearPremium": "Clear premium",
"account.starsAmount": "Stars to grant",
"account.starsAmountAria": "Set Stars amount to grant",
"account.grantStars": "Grant Stars",
"account.setVerified": "Set verified",
"account.clearVerified": "Clear verified",
"channel.pageTitle": "Supergroups and Channels",
@ -372,6 +378,9 @@ const translations: Record<Language, Record<string, string>> = {
"account.notVerified": "未认证",
"account.notPremium": "非会员",
"account.premiumUntil": "会员到期",
"account.starsBalance": "Stars 余额",
"account.startingGrantApplied": "初始赠送已发放",
"account.startingGrantPending": "初始赠送未触发",
"account.activeSessions": "授权设备",
"account.accountFlags": "账号标记",
"account.restriction": "限制状态",
@ -396,6 +405,9 @@ const translations: Record<Language, Record<string, string>> = {
"account.premiumMonthsAria": "设置会员时长,单位月",
"account.setPremium": "设置会员",
"account.clearPremium": "取消会员",
"account.starsAmount": "赠送 Stars 数量",
"account.starsAmountAria": "设置要赠送的 Stars 数量",
"account.grantStars": "赠送 Stars",
"account.setVerified": "设置认证",
"account.clearVerified": "取消认证",
"channel.pageTitle": "超级群与频道",

View file

@ -1,4 +1,4 @@
import { ArrowLeft, BadgeCheck, CircleAlert, Sparkles } from "lucide-react";
import { ArrowLeft, BadgeCheck, CircleAlert, Sparkles, Star } from "lucide-react";
import { useEffect, useState } from "react";
import { api, errorMessage } from "../api";
import { ActionButton } from "../components/ActionButton";
@ -15,6 +15,7 @@ export function AccountDetailPage({ id, navigate }: { id: number; navigate: Navi
const [error, setError] = useState("");
const [busy, setBusy] = useState(false);
const [months, setMonths] = useState("1");
const [starsAmount, setStarsAmount] = useState("1000");
async function load() {
setBusy(true);
@ -64,6 +65,7 @@ export function AccountDetailPage({ id, navigate }: { id: number; navigate: Navi
<Summary label={t("account.userID")} value={String(account.ID)} mono />
<Summary label={t("account.lastActive")} value={formatUnix(detail.LastSeenAt) || "-"} />
<Summary label={t("account.premiumUntil")} value={account.PremiumUntil > 0 ? formatUnix(account.PremiumUntil) : t("common.none")} />
<Summary label={t("account.starsBalance")} value={`${detail.StarsBalance} / ${detail.StarsGranted ? t("account.startingGrantApplied") : t("account.startingGrantPending")}`} />
<Summary label={t("common.updatedAt")} value={formatDate(account.UpdatedAt) || "-"} />
<Summary label={t("account.activeSessions")} value={String(detail.Authorizations.length)} />
<Summary label={t("account.accountFlags")} value={`support=${detail.Support} bot=${detail.Bot}`} />
@ -119,6 +121,25 @@ export function AccountDetailPage({ id, navigate }: { id: number; navigate: Navi
payload={() => ({ user_id: account.ID, months: 0 })}
onDone={load}
/>
<label className="duration-field">
<span>{t("account.starsAmount")}</span>
<input
aria-label={t("account.starsAmountAria")}
value={starsAmount}
onChange={(event) => setStarsAmount(event.target.value)}
type="number"
min="1"
max="1000000000"
/>
</label>
<ActionButton
label={t("account.grantStars")}
icon={<Star size={15} />}
tone="warn"
path="/api/actions/grant-stars"
payload={() => ({ user_id: account.ID, amount: toInt(starsAmount) })}
onDone={load}
/>
<ActionButton
label={detail.Verified ? t("account.clearVerified") : t("account.setVerified")}
icon={<BadgeCheck size={15} />}

View file

@ -57,6 +57,8 @@ export type AccountDetail = {
Verified: boolean;
Support: boolean;
Bot: boolean;
StarsBalance: number;
StarsGranted: boolean;
Restriction: RestrictionRow;
HasRestriction: boolean;
Authorizations: AuthorizationRow[];