CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Coding Standards (pull_request) Successful in 2m53s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / No Debug Code (pull_request) Successful in 3s
CI / PHPStan (pull_request) Successful in 3m12s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m36s
CI / Build Plugin Zip (pull_request) Skipped
Front end: the student "upcoming lessons" panel now shows each booked offering's name and length next to the time, and renders only the soonest five lessons with a "Show all" reveal. GET /bookings returns offering_title and duration_minutes so the list needs no extra request. Admin: the Scheduler and My Lessons week/list views now show the booked offering, and each lesson links to a detail view showing the policy versions the student accepted (with acceptance time and IP) and their intake answers. On My Lessons an instructor may only open their own lessons; the studio Scheduler may open any. composer test / composer lint / composer cs all pass. Co-Authored-By: Claude Opus 4.8 <[email protected]>
74 lines
2.5 KiB
PHP
74 lines
2.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Unsupervised\Schedular\Booking;
|
|
|
|
use Unsupervised\Schedular\Policy\AcceptanceRepository;
|
|
use Unsupervised\Schedular\Policy\PolicyAcceptance;
|
|
use Unsupervised\Schedular\Policy\PolicyRepository;
|
|
use Unsupervised\Schedular\Policy\PolicyVersionRepository;
|
|
use Unsupervised\Schedular\Registration\Answer;
|
|
use Unsupervised\Schedular\Registration\AnswerRepository;
|
|
use Unsupervised\Schedular\Registration\QuestionRepository;
|
|
|
|
/**
|
|
* Builds the display rows for the admin lesson detail view: the intake answers
|
|
* the student submitted and the policy versions they accepted when booking.
|
|
*
|
|
* Scoped to a single lesson (the `lesson` registration type), mirroring the
|
|
* per-student history in {@see \Unsupervised\Schedular\Auth\StudentHistory}.
|
|
*/
|
|
class LessonDetail {
|
|
|
|
public function __construct(
|
|
private AnswerRepository $answers,
|
|
private QuestionRepository $questions,
|
|
private AcceptanceRepository $acceptances,
|
|
private PolicyRepository $policies,
|
|
private PolicyVersionRepository $versions,
|
|
) {}
|
|
|
|
/**
|
|
* The intake-question answers recorded for this lesson, in submission order.
|
|
*
|
|
* @return list<array{question: string, answer: string}>
|
|
*/
|
|
public function answers( int $lessonId ): array {
|
|
return array_map(
|
|
function ( Answer $answer ): array {
|
|
$question = $this->questions->findById( $answer->questionId );
|
|
$value = $answer->answerValue ?? '';
|
|
|
|
return [
|
|
'question' => $question ? $question->label : sprintf( '#%d', $answer->questionId ),
|
|
'answer' => '' === $value ? '—' : $value,
|
|
];
|
|
},
|
|
$this->answers->findByRegistration( Answer::REG_LESSON, $lessonId )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* The policy versions the student accepted when booking this lesson, with the
|
|
* captured acceptance time and IP for the audit trail.
|
|
*
|
|
* @return list<array{policy: string, version: string, accepted_at: string, ip: string}>
|
|
*/
|
|
public function acceptances( int $lessonId ): array {
|
|
return array_map(
|
|
function ( PolicyAcceptance $acceptance ): array {
|
|
$version = $this->versions->findById( $acceptance->policyVersionId );
|
|
$policy = $version ? $this->policies->findById( $version->policyId ) : null;
|
|
|
|
return [
|
|
'policy' => $policy ? $policy->title : sprintf( '#%d', $acceptance->policyVersionId ),
|
|
'version' => $version ? sprintf( 'v%d', $version->versionNumber ) : '—',
|
|
'accepted_at' => $acceptance->acceptedAt ?? '',
|
|
'ip' => $acceptance->ipAddress ?? '',
|
|
];
|
|
},
|
|
$this->acceptances->findByRegistration( PolicyAcceptance::REG_LESSON, $lessonId )
|
|
);
|
|
}
|
|
}
|