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 { DEV_MOCK } from "./previewMock.js";
|
||||||
import { DEMO_DATASET } from "./demoDataset.js";
|
import { DEMO_DATASET } from "./demoDataset.js";
|
||||||
|
import { withDemoAvatar, withDemoAvatarDetail, withDemoAvatarTicket } from "./demoAvatars.js";
|
||||||
|
|
||||||
const DEMO_LANGUAGE_STORAGE_KEY = "rw_minishop_demo_language";
|
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) {
|
function filterDemoUsers(params) {
|
||||||
let out = [...(DEMO_DATASET.adminUsers || [])];
|
let out = [...(DEMO_DATASET.adminUsers || [])];
|
||||||
const q = (params.get("q") || params.get("search") || "").trim().toLowerCase();
|
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);
|
const page = paged(filtered, params, 25);
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
users: clone(page.items),
|
users: clone(withDemoAvatars(page.items)),
|
||||||
total: page.total,
|
total: page.total,
|
||||||
page: page.page,
|
page: page.page,
|
||||||
page_size: page.pageSize,
|
page_size: page.pageSize,
|
||||||
@@ -309,6 +318,7 @@ function demoApiResponse(path, cleanPath, options, context) {
|
|||||||
const id = Number(parts[3]);
|
const id = Number(parts[3]);
|
||||||
const detail = DEMO_DATASET.adminUserDetails?.[String(id)];
|
const detail = DEMO_DATASET.adminUserDetails?.[String(id)];
|
||||||
if (!detail) return { ok: false, error: "not_found" };
|
if (!detail) return { ok: false, error: "not_found" };
|
||||||
|
const decoratedDetail = withDemoAvatarDetail(detail);
|
||||||
if (parts[4]) {
|
if (parts[4]) {
|
||||||
if (parts[4] === "telegram-profile-link") {
|
if (parts[4] === "telegram-profile-link") {
|
||||||
return { ok: true, url: `https://t.me/${detail.user?.username || "demo_user"}` };
|
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") {
|
if (parts[4] === "message" && parts[5] === "preview") {
|
||||||
return { ok: true, text: "Demo broadcast preview for the selected account." };
|
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") {
|
if (cleanPath === "/admin/logs") {
|
||||||
@@ -486,7 +496,7 @@ function demoApiResponse(path, cleanPath, options, context) {
|
|||||||
if (cleanPath === "/admin/support/tickets") {
|
if (cleanPath === "/admin/support/tickets") {
|
||||||
const tickets = filterDemoSupportTickets(demoSupportTickets(), params);
|
const tickets = filterDemoSupportTickets(demoSupportTickets(), params);
|
||||||
const page = paged(tickets, params, 50);
|
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/")) {
|
if (cleanPath.startsWith("/admin/support/tickets/")) {
|
||||||
const parts = cleanPath.split("/");
|
const parts = cleanPath.split("/");
|
||||||
@@ -515,17 +525,17 @@ function demoApiResponse(path, cleanPath, options, context) {
|
|||||||
ticket.last_message_at = message.created_at;
|
ticket.last_message_at = message.created_at;
|
||||||
ticket.last_message_role = "admin";
|
ticket.last_message_role = "admin";
|
||||||
ticket.status = "awaiting_user";
|
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") {
|
if (method === "PATCH") {
|
||||||
Object.assign(ticket, jsonBody(options));
|
Object.assign(ticket, jsonBody(options));
|
||||||
return { ok: true, ticket: clone(ticket) };
|
return { ok: true, ticket: clone(withDemoAvatarTicket(ticket)) };
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
ticket: clone(ticket),
|
ticket: clone(withDemoAvatarTicket(ticket)),
|
||||||
messages: clone(messages),
|
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);
|
const page = paged(tickets, params, 50);
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
tickets: clone(page.items),
|
tickets: clone(withDemoAvatarTickets(page.items)),
|
||||||
total: page.total,
|
total: page.total,
|
||||||
counts: demoSupportCounts(demoSupportTickets()),
|
counts: demoSupportCounts(demoSupportTickets()),
|
||||||
};
|
};
|
||||||
@@ -596,9 +606,9 @@ function demoApiResponse(path, cleanPath, options, context) {
|
|||||||
ticket.last_message_at = message.created_at;
|
ticket.last_message_at = message.created_at;
|
||||||
ticket.last_message_role = "user";
|
ticket.last_message_role = "user";
|
||||||
ticket.status = "awaiting_admin";
|
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") {
|
if (cleanPath === "/support/unread") {
|
||||||
return {
|
return {
|
||||||
@@ -635,7 +645,7 @@ export async function mockApi(path, options = {}, context = {}) {
|
|||||||
normalizeLangCode,
|
normalizeLangCode,
|
||||||
});
|
});
|
||||||
if (demoResponse !== undefined) return demoResponse;
|
if (demoResponse !== undefined) return demoResponse;
|
||||||
const adminUsers = [
|
const adminUsers = withDemoAvatars([
|
||||||
{
|
{
|
||||||
user_id: 100200300,
|
user_id: 100200300,
|
||||||
telegram_id: 100200300,
|
telegram_id: 100200300,
|
||||||
@@ -684,7 +694,7 @@ export async function mockApi(path, options = {}, context = {}) {
|
|||||||
is_banned: true,
|
is_banned: true,
|
||||||
premium_traffic: { state: "none" },
|
premium_traffic: { state: "none" },
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
const supportTickets = [
|
const supportTickets = [
|
||||||
{
|
{
|
||||||
ticket_id: 42,
|
ticket_id: 42,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { DEMO_DATASET } from "./demoDataset.js";
|
import { DEMO_DATASET } from "./demoDataset.js";
|
||||||
|
import { withDemoAvatar } from "./demoAvatars.js";
|
||||||
|
|
||||||
const DEMO_LANGUAGE_STORAGE_KEY = "rw_minishop_demo_language";
|
const DEMO_LANGUAGE_STORAGE_KEY = "rw_minishop_demo_language";
|
||||||
|
|
||||||
@@ -441,11 +442,14 @@ export const DEV_MOCK = {
|
|||||||
|
|
||||||
function applyDemoDataset() {
|
function applyDemoDataset() {
|
||||||
const storedLanguage = readStoredDemoLanguage();
|
const storedLanguage = readStoredDemoLanguage();
|
||||||
const demoUser = {
|
const demoUser = withDemoAvatar(
|
||||||
...(DEMO_DATASET.currentUser || {}),
|
{
|
||||||
id: DEMO_DATASET.currentUser?.id ?? DEMO_DATASET.currentUser?.user_id,
|
...(DEMO_DATASET.currentUser || {}),
|
||||||
language_code: storedLanguage || DEMO_DATASET.currentUser?.language_code || "ru",
|
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 || {});
|
Object.assign(DEV_MOCK.config, DEMO_DATASET.config || {});
|
||||||
DEV_MOCK.config.language = demoUser.language_code || "ru";
|
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;
|
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) {
|
export function applyPreviewMock(kind) {
|
||||||
const mode = String(kind || "")
|
const mode = String(kind || "")
|
||||||
.trim()
|
.trim()
|
||||||
@@ -745,6 +780,26 @@ export function applyPreviewMock(kind) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
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") {
|
} else if (mode === "devices") {
|
||||||
DEV_MOCK.data.settings.my_devices_enabled = true;
|
DEV_MOCK.data.settings.my_devices_enabled = true;
|
||||||
DEV_MOCK.data.subscription = {
|
DEV_MOCK.data.subscription = {
|
||||||
@@ -755,23 +810,6 @@ export function applyPreviewMock(kind) {
|
|||||||
extra_hwid_devices_valid_until_text: "01.06.2026 12:00",
|
extra_hwid_devices_valid_until_text: "01.06.2026 12:00",
|
||||||
};
|
};
|
||||||
} else if (mode === "trial") {
|
} else if (mode === "trial") {
|
||||||
DEV_MOCK.data.settings.traffic_mode = false;
|
applyInactiveSubscriptionScenario({ trialAvailable: true });
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user