Commit de9ab004 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(proposal): Pro Max screenshots, the full feature list, and a real A4 print

The app screenshots came from a 16:9 emulator, so every phone in the
proposal looked squat. They are recaptured on a 1320x2868 @3x emulator — the
iPhone 16 Pro Max's own 440x956pt screen — so the frame now reads as a
modern phone.

The scope section listed what was out of scope, which read as the offer not
covering the club. It is replaced by an inventory of what is built and
working today, checked against both repos: 52 member-app features and 45
across the portal and the gate scanner, grouped into cards, under the four
pillars and the counts (51 app screens, 34 portal sections, 33 wizards,
6 roles). Items the guide implies but the app has no screen for — uploading
a transfer proof, requesting a plan, autopay — are left out.

Print is now true A4. One print stylesheet serves both the browser's Print
and generate-pdf.mjs: printer-safe margins, a cover page, dark bands printed
light, grids held at their desktop columns, every app and portal screenshot
in captioned galleries built into the page, page numbers in the footer, and
breaks that keep sections whole. 17 pages.
Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
parent 969b2048
......@@ -591,30 +591,255 @@ footer > *{ position:relative; z-index:1; }
.shot-note h4{ color:var(--navy); font-size:1rem; margin-bottom:6px; }
.shot-note p{ font-size:.88rem; color:var(--txt-2); line-height:1.85; }
/* ================= PRINT / PDF =================
The page is built for scrolling: reveals start invisible, the nav is fixed,
and the prototypes show one screen at a time. Print needs the opposite —
everything visible, nothing floating, and sections that start on a fresh
page instead of splitting a card in half. */
@media print{
.nav, .progress, .proto-tools, .btn-row, .hero-logo{ display:none !important; }
.reveal{ opacity:1 !important; transform:none !important; }
/* ================= FEATURES =================
The inventory of what is built. A dark band like the cover: the stats say
how much, the four pillars say why, and the chip cards list everything. */
.feat{
background:
radial-gradient(900px 480px at 88% -6%, rgba(201,162,39,.16), transparent 60%),
radial-gradient(820px 520px at 4% 106%, rgba(30,90,170,.28), transparent 62%),
linear-gradient(170deg,#07142B 0%,#0B2144 58%,#07142B 100%);
overflow:hidden;
}
.feat::after{
content:''; position:absolute; inset:0; pointer-events:none; opacity:.08;
background-image:linear-gradient(rgba(255,255,255,.07) 1px,transparent 1px),
linear-gradient(90deg,rgba(255,255,255,.07) 1px,transparent 1px);
background-size:56px 56px;
mask-image:linear-gradient(180deg,#000,transparent 55%);
-webkit-mask-image:linear-gradient(180deg,#000,transparent 55%);
}
.feat > .wrap{ position:relative; z-index:1; }
.feat-head .lede{ max-width:760px; }
.feat-stats{
display:grid; grid-template-columns:repeat(4,1fr); margin-top:40px;
border:1px solid rgba(255,255,255,.1); border-radius:var(--r-l);
background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,.015));
}
.feat-stats > div{ padding:26px 18px 24px; text-align:center; }
.feat-stats > div + div{ border-inline-start:1px solid rgba(255,255,255,.08); }
.feat-stats b{
display:block; font-family:var(--f-head); font-weight:900; line-height:1;
font-size:clamp(2.3rem,4.6vw,3.3rem); color:var(--gold-2);
}
.feat-stats span{ display:block; margin-top:10px; font-size:.84rem; color:#9DB2CF; }
.pillars{ display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-top:18px; }
.pillar{
padding:22px 20px; border-radius:var(--r-m);
background:linear-gradient(180deg,rgba(201,162,39,.11),rgba(201,162,39,.02) 70%);
border:1px solid rgba(201,162,39,.24);
}
.pillar-n{
width:30px; height:30px; border-radius:9px; display:grid; place-items:center; margin-bottom:14px;
font-family:var(--f-head); font-weight:900; font-size:.86rem; background:var(--gold-2); color:var(--ink);
}
.pillar h4{ color:#fff; font-size:1rem; margin-bottom:6px; }
.pillar p{ font-size:.85rem; color:#A9BBD4; line-height:1.8; }
.band{ margin-top:64px; }
.band-h{ display:flex; align-items:center; gap:14px; margin-bottom:24px; }
.band-i{
width:46px; height:46px; border-radius:13px; flex-shrink:0; display:grid; place-items:center;
font-size:1.25rem; background:linear-gradient(135deg,var(--gold-2),var(--gold)); color:var(--ink);
box-shadow:0 8px 22px rgba(201,162,39,.28);
}
.band.portal .band-i{ background:linear-gradient(135deg,#8CC0F5,#3E8CE0); color:#061A33; box-shadow:0 8px 22px rgba(62,140,224,.28); }
.band-h h3{ color:#fff; font-size:1.4rem; line-height:1.3; }
.band-h small{ display:block; font-family:var(--f-body); font-weight:500; font-size:.82rem; color:#8FA6C4; }
/* The rule runs from the heading toward the count; the page is RTL, so the
gradient fades toward the left. */
.band-h::after{ content:''; order:2; flex:1; height:1px; background:linear-gradient(to left, rgba(201,162,39,.45), transparent); }
.band.portal .band-h::after{ background:linear-gradient(to left, rgba(140,192,245,.45), transparent); }
.band-n{
order:3; flex-shrink:0; font-family:var(--f-head); font-weight:800; font-size:.78rem; color:var(--gold-2);
border:1px solid rgba(201,162,39,.4); border-radius:99px; padding:5px 14px;
}
.band.portal .band-n{ color:#8CC0F5; border-color:rgba(140,192,245,.4); }
.fgrid{ columns:3; column-gap:18px; }
.fcard{
break-inside:avoid; margin-bottom:18px; padding:20px 20px 18px; border-radius:18px;
background:rgba(255,255,255,.035); border:1px solid rgba(255,255,255,.09);
transition:border-color .25s, background .25s, transform .25s;
}
.fcard:hover{ border-color:rgba(201,162,39,.42); background:rgba(255,255,255,.055); transform:translateY(-3px); }
.band.portal .fcard:hover{ border-color:rgba(140,192,245,.42); }
.fcard-h{ display:flex; align-items:center; gap:12px; margin-bottom:14px; }
.fcard-h i{
width:38px; height:38px; border-radius:11px; flex-shrink:0; display:grid; place-items:center;
font-style:normal; font-size:1.05rem; background:rgba(201,162,39,.14); color:var(--gold-2);
}
.band.portal .fcard-h i{ background:rgba(62,140,224,.16); color:#8CC0F5; }
.fcard-h i .ico, .band-i .ico{ width:1.15em; height:1.15em; }
.fcard-h h4{ color:#fff; font-size:1rem; }
.chips{ list-style:none; display:flex; flex-wrap:wrap; gap:7px; }
.chips li{
display:inline-flex; align-items:center; gap:8px; padding:5px 12px; border-radius:99px;
font-size:.8rem; line-height:1.55; color:#D5E0EF;
background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.08);
}
.chips li::before{ content:''; width:5px; height:5px; border-radius:50%; flex-shrink:0; background:var(--gold-2); }
.band.portal .chips li::before{ background:#8CC0F5; }
html,body{ background:#fff; }
section{ break-before:page; padding:46px 30px; }
.hero{ break-before:auto; min-height:auto; padding:56px 30px 48px; }
#problem{ break-before:auto; }
@media (max-width:1020px){
.feat-stats{ grid-template-columns:repeat(2,1fr); }
.feat-stats > div:nth-child(3){ border-inline-start:0; }
.feat-stats > div:nth-child(n+3){ border-top:1px solid rgba(255,255,255,.08); }
.pillars{ grid-template-columns:repeat(2,1fr); }
.fgrid{ columns:2; }
}
@media (max-width:760px){
.pillars{ grid-template-columns:1fr; }
.fgrid{ columns:1; }
.band-h::after{ display:none; }
}
/* Never split an atomic block across a page boundary. */
.card,.pain,.tmpl,.pay-step,.tl-i,.faq,.kpi,.pline,.note,.drop,
.price-hero,.phone,.mcard,.stat,.fstep,.tbl,.scope-list li,.shot-grid button,figure{ break-inside:avoid; }
/* ================= PRINT / PDF (A4) =================
Both the browser's own Print and generate-pdf.mjs land here. The screen
page is built for scrolling: reveals start invisible, the nav floats, the
viewers show one screenshot at a time, and dark bands carry the design.
Paper needs the opposite — everything visible, printer-safe margins,
ink-light pages, and every screenshot laid out as a gallery. Sized for A4
at 100% scale, so "actual size" prints exactly what the PDF shows. */
.print-app, .print-portal{ display:none; }
.faq{ break-inside:avoid; }
.faq p{ display:block !important; } /* <details> print collapsed otherwise */
footer{ break-before:page; }
@page{ size:A4; margin:14mm 12mm 16mm; }
/* Watermarks read as grey smudges on paper at screen opacity. */
.wm::before,.hero::before,.price-hero::before,footer::before{ opacity:.045 !important; }
@media print{
html{ font-size:12px; }
body{ font-size:12px; line-height:1.7; background:#fff; -webkit-print-color-adjust:exact; print-color-adjust:exact; }
a{ text-decoration:none; color:inherit; }
.nav,.progress,.btn-row,.screen-only{ display:none !important; }
.reveal{ opacity:1 !important; transform:none !important; transition:none !important; }
.wrap,.narrow{ max-width:none; }
/* The page margin replaces the screen padding. */
section{ padding:0 !important; break-before:page; }
section.alt, section.dark{ background:#fff !important; color:var(--txt); }
.wm::before, .feat::after, .price-hero::before, footer::before{ display:none !important; }
h2.title{ font-size:1.95rem; margin-bottom:3mm; }
.lede{ font-size:1rem; max-width:none; }
.eyebrow{ margin-bottom:2mm; }
h2,h3,h4,.eyebrow{ break-after:avoid; }
/* Cover: the hero fills the first page. 265mm, not the full 267mm content
box, so rounding never pushes it onto a second page. */
.hero{ break-before:auto; min-height:0; height:265mm; border-radius:4mm; padding:14mm 12mm !important; }
.hero-logo{ height:32mm; margin-bottom:7mm; }
.hero h1{ font-size:3rem; }
.hero-sub{ font-size:1.08rem; margin-bottom:9mm; }
.hero-stats{ margin-bottom:9mm; }
.hero-stat{ padding:0 6mm; }
.hero-stat b{ font-size:1.6rem; }
/* Grids keep their desktop columns — at A4 width the phone breakpoints
would otherwise stack everything into one long column. */
.g2{ grid-template-columns:repeat(2,1fr) !important; }
.g3{ grid-template-columns:repeat(3,1fr) !important; }
.g4{ grid-template-columns:repeat(4,1fr) !important; }
.grid{ gap:4mm !important; }
.card{ padding:4.5mm; }
.card:hover,.tmpl:hover,.fcard:hover{ transform:none; box-shadow:none; }
.pain{ padding:4mm; }
.note{ padding:4mm 4.5mm; }
/* Dark bands print light: they are ink-heavy, and white text disappears on
a monochrome printer. */
.dark h2,.dark h3,.dark h4,.dark h2.title{ color:var(--navy) !important; }
.dark .lede{ color:var(--txt-2) !important; }
.dark .eyebrow{ color:#9A7B12 !important; }
.dark .card{ background:#fff !important; border-color:var(--line) !important; }
.dark .card p{ color:var(--txt-2) !important; }
.dark .stat{ background:var(--paper-2) !important; border-color:var(--line) !important; }
.dark .stat b{ color:var(--navy) !important; }
.dark .stat span{ color:var(--txt-3) !important; }
.flow{ grid-template-columns:repeat(4,1fr) !important; row-gap:0 !important; }
.fstep::after{ display:none; }
.fstep .fn{ background:var(--paper-2); color:#9A7B12; }
.fstep p{ color:var(--txt-2); }
/* Features */
.feat-stats{ grid-template-columns:repeat(4,1fr) !important; margin-top:6mm; background:var(--paper-2); border-color:var(--line); }
.feat-stats > div{ padding:4.5mm 3mm; }
.feat-stats > div + div{ border-inline-start:.3mm solid var(--line) !important; border-top:0 !important; }
.feat-stats b{ color:var(--navy); font-size:2.2rem; }
.feat-stats span{ color:var(--txt-2); }
.pillars{ grid-template-columns:repeat(2,1fr) !important; gap:4mm; margin-top:4mm; }
.pillar{ background:var(--paper-2); border-color:var(--line); padding:4.5mm; }
.pillar h4{ color:var(--navy); }
.pillar p{ color:var(--txt-2); }
.band{ margin-top:8mm; }
.band-h{ margin-bottom:4mm; }
.band-i{ box-shadow:none; }
.band-h h3{ color:var(--navy); }
.band-h small{ color:var(--txt-3); }
.band-h::after{ display:block !important; background:var(--line) !important; }
.band-n{ color:#9A7B12; border-color:rgba(201,162,39,.5); }
.band.portal .band-n{ color:#1F5FA8; border-color:rgba(62,140,224,.45); }
/* Multi-column layout fragments out of order across printed pages, so paper
gets a plain two-column grid instead of the screen's masonry. */
.fgrid{ columns:auto !important; display:grid; grid-template-columns:repeat(2,1fr); gap:4mm; align-items:start; }
.fcard{ background:#fff; border-color:var(--line); padding:4mm 4.5mm; margin-bottom:0; }
.fcard-h{ margin-bottom:3mm; }
.fcard-h h4{ color:var(--navy); }
.fcard-h i{ background:var(--gold-dim); color:#9A7B12; }
.band.portal .fcard-h i{ background:rgba(62,140,224,.12); color:#1F5FA8; }
.chips li{ color:var(--txt); background:var(--paper-2); border-color:var(--line); padding:1mm 3mm; font-size:.82rem; }
.chips li::before{ background:var(--gold); }
.band.portal .chips li::before{ background:var(--sky); }
/* Screenshot galleries (built by js/app.js, hidden on screen). */
.print-app{ display:grid; grid-template-columns:repeat(5,1fr); gap:5mm 3.5mm; margin-top:6mm; }
.print-app figure{ margin:0; text-align:center; }
.print-app img{ display:block; width:100%; height:auto; border-radius:3mm; border:.3mm solid var(--line-2); }
.print-app figcaption{ margin-top:1.8mm; font-family:var(--f-head); font-weight:700; font-size:.82rem; line-height:1.35; color:var(--navy); }
.print-portal{ display:grid; grid-template-columns:repeat(2,1fr); gap:6mm 5mm; margin:6mm 0; }
.print-portal figure{ margin:0; }
.print-portal figure.lead{ grid-column:1 / -1; }
.print-portal img{ display:block; width:100%; height:auto; border-radius:2mm; border:.3mm solid var(--line-2); }
.print-portal figcaption{ margin-top:2mm; }
.print-portal figcaption b{ display:block; font-family:var(--f-head); font-size:.92rem; color:var(--navy); }
.print-portal figcaption span{ display:block; font-size:.8rem; line-height:1.6; color:var(--txt-2); }
/* Pricing */
.price-hero{ padding:8mm 9mm; border-radius:4mm; }
.price-hero .amt{ font-size:3.3rem; }
.pay-step{ padding:4mm; }
/* The templates and the work cycle share a page when they fit, and so do
the work cycle and the client inputs. */
#flow{ break-before:auto; break-inside:avoid; margin-top:10mm; }
#inputs{ break-before:auto; break-inside:avoid; margin-top:10mm; }
/* The portal band starts its own page; otherwise its heading is stranded
at the foot of the app band's last page. */
#band-portal{ break-before:page; margin-top:0; }
.band-h{ break-after:avoid; }
/* Pricing and the terms each close on one page instead of spilling a
single block onto an otherwise empty sheet. */
#price .grid{ margin-top:5mm !important; }
/* The terms read as the tail of the offer, so they follow the pricing
cards on the same sheet rather than leaving it two-thirds empty. */
#terms{ break-before:auto; margin-top:12mm; }
#price .pline{ padding:2.2mm 0; }
#price .scope-list li{ padding:1.4mm 0; }
.faq p{ font-size:.94rem; line-height:1.72; }
.faq summary{ padding:3mm 0 1.5mm; }
.faq summary::after{ display:none; }
.faq p{ display:block !important; padding:0 0 2.4mm; }
footer{ break-before:avoid; break-inside:avoid; background:#fff !important; color:var(--txt-2); border-top:.3mm solid var(--line); padding:8mm 0 0 !important; margin-top:10mm; }
footer img{ content:url(../assets/al-arcade-dark.png); height:11mm; }
footer > div{ color:var(--navy) !important; }
footer .fl span{ color:var(--txt); }
footer small{ color:var(--txt-3); }
a{ text-decoration:none; }
/* Never split an atomic block across a page boundary. */
.card,.pain,.tmpl,.pay-step,.faq,.pline,.note,.price-hero,.stat,.fstep,.fcard,.pillar,
.feat-stats,.scope-list li,.print-app figure,.print-portal figure{ break-inside:avoid; }
}
/**
* Renders the proposal microsite to PDF.
* Renders the proposal microsite to an A4 PDF.
*
* The site is built for scrolling: the phone shows one app screenshot at a
* time and the browser frame one portal screenshot at a time. On paper that
* would hide almost every screen, so both viewers are replaced by labelled
* grids of all their screenshots before printing.
* All the print layout lives in css/styles.css under @media print, and the
* screenshot galleries are built into the page by js/app.js — so this script
* and the browser's own Print produce the same document. All it adds is
* page numbers in the footer.
*
* node generate-pdf.mjs # from the working tree
* node generate-pdf.mjs --live # from the deployed site
......@@ -20,85 +20,45 @@ const SRC = LIVE
: 'file://' + path.join(__dirname, 'index.html');
const OUT = path.join(__dirname, 'عرض-تطبيق-الأعضاء-نادي-الصيد-المصري.pdf');
const wait = ms => new Promise(r => setTimeout(r, ms));
// Kept equal to the @page margins in css/styles.css.
const MARGIN = { top: '14mm', bottom: '16mm', left: '12mm', right: '12mm' };
const FOOTER = `
<div style="width:100%; padding:0 12mm; font-size:7.5px; color:#7C8AA0; direction:rtl;
display:flex; justify-content:space-between; font-family:'Geeza Pro','Arial',sans-serif;">
<span>نادي الصيد المصري — تطبيق الأعضاء وبوابة الإدارة · عرض فني ومالي</span>
<span dir="ltr"><span class="pageNumber"></span> / <span class="totalPages"></span></span>
</div>`;
(async () => {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox', '--allow-file-access-from-files', '--font-render-hinting=none'],
});
const page = await browser.newPage();
await page.setViewport({ width: 1240, height: 1754, deviceScaleFactor: 2 });
console.log('· loading', LIVE ? 'live site' : 'working tree');
await page.goto(SRC, { waitUntil: 'networkidle0', timeout: 90000 });
await page.emulateMediaType('print');
await page.evaluate(() => window.dispatchEvent(new Event('beforeprint')));
console.log('· expanding screenshot viewers');
await page.evaluate(() => {
document.querySelectorAll('.reveal').forEach(el => el.classList.add('in'));
const nav = document.getElementById('nav'); if (nav) nav.remove();
/* --- app: the single-phone viewer goes; the all-screens grid stays and
becomes the printed gallery, with its images loaded eagerly. --- */
const proto = document.querySelector('#app .proto');
if (proto) proto.remove();
document.querySelectorAll('#shot-grid img').forEach(img => { img.loading = 'eager'; });
/* --- portal: button row + single frame -> every screen, captioned --- */
const btns = [...document.querySelectorAll('.pn-item[data-pshot]')];
const shots = btns.map(b => ({ k: b.dataset.pshot, t: b.textContent.replace(/^\s*\S+\s+/, '').trim() }));
const row = btns.length ? btns[0].parentElement : null;
const frame = document.querySelector('#portal .browser');
const note = document.getElementById('pshot-note');
if (frame) {
frame.insertAdjacentHTML('beforebegin', `
<div class="print-portal">
${shots.map(s => `
<figure>
<img src="screenshots/portal/${s.k}.jpg" alt="">
<figcaption>${s.t}</figcaption>
</figure>`).join('')}
</div>`);
frame.remove();
}
if (row) row.remove();
if (note) note.remove();
document.querySelectorAll('details').forEach(d => d.open = true);
const css = document.createElement('style');
css.textContent = `
/* Six across keeps all seventeen screens on one page; at five the last
row spilled onto a page of its own. */
.shot-grid{ grid-template-columns:repeat(6,1fr); gap:14px 12px; }
.shot-grid button{ border:0; padding:0; box-shadow:none; }
.shot-grid img{ border:1px solid var(--line); }
.print-portal{ display:grid; grid-template-columns:repeat(2,1fr); gap:22px 18px; margin-top:26px; }
.print-portal figure{ margin:0; break-inside:avoid; }
.print-portal img{ display:block; width:100%; height:auto; border:1px solid var(--line); border-radius:10px; }
.print-portal figcaption{ margin-top:7px; font-family:var(--f-head); font-weight:700; font-size:.82rem; color:var(--navy); }
`;
document.head.appendChild(css);
});
console.log('· waiting for images');
await page.waitForFunction(
() => [...document.images].every(img => img.complete && img.naturalWidth > 0),
{ timeout: 60000 },
);
console.log('· waiting for gallery images and fonts');
await page.waitForFunction(() => {
const imgs = [...document.querySelectorAll('.print-app img, .print-portal img')];
return imgs.length > 0 && imgs.every(i => i.complete && i.naturalWidth > 0);
}, { timeout: 90000 });
await page.evaluate(() => document.fonts.ready);
await wait(1200);
console.log('· printing');
console.log('· printing A4');
await page.pdf({
path: OUT,
width: '1240px',
height: '1754px',
format: 'A4',
printBackground: true,
margin: { top: 0, right: 0, bottom: 0, left: 0 },
displayHeaderFooter: false,
margin: MARGIN,
displayHeaderFooter: true,
headerTemplate: '<span></span>',
footerTemplate: FOOTER,
});
await browser.close();
......
......@@ -157,97 +157,133 @@
</div>
</section>
<!-- ============ SCOPE ============ -->
<section class="alt wm" id="scope">
<!-- ============ FEATURES ============ -->
<section class="dark feat" id="scope">
<div class="wrap">
<div class="reveal">
<div class="eyebrow">حدود المشروع</div>
<h2 class="title">ما يقدّمه النظام: أربعة محاور</h2>
<div class="reveal feat-head">
<div class="eyebrow">ما يشمله النظام</div>
<h2 class="title">كل ما يحتاجه النادي، في نظام واحد</h2>
<p class="lede">
حصر النطاق في أربعة محاور هو ما يجعل التنفيذ ممكنًا في أسبوعين إلى أربعة أسابيع
وبميزانية 500,000 جنيه غير شاملة ضريبة القيمة المضافة.
وما يقع خارج هذه المحاور غير مشمول في هذا العرض، ويُسعَّر على حدة إن طلبه النادي.
</p>
</div>
<div class="grid g4 reveal" style="margin-top:36px;">
<div class="card">
<div class="card-ico"><svg class="ico"><use href="#i-members"/></svg></div>
<h4>١ &mdash; العضوية والاشتراكات</h4>
<p>
تجديد العضوية السنوي وأقساطه. يرى العضو مستحقاته شهرًا بشهر،
ويسدد إلكترونيًا كاملًا أو قسطًا، ويستلم إيصالًا فوريًا في التطبيق.
</p>
</div>
<div class="card">
<div class="card-ico mint"><svg class="ico"><use href="#i-activity"/></svg></div>
<h4>٢ &mdash; الأنشطة الرياضية</h4>
<p>
كتالوج معلن بأسعار كل نشاط، واشتراك مباشر من التطبيق، وفاتورة شهرية،
وجدول تدريب يظهر لكل مشترك.
</p>
</div>
<div class="card">
<div class="card-ico sky"><svg class="ico"><use href="#i-gate"/></svg></div>
<h4>٣ &mdash; الدخول والدعوات</h4>
<p>
كود دخول شخصي متجدد لكل عضو، ورصيد دعوات شهري حسب نوع العضوية،
وشراء دعوات إضافية من التطبيق عند نفادها.
تطبيق للعضو على هاتفه، وبوابة لإدارة النادي على الويب، وتطبيق لماسح البوابة &mdash;
ثلاث واجهات تعمل على قاعدة بيانات واحدة. وكل ما يلي مبنيّ ويعمل اليوم.
</p>
</div>
<div class="card">
<div class="card-ico rose"><svg class="ico"><use href="#i-news"/></svg></div>
<h4>٤ &mdash; أخبار النادي</h4>
<p>
قسم أخبار داخل التطبيق تنشر عليه الإدارة الأخبار والفعاليات والبطولات
بالصور، ويصل الإشعار للعضو فور النشر.
</p>
</div>
</div>
<div class="grid g2 reveal" style="margin-top:44px; align-items:start;">
<div>
<h3 style="color:var(--navy); font-size:1.1rem; margin-bottom:12px;">داخل النطاق</h3>
<ul class="scope-list">
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>تطبيق موبايل للأعضاء على iPhone و Android بواجهة عربية</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>دخول العضو برقم موبايله بكود تحقق &mdash; بلا كلمات مرور</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>بطاقة عضوية رقمية توضح سريان الاشتراك</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>كشف المستحقات: تجديد سنوي وأقساط وغرامات وفواتير أنشطة</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>سداد إلكتروني بالبطاقة والمحفظة مع إيصال فوري</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>كتالوج الأنشطة بالأسعار والفئات السنية والأماكن المتاحة</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>الاشتراك في نشاط وربطه بأحد الأبناء</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>جدول التدريب الأسبوعي لكل مشترك</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>كود دخول متجدد وشاشة مسح على بوابات النادي</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>رصيد الدعوات وإصدارها وشراء دعوات إضافية</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>قسم أخبار النادي بالصور داخل التطبيق</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>إشعارات بالمستحقات وبالأخبار الجديدة فور نشرها</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>أقساط بجدول محدَّد وسعر معلن للسداد المبكر</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>غرامات بأسبابها، وتظلّم من التطبيق يردّ عليه موظف الإدارة</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>رصيد دائن يُخصم تلقائيًا من الفاتورة التالية</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>رفع إيصال التحويل البنكي من التطبيق واعتماده من البوابة</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>شهادة طبية تُرفع بالتصوير وتُراجع وتُعتمد أو تُرفض بسبب مكتوب</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>حضور اللاعبين وتقييمهم، ونشر التقييم لولي الأمر</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>فعاليات النادي بفئات تذاكر وسياسة استرداد وحجز من التطبيق</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>بوابة ويب لإدارة الأعضاء والمستحقات والتسعير والدعوات والأخبار</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>سجل مراجعة يوضّح من غيّر أي حقل ومن أي قيمة إلى أي قيمة</span></li>
<li><span class="tick"><svg class="ico"><use href="#i-check"/></svg></span><span>نشر التطبيق على App Store و Google Play باسم النادي</span></li>
</ul>
</div>
<div>
<h3 style="color:var(--txt-3); font-size:1.1rem; margin-bottom:12px;">خارج النطاق</h3>
<ul class="scope-list">
<li><span class="tick later"><svg class="ico"><use href="#i-arrow"/></svg></span><span>حجز الملاعب والكورتات وحمامات السباحة</span></li>
<li><span class="tick later"><svg class="ico"><use href="#i-arrow"/></svg></span><span>محفظة إلكترونية ورصيد داخلي</span></li>
<li><span class="tick later"><svg class="ico"><use href="#i-arrow"/></svg></span><span>المطاعم والكافيتريات والطلب من التطبيق</span></li>
<li><span class="tick later"><svg class="ico"><use href="#i-arrow"/></svg></span><span>القرى والشاليهات والحجوزات الموسمية</span></li>
<li><span class="tick later"><svg class="ico"><use href="#i-arrow"/></svg></span><span>الشكاوى والطلبات والدعم الفني</span></li>
<li><span class="tick later"><svg class="ico"><use href="#i-arrow"/></svg></span><span>العيادة الطبية والكشف الرياضي داخل النادي</span></li>
<li><span class="tick no"><svg class="ico"><use href="#i-x"/></svg></span><span>نظام ERP محاسبي كامل (دفتر عام وميزانيات وأصول)</span></li>
<li><span class="tick no"><svg class="ico"><use href="#i-x"/></svg></span><span>الموارد البشرية والرواتب والحضور</span></li>
<li><span class="tick no"><svg class="ico"><use href="#i-x"/></svg></span><span>المخازن والمشتريات وأذون الاستلام</span></li>
<li><span class="tick no"><svg class="ico"><use href="#i-x"/></svg></span><span>نقاط البيع داخل النادي</span></li>
<li><span class="tick no"><svg class="ico"><use href="#i-x"/></svg></span><span>إدارة الموظفين والصلاحيات المعقدة</span></li>
</ul>
<div class="feat-stats reveal">
<div><b class="num">51</b><span>شاشة في تطبيق العضو</span></div>
<div><b class="num">34</b><span>قسمًا في بوابة الإدارة</span></div>
<div><b class="num">33</b><span>معالجًا مُرشدًا للعمليات الجماعية</span></div>
<div><b class="num">6</b><span>أدوار بصلاحيات منفصلة</span></div>
</div>
<div class="pillars reveal">
<div class="pillar">
<div class="pillar-n">١</div>
<h4>العضوية والاشتراكات</h4>
<p>تجديد العضوية السنوي وأقساطه. يرى العضو مستحقاته شهرًا بشهر، ويسدد إلكترونيًا كاملًا أو قسطًا، ويستلم إيصالًا فوريًا في التطبيق.</p>
</div>
<div class="pillar">
<div class="pillar-n">٢</div>
<h4>الأنشطة الرياضية</h4>
<p>كتالوج معلن بأسعار كل نشاط، واشتراك مباشر من التطبيق، وفاتورة شهرية، وجدول تدريب يظهر لكل مشترك.</p>
</div>
<div class="pillar">
<div class="pillar-n">٣</div>
<h4>الدخول والدعوات</h4>
<p>كود دخول شخصي متجدد لكل عضو، ورصيد دعوات شهري حسب نوع العضوية، وشراء دعوات إضافية من التطبيق عند نفادها.</p>
</div>
<div class="pillar">
<div class="pillar-n">٤</div>
<h4>أخبار النادي</h4>
<p>قسم أخبار داخل التطبيق تنشر عليه الإدارة الأخبار والفعاليات والبطولات بالصور، ويصل الإشعار للعضو فور النشر.</p>
</div>
</div>
<div class="band reveal" id="band-app">
<div class="band-h">
<div class="band-i"><svg class="ico"><use href="#i-wallet"/></svg></div>
<div><h3>تطبيق العضو</h3><small>iPhone و Android &mdash; بواجهة عربية بالكامل</small></div>
<span class="band-n"><span class="num" data-count-of="band-app"></span> ميزة</span>
</div>
<div class="fgrid">
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-shield"/></svg></i><h4>الدخول والأمان</h4></div>
<ul class="chips"><li>الدخول برقم الموبايل وكود تحقق</li><li>بلا كلمة مرور ولا تسجيل ذاتي</li><li>جهاز نشط واحد لكل عضو</li><li>قفل التطبيق ببصمة الإصبع</li><li>تغيير كود الدخول وإبطال القديم فورًا</li><li>جولة تعريفية عند أول استخدام</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-members"/></svg></i><h4>العضوية والأسرة</h4></div>
<ul class="chips"><li>بطاقة عضوية رقمية بملء الشاشة</li><li>التبديل بين أفراد الأسرة بلمسة</li><li>صلاحيات ولي الأمر على كل فرد</li><li>تنبيه على صورة من عليه مستحقات</li><li>الملف الشخصي وتغيير الصورة</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-card"/></svg></i><h4>المستحقات والسداد</h4></div>
<ul class="chips"><li>كشف موحّد: مستحق الآن وقادم ومتأخر</li><li>توزيع التأخير بعدد الأيام</li><li>سداد عدة بنود في عملية واحدة</li><li>بطاقة بنكية ومحفظة إلكترونية</li><li>رصيد دائن يُخصم تلقائيًا</li><li>إيصال PDF فوري قابل للمشاركة</li><li>سجل المدفوعات وأرشيف الإيصالات</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-receipt"/></svg></i><h4>الأقساط والغرامات</h4></div>
<ul class="chips"><li>خطط تقسيط بجدول كل قسط وحالته</li><li>سعر معلن للسداد المبكر</li><li>غرامات بأسبابها</li><li>تظلّم من التطبيق وردّ النادي عليه</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-gate"/></svg></i><h4>الدخول والدعوات</h4></div>
<ul class="chips"><li>كود QR يتجدد كل ثلاثين ثانية</li><li>يعمل دون اتصال بالإنترنت</li><li>كود مستقل لكل فرد في الأسرة</li><li>سجل الدخول إلى النادي</li><li>رصيد الدعوات الشهري</li><li>إصدار دعوة ومشاركتها مع الضيف</li><li>شراء دعوات إضافية</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-activity"/></svg></i><h4>الأنشطة الرياضية</h4></div>
<ul class="chips"><li>كتالوج مُصفّى بسنّ الفرد</li><li>اشتراك فوري أو قائمة انتظار</li><li>رسوم محسوبة بالتناسب قبل التأكيد</li><li>جدول الأسبوع للأسرة كلها</li><li>نسبة الحضور وتقييمات المدرب</li><li>إبلاغ عن غياب أو إيقاف مؤقت أو إلغاء</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-file"/></svg></i><h4>الشهادات الطبية</h4></div>
<ul class="chips"><li>رفع بالتصوير أو من المعرض</li><li>أكثر من صفحة في الشهادة الواحدة</li><li>ترفيهي وأكاديمي ودولي</li><li>متابعة حالة المراجعة وسببها</li><li>تنبيه قبل انتهاء الشهادة</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-news"/></svg></i><h4>حياة النادي</h4></div>
<ul class="chips"><li>الأخبار والإعلانات بالصور</li><li>رحلات وبطولات وحج وعمرة وفعاليات اجتماعية</li><li>حجز للأسرة والضيوف</li><li>رفع مستندات الحجز</li><li>تذكرة بكود لكل مشترك</li><li>مبلغ الاسترداد معروض قبل الإلغاء</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-bell"/></svg></i><h4>الإشعارات والإعدادات</h4></div>
<ul class="chips"><li>مركز الإشعارات</li><li>تفضيلات لكل فئة: التطبيق والرسائل والبريد</li><li>مظهر فاتح وداكن وتلقائي</li><li>أرقام عربية أو إنجليزية</li><li>آخر بيانات محفوظة عند انقطاع الشبكة</li><li>الأسئلة الشائعة والتواصل مع النادي</li></ul>
</article>
</div>
</div>
<div class="band portal reveal" id="band-portal">
<div class="band-h">
<div class="band-i"><svg class="ico"><use href="#i-dashboard"/></svg></div>
<div><h3>بوابة الإدارة وتطبيق البوابة</h3><small>على الويب &mdash; يرى كل موظف ما يخصّ دوره</small></div>
<span class="band-n"><span class="num" data-count-of="band-portal"></span> ميزة</span>
</div>
<div class="fgrid">
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-dashboard"/></svg></i><h4>التحصيل والمال</h4></div>
<ul class="chips"><li>لوحة التحصيل وتنبيهات ذكية</li><li>تفاصيل الإيرادات</li><li>مطابقة التحصيل مع بوابة الدفع</li><li>المتأخرات وإرسال التذكير</li><li>دورات الفوترة بتشغيل تجريبي</li><li>المرتجعات والاسترداد</li><li>إثباتات التحويل البنكي</li><li>طلبات التقسيط والتظلمات</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-members"/></svg></i><h4>الأعضاء والعضويات</h4></div>
<ul class="chips"><li>ملف العضو بسبعة عشر قسمًا</li><li>مستحق يدوي وسداد نقدي أو بشيك</li><li>إعفاء بسبب مسجَّل</li><li>خطة تقسيط وغرامة من الملف</li><li>إنهاء جلسات الدخول عند فقد الهاتف</li><li>دمج المكرر وتوحيد أرقام الموبايل</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-activity"/></svg></i><h4>الأنشطة</h4></div>
<ul class="chips"><li>البرامج والأسعار</li><li>المجموعات والمواعيد والسعة</li><li>الاشتراكات وقائمة الانتظار وطلبات النقل</li><li>تسجيل الحضور بالحالة الطبية لكل لاعب</li><li>تقييم اللاعبين ونشره لولي الأمر</li><li>تقرير الأنشطة وترحيل الموسم</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-shield"/></svg></i><h4>الطبي</h4></div>
<ul class="chips"><li>مراجعة الشهادات: اعتماد كامل أو مشروط أو رفض</li><li>شهادات على وشك الانتهاء</li><li>تقرير الالتزام الطبي</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-news"/></svg></i><h4>حياة النادي</h4></div>
<ul class="chips"><li>الأخبار مع التثبيت على الرئيسية</li><li>الفعاليات بفئات التذاكر وسياسة الاسترداد</li><li>مراجعة مستندات المشتركين</li><li>دعوة الأعضاء إلى الفعاليات</li><li>أداء الفعاليات</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-mail"/></svg></i><h4>البوابة والدعوات</h4></div>
<ul class="chips"><li>سجل الدخول ومن بداخل النادي الآن</li><li>أجهزة البوابات</li><li>سياسة الدعوات لكل نوع عضوية</li><li>منح دعوات استثنائية</li><li>قائمة الحظر</li><li>وضع الطوارئ</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-gate"/></svg></i><h4>تطبيق ماسح البوابة</h4></div>
<ul class="chips"><li>مسح كود العضو بكاميرا الجهاز</li><li>إدخال الكود يدويًا عند تعذّر المسح</li><li>يعمل عند انقطاع الشبكة ويرسل ما مسحه لاحقًا</li><li>تجاوز يدوي بكود الموظف</li></ul>
</article>
<article class="fcard">
<div class="fcard-h"><i><svg class="ico"><use href="#i-settings"/></svg></i><h4>العمليات والإدارة</h4></div>
<ul class="chips"><li><span class="num">33</span> معالجًا بمراجعة قبل التنفيذ</li><li>استيراد البيانات بستة قوالب وتراجع</li><li>حملات الإشعارات وقوالبها</li><li>الهوية والعلامة التجارية</li><li>المستخدمون والأدوار</li><li>سجل المراجعة لكل تغيير</li><li>تصدير بيانات النادي</li></ul>
</article>
</div>
</div>
</div>
......@@ -265,7 +301,7 @@
</p>
</div>
<div class="proto reveal" style="margin-top:38px;">
<div class="proto reveal screen-only" style="margin-top:38px;">
<div class="proto-nav">
<h4>شاشات التطبيق</h4>
<button class="pn-item on" data-shot="01-signin"><span class="n">١</span> الدخول برقم الموبايل</button>
......@@ -306,7 +342,7 @@
</div>
</div>
<div class="reveal" style="margin-top:44px;">
<div class="reveal screen-only" style="margin-top:44px;">
<h3 style="color:var(--navy);font-size:1.05rem;margin-bottom:14px;">كل الشاشات في لقطة واحدة</h3>
<div class="shot-grid" id="shot-grid"></div>
</div>
......@@ -327,7 +363,7 @@
</p>
</div>
<div class="reveal" style="margin-top:34px; display:flex; gap:8px; flex-wrap:wrap;">
<div class="reveal screen-only" style="margin-top:34px; display:flex; gap:8px; flex-wrap:wrap;">
<button class="pn-item on" style="width:auto;" data-pshot="dashboard"><span class="n">١</span> لوحة التحصيل</button>
<button class="pn-item" style="width:auto;" data-pshot="collections"><span class="n">٢</span> المتأخرات</button>
<button class="pn-item" style="width:auto;" data-pshot="members"><span class="n">٣</span> الأعضاء</button>
......@@ -349,7 +385,7 @@
<button class="pn-item" style="width:auto;" data-pshot="audit"><span class="n">١٩</span> سجل المراجعة</button>
</div>
<div class="browser reveal" style="margin-top:18px;">
<div class="browser reveal screen-only" style="margin-top:18px;">
<div class="bbar">
<div class="bdots"><i style="background:#FF5F57;"></i><i style="background:#FEBC2E;"></i><i style="background:#28C840;"></i></div>
<div class="burl" id="burl">https://portal.sayd-club.eg/portal</div>
......@@ -358,7 +394,7 @@
alt="لوحة التحصيل في بوابة إدارة نادي الصيد المصري">
</div>
<div id="pshot-note" class="shot-note reveal"></div>
<div id="pshot-note" class="shot-note reveal screen-only"></div>
<div class="note reveal" style="margin-top:24px;">
<strong>بوابة بيانات لا نظام ERP:</strong>
......
......@@ -212,4 +212,56 @@
}
pBtns.forEach(function (b) { b.addEventListener('click', function () { showPortal(b.dataset.pshot); }); });
showPortal('dashboard');
/* ---------- feature counts ----------
Counted from the lists themselves, so a band's number can never drift
from what the band actually shows. */
document.querySelectorAll('[data-count-of]').forEach(function (el) {
var band = document.getElementById(el.dataset.countOf);
if (band) el.textContent = band.querySelectorAll('.chips li').length;
});
/* ---------- print galleries ----------
On screen each viewer shows one screenshot at a time; paper needs all of
them. Both galleries live in the page (hidden on screen), so the browser's
own Print produces the same A4 document as generate-pdf.mjs. Their images
attach after load so they never compete with the first paint. */
var appWrap = document.querySelector('#app .wrap');
if (appWrap) {
var pa = document.createElement('div');
pa.className = 'print-app';
pa.innerHTML = shotBtns.map(function (b) {
var k = b.dataset.shot, n = SHOTS[k];
return n ? '<figure><img data-src="screenshots/app/' + k + '.jpg" alt="' + n.t + '">' +
'<figcaption>' + n.t + '</figcaption></figure>' : '';
}).join('');
appWrap.appendChild(pa);
}
var portalWrap = document.querySelector('#portal .wrap');
if (portalWrap) {
var pp = document.createElement('div');
pp.className = 'print-portal';
pp.innerHTML = pBtns.map(function (b, i) {
var k = b.dataset.pshot, n = PSHOTS[k];
return n ? '<figure' + (i === 0 ? ' class="lead"' : '') + '>' +
'<img data-src="screenshots/portal/' + k + '.jpg" alt="' + n.t + '">' +
'<figcaption><b>' + n.t + '</b><span>' + n.p + '</span></figcaption></figure>' : '';
}).join('');
portalWrap.insertBefore(pp, portalWrap.querySelector('.note'));
}
function loadPrintImages() {
document.querySelectorAll('.print-app img[data-src], .print-portal img[data-src]').forEach(function (img) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
});
}
if (document.readyState === 'complete') setTimeout(loadPrintImages, 300);
else window.addEventListener('load', function () { setTimeout(loadPrintImages, 300); });
// Closed <details> print as bare questions; open them all for paper.
window.addEventListener('beforeprint', function () {
loadPrintImages();
document.querySelectorAll('details').forEach(function (d) { d.open = true; });
});
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment