inti project

This commit is contained in:
root
2026-05-29 04:33:03 -04:00
commit cdeab1796f
699 changed files with 20516 additions and 0 deletions
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Attendance;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Contracts\AttendancePolicyContract;
use DateTimeInterface;
final class IslamicSundaySchoolAttendancePolicy implements AttendancePolicyContract
{
public function isSchoolDay(SchoolContext $context, DateTimeInterface $date): bool
{
return (int) $date->format('N') === 7;
}
public function determineStatus(SchoolContext $context, DateTimeInterface $scanTime, array $session): string
{
$startsAt = $session['starts_at'] ?? null;
if ($startsAt instanceof DateTimeInterface && $scanTime > $startsAt) {
return 'late';
}
return 'present';
}
public function duplicateScanWindowSeconds(SchoolContext $context): int
{
return 600;
}
}
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Attendance\Policies;
use App\Domain\SchoolCore\Attendance\Policies\DefaultAttendancePolicy;
use App\Domain\SchoolCore\Context\SchoolContext;
final class IslamicSundaySchoolAttendancePolicy extends DefaultAttendancePolicy
{
public function duplicateScanWindowSeconds(SchoolContext $context): int
{
return 180;
}
}
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Attendance\Policies;
use App\Domain\SchoolCore\Attendance\Contracts\AttendanceStatusPolicyContract;
use App\Domain\SchoolCore\Attendance\DTO\AttendanceSessionView;
use App\Domain\SchoolCore\Attendance\DTO\AttendanceStatusResult;
use App\Domain\SchoolCore\Context\SchoolContext;
use DateTimeInterface;
final class IslamicSundaySchoolAttendanceStatusPolicy implements AttendanceStatusPolicyContract
{
public function statusForScan(SchoolContext $context, AttendanceSessionView $session, DateTimeInterface $timestamp): AttendanceStatusResult
{
$lateThreshold = $session->startsAt->modify('+15 minutes');
$status = $timestamp > $lateThreshold ? 'late' : 'present';
return new AttendanceStatusResult($status, $status === 'late', $status === 'late' ? 'Configured extension late window.' : null);
}
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Attendance\Policies;
final class IslamicSundaySchoolSessionPolicy
{
public function supportsProgramSessionType(string $type): bool
{
return in_array($type, ['program', 'group', 'class', 'event'], true);
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Attendance\Providers;
use App\Domain\IslamicSundaySchool\Attendance\Policies\IslamicSundaySchoolAttendancePolicy;
use App\Domain\IslamicSundaySchool\Attendance\Policies\IslamicSundaySchoolAttendanceStatusPolicy;
use App\Domain\IslamicSundaySchool\Attendance\Services\IslamicSundaySchoolSessionResolver;
use App\Domain\SchoolCore\Attendance\Contracts\AttendancePolicyContract;
use App\Domain\SchoolCore\Attendance\Contracts\AttendanceSessionResolverContract;
use App\Domain\SchoolCore\Attendance\Contracts\AttendanceStatusPolicyContract;
use Illuminate\Support\ServiceProvider;
final class IslamicSundaySchoolAttendanceServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(AttendancePolicyContract::class, IslamicSundaySchoolAttendancePolicy::class);
$this->app->bind(AttendanceStatusPolicyContract::class, IslamicSundaySchoolAttendanceStatusPolicy::class);
$this->app->bind(AttendanceSessionResolverContract::class, IslamicSundaySchoolSessionResolver::class);
}
}
@@ -0,0 +1,3 @@
# Attendance
Extension module. Bind policies/providers here without leaking implementation details into SchoolCore.
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Attendance\Reports;
final class IslamicSundaySchoolAttendanceReportBuilder
{
public function labels(): array
{
return [
'present' => 'Present',
'late' => 'Late',
'absent' => 'Absent',
];
}
}
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Attendance\Services;
use App\Domain\SchoolCore\Attendance\DTO\AttendanceSessionView;
use DateTimeInterface;
final class IslamicSundaySchoolLateArrivalEvaluator
{
public function isLate(AttendanceSessionView $session, DateTimeInterface $timestamp): bool
{
return $timestamp > $session->startsAt->modify('+15 minutes');
}
}
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Attendance\Services;
use App\Domain\SchoolCore\Attendance\Contracts\AttendanceSessionResolverContract;
use App\Domain\SchoolCore\Attendance\DTO\AttendanceSubjectView;
use App\Domain\SchoolCore\Attendance\DTO\AttendanceSessionView;
use App\Domain\SchoolCore\Attendance\Services\AttendanceSessionResolver;
use App\Domain\SchoolCore\Context\SchoolContext;
use DateTimeInterface;
final class IslamicSundaySchoolSessionResolver implements AttendanceSessionResolverContract
{
public function __construct(private AttendanceSessionResolver $fallback) {}
public function resolveForScan(SchoolContext $context, AttendanceSubjectView $subject, DateTimeInterface $timestamp): AttendanceSessionView
{
return $this->fallback->resolveForScan($context, $subject, $timestamp);
}
}
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Policies; use App\Domain\SchoolCore\Communication\Policies\DefaultCommunicationPreferencePolicy; final class IslamicSundaySchoolCommunicationPreferencePolicy extends DefaultCommunicationPreferencePolicy {}
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Policies; final class IslamicSundaySchoolRecipientAuthorizationPolicy {}
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Providers; use Illuminate\Support\ServiceProvider; use App\Domain\IslamicSundaySchool\Communication\Resolvers\{SundayClassRecipientResolver,VolunteerTeamRecipientResolver,MasjidFamilyRecipientResolver,VolunteerRecipientResolver,IslamicSundaySchoolProgramRecipientResolver}; final class IslamicSundaySchoolCommunicationServiceProvider extends ServiceProvider { public function register(): void { $this->app->tag([SundayClassRecipientResolver::class,VolunteerTeamRecipientResolver::class,MasjidFamilyRecipientResolver::class,VolunteerRecipientResolver::class,IslamicSundaySchoolProgramRecipientResolver::class], 'communication.recipient_resolvers'); } }
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Resolvers; use App\Domain\SchoolCore\Context\SchoolContext; use App\Domain\SchoolCore\Communication\Contracts\RecipientResolverContract; use App\Domain\SchoolCore\Communication\DTO\{RecipientData,RecipientQueryData}; use App\Domain\SchoolCore\Communication\Support\RecipientSet; final class IslamicSundaySchoolProgramRecipientResolver implements RecipientResolverContract { public function supports(RecipientQueryData $query): bool { return $query->audienceType === 'program_attendees'; } public function resolve(SchoolContext $context, RecipientQueryData $query): RecipientSet { return new RecipientSet([new RecipientData('guardian',$context->schoolId*2000+1,'Extension Recipient','program_attendees@example.test','+15555550200',$query->language,[],null,null,$context->schoolId,[$query->channel->value],[],['extension'=>'islamic_sunday_school'],['program_attendees'])]); } }
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Resolvers; use App\Domain\SchoolCore\Context\SchoolContext; use App\Domain\SchoolCore\Communication\Contracts\RecipientResolverContract; use App\Domain\SchoolCore\Communication\DTO\{RecipientData,RecipientQueryData}; use App\Domain\SchoolCore\Communication\Support\RecipientSet; final class MasjidFamilyRecipientResolver implements RecipientResolverContract { public function supports(RecipientQueryData $query): bool { return $query->audienceType === 'masjid_family'; } public function resolve(SchoolContext $context, RecipientQueryData $query): RecipientSet { return new RecipientSet([new RecipientData('guardian',$context->schoolId*2000+1,'Extension Recipient','masjid_family@example.test','+15555550200',$query->language,[],null,null,$context->schoolId,[$query->channel->value],[],['extension'=>'islamic_sunday_school'],['masjid_family'])]); } }
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Resolvers; use App\Domain\SchoolCore\Context\SchoolContext; use App\Domain\SchoolCore\Communication\Contracts\RecipientResolverContract; use App\Domain\SchoolCore\Communication\DTO\{RecipientData,RecipientQueryData}; use App\Domain\SchoolCore\Communication\Support\RecipientSet; final class SundayClassRecipientResolver implements RecipientResolverContract { public function supports(RecipientQueryData $query): bool { return $query->audienceType === 'sunday_class'; } public function resolve(SchoolContext $context, RecipientQueryData $query): RecipientSet { return new RecipientSet([new RecipientData('guardian',$context->schoolId*2000+1,'Extension Recipient','sunday_class@example.test','+15555550200',$query->language,[],null,null,$context->schoolId,[$query->channel->value],[],['extension'=>'islamic_sunday_school'],['sunday_class'])]); } }
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Resolvers; use App\Domain\SchoolCore\Context\SchoolContext; use App\Domain\SchoolCore\Communication\Contracts\RecipientResolverContract; use App\Domain\SchoolCore\Communication\DTO\{RecipientData,RecipientQueryData}; use App\Domain\SchoolCore\Communication\Support\RecipientSet; final class VolunteerRecipientResolver implements RecipientResolverContract { public function supports(RecipientQueryData $query): bool { return $query->audienceType === 'volunteers'; } public function resolve(SchoolContext $context, RecipientQueryData $query): RecipientSet { return new RecipientSet([new RecipientData('guardian',$context->schoolId*2000+1,'Extension Recipient','volunteers@example.test','+15555550200',$query->language,[],null,null,$context->schoolId,[$query->channel->value],[],['extension'=>'islamic_sunday_school'],['volunteers'])]); } }
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Resolvers; use App\Domain\SchoolCore\Context\SchoolContext; use App\Domain\SchoolCore\Communication\Contracts\RecipientResolverContract; use App\Domain\SchoolCore\Communication\DTO\{RecipientData,RecipientQueryData}; use App\Domain\SchoolCore\Communication\Support\RecipientSet; final class VolunteerTeamRecipientResolver implements RecipientResolverContract { public function supports(RecipientQueryData $query): bool { return $query->audienceType === 'volunteer_team'; } public function resolve(SchoolContext $context, RecipientQueryData $query): RecipientSet { return new RecipientSet([new RecipientData('guardian',$context->schoolId*2000+1,'Extension Recipient','volunteer_team@example.test','+15555550200',$query->language,[],null,null,$context->schoolId,[$query->channel->value],[],['extension'=>'islamic_sunday_school'],['volunteer_team'])]); } }
@@ -0,0 +1 @@
<?php declare(strict_types=1); namespace App\Domain\IslamicSundaySchool\Communication\Templates; final class IslamicSundaySchoolTemplateCatalog { public function templates(): array { return ['program_class_reminder'=>['category'=>'event'], 'volunteer_reminder'=>['category'=>'event'], 'attendance_follow_up'=>['category'=>'attendance']]; } }
@@ -0,0 +1,3 @@
# Contracts
Extension module. Bind policies/providers here without leaking implementation details into SchoolCore.
@@ -0,0 +1,3 @@
# Curriculum
Extension module. Bind policies/providers here without leaking implementation details into SchoolCore.
@@ -0,0 +1,3 @@
# Families
Extension module. Bind policies/providers here without leaking implementation details into SchoolCore.
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Finance\Policies;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Finance\Contracts\PaymentAllocationPolicyContract;
use App\Domain\SchoolCore\Finance\Support\Money;
final class IslamicSundaySchoolPaymentAllocationPolicy implements PaymentAllocationPolicyContract
{
public function allocate(SchoolContext $context, array $invoiceView, Money $paymentAmount): array
{
return [['invoice_id'=>$invoiceView['id'] ?? null,'amount_minor'=>$paymentAmount->minorAmount(),'currency'=>$paymentAmount->currency(),'strategy'=>'extension_policy_family_program_order']];
}
}
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Finance\Policies;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Finance\Contracts\TuitionPolicyContract;
final class IslamicSundaySchoolTuitionPolicy implements TuitionPolicyContract
{
public function calculateExpectedCharges(SchoolContext $context, array $studentFinanceProfile): array
{
return ['profile'=>$context->domainProfile,'charges'=>[],'labels_are_extension_owned'=>true];
}
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Finance\Providers;
use App\Domain\IslamicSundaySchool\Finance\Policies\IslamicSundaySchoolPaymentAllocationPolicy;
use App\Domain\IslamicSundaySchool\Finance\Policies\IslamicSundaySchoolTuitionPolicy;
use App\Domain\SchoolCore\Finance\Contracts\PaymentAllocationPolicyContract;
use App\Domain\SchoolCore\Finance\Contracts\TuitionPolicyContract;
use Illuminate\Support\ServiceProvider;
final class IslamicSundaySchoolFinanceServiceProvider extends ServiceProvider
{
public function register(): void
{
if (! (bool) config('school_context.extensions.islamic_sunday_school.enabled', false)) { return; }
$this->app->bind(TuitionPolicyContract::class, IslamicSundaySchoolTuitionPolicy::class);
$this->app->bind(PaymentAllocationPolicyContract::class, IslamicSundaySchoolPaymentAllocationPolicy::class);
}
}
@@ -0,0 +1,3 @@
# Islamic Sunday School Finance Extension
Owns extension labels, tuition policies, allocation policies, scholarships, and local reports. It depends on SchoolCore Finance contracts. SchoolCore Finance must not depend back on this namespace.
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Finance\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Finance\Repositories\FinanceReadRepository;
final class IslamicSundaySchoolFinanceReportBuilder
{
public function __construct(private readonly FinanceReadRepository $finance) {}
public function outstandingBalances(SchoolContext $context): array
{
return ['domain_profile'=>$context->domainProfile,'rows'=>$this->finance->outstandingInvoices($context)];
}
}
@@ -0,0 +1,3 @@
# Ministry
Extension module. Bind policies/providers here without leaking implementation details into SchoolCore.
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Policies;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Contracts\PaymentAllocationPolicyContract;
final class IslamicSundaySchoolPaymentAllocationPolicy implements PaymentAllocationPolicyContract
{
public function allocate(SchoolContext $context, int $invoiceId, float $amount): array
{
return [['invoice_id' => $invoiceId, 'amount' => $amount, 'allocation_rule' => 'extension_default']];
}
}
@@ -0,0 +1,3 @@
# Policies
Extension module. Bind policies/providers here without leaking implementation details into SchoolCore.
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Providers;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Contracts\AcademicCalendarProviderContract;
use DateTimeInterface;
final class IslamicSundaySchoolCalendarProvider implements AcademicCalendarProviderContract
{
public function currentAcademicYear(SchoolContext $context): ?int
{
return is_numeric($context->schoolYear) ? (int) $context->schoolYear : null;
}
public function currentTerm(SchoolContext $context): ?int
{
return is_numeric($context->term) ? (int) $context->term : null;
}
public function sessionsForDate(SchoolContext $context, DateTimeInterface $date): array
{
return [];
}
}
@@ -0,0 +1,3 @@
# Providers
Extension module. Bind policies/providers here without leaking implementation details into SchoolCore.
@@ -0,0 +1,3 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\ExportTemplates;
final class FamilySummaryExportTemplate {}
@@ -0,0 +1,3 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\ExportTemplates;
final class QuranProgressExportTemplate {}
@@ -0,0 +1,22 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Providers;
use Illuminate\Support\ServiceProvider;
use App\Domain\IslamicSundaySchool\Reporting\Reports\{QuranProgressReport,ArabicLevelReport,IslamicStudiesProgressReport,HalaqaAttendanceReport,SundayProgramAttendanceReport,MasjidFamilySummaryReport,VolunteerTeamReport,IslamicSundaySchoolDashboardReport};
final class IslamicSundaySchoolReportingServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->tag([
QuranProgressReport::class,
ArabicLevelReport::class,
IslamicStudiesProgressReport::class,
HalaqaAttendanceReport::class,
SundayProgramAttendanceReport::class,
MasjidFamilySummaryReport::class,
VolunteerTeamReport::class,
IslamicSundaySchoolDashboardReport::class,
], 'schoolcore.reports');
}
}
@@ -0,0 +1,4 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\ReadModels;
use App\Domain\SchoolCore\Context\SchoolContext;
final class ArabicProgressReadModel { public function rows(SchoolContext $context, array $filters = []): array { return []; } }
@@ -0,0 +1,4 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\ReadModels;
use App\Domain\SchoolCore\Context\SchoolContext;
final class HalaqaReportReadModel { public function rows(SchoolContext $context, array $filters = []): array { return []; } }
@@ -0,0 +1,4 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\ReadModels;
use App\Domain\SchoolCore\Context\SchoolContext;
final class IslamicStudiesProgressReadModel { public function rows(SchoolContext $context, array $filters = []): array { return []; } }
@@ -0,0 +1,4 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\ReadModels;
use App\Domain\SchoolCore\Context\SchoolContext;
final class MasjidFamilyReportReadModel { public function rows(SchoolContext $context, array $filters = []): array { return []; } }
@@ -0,0 +1,4 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\ReadModels;
use App\Domain\SchoolCore\Context\SchoolContext;
final class QuranProgressReadModel { public function rows(SchoolContext $context, array $filters = []): array { return []; } }
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Reporting\DTO\ReportResultData;
final class ArabicLevelReport extends BaseIslamicSundaySchoolReport
{
public function key(): string { return 'islamic.arabic_level'; }
public function name(): string { return 'Arabic Level'; }
public function columns(): array { return [['key' => 'note', 'label' => 'Note', 'sensitive' => false]]; }
public function sensitiveColumns(): array { return ['sensitive_notes']; }
public function run(SchoolContext $context, array $filters = []): ReportResultData
{
return new ReportResultData($this->key(), $this->columns(), [[
'school_id' => $context->schoolId,
'report_key' => $this->key(),
'note' => 'Extension report scaffold. Wire this to Islamic Sunday School read models.',
]], null, null, null, 1, gmdate('c'));
}
}
@@ -0,0 +1,12 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Reporting\Enums\ReportCategory;
use App\Domain\SchoolCore\Reporting\Reports\BaseReportDefinition;
abstract class BaseIslamicSundaySchoolReport extends BaseReportDefinition
{
public function category(): ReportCategory { return ReportCategory::EXTENSION; }
public function availableForDomainProfile(?string $domainProfile): bool { return $domainProfile === 'islamic_sunday_school'; }
public function requiredPermissions(): array { return ['report.view', 'report.islamic.view']; }
}
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Reporting\DTO\ReportResultData;
final class HalaqaAttendanceReport extends BaseIslamicSundaySchoolReport
{
public function key(): string { return 'islamic.halaqa_attendance'; }
public function name(): string { return 'Halaqa Attendance'; }
public function columns(): array { return [['key' => 'note', 'label' => 'Note', 'sensitive' => false]]; }
public function sensitiveColumns(): array { return ['sensitive_notes']; }
public function run(SchoolContext $context, array $filters = []): ReportResultData
{
return new ReportResultData($this->key(), $this->columns(), [[
'school_id' => $context->schoolId,
'report_key' => $this->key(),
'note' => 'Extension report scaffold. Wire this to Islamic Sunday School read models.',
]], null, null, null, 1, gmdate('c'));
}
}
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Reporting\DTO\ReportResultData;
final class IslamicStudiesProgressReport extends BaseIslamicSundaySchoolReport
{
public function key(): string { return 'islamic.studies_progress'; }
public function name(): string { return 'Islamic Studies Progress'; }
public function columns(): array { return [['key' => 'note', 'label' => 'Note', 'sensitive' => false]]; }
public function sensitiveColumns(): array { return ['sensitive_notes']; }
public function run(SchoolContext $context, array $filters = []): ReportResultData
{
return new ReportResultData($this->key(), $this->columns(), [[
'school_id' => $context->schoolId,
'report_key' => $this->key(),
'note' => 'Extension report scaffold. Wire this to Islamic Sunday School read models.',
]], null, null, null, 1, gmdate('c'));
}
}
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Reporting\DTO\ReportResultData;
final class IslamicSundaySchoolDashboardReport extends BaseIslamicSundaySchoolReport
{
public function key(): string { return 'islamic.dashboard'; }
public function name(): string { return 'Islamic Sunday School Dashboard'; }
public function columns(): array { return [['key' => 'note', 'label' => 'Note', 'sensitive' => false]]; }
public function sensitiveColumns(): array { return ['sensitive_notes']; }
public function run(SchoolContext $context, array $filters = []): ReportResultData
{
return new ReportResultData($this->key(), $this->columns(), [[
'school_id' => $context->schoolId,
'report_key' => $this->key(),
'note' => 'Extension report scaffold. Wire this to Islamic Sunday School read models.',
]], null, null, null, 1, gmdate('c'));
}
}
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Reporting\DTO\ReportResultData;
final class MasjidFamilySummaryReport extends BaseIslamicSundaySchoolReport
{
public function key(): string { return 'islamic.masjid_family_summary'; }
public function name(): string { return 'Masjid Family Summary'; }
public function columns(): array { return [['key' => 'note', 'label' => 'Note', 'sensitive' => false]]; }
public function sensitiveColumns(): array { return ['sensitive_notes']; }
public function run(SchoolContext $context, array $filters = []): ReportResultData
{
return new ReportResultData($this->key(), $this->columns(), [[
'school_id' => $context->schoolId,
'report_key' => $this->key(),
'note' => 'Extension report scaffold. Wire this to Islamic Sunday School read models.',
]], null, null, null, 1, gmdate('c'));
}
}
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Reporting\DTO\ReportResultData;
final class QuranProgressReport extends BaseIslamicSundaySchoolReport
{
public function key(): string { return 'islamic.quran_progress'; }
public function name(): string { return "Qur'an Progress"; }
public function columns(): array { return [['key' => 'note', 'label' => 'Note', 'sensitive' => false]]; }
public function sensitiveColumns(): array { return ['sensitive_notes']; }
public function run(SchoolContext $context, array $filters = []): ReportResultData
{
return new ReportResultData($this->key(), $this->columns(), [[
'school_id' => $context->schoolId,
'report_key' => $this->key(),
'note' => 'Extension report scaffold. Wire this to Islamic Sunday School read models.',
]], null, null, null, 1, gmdate('c'));
}
}
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Reporting\DTO\ReportResultData;
final class SundayProgramAttendanceReport extends BaseIslamicSundaySchoolReport
{
public function key(): string { return 'islamic.sunday_program_attendance'; }
public function name(): string { return 'Sunday Program Attendance'; }
public function columns(): array { return [['key' => 'note', 'label' => 'Note', 'sensitive' => false]]; }
public function sensitiveColumns(): array { return ['sensitive_notes']; }
public function run(SchoolContext $context, array $filters = []): ReportResultData
{
return new ReportResultData($this->key(), $this->columns(), [[
'school_id' => $context->schoolId,
'report_key' => $this->key(),
'note' => 'Extension report scaffold. Wire this to Islamic Sunday School read models.',
]], null, null, null, 1, gmdate('c'));
}
}
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Reporting\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
use App\Domain\SchoolCore\Reporting\DTO\ReportResultData;
final class VolunteerTeamReport extends BaseIslamicSundaySchoolReport
{
public function key(): string { return 'islamic.volunteer_team'; }
public function name(): string { return 'Volunteer Team'; }
public function columns(): array { return [['key' => 'note', 'label' => 'Note', 'sensitive' => false]]; }
public function sensitiveColumns(): array { return ['sensitive_notes']; }
public function run(SchoolContext $context, array $filters = []): ReportResultData
{
return new ReportResultData($this->key(), $this->columns(), [[
'school_id' => $context->schoolId,
'report_key' => $this->key(),
'note' => 'Extension report scaffold. Wire this to Islamic Sunday School read models.',
]], null, null, null, 1, gmdate('c'));
}
}
@@ -0,0 +1,3 @@
# Reports
Extension module. Bind policies/providers here without leaking implementation details into SchoolCore.
@@ -0,0 +1,4 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Students\DTO;
final readonly class IslamicSundaySchoolStudentProfileData { public function __construct(public int $studentId, public ?int $familyProfileId=null, public ?int $quranLevelId=null, public ?int $arabicLevelId=null, public ?int $islamicStudiesTrackId=null, public ?int $halaqaGroupId=null, public ?string $memorizationProgressSummary=null, public ?string $recitationPlacement=null, public bool $volunteerFamilyParticipation=false, public ?string $sensitiveAdminNotes=null, public array $metadata=[]) {} }
@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Students\Policies;
use App\Domain\SchoolCore\Context\SchoolContext; use App\Domain\SchoolCore\Students\DTO\EnrollmentData;
final class IslamicSundaySchoolEnrollmentPolicy { public function canEnroll(SchoolContext $context, EnrollmentData $data): bool { return $context->isIslamicSundaySchool() && $data->studentId>0; } }
@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Students\Policies;
use App\Domain\SchoolCore\Context\SchoolContext; use App\Domain\SchoolCore\Students\Contracts\PromotionPolicyContract; use App\Domain\SchoolCore\Students\DTO\EnrollmentView; use App\Domain\SchoolCore\Students\DTO\PromotionEvaluationResult; use App\Domain\SchoolCore\Students\DTO\StudentView;
final class IslamicSundaySchoolPromotionPolicy implements PromotionPolicyContract { public function evaluate(SchoolContext $context, StudentView $student, EnrollmentView $enrollment): PromotionEvaluationResult { return new PromotionEvaluationResult($context->isIslamicSundaySchool() && $student->status==='active',$student->status==='active'?'promoted':'manual_review'); } }
@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Students\Policies;
use App\Domain\SchoolCore\Context\SchoolContext; use App\Domain\SchoolCore\Students\Contracts\StudentIdentifierGeneratorContract; use App\Domain\SchoolCore\Students\DTO\StudentIdentifier; use App\Domain\SchoolCore\Students\DTO\StudentView;
final class IslamicSundaySchoolStudentIdentifierGenerator implements StudentIdentifierGeneratorContract { public function generateForPersistedStudent(SchoolContext $context, StudentView $student): StudentIdentifier { return new StudentIdentifier(sprintf('SS-%s-%06d',date('Y'),$student->id)); } }
@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Students\Providers;
use App\Domain\IslamicSundaySchool\Students\Policies\IslamicSundaySchoolPromotionPolicy; use App\Domain\IslamicSundaySchool\Students\Policies\IslamicSundaySchoolStudentIdentifierGenerator; use App\Domain\SchoolCore\Students\Contracts\PromotionPolicyContract; use App\Domain\SchoolCore\Students\Contracts\StudentIdentifierGeneratorContract; use Illuminate\Support\ServiceProvider;
final class IslamicSundaySchoolStudentServiceProvider extends ServiceProvider { public function register(): void { if((bool)config('school_context.extensions.islamic_sunday_school.students',false)){ $this->app->bind(StudentIdentifierGeneratorContract::class,IslamicSundaySchoolStudentIdentifierGenerator::class); $this->app->bind(PromotionPolicyContract::class,IslamicSundaySchoolPromotionPolicy::class); } } }
@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Students\Reports;
use App\Domain\SchoolCore\Context\SchoolContext;
final class IslamicSundaySchoolStudentReportBuilder { public function buildSummary(SchoolContext $context,array $filters=[]): array { return ['domain_profile'=>$context->domainProfile,'filters'=>$filters,'labels'=>['quran_level','arabic_level','islamic_studies_track']]; } }
@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Students\Services;
use App\Domain\SchoolCore\Context\SchoolContext;
final class IslamicSundaySchoolFamilyProfileService { public function summarizeForStudent(SchoolContext $context,int $studentId): array { return ['school_id'=>$context->schoolId,'student_id'=>$studentId,'extension'=>'islamic_sunday_school']; } }
@@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Domain\IslamicSundaySchool\Students\Services;
use App\Domain\IslamicSundaySchool\Students\DTO\IslamicSundaySchoolStudentProfileData; use App\Domain\SchoolCore\Context\SchoolContext; use Illuminate\Support\Facades\DB;
final class IslamicSundaySchoolStudentProfileService { public function save(SchoolContext $context, IslamicSundaySchoolStudentProfileData $data): array { DB::table('islamic_sunday_school_student_profiles')->updateOrInsert(['school_id'=>$context->schoolId,'student_id'=>$data->studentId],['family_profile_id'=>$data->familyProfileId,'quran_level_id'=>$data->quranLevelId,'arabic_level_id'=>$data->arabicLevelId,'islamic_studies_track_id'=>$data->islamicStudiesTrackId,'halaqa_group_id'=>$data->halaqaGroupId,'memorization_progress_summary'=>$data->memorizationProgressSummary,'recitation_placement'=>$data->recitationPlacement,'volunteer_family_participation'=>$data->volunteerFamilyParticipation,'sensitive_admin_notes'=>$data->sensitiveAdminNotes,'metadata'=>json_encode($data->metadata),'updated_at'=>now()]); return ['student_id'=>$data->studentId,'profile'=>'islamic_sunday_school']; } }