chore: track Payload web application

This commit is contained in:
AHMED
2026-10-09 15:44:19 +03:00
parent e10491ad1c
commit 690c2deed7
124 changed files with 20205 additions and 0 deletions
@@ -0,0 +1,111 @@
'use client'
import { useEffect } from 'react'
import Link from 'next/link'
export type DeckSlide = {
kicker: string
index: string
title: React.ReactNode
body: string
format: React.ReactNode
link?: { href: string; label: string }
meta?: React.ReactNode
}
const V1_SLIDES: DeckSlide[] = [
{
kicker: 'دليل حصري · المستوى المتوسط',
index: '01',
title: 'ابنِ وكيلك الأول دون فوضى',
body: 'خريطة عملية لتقسيم المهام، اختيار الأدوات، وضبط ذاكرة الوكيل قبل كتابة الكود.',
format: <><span className="play" aria-hidden="true">▶</span>فيديو · 28 دقيقة</>,
link: { href: '/posts/agent-loop-stop-condition', label: 'شاهد المعاينة ←' },
},
{
kicker: 'مقال تطبيقي · للمبتدئين',
index: '02',
title: 'كيف تختار نموذجاً لمهمتك؟',
body: 'مقارنة منهجية حسب الجودة، التكلفة، السرعة، والخصوصية — من دون ضجيج تسويقي.',
format: <>قراءة · 12 دقيقة</>,
link: { href: '/posts/article-agent-guide', label: 'ابدأ القراءة ←' },
},
{
kicker: 'مسار متقدم · للأعضاء',
index: '03',
title: <>من <bdi dir="ltr">RAG</bdi> إلى إجابة موثوقة</>,
body: 'صمّم الاسترجاع، اختبر المصادر، وافهم أين تنهار الإجابة قبل أن تصل إلى المستخدم.',
format: <><span className="play" aria-hidden="true">▶</span>سلسلة · 4 أجزاء</>,
link: { href: '/subscription', label: 'شاهد للأعضاء ←' },
},
]
const DOT_LABELS = ['الشريحة الأولى', 'الشريحة الثانية', 'الشريحة الثالثة']
/* Featured deck — markup and slide logic from arabic-ai-learning-catalog(.v2).html
(autoplay 6.5s, dots, arrows, localStorage persistence). Slides render a
link when given; otherwise the v2 home variant shows a meta tag. */
export default function CatalogDeck({ slides = V1_SLIDES }: { slides?: DeckSlide[] }) {
useEffect(() => {
const slideEls = [...document.querySelectorAll('.deck-slide')] as HTMLElement[]
const dots = [...document.querySelectorAll('.deck-dot')] as HTMLElement[]
let activeSlide = Number(localStorage.getItem('midad-slide') || 0) % slideEls.length
let timer: ReturnType<typeof setInterval> | undefined
function showSlide(index: number) {
activeSlide = (index + slideEls.length) % slideEls.length
slideEls.forEach((slide, i) => slide.classList.toggle('active', i === activeSlide))
dots.forEach((dot, i) => {
dot.classList.toggle('active', i === activeSlide)
dot.setAttribute('aria-current', i === activeSlide ? 'true' : 'false')
})
localStorage.setItem('midad-slide', String(activeSlide))
}
function restartDeck() {
clearInterval(timer)
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) timer = setInterval(() => showSlide(activeSlide + 1), 6500)
}
document.getElementById('prevSlide')?.addEventListener('click', () => { showSlide(activeSlide - 1); restartDeck() })
document.getElementById('nextSlide')?.addEventListener('click', () => { showSlide(activeSlide + 1); restartDeck() })
dots.forEach(dot => dot.addEventListener('click', () => { showSlide(Number((dot as HTMLElement).dataset.dot)); restartDeck() }))
/* QA-05: keep controls stationary — pause autoplay while hovered/focused. */
const deck = document.querySelector('.deck')
deck?.addEventListener('mouseenter', () => clearInterval(timer))
deck?.addEventListener('mouseleave', () => restartDeck())
deck?.addEventListener('focusin', () => clearInterval(timer))
deck?.addEventListener('focusout', () => restartDeck())
showSlide(activeSlide)
restartDeck()
return () => clearInterval(timer)
}, [])
return (
<div className="deck" data-od-id="featured-deck" aria-label="مختارات مميزة">
<div className="deck-frame" aria-live="polite">
{slides.map((s, i) => (
<article className={`deck-slide${i === 0 ? ' active' : ''}`} data-slide={i} key={i}>
<div className="deck-slide-top"><span className="deck-slide-kicker">{s.kicker}</span><span className="deck-slide-index num">{s.index}</span></div>
<div className="deck-slide-body"><h2>{s.title}</h2><p>{s.body}</p></div>
<div className="deck-slide-foot">
<span className="deck-slide-format">{s.format}</span>
{s.link
? <Link className="deck-slide-link" href={s.link.href}>{s.link.label}</Link>
: <span className="meta">{s.meta}</span>}
</div>
</article>
))}
</div>
<div className="deck-controls">
<div className="deck-dots" aria-label="اختيار الشريحة">
{slides.map((_, i) => (
<button className={`deck-dot${i === 0 ? ' active' : ''}`} data-dot={i} aria-label={DOT_LABELS[i]} key={i}></button>
))}
</div>
<div className="deck-arrows">
<button className="icon-btn" id="prevSlide" aria-label="الشريحة السابقة">←</button>
<button className="icon-btn" id="nextSlide" aria-label="الشريحة التالية">→</button>
</div>
</div>
</div>
)
}