40104-vm/code.js
2026-05-27 08:52:05 +00:00

1335 lines
85 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// BeautyHub Figma Plugin v3 (Enhanced Design + Auth + Onboarding)
figma.showUI(__html__, { width: 360, height: 600 });
// ── Palette ──────────────────────────────────────────────────────
const C = {
wine: { r: 0.482, g: 0.176, b: 0.294 },
wineDark: { r: 0.310, g: 0.090, b: 0.180 },
wineLight: { r: 0.600, g: 0.290, b: 0.420 },
wine50: { r: 0.976, g: 0.941, b: 0.953 },
wine100: { r: 0.941, g: 0.839, b: 0.878 },
gold: { r: 0.831, g: 0.659, b: 0.325 },
goldDark: { r: 0.690, g: 0.541, b: 0.239 },
goldLight: { r: 0.960, g: 0.890, b: 0.720 },
cream: { r: 0.961, g: 0.941, b: 0.922 },
creamDark: { r: 0.898, g: 0.867, b: 0.839 },
white: { r: 1, g: 1, b: 1 },
bg: { r: 0.973, g: 0.961, b: 0.949 },
textPrimary: { r: 0.133, g: 0.071, b: 0.110 },
textSecond: { r: 0.310, g: 0.220, b: 0.275 },
textMuted: { r: 0.520, g: 0.430, b: 0.480 },
success: { r: 0.200, g: 0.580, b: 0.400 },
successBg: { r: 0.880, g: 0.960, b: 0.920 },
warning: { r: 0.820, g: 0.600, b: 0.120 },
warningBg: { r: 0.990, g: 0.960, b: 0.860 },
danger: { r: 0.780, g: 0.220, b: 0.220 },
dangerBg: { r: 0.990, g: 0.890, b: 0.890 },
border: { r: 0.880, g: 0.850, b: 0.830 },
borderLight: { r: 0.942, g: 0.922, b: 0.910 },
};
const FW = 1440, FH = 900, SW = 280, TH = 72, CW = FW - SW;
function safe(s) {
return String(s).replace(/[^\u0000-\uFFFF]/g, '') || ' ';
}
async function loadFonts() {
for (const style of ["Regular", "Medium", "Semi Bold", "Bold"]) {
await figma.loadFontAsync({ family: "Inter", style });
}
}
// ── Primitives ────────────────────────────────────────────────────
function R(p, x, y, w, h, col, a, r) {
const n = figma.createRectangle();
n.x = x; n.y = y;
n.resize(Math.max(w, 1), Math.max(h, 1));
n.fills = [{ type: "SOLID", color: col || C.white, opacity: a !== undefined ? a : 1 }];
n.cornerRadius = r || 0;
n.strokes = []; n.strokeWeight = 0;
if (p) p.appendChild(n);
return n;
}
async function T(p, x, y, w, h, chars, size, col, weight, align) {
const style = weight || "Regular";
await figma.loadFontAsync({ family: "Inter", style });
const n = figma.createText();
n.fontName = { family: "Inter", style };
n.fontSize = size || 12;
n.characters = safe(chars);
n.fills = [{ type: "SOLID", color: col || C.textPrimary }];
n.textAlignHorizontal = align || "RIGHT";
n.textAlignVertical = "CENTER";
n.textAutoResize = "NONE";
n.x = x; n.y = y;
n.resize(Math.max(w, 20), Math.max(h, 14));
if (p) p.appendChild(n);
return n;
}
function F(p, x, y, w, h, col, a, r) {
const n = figma.createFrame();
n.x = x; n.y = y;
n.resize(Math.max(w, 1), Math.max(h, 1));
n.fills = [{ type: "SOLID", color: col || C.white, opacity: a !== undefined ? a : 1 }];
n.cornerRadius = r || 0;
n.clipsContent = true;
n.strokes = []; n.strokeWeight = 0;
if (p) p.appendChild(n);
return n;
}
// ── Card (elevated) ───────────────────────────────────────────────
function card(p, x, y, w, h, r) {
const n = F(p, x, y, w, h, C.white, 1, r || 20);
n.effects = [{
type: "DROP_SHADOW",
color: { r: 0.200, g: 0.080, b: 0.150, a: 0.08 },
offset: { x: 0, y: 4 }, radius: 24, spread: -4,
visible: true, blendMode: "NORMAL"
}];
n.strokes = [{ type: "SOLID", color: C.borderLight }];
n.strokeWeight = 1; n.strokeAlign = "INSIDE";
return n;
}
// Soft card (lighter shadow)
function softCard(p, x, y, w, h, r) {
const n = F(p, x, y, w, h, C.white, 1, r || 16);
n.effects = [{
type: "DROP_SHADOW",
color: { r: 0.200, g: 0.080, b: 0.150, a: 0.05 },
offset: { x: 0, y: 2 }, radius: 12, spread: 0,
visible: true, blendMode: "NORMAL"
}];
n.strokes = [{ type: "SOLID", color: C.borderLight }];
n.strokeWeight = 1; n.strokeAlign = "INSIDE";
return n;
}
// ── Input field ───────────────────────────────────────────────────
async function inputField(p, x, y, w, label, val) {
await T(p, p.width - x - 180, y - 20, 180, 18, label, 11, C.textMuted, "Medium");
const b = F(p, x, y, w, 48, C.white, 1, 12);
b.strokes = [{ type: "SOLID", color: C.border }];
b.strokeWeight = 1.5; b.strokeAlign = "INSIDE";
await T(b, 16, 0, w - 32, 48, val, 13, C.textPrimary, "Regular", "LEFT");
return b;
}
// Active input (focused blue border replaced with wine)
async function activeInput(p, x, y, w, label, val) {
await T(p, p.width - x - 180, y - 20, 180, 18, label, 11, C.wine, "Medium");
const b = F(p, x, y, w, 48, C.wine50, 1, 12);
b.strokes = [{ type: "SOLID", color: C.wine }];
b.strokeWeight = 2; b.strokeAlign = "INSIDE";
await T(b, 16, 0, w - 32, 48, val, 13, C.textPrimary, "Regular", "LEFT");
return b;
}
// ── Topbar ────────────────────────────────────────────────────────
async function topbar(p, title, sub) {
const tb = F(p, 0, 0, CW, TH, C.white);
tb.effects = [{
type: "DROP_SHADOW",
color: { r: 0.200, g: 0.080, b: 0.150, a: 0.04 },
offset: { x: 0, y: 2 }, radius: 8, spread: 0,
visible: true, blendMode: "NORMAL"
}];
// Right: title + sub
await T(tb, CW - 24, 10, 340, 28, title, 20, C.textPrimary, "Bold");
await T(tb, CW - 24, 40, 280, 18, sub, 12, C.textMuted);
// Left icons area
// Notification bell (circle + dot)
R(tb, 58, 20, 32, 32, C.cream, 1, 16);
R(tb, 62, 24, 24, 24, C.creamDark, 1, 12);
R(tb, 80, 18, 8, 8, C.wine, 1, 4);
// Avatar
R(tb, 16, 16, 36, 36, C.wine100, 1, 18);
await T(tb, 16, 16, 36, 36, "\u0633", 15, C.wine, "Bold", "CENTER");
// Separator
R(tb, 0, TH - 1, CW, 1, C.borderLight);
}
// ── Sidebar ───────────────────────────────────────────────────────
async function sidebar(p, active) {
const labels = [
"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629",
"\u0627\u0644\u0645\u0648\u0627\u0639\u064a\u062f",
"\u0627\u0644\u062e\u062f\u0645\u0627\u062a",
"\u0627\u0644\u0639\u0645\u0644\u0627\u0621",
"\u0627\u0644\u0645\u0648\u0638\u0641\u0648\u0646",
"\u0627\u0644\u0645\u062e\u0632\u0648\u0646",
"\u0627\u0644\u0645\u062a\u062c\u0631",
"\u0627\u0644\u062a\u0641\u0627\u0639\u0644",
"\u0627\u0644\u0633\u062c\u0644 \u0627\u0644\u0635\u062d\u064a",
"\u0627\u0644\u062a\u0642\u0627\u0631\u064a\u0631",
"\u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a",
];
const sb = F(p, FW - SW, 0, SW, FH, C.wineDark);
// Subtle top gradient effect (darker top)
R(sb, 0, 0, SW, 200, C.wineDark, 0.4);
// Decorative circles
R(sb, SW - 80, -40, 120, 120, C.white, 0.03, 60);
R(sb, -30, FH - 130, 100, 100, C.white, 0.03, 50);
// Logo area
const logo = F(sb, 20, 20, SW - 40, 64, C.white, 0.05, 16);
R(logo, 16, 12, 40, 40, C.gold, 1, 12);
// "B" in logo
await T(logo, 16, 12, 40, 40, "B", 20, C.wineDark, "Bold", "CENTER");
await T(logo, 64, 14, SW - 100, 20, "BeautyHub", 15, C.white, "Bold", "LEFT");
await T(logo, 64, 36, SW - 100, 16, "\u0644\u0648\u062d\u0629 \u0625\u062f\u0627\u0631\u0629 \u0627\u0644\u0635\u0627\u0644\u0648\u0646", 10, { r:1,g:1,b:1 }, undefined, undefined, "LEFT");
R(sb, 20, 96, SW - 40, 1, C.white, 0.10);
// Nav items
for (let i = 0; i < labels.length; i++) {
const iy = 108 + i * 58;
const isA = i === active;
if (isA) {
// Active bg
const activeBg = F(sb, 12, iy, SW - 24, 46, C.white, 0.12, 12);
// Gold left indicator
R(sb, SW - 8, iy + 9, 4, 28, C.gold, 1, 2);
}
// Icon block
R(sb, SW - 50, iy + 11, 24, 24, C.white, isA ? 0.20 : 0.07, 8);
// Icon inner dot
R(sb, SW - 44, iy + 17, 12, 12, C.white, isA ? 0.90 : 0.40, 6);
await T(sb, 20, iy + 6, SW - 78, 34, labels[i], 13, C.white, isA ? "Semi Bold" : "Regular", "LEFT");
}
// Divider
R(sb, 20, FH - 110, SW - 40, 1, C.white, 0.10);
// User card
const uc = F(sb, 12, FH - 100, SW - 24, 84, C.white, 0.07, 16);
R(uc, SW - 60, 16, 44, 44, C.gold, 0.20, 22);
await T(uc, SW - 60, 16, 44, 44, "\u0633", 18, C.gold, "Bold", "CENTER");
await T(uc, 16, 16, SW - 90, 20, "\u0635\u0627\u0644\u0648\u0646 \u0633\u0627\u0631\u0629", 13, C.white, "Semi Bold", "LEFT");
await T(uc, 16, 38, SW - 90, 16, "\u0645\u0633\u0624\u0648\u0644", 10, { r:1,g:1,b:1 }, undefined, undefined, "LEFT");
await T(uc, 16, 58, SW - 90, 14, "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062e\u0631\u0648\u062c", 10, { r:1,g:1,b:1 }, undefined, undefined, "LEFT");
}
// ── Stat Card ─────────────────────────────────────────────────────
async function statCard(p, x, y, w, lbl, val, chg, accent, positive) {
const c = card(p, x, y, w, 108, 20);
// Top accent line
R(c, 0, 0, w, 4, accent, 1, 0);
// Icon circle (right side)
R(c, w - 56, 20, 44, 44, accent, 0.10, 22);
R(c, w - 48, 28, 28, 28, accent, 0.20, 14);
// Value
await T(c, 20, 14, w - 80, 32, val, 22, C.textPrimary, "Bold", "LEFT");
// Label
await T(c, 20, 48, w - 80, 20, lbl, 12, C.textMuted, "Regular", "LEFT");
// Change badge
const bCol = positive === false ? C.dangerBg : C.successBg;
const tCol = positive === false ? C.danger : C.success;
R(c, 20, 74, 130, 22, bCol, 1, 11);
await T(c, 20, 74, 130, 22, chg, 10, tCol, "Medium", "LEFT");
}
// ── Card header ───────────────────────────────────────────────────
async function cardHeader(p, w, title, action) {
await T(p, w - 20, 14, w - 130, 28, title, 14, C.textPrimary, "Bold");
if (action) {
R(p, 16, 16, 90, 28, C.wine50, 1, 14);
await T(p, 16, 16, 90, 28, action, 11, C.wine, "Medium", "CENTER");
}
R(p, 16, 56, w - 32, 1, C.borderLight);
}
// ── Table row ─────────────────────────────────────────────────────
async function tableRow(p, y, cells, rh, isHdr) {
if (isHdr) R(p, 0, y, p.width, rh, C.bg, 1, 0);
else if (y % (rh * 2) === rh) R(p, 0, y, p.width, rh, C.white, 1);
R(p, 16, y + rh - 1, p.width - 32, 1, C.borderLight);
const cw = Math.floor((p.width - 32) / cells.length);
for (let i = 0; i < cells.length; i++) {
const cx = p.width - 16 - (i + 1) * cw;
await T(p, cx, y + 2, cw - 8, rh - 4, cells[i], isHdr ? 11 : 12,
isHdr ? C.textMuted : C.textSecond, isHdr ? "Medium" : "Regular");
}
}
// ── Search bar ────────────────────────────────────────────────────
async function searchBar(p, x, y, w, ph) {
const b = F(p, x, y, w, 44, C.bg, 1, 12);
b.strokes = [{ type: "SOLID", color: C.border }];
b.strokeWeight = 1; b.strokeAlign = "INSIDE";
R(b, w - 34, 12, 20, 20, C.textMuted, 0.25, 5);
R(b, w - 30, 16, 12, 12, C.textMuted, 0.20, 6);
await T(b, 12, 0, w - 50, 44, ph, 12, C.textMuted, "Regular", "LEFT");
}
// ── Bar chart ─────────────────────────────────────────────────────
function bars(p, x, y, w, h, vals, col) {
const v = vals || [55, 72, 45, 88, 62, 78, 58];
const bw = Math.floor((w - (v.length - 1) * 6) / v.length);
for (let i = 0; i < v.length; i++) {
const bh = Math.max(Math.floor(v[i] * (h - 28) / 100), 4);
const alpha = 0.45 + i * 0.08;
R(p, x + i * (bw + 6), y + h - 24 - bh, bw, bh, col || C.wine, Math.min(alpha, 0.95), 4);
// Top rounded cap
R(p, x + i * (bw + 6), y + h - 24 - bh, bw, 4, col || C.wine, Math.min(alpha, 0.95), 2);
}
}
// ── Progress bar ─────────────────────────────────────────────────
async function progBar(p, x, y, w, pct, lbl, cnt) {
await T(p, x + w - 200, y, 200, 20, lbl, 12, C.textPrimary, "Medium");
const badge = F(p, x, y + 2, 52, 16, C.wine50, 1, 8);
await T(badge, 4, 0, 44, 16, cnt + " \u062d\u062c\u0632", 10, C.wine, "Medium", "LEFT");
R(p, x, y + 28, w, 6, C.borderLight, 1, 3);
const fw = Math.max(Math.floor(w * pct / 100), 6);
const barF = F(p, x + w - fw, y + 28, fw, 6, C.wine, 1, 3);
}
// ── Tag / Badge ───────────────────────────────────────────────────
async function badge(p, x, y, text, type) {
const cols = { success: [C.successBg, C.success], danger: [C.dangerBg, C.danger], warning: [C.warningBg, C.warning], wine: [C.wine50, C.wine], neutral: [C.cream, C.textMuted] };
const [bg, tx] = cols[type] || cols.neutral;
R(p, x, y, 76, 22, bg, 1, 11);
await T(p, x, y, 76, 22, text, 10, tx, "Medium", "CENTER");
}
// ── Dot indicator ─────────────────────────────────────────────────
function dot(p, x, y, active) {
R(p, x, y, active ? 24 : 8, 8, active ? C.wine : C.creamDark, 1, 4);
}
// ── Page builder ──────────────────────────────────────────────────
async function buildPage(name, xi, navIdx, fn) {
const frame = figma.createFrame();
frame.name = name;
frame.x = xi; frame.y = 0;
frame.resize(FW, FH);
frame.fills = [{ type: "SOLID", color: C.bg }];
frame.clipsContent = true;
const content = F(frame, 0, 0, CW, FH, C.bg);
content.name = "Content";
content.clipsContent = false;
await fn(content);
await sidebar(frame, navIdx);
figma.currentPage.appendChild(frame);
}
// ═══════════════════════════════════════════════════════════════════
// ONBOARDING 1 — مرحبا
// ═══════════════════════════════════════════════════════════════════
async function pOnboard1(xi) {
const frame = figma.createFrame();
frame.name = "\u0646\u0645\u0648\u0630\u062c 01 - \u0645\u0631\u062d\u0628\u0627";
frame.x = xi; frame.y = 0;
frame.resize(FW, FH);
frame.fills = [{ type: "SOLID", color: C.bg }];
frame.clipsContent = true;
// Left panel — brand
const lp = F(frame, 0, 0, 660, FH, C.wineDark);
// Decorative circles
R(lp, -80, -80, 280, 280, C.white, 0.04, 140);
R(lp, 500, 600, 220, 220, C.white, 0.04, 110);
R(lp, 300, 200, 160, 160, C.white, 0.03, 80);
R(lp, 60, 520, 100, 100, C.gold, 0.08, 50);
// Center content
const logoBox = F(lp, 230, 120, 200, 200, C.white, 0.08, 100);
R(lp, 255, 145, 150, 150, C.gold, 0.15, 75);
await T(lp, 230, 120, 200, 200, "BH", 56, C.white, "Bold", "CENTER");
await T(lp, 100, 350, 460, 56, "BeautyHub", 48, C.white, "Bold", "CENTER");
await T(lp, 100, 418, 460, 28, "\u0646\u0638\u0627\u0645 \u0625\u062f\u0627\u0631\u0629 \u0635\u0627\u0644\u0648\u0646\u0627\u062a \u0645\u062a\u0643\u0627\u0645\u0644", 18, { r:1,g:1,b:1 }, "Regular", "CENTER");
// Bottom dots
dot(lp, 285, FH - 60, true);
dot(lp, 317, FH - 58, false);
dot(lp, 333, FH - 58, false);
// Right panel — content
const rp = F(frame, 660, 0, 780, FH, C.white);
await T(rp, 80, 160, 620, 52, "\u0645\u0631\u062d\u0628\u0627\u064b \u0628\u0643 \u0641\u064a BeautyHub", 36, C.textPrimary, "Bold", "RIGHT");
await T(rp, 80, 228, 620, 28, "\u0646\u0638\u0627\u0645 \u0634\u0627\u0645\u0644 \u0644\u0625\u062f\u0627\u0631\u0629 \u0635\u0627\u0644\u0648\u0646\u0643 \u0628\u0627\u062d\u062a\u0631\u0627\u0641\u064a\u0629 \u0648\u0633\u0647\u0648\u0644\u0629", 15, C.textMuted, "Regular", "RIGHT");
// Feature bullets
const feats = [
"\u0625\u062f\u0627\u0631\u0629 \u0627\u0644\u062d\u062c\u0648\u0632\u0627\u062a \u0648\u0627\u0644\u0639\u0645\u0644\u0627\u0621 \u0628\u0633\u0647\u0648\u0644\u0629",
"\u062a\u0642\u0627\u0631\u064a\u0631 \u0648\u062a\u062d\u0644\u064a\u0644\u0627\u062a \u0645\u0627\u0644\u064a\u0629 \u0645\u062a\u0642\u062f\u0645\u0629",
"\u0625\u062f\u0627\u0631\u0629 \u0627\u0644\u0645\u062e\u0632\u0648\u0646 \u0648\u0627\u0644\u0645\u062a\u062c\u0631",
];
for (let i = 0; i < feats.length; i++) {
R(rp, 80, 290 + i * 64, 44, 44, C.wine50, 1, 22);
R(rp, 92, 302, 20, 20, C.wine, 0.3, 10);
await T(rp, 136, 290 + i * 64, 524, 44, feats[i], 14, C.textSecond, "Regular", "LEFT");
R(rp, 80 + 18, 290 + i * 64 + 17, 8, 8, C.wine, 1, 4);
}
// CTA Button
R(rp, 80, 496, 340, 56, C.wine, 1, 16);
await T(rp, 80, 496, 340, 56, "\u0627\u0628\u062f\u0623 \u0627\u0644\u0622\u0646", 16, C.white, "Bold", "CENTER");
R(rp, 436, 496, 200, 56, C.cream, 1, 16);
await T(rp, 436, 496, 200, 56, "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", 14, C.wine, "Medium", "CENTER");
// Bottom note
await T(rp, 80, 576, 620, 20, "\u0647\u0644 \u0644\u062f\u064a\u0643 \u062d\u0633\u0627\u0628 \u0628\u0627\u0644\u0641\u0639\u0644\u061f \u0633\u062c\u0651\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", 13, C.textMuted, "Regular", "RIGHT");
figma.currentPage.appendChild(frame);
}
// ═══════════════════════════════════════════════════════════════════
// ONBOARDING 2 — المزايا
// ═══════════════════════════════════════════════════════════════════
async function pOnboard2(xi) {
const frame = figma.createFrame();
frame.name = "\u0646\u0645\u0648\u0630\u062c 02 - \u0627\u0644\u0645\u0632\u0627\u064a\u0627";
frame.x = xi; frame.y = 0;
frame.resize(FW, FH);
frame.fills = [{ type: "SOLID", color: C.bg }];
frame.clipsContent = true;
// Top header
const hdr = F(frame, 0, 0, FW, 180, C.wineDark);
R(hdr, -60, -60, 200, 200, C.white, 0.03, 100);
R(hdr, FW - 100, 80, 160, 160, C.white, 0.03, 80);
await T(hdr, 60, 60, FW - 120, 44, "\u0644\u0645\u0627\u0630\u0627 BeautyHub \u061f", 32, C.white, "Bold", "RIGHT");
await T(hdr, 60, 116, FW - 120, 24, "\u0643\u0644 \u0645\u0627 \u062a\u062d\u062a\u0627\u062c\u0647 \u0635\u0627\u0644\u0648\u0646\u0643 \u0641\u064a \u0645\u0643\u0627\u0646 \u0648\u0627\u062d\u062f", 15, { r:1,g:1,b:1 }, "Regular", "RIGHT");
// Dots
dot(hdr, FW/2 - 24, 150, false);
dot(hdr, FW/2 - 8, 148, true);
dot(hdr, FW/2 + 20, 150, false);
// Feature cards 3x2
const features = [
{ t: "\u062d\u062c\u0648\u0632\u0627\u062a \u0630\u0643\u064a\u0629", d: "\u0646\u0638\u0627\u0645 \u062d\u062c\u0632 \u0623\u0648\u062a\u0648\u0645\u0627\u062a\u064a\u0643\u064a \u0645\u0639 \u062a\u0630\u0643\u064a\u0631\u0627\u062a SMS", col: C.wine },
{ t: "\u062a\u0642\u0627\u0631\u064a\u0631 \u0645\u062a\u0642\u062f\u0645\u0629", d: "\u062a\u062d\u0644\u064a\u0644\u0627\u062a \u0645\u0627\u0644\u064a\u0629 \u0648\u0625\u062d\u0635\u0627\u0626\u064a\u0629 \u062a\u0641\u0635\u064a\u0644\u064a\u0629", col: C.gold },
{ t: "\u0625\u062f\u0627\u0631\u0629 \u0627\u0644\u0639\u0645\u0644\u0627\u0621", d: "\u062a\u062a\u0628\u0639 \u0633\u062c\u0644\u0627\u062a \u0648\u0628\u064a\u0627\u0646\u0627\u062a \u0639\u0645\u0644\u0627\u0626\u0643 \u0628\u0633\u0647\u0648\u0644\u0629", col: C.success },
{ t: "\u0645\u062e\u0632\u0648\u0646 \u0630\u0643\u064a", d: "\u062a\u062a\u0628\u0639 \u0645\u0648\u0627\u062f\u0643 \u0648\u0627\u062d\u0635\u0644 \u062a\u0646\u0628\u064a\u0647\u0627\u062a \u0646\u0641\u0627\u062f", col: C.wineLight },
{ t: "\u0641\u0631\u064a\u0642 \u0645\u062a\u0643\u0627\u0645\u0644", d: "\u0625\u062f\u0627\u0631\u0629 \u0645\u0648\u0638\u0641\u064a\u0646\u0643 \u0648\u062c\u062f\u0627\u0648\u0644\u0647\u0645", col: C.wine },
{ t: "\u0645\u062a\u062c\u0631 \u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a", d: "\u0628\u064a\u0639 \u0645\u0646\u062a\u062c\u0627\u062a\u0643 \u0648\u062f\u0648\u0631\u0627\u062a \u062a\u062f\u0631\u064a\u0628\u064a\u0629", col: C.goldDark },
];
const cw = Math.floor((FW - 96 - 48) / 3), ch = 190;
for (let i = 0; i < features.length; i++) {
const col = i % 3, row = Math.floor(i / 3);
const fx = 48 + col * (cw + 24), fy = 200 + row * (ch + 20);
const fc = card(frame, fx, fy, cw, ch, 20);
R(fc, 0, 0, cw, 6, features[i].col, 1, 0);
R(fc, cw - 64, 24, 48, 48, features[i].col, 0.10, 24);
R(fc, cw - 56, 32, 32, 32, features[i].col, 0.18, 16);
await T(fc, 24, 24, cw - 84, 32, features[i].t, 15, C.textPrimary, "Bold", "LEFT");
await T(fc, 24, 64, cw - 48, 48, features[i].d, 12, C.textMuted, "Regular", "LEFT");
R(fc, 24, ch - 36, 80, 24, features[i].col, 0.10, 12);
await T(fc, 24, ch - 36, 80, 24, "\u0627\u0643\u062a\u0634\u0641 \u0623\u0643\u062b\u0631", 10, features[i].col, "Medium", "CENTER");
}
// Bottom CTA
R(frame, FW/2 - 200, FH - 80, 400, 52, C.wine, 1, 16);
await T(frame, FW/2 - 200, FH - 80, 400, 52, "\u0627\u0644\u062a\u0627\u0644\u064a", 15, C.white, "Bold", "CENTER");
figma.currentPage.appendChild(frame);
}
// ═══════════════════════════════════════════════════════════════════
// ONBOARDING 3 — ابدأ الآن
// ═══════════════════════════════════════════════════════════════════
async function pOnboard3(xi) {
const frame = figma.createFrame();
frame.name = "\u0646\u0645\u0648\u0630\u062c 03 - \u0627\u0628\u062f\u0623 \u0627\u0644\u0622\u0646";
frame.x = xi; frame.y = 0;
frame.resize(FW, FH);
frame.fills = [{ type: "SOLID", color: C.white }];
frame.clipsContent = true;
// Split: right side wine
const rp = F(frame, FW - 620, 0, 620, FH, C.wineDark);
R(rp, 400, -60, 240, 240, C.white, 0.04, 120);
R(rp, -40, FH - 200, 200, 200, C.white, 0.04, 100);
R(rp, 200, 300, 120, 120, C.gold, 0.07, 60);
await T(rp, 60, 180, 500, 52, "\u062c\u0627\u0647\u0632 \u0644\u0644\u0628\u062f\u0621\u061f", 36, C.white, "Bold", "RIGHT");
await T(rp, 60, 248, 500, 28, "\u0633\u062c\u0651\u0644 \u0635\u0627\u0644\u0648\u0646\u0643 \u0648\u0627\u0628\u062f\u0623 \u0625\u062f\u0627\u0631\u062a\u0647 \u0628\u0634\u0643\u0644 \u0627\u062d\u062a\u0631\u0627\u0641\u064a", 16, { r:1,g:1,b:1 }, "Regular", "RIGHT");
// Stats
const stats = [["500+","\u0635\u0627\u0644\u0648\u0646"], ["50K+","\u0639\u0645\u064a\u0644"], ["98%","\u0631\u0636\u0627"]];
for (let i = 0; i < stats.length; i++) {
R(rp, 60 + i * 186, 320, 160, 80, C.white, 0.07, 16);
await T(rp, 60 + i * 186, 330, 160, 32, stats[i][0], 24, C.gold, "Bold", "CENTER");
await T(rp, 60 + i * 186, 364, 160, 24, stats[i][1], 13, C.white, "Regular", "CENTER");
}
dot(rp, 240, FH - 60, false);
dot(rp, 256, FH - 58, false);
dot(rp, 272, FH - 60, true);
// Left side
await T(frame, 60, 80, 720, 32, "BeautyHub", 28, C.wine, "Bold", "LEFT");
await T(frame, 60, 200, 760, 44, "\u0627\u0628\u062f\u0623 \u0631\u062d\u0644\u062a\u0643 \u0627\u0644\u064a\u0648\u0645", 32, C.textPrimary, "Bold", "LEFT");
await T(frame, 60, 258, 700, 24, "\u0623\u0646\u0634\u0626 \u062d\u0633\u0627\u0628\u0643 \u0627\u0644\u0622\u0646 \u0645\u062c\u0627\u0646\u0627\u064b \u0644\u0645\u062f\u0629 30 \u064a\u0648\u0645\u0627\u064b", 15, C.textMuted, "Regular", "LEFT");
// Plan cards
const plans = [
{ n: "\u0623\u0633\u0627\u0633\u064a", p: "\u0645\u062c\u0627\u0646\u064a", d: "\u0644\u0644\u0635\u0627\u0644\u0648\u0646\u0627\u062a \u0627\u0644\u0635\u063a\u064a\u0631\u0629" },
{ n: "\u0645\u062a\u0642\u062f\u0645", p: "199 \u0631.\u0633 / \u0634\u0647\u0631", d: "\u0644\u0644\u0635\u0627\u0644\u0648\u0646\u0627\u062a \u0627\u0644\u0645\u062a\u0648\u0633\u0637\u0629" },
];
for (let i = 0; i < plans.length; i++) {
const isRec = i === 1;
const pc = isRec ? card(frame, 60 + i * 320, 310, 290, 160, 16) : softCard(frame, 60 + i * 320, 310, 290, 160, 16);
if (isRec) {
R(pc, 0, 0, 290, 160, C.wine, 0.04, 16);
R(pc, pc.width - 100, 16, 84, 26, C.wine, 1, 13);
await T(pc, pc.width - 100, 16, 84, 26, "\u0645\u0648\u0635\u0649 \u0628\u0647", 10, C.white, "Bold", "CENTER");
}
await T(pc, 24, 24, 200, 24, plans[i].n, 16, isRec ? C.wine : C.textPrimary, "Bold", "LEFT");
await T(pc, 24, 52, 200, 28, plans[i].p, 20, isRec ? C.wine : C.textPrimary, "Bold", "LEFT");
await T(pc, 24, 84, 220, 20, plans[i].d, 11, C.textMuted, "Regular", "LEFT");
R(pc, 24, 116, 200, 32, isRec ? C.wine : C.cream, 1, 10);
await T(pc, 24, 116, 200, 32, "\u0627\u062e\u062a\u0631 \u0647\u0630\u0627 \u0627\u0644\u0628\u0644\u0627\u0646", 12, isRec ? C.white : C.wine, "Medium", "CENTER");
}
// CTA
R(frame, 60, 510, 540, 56, C.wine, 1, 16);
await T(frame, 60, 510, 540, 56, "\u0625\u0646\u0634\u0627\u0621 \u062d\u0633\u0627\u0628 \u0645\u062c\u0627\u0646\u064a", 16, C.white, "Bold", "CENTER");
R(frame, 60, 580, 540, 48, C.cream, 1, 16);
await T(frame, 60, 580, 540, 48, "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644 \u0628\u062d\u0633\u0627\u0628 \u0645\u0648\u062c\u0648\u062f", 14, C.wine, "Medium", "CENTER");
figma.currentPage.appendChild(frame);
}
// ═══════════════════════════════════════════════════════════════════
// LOGIN
// ═══════════════════════════════════════════════════════════════════
async function pLogin(xi) {
const frame = figma.createFrame();
frame.name = "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644";
frame.x = xi; frame.y = 0;
frame.resize(FW, FH);
frame.fills = [{ type: "SOLID", color: C.bg }];
frame.clipsContent = true;
// Right panel — brand (wine)
const rp = F(frame, FW - 600, 0, 600, FH, C.wine);
R(rp, 400, -40, 200, 200, C.white, 0.05, 100);
R(rp, -60, FH - 180, 220, 220, C.white, 0.04, 110);
R(rp, 200, 350, 100, 100, C.gold, 0.10, 50);
R(rp, 100, 140, 80, 80, C.white, 0.04, 40);
// Logo
R(rp, 220, 160, 160, 160, C.white, 0.10, 80);
await T(rp, 220, 160, 160, 160, "BH", 52, C.white, "Bold", "CENTER");
await T(rp, 80, 348, 440, 44, "BeautyHub", 36, C.white, "Bold", "CENTER");
await T(rp, 80, 404, 440, 28, "\u0645\u0646\u0635\u0629 \u0625\u062f\u0627\u0631\u0629 \u0635\u0627\u0644\u0648\u0646\u0627\u062a \u0645\u062a\u0643\u0627\u0645\u0644\u0629", 16, { r:1,g:1,b:1 }, "Regular", "CENTER");
// Testimonial
R(rp, 60, FH - 180, 480, 120, C.white, 0.08, 16);
await T(rp, 80, FH - 166, 440, 48, "\u0642\u0645\u062a \u0628\u0632\u064a\u0627\u062f\u0629 \u0625\u064a\u0631\u0627\u062f\u0627\u062a\u064a 40% \u0628\u0639\u062f \u0627\u0633\u062a\u062e\u062f\u0627\u0645 BeautyHub!", 13, C.white, "Regular", "CENTER");
await T(rp, 80, FH - 108, 440, 20, "\u0633\u0627\u0631\u0629 - \u0645\u062f\u064a\u0631\u0629 \u0635\u0627\u0644\u0648\u0646 \u0633\u0627\u0631\u0629", 12, C.goldLight, "Medium", "CENTER");
// Left panel — form
const lp = F(frame, 0, 0, FW - 600, FH, C.white);
// Back link
R(lp, 40, 36, 80, 32, C.cream, 1, 16);
await T(lp, 40, 36, 80, 32, "\u0627\u0644\u0631\u062c\u0648\u0639", 12, C.wine, "Medium", "CENTER");
await T(lp, 80, 120, 680, 44, "\u0645\u0631\u062d\u0628\u0627\u064b \u0628\u0639\u0648\u062f\u062a\u0643", 32, C.textPrimary, "Bold", "RIGHT");
await T(lp, 80, 176, 680, 24, "\u0633\u062c\u0651\u0644 \u062f\u062e\u0648\u0644\u0643 \u0644\u0625\u062f\u0627\u0631\u0629 \u0635\u0627\u0644\u0648\u0646\u0643", 15, C.textMuted, "Regular", "RIGHT");
// Form card
const fc = card(lp, 80, 224, 680, 400, 24);
await T(fc, 40, 28, 600, 24, "\u0627\u0644\u0628\u0631\u064a\u062f \u0627\u0644\u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a", 12, C.textMuted, "Medium");
await activeInput(fc, 40, 56, 600, "", "salon@example.com");
await T(fc, 40, 128, 600, 24, "\u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631", 12, C.textMuted, "Medium");
const pi = await inputField(fc, 40, 156, 600, "", "\u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631");
// password dots
for (let i = 0; i < 8; i++) R(fc, 56 + i * 16, 174, 8, 8, C.textMuted, 0.4, 4);
// Show password icon
R(fc, 56, 164, 20, 20, C.textMuted, 0.2, 5);
// Remember + Forgot
R(fc, 40, 232, 20, 20, C.wine, 0.15, 6);
R(fc, 44, 236, 12, 12, C.wine, 1, 3);
await T(fc, 68, 228, 200, 28, "\u062a\u0630\u0643\u0651\u0631\u0646\u064a", 12, C.textSecond, "Regular", "LEFT");
R(fc, 480, 232, 160, 28, C.wine50, 1, 14);
await T(fc, 480, 232, 160, 28, "\u0646\u0633\u064a\u062a \u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631\u061f", 12, C.wine, "Medium", "CENTER");
// Login button
R(fc, 40, 280, 600, 56, C.wine, 1, 16);
await T(fc, 40, 280, 600, 56, "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", 16, C.white, "Bold", "CENTER");
// Divider
R(fc, 40, 356, 260, 1, C.borderLight);
await T(fc, 290, 344, 60, 24, "\u0623\u0648", 13, C.textMuted, "Regular", "CENTER");
R(fc, 360, 356, 260, 1, C.borderLight);
// Google login
R(fc, 40, 368, 600, 20, C.cream, 1, 14);
await T(lp, 80, 644, 680, 24, "\u0644\u064a\u0633 \u0644\u062f\u064a\u0643 \u062d\u0633\u0627\u0628\u061f \u0633\u062c\u0651\u0644 \u0627\u0644\u0622\u0646", 14, C.textMuted, "Regular", "RIGHT");
R(lp, 480, 640, 280, 40, C.wine50, 1, 12);
await T(lp, 480, 640, 280, 40, "\u0625\u0646\u0634\u0627\u0621 \u062d\u0633\u0627\u0628 \u062c\u062f\u064a\u062f", 13, C.wine, "Medium", "CENTER");
figma.currentPage.appendChild(frame);
}
// ═══════════════════════════════════════════════════════════════════
// REGISTER
// ═══════════════════════════════════════════════════════════════════
async function pRegister(xi) {
const frame = figma.createFrame();
frame.name = "\u0625\u0646\u0634\u0627\u0621 \u062d\u0633\u0627\u0628";
frame.x = xi; frame.y = 0;
frame.resize(FW, FH);
frame.fills = [{ type: "SOLID", color: C.bg }];
frame.clipsContent = true;
// Left panel — brand (gold theme)
const lp = F(frame, 0, 0, 560, FH, C.wineDark);
R(lp, -60, -60, 200, 200, C.white, 0.03, 100);
R(lp, 460, FH - 200, 180, 180, C.white, 0.03, 90);
R(lp, 350, 240, 120, 120, C.gold, 0.08, 60);
// Logo
R(lp, 200, 120, 160, 160, C.white, 0.08, 80);
await T(lp, 200, 120, 160, 160, "BH", 48, C.gold, "Bold", "CENTER");
await T(lp, 60, 308, 440, 40, "BeautyHub", 32, C.white, "Bold", "CENTER");
await T(lp, 60, 360, 440, 24, "\u0627\u0646\u0636\u0645 \u0625\u0644\u0649 \u0623\u0643\u062b\u0631 \u0645\u0646 500 \u0635\u0627\u0644\u0648\u0646", 14, { r:1,g:1,b:1 }, "Regular", "CENTER");
// Benefit list
const bens = ["\u062a\u062c\u0631\u0628\u0629 \u0645\u062c\u0627\u0646\u064a\u0629 30 \u064a\u0648\u0645\u0627\u064b","\u0644\u0627 \u062d\u0627\u062c\u0629 \u0644\u0628\u0637\u0627\u0642\u0629 \u0627\u0626\u062a\u0645\u0627\u0646","\u062f\u0639\u0645 \u0641\u0648\u0631\u064a 24/7"];
for (let i = 0; i < bens.length; i++) {
R(lp, 80, 420 + i * 52, 20, 20, C.gold, 1, 10);
await T(lp, 110, 418 + i * 52, 380, 24, bens[i], 13, C.white, "Regular", "LEFT");
}
// Right panel — form
const rp = F(frame, 560, 0, FW - 560, FH, C.white);
// Back
R(rp, 40, 28, 80, 32, C.cream, 1, 16);
await T(rp, 40, 28, 80, 32, "\u0627\u0644\u0631\u062c\u0648\u0639", 12, C.wine, "Medium", "CENTER");
await T(rp, 60, 80, 780, 36, "\u0625\u0646\u0634\u0627\u0621 \u062d\u0633\u0627\u0628 \u062c\u062f\u064a\u062f", 28, C.textPrimary, "Bold", "RIGHT");
await T(rp, 60, 128, 780, 24, "\u0623\u062f\u062e\u0644 \u0628\u064a\u0627\u0646\u0627\u062a\u0643 \u0644\u0625\u0646\u0634\u0627\u0621 \u062d\u0633\u0627\u0628\u0643", 14, C.textMuted, "Regular", "RIGHT");
// Form two-column
const fw = (FW - 560 - 120 - 20) / 2;
// Row 1: Name + Salon name
await T(rp, 60, 172, fw, 18, "\u0627\u0644\u0627\u0633\u0645 \u0627\u0644\u0643\u0627\u0645\u0644", 11, C.textMuted, "Medium", "LEFT");
const n1 = F(rp, 60, 194, fw, 48, C.bg, 1, 12);
n1.strokes = [{ type: "SOLID", color: C.border }]; n1.strokeWeight = 1.5; n1.strokeAlign = "INSIDE";
await T(n1, 16, 0, fw - 32, 48, "\u0633\u0627\u0631\u0629 \u0623\u062d\u0645\u062f", 13, C.textPrimary, "Regular", "LEFT");
await T(rp, 80 + fw, 172, fw, 18, "\u0627\u0633\u0645 \u0627\u0644\u0635\u0627\u0644\u0648\u0646", 11, C.textMuted, "Medium", "LEFT");
const n2 = F(rp, 80 + fw, 194, fw, 48, C.wine50, 1, 12);
n2.strokes = [{ type: "SOLID", color: C.wine }]; n2.strokeWeight = 2; n2.strokeAlign = "INSIDE";
await T(n2, 16, 0, fw - 32, 48, "\u0635\u0627\u0644\u0648\u0646 \u0633\u0627\u0631\u0629", 13, C.textPrimary, "Regular", "LEFT");
// Row 2: Email + Phone
await T(rp, 60, 262, fw, 18, "\u0627\u0644\u0628\u0631\u064a\u062f \u0627\u0644\u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a", 11, C.textMuted, "Medium", "LEFT");
const n3 = F(rp, 60, 284, fw, 48, C.bg, 1, 12);
n3.strokes = [{ type: "SOLID", color: C.border }]; n3.strokeWeight = 1.5; n3.strokeAlign = "INSIDE";
await T(n3, 16, 0, fw - 32, 48, "salon@example.com", 13, C.textPrimary, "Regular", "LEFT");
await T(rp, 80 + fw, 262, fw, 18, "\u0631\u0642\u0645 \u0627\u0644\u062c\u0648\u0627\u0644", 11, C.textMuted, "Medium", "LEFT");
const n4 = F(rp, 80 + fw, 284, fw, 48, C.bg, 1, 12);
n4.strokes = [{ type: "SOLID", color: C.border }]; n4.strokeWeight = 1.5; n4.strokeAlign = "INSIDE";
await T(n4, 16, 0, fw - 32, 48, "05x xxx xxxx", 13, C.textPrimary, "Regular", "LEFT");
// Row 3: Password + Confirm
await T(rp, 60, 352, fw, 18, "\u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631", 11, C.textMuted, "Medium", "LEFT");
const n5 = F(rp, 60, 374, fw, 48, C.bg, 1, 12);
n5.strokes = [{ type: "SOLID", color: C.border }]; n5.strokeWeight = 1.5; n5.strokeAlign = "INSIDE";
for (let i = 0; i < 8; i++) R(n5, 20 + i * 14, 20, 8, 8, C.textMuted, 0.4, 4);
await T(rp, 80 + fw, 352, fw, 18, "\u062a\u0623\u0643\u064a\u062f \u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631", 11, C.textMuted, "Medium", "LEFT");
const n6 = F(rp, 80 + fw, 374, fw, 48, C.bg, 1, 12);
n6.strokes = [{ type: "SOLID", color: C.border }]; n6.strokeWeight = 1.5; n6.strokeAlign = "INSIDE";
for (let i = 0; i < 8; i++) R(n6, 20 + i * 14, 20, 8, 8, C.textMuted, 0.4, 4);
// City
const totalW = fw * 2 + 20;
await T(rp, 60, 442, totalW, 18, "\u0627\u0644\u0645\u062f\u064a\u0646\u0629", 11, C.textMuted, "Medium", "LEFT");
const n7 = F(rp, 60, 464, totalW, 48, C.bg, 1, 12);
n7.strokes = [{ type: "SOLID", color: C.border }]; n7.strokeWeight = 1.5; n7.strokeAlign = "INSIDE";
await T(n7, 16, 0, totalW - 32, 48, "\u0627\u0644\u0631\u064a\u0627\u0636", 13, C.textPrimary, "Regular", "LEFT");
R(n7, totalW - 40, 16, 16, 16, C.textMuted, 0.2, 4);
// Terms
R(rp, 60, 530, 20, 20, C.wine, 0.15, 6);
R(rp, 64, 534, 12, 12, C.wine, 1, 3);
await T(rp, 88, 526, 660, 28, "\u0623\u0648\u0627\u0641\u0642 \u0639\u0644\u0649 \u0634\u0631\u0648\u0637 \u0627\u0644\u0627\u0633\u062a\u062e\u062f\u0627\u0645 \u0648\u0633\u064a\u0627\u0633\u0629 \u0627\u0644\u062e\u0635\u0648\u0635\u064a\u0629", 12, C.textSecond, "Regular", "LEFT");
// Register button
R(rp, 60, 570, totalW, 56, C.wine, 1, 16);
await T(rp, 60, 570, totalW, 56, "\u0625\u0646\u0634\u0627\u0621 \u0627\u0644\u062d\u0633\u0627\u0628", 16, C.white, "Bold", "CENTER");
await T(rp, 60, 644, totalW, 24, "\u0644\u062f\u064a\u0643 \u062d\u0633\u0627\u0628 \u0628\u0627\u0644\u0641\u0639\u0644\u061f \u0633\u062c\u0651\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", 13, C.textMuted, "Regular", "CENTER");
figma.currentPage.appendChild(frame);
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 01 الرئيسية
// ═══════════════════════════════════════════════════════════════════
async function p01(xi) {
await buildPage("01 - \u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629", xi, 0, async (c) => {
await topbar(c, "\u0644\u0648\u062d\u0629 \u0627\u0644\u062a\u062d\u0643\u0645", "\u0627\u0644\u062b\u0644\u0627\u062b\u0627\u0621\u060c 26 \u0645\u0627\u064a\u0648 2026");
const cx = 24, cy = TH + 20;
// Hero banner
const bn = F(c, cx, cy, CW - 48, 108, C.wine, 1, 20);
R(bn, -40, -40, 180, 180, C.white, 0.04, 90);
R(bn, bn.width - 160, 60, 180, 180, C.white, 0.04, 90);
R(bn, bn.width - 400, -20, 100, 100, C.gold, 0.07, 50);
await T(bn, bn.width - 24, 20, 360, 30, "\u0623\u0647\u0644\u0627\u064b \u0628\u0643\u0650\u060c \u0635\u0627\u0644\u0648\u0646 \u0633\u0627\u0631\u0629", 18, C.white, "Bold");
await T(bn, bn.width - 24, 54, 360, 20, "\u0644\u062f\u064a\u0643 \u0627\u0644\u064a\u0648\u0645 5 \u062d\u062c\u0648\u0632\u0627\u062a \u0648\u062d\u062c\u0632 \u062c\u062f\u064a\u062f \u0641\u064a \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631", 13, { r:1,g:1,b:1 });
R(bn, 24, 36, 140, 36, C.white, 0.12, 18);
await T(bn, 24, 36, 140, 36, "\u0627\u0644\u064a\u0648\u0645: 5 \u062d\u062c\u0648\u0632\u0627\u062a", 12, C.white, "Medium", "CENTER");
R(bn, 176, 36, 156, 36, C.gold, 0.25, 18);
await T(bn, 176, 36, 156, 36, "\u0625\u064a\u0631\u0627\u062f\u0627\u062a: 2,400 \u0631.\u0633", 12, C.white, "Medium", "CENTER");
// Stats row
const sdata = [
["\u0627\u0644\u0625\u064a\u0631\u0627\u062f\u0627\u062a","48,250 \u0631.\u0633","+ 12% \u0647\u0630\u0627 \u0627\u0644\u0634\u0647\u0631", C.wine, true],
["\u0627\u0644\u062d\u062c\u0648\u0632\u0627\u062a","284 \u062d\u062c\u0632","+ 8% \u0647\u0630\u0627 \u0627\u0644\u0634\u0647\u0631", C.wineLight, true],
["\u0639\u0645\u0644\u0627\u0621 \u062c\u062f\u062f","42 \u0639\u0645\u064a\u0644","+ 15% \u0647\u0630\u0627 \u0627\u0644\u0634\u0647\u0631", C.success, true],
["\u0627\u0644\u062a\u0642\u064a\u064a\u0645","4.8 / 5","\u062b\u0627\u0628\u062a \u0647\u0630\u0627 \u0627\u0644\u0634\u0647\u0631", C.gold, true],
];
const sw = Math.floor((CW - 48 - 36) / 4);
for (let i = 0; i < sdata.length; i++) {
await statCard(c, cx + i * (sw + 12), cy + 128, sw, sdata[i][0], sdata[i][1], sdata[i][2], sdata[i][3], sdata[i][4]);
}
// Two columns
const tY = cy + 260, cw2 = Math.floor((CW - 48 - 20) / 2);
// Bookings card
const bc = card(c, cx + cw2 + 20, tY, cw2, 294);
await cardHeader(bc, cw2, "\u0623\u062d\u062f\u062b \u0627\u0644\u062d\u062c\u0648\u0632\u0627\u062a", "\u0639\u0631\u0636 \u0627\u0644\u0643\u0644");
const brows = [
["\u0646\u0648\u0631\u0629 \u0627\u0644\u0639\u062a\u064a\u0628\u064a","\u0635\u0628\u063a \u0634\u0639\u0631","350 \u0631.\u0633","\u0645\u0643\u062a\u0645\u0644"],
["\u0647\u0646\u062f \u0627\u0644\u0634\u0645\u0631\u064a","\u0645\u0627\u0646\u064a\u0643\u064a\u0631","180 \u0631.\u0633","\u0645\u0624\u0643\u062f"],
["\u0631\u064a\u0645 \u0627\u0644\u0642\u062d\u0637\u0627\u0646\u064a","\u062a\u0646\u0638\u064a\u0641 \u0628\u0634\u0631\u0629","220 \u0631.\u0633","\u0627\u0646\u062a\u0638\u0627\u0631"],
["\u0633\u0627\u0631\u0629 \u0627\u0644\u063a\u0627\u0645\u062f\u064a","\u0645\u0633\u0627\u062c","300 \u0631.\u0633","\u0645\u0643\u062a\u0645\u0644"],
["\u0641\u0627\u0637\u0645\u0629 \u0627\u0644\u0632\u0647\u0631\u0627\u0646\u064a","\u0645\u064a\u0643\u0627\u0628","250 \u0631.\u0633","\u0645\u0644\u063a\u064a"],
];
const badgeT = {"\u0645\u0643\u062a\u0645\u0644":"success","\u0645\u0624\u0643\u062f":"wine","\u0627\u0646\u062a\u0638\u0627\u0631":"warning","\u0645\u0644\u063a\u064a":"danger"};
for (let i = 0; i < brows.length; i++) {
const ry = 64 + i * 44;
R(bc, bc.width - 46, ry + 8, 30, 30, C.wine50, 1, 15);
await T(bc, bc.width - 46, ry + 8, 30, 30, brows[i][0].charAt(0), 12, C.wine, "Bold", "CENTER");
await T(bc, bc.width - 86, ry + 6, 170, 18, brows[i][0], 12, C.textPrimary, "Semi Bold");
await T(bc, bc.width - 86, ry + 26, 170, 16, brows[i][1], 10, C.textMuted);
await T(bc, 90, ry + 10, 80, 20, brows[i][2], 12, C.goldDark, "Bold", "LEFT");
await badge(bc, 16, ry + 10, brows[i][3], badgeT[brows[i][3]] || "neutral");
if (i < brows.length - 1) R(bc, 16, ry + 43, cw2 - 32, 1, C.borderLight);
}
// Services card
const sc = card(c, cx, tY, cw2, 294);
await cardHeader(sc, cw2, "\u0627\u0644\u062e\u062f\u0645\u0627\u062a \u0627\u0644\u0623\u0643\u062b\u0631 \u0637\u0644\u0628\u0627\u064b", "");
const srows = [
["\u0635\u0628\u063a \u0627\u0644\u0634\u0639\u0631",46,92],
["\u0642\u0635 \u0627\u0644\u0634\u0639\u0631",38,76],
["\u062a\u0646\u0638\u064a\u0641 \u0628\u0634\u0631\u0629",33,66],
["\u0645\u0627\u0646\u064a\u0643\u064a\u0631",28,56],
["\u0645\u0633\u0627\u062c",22,44],
];
for (let i = 0; i < srows.length; i++) {
await progBar(sc, 20, 68 + i * 42, cw2 - 40, srows[i][2], srows[i][0], srows[i][1]);
}
// Revenue chart
const chY = tY + 310;
const cc = card(c, cx, chY, CW - 48, 200);
await cardHeader(cc, CW - 48, "\u0627\u0644\u0625\u064a\u0631\u0627\u062f\u0627\u062a \u0627\u0644\u0623\u0633\u0628\u0648\u0639\u064a\u0629");
R(cc, 20, 68, CW - 88, 108, C.bg, 1, 8);
bars(cc, 24, 68, CW - 96, 108);
const days = ["\u0633\u0628\u062a","\u0623\u062d\u062f","\u0625\u062b\u0646","\u062b\u0644\u062b","\u0623\u0631\u0628","\u062e\u0645\u0633","\u062c\u0645\u0639"];
const dw = Math.floor((CW - 96) / 7);
for (let i = 0; i < days.length; i++) {
await T(cc, 24 + i * dw, 180, dw, 14, days[i], 9, C.textMuted, "Regular", "CENTER");
}
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 02 المواعيد
// ═══════════════════════════════════════════════════════════════════
async function p02(xi) {
await buildPage("02 - \u0627\u0644\u0645\u0648\u0627\u0639\u064a\u062f", xi, 1, async (c) => {
await topbar(c, "\u0627\u0644\u0645\u0648\u0627\u0639\u064a\u062f", "\u0625\u062f\u0627\u0631\u0629 \u062d\u062c\u0648\u0632\u0627\u062a \u0627\u0644\u0639\u0645\u0644\u0627\u0621");
const cx = 24, cy = TH + 20;
// Filter bar
const fb = softCard(c, cx, cy, CW - 48, 60);
const ftabs = ["\u0627\u0644\u0643\u0644","\u0642\u064a\u062f \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631","\u0645\u0624\u0643\u062f","\u0645\u0643\u062a\u0645\u0644","\u0645\u0644\u063a\u064a"];
for (let i = 0; i < ftabs.length; i++) {
R(fb, fb.width - 16 - (i + 1) * 100, 12, 92, 36, i === 0 ? C.wine : C.bg, 1, 18);
await T(fb, fb.width - 16 - (i + 1) * 100, 12, 92, 36, ftabs[i], 11, i === 0 ? C.white : C.textMuted, "Medium", "CENTER");
}
await searchBar(fb, 16, 8, 240, "\u0627\u0644\u0628\u062d\u062b...");
R(fb, fb.width - 558, 12, 36, 36, C.wine50, 1, 10);
await T(fb, fb.width - 558, 12, 36, 36, "+", 18, C.wine, "Bold", "CENTER");
// Table card
const tc = card(c, cx, cy + 76, CW - 48, FH - TH - cy - 104);
const hdrs = ["\u0627\u0644\u062d\u0627\u0644\u0629","\u0627\u0644\u0645\u0628\u0644\u063a","\u0627\u0644\u0648\u0642\u062a","\u0627\u0644\u062a\u0627\u0631\u064a\u062e","\u0627\u0644\u062e\u062f\u0645\u0629","\u0627\u0644\u0639\u0645\u064a\u0644","#"];
await tableRow(tc, 0, hdrs, 48, true);
const rows = [
["\u0645\u0643\u062a\u0645\u0644","350 \u0631.\u0633","10:00","26/5"," \u0635\u0628\u063a \u0634\u0639\u0631","\u0646\u0648\u0631\u0629 \u0627\u0644\u0639\u062a\u064a\u0628\u064a","001"],
["\u0645\u0624\u0643\u062f","180 \u0631.\u0633","11:30","26/5","\u0645\u0627\u0646\u064a\u0643\u064a\u0631","\u0647\u0646\u062f \u0627\u0644\u0634\u0645\u0631\u064a","002"],
["\u0627\u0646\u062a\u0638\u0627\u0631","220 \u0631.\u0633","13:00","26/5","\u062a\u0646\u0638\u064a\u0641 \u0628\u0634\u0631\u0629","\u0631\u064a\u0645 \u0627\u0644\u0642\u062d\u0637\u0627\u0646\u064a","003"],
["\u0645\u0643\u062a\u0645\u0644","300 \u0631.\u0633","14:30","26/5","\u0645\u0633\u0627\u062c","\u0633\u0627\u0631\u0629 \u0627\u0644\u063a\u0627\u0645\u062f\u064a","004"],
["\u0645\u0644\u063a\u064a","0 \u0631.\u0633","15:00","26/5","\u0645\u064a\u0643\u0627\u0628","\u0641\u0627\u0637\u0645\u0629 \u0627\u0644\u0632\u0647\u0631\u0627\u0646\u064a","005"],
["\u0645\u0624\u0643\u062f","450 \u0631.\u0633","09:00","27/5","\u0635\u0628\u063a + \u062a\u0633\u0631\u064a\u062d\u0629","\u0645\u0646\u0649 \u0627\u0644\u062d\u0631\u0628\u064a","006"],
["\u0627\u0646\u062a\u0638\u0627\u0631","150 \u0631.\u0633","10:30","27/5","\u0642\u0635 \u0634\u0639\u0631","\u0623\u0645\u0644 \u0627\u0644\u0639\u0645\u0631\u064a","007"],
["\u0645\u0643\u062a\u0645\u0644","280 \u0631.\u0633","12:00","27/5","\u0645\u0627\u0646\u064a\u0643\u064a\u0631","\u0644\u0645\u0649 \u0627\u0644\u062d\u0645\u064a\u062f","008"],
["\u0645\u0624\u0643\u062f","320 \u0631.\u0633","14:00","27/5","\u0635\u0628\u063a \u0634\u0639\u0631","\u062f\u0644\u0627\u0644 \u0627\u0644\u0639\u0645\u0631\u064a","009"],
];
for (let i = 0; i < rows.length; i++) {
await tableRow(tc, 48 + i * 48, rows[i], 48, false);
}
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 03 الخدمات
// ═══════════════════════════════════════════════════════════════════
async function p03(xi) {
await buildPage("03 - \u0627\u0644\u062e\u062f\u0645\u0627\u062a", xi, 2, async (c) => {
await topbar(c, "\u0627\u0644\u062e\u062f\u0645\u0627\u062a", "\u0625\u062f\u0627\u0631\u0629 \u062e\u062f\u0645\u0627\u062a \u0627\u0644\u0635\u0627\u0644\u0648\u0646");
const cx = 24, cy = TH + 20;
// Action bar
R(c, cx, cy, 150, 44, C.wine, 1, 12);
await T(c, cx, cy, 150, 44, "+ \u0625\u0636\u0627\u0641\u0629 \u062e\u062f\u0645\u0629", 13, C.white, "Semi Bold", "CENTER");
R(c, cx + 162, cy, 150, 44, C.cream, 1, 12);
await T(c, cx + 162, cy, 150, 44, "\u0625\u062f\u0627\u0631\u0629 \u0627\u0644\u0641\u0626\u0627\u062a", 13, C.wine, "Medium", "CENTER");
const groups = [
{ name:"\u062e\u062f\u0645\u0627\u062a \u0627\u0644\u0634\u0639\u0631", col: C.wine, items:[
{n:"\u0635\u0628\u063a \u0627\u0644\u0634\u0639\u0631",p:"250-500 \u0631.\u0633",d:"90 \u062f\u0642\u064a\u0642\u0629",active:true},
{n:"\u0642\u0635 \u0627\u0644\u0634\u0639\u0631",p:"80-150 \u0631.\u0633",d:"45 \u062f\u0642\u064a\u0642\u0629",active:true},
{n:"\u062a\u0633\u0631\u064a\u062d\u0629",p:"120-200 \u0631.\u0633",d:"60 \u062f\u0642\u064a\u0642\u0629",active:true},
{n:"\u0643\u064a\u0631\u0627\u062a\u064a\u0646",p:"400-700 \u0631.\u0633",d:"120 \u062f\u0642\u064a\u0642\u0629",active:false},
]},
{ name:"\u0627\u0644\u0639\u0646\u0627\u064a\u0629 \u0628\u0627\u0644\u0628\u0634\u0631\u0629", col: C.gold, items:[
{n:"\u062a\u0646\u0638\u064a\u0641 \u0627\u0644\u0628\u0634\u0631\u0629",p:"180-280 \u0631.\u0633",d:"60 \u062f\u0642\u064a\u0642\u0629",active:true},
{n:"\u0645\u0633\u0627\u062c \u0648\u062c\u0647",p:"150-250 \u0631.\u0633",d:"45 \u062f\u0642\u064a\u0642\u0629",active:true},
]},
{ name:"\u0627\u0644\u0623\u0638\u0627\u0641\u0631", col: C.success, items:[
{n:"\u0645\u0627\u0646\u064a\u0643\u064a\u0631",p:"80-150 \u0631.\u0633",d:"45 \u062f\u0642\u064a\u0642\u0629",active:true},
{n:"\u0628\u064a\u062f\u064a\u0643\u064a\u0631",p:"100-180 \u0631.\u0633",d:"60 \u062f\u0642\u064a\u0642\u0629",active:true},
]},
];
let gy = cy + 60;
for (const g of groups) {
const gh = 60 + g.items.length * 60;
const gc = card(c, cx, gy, CW - 48, gh, 20);
// Category header
R(gc, 0, 0, CW - 48, 60, C.bg, 1, 20);
R(gc, 0, 40, CW - 48, 20, C.bg); // remove bottom radius on bg
R(gc, 0, 0, 6, 60, g.col, 1, 0);
await T(gc, gc.width - 20, 10, 240, 28, g.name, 14, C.textPrimary, "Bold");
R(gc, 16, 18, 60, 24, g.col, 0.10, 12);
await T(gc, 16, 18, 60, 24, g.items.length + " \u062e\u062f\u0645\u0629", 11, g.col, "Medium", "CENTER");
R(gc, 0, 60, gc.width, 1, C.borderLight);
for (let i = 0; i < g.items.length; i++) {
const iy = 60 + i * 60;
const item = g.items[i];
// Active toggle
R(gc, 20, iy + 20, 36, 20, item.active ? C.wine : C.creamDark, 1, 10);
R(gc, item.active ? 40 : 22, iy + 23, 14, 14, C.white, 1, 7);
await T(gc, gc.width - 20, iy + 10, 240, 20, item.n, 13, C.textPrimary, "Semi Bold");
await T(gc, gc.width - 20, iy + 34, 200, 16, item.d, 11, C.textMuted);
await T(gc, 80, iy + 16, 160, 22, item.p, 13, C.goldDark, "Bold", "LEFT");
R(gc, 252, iy + 18, 60, 24, C.wine50, 1, 8);
await T(gc, 252, iy + 18, 60, 24, "\u062a\u0639\u062f\u064a\u0644", 11, C.wine, "Medium", "CENTER");
if (i < g.items.length - 1) R(gc, 16, iy + 59, gc.width - 32, 1, C.borderLight);
}
gy += gh + 16;
}
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 04 العملاء
// ═══════════════════════════════════════════════════════════════════
async function p04(xi) {
await buildPage("04 - \u0627\u0644\u0639\u0645\u0644\u0627\u0621", xi, 3, async (c) => {
await topbar(c, "\u0627\u0644\u0639\u0645\u0644\u0627\u0621", "\u0625\u062f\u0627\u0631\u0629 \u0642\u0627\u0639\u062f\u0629 \u0628\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0639\u0645\u0644\u0627\u0621");
const cx = 24, cy = TH + 20;
const sw = Math.floor((CW - 48 - 24) / 3);
const sdata = [
["\u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0639\u0645\u0644\u0627\u0621","428","\u0632\u064a\u0627\u062f\u0629 12 \u0639\u0645\u064a\u0644 \u0647\u0630\u0627 \u0627\u0644\u0634\u0647\u0631", C.wine, true],
["\u0639\u0645\u0644\u0627\u0621 \u0646\u0634\u0637\u0648\u0646","312","\u0646\u0633\u0628\u0629 73% \u0645\u0646 \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a", C.success, true],
["\u0645\u062a\u0648\u0633\u0637 \u0627\u0644\u0625\u0646\u0641\u0627\u0642","580 \u0631.\u0633","\u0632\u064a\u0627\u062f\u0629 5% \u0647\u0630\u0627 \u0627\u0644\u0634\u0647\u0631", C.gold, true],
];
for (let i = 0; i < sdata.length; i++) {
await statCard(c, cx + i * (sw + 12), cy, sw, sdata[i][0], sdata[i][1], sdata[i][2], sdata[i][3], sdata[i][4]);
}
const tc = card(c, cx, cy + 124, CW - 48, FH - TH - cy - 152);
// Toolbar
await searchBar(tc, tc.width - 340, 12, 300, "\u0628\u062d\u062b \u0639\u0646 \u0639\u0645\u064a\u0644...");
R(tc, 16, 12, 110, 36, C.wine, 1, 12);
await T(tc, 16, 12, 110, 36, "+ \u0639\u0645\u064a\u0644 \u062c\u062f\u064a\u062f", 11, C.white, "Medium", "CENTER");
R(tc, 136, 12, 80, 36, C.cream, 1, 12);
await T(tc, 136, 12, 80, 36, "\u062a\u0635\u0641\u064a\u0629", 11, C.wine, "Medium", "CENTER");
R(tc, 0, 60, tc.width, 1, C.borderLight);
const hdrs = ["\u0646\u0642\u0627\u0637 \u0627\u0644\u0648\u0644\u0627\u0621","\u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0625\u0646\u0641\u0627\u0642","\u0639\u062f\u062f \u0627\u0644\u0632\u064a\u0627\u0631\u0627\u062a","\u0627\u0644\u0647\u0627\u062a\u0641","\u0627\u0644\u0639\u0645\u064a\u0644"];
await tableRow(tc, 60, hdrs, 48, true);
const rows = [
["850 \u0646\u0642\u0637\u0629","4,200 \u0631.\u0633","12 \u0632\u064a\u0627\u0631\u0629","05x xxx xxxx","\u0646\u0648\u0631\u0629 \u0627\u0644\u0639\u062a\u064a\u0628\u064a"],
["620 \u0646\u0642\u0637\u0629","3,100 \u0631.\u0633","9 \u0632\u064a\u0627\u0631\u0627\u062a","05x xxx xxxx","\u0647\u0646\u062f \u0627\u0644\u0634\u0645\u0631\u064a"],
["430 \u0646\u0642\u0637\u0629","2,150 \u0631.\u0633","7 \u0632\u064a\u0627\u0631\u0627\u062a","05x xxx xxxx","\u0631\u064a\u0645 \u0627\u0644\u0642\u062d\u0637\u0627\u0646\u064a"],
["380 \u0646\u0642\u0637\u0629","1,900 \u0631.\u0633","6 \u0632\u064a\u0627\u0631\u0627\u062a","05x xxx xxxx","\u0633\u0627\u0631\u0629 \u0627\u0644\u063a\u0627\u0645\u062f\u064a"],
["750 \u0646\u0642\u0637\u0629","3,750 \u0631.\u0633","10 \u0632\u064a\u0627\u0631\u0627\u062a","05x xxx xxxx","\u0645\u0646\u0649 \u0627\u0644\u062d\u0631\u0628\u064a"],
["290 \u0646\u0642\u0637\u0629","1,450 \u0631.\u0633","5 \u0632\u064a\u0627\u0631\u0627\u062a","05x xxx xxxx","\u0641\u0627\u0637\u0645\u0629 \u0627\u0644\u0632\u0647\u0631\u0627\u0646\u064a"],
["510 \u0646\u0642\u0637\u0629","2,550 \u0631.\u0633","8 \u0632\u064a\u0627\u0631\u0627\u062a","05x xxx xxxx","\u0623\u0645\u0644 \u0627\u0644\u0639\u0645\u0631\u064a"],
];
for (let i = 0; i < rows.length; i++) await tableRow(tc, 108 + i * 48, rows[i], 48, false);
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 05 الموظفون
// ═══════════════════════════════════════════════════════════════════
async function p05(xi) {
await buildPage("05 - \u0627\u0644\u0645\u0648\u0638\u0641\u0648\u0646", xi, 4, async (c) => {
await topbar(c, "\u0627\u0644\u0645\u0648\u0638\u0641\u0648\u0646", "\u0625\u062f\u0627\u0631\u0629 \u0641\u0631\u064a\u0642 \u0627\u0644\u0639\u0645\u0644");
const cx = 24, cy = TH + 20;
const tabs = ["\u0623\u0639\u0636\u0627\u0621 \u0627\u0644\u0641\u0631\u064a\u0642","\u0637\u0644\u0628\u0627\u062a \u0627\u0644\u062a\u0648\u0638\u064a\u0641","\u0627\u0644\u062c\u062f\u0627\u0648\u0644"];
for (let i = 0; i < tabs.length; i++) {
R(c, cx + i * (i === 0 ? 0 : i * 160), cy, 152, 40, i === 0 ? C.wine : C.cream, 1, 12);
await T(c, cx + i * 160, cy, 152, 40, tabs[i], 12, i === 0 ? C.white : C.textMuted, "Medium", "CENTER");
}
const staff = [
["\u0633\u0627\u0631\u0629 \u0627\u0644\u0645\u0647\u0646\u062f\u0633","\u0645\u062f\u064a\u0631\u0629 \u0627\u0644\u0635\u0627\u0644\u0648\u0646","4.9","92 \u062d\u062c\u0632", C.wine],
["\u0646\u0648\u0631\u0629 \u0627\u0644\u0639\u0644\u064a","\u0623\u062e\u0635\u0627\u0626\u064a\u0629 \u0634\u0639\u0631","4.8","78 \u062d\u062c\u0632", C.wineLight],
["\u0647\u0646\u062f \u0627\u0644\u062e\u0627\u0644\u062f","\u0623\u062e\u0635\u0627\u0626\u064a\u0629 \u0628\u0634\u0631\u0629","4.7","65 \u062d\u062c\u0632", C.gold],
["\u0631\u064a\u0645 \u0627\u0644\u0641\u0647\u062f","\u0623\u062e\u0635\u0627\u0626\u064a\u0629 \u0623\u0638\u0627\u0641\u0631","4.9","84 \u062d\u062c\u0632", C.success],
["\u0623\u0645\u0644 \u0627\u0644\u0631\u0627\u0634\u062f","\u0645\u0633\u0627\u062c\u064a\u0633\u062a","4.6","55 \u062d\u062c\u0632", C.wine],
["\u0644\u0645\u0649 \u0627\u0644\u0645\u0637\u064a\u0631\u064a","\u0645\u064a\u0643\u0627\u0628 \u0623\u0631\u062a\u064a\u0633\u062a","4.8","70 \u062d\u062c\u0632", C.wineLight],
];
const cw = Math.floor((CW - 48 - 40) / 3);
for (let i = 0; i < staff.length; i++) {
const col = i % 3, row = Math.floor(i / 3);
const sx = cx + col * (cw + 20), sy = cy + 56 + row * 168;
const sc = card(c, sx, sy, cw, 152, 20);
// Top accent
R(sc, 0, 0, cw, 5, staff[i][4], 1, 0);
// Avatar
R(sc, sc.width - 60, 20, 48, 48, staff[i][4], 0.12, 24);
R(sc, sc.width - 56, 24, 40, 40, staff[i][4], 0.18, 20);
await T(sc, sc.width - 60, 20, 48, 48, staff[i][0].charAt(0), 18, staff[i][4], "Bold", "CENTER");
// Info
await T(sc, 20, 20, cw - 88, 22, staff[i][0], 14, C.textPrimary, "Bold", "LEFT");
await T(sc, 20, 44, cw - 88, 18, staff[i][1], 11, C.textMuted, "Regular", "LEFT");
// Rating
R(sc, 20, 72, 100, 24, C.goldLight, 1, 12);
await T(sc, 20, 72, 100, 24, staff[i][2] + " / 5", 12, C.goldDark, "Bold", "CENTER");
// Bookings
await T(sc, 136, 72, 100, 24, staff[i][3], 12, C.textSecond, "Regular", "LEFT");
// Action buttons
R(sc, 20, 108, 80, 28, C.wine50, 1, 10);
await T(sc, 20, 108, 80, 28, "\u0627\u0644\u062c\u062f\u0648\u0644", 10, C.wine, "Medium", "CENTER");
R(sc, 110, 108, 80, 28, C.cream, 1, 10);
await T(sc, 110, 108, 80, 28, "\u0627\u0644\u0645\u0644\u0641", 10, C.textSecond, "Medium", "CENTER");
}
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 06 المخزون
// ═══════════════════════════════════════════════════════════════════
async function p06(xi) {
await buildPage("06 - \u0627\u0644\u0645\u062e\u0632\u0648\u0646", xi, 5, async (c) => {
await topbar(c, "\u0627\u0644\u0645\u062e\u0632\u0648\u0646", "\u0625\u062f\u0627\u0631\u0629 \u0645\u0648\u0627\u062f \u0648\u0623\u062f\u0648\u0627\u062a \u0627\u0644\u0635\u0627\u0644\u0648\u0646");
const cx = 24, cy = TH + 20;
const sw = Math.floor((CW - 48 - 24) / 3);
const sdata = [
["\u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0648\u0627\u062f","124 \u0635\u0646\u0641","\u0645\u062a\u0627\u062d \u0641\u064a \u0627\u0644\u0645\u062e\u0632\u0648\u0646", C.wine, true],
["\u062a\u062d\u062a \u0627\u0644\u062d\u062f \u0627\u0644\u0623\u062f\u0646\u0649","8 \u0623\u0635\u0646\u0627\u0641","\u062a\u062d\u062a\u0627\u062c \u0625\u0639\u0627\u062f\u0629 \u0637\u0644\u0628", C.danger, false],
["\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u062e\u0632\u0648\u0646","28,500 \u0631.\u0633","\u0627\u0644\u062a\u0642\u062f\u064a\u0631 \u0627\u0644\u0643\u0644\u064a", C.gold, true],
];
for (let i = 0; i < sdata.length; i++) {
await statCard(c, cx + i * (sw + 12), cy, sw, sdata[i][0], sdata[i][1], sdata[i][2], sdata[i][3], sdata[i][4]);
}
const tc = card(c, cx, cy + 124, CW - 48, FH - TH - cy - 152);
await searchBar(tc, tc.width - 340, 12, 300, "\u0628\u062d\u062b \u0639\u0646 \u0645\u0627\u062f\u0629...");
R(tc, 16, 12, 110, 36, C.wine, 1, 12);
await T(tc, 16, 12, 110, 36, "+ \u0625\u0636\u0627\u0641\u0629 \u0645\u0627\u062f\u0629", 11, C.white, "Medium", "CENTER");
R(tc, 0, 60, tc.width, 1, C.borderLight);
const hdrs = ["\u0627\u0644\u062d\u0627\u0644\u0629","\u0627\u0644\u062d\u062f \u0627\u0644\u0623\u062f\u0646\u0649","\u0627\u0644\u0643\u0645\u064a\u0629","\u0627\u0644\u0648\u062d\u062f\u0629","\u0627\u0633\u0645 \u0627\u0644\u0645\u0627\u062f\u0629"];
await tableRow(tc, 60, hdrs, 48, true);
const rows = [
["\u062c\u064a\u062f","10","45","\u0639\u0644\u0628\u0629","\u0635\u0628\u063a\u0629 \u0627\u0644\u0634\u0639\u0631"],
["\u062c\u064a\u062f","5","28","\u0643\u064a\u0644\u0648","\u0643\u0631\u064a\u0645 \u0627\u0644\u0634\u0639\u0631"],
["\u0645\u0646\u062e\u0641\u0636","20","6","\u0642\u0637\u0639\u0629","\u0623\u062f\u0648\u0627\u062a \u0627\u0644\u0642\u0635"],
["\u062c\u064a\u062f","15","62","\u0632\u062c\u0627\u062c\u0629","\u0634\u0627\u0645\u0628\u0648"],
["\u062c\u064a\u062f","10","34","\u0639\u0644\u0628\u0629","\u0623\u0635\u0628\u0627\u063a \u0627\u0644\u0623\u0638\u0627\u0641\u0631"],
["\u0645\u0646\u062e\u0641\u0636","8","2","\u0639\u0644\u0628\u0629","\u0648\u0631\u0642 \u0627\u0644\u062a\u0644\u0648\u064a\u0646"],
["\u062c\u064a\u062f","5","20","\u0643\u064a\u0644\u0648","\u0642\u0646\u0627\u0639 \u0627\u0644\u0648\u062c\u0647"],
];
for (let i = 0; i < rows.length; i++) {
await tableRow(tc, 108 + i * 48, rows[i], 48, false);
if (rows[i][0] === "\u0645\u0646\u062e\u0641\u0636") {
R(tc, 0, 108 + i * 48, tc.width, 47, C.danger, 0.04);
R(tc, 0, 108 + i * 48, 3, 47, C.danger, 0.8, 0);
}
}
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 07 المتجر
// ═══════════════════════════════════════════════════════════════════
async function p07(xi) {
await buildPage("07 - \u0627\u0644\u0645\u062a\u062c\u0631", xi, 6, async (c) => {
await topbar(c, "\u0627\u0644\u0645\u062a\u062c\u0631", "\u0625\u062f\u0627\u0631\u0629 \u0627\u0644\u0645\u0646\u062a\u062c\u0627\u062a \u0648\u0627\u0644\u0637\u0644\u0628\u0627\u062a");
const cx = 24, cy = TH + 20;
const tabs = ["\u0627\u0644\u0645\u0646\u062a\u062c\u0627\u062a","\u0627\u0644\u0637\u0644\u0628\u0627\u062a","\u0627\u0644\u062f\u0648\u0631\u0627\u062a \u0627\u0644\u062a\u062f\u0631\u064a\u0628\u064a\u0629"];
for (let i = 0; i < tabs.length; i++) {
R(c, cx + i * 170, cy, 162, 40, i === 0 ? C.wine : C.cream, 1, 12);
await T(c, cx + i * 170, cy, 162, 40, tabs[i], 12, i === 0 ? C.white : C.textMuted, "Medium", "CENTER");
}
const prods = [
{n:"\u0634\u0627\u0645\u0628\u0648 \u0645\u0631\u0637\u0628",p:"85 \u0631.\u0633",q:"45",on:true,col:C.wine},
{n:"\u0643\u0631\u064a\u0645 \u0627\u0644\u0634\u0639\u0631",p:"120 \u0631.\u0633",q:"32",on:true,col:C.gold},
{n:"\u0635\u0628\u063a\u0629 \u0637\u0628\u064a\u0639\u064a\u0629",p:"95 \u0631.\u0633",q:"28",on:false,col:C.wineLight},
{n:"\u0642\u0646\u0627\u0639 \u0627\u0644\u0639\u0633\u0644",p:"150 \u0631.\u0633",q:"18",on:true,col:C.success},
{n:"\u0632\u064a\u062a \u0627\u0644\u0623\u0631\u063a\u0627\u0646",p:"200 \u0631.\u0633",q:"12",on:true,col:C.wine},
{n:"\u0643\u0631\u064a\u0645 \u0627\u0644\u0648\u062c\u0647",p:"180 \u0631.\u0633",q:"0",on:false,col:C.textMuted},
];
const pw = Math.floor((CW - 48 - 50) / 3);
for (let i = 0; i < prods.length; i++) {
const col = i % 3, row = Math.floor(i / 3);
const px = cx + col * (pw + 25), py = cy + 60 + row * 196;
const pc = card(c, px, py, pw, 180, 20);
// Product image area
R(pc, 0, 0, pw, 100, prods[i].on ? C.wine50 : C.cream, 1, 20);
R(pc, 0, 80, pw, 20, prods[i].on ? C.wine50 : C.cream, 1); // bottom fill
R(pc, pw/2 - 30, 20, 60, 60, prods[i].col, 0.15, 20);
R(pc, pw/2 - 20, 30, 40, 40, prods[i].col, 0.25, 12);
// Out of stock badge
if (prods[i].q === "0") {
R(pc, pw - 80, 12, 64, 22, C.dangerBg, 1, 11);
await T(pc, pw - 80, 12, 64, 22, "\u0646\u0641\u062f", 9, C.danger, "Bold", "CENTER");
} else if (prods[i].on) {
R(pc, pw - 72, 12, 56, 22, C.successBg, 1, 11);
await T(pc, pw - 72, 12, 56, 22, "\u0645\u062a\u0627\u062d", 9, C.success, "Bold", "CENTER");
}
await T(pc, pw - 20, 108, pw - 40, 20, prods[i].n, 13, C.textPrimary, "Semi Bold");
await T(pc, 20, 108, 100, 20, prods[i].p, 14, C.goldDark, "Bold", "LEFT");
await T(pc, 20, 132, 120, 16, prods[i].q + " \u0642\u0637\u0639\u0629", 10, C.textMuted, "Regular", "LEFT");
R(pc, 20, 152, 80, 24, C.wine50, 1, 8);
await T(pc, 20, 152, 80, 24, "\u062a\u0639\u062f\u064a\u0644", 10, C.wine, "Medium", "CENTER");
// Toggle
R(pc, pw - 66, 152, 44, 24, prods[i].on ? C.wine : C.creamDark, 1, 12);
R(pc, prods[i].on ? pw - 34 : pw - 64, 155, 18, 18, C.white, 1, 9);
}
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 08 التفاعل
// ═══════════════════════════════════════════════════════════════════
async function p08(xi) {
await buildPage("08 - \u0627\u0644\u062a\u0641\u0627\u0639\u0644", xi, 7, async (c) => {
await topbar(c, "\u0627\u0644\u062a\u0641\u0627\u0639\u0644", "\u0625\u062f\u0627\u0631\u0629 \u0627\u0644\u0645\u062a\u0627\u0628\u0639\u064a\u0646 \u0648\u0627\u0644\u062a\u0642\u064a\u064a\u0645\u0627\u062a");
const cx = 24, cy = TH + 20;
const tabs = ["\u0627\u0644\u0645\u062a\u0627\u0628\u0639\u0648\u0646","\u0627\u0644\u062a\u0642\u064a\u064a\u0645\u0627\u062a","\u0627\u0644\u0645\u062d\u062a\u0648\u0649","\u0627\u0644\u0634\u0643\u0627\u0648\u0649"];
for (let i = 0; i < tabs.length; i++) {
R(c, cx + i * 152, cy, 144, 40, i === 1 ? C.wine : C.cream, 1, 12);
await T(c, cx + i * 152, cy, 144, 40, tabs[i], 12, i === 1 ? C.white : C.textMuted, "Medium", "CENTER");
}
const sw = Math.floor((CW - 48 - 24) / 3);
const sdata = [
["\u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u062a\u0642\u064a\u064a\u0645\u0627\u062a","284 \u062a\u0642\u064a\u064a\u0645","\u0632\u064a\u0627\u062f\u0629 18 \u0647\u0630\u0627 \u0627\u0644\u0634\u0647\u0631", C.wine, true],
["\u0627\u0644\u0645\u062a\u0648\u0633\u0637 \u0627\u0644\u0639\u0627\u0645","4.8 / 5","\u062a\u0642\u064a\u064a\u0645 \u0645\u0645\u062a\u0627\u0632", C.gold, true],
["5 \u0646\u062c\u0648\u0645","68% \u0645\u0646 \u0627\u0644\u062a\u0642\u064a\u064a\u0645\u0627\u062a","\u0627\u0644\u0641\u0626\u0629 \u0627\u0644\u0623\u0639\u0644\u0649", C.success, true],
];
for (let i = 0; i < sdata.length; i++) {
await statCard(c, cx + i * (sw + 12), cy + 56, sw, sdata[i][0], sdata[i][1], sdata[i][2], sdata[i][3], sdata[i][4]);
}
const rc = card(c, cx, cy + 180, CW - 48, FH - TH - cy - 210);
await cardHeader(rc, CW - 48, "\u0623\u062d\u062f\u062b \u0627\u0644\u062a\u0642\u064a\u064a\u0645\u0627\u062a", "\u0631\u062f \u0639\u0644\u0649 \u0627\u0644\u0643\u0644");
const revs = [
["\u0646\u0648\u0631\u0629 \u0627\u0644\u0639\u062a\u064a\u0628\u064a","5 / 5","\u062e\u062f\u0645\u0629 \u0645\u0645\u062a\u0627\u0632\u0629 \u0648\u0641\u0631\u064a\u0642 \u0645\u062d\u062a\u0631\u0641 \u062c\u062f\u0627\u064b","26 \u0645\u0627\u064a\u0648"],
["\u0647\u0646\u062f \u0627\u0644\u0634\u0645\u0631\u064a","4 / 5","\u0631\u0627\u0636\u064a\u0629 \u0639\u0646 \u0627\u0644\u062e\u062f\u0645\u0629 \u0628\u0634\u0643\u0644 \u0639\u0627\u0645","25 \u0645\u0627\u064a\u0648"],
["\u0631\u064a\u0645 \u0627\u0644\u0642\u062d\u0637\u0627\u0646\u064a","5 / 5","\u0623\u0641\u0636\u0644 \u0635\u0627\u0644\u0648\u0646 \u062a\u0639\u0627\u0645\u0644\u062a \u0645\u0639\u0647","24 \u0645\u0627\u064a\u0648"],
["\u0633\u0627\u0631\u0629 \u0627\u0644\u063a\u0627\u0645\u062f\u064a","5 / 5","\u0646\u062a\u064a\u062c\u0629 \u0631\u0627\u0626\u0639\u0629 \u0648\u0633\u0639\u0631 \u0645\u0646\u0627\u0633\u0628","23 \u0645\u0627\u064a\u0648"],
];
for (let i = 0; i < revs.length; i++) {
const ry = 68 + i * 82;
R(rc, rc.width - 60, ry + 8, 44, 44, C.wine50, 1, 22);
await T(rc, rc.width - 60, ry + 8, 44, 44, revs[i][0].charAt(0), 16, C.wine, "Bold", "CENTER");
await T(rc, rc.width - 116, ry + 10, 200, 22, revs[i][0], 13, C.textPrimary, "Bold");
// Star rating
R(rc, rc.width - 116, ry + 34, 80, 22, C.goldLight, 1, 11);
await T(rc, rc.width - 116, ry + 34, 80, 22, revs[i][1], 11, C.goldDark, "Bold", "CENTER");
await T(rc, 80, ry + 16, rc.width - 230, 36, revs[i][2], 12, C.textMuted, "Regular", "LEFT");
await T(rc, 20, ry + 20, 60, 18, revs[i][3], 10, C.textMuted, "Regular", "LEFT");
R(rc, 20, ry + 44, 70, 22, C.wine50, 1, 11);
await T(rc, 20, ry + 44, 70, 22, "\u0631\u062f", 10, C.wine, "Medium", "CENTER");
if (i < revs.length - 1) R(rc, 16, ry + 81, rc.width - 32, 1, C.borderLight);
}
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 09 السجل الصحي
// ═══════════════════════════════════════════════════════════════════
async function p09(xi) {
await buildPage("09 - \u0627\u0644\u0633\u062c\u0644 \u0627\u0644\u0635\u062d\u064a", xi, 8, async (c) => {
await topbar(c, "\u0627\u0644\u0633\u062c\u0644 \u0627\u0644\u0635\u062d\u064a", "\u0628\u064a\u0627\u0646\u0627\u062a \u0635\u062d\u064a\u0629 \u0633\u0631\u064a\u0629 \u0644\u0644\u0639\u0645\u0644\u0627\u0621");
const cx = 24, cy = TH + 20;
// Privacy banner
const pb = F(c, cx, cy, CW - 48, 48, C.warningBg, 1, 12);
R(pb, pb.width - 40, 12, 24, 24, C.warning, 0.3, 12);
await T(pb, 16, 0, pb.width - 60, 48, "\u062a\u0646\u0628\u064a\u0647: \u0647\u0630\u0647 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0633\u0631\u064a\u0629 - \u0644\u0627 \u064a\u062c\u0648\u0632 \u0627\u0644\u0627\u0637\u0644\u0627\u0639 \u0625\u0644\u0627 \u0628\u0645\u0648\u0627\u0641\u0642\u0629 \u0627\u0644\u0639\u0645\u064a\u0644", 12, C.warning, "Medium", "LEFT");
const lw = 300, dw = CW - 48 - lw - 16, spY = cy + 64, spH = FH - TH - cy - 92;
// Client list
const lc = card(c, cx + dw + 16, spY, lw, spH, 16);
await searchBar(lc, 12, 12, lw - 24, "\u0628\u062d\u062b...");
R(lc, 0, 64, lw, 1, C.borderLight);
const names = ["\u0646\u0648\u0631\u0629 \u0627\u0644\u0639\u062a\u064a\u0628\u064a","\u0647\u0646\u062f \u0627\u0644\u0634\u0645\u0631\u064a","\u0631\u064a\u0645 \u0627\u0644\u0642\u062d\u0637\u0627\u0646\u064a","\u0633\u0627\u0631\u0629 \u0627\u0644\u063a\u0627\u0645\u062f\u064a","\u0641\u0627\u0637\u0645\u0629 \u0627\u0644\u0632\u0647\u0631\u0627\u0646\u064a"];
for (let i = 0; i < names.length; i++) {
R(lc, 8, 72 + i * 60, lw - 16, 52, i === 0 ? C.wine50 : C.white, 1, 10);
if (i === 0) R(lc, 8, 72, 4, 52, C.wine, 1, 2);
R(lc, lw - 52, 72 + i * 60 + 10, 36, 36, i === 0 ? C.wine100 : C.cream, 1, 18);
await T(lc, lw - 52, 72 + i * 60 + 10, 36, 36, names[i].charAt(0), 14, i === 0 ? C.wine : C.textMuted, "Bold", "CENTER");
await T(lc, 12, 72 + i * 60 + 8, lw - 76, 20, names[i], 12, i === 0 ? C.wine : C.textPrimary, i === 0 ? "Semi Bold" : "Regular", "LEFT");
await T(lc, 12, 72 + i * 60 + 30, 100, 14, i < 3 ? "\u0645\u0648\u0627\u0641\u0642" : "\u063a\u064a\u0631 \u0645\u0648\u0627\u0641\u0642", 10, i < 3 ? C.success : C.danger, "Regular", "LEFT");
}
// Detail panel
const dc = card(c, cx, spY, dw, spH, 16);
R(dc, 0, 0, dw, 72, C.bg, 1, 16);
R(dc, 0, 52, dw, 20, C.bg, 1);
await T(dc, dc.width - 20, 16, 280, 28, "\u0646\u0648\u0631\u0629 \u0627\u0644\u0639\u062a\u064a\u0628\u064a", 18, C.textPrimary, "Bold");
await T(dc, dc.width - 20, 46, 220, 20, "\u0639\u0645\u064a\u0644\u0629 \u0645\u0646\u0630 \u064a\u0646\u0627\u064a\u0631 2024 - 12 \u0632\u064a\u0627\u0631\u0629", 11, C.textMuted);
R(dc, 0, 72, dc.width, 1, C.borderLight);
// Sections
let sy = 84;
const sections = [
["\u0627\u0644\u062d\u0633\u0627\u0633\u064a\u0627\u062a",["\u062d\u0633\u0627\u0633\u064a\u0629 \u0645\u0646 \u0627\u0644\u0628\u0646\u0633\u0644\u064a\u0646","\u062d\u0633\u0627\u0633\u064a\u0629 \u0645\u0646 \u0627\u0644\u0645\u0643\u0633\u0631\u0627\u062a"]],
["\u0627\u0644\u0623\u0645\u0631\u0627\u0636 \u0627\u0644\u0645\u0632\u0645\u0646\u0629",["\u0636\u063a\u0637 \u0627\u0644\u062f\u0645 \u0627\u0644\u0645\u0631\u062a\u0641\u0639","\u0644\u0627 \u064a\u0648\u062c\u062f \u0633\u0643\u0631\u064a"]],
];
for (const sec of sections) {
R(dc, dc.width - 20, sy, 140, 28, C.wine50, 1, 14);
await T(dc, dc.width - 20, sy, 140, 28, sec[0], 12, C.wine, "Semi Bold");
for (let i = 0; i < sec[1].length; i++) {
R(dc, 20, sy + 38 + i * 36, 10, 10, C.gold, 1, 5);
await T(dc, 36, sy + 34 + i * 36, dc.width - 60, 20, sec[1][i], 12, C.textPrimary, "Regular", "LEFT");
}
sy += 40 + sec[1].length * 36 + 20;
}
// Visit history timeline
R(dc, dc.width - 20, sy, 160, 28, C.wine50, 1, 14);
await T(dc, dc.width - 20, sy, 160, 28, "\u0633\u062c\u0644 \u0627\u0644\u0632\u064a\u0627\u0631\u0627\u062a", 12, C.wine, "Semi Bold");
const visits = [
["26 \u0645\u0627\u064a\u0648 2026","\u0635\u0628\u063a \u0634\u0639\u0631","350 \u0631.\u0633"],
["10 \u0645\u0627\u064a\u0648 2026","\u0642\u0635 \u0648\u062a\u0633\u0631\u064a\u062d\u0629","180 \u0631.\u0633"],
["28 \u0623\u0628\u0631\u064a\u0644 2026","\u0643\u064a\u0631\u0627\u062a\u064a\u0646","500 \u0631.\u0633"],
];
for (let i = 0; i < visits.length; i++) {
R(dc, dc.width - 22, sy + 46 + i * 48, 12, 12, i === 0 ? C.wine : C.borderLight, 1, 6);
if (i < visits.length - 1) R(dc, dc.width - 17, sy + 58 + i * 48, 2, 36, C.borderLight);
const row = F(dc, 20, sy + 40 + i * 48, dc.width - 50, 40, C.bg, 1, 10);
await T(row, row.width - 16, 0, 180, 20, visits[i][0], 11, C.textMuted, "Regular");
await T(row, row.width - 16, 20, 180, 16, visits[i][1], 11, C.textPrimary, "Medium");
await T(row, 16, 10, 100, 20, visits[i][2], 12, C.goldDark, "Bold", "LEFT");
}
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 10 التقارير
// ═══════════════════════════════════════════════════════════════════
async function p10(xi) {
await buildPage("10 - \u0627\u0644\u062a\u0642\u0627\u0631\u064a\u0631", xi, 9, async (c) => {
await topbar(c, "\u0627\u0644\u062a\u0642\u0627\u0631\u064a\u0631", "\u062a\u062d\u0644\u064a\u0644\u0627\u062a \u0648\u0625\u062d\u0635\u0627\u0621\u0627\u062a \u0627\u0644\u0623\u062f\u0627\u0621");
const cx = 24, cy = TH + 20;
const sw = Math.floor((CW - 48 - 36) / 4);
const sdata = [
["\u0627\u0644\u0625\u064a\u0631\u0627\u062f\u0627\u062a","48,250 \u0631.\u0633","+ 12%", C.wine, true],
["\u0627\u0644\u0645\u0635\u0631\u0648\u0641\u0627\u062a","18,800 \u0631.\u0633","- 3%", C.danger, false],
["\u0635\u0627\u0641\u064a \u0627\u0644\u0631\u0628\u062d","29,450 \u0631.\u0633","+ 18%", C.success, true],
["\u0645\u0639\u062f\u0644 \u0627\u0644\u0646\u0645\u0648","15.4%","\u0647\u0630\u0627 \u0627\u0644\u0634\u0647\u0631", C.gold, true],
];
for (let i = 0; i < sdata.length; i++) {
await statCard(c, cx + i * (sw + 12), cy, sw, sdata[i][0], sdata[i][1], sdata[i][2], sdata[i][3], sdata[i][4]);
}
const cw = Math.floor((CW - 48 - 20) / 2), chY = cy + 124;
// Revenue chart
const rcc = card(c, cx + cw + 20, chY, cw, 276, 20);
await cardHeader(rcc, cw, "\u0627\u0644\u0625\u064a\u0631\u0627\u062f\u0627\u062a \u0627\u0644\u0634\u0647\u0631\u064a\u0629");
R(rcc, 20, 68, cw - 40, 168, C.bg, 1, 8);
bars(rcc, 24, 68, cw - 48, 168, [60,75,45,88,70,82,65]);
const months = ["\u064a\u0646\u0627","\u0641\u0628\u0631","\u0645\u0627\u0631","\u0623\u0628\u0631","\u0645\u0627\u064a","\u064a\u0648\u0646","\u064a\u0648\u0644"];
for (let i = 0; i < months.length; i++) {
await T(rcc, 24 + i * Math.floor((cw - 48) / 7), 242, 36, 14, months[i], 9, C.textMuted, "Regular", "CENTER");
}
// Donut chart
const pcc = card(c, cx, chY, cw, 276, 20);
await cardHeader(pcc, cw, "\u062a\u0648\u0632\u064a\u0639 \u0627\u0644\u062e\u062f\u0645\u0627\u062a");
const pieC = [C.wine, C.gold, C.success, C.wineLight, { r:0.65,g:0.80,b:0.68 }];
const pieL = ["\u0634\u0639\u0631 40%","\u0628\u0634\u0631\u0629 25%","\u0623\u0638\u0627\u0641\u0631 20%","\u0645\u0633\u0627\u062c 10%","\u0623\u062e\u0631\u0649 5%"];
for (let i = 0; i < pieL.length; i++) {
R(pcc, 24, 72 + i * 32, 14, 14, pieC[i], 1, 7);
await T(pcc, 44, 70 + i * 32, 140, 18, pieL[i], 12, C.textPrimary, "Regular", "LEFT");
}
R(pcc, pcc.width - 176, 72, 144, 144, C.wine, 1, 72);
R(pcc, pcc.width - 140, 108, 72, 72, C.white, 1, 36);
await T(pcc, pcc.width - 140, 108, 72, 72, "284", 16, C.wine, "Bold", "CENTER");
// Staff performance
const spc = card(c, cx, chY + 292, CW - 48, 192, 20);
await cardHeader(spc, CW - 48, "\u0623\u062f\u0627\u0621 \u0627\u0644\u0645\u0648\u0638\u0641\u064a\u0646", "\u0639\u0631\u0636 \u0627\u0644\u0643\u0644");
const ph = ["\u0627\u0644\u062a\u0642\u064a\u064a\u0645","\u0627\u0644\u0625\u064a\u0631\u0627\u062f","\u0639\u062f\u062f \u0627\u0644\u062d\u062c\u0648\u0632\u0627\u062a","\u0627\u0644\u0645\u0648\u0638\u0641"];
await tableRow(spc, 60, ph, 44, true);
const perf = [
["4.9 / 5","18,500 \u0631.\u0633","92 \u062d\u062c\u0632","\u0633\u0627\u0631\u0629 \u0627\u0644\u0645\u0647\u0646\u062f\u0633"],
["4.8 / 5","15,200 \u0631.\u0633","78 \u062d\u062c\u0632","\u0646\u0648\u0631\u0629 \u0627\u0644\u0639\u0644\u064a"],
["4.7 / 5","12,800 \u0631.\u0633","65 \u062d\u062c\u0632","\u0647\u0646\u062f \u0627\u0644\u062e\u0627\u0644\u062f"],
];
for (let i = 0; i < perf.length; i++) await tableRow(spc, 104 + i * 40, perf[i], 40, false);
});
}
// ═══════════════════════════════════════════════════════════════════
// PAGE 11 الإعدادات
// ═══════════════════════════════════════════════════════════════════
async function p11(xi) {
await buildPage("11 - \u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a", xi, 10, async (c) => {
await topbar(c, "\u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a", "\u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0648\u062a\u062e\u0635\u064a\u0635\u0627\u062a \u0627\u0644\u0635\u0627\u0644\u0648\u0646");
const cx = 24, cy = TH + 20;
const cw = Math.floor((CW - 48 - 20) / 2);
// Salon info
const ic = card(c, cx + cw + 20, cy, cw, 316, 20);
R(ic, 0, 0, cw, 52, C.bg, 1, 20);
R(ic, 0, 32, cw, 20, C.bg, 1);
await T(ic, ic.width - 20, 12, 200, 28, "\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0627\u0644\u0635\u0627\u0644\u0648\u0646", 14, C.wine, "Bold");
R(ic, 0, 52, ic.width, 1, C.borderLight);
const fields = [
["\u0627\u0633\u0645 \u0627\u0644\u0635\u0627\u0644\u0648\u0646","\u0635\u0627\u0644\u0648\u0646 \u0633\u0627\u0631\u0629"],
["\u0631\u0642\u0645 \u0627\u0644\u062c\u0648\u0627\u0644","05x xxx xxxx"],
["\u0627\u0644\u0639\u0646\u0648\u0627\u0646","\u0627\u0644\u0631\u064a\u0627\u0636\u060c \u062d\u064a \u0627\u0644\u0646\u0632\u0647\u0629"],
["\u0627\u0644\u0628\u0631\u064a\u062f","sarah@salon.com"],
];
for (let i = 0; i < fields.length; i++) {
await T(ic, ic.width - 20, 60 + i * 62, ic.width - 40, 16, fields[i][0], 10, C.textMuted, "Regular");
const fi = F(ic, 16, 78 + i * 62, ic.width - 32, 40, C.bg, 1, 10);
fi.strokes = [{ type: "SOLID", color: C.borderLight }]; fi.strokeWeight = 1; fi.strokeAlign = "INSIDE";
await T(fi, 12, 0, fi.width - 24, 40, fields[i][1], 13, C.textPrimary, "Regular", "LEFT");
}
R(ic, 16, ic.height - 52, ic.width - 32, 40, C.wine, 1, 12);
await T(ic, 16, ic.height - 52, ic.width - 32, 40, "\u062d\u0641\u0638 \u0627\u0644\u0645\u0639\u0644\u0648\u0645\u0627\u062a", 13, C.white, "Medium", "CENTER");
// Hours
const hc = card(c, cx, cy, cw, 316, 20);
R(hc, 0, 0, cw, 52, C.bg, 1, 20);
R(hc, 0, 32, cw, 20, C.bg, 1);
await T(hc, hc.width - 20, 12, 200, 28, "\u0633\u0627\u0639\u0627\u062a \u0627\u0644\u0639\u0645\u0644", 14, C.wine, "Bold");
R(hc, 0, 52, hc.width, 1, C.borderLight);
const wdays = [
["\u0627\u0644\u0623\u062d\u062f - \u0627\u0644\u062e\u0645\u064a\u0633","9:00 - 22:00"],
["\u0627\u0644\u062c\u0645\u0639\u0629","14:00 - 22:00"],
["\u0627\u0644\u0633\u0628\u062a","9:00 - 23:00"],
];
for (let i = 0; i < wdays.length; i++) {
R(hc, 16, 60 + i * 82, hc.width - 32, 68, C.bg, 1, 12);
await T(hc, hc.width - 36, 74 + i * 82, cw - 60, 20, wdays[i][0], 12, C.textPrimary, "Medium");
await T(hc, 32, 74 + i * 82, 160, 20, wdays[i][1], 13, C.goldDark, "Bold", "LEFT");
R(hc, hc.width - 40, 68 + i * 82, 24, 24, C.wine50, 1, 8);
await T(hc, hc.width - 40, 68 + i * 82, 24, 24, "\u062a", 10, C.wine, "Bold", "CENTER");
}
// Notifications
const nc = card(c, cx, cy + 332, CW - 48, 204, 20);
R(nc, 0, 0, CW - 48, 52, C.bg, 1, 20);
R(nc, 0, 32, CW - 48, 20, C.bg, 1);
await T(nc, nc.width - 20, 12, 240, 28, "\u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u0625\u0634\u0639\u0627\u0631\u0627\u062a", 14, C.wine, "Bold");
R(nc, 0, 52, nc.width, 1, C.borderLight);
const notifs = [
["\u0625\u0634\u0639\u0627\u0631\u0627\u062a \u0627\u0644\u0628\u0631\u064a\u062f \u0627\u0644\u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a", true],
["\u0627\u0644\u0625\u0634\u0639\u0627\u0631\u0627\u062a \u0627\u0644\u0641\u0648\u0631\u064a\u0629", true],
["\u0631\u0633\u0627\u0626\u0644 SMS", false],
["\u062a\u0642\u0627\u0631\u064a\u0631 \u0623\u0633\u0628\u0648\u0639\u064a\u0629", true],
];
for (let i = 0; i < notifs.length; i++) {
const col = i % 2, row = Math.floor(i / 2);
const nw = (nc.width - 48) / 2;
const nx = col === 0 ? nc.width - 24 - nw : 24;
const ny = 60 + row * 68;
await T(nc, nx - nw + 60, ny + 8, nw - 68, 22, notifs[i][0], 13, C.textPrimary);
R(nc, nx - 52, ny + 6, 44, 24, notifs[i][1] ? C.wine : C.creamDark, 1, 12);
R(nc, notifs[i][1] ? nx - 28 : nx - 50, ny + 9, 18, 18, C.white, 1, 9);
}
});
}
// ═══════════════════════════════════════════════════════════════════
// ENTRY POINT
// ═══════════════════════════════════════════════════════════════════
figma.ui.onmessage = async (msg) => {
if (msg.type !== "generate") return;
const pages = [
{ fn: pOnboard1, label: "\u0646\u0645\u0648\u0630\u062c 1" },
{ fn: pOnboard2, label: "\u0646\u0645\u0648\u0630\u062c 2" },
{ fn: pOnboard3, label: "\u0646\u0645\u0648\u0630\u062c 3" },
{ fn: pLogin, label: "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644" },
{ fn: pRegister, label: "\u0625\u0646\u0634\u0627\u0621 \u062d\u0633\u0627\u0628" },
{ fn: p01, label: "\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629" },
{ fn: p02, label: "\u0627\u0644\u0645\u0648\u0627\u0639\u064a\u062f" },
{ fn: p03, label: "\u0627\u0644\u062e\u062f\u0645\u0627\u062a" },
{ fn: p04, label: "\u0627\u0644\u0639\u0645\u0644\u0627\u0621" },
{ fn: p05, label: "\u0627\u0644\u0645\u0648\u0638\u0641\u0648\u0646" },
{ fn: p06, label: "\u0627\u0644\u0645\u062e\u0632\u0648\u0646" },
{ fn: p07, label: "\u0627\u0644\u0645\u062a\u062c\u0631" },
{ fn: p08, label: "\u0627\u0644\u062a\u0641\u0627\u0639\u0644" },
{ fn: p09, label: "\u0627\u0644\u0633\u062c\u0644 \u0627\u0644\u0635\u062d\u064a" },
{ fn: p10, label: "\u0627\u0644\u062a\u0642\u0627\u0631\u064a\u0631" },
{ fn: p11, label: "\u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a" },
];
try {
await loadFonts();
for (let i = 0; i < pages.length; i++) {
figma.ui.postMessage({
type: "progress",
current: i + 1,
total: pages.length,
label: pages[i].label
});
await pages[i].fn(i * (FW + 60));
}
figma.ui.postMessage({ type: "done", count: pages.length });
figma.viewport.scrollAndZoomIntoView(figma.currentPage.children);
} catch (e) {
figma.ui.postMessage({ type: "error", message: e && e.message ? e.message : String(e) });
}
};