// 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) }); } };