Implement academy analytics, billing, and web stories updates

This commit is contained in:
2026-05-26 07:27:29 +02:00
parent 456c3d6bb0
commit 0b33a1b074
177 changed files with 27360 additions and 2685 deletions

View File

@@ -2,17 +2,23 @@ import React from 'react'
// ── Pagination ────────────────────────────────────────────────────────────────
function Pagination({ meta, onPageChange }) {
if (!meta || meta.last_page <= 1) return null
if (!meta) return null
const currentPage = Number(meta.current_page || 1)
const lastPage = meta.last_page != null ? Number(meta.last_page) : null
const hasMore = Boolean(meta.has_more)
if (lastPage !== null && lastPage <= 1) return null
if (lastPage === null && currentPage <= 1 && !hasMore) return null
const { current_page, last_page } = meta
const pages = []
if (last_page <= 7) {
for (let i = 1; i <= last_page; i++) pages.push(i)
} else {
if (lastPage !== null && lastPage <= 7) {
for (let i = 1; i <= lastPage; i++) pages.push(i)
} else if (lastPage !== null) {
const around = new Set(
[1, last_page, current_page, current_page - 1, current_page + 1].filter(
(p) => p >= 1 && p <= last_page
[1, lastPage, currentPage, currentPage - 1, currentPage + 1].filter(
(p) => p >= 1 && p <= lastPage
)
)
const sorted = [...around].sort((a, b) => a - b)
@@ -28,15 +34,15 @@ function Pagination({ meta, onPageChange }) {
className="mt-10 flex items-center justify-center gap-1 flex-wrap"
>
<button
disabled={current_page <= 1}
onClick={() => onPageChange(current_page - 1)}
disabled={currentPage <= 1}
onClick={() => onPageChange(currentPage - 1)}
className="px-3 py-1.5 rounded-md text-sm text-white/50 hover:text-white hover:bg-white/[0.06] disabled:opacity-25 disabled:pointer-events-none transition-colors"
aria-label="Previous page"
>
Prev
</button>
{pages.map((p, i) =>
{lastPage !== null ? pages.map((p, i) =>
p === '…' ? (
<span key={`sep-${i}`} className="px-2 text-white/25 text-sm select-none">
@@ -44,11 +50,11 @@ function Pagination({ meta, onPageChange }) {
) : (
<button
key={p}
onClick={() => p !== current_page && onPageChange(p)}
aria-current={p === current_page ? 'page' : undefined}
onClick={() => p !== currentPage && onPageChange(p)}
aria-current={p === currentPage ? 'page' : undefined}
className={[
'min-w-[2rem] px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
p === current_page
p === currentPage
? 'bg-sky-600/30 text-sky-300 ring-1 ring-sky-500/40'
: 'text-white/50 hover:text-white hover:bg-white/[0.06]',
].join(' ')}
@@ -56,11 +62,15 @@ function Pagination({ meta, onPageChange }) {
{p}
</button>
)
) : (
<span className="px-2 text-sm text-white/35">
Page {currentPage}
</span>
)}
<button
disabled={current_page >= last_page}
onClick={() => onPageChange(current_page + 1)}
disabled={lastPage !== null ? currentPage >= lastPage : !hasMore}
onClick={() => onPageChange(currentPage + 1)}
className="px-3 py-1.5 rounded-md text-sm text-white/50 hover:text-white hover:bg-white/[0.06] disabled:opacity-25 disabled:pointer-events-none transition-colors"
aria-label="Next page"
>