feat: enrich docs demo mock data
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
const DEMO_ADMIN_EMAIL = "3252a8@proton.me";
|
||||
const DEMO_ADMIN_GRAVATAR_HASH = "e06e9ae24816fb1d6ed86b58e6fd00d3abe7860b8d51128b8a3e848f208d5e92";
|
||||
const DEMO_ADMIN_GRAVATAR_URL = `https://www.gravatar.com/avatar/${DEMO_ADMIN_GRAVATAR_HASH}?d=mp&s=160`;
|
||||
|
||||
function normalizedEmail(value) {
|
||||
return String(value || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function hasLinkedTelegram(user) {
|
||||
if (!user || user.telegram_linked === false) return false;
|
||||
return Boolean(user.telegram_linked || Number(user.telegram_id || 0) > 0);
|
||||
}
|
||||
|
||||
function avatarSeed(user) {
|
||||
return encodeURIComponent(
|
||||
String(
|
||||
user?.telegram_id || user?.user_id || user?.id || user?.username || user?.email || "user"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function demoAvatarUrl(user, size = 96) {
|
||||
if (!user) return "";
|
||||
if (normalizedEmail(user.email) === DEMO_ADMIN_EMAIL) return DEMO_ADMIN_GRAVATAR_URL;
|
||||
if (!hasLinkedTelegram(user)) return "";
|
||||
return `https://i.pravatar.cc/${size}?u=remnawave-minishop-demo-${avatarSeed(user)}`;
|
||||
}
|
||||
|
||||
export function withDemoAvatar(user, size = 96) {
|
||||
if (!user || typeof user !== "object") return user;
|
||||
const avatarUrl = demoAvatarUrl(user, size);
|
||||
if (!avatarUrl) return user;
|
||||
return {
|
||||
...user,
|
||||
avatar_url: avatarUrl,
|
||||
telegram_photo_url: avatarUrl,
|
||||
};
|
||||
}
|
||||
|
||||
export function withDemoAvatarDetail(detail, size = 96) {
|
||||
if (!detail || typeof detail !== "object") return detail;
|
||||
return {
|
||||
...detail,
|
||||
user: withDemoAvatar(detail.user, size),
|
||||
};
|
||||
}
|
||||
|
||||
export function withDemoAvatarTicket(ticket, size = 96) {
|
||||
if (!ticket || typeof ticket !== "object") return ticket;
|
||||
return {
|
||||
...ticket,
|
||||
user: withDemoAvatar(ticket.user, size),
|
||||
};
|
||||
}
|
||||
+4978
-4841
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
import { DEV_MOCK } from "./previewMock.js";
|
||||
import { DEMO_DATASET } from "./demoDataset.js";
|
||||
import { withDemoAvatar, withDemoAvatarDetail, withDemoAvatarTicket } from "./demoAvatars.js";
|
||||
|
||||
const DEMO_LANGUAGE_STORAGE_KEY = "rw_minishop_demo_language";
|
||||
|
||||
@@ -108,6 +109,14 @@ function userName(user) {
|
||||
);
|
||||
}
|
||||
|
||||
function withDemoAvatars(users, size = 96) {
|
||||
return (users || []).map((user) => withDemoAvatar(user, size));
|
||||
}
|
||||
|
||||
function withDemoAvatarTickets(tickets, size = 96) {
|
||||
return (tickets || []).map((ticket) => withDemoAvatarTicket(ticket, size));
|
||||
}
|
||||
|
||||
function filterDemoUsers(params) {
|
||||
let out = [...(DEMO_DATASET.adminUsers || [])];
|
||||
const q = (params.get("q") || params.get("search") || "").trim().toLowerCase();
|
||||
@@ -298,7 +307,7 @@ function demoApiResponse(path, cleanPath, options, context) {
|
||||
const page = paged(filtered, params, 25);
|
||||
return {
|
||||
ok: true,
|
||||
users: clone(page.items),
|
||||
users: clone(withDemoAvatars(page.items)),
|
||||
total: page.total,
|
||||
page: page.page,
|
||||
page_size: page.pageSize,
|
||||
@@ -309,6 +318,7 @@ function demoApiResponse(path, cleanPath, options, context) {
|
||||
const id = Number(parts[3]);
|
||||
const detail = DEMO_DATASET.adminUserDetails?.[String(id)];
|
||||
if (!detail) return { ok: false, error: "not_found" };
|
||||
const decoratedDetail = withDemoAvatarDetail(detail);
|
||||
if (parts[4]) {
|
||||
if (parts[4] === "telegram-profile-link") {
|
||||
return { ok: true, url: `https://t.me/${detail.user?.username || "demo_user"}` };
|
||||
@@ -316,9 +326,9 @@ function demoApiResponse(path, cleanPath, options, context) {
|
||||
if (parts[4] === "message" && parts[5] === "preview") {
|
||||
return { ok: true, text: "Demo broadcast preview for the selected account." };
|
||||
}
|
||||
return { ok: true, user: clone(detail.user), detail: clone(detail) };
|
||||
return { ok: true, user: clone(decoratedDetail.user), detail: clone(decoratedDetail) };
|
||||
}
|
||||
return clone(detail);
|
||||
return clone(decoratedDetail);
|
||||
}
|
||||
|
||||
if (cleanPath === "/admin/logs") {
|
||||
@@ -486,7 +496,7 @@ function demoApiResponse(path, cleanPath, options, context) {
|
||||
if (cleanPath === "/admin/support/tickets") {
|
||||
const tickets = filterDemoSupportTickets(demoSupportTickets(), params);
|
||||
const page = paged(tickets, params, 50);
|
||||
return { ok: true, tickets: clone(page.items), total: page.total };
|
||||
return { ok: true, tickets: clone(withDemoAvatarTickets(page.items)), total: page.total };
|
||||
}
|
||||
if (cleanPath.startsWith("/admin/support/tickets/")) {
|
||||
const parts = cleanPath.split("/");
|
||||
@@ -515,17 +525,17 @@ function demoApiResponse(path, cleanPath, options, context) {
|
||||
ticket.last_message_at = message.created_at;
|
||||
ticket.last_message_role = "admin";
|
||||
ticket.status = "awaiting_user";
|
||||
return { ok: true, ticket: clone(ticket), message: clone(message) };
|
||||
return { ok: true, ticket: clone(withDemoAvatarTicket(ticket)), message: clone(message) };
|
||||
}
|
||||
if (method === "PATCH") {
|
||||
Object.assign(ticket, jsonBody(options));
|
||||
return { ok: true, ticket: clone(ticket) };
|
||||
return { ok: true, ticket: clone(withDemoAvatarTicket(ticket)) };
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
ticket: clone(ticket),
|
||||
ticket: clone(withDemoAvatarTicket(ticket)),
|
||||
messages: clone(messages),
|
||||
user_snapshot: userSnapshotForTicket(ticket),
|
||||
user_snapshot: userSnapshotForTicket(withDemoAvatarTicket(ticket)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -564,7 +574,7 @@ function demoApiResponse(path, cleanPath, options, context) {
|
||||
const page = paged(tickets, params, 50);
|
||||
return {
|
||||
ok: true,
|
||||
tickets: clone(page.items),
|
||||
tickets: clone(withDemoAvatarTickets(page.items)),
|
||||
total: page.total,
|
||||
counts: demoSupportCounts(demoSupportTickets()),
|
||||
};
|
||||
@@ -596,9 +606,9 @@ function demoApiResponse(path, cleanPath, options, context) {
|
||||
ticket.last_message_at = message.created_at;
|
||||
ticket.last_message_role = "user";
|
||||
ticket.status = "awaiting_admin";
|
||||
return { ok: true, ticket: clone(ticket), message: clone(message) };
|
||||
return { ok: true, ticket: clone(withDemoAvatarTicket(ticket)), message: clone(message) };
|
||||
}
|
||||
return { ok: true, ticket: clone(ticket), messages: clone(messages) };
|
||||
return { ok: true, ticket: clone(withDemoAvatarTicket(ticket)), messages: clone(messages) };
|
||||
}
|
||||
if (cleanPath === "/support/unread") {
|
||||
return {
|
||||
@@ -635,7 +645,7 @@ export async function mockApi(path, options = {}, context = {}) {
|
||||
normalizeLangCode,
|
||||
});
|
||||
if (demoResponse !== undefined) return demoResponse;
|
||||
const adminUsers = [
|
||||
const adminUsers = withDemoAvatars([
|
||||
{
|
||||
user_id: 100200300,
|
||||
telegram_id: 100200300,
|
||||
@@ -684,7 +694,7 @@ export async function mockApi(path, options = {}, context = {}) {
|
||||
is_banned: true,
|
||||
premium_traffic: { state: "none" },
|
||||
},
|
||||
];
|
||||
]);
|
||||
const supportTickets = [
|
||||
{
|
||||
ticket_id: 42,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DEMO_DATASET } from "./demoDataset.js";
|
||||
import { withDemoAvatar } from "./demoAvatars.js";
|
||||
|
||||
const DEMO_LANGUAGE_STORAGE_KEY = "rw_minishop_demo_language";
|
||||
|
||||
@@ -441,11 +442,14 @@ export const DEV_MOCK = {
|
||||
|
||||
function applyDemoDataset() {
|
||||
const storedLanguage = readStoredDemoLanguage();
|
||||
const demoUser = {
|
||||
...(DEMO_DATASET.currentUser || {}),
|
||||
id: DEMO_DATASET.currentUser?.id ?? DEMO_DATASET.currentUser?.user_id,
|
||||
language_code: storedLanguage || DEMO_DATASET.currentUser?.language_code || "ru",
|
||||
};
|
||||
const demoUser = withDemoAvatar(
|
||||
{
|
||||
...(DEMO_DATASET.currentUser || {}),
|
||||
id: DEMO_DATASET.currentUser?.id ?? DEMO_DATASET.currentUser?.user_id,
|
||||
language_code: storedLanguage || DEMO_DATASET.currentUser?.language_code || "ru",
|
||||
},
|
||||
160
|
||||
);
|
||||
|
||||
Object.assign(DEV_MOCK.config, DEMO_DATASET.config || {});
|
||||
DEV_MOCK.config.language = demoUser.language_code || "ru";
|
||||
@@ -488,6 +492,37 @@ function applyDemoTariffScenario(subscriptionPatch = {}) {
|
||||
DEMO_DATASET.device_topup_options || DEV_MOCK.data.device_topup_options;
|
||||
}
|
||||
|
||||
function applyInactiveSubscriptionScenario({ trialAvailable = false } = {}) {
|
||||
DEV_MOCK.data.settings.traffic_mode = false;
|
||||
DEV_MOCK.data.settings.trial_enabled = true;
|
||||
DEV_MOCK.data.settings.trial_available = Boolean(trialAvailable);
|
||||
DEV_MOCK.data.settings.trial_duration_days = 5;
|
||||
DEV_MOCK.data.settings.trial_traffic_limit_gb = 10;
|
||||
DEV_MOCK.data.subscription = {
|
||||
...DEV_MOCK.data.subscription,
|
||||
active: false,
|
||||
status: "INACTIVE",
|
||||
remaining_text: "Подписка не активна",
|
||||
end_date_text: "",
|
||||
days_left: 0,
|
||||
config_link: null,
|
||||
connect_url: null,
|
||||
panel_short_uuid: "",
|
||||
install_share_token: "",
|
||||
install_share_url: "",
|
||||
traffic_used: "0 B",
|
||||
traffic_limit: "0 GB",
|
||||
traffic_used_bytes: 0,
|
||||
traffic_limit_bytes: 0,
|
||||
premium_used_bytes: 0,
|
||||
premium_limit_bytes: 0,
|
||||
premium_is_limited: false,
|
||||
};
|
||||
DEV_MOCK.data.plans = DEMO_DATASET.plans || DEV_MOCK.data.plans;
|
||||
DEV_MOCK.data.tariff_change_options =
|
||||
DEMO_DATASET.tariff_change_options || DEV_MOCK.data.tariff_change_options;
|
||||
}
|
||||
|
||||
export function applyPreviewMock(kind) {
|
||||
const mode = String(kind || "")
|
||||
.trim()
|
||||
@@ -745,6 +780,26 @@ export function applyPreviewMock(kind) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
} else if (mode === "no-subscription" || mode === "inactive") {
|
||||
applyInactiveSubscriptionScenario();
|
||||
} else if (mode === "expiring" || mode === "ending-soon") {
|
||||
DEV_MOCK.data.settings.traffic_mode = false;
|
||||
DEV_MOCK.data.settings.trial_available = false;
|
||||
if (DEMO_DATASET.plans?.length) {
|
||||
applyDemoTariffScenario({
|
||||
remaining_text: "2 д.",
|
||||
end_date_text: "30.05.2026",
|
||||
days_left: 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
DEV_MOCK.data.subscription = {
|
||||
...DEV_MOCK.data.subscription,
|
||||
active: true,
|
||||
remaining_text: "2 д.",
|
||||
end_date_text: "30.05.2026",
|
||||
days_left: 2,
|
||||
};
|
||||
} else if (mode === "devices") {
|
||||
DEV_MOCK.data.settings.my_devices_enabled = true;
|
||||
DEV_MOCK.data.subscription = {
|
||||
@@ -755,23 +810,6 @@ export function applyPreviewMock(kind) {
|
||||
extra_hwid_devices_valid_until_text: "01.06.2026 12:00",
|
||||
};
|
||||
} else if (mode === "trial") {
|
||||
DEV_MOCK.data.settings.traffic_mode = false;
|
||||
DEV_MOCK.data.settings.trial_enabled = true;
|
||||
DEV_MOCK.data.settings.trial_available = true;
|
||||
DEV_MOCK.data.settings.trial_duration_days = 5;
|
||||
DEV_MOCK.data.settings.trial_traffic_limit_gb = 10;
|
||||
DEV_MOCK.data.subscription = {
|
||||
active: false,
|
||||
status: "INACTIVE",
|
||||
remaining_text: "Подписка не активна",
|
||||
end_date_text: "",
|
||||
days_left: 0,
|
||||
config_link: null,
|
||||
connect_url: null,
|
||||
traffic_used: "0 B",
|
||||
traffic_limit: "10 GB",
|
||||
traffic_used_bytes: 0,
|
||||
traffic_limit_bytes: 10737418240,
|
||||
};
|
||||
applyInactiveSubscriptionScenario({ trialAvailable: true });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user