Journal renders correctly
This commit is contained in:
parent
4ea55d4d25
commit
0b7fa77247
@ -4,6 +4,22 @@ namespace MyPrayerJournal;
|
|||||||
|
|
||||||
class Layout
|
class Layout
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Generate the heading for a bare result
|
||||||
|
*/
|
||||||
|
public static function bareHead(): void
|
||||||
|
{
|
||||||
|
echo '<!DOCTYPE html><html lang=en><head><title></title></head><body>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the end of a bare result
|
||||||
|
*/
|
||||||
|
public static function bareFoot(): void
|
||||||
|
{
|
||||||
|
echo '</body></html>';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the `DOCTYPE` declaration, `html`, and `head` tags for the page
|
* Create the `DOCTYPE` declaration, `html`, and `head` tags for the page
|
||||||
*
|
*
|
||||||
@ -38,7 +54,7 @@ class Layout
|
|||||||
default => []
|
default => []
|
||||||
};
|
};
|
||||||
echo '<li class=nav-item>';
|
echo '<li class=nav-item>';
|
||||||
page_link($url, $text, $classAttr);
|
UI::pageLink($url, $text, $classAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,7 +64,7 @@ class Layout
|
|||||||
{ ?>
|
{ ?>
|
||||||
<nav class="navbar navbar-dark" role="navigation">
|
<nav class="navbar navbar-dark" role="navigation">
|
||||||
<div class=container-fluid><?php
|
<div class=container-fluid><?php
|
||||||
page_link('/', '<span class=m>my</span><span class=p>Prayer</span><span class=j>Journal</span>',
|
UI::pageLink('/', '<span class=m>my</span><span class=p>Prayer</span><span class=j>Journal</span>',
|
||||||
['class' => 'navbar-brand']); ?>
|
['class' => 'navbar-brand']); ?>
|
||||||
<ul class="navbar-nav me-auto d-flex flex-row"><?php
|
<ul class="navbar-nav me-auto d-flex flex-row"><?php
|
||||||
if (key_exists('user_id', $_SESSION)) {
|
if (key_exists('user_id', $_SESSION)) {
|
||||||
@ -91,9 +107,9 @@ class Layout
|
|||||||
<p class="text-muted text-end">
|
<p class="text-muted text-end">
|
||||||
myPrayerJournal <?=self::displayVersion();?><br>
|
myPrayerJournal <?=self::displayVersion();?><br>
|
||||||
<em><small><?php
|
<em><small><?php
|
||||||
page_link('/legal/privacy-policy', 'Privacy Policy');
|
UI::pageLink('/legal/privacy-policy', 'Privacy Policy');
|
||||||
echo ' • ';
|
echo ' • ';
|
||||||
page_link('/legal/terms-of-service', 'Terms of Service');
|
UI::pageLink('/legal/terms-of-service', 'Terms of Service');
|
||||||
echo ' • '; ?>
|
echo ' • '; ?>
|
||||||
<a href=https://git.bitbadger.solutions/bit-badger/myPrayerJournal target=_blank
|
<a href=https://git.bitbadger.solutions/bit-badger/myPrayerJournal target=_blank
|
||||||
rel=noopener>Developed</a> and hosted by
|
rel=noopener>Developed</a> and hosted by
|
||||||
@ -114,15 +130,4 @@ class Layout
|
|||||||
</footer><?php
|
</footer><?php
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a card when there are no results found
|
|
||||||
public static function noResults(string $heading, string $link, string $buttonText, string $text): void
|
|
||||||
{ ?>
|
|
||||||
<div class=card>
|
|
||||||
<h5 class=card-header><?=$heading?></h5>
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<p class=card-text><?=$text?></p><?php
|
|
||||||
page_link($link, $buttonText, ['class' => 'btn btn-primary']); ?>
|
|
||||||
</div>
|
|
||||||
</div><?php
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace MyPrayerJournal;
|
namespace MyPrayerJournal;
|
||||||
|
|
||||||
use BitBadger\PDODocument\{DocumentException, Find};
|
use DateTimeImmutable;
|
||||||
|
use BitBadger\PDODocument\{Custom, DocumentException, DocumentList, Find, Mapper\DocumentMapper};
|
||||||
use Exception;
|
use Exception;
|
||||||
use JsonSerializable;
|
use JsonSerializable;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
@ -34,16 +35,25 @@ class Request implements JsonSerializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a request by its ID
|
* Get the current text for this request
|
||||||
*
|
*
|
||||||
* @param string $id The ID of the request
|
* @return string The most recent text for the request
|
||||||
* @return Request|false The request if it is found and belongs to the current user, false if not
|
|
||||||
* @throws DocumentException If any is encountered
|
|
||||||
*/
|
*/
|
||||||
public static function byId(string $id): Request|false
|
public function currentText(): string
|
||||||
{
|
{
|
||||||
$req = Find::byId(Table::REQUEST, $id, self::class);
|
foreach ($this->history as $hist) if (isset($hist->text)) return $hist->text;
|
||||||
return ($req && $req->userId == $_SESSION['user_id']) ? $req : false;
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the date/time this request was last marked as prayed
|
||||||
|
*
|
||||||
|
* @return string|null The date/time this request was last marked as prayed
|
||||||
|
*/
|
||||||
|
public function lastPrayed(): ?string
|
||||||
|
{
|
||||||
|
foreach ($this->history as $hist) if ($hist->action == RequestAction::Prayed) return $hist->asOf;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): mixed
|
public function jsonSerialize(): mixed
|
||||||
@ -60,4 +70,41 @@ class Request implements JsonSerializable
|
|||||||
if (!is_null($this->showAfter)) $values['showAfter'] = $this->showAfter;
|
if (!is_null($this->showAfter)) $values['showAfter'] = $this->showAfter;
|
||||||
return $values;
|
return $values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a request by its ID
|
||||||
|
*
|
||||||
|
* @param string $id The ID of the request
|
||||||
|
* @return Request|false The request if it is found and belongs to the current user, false if not
|
||||||
|
* @throws DocumentException If any is encountered
|
||||||
|
*/
|
||||||
|
public static function byId(string $id): Request|false
|
||||||
|
{
|
||||||
|
$req = Find::byId(Table::REQUEST, $id, self::class);
|
||||||
|
return ($req && $req->userId == $_SESSION['user_id']) ? $req : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user's active journal requests
|
||||||
|
*
|
||||||
|
* @return DocumentList<Request> The requests for the user's journal
|
||||||
|
* @throws DocumentException If any is encountered
|
||||||
|
*/
|
||||||
|
public static function forJournal(): DocumentList
|
||||||
|
{
|
||||||
|
$table = Table::REQUEST;
|
||||||
|
return Custom::list(<<<SQL
|
||||||
|
SELECT data, (
|
||||||
|
SELECT h.value->>'asOf' as_of
|
||||||
|
FROM $table i LEFT JOIN json_each(i.data, '$.history') h
|
||||||
|
WHERE r.data->>'id' = i.data->>'id' AND h.value->>'action' = 'Prayed'
|
||||||
|
LIMIT 1) last_prayed
|
||||||
|
FROM $table r
|
||||||
|
WHERE data->>'userId' = :userId
|
||||||
|
AND data->>'$.history[0].action' <> 'Answered'
|
||||||
|
AND (data->>'snoozedUntil' IS NULL OR data->>'snoozedUntil' < datetime('now'))
|
||||||
|
AND (data->>'showAfter' IS NULL OR data->>'showAfter' < datetime('now'))
|
||||||
|
ORDER BY coalesce(last_prayed, data->>'snoozedUntil', data->>'showAfter', data->>'$.history[0].asOf')
|
||||||
|
SQL, [':userId' => $_SESSION['user_id']], new DocumentMapper(self::class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
79
src/lib/UI.php
Normal file
79
src/lib/UI.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace MyPrayerJournal;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use DateTimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User interface building blocks
|
||||||
|
*/
|
||||||
|
class UI
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Generate a material icon
|
||||||
|
*
|
||||||
|
* @param string $name The name of the material icon
|
||||||
|
* @return string The material icon wrapped in a `span` tag
|
||||||
|
*/
|
||||||
|
public static function icon(string $name): string {
|
||||||
|
return "<span class=material-icons>$name</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a card when there are no results found
|
||||||
|
*/
|
||||||
|
public static function noResults(string $heading, string $link, string $buttonText, string $text): void
|
||||||
|
{ ?>
|
||||||
|
<div class=card>
|
||||||
|
<h5 class=card-header><?=$heading?></h5>
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<p class=card-text><?=$text?></p><?php
|
||||||
|
self::pageLink($link, $buttonText, ['class' => 'btn btn-primary']); ?>
|
||||||
|
</div>
|
||||||
|
</div><?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a link to a page within myPrayerJournal
|
||||||
|
*
|
||||||
|
* @param string $href The URL for the link
|
||||||
|
* @param string $text The text for the link
|
||||||
|
* @param array $attrs Any additional attributes that should be placed on the `a` tag
|
||||||
|
*/
|
||||||
|
public static function pageLink(string $href, string $text, array $attrs = []): void
|
||||||
|
{ ?>
|
||||||
|
<a href="<?=$href?>" hx-get="<?=$href?>" hx-target=#top hx-swap=innerHTML hx-push-url=true<?php
|
||||||
|
foreach ($attrs as $key => $value) echo " $key=\"" . htmlspecialchars($value) . "\""; ?>><?=$text?></a><?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thanks, William Entriken! https://stackoverflow.com/a/42446994/276707
|
||||||
|
/** @var array Periods for the relative date/time display */
|
||||||
|
private static array $periods = [
|
||||||
|
[ 60, 1, '%s seconds ago', 'a second ago'],
|
||||||
|
[ 60*100, 60, '%s minutes ago', 'one minute ago'],
|
||||||
|
[ 3600*70, 3600, '%s hours ago', 'an hour ago'],
|
||||||
|
[ 3600*24*10, 3600*24, '%s days ago', 'yesterday'],
|
||||||
|
[ 3600*24*30, 3600*24*7, '%s weeks ago', 'a week ago'],
|
||||||
|
[3600*24*30*30, 3600*24*30, '%s months ago', 'last month'],
|
||||||
|
[ INF, 3600*24*265, '%s years ago', 'last year']
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function relativeDate(string $date): void
|
||||||
|
{
|
||||||
|
$parsed = new DateTimeImmutable($date);
|
||||||
|
$diff = time() - $parsed->getTimestamp();
|
||||||
|
foreach (self::$periods as $period) {
|
||||||
|
if ($diff > $period[0]) continue;
|
||||||
|
$diff = floor($diff / $period[1]);
|
||||||
|
$value = $diff > 1 ? sprintf($period[2], $diff) : $period[3];
|
||||||
|
$inZone = $parsed->setTimezone(new DateTimeZone($_SERVER['HTTP_X_TIME_ZONE'] ?? 'Etc/UTC'));
|
||||||
|
echo '<span title="' . date_format($inZone, 'l, F j, Y \a\t g:ia T') . '">' . $value . '</span>';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,64 +1,54 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
use BitBadger\PDODocument\{Custom, Query};
|
use MyPrayerJournal\{Auth, Layout, Request, UI};
|
||||||
use BitBadger\PDODocument\Mapper\DocumentMapper;
|
|
||||||
use MyPrayerJournal\{Auth,History,Layout,Request,Table};
|
|
||||||
|
|
||||||
require '../../start.php';
|
require '../../start.php';
|
||||||
|
|
||||||
Auth::requireUser(false);
|
Auth::requireUser(false);
|
||||||
bare_head();
|
Layout::bareHead();
|
||||||
|
|
||||||
$reqs = Custom::list(
|
|
||||||
Query::selectFromTable(Table::REQUEST) . " WHERE data->>'userId' = :userId AND data->>'$.history[0].action' <> 'Answered'",
|
|
||||||
[':userId' => Auth::user()['sub']], new DocumentMapper(Request::class));
|
|
||||||
|
|
||||||
|
$reqs = Request::forJournal();
|
||||||
if ($reqs->hasItems()) { ?>
|
if ($reqs->hasItems()) { ?>
|
||||||
<section id=journalItems class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-3" hx-target=this
|
<section id=journalItems class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-3" hx-target=this
|
||||||
hx-swap=outerHTML aria-label="Prayer Requests"><?php
|
hx-swap=outerHTML aria-label="Prayer Requests"><?php
|
||||||
$spacer = '<span> </span>';
|
$spacer = '<span> </span>';
|
||||||
foreach ($reqs->items() as /** @var Request $req */ $req) {
|
foreach ($reqs->items() as /** @var Request $req */ $req) { ?>
|
||||||
$withText = array_filter($req->history, fn($hist) => isset($hist->text));
|
|
||||||
$text = $withText[array_key_first($withText)]->text; ?>
|
|
||||||
<div class=col>
|
<div class=col>
|
||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-header p-0 d-flex" role=toolbar><?php
|
<div class="card-header p-0 d-flex" role=toolbar><?php
|
||||||
page_link("/request/edit?id=$req->id", icon('edit'),
|
UI::pageLink("/request/edit?id=$req->id", UI::icon('edit'),
|
||||||
['class' => 'btn btn-secondary', 'title' => 'Edit Request']); ?>
|
['class' => 'btn btn-secondary', 'title' => 'Edit Request']); ?>
|
||||||
<?=$spacer?>
|
<?=$spacer?>
|
||||||
<button type=button class="btn btn-secondary" title="Add Notes" data-bs-toggle=modal
|
<button type=button class="btn btn-secondary" title="Add Notes" data-bs-toggle=modal
|
||||||
data-bs-target=#notesModal hx-get="/components/request/add-notes?id=<?=$req->id?>"
|
data-bs-target=#notesModal hx-get="/components/request/add-notes?id=<?=$req->id?>"
|
||||||
hx-target=#notesBody hx-swap=innerHTML><?=icon('comment');?></button>
|
hx-target=#notesBody hx-swap=innerHTML><?=UI::icon('comment');?></button>
|
||||||
<?=$spacer?>
|
<?=$spacer?>
|
||||||
<button type=button class="btn btn-secondary" title="Snooze Request" data-bs-toggle=modal
|
<button type=button class="btn btn-secondary" title="Snooze Request" data-bs-toggle=modal
|
||||||
data-bs-target=#snoozeModal hx-get="/components/request/snooze?id=<?=$req->id?>"
|
data-bs-target=#snoozeModal hx-get="/components/request/snooze?id=<?=$req->id?>"
|
||||||
hx-target=#snoozeBody hx-swap=innerHTML><?=icon('schedule');?></button>
|
hx-target=#snoozeBody hx-swap=innerHTML><?=UI::icon('schedule');?></button>
|
||||||
<div class=flex-grow-1></div>
|
<div class=flex-grow-1></div>
|
||||||
<button type=button class="btn btn-success w-25" hx-patch="/request/prayed?id=<?=$req->id?>"
|
<button type=button class="btn btn-success w-25" hx-patch="/request/prayed?id=<?=$req->id?>"
|
||||||
title="Mark as Prayed"><?=icon('done');?></button>
|
title="Mark as Prayed"><?=UI::icon('done');?></button>
|
||||||
</div>
|
</div>
|
||||||
<div class=card-body>
|
<div class=card-body>
|
||||||
<p class=request-text><?=htmlentities($text);?>
|
<p class=request-text><?=htmlentities($req->currentText());?>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer text-end text-muted px-1 py-0">
|
<div class="card-footer text-end text-muted px-1 py-0">
|
||||||
<em>last activity <?=$req->history[0]->asOf?></em>
|
<em><?php
|
||||||
<?php /*
|
$lastPrayed = $req->lastPrayed();
|
||||||
TODO: relative time
|
echo 'last ' . (is_null($lastPrayed) ? 'activity': 'prayed') . ' ';
|
||||||
[] [
|
UI::relativeDate($lastPrayed ?? $req->history[0]->asOf); ?>
|
||||||
match req.LastPrayed with
|
</em>
|
||||||
| Some dt -> str "last prayed "; relativeDate dt now tz
|
|
||||||
| None -> str "last activity "; relativeDate req.AsOf now tz
|
|
||||||
] */ ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div><?php
|
</div><?php
|
||||||
} ?>
|
} ?>
|
||||||
</section><?php
|
</section><?php
|
||||||
} else {
|
} else {
|
||||||
Layout::noResults('No Active Requests', '/request/edit?id=new', 'Add a Request', <<<'TEXT'
|
UI::noResults('No Active Requests', '/request/edit?id=new', 'Add a Request', <<<'TEXT'
|
||||||
You have no requests to be shown; see the “Active” link above for snoozed or deferred requests, and
|
You have no requests to be shown; see the “Active” link above for snoozed or deferred requests, and
|
||||||
the “Answered” link for answered requests
|
the “Answered” link for answered requests
|
||||||
TEXT);
|
TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bare_foot();
|
Layout::bareFoot();
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
use MyPrayerJournal\Auth;
|
use MyPrayerJournal\Auth;
|
||||||
|
use MyPrayerJournal\UI;
|
||||||
|
|
||||||
require '../start.php';
|
require '../start.php';
|
||||||
|
|
||||||
Auth::requireUser();
|
Auth::requireUser();
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$name = $user['first_name'] ?? 'Your';
|
$name = $user['given_name'] ?? 'Your';
|
||||||
page_head('Welcome'); ?>
|
page_head('Welcome'); ?>
|
||||||
<article class="container-fluid mt-3">
|
<article class="container-fluid mt-3">
|
||||||
<h2 class=pb-3><?=$name?><?=$name == 'Your' ? '' : '’s'?> Prayer Journal</h2>
|
<h2 class=pb-3><?=$name?><?=$name == 'Your' ? '' : '’s'?> Prayer Journal</h2>
|
||||||
<p class="pb-3 text-center"><?php
|
<p class="pb-3 text-center"><?php
|
||||||
page_link('/request/new/edit', icon('add_box') . ' Add a Prayer Request', ['class' => 'btn btn-primary']); ?>
|
UI::pageLink('/request/edit?id=new', UI::icon('add_box') . ' Add a Prayer Request',
|
||||||
|
['class' => 'btn btn-primary']); ?>
|
||||||
<p hx-get=/components/journal-items hx-swap=outerHTML hx-trigger=load hx-target=this>
|
<p hx-get=/components/journal-items hx-swap=outerHTML hx-trigger=load hx-target=this>
|
||||||
Loading your prayer journal…
|
Loading your prayer journal…
|
||||||
<div id=notesModal class="modal fade" tabindex=-1 aria-labelledby=nodesModalLabel aria-hidden=true>
|
<div id=notesModal class="modal fade" tabindex=-1 aria-labelledby=nodesModalLabel aria-hidden=true>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
use MyPrayerJournal\UI;
|
||||||
|
|
||||||
require '../../start.php';
|
require '../../start.php';
|
||||||
|
|
||||||
page_head('Terms of Service'); ?>
|
page_head('Terms of Service'); ?>
|
||||||
@ -20,8 +22,9 @@ page_head('Terms of Service'); ?>
|
|||||||
<p class=card-text>
|
<p class=card-text>
|
||||||
myPrayerJournal is a service that allows individuals to enter and amend their prayer requests. It
|
myPrayerJournal is a service that allows individuals to enter and amend their prayer requests. It
|
||||||
requires no registration by itself, but access is granted based on a successful login with an
|
requires no registration by itself, but access is granted based on a successful login with an
|
||||||
external identity provider. See <?php page_link('/legal/privacy-policy', 'our privacy policy'); ?>
|
external identity provider. See
|
||||||
for details on how that information is accessed and stored.
|
<?php UI::pageLink('/legal/privacy-policy', 'our privacy policy'); ?> for details on how that
|
||||||
|
information is accessed and stored.
|
||||||
</div>
|
</div>
|
||||||
<div class=list-group-item>
|
<div class=list-group-item>
|
||||||
<h3>3. Third Party Services</h3>
|
<h3>3. Third Party Services</h3>
|
||||||
@ -49,7 +52,7 @@ page_head('Terms of Service'); ?>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class=pt-3>
|
<p class=pt-3>
|
||||||
You may also wish to review our <?php page_link('/legal/privacy-policy', 'privacy policy'); ?> to learn how we
|
You may also wish to review our <?php UI::pageLink('/legal/privacy-policy', 'privacy policy'); ?> to learn how
|
||||||
handle your data.
|
we handle your data.
|
||||||
</article><?php
|
</article><?php
|
||||||
page_foot();
|
page_foot();
|
||||||
|
@ -10,11 +10,14 @@ require __DIR__ . '/vendor/autoload.php';
|
|||||||
const MPJ_VERSION = '4.0.0-alpha1';
|
const MPJ_VERSION = '4.0.0-alpha1';
|
||||||
|
|
||||||
(Dotenv::createImmutable(__DIR__))->load();
|
(Dotenv::createImmutable(__DIR__))->load();
|
||||||
session_start();
|
|
||||||
|
|
||||||
$auth0_session = Auth::client()->getCredentials();
|
if (php_sapi_name() != 'cli') {
|
||||||
if (!is_null($auth0_session)) {
|
session_start();
|
||||||
|
|
||||||
|
$auth0_session = Auth::client()->getCredentials();
|
||||||
|
if (!is_null($auth0_session)) {
|
||||||
$_SESSION['user_id'] = $auth0_session->user['sub'];
|
$_SESSION['user_id'] = $auth0_session->user['sub'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Configuration::$pdoDSN = 'sqlite:' . implode(DIRECTORY_SEPARATOR, [__DIR__, 'data', 'mpj.db']);
|
Configuration::$pdoDSN = 'sqlite:' . implode(DIRECTORY_SEPARATOR, [__DIR__, 'data', 'mpj.db']);
|
||||||
@ -31,23 +34,6 @@ function is_htmx(): bool
|
|||||||
return key_exists('HTTP_HX_REQUEST', $_SERVER) && !key_exists('HTTP_HX_HISTORY_RESTORE_REQUEST', $_SERVER);
|
return key_exists('HTTP_HX_REQUEST', $_SERVER) && !key_exists('HTTP_HX_HISTORY_RESTORE_REQUEST', $_SERVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
function page_link(string $href, string $text, array $attrs = []): void
|
|
||||||
{
|
|
||||||
echo '<a href="' . $href . '" hx-get="' . $href . '" hx-target="#top" hx-swap=innerHTML hx-push-url=true';
|
|
||||||
foreach ($attrs as $key => $value) echo " $key=\"" . htmlspecialchars($value) . "\"";
|
|
||||||
echo ">$text</a>";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a material icon
|
|
||||||
*
|
|
||||||
* @param string $name The name of the material icon
|
|
||||||
* @return string The material icon wrapped in a `span` tag
|
|
||||||
*/
|
|
||||||
function icon(string $name): string {
|
|
||||||
return "<span class=material-icons>$name</span>";
|
|
||||||
}
|
|
||||||
|
|
||||||
function page_head(string $title): void
|
function page_head(string $title): void
|
||||||
{
|
{
|
||||||
Layout::htmlHead($title);
|
Layout::htmlHead($title);
|
||||||
@ -57,11 +43,6 @@ function page_head(string $title): void
|
|||||||
echo '<main role=main>';
|
echo '<main role=main>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function bare_head(): void
|
|
||||||
{
|
|
||||||
echo '<!DOCTYPE html><html lang=en><head><title></title></head><body>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function page_foot(): void
|
function page_foot(): void
|
||||||
{
|
{
|
||||||
echo '</main>';
|
echo '</main>';
|
||||||
@ -71,8 +52,3 @@ function page_foot(): void
|
|||||||
}
|
}
|
||||||
echo '</body></html>';
|
echo '</body></html>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function bare_foot(): void
|
|
||||||
{
|
|
||||||
echo '</body></html>';
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user