Render requests (crudely)

This commit is contained in:
Daniel J. Summers 2023-11-19 20:15:29 -05:00
parent 5f425adc1d
commit f8b5902aa1
11 changed files with 57 additions and 54 deletions

View File

@ -9,7 +9,7 @@ use MyPrayerJournal\Domain\{ History, JournalRequest, Note, Request, RequestActi
class Data class Data
{ {
/** The prayer request table */ /** The prayer request table */
const REQ_TABLE = 'prayer_request'; const REQ_TABLE = 'mpj.request';
/** /**
* Ensure the table and index exist * Ensure the table and index exist
@ -94,11 +94,11 @@ class Data
*/ */
private static function getJournalByAnswered(string $userId, string $op): array private static function getJournalByAnswered(string $userId, string $op): array
{ {
$sql = Query::selectFromTable(self::REQ_TABLE) $sql = sprintf('%s WHERE %s AND %s', Query::selectFromTable(self::REQ_TABLE), Query::whereDataContains('$1'),
. ' WHERE ' . Query::whereDataContains('$1') . ' AND ' . Query::whereJsonPathMatches('$2'); Query::whereJsonPathMatches('$2'));
$params = [ $params = [
Query::jsonbDocParam([ 'userId' => $userId ]), Query::jsonbDocParam([ 'userId' => $userId ]),
sprintf("$.history[*].action ? (@ $op \"%s\")", RequestAction::Answered->name) sprintf("$.history[0].status ? (@ $op \"%s\")", RequestAction::Answered->name)
]; ];
return self::mapToJournalRequest( return self::mapToJournalRequest(
Document::customList($sql, $params, Request::class, Document::mapFromJson(...)), true); Document::customList($sql, $params, Request::class, Document::mapFromJson(...)), true);

View File

@ -32,19 +32,19 @@ enum DistanceFormat
public static function format(DistanceFormat $it, bool $singular = false): string public static function format(DistanceFormat $it, bool $singular = false): string
{ {
return match ($it) { return match ($it) {
self::LessThanXMinutes => $singular ? 'less than a minute' : 'less than %i minutes', self::LessThanXMinutes => $singular ? 'less than a minute' : 'less than %d minutes',
self::XMinutes => $singular ? 'a minute' : '%i minutes', self::XMinutes => $singular ? 'a minute' : '%d minutes',
self::AboutXHours => $singular ? 'about an hour' : 'about %i hours', self::AboutXHours => $singular ? 'about an hour' : 'about %d hours',
self::XHours => $singular ? 'an hour' : '%i hours', self::XHours => $singular ? 'an hour' : '%d hours',
self::XDays => $singular ? 'a day' : '%i days', self::XDays => $singular ? 'a day' : '%d days',
self::AboutXWeeks => $singular ? 'about a week' : 'about %i weeks', self::AboutXWeeks => $singular ? 'about a week' : 'about %d weeks',
self::XWeeks => $singular ? 'a week' : '%i weeks', self::XWeeks => $singular ? 'a week' : '%d weeks',
self::AboutXMonths => $singular ? 'about a month' : 'about %i months', self::AboutXMonths => $singular ? 'about a month' : 'about %d months',
self::XMonths => $singular ? 'a month' : '%i months', self::XMonths => $singular ? 'a month' : '%d months',
self::AboutXYears => $singular ? 'about a year' : 'about %i years', self::AboutXYears => $singular ? 'about a year' : 'about %d years',
self::XYears => $singular ? 'a year' : '%i years', self::XYears => $singular ? 'a year' : '%d years',
self::OverXYears => $singular ? 'over a year' : 'over %i years', self::OverXYears => $singular ? 'over a year' : 'over %d years',
self::AlmostXYears => $singular ? 'almost a year' : 'almost %i years', self::AlmostXYears => $singular ? 'almost a year' : 'almost %d years',
}; };
} }
} }

View File

@ -5,7 +5,7 @@ namespace MyPrayerJournal\Domain;
use DateTimeImmutable; use DateTimeImmutable;
trait AsOf class AsOf
{ {
/** The "as of" date/time */ /** The "as of" date/time */
public DateTimeImmutable $asOf; public DateTimeImmutable $asOf;

View File

@ -8,33 +8,31 @@ use DateTimeImmutable, DateTimeZone;
/** /**
* A record of action taken on a prayer request, including updates to its text * A record of action taken on a prayer request, including updates to its text
*/ */
class History class History extends AsOf
{ {
use AsOf;
/** The action taken that generated this history entry */ /** The action taken that generated this history entry */
public RequestAction $action = RequestAction::Created; public RequestAction $status = RequestAction::Created;
/** The text of the update, if applicable */ /** The text of the update, if applicable */
public ?string $text = null; public ?string $text = null;
public function __construct() public function __construct()
{ {
$this->asOf = new DateTimeImmutable('1/1/1970', new DateTimeZone('Etc/UTC')); $this->asOf = unix_epoch();
} }
public function isCreated(): bool public function isCreated(): bool
{ {
return $this->action == RequestAction::Created; return $this->status == RequestAction::Created;
} }
public function isPrayed(): bool public function isPrayed(): bool
{ {
return $this->action == RequestAction::Prayed; return $this->status == RequestAction::Prayed;
} }
public function isAnswered(): bool public function isAnswered(): bool
{ {
return $this->action == RequestAction::Answered; return $this->status == RequestAction::Answered;
} }
} }

View File

@ -8,10 +8,8 @@ use DateTimeImmutable, DateTimeZone;
/** /**
* A prayer request, along with calculated fields, for use in displaying journal lists * A prayer request, along with calculated fields, for use in displaying journal lists
*/ */
class JournalRequest class JournalRequest extends AsOf
{ {
use AsOf;
/** The ID of the prayer request */ /** The ID of the prayer request */
public string $id = ''; public string $id = '';
@ -22,7 +20,7 @@ class JournalRequest
public string $text = ''; public string $text = '';
/** The date/time this request was last marked as prayed */ /** The date/time this request was last marked as prayed */
public DateTimeImmutable $lastPrayed; public ?DateTimeImmutable $lastPrayed = null;
/** The last action taken on this request */ /** The last action taken on this request */
public RequestAction $lastAction = RequestAction::Created; public RequestAction $lastAction = RequestAction::Created;
@ -60,8 +58,8 @@ class JournalRequest
public function __construct(?Request $req = null, bool $full = false) public function __construct(?Request $req = null, bool $full = false)
{ {
if (is_null($req)) { if (is_null($req)) {
$this->asOf = new DateTimeImmutable('1/1/1970', new DateTimeZone('Etc/UTC')); $this->asOf = unix_epoch();
$this->lastPrayed = new DateTimeImmutable('1/1/1970', new DateTimeZone('Etc/UTC')); $this->lastPrayed = null;
} else { } else {
$this->id = $req->id; $this->id = $req->id;
$this->userId = $req->userId; $this->userId = $req->userId;
@ -71,9 +69,11 @@ class JournalRequest
$this->recurrence = $req->recurrence; $this->recurrence = $req->recurrence;
usort($req->history, AsOf::newestToOldest(...)); usort($req->history, AsOf::newestToOldest(...));
$this->asOf = $req->history[0]->asOf; $this->asOf = $req->history[array_key_first($req->history)]->asOf;
$this->lastPrayed = array_values( $lastText = array_filter($req->history, fn (History $it) => !is_null($it->text));
array_filter($req->history, fn (History $it) => $it->isPrayed()))[0]?->asOf; $this->text = $lastText[array_key_first($lastText)]->text;
$lastPrayed = array_filter($req->history, fn (History $it) => $it->isPrayed());
if ($lastPrayed) $this->lastPrayed = $lastPrayed[array_key_first($lastPrayed)]->asOf;
if ($full) { if ($full) {
usort($req->notes, AsOf::newestToOldest(...)); usort($req->notes, AsOf::newestToOldest(...));

View File

@ -8,15 +8,13 @@ use DateTimeImmutable, DateTimeZone;
/** /**
* A note entered on a prayer request * A note entered on a prayer request
*/ */
class Note class Note extends AsOf
{ {
use AsOf;
/** The note */ /** The note */
public string $notes = ''; public string $notes = '';
public function __construct() public function __construct()
{ {
$this->asOf = new DateTimeImmutable('1/1/1970', new DateTimeZone('Etc/UTC')); $this->asOf = unix_epoch();
} }
} }

View File

@ -46,7 +46,7 @@ class Request
public function __construct() public function __construct()
{ {
$this->id = new Cuid2(); $this->id = (new Cuid2())->toString();
$this->enteredOn = new DateTimeImmutable('1/1/1970', new DateTimeZone('Etc/UTC')); $this->enteredOn = unix_epoch();
} }
} }

View File

@ -8,19 +8,19 @@ use JsonSerializable;
/** /**
* An action that was taken on a request * An action that was taken on a request
*/ */
enum RequestAction implements JsonSerializable enum RequestAction: string implements JsonSerializable
{ {
/** The request was entered */ /** The request was entered */
case Created; case Created = 'Created';
/** Prayer was recorded for the request */ /** Prayer was recorded for the request */
case Prayed; case Prayed = 'Prayed';
/** The request was updated */ /** The request was updated */
case Updated; case Updated = 'Updated';
/** The request was marked as answered */ /** The request was marked as answered */
case Answered; case Answered = 'Answered';
/** /**
* Serialize this enum using its name * Serialize this enum using its name

View File

@ -55,7 +55,7 @@ function require_user(bool $fail = false)
if ($fail) { if ($fail) {
http_response_code(403); http_response_code(403);
} else { } else {
header("Location: /user/log-on?{${Constants::RETURN_URL}}={$_SERVER[Constants::REQUEST_URI]}"); header(sprintf('Location: /user/log-on?%s=%s', Constants::RETURN_URL, $_SERVER[Constants::REQUEST_URI]));
} }
exit; exit;
} }
@ -83,7 +83,7 @@ function page_link(string $url, array $classNames = [], bool $checkActive = fals
array_push($classNames, 'is-active-route'); array_push($classNames, 'is-active-route');
} }
if (!empty($classNames)) { if (!empty($classNames)) {
echo ' class="' . implode(' ', $classNames) . '"'; echo sprintf(' class="%s"', implode(' ', $classNames));
} }
echo ' hx-target="#top" hx-swap="innerHTML" hx-push-url="true"'; echo ' hx-target="#top" hx-swap="innerHTML" hx-push-url="true"';
} }
@ -96,3 +96,13 @@ function end_request()
Configuration::closeConn(); Configuration::closeConn();
echo '</body></html>'; echo '</body></html>';
} }
/**
* Create a new instance of the Unix epoch
*
* @return DateTimeImmutable An immutable date/time as of the Unix epoch
*/
function unix_epoch(): DateTimeImmutable
{
return new DateTimeImmutable('1/1/1970', new DateTimeZone('Etc/UTC'));
}

View File

@ -40,11 +40,9 @@ end_request();
*/ */
function format_activity(string $activity, DateTimeImmutable $asOf) function format_activity(string $activity, DateTimeImmutable $asOf)
{ {
echo sprintf('last %s <span title="%s">%s</span>', [ echo sprintf('last %s <span title="%s">%s</span>', $activity,
$activity,
$asOf->setTimezone($_REQUEST[Constants::TIME_ZONE])->format('l, F jS, Y/g:ia T'), $asOf->setTimezone($_REQUEST[Constants::TIME_ZONE])->format('l, F jS, Y/g:ia T'),
Dates::formatDistance(Dates::now(), $asOf) Dates::formatDistance(Dates::now(), $asOf));
]);
} }
/** /**

View File

@ -11,11 +11,10 @@ $_REQUEST[Constants::PAGE_TITLE] = "{$session->user[Constants::CLAIM_GIVEN_NAME]
template('layout/page_header'); ?> template('layout/page_header'); ?>
<main class="container"> <main class="container">
<h2 class="title"><?php echo $_REQUEST[Constants::PAGE_TITLE]; ?>&rsquo;s Prayer Journal</h2> <h2 class="title"><?php echo $_REQUEST[Constants::PAGE_TITLE]; ?></h2>
<p hx-get="/components/journal-items" hx-swap="outerHTML" hx-trigger="load delay:.25s"> <p hx-get="/components/journal-items" hx-swap="outerHTML" hx-trigger="load delay:.25s">
Loading your prayer journal&hellip; Loading your prayer journal&hellip;
</p> </p>
<pre><?php var_dump($_SERVER); ?></pre>
</main><?php </main><?php
template('layout/page_footer'); template('layout/page_footer');
end_request(); end_request();