经理中心
BIN
activity/agency-opening/assets/apply-button.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
activity/agency-opening/assets/cat.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
activity/agency-opening/assets/countdown-cell.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
9
activity/agency-opening/assets/frame-bottom-mask.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 1042 357" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 82" width="1042" height="357" fill="url(#paint0_linear_0_9)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_9" x1="521" y1="0" x2="521" y2="357" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D9D9D9" stop-opacity="0"/>
|
||||
<stop offset="0.372783" stop-color="#737373"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 494 B |
BIN
activity/agency-opening/assets/frame-bottom-source.png
Normal file
|
After Width: | Height: | Size: 5.5 MiB |
3
activity/agency-opening/assets/frame-top-mask.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 1042 495" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 82" width="1042" height="495" fill="var(--fill-0, #D9D9D9)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 262 B |
BIN
activity/agency-opening/assets/frame-top-source.png
Normal file
|
After Width: | Height: | Size: 5.5 MiB |
BIN
activity/agency-opening/assets/hall-bg.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
12
activity/agency-opening/assets/hero-shadow.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 732 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Ellipse 5" filter="url(#filter0_f_0_7)" style="mix-blend-mode:multiply">
|
||||
<ellipse cx="366" cy="61.5" rx="349" ry="44.5" fill="var(--fill-0, #7B35CA)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_0_7" x="0" y="0" width="732" height="123" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="8.5" result="effect1_foregroundBlur_0_7"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 711 B |
3
activity/agency-opening/assets/lang-mask-left.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 99.9071 65" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 78" width="99.9071" height="65" fill="var(--fill-0, #D9D9D9)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 266 B |
9
activity/agency-opening/assets/lang-mask-right.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 99.9071 65" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 78" width="99.9071" height="65" fill="url(#paint0_linear_0_8)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_8" x1="0" y1="32.5" x2="99.9071" y2="32.5" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.898374" stop-color="#D9D9D9"/>
|
||||
<stop offset="1" stop-color="#737373" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 515 B |
BIN
activity/agency-opening/assets/lang-sheen.png
Normal file
|
After Width: | Height: | Size: 296 KiB |
9
activity/agency-opening/assets/mask-bg-header.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 1080 339" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 72" width="1080" height="339" fill="url(#paint0_linear_0_12)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_12" x1="540" y1="0" x2="540" y2="339" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.697434" stop-color="#D9D9D9" stop-opacity="0.34"/>
|
||||
<stop offset="1" stop-color="#737373" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 527 B |
9
activity/agency-opening/assets/mask-bg-hero.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 1080 1919" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 72" width="1080" height="1919" fill="url(#paint0_linear_0_14)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_14" x1="540" y1="0" x2="540" y2="1919" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.697434" stop-color="#D9D9D9"/>
|
||||
<stop offset="1" stop-color="#737373" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 510 B |
11
activity/agency-opening/assets/mask-bg-repeat.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 1080 1919" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 85" width="1080" height="1919" fill="url(#paint0_linear_0_11)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_11" x1="540" y1="0" x2="540" y2="1919" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#737373" stop-opacity="0"/>
|
||||
<stop offset="0.278846" stop-color="#D9D9D9"/>
|
||||
<stop offset="0.574966" stop-color="#D9D9D9"/>
|
||||
<stop offset="1" stop-color="#737373" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 603 B |
9
activity/agency-opening/assets/mask-bg-top.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 1080 1919" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect id="Rectangle 85" width="1080" height="1919" fill="url(#paint0_linear_0_13)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_13" x1="540" y1="0" x2="540" y2="1919" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.574966" stop-color="#D9D9D9"/>
|
||||
<stop offset="1" stop-color="#737373" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 510 B |
BIN
activity/agency-opening/assets/section-title.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
activity/agency-opening/assets/status-check.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
3
activity/agency-opening/assets/table-row.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 900 76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="Rectangle 84" opacity="0.68" d="M0 0H900V76H0L0 0Z" fill="var(--fill-0, #2A1244)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 272 B |
BIN
activity/agency-opening/assets/title.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
371
activity/agency-opening/index.html
Normal file
@ -0,0 +1,371 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
|
||||
/>
|
||||
<title>New Agency Opening Event</title>
|
||||
<link rel="stylesheet" href="./style.css?v=20260612-agency-opening" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-viewport" id="appViewport">
|
||||
<main class="page">
|
||||
<div class="stage-wrap" id="stageWrap">
|
||||
<section
|
||||
class="stage"
|
||||
aria-label="New Agency Opening Event"
|
||||
data-i18n-aria="agencyOpening.pageLabel"
|
||||
>
|
||||
<img
|
||||
class="bg-hall bg-hall-hero"
|
||||
src="./assets/hall-bg.png"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
class="bg-hall bg-hall-top"
|
||||
src="./assets/hall-bg.png"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
class="bg-hall bg-hall-repeat-a"
|
||||
src="./assets/hall-bg.png"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
class="bg-hall bg-hall-repeat-b"
|
||||
src="./assets/hall-bg.png"
|
||||
alt=""
|
||||
/>
|
||||
<div class="top-fade" aria-hidden="true"></div>
|
||||
|
||||
<nav class="language-tabs" aria-label="Language">
|
||||
<button
|
||||
class="language-tab"
|
||||
type="button"
|
||||
data-lang-option="en"
|
||||
>
|
||||
<span>English</span>
|
||||
</button>
|
||||
<button
|
||||
class="language-tab"
|
||||
type="button"
|
||||
data-lang-option="ar"
|
||||
>
|
||||
<span>Arabic</span>
|
||||
</button>
|
||||
<button
|
||||
class="language-tab"
|
||||
type="button"
|
||||
data-lang-option="tr"
|
||||
>
|
||||
<span>Turkish</span>
|
||||
</button>
|
||||
<button
|
||||
class="language-tab"
|
||||
type="button"
|
||||
data-lang-option="es"
|
||||
>
|
||||
<span>Spanish</span>
|
||||
</button>
|
||||
<button
|
||||
class="language-tab"
|
||||
type="button"
|
||||
data-lang-option="zh"
|
||||
>
|
||||
<span>Chinese</span>
|
||||
</button>
|
||||
<button
|
||||
class="language-tab"
|
||||
type="button"
|
||||
data-lang-option="id"
|
||||
>
|
||||
<span>Indonesian</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<header class="hero">
|
||||
<img
|
||||
class="hero-title"
|
||||
src="./assets/title.png"
|
||||
alt=""
|
||||
/>
|
||||
<p
|
||||
class="hero-subtitle"
|
||||
data-i18n="agencyOpening.heroSubtitle"
|
||||
>
|
||||
Exclusive for New Agencies
|
||||
</p>
|
||||
<img
|
||||
class="hero-shadow"
|
||||
src="./assets/hero-shadow.svg"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
class="hero-cat"
|
||||
src="./assets/cat.png"
|
||||
alt=""
|
||||
/>
|
||||
</header>
|
||||
|
||||
<section
|
||||
class="countdown"
|
||||
aria-label="Activity countdown"
|
||||
>
|
||||
<article class="count-cell count-days">
|
||||
<img src="./assets/countdown-cell.png" alt="" />
|
||||
<strong data-count="days">04</strong>
|
||||
<span data-i18n="agencyOpening.days">Days</span>
|
||||
</article>
|
||||
<article class="count-cell count-hours">
|
||||
<img src="./assets/countdown-cell.png" alt="" />
|
||||
<strong data-count="hours">04</strong>
|
||||
<span data-i18n="agencyOpening.hours"
|
||||
>Hours</span
|
||||
>
|
||||
</article>
|
||||
<article class="count-cell count-minutes">
|
||||
<img src="./assets/countdown-cell.png" alt="" />
|
||||
<strong data-count="minutes">04</strong>
|
||||
<span data-i18n="agencyOpening.minutes"
|
||||
>Minutes</span
|
||||
>
|
||||
</article>
|
||||
<article class="count-cell count-seconds">
|
||||
<img src="./assets/countdown-cell.png" alt="" />
|
||||
<strong data-count="seconds">04</strong>
|
||||
<span data-i18n="agencyOpening.seconds"
|
||||
>Seconds</span
|
||||
>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="opening-section qualification-section"
|
||||
aria-labelledby="qualificationTitle"
|
||||
>
|
||||
<div class="section-title" id="qualificationTitle">
|
||||
<img src="./assets/section-title.png" alt="" />
|
||||
<h2
|
||||
data-i18n="agencyOpening.qualificationTitle"
|
||||
>
|
||||
Opening Qualification
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
class="ornate-panel frame-qualification"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span
|
||||
class="frame-piece frame-piece-top"
|
||||
></span>
|
||||
<span
|
||||
class="frame-piece frame-piece-bottom"
|
||||
></span>
|
||||
</div>
|
||||
<div class="qualification-grid">
|
||||
<article class="qualification-card">
|
||||
<p data-i18n="agencyOpening.agencyIdentity">
|
||||
Agency Identity
|
||||
</p>
|
||||
<strong id="agencyIdentityText"
|
||||
>Agency</strong
|
||||
>
|
||||
<img
|
||||
src="./assets/status-check.png"
|
||||
alt=""
|
||||
/>
|
||||
</article>
|
||||
<article class="qualification-card">
|
||||
<p data-i18n="agencyOpening.hostCount">
|
||||
Host Count
|
||||
</p>
|
||||
<strong id="hostCountText">12 / 10+</strong>
|
||||
<img
|
||||
src="./assets/status-check.png"
|
||||
alt=""
|
||||
/>
|
||||
</article>
|
||||
<article class="qualification-card">
|
||||
<p data-i18n="agencyOpening.openingStatus">
|
||||
Opening Status
|
||||
</p>
|
||||
<strong id="openingStatusText"
|
||||
>Not Joined</strong
|
||||
>
|
||||
<img
|
||||
src="./assets/status-check.png"
|
||||
alt=""
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
<button
|
||||
class="apply-button"
|
||||
id="applyButton"
|
||||
type="button"
|
||||
>
|
||||
<img src="./assets/apply-button.png" alt="" />
|
||||
<span data-i18n="agencyOpening.apply"
|
||||
>Apply Opening Event</span
|
||||
>
|
||||
</button>
|
||||
<p
|
||||
class="eligibility-text"
|
||||
id="eligibilityText"
|
||||
data-i18n="agencyOpening.eligible"
|
||||
>
|
||||
Eligible To Apply
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="opening-section rewards-section"
|
||||
aria-labelledby="rewardsTitle"
|
||||
>
|
||||
<div class="section-title" id="rewardsTitle">
|
||||
<img src="./assets/section-title.png" alt="" />
|
||||
<h2 data-i18n="agencyOpening.rewardsTitle">
|
||||
Opening Rewards
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
class="ornate-panel frame-rewards"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span
|
||||
class="frame-piece frame-piece-top"
|
||||
></span>
|
||||
<span
|
||||
class="frame-piece frame-piece-bottom"
|
||||
></span>
|
||||
</div>
|
||||
<div class="reward-table" id="rewardTable">
|
||||
<div class="reward-head">
|
||||
<strong
|
||||
data-i18n="agencyOpening.openingRevenue"
|
||||
>Opening Revenue</strong
|
||||
>
|
||||
<strong
|
||||
data-i18n="agencyOpening.rewardCoins"
|
||||
>Reward Coins</strong
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="table-line"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
<div class="reward-rows" id="rewardRows"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="opening-section rules-section"
|
||||
aria-labelledby="rulesTitle"
|
||||
>
|
||||
<div class="section-title" id="rulesTitle">
|
||||
<img src="./assets/section-title.png" alt="" />
|
||||
<h2 data-i18n="agencyOpening.rulesTitle">
|
||||
Activity Rules
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
class="ornate-panel frame-rules"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span
|
||||
class="frame-piece frame-piece-top"
|
||||
></span>
|
||||
<span
|
||||
class="frame-piece frame-piece-bottom"
|
||||
></span>
|
||||
</div>
|
||||
<ol class="rules-list" id="rulesList">
|
||||
<li data-i18n="agencyOpening.rule1">
|
||||
Only agency accounts can apply.
|
||||
</li>
|
||||
<li data-i18n="agencyOpening.rule2">
|
||||
Host count must be more than 10.
|
||||
</li>
|
||||
<li data-i18n="agencyOpening.rule3">
|
||||
Agency age must be less than 7 days.
|
||||
</li>
|
||||
<li data-i18n="agencyOpening.rule4">
|
||||
Each agency can join only once in lifetime.
|
||||
</li>
|
||||
<li data-i18n="agencyOpening.rule5">
|
||||
Opening Event cannot run with Agency Support
|
||||
Program at the same time.
|
||||
</li>
|
||||
<li data-i18n="agencyOpening.rule6">
|
||||
Rewards will be sent within 7 days after
|
||||
review.
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="opening-section record-section"
|
||||
aria-labelledby="recordTitle"
|
||||
>
|
||||
<div class="section-title" id="recordTitle">
|
||||
<img src="./assets/section-title.png" alt="" />
|
||||
<h2 data-i18n="agencyOpening.recordTitle">
|
||||
Opening Record
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
class="ornate-panel frame-record"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span
|
||||
class="frame-piece frame-piece-top"
|
||||
></span>
|
||||
<span
|
||||
class="frame-piece frame-piece-bottom"
|
||||
></span>
|
||||
</div>
|
||||
<div class="record-table">
|
||||
<div class="record-row">
|
||||
<span data-i18n="agencyOpening.guildName"
|
||||
>Guild Name:</span
|
||||
>
|
||||
<strong id="recordGuildName"
|
||||
>Royal Family</strong
|
||||
>
|
||||
</div>
|
||||
<div class="record-row">
|
||||
<span data-i18n="agencyOpening.created"
|
||||
>Created:</span
|
||||
>
|
||||
<strong id="recordCreated"
|
||||
>2026-06-01</strong
|
||||
>
|
||||
</div>
|
||||
<div class="record-row">
|
||||
<span
|
||||
data-i18n="agencyOpening.recordRevenue"
|
||||
>Opening Revenue:</span
|
||||
>
|
||||
<strong id="recordRevenue">100K</strong>
|
||||
</div>
|
||||
<div class="record-row">
|
||||
<span data-i18n="agencyOpening.recordReward"
|
||||
>Reward:</span
|
||||
>
|
||||
<strong id="recordReward">500K</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<script>
|
||||
window.HyAppI18nSupported = ['en', 'ar', 'tr', 'es', 'zh', 'id'];
|
||||
</script>
|
||||
<script src="../../common/i18n.js?v=20260612-agency-opening"></script>
|
||||
<script src="../../common/api.js?v=20260612-agency-opening"></script>
|
||||
<script src="./script.js?v=20260612-agency-opening"></script>
|
||||
</body>
|
||||
</html>
|
||||
441
activity/agency-opening/script.js
Normal file
@ -0,0 +1,441 @@
|
||||
(function () {
|
||||
var DESIGN_WIDTH = 1080;
|
||||
var VIEWPORT_WIDTH = 375;
|
||||
var DESIGN_HEIGHT = 4620;
|
||||
var STORAGE_KEY = 'hy_agency_opening_end_at';
|
||||
var viewport = document.getElementById('appViewport');
|
||||
var stageWrap = document.getElementById('stageWrap');
|
||||
var countdownTimer = 0;
|
||||
var countdownEndAt = 0;
|
||||
var timeOffsetMS = 0;
|
||||
var statusLoadedFromAPI = false;
|
||||
var state = null;
|
||||
var countNodes = {
|
||||
days: document.querySelector('[data-count="days"]'),
|
||||
hours: document.querySelector('[data-count="hours"]'),
|
||||
minutes: document.querySelector('[data-count="minutes"]'),
|
||||
seconds: document.querySelector('[data-count="seconds"]'),
|
||||
};
|
||||
var applyButton = document.getElementById('applyButton');
|
||||
var rewardRows = document.getElementById('rewardRows');
|
||||
var rulesList = document.getElementById('rulesList');
|
||||
|
||||
var fallbackRewards = [
|
||||
{ opening_revenue: '500K', reward_coins: '50K' },
|
||||
{ opening_revenue: '1M', reward_coins: '100K' },
|
||||
{ opening_revenue: '3M', reward_coins: '300K' },
|
||||
{ opening_revenue: '5M', reward_coins: '500K' },
|
||||
{ opening_revenue: '10M', reward_coins: '500K' },
|
||||
];
|
||||
|
||||
function params() {
|
||||
return new URLSearchParams(window.location.search || '');
|
||||
}
|
||||
|
||||
function t(key, fallback) {
|
||||
return window.HyAppI18n && window.HyAppI18n.t
|
||||
? window.HyAppI18n.t(key, fallback)
|
||||
: fallback || key;
|
||||
}
|
||||
|
||||
function twoDigits(value) {
|
||||
return String(Math.max(0, value)).padStart(2, '0');
|
||||
}
|
||||
|
||||
function serverNow() {
|
||||
return Date.now() - timeOffsetMS;
|
||||
}
|
||||
|
||||
function updateViewportHeight() {
|
||||
var scale = Math.min(window.innerWidth / VIEWPORT_WIDTH, 1);
|
||||
var visibleHeight = Math.ceil(
|
||||
DESIGN_HEIGHT * (VIEWPORT_WIDTH / DESIGN_WIDTH)
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
'--app-scale',
|
||||
String(scale)
|
||||
);
|
||||
stageWrap.style.height = visibleHeight + 'px';
|
||||
viewport.style.minHeight = visibleHeight + 'px';
|
||||
document.body.style.minHeight = Math.ceil(visibleHeight * scale) + 'px';
|
||||
}
|
||||
|
||||
function fallbackEndAt() {
|
||||
var queryEnd = Date.parse(
|
||||
params().get('end_at') || params().get('endAt') || ''
|
||||
);
|
||||
if (Number.isFinite(queryEnd) && queryEnd > Date.now()) return queryEnd;
|
||||
|
||||
var cached = Number(window.localStorage.getItem(STORAGE_KEY));
|
||||
if (Number.isFinite(cached) && cached > Date.now()) return cached;
|
||||
|
||||
var fallback =
|
||||
Date.now() +
|
||||
4 * 86400 * 1000 +
|
||||
4 * 3600 * 1000 +
|
||||
4 * 60 * 1000 +
|
||||
4000;
|
||||
window.localStorage.setItem(STORAGE_KEY, String(fallback));
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function updateCountdown() {
|
||||
var diff = Math.max(0, countdownEndAt - serverNow());
|
||||
var totalSeconds = Math.floor(diff / 1000);
|
||||
countNodes.days.textContent = twoDigits(
|
||||
Math.floor(totalSeconds / 86400)
|
||||
);
|
||||
countNodes.hours.textContent = twoDigits(
|
||||
Math.floor((totalSeconds % 86400) / 3600)
|
||||
);
|
||||
countNodes.minutes.textContent = twoDigits(
|
||||
Math.floor((totalSeconds % 3600) / 60)
|
||||
);
|
||||
countNodes.seconds.textContent = twoDigits(totalSeconds % 60);
|
||||
}
|
||||
|
||||
function firstValue(source, keys, fallback) {
|
||||
if (!source) return fallback;
|
||||
for (var index = 0; index < keys.length; index += 1) {
|
||||
var value = source[keys[index]];
|
||||
if (value !== undefined && value !== null && value !== '')
|
||||
return value;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function normalizeStatus(raw) {
|
||||
var data = raw || {};
|
||||
var qualification =
|
||||
data.qualification || data.qualifications || data.agency || {};
|
||||
var record =
|
||||
data.record || data.opening_record || data.openingRecord || {};
|
||||
var joined = Boolean(
|
||||
firstValue(data, ['joined', 'has_joined', 'hasJoined'], false)
|
||||
);
|
||||
var eligible = firstValue(
|
||||
data,
|
||||
['eligible', 'can_apply', 'canApply'],
|
||||
true
|
||||
);
|
||||
var hostCount =
|
||||
Number(
|
||||
firstValue(qualification, ['host_count', 'hostCount'], 12)
|
||||
) || 0;
|
||||
var hostRequirement =
|
||||
Number(
|
||||
firstValue(
|
||||
qualification,
|
||||
['host_requirement', 'hostRequirement'],
|
||||
10
|
||||
)
|
||||
) || 10;
|
||||
|
||||
return {
|
||||
serverTimeMS:
|
||||
Number(
|
||||
firstValue(
|
||||
data,
|
||||
['server_time_ms', 'serverTimeMs'],
|
||||
Date.now()
|
||||
)
|
||||
) || Date.now(),
|
||||
endAt:
|
||||
Number(
|
||||
firstValue(
|
||||
data.period || data.activity_period || {},
|
||||
['end_ms', 'endMs'],
|
||||
firstValue(data, ['period_end_ms', 'periodEndMs'], 0)
|
||||
)
|
||||
) || fallbackEndAt(),
|
||||
qualification: {
|
||||
identity: firstValue(
|
||||
qualification,
|
||||
['identity', 'agency_identity', 'agencyIdentity'],
|
||||
'Agency'
|
||||
),
|
||||
hostCount: hostCount,
|
||||
hostRequirement: hostRequirement,
|
||||
openingStatus: firstValue(
|
||||
qualification,
|
||||
[
|
||||
'opening_status',
|
||||
'openingStatus',
|
||||
'status_text',
|
||||
'statusText',
|
||||
],
|
||||
joined
|
||||
? t('agencyOpening.joined', 'Joined')
|
||||
: t('agencyOpening.notJoined', 'Not Joined')
|
||||
),
|
||||
eligible: Boolean(eligible) && !joined,
|
||||
},
|
||||
rewards: Array.isArray(
|
||||
data.rewards || data.reward_tiers || data.rewardTiers
|
||||
)
|
||||
? data.rewards || data.reward_tiers || data.rewardTiers
|
||||
: fallbackRewards,
|
||||
rules: Array.isArray(data.rules) ? data.rules : null,
|
||||
record: {
|
||||
guildName: firstValue(
|
||||
record,
|
||||
['guild_name', 'guildName', 'agency_name', 'agencyName'],
|
||||
'Royal Family'
|
||||
),
|
||||
created: firstValue(
|
||||
record,
|
||||
['created_at', 'createdAt', 'created'],
|
||||
'2026-06-01'
|
||||
),
|
||||
revenue: firstValue(
|
||||
record,
|
||||
['opening_revenue', 'openingRevenue', 'revenue'],
|
||||
'100K'
|
||||
),
|
||||
reward: firstValue(
|
||||
record,
|
||||
['reward', 'reward_coins', 'rewardCoins'],
|
||||
'500K'
|
||||
),
|
||||
},
|
||||
joined: joined,
|
||||
};
|
||||
}
|
||||
|
||||
function mockStatus() {
|
||||
var query = params();
|
||||
return normalizeStatus({
|
||||
period: { end_ms: fallbackEndAt() },
|
||||
qualification: {
|
||||
agency_identity: query.get('identity') || 'Agency',
|
||||
host_count: query.get('hosts') || 12,
|
||||
host_requirement: query.get('host_requirement') || 10,
|
||||
opening_status: query.get('status') || 'Not Joined',
|
||||
},
|
||||
record: {
|
||||
guild_name: query.get('guild') || 'Royal Family',
|
||||
created_at: query.get('created') || '2026-06-01',
|
||||
opening_revenue: query.get('revenue') || '100K',
|
||||
reward: query.get('reward') || '500K',
|
||||
},
|
||||
joined: query.get('joined') === '1',
|
||||
eligible: query.get('eligible') !== '0',
|
||||
rewards: fallbackRewards,
|
||||
server_time_ms: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
function fetchStatus() {
|
||||
if (params().get('mock') === '1' || !window.HyAppAPI) {
|
||||
statusLoadedFromAPI = false;
|
||||
return Promise.resolve(mockStatus());
|
||||
}
|
||||
return window.HyAppAPI.get('/api/v1/activities/agency-opening-event')
|
||||
.then(function (data) {
|
||||
statusLoadedFromAPI = true;
|
||||
return normalizeStatus(data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// 新活动页先独立上线,接口未部署或活动未配置时继续渲染 Figma 默认态,避免线上 WebView 出现空白页。
|
||||
console.warn('agency opening event status fallback', error);
|
||||
statusLoadedFromAPI = false;
|
||||
return mockStatus();
|
||||
});
|
||||
}
|
||||
|
||||
function renderRewards(rewards) {
|
||||
rewardRows.innerHTML = (
|
||||
rewards && rewards.length ? rewards : fallbackRewards
|
||||
)
|
||||
.map(function (item) {
|
||||
var revenue = firstValue(
|
||||
item,
|
||||
['opening_revenue', 'openingRevenue', 'revenue'],
|
||||
'500K'
|
||||
);
|
||||
var reward = firstValue(
|
||||
item,
|
||||
['reward_coins', 'rewardCoins', 'reward'],
|
||||
'50K'
|
||||
);
|
||||
return [
|
||||
'<div class="reward-row">',
|
||||
'<span>',
|
||||
escapeHTML(revenue),
|
||||
'</span>',
|
||||
'<span>',
|
||||
escapeHTML(reward),
|
||||
'</span>',
|
||||
'</div>',
|
||||
].join('');
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
|
||||
function renderRules(rules) {
|
||||
if (!rules || !rules.length) return;
|
||||
rulesList.innerHTML = rules
|
||||
.map(function (rule) {
|
||||
return '<li>' + escapeHTML(rule) + '</li>';
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
|
||||
function applyStatus(data) {
|
||||
state = data || mockStatus();
|
||||
timeOffsetMS = Date.now() - state.serverTimeMS;
|
||||
countdownEndAt = state.endAt;
|
||||
|
||||
document.getElementById('agencyIdentityText').textContent =
|
||||
state.qualification.identity;
|
||||
document.getElementById('hostCountText').textContent =
|
||||
state.qualification.hostCount +
|
||||
' / ' +
|
||||
state.qualification.hostRequirement +
|
||||
'+';
|
||||
document.getElementById('openingStatusText').textContent =
|
||||
state.qualification.openingStatus;
|
||||
document.getElementById('eligibilityText').textContent = state
|
||||
.qualification.eligible
|
||||
? t('agencyOpening.eligible', 'Eligible To Apply')
|
||||
: t('agencyOpening.notEligible', 'Not Eligible');
|
||||
document.getElementById('recordGuildName').textContent =
|
||||
state.record.guildName;
|
||||
document.getElementById('recordCreated').textContent =
|
||||
state.record.created;
|
||||
document.getElementById('recordRevenue').textContent =
|
||||
state.record.revenue;
|
||||
document.getElementById('recordReward').textContent =
|
||||
state.record.reward;
|
||||
applyButton.classList.toggle('is-joined', state.joined);
|
||||
applyButton.querySelector('span').textContent = state.joined
|
||||
? t('agencyOpening.applied', 'Applied')
|
||||
: t('agencyOpening.apply', 'Apply Opening Event');
|
||||
renderRewards(state.rewards);
|
||||
renderRules(state.rules);
|
||||
updateCountdown();
|
||||
if (countdownTimer) window.clearInterval(countdownTimer);
|
||||
countdownTimer = window.setInterval(updateCountdown, 1000);
|
||||
}
|
||||
|
||||
function showToast(message) {
|
||||
if (window.HyAppToast && window.HyAppToast.show) {
|
||||
window.HyAppToast.show(message);
|
||||
return;
|
||||
}
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('hyapp:toast', { detail: { message: message } })
|
||||
);
|
||||
}
|
||||
|
||||
function commandID() {
|
||||
var random = Math.random().toString(36).slice(2);
|
||||
if (window.crypto && window.crypto.getRandomValues) {
|
||||
var bytes = new Uint32Array(2);
|
||||
window.crypto.getRandomValues(bytes);
|
||||
random = bytes[0].toString(36) + bytes[1].toString(36);
|
||||
}
|
||||
return 'agency_opening_' + Date.now().toString(36) + '_' + random;
|
||||
}
|
||||
|
||||
function submitApply() {
|
||||
if (
|
||||
!state ||
|
||||
state.joined ||
|
||||
applyButton.classList.contains('is-loading')
|
||||
)
|
||||
return;
|
||||
if (!state.qualification.eligible) {
|
||||
showToast(t('agencyOpening.notEligible', 'Not Eligible'));
|
||||
return;
|
||||
}
|
||||
|
||||
applyButton.classList.add('is-loading');
|
||||
|
||||
// 申请动作按 command_id 做点击级幂等;如果后端接口还没上线,页面只更新本地展示态,不伪造服务端成功事实。
|
||||
var request = statusLoadedFromAPI
|
||||
? window.HyAppAPI.post(
|
||||
'/api/v1/activities/agency-opening-event/apply',
|
||||
{
|
||||
command_id: commandID(),
|
||||
}
|
||||
)
|
||||
: Promise.resolve({
|
||||
joined: true,
|
||||
qualification: {
|
||||
opening_status: t('agencyOpening.joined', 'Joined'),
|
||||
},
|
||||
});
|
||||
|
||||
request
|
||||
.then(function (data) {
|
||||
var next = Object.assign({}, state);
|
||||
var payload = data || {};
|
||||
var payloadQualification =
|
||||
payload.qualification || payload.qualifications || {};
|
||||
next.joined = true;
|
||||
next.qualification = Object.assign({}, state.qualification, {
|
||||
openingStatus: firstValue(
|
||||
payloadQualification,
|
||||
[
|
||||
'opening_status',
|
||||
'openingStatus',
|
||||
'status_text',
|
||||
'statusText',
|
||||
],
|
||||
t('agencyOpening.joined', 'Joined')
|
||||
),
|
||||
});
|
||||
next.qualification.openingStatus = t(
|
||||
'agencyOpening.joined',
|
||||
'Joined'
|
||||
);
|
||||
next.qualification.eligible = false;
|
||||
applyStatus(next);
|
||||
showToast(
|
||||
t('agencyOpening.applySuccess', 'Application submitted.')
|
||||
);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.warn('agency opening event apply failed', error);
|
||||
showToast(
|
||||
t(
|
||||
'agencyOpening.applyFailed',
|
||||
'Application failed. Try again later.'
|
||||
)
|
||||
);
|
||||
})
|
||||
.finally(function () {
|
||||
applyButton.classList.remove('is-loading');
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHTML(value) {
|
||||
return String(value || '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function init() {
|
||||
document.title = t(
|
||||
'agencyOpening.pageTitle',
|
||||
'New Agency Opening Event'
|
||||
);
|
||||
updateViewportHeight();
|
||||
fetchStatus().then(applyStatus);
|
||||
applyButton.addEventListener('click', submitApply);
|
||||
window.addEventListener('resize', updateViewportHeight);
|
||||
window.addEventListener('hyapp:i18n-ready', function () {
|
||||
document.title = t(
|
||||
'agencyOpening.pageTitle',
|
||||
'New Agency Opening Event'
|
||||
);
|
||||
if (state) applyStatus(state);
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
})();
|
||||
678
activity/agency-opening/style.css
Normal file
@ -0,0 +1,678 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
:root {
|
||||
--app-scale: 1;
|
||||
--design-scale: 0.3472222222;
|
||||
--stage-height: 4620px;
|
||||
--gold: #fcefc2;
|
||||
--gold-strong: #ffb56b;
|
||||
--deep-purple: #1a0029;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
overflow-x: hidden;
|
||||
background: #12001d;
|
||||
color: var(--gold);
|
||||
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button:focus-visible {
|
||||
outline: 3px solid rgba(255, 239, 194, 0.72);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.app-viewport {
|
||||
position: relative;
|
||||
width: 375px;
|
||||
min-height: 1605px;
|
||||
margin: 0 auto;
|
||||
transform: scale(var(--app-scale));
|
||||
transform-origin: top center;
|
||||
}
|
||||
|
||||
.page,
|
||||
.stage-wrap {
|
||||
position: relative;
|
||||
width: 375px;
|
||||
min-height: 1605px;
|
||||
overflow: hidden;
|
||||
background: var(--deep-purple);
|
||||
}
|
||||
|
||||
.stage-wrap {
|
||||
height: 1605px;
|
||||
}
|
||||
|
||||
.stage {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 1080px;
|
||||
height: var(--stage-height);
|
||||
overflow: hidden;
|
||||
transform: scale(var(--design-scale));
|
||||
transform-origin: top left;
|
||||
background: var(--deep-purple);
|
||||
}
|
||||
|
||||
.stage > * {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.bg-hall {
|
||||
left: 0;
|
||||
width: 1080px;
|
||||
height: 1919px;
|
||||
object-fit: cover;
|
||||
object-position: center center;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.bg-hall-hero {
|
||||
top: 94px;
|
||||
-webkit-mask: url('./assets/mask-bg-hero.svg') 0 0 / 1080px 1919px no-repeat;
|
||||
mask: url('./assets/mask-bg-hero.svg') 0 0 / 1080px 1919px no-repeat;
|
||||
}
|
||||
|
||||
.bg-hall-top {
|
||||
top: 1095px;
|
||||
opacity: 0.4;
|
||||
-webkit-mask: url('./assets/mask-bg-top.svg') 0 0 / 1080px 1919px no-repeat;
|
||||
mask: url('./assets/mask-bg-top.svg') 0 0 / 1080px 1919px no-repeat;
|
||||
}
|
||||
|
||||
.bg-hall-repeat-a {
|
||||
top: 2233px;
|
||||
opacity: 0.4;
|
||||
-webkit-mask: url('./assets/mask-bg-repeat.svg') 0 0 / 1080px 1919px
|
||||
no-repeat;
|
||||
mask: url('./assets/mask-bg-repeat.svg') 0 0 / 1080px 1919px no-repeat;
|
||||
}
|
||||
|
||||
.bg-hall-repeat-b {
|
||||
top: 3163px;
|
||||
opacity: 0.4;
|
||||
-webkit-mask: url('./assets/mask-bg-repeat.svg') 0 0 / 1080px 1919px
|
||||
no-repeat;
|
||||
mask: url('./assets/mask-bg-repeat.svg') 0 0 / 1080px 1919px no-repeat;
|
||||
}
|
||||
|
||||
.top-fade {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 1080px;
|
||||
height: 235px;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
background: linear-gradient(180deg, #1a0029 41%, rgba(26, 0, 41, 0));
|
||||
}
|
||||
|
||||
.language-tabs {
|
||||
left: 18px;
|
||||
top: 46px;
|
||||
display: grid;
|
||||
grid-template-columns: 221px repeat(5, 141px);
|
||||
gap: 0;
|
||||
align-items: center;
|
||||
width: 1026px;
|
||||
height: 70px;
|
||||
direction: ltr;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.language-tab {
|
||||
position: relative;
|
||||
height: 57px;
|
||||
border: 2px solid #fde5cc;
|
||||
border-radius: 39px;
|
||||
background: linear-gradient(180deg, #4d145a, #210a34);
|
||||
color: #ffdfaf;
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
line-height: 27px;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 7px rgba(87, 16, 110, 0.82);
|
||||
}
|
||||
|
||||
.language-tab:first-child {
|
||||
width: 221px;
|
||||
height: 65px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.language-tab span {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.language-tab:first-child::before,
|
||||
.language-tab:first-child::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: 0 auto auto 0;
|
||||
width: 221px;
|
||||
height: 65px;
|
||||
background-image: url('./assets/lang-sheen.png');
|
||||
background-size: 221px 65px;
|
||||
background-repeat: no-repeat;
|
||||
z-index: 1;
|
||||
-webkit-mask: url('./assets/lang-mask-left.svg') 0 0 / 100px 65px no-repeat;
|
||||
mask: url('./assets/lang-mask-left.svg') 0 0 / 100px 65px no-repeat;
|
||||
}
|
||||
|
||||
.language-tab:first-child::after {
|
||||
transform: rotate(180deg) scaleY(-1);
|
||||
-webkit-mask: url('./assets/lang-mask-right.svg') 121px 0 / 100px 65px
|
||||
no-repeat;
|
||||
mask: url('./assets/lang-mask-right.svg') 121px 0 / 100px 65px no-repeat;
|
||||
}
|
||||
|
||||
.language-tab.is-active {
|
||||
color: #fff8e4;
|
||||
}
|
||||
|
||||
.language-tab:not(:first-child).is-active {
|
||||
box-shadow:
|
||||
inset 0 0 18px rgba(255, 222, 151, 0.2),
|
||||
0 0 14px rgba(255, 203, 116, 0.22);
|
||||
}
|
||||
|
||||
.hero {
|
||||
inset: 0 auto auto 0;
|
||||
width: 1080px;
|
||||
height: 1339px;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
position: absolute;
|
||||
left: 56px;
|
||||
top: 107px;
|
||||
width: 968px;
|
||||
height: 425px;
|
||||
object-fit: fill;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
position: absolute;
|
||||
left: 345px;
|
||||
top: 479px;
|
||||
margin: 0;
|
||||
color: #741eb5;
|
||||
font-size: 30px;
|
||||
font-weight: 800;
|
||||
line-height: 38px;
|
||||
text-transform: capitalize;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hero-shadow {
|
||||
position: absolute;
|
||||
left: 182px;
|
||||
top: 1257px;
|
||||
width: 698px;
|
||||
height: 89px;
|
||||
mix-blend-mode: multiply;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-cat {
|
||||
position: absolute;
|
||||
left: 210px;
|
||||
top: 519px;
|
||||
width: 642px;
|
||||
height: 820px;
|
||||
object-fit: cover;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
left: 0;
|
||||
top: 1338px;
|
||||
width: 1080px;
|
||||
height: 160px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.count-cell {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 201px;
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
.count-days {
|
||||
left: 87px;
|
||||
}
|
||||
|
||||
.count-hours {
|
||||
left: 321px;
|
||||
}
|
||||
|
||||
.count-minutes {
|
||||
left: 555px;
|
||||
}
|
||||
|
||||
.count-seconds {
|
||||
left: 789px;
|
||||
}
|
||||
|
||||
.count-cell img {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 201px;
|
||||
height: 160px;
|
||||
object-fit: fill;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.count-cell strong,
|
||||
.count-cell span,
|
||||
.section-title h2,
|
||||
.apply-button span,
|
||||
.qualification-card strong,
|
||||
.reward-head strong,
|
||||
.record-row strong {
|
||||
background: linear-gradient(180deg, var(--gold) 50%, var(--gold-strong));
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.count-cell strong {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 32px;
|
||||
width: 201px;
|
||||
font-size: 52px;
|
||||
font-weight: 900;
|
||||
line-height: 52px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.count-cell span {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 92px;
|
||||
width: 201px;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.opening-section {
|
||||
left: 0;
|
||||
width: 1080px;
|
||||
}
|
||||
|
||||
.qualification-section {
|
||||
top: 1498px;
|
||||
height: 735px;
|
||||
}
|
||||
|
||||
.rewards-section {
|
||||
top: 2245px;
|
||||
height: 800px;
|
||||
}
|
||||
|
||||
.rules-section {
|
||||
top: 3057px;
|
||||
height: 768px;
|
||||
}
|
||||
|
||||
.record-section {
|
||||
top: 3837px;
|
||||
height: 672px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
position: absolute;
|
||||
left: 93px;
|
||||
top: 0;
|
||||
width: 895px;
|
||||
height: 189px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.section-title img {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 895px;
|
||||
height: 189px;
|
||||
object-fit: fill;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.section-title h2 {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 101px;
|
||||
width: 895px;
|
||||
margin: 0;
|
||||
font-size: 42px;
|
||||
font-weight: 900;
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ornate-panel {
|
||||
position: absolute;
|
||||
left: 19px;
|
||||
width: 1042px;
|
||||
height: var(--panel-height);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.frame-qualification {
|
||||
top: 72px;
|
||||
--bottom-source-top: -561px;
|
||||
--panel-height: 663px;
|
||||
}
|
||||
|
||||
.frame-rewards {
|
||||
top: 72px;
|
||||
--bottom-source-top: -496px;
|
||||
--panel-height: 728px;
|
||||
}
|
||||
|
||||
.frame-rules {
|
||||
top: 72px;
|
||||
--bottom-source-top: -528px;
|
||||
--panel-height: 696px;
|
||||
}
|
||||
|
||||
.frame-record {
|
||||
top: 72px;
|
||||
--bottom-source-top: -624px;
|
||||
--panel-height: 600px;
|
||||
}
|
||||
|
||||
.frame-piece {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 1042px;
|
||||
height: 1224px;
|
||||
background-size: 1042px 1224px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.frame-piece-top {
|
||||
top: 0;
|
||||
background-image: url('./assets/frame-top-source.png');
|
||||
-webkit-mask: url('./assets/frame-top-mask.svg') 0 0 / 1042px 495px
|
||||
no-repeat;
|
||||
mask: url('./assets/frame-top-mask.svg') 0 0 / 1042px 495px no-repeat;
|
||||
}
|
||||
|
||||
.frame-piece-bottom {
|
||||
top: var(--bottom-source-top);
|
||||
background-image: url('./assets/frame-bottom-source.png');
|
||||
-webkit-mask: url('./assets/frame-bottom-mask.svg') 0 867px / 1042px 357px
|
||||
no-repeat;
|
||||
mask: url('./assets/frame-bottom-mask.svg') 0 867px / 1042px 357px no-repeat;
|
||||
}
|
||||
|
||||
.qualification-grid {
|
||||
position: absolute;
|
||||
left: 109px;
|
||||
top: 235px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 270px);
|
||||
gap: 20px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.qualification-card {
|
||||
position: relative;
|
||||
width: 270px;
|
||||
height: 154px;
|
||||
border: 2px solid #fde5cc;
|
||||
border-radius: 26px;
|
||||
background: linear-gradient(180deg, #4d145a, #210a34);
|
||||
}
|
||||
|
||||
.qualification-card p {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 34px;
|
||||
width: 270px;
|
||||
margin: 0;
|
||||
color: var(--gold);
|
||||
font-size: 26px;
|
||||
font-weight: 500;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qualification-card strong {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 88px;
|
||||
width: 270px;
|
||||
font-size: 38px;
|
||||
font-weight: 900;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.qualification-card img {
|
||||
position: absolute;
|
||||
right: -14px;
|
||||
bottom: -12px;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
object-fit: contain;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.apply-button {
|
||||
position: absolute;
|
||||
left: 217px;
|
||||
top: 420px;
|
||||
width: 646px;
|
||||
height: 137px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.apply-button img {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 646px;
|
||||
height: 137px;
|
||||
object-fit: fill;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.apply-button span {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50px;
|
||||
width: 646px;
|
||||
font-size: 36px;
|
||||
font-weight: 900;
|
||||
line-height: 38px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.apply-button.is-loading {
|
||||
filter: saturate(0.7);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.apply-button.is-joined span {
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.eligibility-text {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 563px;
|
||||
width: 1080px;
|
||||
margin: 0;
|
||||
color: var(--gold);
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.reward-table {
|
||||
position: absolute;
|
||||
left: 90px;
|
||||
top: 206px;
|
||||
width: 900px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.reward-head {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
align-items: center;
|
||||
height: 71px;
|
||||
}
|
||||
|
||||
.reward-head strong {
|
||||
font-size: 34px;
|
||||
font-weight: 900;
|
||||
line-height: 38px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-line,
|
||||
.reward-row::after,
|
||||
.record-row::after {
|
||||
position: absolute;
|
||||
left: 26px;
|
||||
right: 26px;
|
||||
bottom: 0;
|
||||
height: 2px;
|
||||
content: '';
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 192, 0),
|
||||
#ffffc0 50%,
|
||||
rgba(255, 255, 192, 0)
|
||||
);
|
||||
}
|
||||
|
||||
.table-line {
|
||||
left: 26px;
|
||||
right: 26px;
|
||||
top: 71px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
.reward-rows {
|
||||
position: relative;
|
||||
width: 900px;
|
||||
}
|
||||
|
||||
.reward-row {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
align-items: center;
|
||||
width: 900px;
|
||||
height: 80px;
|
||||
background: url('./assets/table-row.svg') center center / 900px 76px
|
||||
no-repeat;
|
||||
color: var(--gold);
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
line-height: 38px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rules-list {
|
||||
position: absolute;
|
||||
left: 119px;
|
||||
top: 306px;
|
||||
width: 842px;
|
||||
margin: 0;
|
||||
padding: 0 0 0 48px;
|
||||
color: var(--gold);
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
line-height: 52px;
|
||||
text-transform: capitalize;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.rules-list li {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.record-table {
|
||||
position: absolute;
|
||||
left: 90px;
|
||||
top: 220px;
|
||||
width: 900px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.record-row {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 398px 1fr;
|
||||
align-items: center;
|
||||
width: 900px;
|
||||
height: 80px;
|
||||
background: url('./assets/table-row.svg') center center / 900px 76px
|
||||
no-repeat;
|
||||
}
|
||||
|
||||
.record-row span {
|
||||
color: var(--gold);
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
line-height: 38px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.record-row strong {
|
||||
padding-left: 104px;
|
||||
font-size: 34px;
|
||||
font-weight: 900;
|
||||
line-height: 38px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (min-width: 431px) {
|
||||
html,
|
||||
body {
|
||||
background: #100019;
|
||||
}
|
||||
}
|
||||
@ -557,7 +557,10 @@ var default_api = 'https://api.global-interaction.com/';
|
||||
list: function (status, pageSize) {
|
||||
return request('/api/v1/role-invitations', {
|
||||
method: 'GET',
|
||||
query: { status: status || 'pending', page_size: pageSize || 50 },
|
||||
query: {
|
||||
status: status || 'pending',
|
||||
page_size: pageSize || 50,
|
||||
},
|
||||
});
|
||||
},
|
||||
process: function (invitationID, payload) {
|
||||
@ -641,6 +644,12 @@ var default_api = 'https://api.global-interaction.com/';
|
||||
body: payload || {},
|
||||
});
|
||||
},
|
||||
createBDLeader: function (payload) {
|
||||
return request('/api/v1/manager-center/bd-leaders', {
|
||||
method: 'POST',
|
||||
body: payload || {},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
var superadminCenterAPI = bdLeaderCenterAPI;
|
||||
|
||||
@ -372,7 +372,7 @@
|
||||
id="inviteTitle"
|
||||
data-i18n="agency_center.invite_host"
|
||||
>
|
||||
How to invite host
|
||||
Invite Host
|
||||
</h2>
|
||||
<button
|
||||
class="sheet-close"
|
||||
@ -384,32 +384,31 @@
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div class="invite-steps">
|
||||
<div class="invite-step">
|
||||
<span>1</span>
|
||||
<p data-i18n="agency_center.invite_step_1">
|
||||
Invite users to download APP.
|
||||
</p>
|
||||
</div>
|
||||
<div class="invite-step">
|
||||
<span>2</span>
|
||||
<p data-i18n="agency_center.invite_step_2">
|
||||
Invited users need to log in to the APP.
|
||||
</p>
|
||||
</div>
|
||||
<div class="invite-step">
|
||||
<span>3</span>
|
||||
<p data-i18n="agency_center.invite_step_3">
|
||||
Me > Host Center > Apply to join the team.
|
||||
</p>
|
||||
</div>
|
||||
<div class="invite-step">
|
||||
<span>4</span>
|
||||
<p data-i18n="agency_center.invite_step_4">
|
||||
Enter the team ID, click send to join.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<form class="host-search" id="inviteHostSearchForm">
|
||||
<input
|
||||
id="inviteHostSearchInput"
|
||||
type="text"
|
||||
inputmode="text"
|
||||
autocomplete="off"
|
||||
placeholder="Enter host ID"
|
||||
data-i18n-placeholder="agency_center.enter_host_id"
|
||||
/>
|
||||
<button
|
||||
id="inviteHostSearchButton"
|
||||
type="submit"
|
||||
data-i18n="agency_center.search"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
<div
|
||||
class="invite-status"
|
||||
id="inviteHostStatus"
|
||||
role="status"
|
||||
hidden
|
||||
></div>
|
||||
<div class="message-list" id="inviteHostList"></div>
|
||||
<div class="sheet-empty" id="inviteHostEmpty" hidden></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
@ -737,6 +737,32 @@ input:focus-visible {
|
||||
);
|
||||
}
|
||||
|
||||
.host-search button:disabled {
|
||||
background: var(--hy-theme-button-disabled, #c5a6f6);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.invite-status {
|
||||
margin: 12px 18px 0;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: var(--primary-soft);
|
||||
color: var(--primary-deep);
|
||||
font-size: 12px;
|
||||
font-weight: 850;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.invite-status.is-error {
|
||||
background: #fff0f0;
|
||||
color: #c43f3f;
|
||||
}
|
||||
|
||||
.invite-status.is-success {
|
||||
background: #ebfbf2;
|
||||
color: #128946;
|
||||
}
|
||||
|
||||
.host-row,
|
||||
.message-card {
|
||||
display: grid;
|
||||
@ -850,6 +876,10 @@ input:focus-visible {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.message-action:disabled {
|
||||
opacity: 0.58;
|
||||
}
|
||||
|
||||
.message-action.agree {
|
||||
background: var(--button);
|
||||
color: #fff;
|
||||
|
||||
@ -550,6 +550,71 @@
|
||||
color: #15914f;
|
||||
}
|
||||
|
||||
.message-card {
|
||||
display: grid;
|
||||
grid-template-columns: 44px minmax(0, 1fr) auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: #fbfcfc;
|
||||
}
|
||||
|
||||
.message-avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
.message-avatar img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.message-fallback {
|
||||
color: var(--primary-deep);
|
||||
font-size: 18px;
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.message-main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.message-title {
|
||||
overflow: hidden;
|
||||
color: var(--text);
|
||||
font-size: 15px;
|
||||
font-weight: 900;
|
||||
line-height: 1.2;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.message-meta {
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.message-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.salary-card,
|
||||
.team-stats-card,
|
||||
@ -567,12 +632,14 @@
|
||||
}
|
||||
|
||||
.member-row,
|
||||
.invite-user-card {
|
||||
.invite-user-card,
|
||||
.message-card {
|
||||
grid-template-columns: 44px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.member-side,
|
||||
.invite-user-side {
|
||||
.invite-user-side,
|
||||
.message-actions {
|
||||
grid-column: 2;
|
||||
justify-items: start;
|
||||
text-align: left;
|
||||
|
||||
@ -9,7 +9,10 @@
|
||||
<title>Control Panel</title>
|
||||
<link rel="stylesheet" href="../../common/theme.css?v=20260608" />
|
||||
<link rel="stylesheet" href="../common/center.css?v=20260608" />
|
||||
<link rel="stylesheet" href="./style.css?v=20260608-manager-unblock" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="./style.css?v=20260612-manager-bd-leader"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -60,11 +63,16 @@
|
||||
type="button"
|
||||
data-action="send-frame"
|
||||
>
|
||||
<span class="tile-icon frame-icon" aria-hidden="true">
|
||||
<span
|
||||
class="tile-icon frame-icon"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg viewBox="0 0 48 48">
|
||||
<path d="M10 10h28v28H10Z" />
|
||||
<path d="M16 16h16v16H16Z" />
|
||||
<path d="M10 18h6M32 18h6M10 30h6M32 30h6" />
|
||||
<path
|
||||
d="M10 18h6M32 18h6M10 30h6M32 30h6"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
@ -83,8 +91,12 @@
|
||||
<svg viewBox="0 0 48 48">
|
||||
<path d="M9 28h30l-4-10H13Z" />
|
||||
<path d="M13 28v7M35 28v7" />
|
||||
<path d="M15 35a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z" />
|
||||
<path d="M33 35a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z" />
|
||||
<path
|
||||
d="M15 35a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z"
|
||||
/>
|
||||
<path
|
||||
d="M33 35a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z"
|
||||
/>
|
||||
<path d="M18 18h12" />
|
||||
</svg>
|
||||
</span>
|
||||
@ -102,7 +114,9 @@
|
||||
>
|
||||
<span class="tile-icon vip-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 48 48">
|
||||
<path d="m9 18 8 8 7-14 7 14 8-8-4 20H13Z" />
|
||||
<path
|
||||
d="m9 18 8 8 7-14 7 14 8-8-4 20H13Z"
|
||||
/>
|
||||
<path d="M13 38h22" />
|
||||
<path d="M20 29h8" />
|
||||
</svg>
|
||||
@ -140,12 +154,98 @@
|
||||
>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="manager-tile"
|
||||
type="button"
|
||||
data-action="add-bd-leader"
|
||||
>
|
||||
<span
|
||||
class="tile-icon bd-leader-icon"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg viewBox="0 0 48 48">
|
||||
<path
|
||||
d="M24 7a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z"
|
||||
/>
|
||||
<path
|
||||
d="M9 42c2.3-8 7.4-12 15-12s12.7 4 15 12"
|
||||
/>
|
||||
<path d="M36 8v10M31 13h10" />
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="tile-title"
|
||||
data-i18n="manager_center.add_bd_leader"
|
||||
>Add BD Leader</span
|
||||
>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="manager-tile"
|
||||
type="button"
|
||||
data-action="add-admin"
|
||||
>
|
||||
<span
|
||||
class="tile-icon admin-icon"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg viewBox="0 0 48 48">
|
||||
<path
|
||||
d="M24 7a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z"
|
||||
/>
|
||||
<path
|
||||
d="M9 42c2.3-8 7.4-12 15-12s12.7 4 15 12"
|
||||
/>
|
||||
<path
|
||||
d="m35 9 2.2 4.4 4.8.7-3.5 3.4.8 4.8-4.3-2.3-4.3 2.3.8-4.8-3.5-3.4 4.8-.7Z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="tile-title"
|
||||
data-i18n="manager_center.add_admin"
|
||||
>Add Admin</span
|
||||
>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="manager-tile"
|
||||
type="button"
|
||||
data-action="add-superadmin"
|
||||
>
|
||||
<span
|
||||
class="tile-icon superadmin-icon"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg viewBox="0 0 48 48">
|
||||
<path
|
||||
d="M24 7a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z"
|
||||
/>
|
||||
<path
|
||||
d="M9 42c2.3-8 7.4-12 15-12s12.7 4 15 12"
|
||||
/>
|
||||
<path
|
||||
d="m34 8 2.5 5 5.5.8-4 3.9.9 5.5-4.9-2.6-4.9 2.6.9-5.5-4-3.9 5.5-.8Z"
|
||||
/>
|
||||
<path d="M34 12v8M30 16h8" />
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="tile-title"
|
||||
data-i18n="manager_center.add_superadmin"
|
||||
>Add Superadmin</span
|
||||
>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="manager-tile manager-tile-wide"
|
||||
type="button"
|
||||
data-action="block-user"
|
||||
>
|
||||
<span class="tile-icon block-icon" aria-hidden="true">
|
||||
<span
|
||||
class="tile-icon block-icon"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg viewBox="0 0 48 48">
|
||||
<path
|
||||
d="M18 22a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z"
|
||||
@ -246,12 +346,7 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="block-modal"
|
||||
id="blockModal"
|
||||
aria-hidden="true"
|
||||
hidden
|
||||
>
|
||||
<div class="block-modal" id="blockModal" aria-hidden="true" hidden>
|
||||
<button
|
||||
class="block-backdrop"
|
||||
id="blockBackdrop"
|
||||
@ -375,10 +470,10 @@
|
||||
</div>
|
||||
|
||||
<script src="../../common/toast.js"></script>
|
||||
<script src="../../common/api.js?v=20260608-manager-real"></script>
|
||||
<script src="../../common/api.js?v=20260612-manager-bd-leader"></script>
|
||||
<script src="../../common/params.js?v=20260608-manager-real"></script>
|
||||
<script src="../../common/jsbridge.js"></script>
|
||||
<script src="../../common/i18n.js?v=20260608"></script>
|
||||
<script src="./script.js?v=20260608-manager-duration"></script>
|
||||
<script src="./script.js?v=20260612-manager-bd-leader"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -5,6 +5,9 @@
|
||||
'send-car': 'Send Car',
|
||||
'send-vip': 'Send VIP',
|
||||
'update-user-level': 'Update User Level',
|
||||
'add-bd-leader': 'Add BD Leader',
|
||||
'add-admin': 'Add Admin',
|
||||
'add-superadmin': 'Add Superadmin',
|
||||
};
|
||||
var DAY_MS = 24 * 60 * 60 * 1000;
|
||||
var PERMANENT_BLOCK_MS = 10 * 365 * DAY_MS;
|
||||
@ -54,6 +57,12 @@
|
||||
selectedVipLevel: 0,
|
||||
submitting: false,
|
||||
};
|
||||
var bdLeaderState = {
|
||||
action: '',
|
||||
title: '',
|
||||
searchedUser: null,
|
||||
submitting: false,
|
||||
};
|
||||
|
||||
function $(id) {
|
||||
return document.getElementById(id);
|
||||
@ -193,7 +202,10 @@
|
||||
target_avatar: item.target_avatar || item.targetAvatar || '',
|
||||
country: item.country || '',
|
||||
blocked_until_ms:
|
||||
item.blocked_until_ms || item.blockedUntilMs || item.unblockAtMs || 0,
|
||||
item.blocked_until_ms ||
|
||||
item.blockedUntilMs ||
|
||||
item.unblockAtMs ||
|
||||
0,
|
||||
status: item.status || 'active',
|
||||
};
|
||||
}
|
||||
@ -300,7 +312,11 @@
|
||||
function ensureTrackSelector() {
|
||||
var panel = $('levelPanel');
|
||||
if (!panel || $('levelTrackGrid')) return;
|
||||
var title = createNode('div', 'block-section-title', 'Select level type');
|
||||
var title = createNode(
|
||||
'div',
|
||||
'block-section-title',
|
||||
'Select level type'
|
||||
);
|
||||
var grid = createNode('div', 'track-grid');
|
||||
grid.id = 'levelTrackGrid';
|
||||
Object.keys(TRACK_LABELS).forEach(function (track) {
|
||||
@ -359,7 +375,9 @@
|
||||
}
|
||||
|
||||
function renderLevelButtons() {
|
||||
var trackButtons = document.querySelectorAll('#levelTrackGrid [data-track]');
|
||||
var trackButtons = document.querySelectorAll(
|
||||
'#levelTrackGrid [data-track]'
|
||||
);
|
||||
trackButtons.forEach(function (button) {
|
||||
button.classList.toggle(
|
||||
'is-selected',
|
||||
@ -388,7 +406,9 @@
|
||||
}
|
||||
|
||||
function renderDurationButtons() {
|
||||
var buttons = document.querySelectorAll('#durationGrid [data-duration-ms]');
|
||||
var buttons = document.querySelectorAll(
|
||||
'#durationGrid [data-duration-ms]'
|
||||
);
|
||||
buttons.forEach(function (button) {
|
||||
var isSelected =
|
||||
Number(button.getAttribute('data-duration-ms')) ===
|
||||
@ -679,7 +699,8 @@
|
||||
grantState.title = ACTION_LABELS[action] || 'Send';
|
||||
grantState.searchedUser = null;
|
||||
grantState.selectedResourceId = '';
|
||||
grantState.selectedResourceDurationMs = DEFAULT_RESOURCE_GRANT_DURATION_MS;
|
||||
grantState.selectedResourceDurationMs =
|
||||
DEFAULT_RESOURCE_GRANT_DURATION_MS;
|
||||
grantState.selectedVipLevel = 0;
|
||||
$('grantDialogTitle').textContent = grantState.title;
|
||||
$('grantSearchInput').value = '';
|
||||
@ -688,7 +709,8 @@
|
||||
$('grantModal').hidden = false;
|
||||
$('grantModal').setAttribute('aria-hidden', 'false');
|
||||
renderGrantOptions();
|
||||
if (grantState.resourceType) loadGrantResources(grantState.resourceType);
|
||||
if (grantState.resourceType)
|
||||
loadGrantResources(grantState.resourceType);
|
||||
setTimeout(function () {
|
||||
$('grantSearchInput').focus();
|
||||
}, 30);
|
||||
@ -732,10 +754,14 @@
|
||||
var container = $('grantSearchResult');
|
||||
container.innerHTML = '';
|
||||
container.appendChild(
|
||||
renderUserRow(grantState.searchedUser, 'Select', function () {
|
||||
$('grantPanel').hidden = false;
|
||||
renderGrantOptions();
|
||||
})
|
||||
renderUserRow(
|
||||
grantState.searchedUser,
|
||||
'Select',
|
||||
function () {
|
||||
$('grantPanel').hidden = false;
|
||||
renderGrantOptions();
|
||||
}
|
||||
)
|
||||
);
|
||||
$('grantPanel').hidden = false;
|
||||
renderGrantOptions();
|
||||
@ -777,9 +803,12 @@
|
||||
});
|
||||
} else {
|
||||
$('grantOptionTitle').textContent = 'Select resource';
|
||||
var resources = pageState.resourcesByType[grantState.resourceType] || [];
|
||||
var resources =
|
||||
pageState.resourcesByType[grantState.resourceType] || [];
|
||||
if (!resources.length) {
|
||||
grid.appendChild(createNode('div', 'blocked-empty', 'No resource available.'));
|
||||
grid.appendChild(
|
||||
createNode('div', 'blocked-empty', 'No resource available.')
|
||||
);
|
||||
}
|
||||
resources.forEach(function (resource) {
|
||||
var button = createNode('button', 'grant-resource-option');
|
||||
@ -789,7 +818,8 @@
|
||||
String(grantState.selectedResourceId) ===
|
||||
String(resource.resource_id)
|
||||
);
|
||||
button.textContent = resource.name || 'Resource #' + resource.resource_id;
|
||||
button.textContent =
|
||||
resource.name || 'Resource #' + resource.resource_id;
|
||||
button.addEventListener('click', function () {
|
||||
grantState.selectedResourceId = resource.resource_id;
|
||||
renderGrantOptions();
|
||||
@ -854,6 +884,132 @@
|
||||
});
|
||||
}
|
||||
|
||||
function ensureBDLeaderModal() {
|
||||
if ($('bdLeaderModal')) return;
|
||||
var modal = createNode('div', 'block-modal bd-leader-modal');
|
||||
modal.id = 'bdLeaderModal';
|
||||
modal.hidden = true;
|
||||
modal.setAttribute('aria-hidden', 'true');
|
||||
modal.innerHTML =
|
||||
'<button class="block-backdrop" id="bdLeaderBackdrop" type="button" aria-label="Close"></button>' +
|
||||
'<section class="block-dialog" role="dialog" aria-modal="true" aria-labelledby="bdLeaderDialogTitle">' +
|
||||
'<div class="block-dialog-header"><h2 id="bdLeaderDialogTitle">Add BD Leader</h2>' +
|
||||
'<button class="block-close" id="bdLeaderCloseButton" type="button" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button></div>' +
|
||||
'<div class="block-dialog-body"><div class="block-field"><label for="bdLeaderSearchInput">User ID</label>' +
|
||||
'<div class="block-search-row"><input id="bdLeaderSearchInput" type="text" inputmode="numeric" autocomplete="off" placeholder="Enter user ID"/><button id="bdLeaderSearchButton" type="button">Search</button></div></div>' +
|
||||
'<div class="block-search-result" id="bdLeaderSearchResult" aria-live="polite"></div>' +
|
||||
'<div class="level-panel" id="bdLeaderPanel" hidden><div class="block-section-title">Add this user as BD Leader</div>' +
|
||||
'<button class="confirm-level-button" id="confirmBDLeaderButton" type="button" disabled>Add BD Leader</button></div></div></section>';
|
||||
document.querySelector('.manager-center').appendChild(modal);
|
||||
$('bdLeaderBackdrop').addEventListener('click', closeBDLeaderModal);
|
||||
$('bdLeaderCloseButton').addEventListener('click', closeBDLeaderModal);
|
||||
$('bdLeaderSearchButton').addEventListener('click', searchBDLeaderUser);
|
||||
$('bdLeaderSearchInput').addEventListener('keydown', function (event) {
|
||||
if (event.key === 'Enter') searchBDLeaderUser();
|
||||
});
|
||||
$('confirmBDLeaderButton').addEventListener('click', confirmBDLeader);
|
||||
}
|
||||
|
||||
function openBDLeaderModal(action) {
|
||||
if (pageState.denied) {
|
||||
toast('Permission denied.');
|
||||
return;
|
||||
}
|
||||
ensureBDLeaderModal();
|
||||
bdLeaderState.action = action;
|
||||
bdLeaderState.title = ACTION_LABELS[action] || 'Add BD Leader';
|
||||
bdLeaderState.searchedUser = null;
|
||||
bdLeaderState.submitting = false;
|
||||
$('bdLeaderDialogTitle').textContent = bdLeaderState.title;
|
||||
$('confirmBDLeaderButton').textContent = bdLeaderState.title;
|
||||
$('bdLeaderSearchInput').value = '';
|
||||
$('bdLeaderSearchResult').innerHTML = '';
|
||||
$('bdLeaderPanel').hidden = true;
|
||||
renderBDLeaderActions();
|
||||
$('bdLeaderModal').hidden = false;
|
||||
$('bdLeaderModal').setAttribute('aria-hidden', 'false');
|
||||
setTimeout(function () {
|
||||
$('bdLeaderSearchInput').focus();
|
||||
}, 30);
|
||||
}
|
||||
|
||||
function closeBDLeaderModal() {
|
||||
var modal = $('bdLeaderModal');
|
||||
if (!modal) return;
|
||||
modal.hidden = true;
|
||||
modal.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
|
||||
function searchBDLeaderUser() {
|
||||
var value = $('bdLeaderSearchInput').value.trim();
|
||||
if (!value) {
|
||||
toast('Enter user ID.');
|
||||
return;
|
||||
}
|
||||
apiSearchUser(value)
|
||||
.then(function (items) {
|
||||
if (!items.length) {
|
||||
bdLeaderState.searchedUser = null;
|
||||
$('bdLeaderSearchResult').innerHTML = '';
|
||||
$('bdLeaderPanel').hidden = true;
|
||||
renderBDLeaderActions();
|
||||
toast('No user found.');
|
||||
return;
|
||||
}
|
||||
bdLeaderState.searchedUser = items[0];
|
||||
var container = $('bdLeaderSearchResult');
|
||||
container.innerHTML = '';
|
||||
container.appendChild(
|
||||
renderUserRow(
|
||||
bdLeaderState.searchedUser,
|
||||
'Select',
|
||||
function () {
|
||||
$('bdLeaderPanel').hidden = false;
|
||||
renderBDLeaderActions();
|
||||
}
|
||||
)
|
||||
);
|
||||
$('bdLeaderPanel').hidden = false;
|
||||
renderBDLeaderActions();
|
||||
})
|
||||
.catch(showError);
|
||||
}
|
||||
|
||||
function renderBDLeaderActions() {
|
||||
var confirm = $('confirmBDLeaderButton');
|
||||
if (!confirm) return;
|
||||
confirm.disabled =
|
||||
!bdLeaderState.searchedUser || bdLeaderState.submitting;
|
||||
}
|
||||
|
||||
function confirmBDLeader() {
|
||||
if (!bdLeaderState.searchedUser) {
|
||||
toast('Search a user first.');
|
||||
return;
|
||||
}
|
||||
if (!api || !api.createBDLeader) {
|
||||
toast('API not available.');
|
||||
return;
|
||||
}
|
||||
if (bdLeaderState.submitting) return;
|
||||
bdLeaderState.submitting = true;
|
||||
renderBDLeaderActions();
|
||||
api.createBDLeader({
|
||||
command_id: commandId('manager-bd-leader'),
|
||||
target_user_id: userId(bdLeaderState.searchedUser),
|
||||
reason: 'manager_center',
|
||||
})
|
||||
.then(function () {
|
||||
toast('BD Leader added.');
|
||||
closeBDLeaderModal();
|
||||
})
|
||||
.catch(showError)
|
||||
.finally(function () {
|
||||
bdLeaderState.submitting = false;
|
||||
renderBDLeaderActions();
|
||||
});
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
var backButton = $('backButton');
|
||||
if (backButton) backButton.addEventListener('click', goBack);
|
||||
@ -868,6 +1024,14 @@
|
||||
openLevelModal();
|
||||
return;
|
||||
}
|
||||
if (
|
||||
action === 'add-bd-leader' ||
|
||||
action === 'add-admin' ||
|
||||
action === 'add-superadmin'
|
||||
) {
|
||||
openBDLeaderModal(action);
|
||||
return;
|
||||
}
|
||||
openGrantModal(action);
|
||||
});
|
||||
});
|
||||
@ -885,21 +1049,26 @@
|
||||
var confirmLevelButton = $('confirmLevelButton');
|
||||
if (closeButton) closeButton.addEventListener('click', closeBlockModal);
|
||||
if (backdrop) backdrop.addEventListener('click', closeBlockModal);
|
||||
if (levelCloseButton) levelCloseButton.addEventListener('click', closeLevelModal);
|
||||
if (levelBackdrop) levelBackdrop.addEventListener('click', closeLevelModal);
|
||||
if (searchButton) searchButton.addEventListener('click', searchBlockUser);
|
||||
if (levelCloseButton)
|
||||
levelCloseButton.addEventListener('click', closeLevelModal);
|
||||
if (levelBackdrop)
|
||||
levelBackdrop.addEventListener('click', closeLevelModal);
|
||||
if (searchButton)
|
||||
searchButton.addEventListener('click', searchBlockUser);
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('keydown', function (event) {
|
||||
if (event.key === 'Enter') searchBlockUser();
|
||||
});
|
||||
}
|
||||
if (levelSearchButton) levelSearchButton.addEventListener('click', searchLevelUser);
|
||||
if (levelSearchButton)
|
||||
levelSearchButton.addEventListener('click', searchLevelUser);
|
||||
if (levelSearchInput) {
|
||||
levelSearchInput.addEventListener('keydown', function (event) {
|
||||
if (event.key === 'Enter') searchLevelUser();
|
||||
});
|
||||
}
|
||||
if (confirmButton) confirmButton.addEventListener('click', confirmBlockUser);
|
||||
if (confirmButton)
|
||||
confirmButton.addEventListener('click', confirmBlockUser);
|
||||
if (confirmLevelButton) {
|
||||
confirmLevelButton.addEventListener('click', confirmLevelUpdate);
|
||||
}
|
||||
@ -913,11 +1082,13 @@
|
||||
);
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('#levelGrid [data-level]').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
selectLevel(button.getAttribute('data-level'));
|
||||
document
|
||||
.querySelectorAll('#levelGrid [data-level]')
|
||||
.forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
selectLevel(button.getAttribute('data-level'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
||||
@ -15,10 +15,15 @@
|
||||
.manager-center.is-loading .manager-panel::after {
|
||||
position: absolute;
|
||||
inset: 16px;
|
||||
content: "";
|
||||
content: '';
|
||||
border-radius: 10px;
|
||||
background:
|
||||
linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.72), transparent)
|
||||
linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.72),
|
||||
transparent
|
||||
)
|
||||
0 0 / 180px 100% no-repeat,
|
||||
linear-gradient(#f1e7ff, #f1e7ff) 0 0 / 100% 132px no-repeat,
|
||||
linear-gradient(#f1e7ff, #f1e7ff) 0 144px / 100% 132px no-repeat,
|
||||
@ -167,6 +172,21 @@
|
||||
background: #fff1f4;
|
||||
}
|
||||
|
||||
.bd-leader-icon {
|
||||
color: #3c8a6b;
|
||||
background: #eaf8f1;
|
||||
}
|
||||
|
||||
.admin-icon {
|
||||
color: #4e78d7;
|
||||
background: #eef4ff;
|
||||
}
|
||||
|
||||
.superadmin-icon {
|
||||
color: #bd6c2f;
|
||||
background: #fff2e6;
|
||||
}
|
||||
|
||||
.block-modal[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||