Commit 23bdc6f8 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat: add heading component + update ecs rules to fix views

parent a50abc13
/// <reference types="vite/client" />
declare module "*";
<?php
declare(strict_types=1);
namespace App\Views\Components;
use Exception;
use ViewComponents\Component;
class Heading extends Component
{
protected string $level = '';
/**
* @var "small"|"base"|"large"
*/
protected string $size = 'base';
public function render(): string
{
if ($this->level === '') {
throw new Exception('level property must be set for Heading component.');
}
$sizeClasses = [
'small' => 'tracking-wide text-base',
'base' => 'text-xl',
'large' => 'text-3xl',
];
$class = 'relative z-10 font-bold text-pine-800 font-display before:w-full before:absolute before:h-1/2 before:left-0 before:bottom-0 before:rounded-full before:bg-pine-100 before:-z-10 ' . $sizeClasses[$this->size];
$level = $this->level;
return <<<HTML
<h{$level} class="{$class}">{$this->slot}</h{$level}>
HTML;
}
}
<?php if (session()->has('message')): ?>
<?php declare(strict_types=1);
if (session()->has('message')): ?>
<div class="px-4 py-2 mb-4 font-semibold text-green-900 bg-green-200 border border-green-700">
<?= session('message') ?>
</div>
......
<?php
declare(strict_types=1);
use CodeIgniter\CLI\CLI;
CLI::error('ERROR: ' . $code);
......
<?php
declare(strict_types=1);
use CodeIgniter\CLI\CLI;
// The main Exception
CLI::newLine();
CLI::write('[' . $exception::class . ']', 'light_gray', 'red');
CLI::newLine();
CLI::write($message);
CLI::newLine();
CLI::write(
'at ' .
CLI::color(
clean_path($exception->getFile()) . ':' . $exception->getLine(),
'green',
),
);
CLI::write('at ' . CLI::color(clean_path($exception->getFile()) . ':' . $exception->getLine(), 'green', ), );
CLI::newLine();
// The backtrace
if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
......@@ -24,8 +21,7 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
}
foreach ($backtraces as $i => $error) {
$padFile = ' '; // 4 spaces
$padClass = ' '; // 7 spaces
$padFile = ' ';
$c = str_pad($i + 1, 3, ' ', STR_PAD_LEFT);
if (isset($error['file'])) {
......@@ -33,9 +29,7 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
CLI::write($c . $padFile . CLI::color($filepath, 'yellow'));
} else {
CLI::write(
$c . $padFile . CLI::color('[internal function]', 'yellow'),
);
CLI::write($c . $padFile . CLI::color('[internal function]', 'yellow'), );
}
$function = '';
......@@ -47,7 +41,7 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
: $error['type'];
$function .=
$padClass . $error['class'] . $type . $error['function'];
} elseif (!isset($error['class']) && isset($error['function'])) {
} elseif (! isset($error['class']) && isset($error['function'])) {
$function .= $padClass . $error['function'];
}
......@@ -57,7 +51,7 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
return match (true) {
is_object($value) => 'Object(' . $value::class . ')',
is_array($value) => $value !== [] ? '[...]' : '[]',
is_null($value) => 'null',
$value === null => 'null',
default => var_export($value, true),
};
}, array_values($error['args'] ?? [])),
......
<?php
declare(strict_types=1);
// On the CLI, we still want errors in productions
// so just use the exception template.
include __DIR__ . '/error_exception.php';
<?php
<?php declare(strict_types=1);
use CodeIgniter\CodeIgniter;
use Config\Services;
......@@ -70,9 +70,9 @@ $errorId = uniqid('error', true); ?>
<?php if (isset($row['file']) && is_file($row['file'])): ?>
<?php
if (isset($row['function']) && in_array($row['function'], ['include', 'include_once', 'require', 'require_once'], true)) {
echo esc($row['function'] . ' ' . static::cleanPath($row['file']));
echo esc($row['function'] . ' ' . static::cleanPath($row['file']));
} else {
echo esc(static::cleanPath($row['file']) . ' : ' . $row['line']);
echo esc(static::cleanPath($row['file']) . ' : ' . $row['line']);
}
?>
<?php else: ?>
......@@ -82,7 +82,7 @@ $errorId = uniqid('error', true); ?>
<!-- Class/Method -->
<?php if (isset($row['class'])) : ?>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp;<?= esc($row['class'] . $row['type'] . $row['function']) ?>
<?php if (!empty($row['args'])) : ?>
<?php if (! empty($row['args'])) : ?>
<?php $args_id = $errorId . 'args' . $index ?>
( <a href="#" onclick="return toggle('<?= esc($args_id, 'attr') ?>');">arguments</a> )
<div class="args" id="<?= esc($args_id, 'attr') ?>">
......@@ -91,9 +91,9 @@ $errorId = uniqid('error', true); ?>
<?php
$params = null;
// Reflection by name is not available for closure function
if (!str_ends_with($row['function'], '}')) {
$mirror = isset($row['class']) ? new ReflectionMethod($row['class'], $row['function']) : new ReflectionFunction($row['function']);
$params = $mirror->getParameters();
if (! str_ends_with($row['function'], '}')) {
$mirror = isset($row['class']) ? new ReflectionMethod($row['class'], $row['function']) : new ReflectionFunction($row['function']);
$params = $mirror->getParameters();
}
foreach ($row['args'] as $key => $value): ?>
<tr>
......@@ -111,7 +111,7 @@ $errorId = uniqid('error', true); ?>
<?php endif; ?>
<?php endif; ?>
<?php if (!isset($row['class']) && isset($row['function'])): ?>
<?php if (! isset($row['class']) && isset($row['function'])): ?>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp; <?= esc($row['function']) ?>()
<?php endif; ?>
</p>
......@@ -132,9 +132,9 @@ $errorId = uniqid('error', true); ?>
<!-- Server -->
<div class="content" id="server">
<?php foreach (['_SERVER', '_SESSION'] as $var): ?>
<?php if (empty($GLOBALS[$var]) || !is_array($GLOBALS[$var])) {
continue;
} ?>
<?php if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
continue;
} ?>
<h3>$<?= esc($var) ?></h3>
......@@ -165,7 +165,7 @@ $errorId = uniqid('error', true); ?>
<!-- Constants -->
<?php $constants = get_defined_constants(true); ?>
<?php if (!empty($constants['user'])): ?>
<?php if (! empty($constants['user'])): ?>
<h3>Constants</h3>
<table>
......@@ -234,9 +234,9 @@ $errorId = uniqid('error', true); ?>
<?php $empty = true; ?>
<?php foreach (['_GET', '_POST', '_COOKIE'] as $var): ?>
<?php if (empty($GLOBALS[$var]) || !is_array($GLOBALS[$var])) {
continue;
} ?>
<?php if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
continue;
} ?>
<?php $empty = false; ?>
......@@ -276,7 +276,7 @@ $errorId = uniqid('error', true); ?>
<?php endif; ?>
<?php $headers = $request->getHeaders(); ?>
<?php if (!empty($headers)): ?>
<?php if (! empty($headers)): ?>
<h3>Headers</h3>
......@@ -290,11 +290,11 @@ $errorId = uniqid('error', true); ?>
<tbody>
<?php foreach ($headers as $value): ?>
<?php if (empty($value)) {
continue;
} ?>
<?php if (!is_array($value)) {
$value = [$value];
} ?>
continue;
} ?>
<?php if (! is_array($value)) {
$value = [$value];
} ?>
<?php foreach ($value as $h) : ?>
<tr>
<td><?= esc($h->getName(), 'html') ?></td>
......@@ -389,7 +389,7 @@ $errorId = uniqid('error', true); ?>
<p>
Displayed at <?= esc(date('H:i:sa')) ?> &mdash;
PHP: <?= esc(phpversion()) ?> &mdash;
PHP: <?= esc(PHP_VERSION) ?> &mdash;
CodeIgniter: <?= esc(CodeIgniter::CI_VERSION) ?>
</p>
......
<?php
<?php declare(strict_types=1);
use CodeIgniter\Pager\PagerRenderer;
/**
* @var PagerRenderer $pager
*/
/** @var PagerRenderer $pager */
$pager->setSurroundCount(2);
?>
......@@ -23,8 +21,8 @@ $pager->setSurroundCount(2);
'Pager.previous',
) ?>" class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
<span aria-hidden="true"><?= lang(
'Pager.previous',
) ?></span>
'Pager.previous',
) ?></span>
</a>
</li>
<?php endif; ?>
......@@ -48,15 +46,15 @@ $pager->setSurroundCount(2);
<?php if ($pager->hasNextPage()): ?>
<li>
<a href="<?= $pager->getNextPage() ?>" aria-label="<?= lang(
'Pager.next',
) ?>" class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
'Pager.next',
) ?>" class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
<span aria-hidden="true"><?= lang('Pager.next') ?></span>
</a>
</li>
<li>
<a href="<?= $pager->getLast() ?>" aria-label="<?= lang(
'Pager.last',
) ?>" class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
'Pager.last',
) ?>" class="block px-3 py-2 text-gray-700 hover:bg-gray-200 hover:text-black">
<span aria-hidden="true"><?= lang('Pager.last') ?></span>
</a>
</li>
......
<?php
use PhpCsFixer\Fixer\Whitespace\IndentationTypeFixer;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\CodingStandard\Fixer\Naming\StandardizeHereNowDocKeywordFixer;
use Symplify\EasyCodingStandard\ValueObject\Option;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
......@@ -12,21 +14,32 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$parameters->set(Option::PATHS, [
__DIR__ . '/app',
__DIR__ . '/modules',
__DIR__ . '/themes',
__DIR__ . '/tests',
__DIR__ . '/public',
__DIR__ . '/public',
]);
$parameters->set(Option::SKIP, [
// TODO: restrict some rules for views?
__DIR__ . '/app/Views/*',
__DIR__ . '/modules/**/Views/*',
// skip specific generated files
__DIR__ . '/modules/Admin/Language/*/PersonsTaxonomy.php',
StandardizeHereNowDocKeywordFixer::class => [
__DIR__ . '/app/View/Components',
__DIR__ . '/modules/**/View/Components',
__DIR__ . '/app/Views/Components/*',
__DIR__ . '/modules/**/Views/Components/*',
__DIR__ . '/themes/**/Views/Components/*',
],
LineLengthFixer::class => [
__DIR__ . '/app/Views/*',
__DIR__ . '/modules/**/Views/*',
__DIR__ . '/themes/*',
],
IndentationTypeFixer::class => [
__DIR__ . '/app/Views/*',
__DIR__ . '/modules/**/Views/*',
__DIR__ . '/themes/*',
]
]);
......
......@@ -4,7 +4,6 @@ module.exports = {
mode: "jit",
purge: [
"./app/Views/**/*.php",
"./app/View/Components/**/*.php",
"./modules/**/Views/**/*.php",
"./themes/**/*.php",
"./app/Helpers/*.php",
......@@ -49,6 +48,9 @@ module.exports = {
gridTemplateColumns: {
podcasts: "repeat(auto-fill, minmax(14rem, 1fr))",
},
zIndex: {
"-10": "-10",
},
},
},
variants: {},
......
<!DOCTYPE html>
<html lang="<?= service('request')->getLocale() ?>">
<html lang="<?= service('request')
->getLocale() ?>">
<head>
<meta charset="UTF-8"/>
......@@ -8,9 +9,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
<?= service('vite')->asset('styles/index.css', 'css') ?>
<?= service('vite')->asset('js/admin.ts', 'js') ?>
<?= service('vite')->asset('js/audio-player.ts', 'js') ?>
<?= service('vite')
->asset('styles/index.css', 'css') ?>
<?= service('vite')
->asset('js/admin.ts', 'js') ?>
<?= service('vite')
->asset('js/audio-player.ts', 'js') ?>
</head>
<body class="relative bg-pine-50 holy-grail-grid">
......@@ -18,13 +22,13 @@
<header class="sticky top-0 z-50 flex items-center justify-between h-10 text-white border-b holy-grail__header bg-pine-800 border-pine-900">
<div class="inline-flex items-center h-full">
<a href="<?= route_to(
'admin',
) ?>" class="inline-flex items-center h-full px-2 border-r border-pine-900">
'admin',
) ?>" class="inline-flex items-center h-full px-2 border-r border-pine-900">
<?= (isset($podcast) ? icon('arrow-left', 'mr-2') : '') . svg('castopod-logo', 'h-6') ?>
</a>
<a href="<?= route_to(
'home',
) ?>" class="inline-flex items-center h-full px-6 text-sm font-semibold outline-none hover:underline focus:ring">
'home',
) ?>" class="inline-flex items-center h-full px-6 text-sm font-semibold outline-none hover:underline focus:ring">
<?= lang('AdminNavigation.go_to_website') ?>
<?= icon('external-link', 'ml-1 opacity-60') ?>
</a>
......@@ -38,7 +42,8 @@
aria-haspopup="true"
aria-expanded="false">
<?= icon('account-circle', 'text-2xl opacity-60 mr-2') ?>
<?= user()->username ?>
<?= user()
->username ?>
<?= icon('caret-down', 'ml-auto text-2xl') ?>
</button>
<nav
......@@ -48,14 +53,14 @@
data-dropdown="menu"
data-dropdown-placement="bottom-end">
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
'my-account',
) ?>"><?= lang('AdminNavigation.account.my-account') ?></a>
'my-account',
) ?>"><?= lang('AdminNavigation.account.my-account') ?></a>
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
'change-password',
) ?>"><?= lang('AdminNavigation.account.change-password') ?></a>
'change-password',
) ?>"><?= lang('AdminNavigation.account.change-password') ?></a>
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
'logout',
) ?>"><?= lang('AdminNavigation.account.logout') ?></a>
'logout',
) ?>"><?= lang('AdminNavigation.account.logout') ?></a>
</nav>
</header>
<aside id="admin-sidebar" class="sticky z-50 flex flex-col text-white transition duration-200 ease-in-out transform -translate-x-full border-r top-10 border-pine-900 bg-pine-800 holy-grail__sidebar md:translate-x-0">
......@@ -80,15 +85,15 @@
<div class="flex flex-col">
<?= render_breadcrumb('text-gray-800 text-xs') ?>
<div class="flex flex-wrap items-center">
<h1 class="text-3xl font-bold font-display"><?= $this->renderSection(
'pageTitle',
) ?></h1>
<Heading level="1" size="large"><?= $this->renderSection(
'pageTitle',
) ?></Heading>
<?= $this->renderSection('headerLeft') ?>
</div>
</div>
<div class="flex flex-wrap"><?= $this->renderSection(
'headerRight',
) ?></div>
'headerRight',
) ?></div>
</div>
</header>
<div class="container px-2 py-8 mx-auto md:px-12">
......
<?php
<?php declare(strict_types=1);
$navigation = [
'podcasts' => [
'icon' => 'mic',
......@@ -12,8 +13,15 @@ $navigation = [
'icon' => 'star-smile',
'items' => ['fediverse-blocked-actors', 'fediverse-blocked-domains'],
],
'users' => ['icon' => 'group', 'items' => ['user-list', 'user-create']],
'pages' => ['icon' => 'pages', 'items' => ['page-list', 'page-create']],
'users' => [
'icon' => 'group',
'items' => ['user-list', 'user-create'],
],
'pages' => [
'icon' => 'pages',
'items' => ['page-list', 'page-create'],
],
]; ?>
<nav class="flex flex-col flex-1 py-4 overflow-y-auto gap-y-4">
......@@ -29,9 +37,9 @@ $navigation = [
<li class="inline-flex">
<a class="w-full py-1 pl-14 pr-2 text-sm outline-none hover:opacity-100 focus:ring<?= $isActive
? ' font-semibold opacity-100 inline-flex items-center'
: ' opacity-75' ?>" href="<?= route_to($item) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') .lang(
'AdminNavigation.' . $item,
) ?></a>
: ' opacity-75' ?>" href="<?= route_to($item) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') . lang(
'AdminNavigation.' . $item,
) ?></a>
</li>
<?php endforeach; ?>
</ul>
......
......@@ -21,7 +21,7 @@
'id' => 'user',
'class' => 'form-select mb-4',
'required' => 'required',
'placeholder' => lang('Contributor.form.user_placeholder')
'placeholder' => lang('Contributor.form.user_placeholder'),
]) ?>
<Forms.Label for="role"><?= lang('Contributor.form.role') ?></Forms.Label>
......@@ -29,14 +29,19 @@
'id' => 'role',
'class' => 'form-select mb-4',
'required' => 'required',
'placeholder' => lang('Contributor.form.role_placeholder')
'placeholder' => lang('Contributor.form.role_placeholder'),
]) ?>
<?= button(
lang('Contributor.form.submit_add'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
[
'variant' => 'primary',
],
[
'type' => 'submit',
'class' => 'self-end',
],
) ?>
<?= form_close() ?>
......
......@@ -26,8 +26,13 @@
<?= button(
lang('Contributor.form.submit_edit'),
'',
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'],
[
'variant' => 'primary',
],
[
'type' => 'submit',
'class' => 'self-end',
],
) ?>
<?= form_close() ?>
......
......@@ -46,7 +46,9 @@
'variant' => 'info',
'size' => 'small',
],
['class' => 'mr-2'],
[
'class' => 'mr-2',
],
) .
button(
lang('Contributor.remove'),
......@@ -59,7 +61,9 @@
'variant' => 'danger',
'size' => 'small',
],
['class' => 'mr-2'],
[
'class' => 'mr-2',
],
);
},
],
......
<?php
<?php declare(strict_types=1);
$podcastNavigation = [
'dashboard' => [
'icon' => 'dashboard',
......@@ -7,7 +8,7 @@ $podcastNavigation = [
]; ?>
<a href="<?= route_to('podcast-view', $podcast->id) ?>" class="flex items-center px-4 py-2 border-b border-pine-900 focus:ring">
<?= icon('arrow-left', 'mr-2' ) ?>
<?= icon('arrow-left', 'mr-2') ?>
<img
src="<?= $podcast->image->thumbnail_url ?>"
alt="<?= $podcast->title ?>"
......@@ -24,12 +25,12 @@ $podcastNavigation = [
<div class="flex flex-col items-start flex-1 w-48 px-2">
<span class="w-full font-semibold truncate" title="<?= $episode->title ?>"><?= $episode->title ?></span>
<a href="<?= route_to(
'episode',
$podcast->handle,
$episode->slug,
) ?>" class="inline-flex items-center text-xs outline-none hover:underline focus:ring"><?= lang(
'EpisodeNavigation.go_to_page',
) ?>
'episode',
$podcast->handle,
$episode->slug,
) ?>" class="inline-flex items-center text-xs outline-none hover:underline focus:ring"><?= lang(
'EpisodeNavigation.go_to_page',
) ?>
<?= icon('external-link', 'ml-1 opacity-60') ?>
</a>
</div>
......@@ -48,10 +49,10 @@ $podcastNavigation = [
<a class="w-full py-1 pl-14 pr-2 text-sm outline-none hover:opacity-100 focus:ring <?= $isActive
? 'font-semibold opacity-100 inline-flex items-center'
: 'opacity-75' ?>" href="<?= route_to(
$item,
$podcast->id,
$episode->id
) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') .lang('EpisodeNavigation.' . $item) ?></a>
$item,
$podcast->id,
$episode->id
) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') . lang('EpisodeNavigation.' . $item) ?></a>
</li>
<?php endforeach; ?>
</ul>
......