diff --git a/app-modules/analytics/composer.json b/app-modules/analytics/composer.json index c90f3340de..27d25b6af8 100644 --- a/app-modules/analytics/composer.json +++ b/app-modules/analytics/composer.json @@ -1,5 +1,5 @@ { - "name": "canyon-gbs/analytics", + "name": "canyon-gbs/advising-app-analytics", "description": "", "type": "library", "version": "1.0", diff --git a/app-modules/caseload-management/database/factories/CaseloadFactory.php b/app-modules/caseload-management/database/factories/CaseloadFactory.php index 9d42e2317a..0373534407 100644 --- a/app-modules/caseload-management/database/factories/CaseloadFactory.php +++ b/app-modules/caseload-management/database/factories/CaseloadFactory.php @@ -56,13 +56,7 @@ public function definition(): array 'name' => fake()->words(asText: true), 'model' => fake()->randomElement(CaseloadModel::cases()), 'type' => CaseloadType::Dynamic, //TODO: add static later + 'user_id' => User::inRandomOrder()->first()?->getKey() ?? User::factory()->create()?->getKey(), ]; } - - public function configure(): CaseloadFactory|Factory - { - return $this->afterMaking(function (Caseload $caseload) { - $caseload->user()->associate(User::inRandomOrder()->first() ?? User::factory()->create()); - }); - } } diff --git a/app-modules/caseload-management/resources/views/filament/resources/caseloads/pages/edit-caseload.blade.php b/app-modules/caseload-management/resources/views/filament/resources/caseloads/pages/edit-caseload.blade.php index bde2b3294a..b309018090 100644 --- a/app-modules/caseload-management/resources/views/filament/resources/caseloads/pages/edit-caseload.blade.php +++ b/app-modules/caseload-management/resources/views/filament/resources/caseloads/pages/edit-caseload.blade.php @@ -31,10 +31,6 @@ --}} -@php - use AdvisingApp\CaseloadManagement\Enums\CaseloadType; -@endphp - getResource()::getSlug()), diff --git a/app-modules/caseload-management/src/Enums/CaseloadModel.php b/app-modules/caseload-management/src/Enums/CaseloadModel.php index 0d3bb2067e..8c0af64bf6 100644 --- a/app-modules/caseload-management/src/Enums/CaseloadModel.php +++ b/app-modules/caseload-management/src/Enums/CaseloadModel.php @@ -36,10 +36,13 @@ namespace AdvisingApp\CaseloadManagement\Enums; +use Filament\Tables\Table; use Filament\Support\Contracts\HasLabel; use AdvisingApp\Prospect\Models\Prospect; use Illuminate\Database\Eloquent\Builder; use AdvisingApp\StudentDataModel\Models\Student; +use AdvisingApp\Prospect\Filament\Tables\ProspectsTable; +use AdvisingApp\StudentDataModel\Filament\Tables\StudentsTable; use AdvisingApp\CaseloadManagement\Importers\StudentCaseloadSubjectImporter; use AdvisingApp\CaseloadManagement\Importers\ProspectCaseloadSubjectImporter; @@ -54,34 +57,42 @@ public function getLabel(): ?string return $this->name; } - public static function default(): CaseloadModel + public static function default(): static { - return CaseloadModel::Student; + return static::Student; } public function query(): Builder { return match ($this) { - CaseloadModel::Student => Student::query(), - CaseloadModel::Prospect => Prospect::query(), + static::Student => Student::query(), + static::Prospect => Prospect::query(), }; } public function class(): string { return match ($this) { - CaseloadModel::Student => Student::class, - CaseloadModel::Prospect => Prospect::class, + static::Student => Student::class, + static::Prospect => Prospect::class, }; } - public static function tryFromCaseOrValue(CaseloadModel | string $value): ?CaseloadModel + public function table(Table $table): Table + { + return $table->tap(app(match ($this) { + static::Student => StudentsTable::class, + static::Prospect => ProspectsTable::class, + })); + } + + public static function tryFromCaseOrValue(CaseloadModel | string $value): ?static { if ($value instanceof CaseloadModel) { return $value; } - return CaseloadModel::tryFrom($value); + return static::tryFrom($value); } /** @@ -90,8 +101,8 @@ public static function tryFromCaseOrValue(CaseloadModel | string $value): ?Casel public function getSubjectImporter(): string { return match ($this) { - CaseloadModel::Prospect => ProspectCaseloadSubjectImporter::class, - CaseloadModel::Student => StudentCaseloadSubjectImporter::class, + static::Prospect => ProspectCaseloadSubjectImporter::class, + static::Student => StudentCaseloadSubjectImporter::class, }; } } diff --git a/app-modules/caseload-management/src/Enums/CaseloadType.php b/app-modules/caseload-management/src/Enums/CaseloadType.php index cac80c169d..148d0e85ea 100644 --- a/app-modules/caseload-management/src/Enums/CaseloadType.php +++ b/app-modules/caseload-management/src/Enums/CaseloadType.php @@ -47,19 +47,19 @@ enum CaseloadType: string implements HasLabel public function getLabel(): ?string { return match ($this) { - CaseloadType::Static => 'Static', + static::Static => 'Static', default => $this->name, }; } - public static function default(): CaseloadType + public static function default(): static { - return CaseloadType::Dynamic; + return static::Dynamic; } - public static function tryFromCaseOrValue(CaseloadType | string $value): ?CaseloadType + public static function tryFromCaseOrValue(CaseloadType | string $value): ?static { - if ($value instanceof CaseloadType) { + if ($value instanceof static) { return $value; } diff --git a/app-modules/caseload-management/src/Filament/Resources/CaseloadResource.php b/app-modules/caseload-management/src/Filament/Resources/CaseloadResource.php index 3a6f41ae96..c8781ac36f 100644 --- a/app-modules/caseload-management/src/Filament/Resources/CaseloadResource.php +++ b/app-modules/caseload-management/src/Filament/Resources/CaseloadResource.php @@ -36,28 +36,11 @@ namespace AdvisingApp\CaseloadManagement\Filament\Resources; -use Exception; -use Illuminate\Support\Str; use Filament\Resources\Resource; -use Filament\Tables\Actions\EditAction; -use Filament\Tables\Actions\ViewAction; -use Filament\Tables\Columns\TextColumn; -use AdvisingApp\Prospect\Models\Prospect; -use Filament\Tables\Filters\QueryBuilder; -use Illuminate\Database\Eloquent\Builder; -use AdvisingApp\StudentDataModel\Models\Student; use AdvisingApp\CaseloadManagement\Models\Caseload; -use AdvisingApp\CaseloadManagement\Enums\CaseloadModel; -use Filament\Tables\Filters\QueryBuilder\Constraints\Constraint; -use Filament\Tables\Filters\QueryBuilder\Constraints\TextConstraint; -use Filament\Tables\Filters\QueryBuilder\Constraints\NumberConstraint; -use Filament\Tables\Filters\QueryBuilder\Constraints\BooleanConstraint; -use Filament\Tables\Filters\QueryBuilder\Constraints\Operators\Operator; -use Filament\Tables\Filters\QueryBuilder\Constraints\RelationshipConstraint; use AdvisingApp\CaseloadManagement\Filament\Resources\CaseloadResource\Pages\EditCaseload; use AdvisingApp\CaseloadManagement\Filament\Resources\CaseloadResource\Pages\ListCaseloads; use AdvisingApp\CaseloadManagement\Filament\Resources\CaseloadResource\Pages\CreateCaseload; -use Filament\Tables\Filters\QueryBuilder\Constraints\RelationshipConstraint\Operators\IsRelatedToOperator; class CaseloadResource extends Resource { @@ -77,33 +60,6 @@ class CaseloadResource extends Resource protected static ?string $pluralModelLabel = 'Caseloads'; - public static function filters(CaseloadModel $subject): array - { - return match ($subject) { - CaseloadModel::Student => static::studentFilters(), - CaseloadModel::Prospect => static::prospectFilters(), - default => throw new Exception("{$subject->name} filters not implemented"), - }; - } - - public static function columns(CaseloadModel $subject): array - { - return match ($subject) { - CaseloadModel::Student => static::studentColumns(), - CaseloadModel::Prospect => static::prospectColumns(), - default => throw new Exception("{$subject->name} columns not implemented"), - }; - } - - public static function actions(CaseloadModel $subject): array - { - return match ($subject) { - CaseloadModel::Student => static::studentActions(), - CaseloadModel::Prospect => static::prospectActions(), - default => throw new Exception("{$subject->name} actions not implemented"), - }; - } - public static function getPages(): array { return [ @@ -112,263 +68,4 @@ public static function getPages(): array 'edit' => EditCaseload::route('/{record}/edit'), ]; } - - private static function studentFilters(): array - { - return [ - QueryBuilder::make() - ->constraints([ - TextConstraint::make('full_name') - ->label('Full Name') - ->icon('heroicon-m-user'), - TextConstraint::make('first') - ->label('First Name') - ->icon('heroicon-m-user'), - TextConstraint::make('last') - ->label('Last Name') - ->icon('heroicon-m-user'), - TextConstraint::make('preferred') - ->label('Preferred Name') - ->icon('heroicon-m-user'), - TextConstraint::make('sisid') - ->label('Student ID') - ->icon('heroicon-m-finger-print'), - TextConstraint::make('otherid') - ->label('Other ID') - ->icon('heroicon-m-finger-print'), - TextConstraint::make('email') - ->label('Email Address') - ->icon('heroicon-m-envelope'), - TextConstraint::make('mobile') - ->icon('heroicon-m-phone'), - TextConstraint::make('address') - ->icon('heroicon-m-map-pin'), - TextConstraint::make('holds') - ->icon('heroicon-m-exclamation-triangle'), - BooleanConstraint::make('sap') - ->label('SAP') - ->icon('heroicon-m-academic-cap'), - BooleanConstraint::make('dual'), - BooleanConstraint::make('ferpa') - ->label('FERPA') - ->icon('heroicon-m-lock-open'), - Constraint::make('subscribed') - ->icon('heroicon-m-bell') - ->operators([ - Operator::make('subscribed') - ->label(fn (bool $isInverse): string => $isInverse ? 'Not subscribed' : 'Subscribed') - ->summary(fn (bool $isInverse): string => $isInverse ? 'You are not subscribed' : 'You are subscribed') - ->baseQuery(fn (Builder $query, bool $isInverse) => $query->{$isInverse ? 'whereDoesntHave' : 'whereHas'}( - 'subscriptions.user', - fn (Builder $query) => $query->whereKey(auth()->user()), - )), - ]), - RelationshipConstraint::make('programs') - ->multiple() - ->attributeLabel(fn (array $settings): string => Str::plural('program', $settings['count'])) - ->icon('heroicon-m-academic-cap'), - TextConstraint::make('programSisid') - ->label('Program SISID') - ->relationship('programs', 'sisid'), - TextConstraint::make('programOtherid') - ->label('Program STUID') - ->relationship('programs', 'otherid'), - TextConstraint::make('programDivision') - ->label('Program College') - ->relationship('programs', 'division'), - TextConstraint::make('programDescr') - ->label('Program Description') - ->relationship('programs', 'descr'), - TextConstraint::make('programFoi') - ->label('Program Field of Interest') - ->relationship('programs', 'foi'), - NumberConstraint::make('programCumGpa') - ->label('Program Cumulative GPA') - ->relationship('programs', 'cum_gpa'), - RelationshipConstraint::make('enrollments') - ->multiple() - ->attributeLabel(fn (array $settings): string => Str::plural('enrollment', $settings['count'])) - ->icon('heroicon-m-folder-open'), - TextConstraint::make('enrollmentSisid') - ->label('Enrollment SISID') - ->relationship('enrollments', 'sisid'), - TextConstraint::make('enrollmentDivision') - ->label('Enrollment College') - ->relationship('enrollments', 'division'), - TextConstraint::make('enrollmentClassNbr') - ->label('Enrollment Course') - ->relationship('enrollments', 'class_nbr'), - TextConstraint::make('enrollmentCrseGradeOff') - ->label('Enrollment Grade') - ->relationship('enrollments', 'crse_grade_off'), - NumberConstraint::make('enrollmentUntTaken') - ->label('Enrollment Attempted') - ->relationship('enrollments', 'unt_taken'), - NumberConstraint::make('enrollmentUntEarned') - ->label('Enrollment Earned') - ->relationship('enrollments', 'unt_earned'), - RelationshipConstraint::make('performances') - ->multiple() - ->attributeLabel(fn (array $settings): string => Str::plural('performance', $settings['count'])) - ->icon('heroicon-m-presentation-chart-line'), - TextConstraint::make('performanceSisid') - ->label('Performance SISID') - ->relationship('performances', 'sisid'), - TextConstraint::make('performanceAcadCareer') - ->label('Performance Academic Career') - ->relationship('performances', 'acad_career'), - TextConstraint::make('performanceDivision') - ->label('Performance College') - ->relationship('performances', 'division'), - BooleanConstraint::make('performanceFirstGen') - ->label('Performance First Gen') - ->relationship('performances', 'first_gen'), - NumberConstraint::make('performanceCumAtt') - ->label('Performance Cumulative Attempted') - ->relationship('performances', 'cum_att'), - NumberConstraint::make('performanceCumErn') - ->label('Performance Cumulative Earned') - ->relationship('performances', 'cum_ern'), - NumberConstraint::make('performancePctErn') - ->label('Performance Percent Earned') - ->relationship('performances', 'pct_ern'), - NumberConstraint::make('performanceCumGpa') - ->label('Performance Cumulative GPA') - ->relationship('performances', 'cum_gpa'), - ]) - ->constraintPickerColumns([ - 'md' => 2, - 'lg' => 3, - 'xl' => 4, - ]) - ->constraintPickerWidth('7xl'), - ]; - } - - private static function prospectFilters(): array - { - return [ - QueryBuilder::make() - ->constraints([ - TextConstraint::make('first_name') - ->icon('heroicon-m-user'), - TextConstraint::make('last_name') - ->icon('heroicon-m-user'), - TextConstraint::make('full_name') - ->icon('heroicon-m-user'), - TextConstraint::make('preferred') - ->label('Preferred Name') - ->icon('heroicon-m-user'), - TextConstraint::make('email') - ->label('Email Address') - ->icon('heroicon-m-envelope'), - TextConstraint::make('email_2') - ->label('Email Address 2') - ->icon('heroicon-m-envelope'), - TextConstraint::make('mobile') - ->icon('heroicon-m-phone'), - TextConstraint::make('phone') - ->icon('heroicon-m-phone'), - TextConstraint::make('address') - ->icon('heroicon-m-map-pin'), - TextConstraint::make('address_2') - ->icon('heroicon-m-map-pin'), - BooleanConstraint::make('sms_opt_out') - ->label('SMS Opt Out') - ->icon('heroicon-m-chat-bubble-bottom-center'), - BooleanConstraint::make('email_bounce') - ->icon('heroicon-m-arrow-uturn-left'), - TextConstraint::make('hsgrad') - ->label('HS Grad') - ->icon('heroicon-m-academic-cap'), - RelationshipConstraint::make('status') - ->icon('heroicon-m-flag') - ->selectable( - IsRelatedToOperator::make() - ->titleAttribute('name') - ->multiple() - ->preload(), - ), - RelationshipConstraint::make('source') - ->icon('heroicon-m-arrow-left-on-rectangle') - ->selectable( - IsRelatedToOperator::make() - ->titleAttribute('name') - ->multiple() - ->preload(), - ), - ]) - ->constraintPickerColumns([ - 'md' => 2, - 'lg' => 3, - 'xl' => 4, - ]) - ->constraintPickerWidth('7xl'), - ]; - } - - private static function studentColumns(): array - { - return [ - TextColumn::make(Student::displayNameKey()) - ->label('Name') - ->sortable(), - TextColumn::make('email'), - TextColumn::make('mobile'), - TextColumn::make('phone'), - TextColumn::make('sisid'), - TextColumn::make('otherid'), - ]; - } - - private static function prospectColumns(): array - { - return [ - TextColumn::make(Prospect::displayNameKey()) - ->label('Name') - ->sortable(), - TextColumn::make('email') - ->label('Email') - ->sortable(), - TextColumn::make('mobile') - ->label('Mobile') - ->sortable(), - TextColumn::make('status') - ->badge() - ->state(function (Prospect $record) { - return $record->status->name; - }) - ->color(function (Prospect $record) { - return $record->status->color->value; - }) - ->sortable(query: function (Builder $query, string $direction): Builder { - return $query - ->join('prospect_statuses', 'prospects.status_id', '=', 'prospect_statuses.id') - ->orderBy('prospect_statuses.name', $direction); - }), - TextColumn::make('source.name') - ->label('Source') - ->sortable(), - TextColumn::make('created_at') - ->label('Created') - ->dateTime('g:ia - M j, Y ') - ->sortable(), - ]; - } - - private static function studentActions(): array - { - return [ - ViewAction::make(), - EditAction::make(), - ]; - } - - private static function prospectActions(): array - { - return [ - ViewAction::make(), - EditAction::make(), - ]; - } } diff --git a/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/CreateCaseload.php b/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/CreateCaseload.php index a78039c5d3..7b5edc2cab 100644 --- a/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/CreateCaseload.php +++ b/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/CreateCaseload.php @@ -45,6 +45,7 @@ use Illuminate\Support\Arr; use Illuminate\Support\Str; use App\Support\ChunkIterator; +use Filament\Forms\Components\View; use Illuminate\Support\Facades\Bus; use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; @@ -52,14 +53,11 @@ use Illuminate\Support\Facades\Storage; use Filament\Forms\Components\TextInput; use Filament\Notifications\Notification; -use Filament\Tables\Enums\FiltersLayout; use AdvisingApp\Prospect\Models\Prospect; use Filament\Forms\Components\FileUpload; use Illuminate\Filesystem\AwsS3V3Adapter; -use Filament\Forms\Components\Placeholder; use Filament\Forms\Components\Wizard\Step; use Filament\Resources\Pages\CreateRecord; -use Illuminate\Contracts\Support\Htmlable; use Filament\Actions\Imports\Jobs\ImportCsv; use AdvisingApp\StudentDataModel\Models\Student; use Filament\Tables\Concerns\InteractsWithTable; @@ -114,9 +112,7 @@ public function getSteps(): array ->columns(2), Step::make('Create Caseload') ->schema([ - Placeholder::make('table') - ->content(fn (): Htmlable => $this->table) - ->hiddenLabel() + View::make('filament.forms.components.table') ->visible(fn (Get $get): bool => CaseloadType::tryFromCaseOrValue($get('type')) === CaseloadType::Dynamic), FileUpload::make('file') ->acceptedFileTypes(['text/csv', 'text/plain']) @@ -135,16 +131,35 @@ public function getSteps(): array public function table(Table $table): Table { - $model = $this->getCaseloadModel(); + return $this->getCaseloadModel()->table($table); + } + + /** + * @return resource | false + */ + public function getUploadedFileStream(TemporaryUploadedFile $file) + { + $filePath = $file->getRealPath(); + + if (config('filament.default_filesystem_disk') !== 's3') { + return fopen($filePath, mode: 'r'); + } - return $table - ->columns(CaseloadResource::columns($model)) - ->filters(CaseloadResource::filters($model), layout: FiltersLayout::AboveContent) - // ->actions(CaseloadResource::actions($model)) - ->query(fn () => $model->query()); + /** @var AwsS3V3Adapter $s3Adapter */ + $s3Adapter = Storage::disk('s3')->getAdapter(); + + invade($s3Adapter)->client->registerStreamWrapper(); + + $fileS3Path = 's3://' . config('filesystems.disks.s3.bucket') . '/' . $filePath; + + return fopen($fileS3Path, mode: 'r', context: stream_context_create([ + 's3' => [ + 'seekable' => true, + ], + ])); } - public function afterCreate(): void + protected function afterCreate(): void { $data = $this->form->getRawState(); @@ -272,31 +287,6 @@ public function afterCreate(): void ->send(); } - /** - * @return resource | false - */ - public function getUploadedFileStream(TemporaryUploadedFile $file) - { - $filePath = $file->getRealPath(); - - if (config('filament.default_filesystem_disk') !== 's3') { - return fopen($filePath, mode: 'r'); - } - - /** @var AwsS3V3Adapter $s3Adapter */ - $s3Adapter = Storage::disk('s3')->getAdapter(); - - invade($s3Adapter)->client->registerStreamWrapper(); - - $fileS3Path = 's3://' . config('filesystems.disks.s3.bucket') . '/' . $filePath; - - return fopen($fileS3Path, mode: 'r', context: stream_context_create([ - 's3' => [ - 'seekable' => true, - ], - ])); - } - protected function getCaseloadModel(): CaseloadModel { $canAccessStudents = auth()->user()->hasLicense(Student::getLicenseType()); diff --git a/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/EditCaseload.php b/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/EditCaseload.php index 4fe0b09d11..28d5a05fb1 100644 --- a/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/EditCaseload.php +++ b/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/EditCaseload.php @@ -45,8 +45,8 @@ use Filament\Tables\Contracts\HasTable; use Filament\Forms\Components\TextInput; use Filament\Resources\Pages\EditRecord; -use Filament\Tables\Enums\FiltersLayout; use AdvisingApp\Prospect\Models\Prospect; +use Illuminate\Database\Eloquent\Builder; use AdvisingApp\StudentDataModel\Models\Student; use Filament\Tables\Concerns\InteractsWithTable; use AdvisingApp\CaseloadManagement\Enums\CaseloadType; @@ -63,13 +63,6 @@ class EditCaseload extends EditRecord implements HasTable protected static string $view = 'caseload-management::filament.resources.caseloads.pages.edit-caseload'; - public function afterFill(): void - { - $this->data['model'] = CaseloadModel::from($this->data['model']); - $this->data['type'] = CaseloadType::from($this->data['type']); - $this->data['user']['name'] = $this->getRecord()->user->name; - } - public function form(Form $form): Form { return $form @@ -101,23 +94,17 @@ public function form(Form $form): Form public function table(Table $table): Table { - return $table - ->columns(CaseloadResource::columns($this->data['model'])) - ->filters(CaseloadResource::filters($this->data['model']), layout: FiltersLayout::AboveContent) - // ->actions(CaseloadResource::actions($this->data['model'])) - ->query(function () { - $model = $this->data['model']; - $query = $model->query(); - - if ($this->getRecord()->type === CaseloadType::Static) { - $column = app($model->class())->getKeyName(); - $ids = $this->getRecord()->subjects()->pluck('subject_id'); - - $query->whereIn($column, $ids); - } - - return $query; - }); + $caseload = $this->getRecord(); + + $table = $caseload->model->table($table); + + if ($caseload->type === CaseloadType::Static) { + $keys = $caseload->subjects()->pluck('subject_id'); + + $table->modifyQueryUsing(fn (Builder $query) => $query->whereKey($keys)); + } + + return $table; } public function bootedInteractsWithTable(): void @@ -129,7 +116,16 @@ public function bootedInteractsWithTable(): void $this->baseBootedInteractsWithTable(); } - public function afterSave(): void {} + protected function mutateFormDataBeforeFill(array $data): array + { + $caseload = $this->getRecord(); + + $data['model'] = $caseload->model; + $data['type'] = $caseload->type; + $data['user']['name'] = $caseload->user->name; + + return $data; + } protected function getHeaderActions(): array { diff --git a/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/GetCaseloadQuery.php b/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/GetCaseloadQuery.php index f42629ac9f..5919315cb7 100644 --- a/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/GetCaseloadQuery.php +++ b/app-modules/caseload-management/src/Filament/Resources/CaseloadResource/Pages/GetCaseloadQuery.php @@ -39,10 +39,9 @@ use Filament\Tables\Table; use Filament\Tables\Contracts\HasTable; use Filament\Resources\Pages\EditRecord; -use Filament\Tables\Enums\FiltersLayout; +use Illuminate\Database\Eloquent\Builder; use Filament\Tables\Concerns\InteractsWithTable; use AdvisingApp\CaseloadManagement\Enums\CaseloadType; -use AdvisingApp\CaseloadManagement\Enums\CaseloadModel; use AdvisingApp\CaseloadManagement\Filament\Resources\CaseloadResource; class GetCaseloadQuery extends EditRecord implements HasTable @@ -62,32 +61,19 @@ public function mount(int | string $record): void $this->previousUrl = url()->previous(); } - public function afterFill(): void - { - $this->data['model'] = CaseloadModel::from($this->data['model']); - $this->data['type'] = CaseloadType::from($this->data['type']); - $this->data['user']['name'] = $this->getRecord()->user->name; - } - public function table(Table $table): Table { - return $table - ->columns(CaseloadResource::columns($this->data['model'])) - ->filters(CaseloadResource::filters($this->data['model']), layout: FiltersLayout::AboveContent) - // ->actions(CaseloadResource::actions($this->data['model'])) - ->query(function () { - $model = $this->data['model']; - $query = $model->query(); - - if ($this->getRecord()->type === CaseloadType::Static) { - $column = app($model->class())->getKeyName(); - $ids = $this->getRecord()->subjects()->pluck('subject_id'); - - $query->whereIn($column, $ids); - } - - return $query; - }); + $caseload = $this->getRecord(); + + $table = $caseload->model->table($table); + + if ($caseload->type === CaseloadType::Static) { + $keys = $caseload->subjects()->pluck('subject_id'); + + $table->modifyQueryUsing(fn (Builder $query) => $query->whereKey($keys)); + } + + return $table; } public function bootedInteractsWithTable(): void @@ -98,4 +84,15 @@ public function bootedInteractsWithTable(): void $this->baseBootedInteractsWithTable(); } + + protected function mutateFormDataBeforeFill(array $data): array + { + $caseload = $this->getRecord(); + + $data['model'] = $caseload->model; + $data['type'] = $caseload->type; + $data['user']['name'] = $caseload->user->name; + + return $data; + } } diff --git a/app-modules/inventory-management/composer.json b/app-modules/inventory-management/composer.json index e89edf975e..69a7513d7a 100644 --- a/app-modules/inventory-management/composer.json +++ b/app-modules/inventory-management/composer.json @@ -1,5 +1,5 @@ { - "name": "canyon-gbs/inventory-management", + "name": "canyon-gbs/advising-app-inventory-management", "description": "", "type": "library", "version": "1.0", diff --git a/app-modules/prospect/src/Filament/Tables/ProspectsTable.php b/app-modules/prospect/src/Filament/Tables/ProspectsTable.php new file mode 100644 index 0000000000..5625c8128e --- /dev/null +++ b/app-modules/prospect/src/Filament/Tables/ProspectsTable.php @@ -0,0 +1,155 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Prospect\Filament\Tables; + +use Filament\Tables\Table; +use Filament\Tables\Actions\ViewAction; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Enums\FiltersLayout; +use AdvisingApp\Prospect\Models\Prospect; +use Filament\Tables\Filters\QueryBuilder; +use Illuminate\Database\Eloquent\Builder; +use AdvisingApp\Prospect\Filament\Resources\ProspectResource; +use Filament\Tables\Filters\QueryBuilder\Constraints\TextConstraint; +use Filament\Tables\Filters\QueryBuilder\Constraints\BooleanConstraint; +use Filament\Tables\Filters\QueryBuilder\Constraints\RelationshipConstraint; +use Filament\Tables\Filters\QueryBuilder\Constraints\RelationshipConstraint\Operators\IsRelatedToOperator; + +class ProspectsTable +{ + public function __invoke(Table $table): Table + { + return $table + ->query(fn () => Prospect::query()) + ->columns([ + TextColumn::make(Prospect::displayNameKey()) + ->label('Name') + ->sortable(), + TextColumn::make('email') + ->label('Email') + ->sortable(), + TextColumn::make('mobile') + ->label('Mobile') + ->sortable(), + TextColumn::make('status') + ->badge() + ->state(function (Prospect $record) { + return $record->status->name; + }) + ->color(function (Prospect $record) { + return $record->status->color->value; + }) + ->sortable(query: function (Builder $query, string $direction): Builder { + return $query + ->join('prospect_statuses', 'prospects.status_id', '=', 'prospect_statuses.id') + ->orderBy('prospect_statuses.name', $direction); + }), + TextColumn::make('source.name') + ->label('Source') + ->sortable(), + TextColumn::make('created_at') + ->label('Created') + ->dateTime('g:ia - M j, Y ') + ->sortable(), + ]) + ->filters([ + QueryBuilder::make() + ->constraints([ + TextConstraint::make('first_name') + ->icon('heroicon-m-user'), + TextConstraint::make('last_name') + ->icon('heroicon-m-user'), + TextConstraint::make('full_name') + ->icon('heroicon-m-user'), + TextConstraint::make('preferred') + ->label('Preferred Name') + ->icon('heroicon-m-user'), + QueryBuilder\Constraints\DateConstraint::make('created_at') + ->icon('heroicon-m-calendar'), + TextConstraint::make('email') + ->label('Email Address') + ->icon('heroicon-m-envelope'), + TextConstraint::make('email_2') + ->label('Email Address 2') + ->icon('heroicon-m-envelope'), + TextConstraint::make('mobile') + ->icon('heroicon-m-phone'), + TextConstraint::make('phone') + ->icon('heroicon-m-phone'), + TextConstraint::make('address') + ->icon('heroicon-m-map-pin'), + TextConstraint::make('address_2') + ->icon('heroicon-m-map-pin'), + BooleanConstraint::make('sms_opt_out') + ->label('SMS Opt Out') + ->icon('heroicon-m-chat-bubble-bottom-center'), + BooleanConstraint::make('email_bounce') + ->icon('heroicon-m-arrow-uturn-left'), + TextConstraint::make('hsgrad') + ->label('HS Grad') + ->icon('heroicon-m-academic-cap'), + RelationshipConstraint::make('status') + ->icon('heroicon-m-flag') + ->selectable( + IsRelatedToOperator::make() + ->titleAttribute('name') + ->multiple() + ->preload(), + ), + RelationshipConstraint::make('source') + ->icon('heroicon-m-arrow-left-on-rectangle') + ->selectable( + IsRelatedToOperator::make() + ->titleAttribute('name') + ->multiple() + ->preload(), + ), + ]) + ->constraintPickerColumns([ + 'md' => 2, + 'lg' => 3, + 'xl' => 4, + ]) + ->constraintPickerWidth('7xl'), + ], layout: FiltersLayout::AboveContent) + ->actions([ + ViewAction::make() + ->authorize('view') + ->url(fn (Prospect $record) => ProspectResource::getUrl('view', ['record' => $record])), + ]); + } +} diff --git a/app-modules/report/composer.json b/app-modules/report/composer.json new file mode 100644 index 0000000000..7a9d4f545c --- /dev/null +++ b/app-modules/report/composer.json @@ -0,0 +1,26 @@ +{ + "name": "canyon-gbs/advising-app-report", + "description": "", + "type": "library", + "version": "1.0", + "license": "proprietary", + "require": { + "filament/filament": "^3.0.0" + }, + "autoload": { + "psr-4": { + "AdvisingApp\\Report\\": "src/", + "AdvisingApp\\Report\\Tests\\": "tests/", + "AdvisingApp\\Report\\Database\\Factories\\": "database/factories/", + "AdvisingApp\\Report\\Database\\Seeders\\": "database/seeders/" + } + }, + "minimum-stability": "dev", + "extra": { + "laravel": { + "providers": [ + "AdvisingApp\\Report\\Providers\\ReportServiceProvider" + ] + } + } +} diff --git a/app-modules/report/config/permissions/api/custom.php b/app-modules/report/config/permissions/api/custom.php new file mode 100644 index 0000000000..2c194089ba --- /dev/null +++ b/app-modules/report/config/permissions/api/custom.php @@ -0,0 +1,37 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +return []; diff --git a/app-modules/report/config/permissions/web/custom.php b/app-modules/report/config/permissions/web/custom.php new file mode 100644 index 0000000000..2c194089ba --- /dev/null +++ b/app-modules/report/config/permissions/web/custom.php @@ -0,0 +1,37 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +return []; diff --git a/app-modules/report/config/roles/api/report_management.php b/app-modules/report/config/roles/api/report_management.php new file mode 100644 index 0000000000..2c194089ba --- /dev/null +++ b/app-modules/report/config/roles/api/report_management.php @@ -0,0 +1,37 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +return []; diff --git a/app-modules/report/config/roles/web/report_management.php b/app-modules/report/config/roles/web/report_management.php new file mode 100644 index 0000000000..3523d3a2e9 --- /dev/null +++ b/app-modules/report/config/roles/web/report_management.php @@ -0,0 +1,43 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +return [ + 'model' => [ + 'report' => [ + '*', + ], + ], +]; diff --git a/app-modules/report/database/factories/ReportFactory.php b/app-modules/report/database/factories/ReportFactory.php new file mode 100644 index 0000000000..ba400a94aa --- /dev/null +++ b/app-modules/report/database/factories/ReportFactory.php @@ -0,0 +1,60 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Database\Factories; + +use App\Models\User; +use AdvisingApp\Report\Models\Report; +use AdvisingApp\Report\Enums\ReportModel; +use Illuminate\Database\Eloquent\Factories\Factory; + +/** + * @extends Factory + */ +class ReportFactory extends Factory +{ + /** + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->words(asText: true), + 'model' => fake()->randomElement(ReportModel::cases()), + 'user_id' => User::inRandomOrder()->first()?->getKey() ?? User::factory()->create()?->getKey(), + ]; + } +} diff --git a/app-modules/report/database/migrations/2023_09_19_173213_create_reports_table.php b/app-modules/report/database/migrations/2023_09_19_173213_create_reports_table.php new file mode 100644 index 0000000000..ff71460410 --- /dev/null +++ b/app-modules/report/database/migrations/2023_09_19_173213_create_reports_table.php @@ -0,0 +1,59 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +use Illuminate\Support\Facades\Schema; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Migrations\Migration; + +return new class () extends Migration { + public function up(): void + { + Schema::create('reports', function (Blueprint $table) { + $table->uuid('id')->primary(); + + $table->string('name'); + $table->text('description')->nullable(); + $table->json('filters')->nullable(); + $table->json('columns'); + $table->string('model'); + + $table->foreignUuid('user_id')->constrained('users'); + + $table->timestamps(); + $table->softDeletes(); + }); + } +}; diff --git a/app-modules/report/database/seeders/.gitkeep b/app-modules/report/database/seeders/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app-modules/report/resources/views/filament/resources/reports/pages/edit-report.blade.php b/app-modules/report/resources/views/filament/resources/reports/pages/edit-report.blade.php new file mode 100644 index 0000000000..b309018090 --- /dev/null +++ b/app-modules/report/resources/views/filament/resources/reports/pages/edit-report.blade.php @@ -0,0 +1,83 @@ +{{-- + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +--}} +getResource()::getSlug()), + 'fi-resource-record-' . $record->getKey(), +])> + @capture($form) + + {{ $this->form }} + + + + @endcapture + + @php + $relationManagers = $this->getRelationManagers(); + $hasCombinedRelationManagerTabsWithContent = $this->hasCombinedRelationManagerTabsWithContent(); + @endphp + + @if (!$hasCombinedRelationManagerTabsWithContent || !count($relationManagers)) + {{ $form() }} + @endif + + @if (count($relationManagers)) + + @if ($hasCombinedRelationManagerTabsWithContent) + + {{ $form() }} + + @endif + + @endif + + @if ($data['model']) + {{ $this->table }} + @endif + diff --git a/app-modules/report/src/Enums/ReportModel.php b/app-modules/report/src/Enums/ReportModel.php new file mode 100644 index 0000000000..4402fb347e --- /dev/null +++ b/app-modules/report/src/Enums/ReportModel.php @@ -0,0 +1,123 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Enums; + +use App\Models\User; +use Filament\Tables\Table; +use App\Models\Authenticatable; +use App\Filament\Tables\UsersTable; +use Filament\Support\Contracts\HasLabel; +use AdvisingApp\Prospect\Models\Prospect; +use Illuminate\Database\Eloquent\Builder; +use AdvisingApp\StudentDataModel\Models\Student; +use AdvisingApp\Report\Filament\Exports\UserExporter; +use AdvisingApp\Prospect\Filament\Tables\ProspectsTable; +use AdvisingApp\Report\Filament\Exports\StudentExporter; +use AdvisingApp\Report\Filament\Exports\ProspectExporter; +use AdvisingApp\StudentDataModel\Filament\Tables\StudentsTable; + +enum ReportModel: string implements HasLabel +{ + case Prospect = 'prospect'; + + case Student = 'student'; + + case User = 'user'; + + public function getLabel(): ?string + { + return $this->name; + } + + public static function default(): static + { + return static::Student; + } + + public function query(): Builder + { + return match ($this) { + static::Student => Student::query(), + static::Prospect => Prospect::query(), + static::User => User::query(), + }; + } + + public function table(Table $table): Table + { + return $table->tap(app(match ($this) { + static::Student => StudentsTable::class, + static::Prospect => ProspectsTable::class, + static::User => UsersTable::class, + })); + } + + public function class(): string + { + return match ($this) { + static::Student => Student::class, + static::Prospect => Prospect::class, + static::User => User::class, + }; + } + + public function exporter(): string + { + return match ($this) { + static::Student => StudentExporter::class, + static::Prospect => ProspectExporter::class, + static::User => UserExporter::class, + }; + } + + public function canBeAccessed(Authenticatable $user): bool + { + return match ($this) { + static::Student, static::Prospect => $user->hasLicense($this->class()::getLicenseType()), + static::User => $user->can('viewAny', User::class), + }; + } + + public static function tryFromCaseOrValue(ReportModel | string $value): ?static + { + if ($value instanceof static) { + return $value; + } + + return static::tryFrom($value); + } +} diff --git a/app-modules/report/src/Filament/Exports/ProspectExporter.php b/app-modules/report/src/Filament/Exports/ProspectExporter.php new file mode 100644 index 0000000000..0546561f85 --- /dev/null +++ b/app-modules/report/src/Filament/Exports/ProspectExporter.php @@ -0,0 +1,101 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Filament\Exports; + +use Filament\Actions\Exports\Exporter; +use Filament\Tables\Columns\TextColumn; +use AdvisingApp\Prospect\Models\Prospect; +use Filament\Actions\Exports\ExportColumn; +use Filament\Actions\Exports\Models\Export; + +class ProspectExporter extends Exporter +{ + protected static ?string $model = Prospect::class; + + /** + * @param class-string $type + */ + public static function getColumns(string $type = ExportColumn::class): array + { + return [ + $type::make('id') + ->label('ID'), + static::notDefault($type::make('status.name')), + static::notDefault($type::make('source.name')), + $type::make('first_name'), + $type::make('last_name'), + static::notDefault($type::make('full_name')), + static::notDefault($type::make('preferred') + ->label('Preferred Name')), + static::notDefault($type::make('description')), + $type::make('email'), + static::notDefault($type::make('email_2') + ->label('Email 2')), + static::notDefault($type::make('mobile')), + $type::make('phone'), + static::notDefault($type::make('address')), + $type::make('address_2') + ->label('Address 2'), + static::notDefault($type::make('birthdate')), + static::notDefault($type::make('hsgrad') + ->label('High School Grad')), + $type::make('created_at'), + static::notDefault($type::make('assigned_to.name')), + static::notDefault($type::make('created_by.name')), + ]; + } + + public static function getCompletedNotificationBody(Export $export): string + { + $body = 'Your prospect report export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.'; + + if ($failedRowsCount = $export->getFailedRowsCount()) { + $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.'; + } + + return $body; + } + + protected static function notDefault(ExportColumn | TextColumn $column): ExportColumn | TextColumn + { + if ($column instanceof ExportColumn) { + $column->enabledByDefault(false); + } + + return $column; + } +} diff --git a/app-modules/report/src/Filament/Exports/StudentExporter.php b/app-modules/report/src/Filament/Exports/StudentExporter.php new file mode 100644 index 0000000000..b15c1d8019 --- /dev/null +++ b/app-modules/report/src/Filament/Exports/StudentExporter.php @@ -0,0 +1,196 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Filament\Exports; + +use AdvisingApp\Task\Enums\TaskStatus; +use Filament\Actions\Exports\Exporter; +use Filament\Tables\Columns\TextColumn; +use AdvisingApp\Alert\Enums\AlertStatus; +use Illuminate\Database\Eloquent\Builder; +use Filament\Actions\Exports\ExportColumn; +use Filament\Actions\Exports\Models\Export; +use AdvisingApp\StudentDataModel\Models\Student; +use AdvisingApp\Interaction\Models\InteractionType; +use AdvisingApp\Interaction\Models\InteractionStatus; +use AdvisingApp\ServiceManagement\Models\ServiceRequestStatus; + +class StudentExporter extends Exporter +{ + protected static ?string $model = Student::class; + + /** + * @param class-string $type + */ + public static function getColumns(string $type = ExportColumn::class): array + { + return [ + $type::make('sisid'), + $type::make('otherid'), + $type::make('first') + ->label('First Name'), + $type::make('last') + ->label('Last Name'), + static::notDefault($type::make('full_name') + ->label('Full Name')), + static::notDefault($type::make('preferred') + ->label('Preferred Name')), + $type::make('email') + ->label('Email Address'), + static::notDefault($type::make('email_2') + ->label('Email Address 2')), + static::notDefault($type::make('mobile')), + $type::make('phone'), + static::notDefault($type::make('address')), + static::notDefault($type::make('address2') + ->label('Address 2')), + static::notDefault($type::make('address3') + ->label('Address 3')), + static::notDefault($type::make('city')), + static::notDefault($type::make('state')), + static::notDefault($type::make('postal')), + static::notDefault($type::make('birthdate')), + static::notDefault($type::make('hsgrad') + ->label('High School Graduation')), + static::notDefault($type::make('dfw') + ->label('DFW')), + static::notDefault($type::make('holds')), + static::notDefault($type::make('ethnicity')), + static::notDefault($type::make('lastlsmlogin') + ->label('Last LMS Login')), + static::notDefault($type::make('f_e_term') + ->label('First Enrollment Term')), + static::notDefault($type::make('mr_e_term') + ->label('Most Recent Enrollment Term')), + static::notDefault($type::make('programs_count') + ->label('Count of Programs') + ->counts('programs')), + static::notDefault($type::make('enrollments_count') + ->label('Count of Enrollments') + ->counts('enrollments')), + static::notDefault($type::make('enrollments_avg_unt_taken') + ->label('Average Attempted Enrollments') + ->avg('enrollments', 'unt_taken')), + static::notDefault($type::make('enrollments_avg_unt_earned') + ->label('Average Earned Enrollments') + ->avg('enrollments', 'unt_earned')), + static::notDefault($type::make('engagement_files_count') + ->label('Count of Files') + ->counts('engagementFiles')), + static::notDefault($type::make('alerts_count') + ->label('Count of Alerts') + ->counts('alerts')), + ...array_map( + fn (AlertStatus $status): TextColumn | ExportColumn => static::notDefault($type::make("alerts_{$status->value}_count") + ->label("Count of {$status->getLabel()} Alerts") + ->counts([ + "alerts as alerts_{$status->value}_count" => fn (Builder $query) => $query->status($status), + ])), + AlertStatus::cases(), + ), + static::notDefault($type::make('tasks_count') + ->label('Count of Tasks') + ->counts('tasks')), + ...array_map( + fn (TaskStatus $status): TextColumn | ExportColumn => static::notDefault($type::make("tasks_{$status->value}_count") + ->label("Count of {$status->getLabel()} Tasks") + ->counts([ + "tasks as tasks_{$status->value}_count" => fn (Builder $query) => $query->where('status', $status), + ])), + TaskStatus::cases(), + ), + static::notDefault($type::make('subscribed_users_count') + ->label('Count of Subscribers') + ->counts('subscribedUsers')), + static::notDefault($type::make('interactions_count') + ->label('Count of Interactions') + ->counts('interactions')), + ...InteractionType::all()->map(fn (InteractionType $interactionType): TextColumn | ExportColumn => static::notDefault($type::make("interactions_{$interactionType->getKey()}_count") + ->label("Count of {$interactionType->name} Interactions") + ->counts([ + "interactions as interactions_{$interactionType->getKey()}_count" => fn (Builder $query) => $query->whereBelongsTo($interactionType, 'type'), + ]))), + ...InteractionStatus::all()->map(fn (InteractionStatus $status): TextColumn | ExportColumn => static::notDefault($type::make("interactions_{$status->getKey()}_count") + ->label("Count of {$status->name} Interactions") + ->counts([ + "interactions as interactions_{$status->getKey()}_count" => fn (Builder $query) => $query->whereBelongsTo($status, 'status'), + ]))), + static::notDefault($type::make('care_team_count') + ->label('Count of Care Team Members') + ->counts('careTeam')), + static::notDefault($type::make('care_team_count') + ->label('Count of Care Team Members') + ->counts('careTeam')), + static::notDefault($type::make('service_requests_count') + ->label('Count of Service Requests') + ->counts('serviceRequests')), + ...ServiceRequestStatus::all()->map(fn (ServiceRequestStatus $status): TextColumn | ExportColumn => static::notDefault($type::make("service_requests_{$status->getKey()}_count") + ->label("Count of {$status->name} Service Requests") + ->counts([ + "serviceRequests as service_requests_{$status->getKey()}_count" => fn (Builder $query) => $query->whereBelongsTo($status, 'status'), + ]))), + static::notDefault($type::make('asset_check_ins_count') + ->label('Count of Returned Assets') + ->counts('assetCheckIns')), + static::notDefault($type::make('asset_check_outs_count') + ->label('Count of Checked Out Assets') + ->counts('assetCheckOuts')), + static::notDefault($type::make('event_attendee_records_count') + ->label('Count of Events') + ->counts('eventAttendeeRecords')), + ]; + } + + public static function getCompletedNotificationBody(Export $export): string + { + $body = 'Your student report export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.'; + + if ($failedRowsCount = $export->getFailedRowsCount()) { + $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.'; + } + + return $body; + } + + protected static function notDefault(ExportColumn | TextColumn $column): ExportColumn | TextColumn + { + if ($column instanceof ExportColumn) { + $column->enabledByDefault(false); + } + + return $column; + } +} diff --git a/app-modules/report/src/Filament/Exports/UserExporter.php b/app-modules/report/src/Filament/Exports/UserExporter.php new file mode 100644 index 0000000000..b374fd571d --- /dev/null +++ b/app-modules/report/src/Filament/Exports/UserExporter.php @@ -0,0 +1,74 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Filament\Exports; + +use App\Models\User; +use Filament\Actions\Exports\Exporter; +use Filament\Tables\Columns\TextColumn; +use Filament\Actions\Exports\ExportColumn; +use Filament\Actions\Exports\Models\Export; + +class UserExporter extends Exporter +{ + protected static ?string $model = User::class; + + /** + * @param class-string $type + */ + public static function getColumns(string $type = ExportColumn::class): array + { + return [ + $type::make('id') + ->label('ID'), + $type::make('name'), + $type::make('email'), + $type::make('phone_number'), + $type::make('created_at'), + ]; + } + + public static function getCompletedNotificationBody(Export $export): string + { + $body = 'Your user report export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.'; + + if ($failedRowsCount = $export->getFailedRowsCount()) { + $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.'; + } + + return $body; + } +} diff --git a/app-modules/report/src/Filament/Resources/ReportResource.php b/app-modules/report/src/Filament/Resources/ReportResource.php new file mode 100644 index 0000000000..399f99947c --- /dev/null +++ b/app-modules/report/src/Filament/Resources/ReportResource.php @@ -0,0 +1,67 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Filament\Resources; + +use Filament\Resources\Resource; +use AdvisingApp\Report\Models\Report; +use AdvisingApp\Report\Filament\Resources\ReportResource\Pages\EditReport; +use AdvisingApp\Report\Filament\Resources\ReportResource\Pages\ListReports; +use AdvisingApp\Report\Filament\Resources\ReportResource\Pages\CreateReport; + +class ReportResource extends Resource +{ + protected static ?string $model = Report::class; + + protected static ?string $navigationIcon = 'heroicon-o-document-text'; + + protected static ?string $navigationGroup = 'Reporting'; + + protected static ?int $navigationSort = 10; + + protected static ?string $navigationLabel = 'Report Center'; + + protected static ?string $breadcrumb = 'Report Center'; + + public static function getPages(): array + { + return [ + 'index' => ListReports::route('/'), + 'create' => CreateReport::route('/create'), + 'edit' => EditReport::route('/{record}/edit'), + ]; + } +} diff --git a/app-modules/report/src/Filament/Resources/ReportResource/Pages/CreateReport.php b/app-modules/report/src/Filament/Resources/ReportResource/Pages/CreateReport.php new file mode 100644 index 0000000000..c1b5773ec2 --- /dev/null +++ b/app-modules/report/src/Filament/Resources/ReportResource/Pages/CreateReport.php @@ -0,0 +1,193 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Filament\Resources\ReportResource\Pages; + +use App\Models\User; +use Filament\Forms\Set; +use Filament\Tables\Table; +use Illuminate\Support\Arr; +use Filament\Actions\ExportAction; +use Filament\Forms\Components\View; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\Textarea; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Contracts\HasTable; +use Filament\Forms\Components\TextInput; +use AdvisingApp\Prospect\Models\Prospect; +use AdvisingApp\Report\Enums\ReportModel; +use Filament\Actions\Exports\ExportColumn; +use Filament\Forms\Components\Wizard\Step; +use Filament\Resources\Pages\CreateRecord; +use Filament\Forms\Components\CheckboxList; +use AdvisingApp\StudentDataModel\Models\Student; +use Filament\Tables\Concerns\InteractsWithTable; +use AdvisingApp\Report\Filament\Resources\ReportResource; + +class CreateReport extends CreateRecord implements HasTable +{ + use InteractsWithTable; + use CreateRecord\Concerns\HasWizard; + + protected static string $resource = ReportResource::class; + + public function getSteps(): array + { + $models = $this->getReportModels(); + + return [ + Step::make('Characteristics') + ->schema([ + TextInput::make('name') + ->autocomplete(false) + ->string() + ->required(), + Textarea::make('description'), + ]), + Step::make('Report Type') + ->schema([ + Select::make('model') + ->options(array_reduce($models, fn (array $options, ReportModel $model): array => [ + ...$options, + $model->value => $model->getLabel(), + ], [])) + ->required() + ->default(Arr::first($models)) + ->selectablePlaceholder(false) + ->afterStateUpdated(function (Set $set) { + $set('columns', collect($this->getReportModel()->exporter()::getColumns()) + ->filter(fn (ExportColumn $column): bool => $column->isEnabledByDefault()) + ->map(fn (ExportColumn $column): string => $column->getName()) + ->values() + ->all()); + + $this->cacheForms(); + $this->bootedInteractsWithTable(); + $this->resetTableFiltersForm(); + }), + ]) + ->columns(2) + ->visible(count($models) > 1), + Step::make('Create Report') + ->schema([ + CheckboxList::make('columns') + ->options(fn (): array => array_reduce( + $this->getReportModel()->exporter()::getColumns(), + fn (array $options, ExportColumn $column): array => [ + ...$options, + $column->getName() => $column->getLabel(), + ], + [], + )) + ->default(fn (): array => collect($this->getReportModel()->exporter()::getColumns()) + ->filter(fn (ExportColumn $column): bool => $column->isEnabledByDefault()) + ->map(fn (ExportColumn $column): string => $column->getName()) + ->values() + ->all()) + ->columns(3) + ->live() + ->afterStateUpdated($this->bootedInteractsWithTable(...)), + View::make('filament.forms.components.table'), + ]), + ]; + } + + public function table(Table $table): Table + { + $model = $this->getReportModel(); + $columns = $this->form->getRawState()['columns'] ?? []; + + return $model->table($table) + ->columns(array_reduce( + $model->exporter()::getColumns(type: TextColumn::class), + fn (array $carry, TextColumn $column): array => [ + ...$carry, + ...(in_array($column->getName(), $columns) ? [$column->getName() => $column] : []), + ], + [], + )); + } + + protected function getReportModels(): array + { + return [ + ...(auth()->user()->hasLicense(Student::getLicenseType()) ? [ReportModel::Student] : []), + ...(auth()->user()->hasLicense(Prospect::getLicenseType()) ? [ReportModel::Prospect] : []), + ...(auth()->user()->can('viewAny', User::class) ? [ReportModel::User] : []), + ]; + } + + protected function getReportModel(): ReportModel + { + $model = $this->form->getRawState()['model'] ?? null; + $models = $this->getReportModels(); + + if (filled($model) && in_array(ReportModel::tryFromCaseOrValue($model), $models)) { + return $model; + } + + return Arr::first($models); + } + + protected function mutateFormDataBeforeCreate(array $data): array + { + $data['model'] = $this->getReportModel(); + $data['filters'] = $this->tableFilters ?? []; + + return $data; + } + + protected function afterCreate(): void + { + $exporter = $this->getReportModel()->exporter(); + $columns = $this->form->getRawState()['columns'] ?? []; + + ExportAction::make() + ->livewire($this) + ->exporter($exporter) + ->modalHidden() + ->formData([ + 'columnMap' => array_reduce($exporter::getColumns(), fn (array $carry, ExportColumn $column): array => [ + ...$carry, + $column->getName() => [ + 'isEnabled' => in_array($column->getName(), $columns), + 'label' => $column->getLabel(), + ], + ], []), + ]) + ->call(); + } +} diff --git a/app-modules/report/src/Filament/Resources/ReportResource/Pages/EditReport.php b/app-modules/report/src/Filament/Resources/ReportResource/Pages/EditReport.php new file mode 100644 index 0000000000..52350bf88c --- /dev/null +++ b/app-modules/report/src/Filament/Resources/ReportResource/Pages/EditReport.php @@ -0,0 +1,180 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Filament\Resources\ReportResource\Pages; + +use App\Models\User; +use Filament\Forms\Form; +use Filament\Tables\Table; +use Filament\Actions\Action; +use Filament\Actions\DeleteAction; +use Filament\Actions\ExportAction; +use Filament\Forms\Components\Grid; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\Textarea; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Contracts\HasTable; +use Filament\Forms\Components\TextInput; +use Filament\Resources\Pages\EditRecord; +use AdvisingApp\Prospect\Models\Prospect; +use AdvisingApp\Report\Enums\ReportModel; +use Filament\Actions\Exports\ExportColumn; +use Filament\Forms\Components\CheckboxList; +use AdvisingApp\StudentDataModel\Models\Student; +use Filament\Tables\Concerns\InteractsWithTable; +use AdvisingApp\Report\Filament\Resources\ReportResource; + +class EditReport extends EditRecord implements HasTable +{ + use InteractsWithTable { + bootedInteractsWithTable as baseBootedInteractsWithTable; + } + + protected static string $resource = ReportResource::class; + + protected static string $view = 'report::filament.resources.reports.pages.edit-report'; + + public function form(Form $form): Form + { + return $form + ->schema([ + TextInput::make('name') + ->autocomplete(false) + ->string() + ->required() + ->columnSpanFull(), + Textarea::make('description') + ->columnSpanFull(), + Grid::make() + ->schema([ + Select::make('model') + ->options(ReportModel::class) + ->disabled() + ->visible(auth()->user()->hasLicense([Student::getLicenseType(), Prospect::getLicenseType()]) || auth()->user()->can('viewAny', User::class)), + TextInput::make('user.name') + ->label('User') + ->disabled(), + ]) + ->columns(3), + CheckboxList::make('columns') + ->options(fn (): array => array_reduce( + $this->getRecord()->model->exporter()::getColumns(), + fn (array $options, ExportColumn $column): array => [ + ...$options, + $column->getName() => $column->getLabel(), + ], + [], + )) + ->columns(3) + ->live() + ->columnSpanFull() + ->afterStateUpdated($this->bootedInteractsWithTable(...)), + ]); + } + + public function table(Table $table): Table + { + $report = $this->getRecord(); + + $columns = $this->form->getRawState()['columns'] ?? []; + + return $report->model->table($table) + ->columns(array_reduce( + $report->model->exporter()::getColumns(type: TextColumn::class), + fn (array $carry, TextColumn $column): array => [ + ...$carry, + ...(in_array($column->getName(), $columns) ? [$column->getName() => $column] : []), + ], + [], + )); + } + + public function bootedInteractsWithTable(): void + { + if ($this->shouldMountInteractsWithTable) { + $this->tableFilters = $this->getRecord()->filters; + } + + $this->baseBootedInteractsWithTable(); + } + + protected function mutateFormDataBeforeFill(array $data): array + { + $report = $this->getRecord(); + + $data['model'] = $report->model; + $data['user']['name'] = $report->user->name; + + return $data; + } + + protected function getHeaderActions(): array + { + return [ + Action::make('export') + ->icon('heroicon-m-arrow-down-tray') + ->action(function () { + $this->save(); + + $exporter = $this->getRecord()->model->exporter(); + $columns = $this->form->getRawState()['columns'] ?? []; + + ExportAction::make() + ->livewire($this) + ->exporter($exporter) + ->modalHidden() + ->formData([ + 'columnMap' => array_reduce($exporter::getColumns(), fn (array $carry, ExportColumn $column): array => [ + ...$carry, + $column->getName() => [ + 'isEnabled' => in_array($column->getName(), $columns), + 'label' => $column->getLabel(), + ], + ], []), + ]) + ->call(); + }), + DeleteAction::make(), + ]; + } + + protected function mutateFormDataBeforeSave(array $data): array + { + $data['filters'] = $this->tableFilters ?? []; + + return $data; + } +} diff --git a/app-modules/report/src/Filament/Resources/ReportResource/Pages/ListReports.php b/app-modules/report/src/Filament/Resources/ReportResource/Pages/ListReports.php new file mode 100644 index 0000000000..6a98394c86 --- /dev/null +++ b/app-modules/report/src/Filament/Resources/ReportResource/Pages/ListReports.php @@ -0,0 +1,95 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Filament\Resources\ReportResource\Pages; + +use App\Models\User; +use Filament\Tables\Table; +use App\Filament\Columns\IdColumn; +use Filament\Actions\CreateAction; +use Filament\Tables\Filters\Filter; +use Filament\Tables\Actions\EditAction; +use Filament\Tables\Columns\TextColumn; +use AdvisingApp\Prospect\Models\Prospect; +use Filament\Resources\Pages\ListRecords; +use Filament\Tables\Actions\DeleteAction; +use AdvisingApp\StudentDataModel\Models\Student; +use AdvisingApp\Report\Filament\Resources\ReportResource; + +class ListReports extends ListRecords +{ + protected ?string $heading = 'Report Management'; + + protected static string $resource = ReportResource::class; + + public function table(Table $table): Table + { + return $table + ->columns([ + IdColumn::make(), + TextColumn::make('name') + ->sortable(), + TextColumn::make('model') + ->sortable() + ->visible(auth()->user()->hasLicense([Student::getLicenseType(), Prospect::getLicenseType()]) || auth()->user()->can('viewAny', User::class)), + TextColumn::make('user.name') + ->label('Owner') + ->sortable() + ->hidden(function (Table $table) { + return $table->getFilter('my_reports')->getState()['isActive']; + }), + ]) + ->actions([ + EditAction::make(), + DeleteAction::make(), + ]) + ->filters([ + Filter::make('my_reports') + ->label('My Reports') + ->query( + fn ($query) => $query->where('user_id', auth()->id()) + ) + ->default(), + ]); + } + + protected function getHeaderActions(): array + { + return [ + CreateAction::make(), + ]; + } +} diff --git a/app-modules/report/src/Models/Report.php b/app-modules/report/src/Models/Report.php new file mode 100644 index 0000000000..f2bce09941 --- /dev/null +++ b/app-modules/report/src/Models/Report.php @@ -0,0 +1,88 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Models; + +use App\Models\User; +use App\Models\BaseModel; +use App\Models\Authenticatable; +use AdvisingApp\Report\Enums\ReportModel; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\BelongsTo; + +/** + * @mixin IdeHelperReport + */ +class Report extends BaseModel +{ + protected $fillable = [ + 'query', + 'columns', + 'filters', + 'name', + 'description', + 'model', + ]; + + protected $casts = [ + 'columns' => 'array', + 'filters' => 'array', + 'model' => ReportModel::class, + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + protected static function booted(): void + { + static::addGlobalScope('authorized', function (Builder $builder) { + if (! auth()->check()) { + return; + } + + /** @var Authenticatable $user */ + $user = auth()->user(); + + foreach (ReportModel::cases() as $model) { + if (! $model->canBeAccessed($user)) { + $builder->where('model', '!=', $model); + } + } + }); + } +} diff --git a/app/Filament/Pages/Reports.php b/app-modules/report/src/Observers/ReportObserver.php similarity index 78% rename from app/Filament/Pages/Reports.php rename to app-modules/report/src/Observers/ReportObserver.php index 43fe520fec..587544dd40 100644 --- a/app/Filament/Pages/Reports.php +++ b/app-modules/report/src/Observers/ReportObserver.php @@ -34,21 +34,14 @@ */ -namespace App\Filament\Pages; +namespace AdvisingApp\Report\Observers; -use Filament\Pages\Page; +use AdvisingApp\Report\Models\Report; -class Reports extends Page +class ReportObserver { - protected static ?string $navigationIcon = 'heroicon-o-document-text'; - - protected static ?string $navigationGroup = 'Reporting'; - - protected static ?int $navigationSort = 10; - - protected static string $view = 'filament.pages.coming-soon'; - - protected static ?string $title = 'Report Center'; - - protected static bool $shouldRegisterNavigation = false; + public function creating(Report $report): void + { + $report->user()->associate($report->user ?? auth()->user()); + } } diff --git a/app-modules/report/src/Policies/ReportPolicy.php b/app-modules/report/src/Policies/ReportPolicy.php new file mode 100644 index 0000000000..f51f3beea7 --- /dev/null +++ b/app-modules/report/src/Policies/ReportPolicy.php @@ -0,0 +1,138 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Policies; + +use App\Models\User; +use App\Models\Authenticatable; +use Illuminate\Auth\Access\Response; +use AdvisingApp\Report\Models\Report; +use App\Concerns\PerformsLicenseChecks; +use AdvisingApp\Authorization\Enums\LicenseType; +use App\Policies\Contracts\PerformsChecksBeforeAuthorization; + +class ReportPolicy implements PerformsChecksBeforeAuthorization +{ + use PerformsLicenseChecks; + + public function before(Authenticatable $authenticatable): ?Response + { + if ( + (! $authenticatable->hasAnyLicense([LicenseType::RetentionCrm, LicenseType::RecruitmentCrm])) && + $authenticatable->cannot('viewAny', User::class) + ) { + return Response::deny('You can not access this resource.'); + } + + return null; + } + + public function viewAny(Authenticatable $authenticatable): Response + { + return $authenticatable->canOrElse( + abilities: 'report.view-any', + denyResponse: 'You do not have permission to view reports.' + ); + } + + public function view(Authenticatable $authenticatable, Report $report): Response + { + if (! $report->model->canBeAccessed($authenticatable)) { + return Response::deny('You do not have permission to view this report.'); + } + + return $authenticatable->canOrElse( + abilities: ['report.*.view', "report.{$report->id}.view"], + denyResponse: 'You do not have permission to view this report.' + ); + } + + public function create(Authenticatable $authenticatable): Response + { + return $authenticatable->canOrElse( + abilities: 'report.create', + denyResponse: 'You do not have permission to create reports.' + ); + } + + public function update(Authenticatable $authenticatable, Report $report): Response + { + if (! $report->model->canBeAccessed($authenticatable)) { + return Response::deny('You do not have permission to update this report.'); + } + + return $authenticatable->canOrElse( + abilities: ['report.*.update', "report.{$report->id}.update"], + denyResponse: 'You do not have permission to update this report.' + ); + } + + public function delete(Authenticatable $authenticatable, Report $report): Response + { + if (! $report->model->canBeAccessed($authenticatable)) { + return Response::deny('You do not have permission to delete this report.'); + } + + return $authenticatable->canOrElse( + abilities: ['report.*.delete', "report.{$report->id}.delete"], + denyResponse: 'You do not have permission to delete this report.' + ); + } + + public function restore(Authenticatable $authenticatable, Report $report): Response + { + if (! $report->model->canBeAccessed($authenticatable)) { + return Response::deny('You do not have permission to restore this report.'); + } + + return $authenticatable->canOrElse( + abilities: ['report.*.restore', "report.{$report->id}.restore"], + denyResponse: 'You do not have permission to restore this report.' + ); + } + + public function forceDelete(Authenticatable $authenticatable, Report $report): Response + { + if (! $report->model->canBeAccessed($authenticatable)) { + return Response::deny('You do not have permission to permanently delete this report.'); + } + + return $authenticatable->canOrElse( + abilities: ['report.*.force-delete', "report.{$report->id}.force-delete"], + denyResponse: 'You do not have permission to permanently delete this report.' + ); + } +} diff --git a/app-modules/report/src/Providers/ReportServiceProvider.php b/app-modules/report/src/Providers/ReportServiceProvider.php new file mode 100644 index 0000000000..436a5ad826 --- /dev/null +++ b/app-modules/report/src/Providers/ReportServiceProvider.php @@ -0,0 +1,97 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report\Providers; + +use Filament\Panel; +use AdvisingApp\Report\ReportPlugin; +use AdvisingApp\Report\Models\Report; +use Illuminate\Support\ServiceProvider; +use AdvisingApp\Report\Observers\ReportObserver; +use Illuminate\Database\Eloquent\Relations\Relation; +use AdvisingApp\Authorization\AuthorizationRoleRegistry; +use AdvisingApp\Authorization\AuthorizationPermissionRegistry; + +class ReportServiceProvider extends ServiceProvider +{ + public function register() + { + Panel::configureUsing(fn (Panel $panel) => $panel->plugin(new ReportPlugin())); + } + + public function boot() + { + Relation::morphMap([ + 'report' => Report::class, + ]); + + $this->registerRolesAndPermissions(); + + $this->registerObservers(); + } + + protected function registerRolesAndPermissions() + { + $permissionRegistry = app(AuthorizationPermissionRegistry::class); + + $permissionRegistry->registerApiPermissions( + module: 'report', + path: 'permissions/api/custom' + ); + + $permissionRegistry->registerWebPermissions( + module: 'report', + path: 'permissions/web/custom' + ); + + $roleRegistry = app(AuthorizationRoleRegistry::class); + + $roleRegistry->registerApiRoles( + module: 'report', + path: 'roles/api' + ); + + $roleRegistry->registerWebRoles( + module: 'report', + path: 'roles/web' + ); + } + + protected function registerObservers(): void + { + Report::observe(ReportObserver::class); + } +} diff --git a/app-modules/report/src/ReportPlugin.php b/app-modules/report/src/ReportPlugin.php new file mode 100644 index 0000000000..88b9d0aeec --- /dev/null +++ b/app-modules/report/src/ReportPlugin.php @@ -0,0 +1,58 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\Report; + +use Filament\Panel; +use Filament\Contracts\Plugin; + +class ReportPlugin implements Plugin +{ + public function getId(): string + { + return 'report'; + } + + public function register(Panel $panel): void + { + $panel->discoverResources( + in: __DIR__ . '/Filament/Resources', + for: 'AdvisingApp\\Report\\Filament\\Resources' + ); + } + + public function boot(Panel $panel): void {} +} diff --git a/app-modules/student-data-model/src/Filament/Tables/StudentsTable.php b/app-modules/student-data-model/src/Filament/Tables/StudentsTable.php new file mode 100644 index 0000000000..f19ee8b64c --- /dev/null +++ b/app-modules/student-data-model/src/Filament/Tables/StudentsTable.php @@ -0,0 +1,205 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace AdvisingApp\StudentDataModel\Filament\Tables; + +use Filament\Tables\Table; +use Illuminate\Support\Str; +use Filament\Tables\Actions\ViewAction; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Enums\FiltersLayout; +use Filament\Tables\Filters\QueryBuilder; +use Illuminate\Database\Eloquent\Builder; +use AdvisingApp\StudentDataModel\Models\Student; +use Filament\Tables\Filters\QueryBuilder\Constraints\Constraint; +use AdvisingApp\StudentDataModel\Filament\Resources\StudentResource; +use Filament\Tables\Filters\QueryBuilder\Constraints\TextConstraint; +use Filament\Tables\Filters\QueryBuilder\Constraints\NumberConstraint; +use Filament\Tables\Filters\QueryBuilder\Constraints\BooleanConstraint; +use Filament\Tables\Filters\QueryBuilder\Constraints\Operators\Operator; +use Filament\Tables\Filters\QueryBuilder\Constraints\RelationshipConstraint; + +class StudentsTable +{ + public function __invoke(Table $table): Table + { + return $table + ->query(fn () => Student::query()) + ->columns([ + TextColumn::make(Student::displayNameKey()) + ->label('Name') + ->sortable(), + TextColumn::make('email'), + TextColumn::make('mobile'), + TextColumn::make('phone'), + TextColumn::make('sisid'), + TextColumn::make('otherid'), + ]) + ->filters([ + QueryBuilder::make() + ->constraints([ + TextConstraint::make('full_name') + ->label('Full Name') + ->icon('heroicon-m-user'), + TextConstraint::make('first') + ->label('First Name') + ->icon('heroicon-m-user'), + TextConstraint::make('last') + ->label('Last Name') + ->icon('heroicon-m-user'), + TextConstraint::make('preferred') + ->label('Preferred Name') + ->icon('heroicon-m-user'), + TextConstraint::make('sisid') + ->label('Student ID') + ->icon('heroicon-m-finger-print'), + TextConstraint::make('otherid') + ->label('Other ID') + ->icon('heroicon-m-finger-print'), + TextConstraint::make('email') + ->label('Email Address') + ->icon('heroicon-m-envelope'), + TextConstraint::make('mobile') + ->icon('heroicon-m-phone'), + TextConstraint::make('address') + ->icon('heroicon-m-map-pin'), + TextConstraint::make('holds') + ->icon('heroicon-m-exclamation-triangle'), + BooleanConstraint::make('sap') + ->label('SAP') + ->icon('heroicon-m-academic-cap'), + BooleanConstraint::make('dual'), + BooleanConstraint::make('ferpa') + ->label('FERPA') + ->icon('heroicon-m-lock-open'), + Constraint::make('subscribed') + ->icon('heroicon-m-bell') + ->operators([ + Operator::make('subscribed') + ->label(fn (bool $isInverse): string => $isInverse ? 'Not subscribed' : 'Subscribed') + ->summary(fn (bool $isInverse): string => $isInverse ? 'You are not subscribed' : 'You are subscribed') + ->baseQuery(fn (Builder $query, bool $isInverse) => $query->{$isInverse ? 'whereDoesntHave' : 'whereHas'}( + 'subscriptions.user', + fn (Builder $query) => $query->whereKey(auth()->user()), + )), + ]), + RelationshipConstraint::make('programs') + ->multiple() + ->attributeLabel(fn (array $settings): string => Str::plural('program', $settings['count'])) + ->icon('heroicon-m-academic-cap'), + TextConstraint::make('programSisid') + ->label('Program SISID') + ->relationship('programs', 'sisid'), + TextConstraint::make('programOtherid') + ->label('Program STUID') + ->relationship('programs', 'otherid'), + TextConstraint::make('programDivision') + ->label('Program College') + ->relationship('programs', 'division'), + TextConstraint::make('programDescr') + ->label('Program Description') + ->relationship('programs', 'descr'), + TextConstraint::make('programFoi') + ->label('Program Field of Interest') + ->relationship('programs', 'foi'), + NumberConstraint::make('programCumGpa') + ->label('Program Cumulative GPA') + ->relationship('programs', 'cum_gpa'), + RelationshipConstraint::make('enrollments') + ->multiple() + ->attributeLabel(fn (array $settings): string => Str::plural('enrollment', $settings['count'])) + ->icon('heroicon-m-folder-open'), + TextConstraint::make('enrollmentSisid') + ->label('Enrollment SISID') + ->relationship('enrollments', 'sisid'), + TextConstraint::make('enrollmentDivision') + ->label('Enrollment College') + ->relationship('enrollments', 'division'), + TextConstraint::make('enrollmentClassNbr') + ->label('Enrollment Course') + ->relationship('enrollments', 'class_nbr'), + TextConstraint::make('enrollmentCrseGradeOff') + ->label('Enrollment Grade') + ->relationship('enrollments', 'crse_grade_off'), + NumberConstraint::make('enrollmentUntTaken') + ->label('Enrollment Attempted') + ->relationship('enrollments', 'unt_taken'), + NumberConstraint::make('enrollmentUntEarned') + ->label('Enrollment Earned') + ->relationship('enrollments', 'unt_earned'), + RelationshipConstraint::make('performances') + ->multiple() + ->attributeLabel(fn (array $settings): string => Str::plural('performance', $settings['count'])) + ->icon('heroicon-m-presentation-chart-line'), + TextConstraint::make('performanceSisid') + ->label('Performance SISID') + ->relationship('performances', 'sisid'), + TextConstraint::make('performanceAcadCareer') + ->label('Performance Academic Career') + ->relationship('performances', 'acad_career'), + TextConstraint::make('performanceDivision') + ->label('Performance College') + ->relationship('performances', 'division'), + BooleanConstraint::make('performanceFirstGen') + ->label('Performance First Gen') + ->relationship('performances', 'first_gen'), + NumberConstraint::make('performanceCumAtt') + ->label('Performance Cumulative Attempted') + ->relationship('performances', 'cum_att'), + NumberConstraint::make('performanceCumErn') + ->label('Performance Cumulative Earned') + ->relationship('performances', 'cum_ern'), + NumberConstraint::make('performancePctErn') + ->label('Performance Percent Earned') + ->relationship('performances', 'pct_ern'), + NumberConstraint::make('performanceCumGpa') + ->label('Performance Cumulative GPA') + ->relationship('performances', 'cum_gpa'), + ]) + ->constraintPickerColumns([ + 'md' => 2, + 'lg' => 3, + 'xl' => 4, + ]) + ->constraintPickerWidth('7xl'), + ], layout: FiltersLayout::AboveContent) + ->actions([ + ViewAction::make() + ->authorize('view') + ->url(fn (Student $record) => StudentResource::getUrl('view', ['record' => $record])), + ]); + } +} diff --git a/app/Filament/Tables/UsersTable.php b/app/Filament/Tables/UsersTable.php new file mode 100644 index 0000000000..f41350150c --- /dev/null +++ b/app/Filament/Tables/UsersTable.php @@ -0,0 +1,78 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace App\Filament\Tables; + +use App\Models\User; +use Filament\Tables\Table; +use Filament\Tables\Actions\ViewAction; +use App\Filament\Resources\UserResource; +use Filament\Tables\Enums\FiltersLayout; +use Filament\Tables\Filters\QueryBuilder; +use Filament\Tables\Filters\QueryBuilder\Constraints\TextConstraint; + +class UsersTable +{ + public function __invoke(Table $table): Table + { + return $table + ->query(fn () => User::query()) + ->filters([ + QueryBuilder::make() + ->constraints([ + TextConstraint::make('name'), + QueryBuilder\Constraints\DateConstraint::make('created_at') + ->icon('heroicon-m-calendar'), + TextConstraint::make('email') + ->label('Email Address') + ->icon('heroicon-m-envelope'), + TextConstraint::make('phone_number') + ->icon('heroicon-m-phone'), + ]) + ->constraintPickerColumns([ + 'md' => 2, + 'lg' => 3, + 'xl' => 4, + ]) + ->constraintPickerWidth('7xl'), + ], layout: FiltersLayout::AboveContent) + ->actions([ + ViewAction::make() + ->authorize('view') + ->url(fn (User $record) => UserResource::getUrl('view', ['record' => $record])), + ]); + } +} diff --git a/app/Models/Export.php b/app/Models/Export.php new file mode 100644 index 0000000000..d8a51c828d --- /dev/null +++ b/app/Models/Export.php @@ -0,0 +1,45 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +namespace App\Models; + +use Illuminate\Database\Eloquent\Concerns\HasUuids; +use Filament\Actions\Exports\Models\Export as BaseExport; + +class Export extends BaseExport +{ + use HasUuids; +} diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index dc7d889096..61a313d368 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -41,6 +41,7 @@ use App\Models\SettingsProperty; use App\Filament\Pages\Dashboard; use Filament\Navigation\MenuItem; +use Filament\Actions\ExportAction; use Filament\Actions\ImportAction; use App\Filament\Pages\EditProfile; use Filament\Tables\Columns\Column; @@ -75,6 +76,7 @@ public function register(): void Field::configureUsing(fn ($field) => $field->translateLabel()); Entry::configureUsing(fn ($entry) => $entry->translateLabel()); Column::configureUsing(fn ($column) => $column->translateLabel()); + ExportAction::configureUsing(fn (ExportAction $action) => $action->maxRows(100000)); ImportAction::configureUsing(fn (ImportAction $action) => $action->maxRows(100000)); TiptapEditor::configureUsing(fn (TiptapEditor $editor) => $editor->gridLayouts([ 'two-columns', diff --git a/app/Providers/FilamentServiceProvider.php b/app/Providers/FilamentServiceProvider.php index 5a9f1e4f13..f9ee80d71b 100644 --- a/app/Providers/FilamentServiceProvider.php +++ b/app/Providers/FilamentServiceProvider.php @@ -36,6 +36,7 @@ namespace App\Providers; +use App\Models\Export; use App\Models\Import; use Illuminate\View\View; use App\Models\FailedImportRow; @@ -43,6 +44,7 @@ use Illuminate\Support\ServiceProvider; use Filament\Support\Facades\FilamentView; use Filament\Support\Facades\FilamentColor; +use Filament\Actions\Exports\Models\Export as BaseExport; use Filament\Actions\Imports\Models\Import as BaseImport; use Filament\Actions\Imports\Models\FailedImportRow as BaseFailedImportRow; @@ -50,6 +52,7 @@ class FilamentServiceProvider extends ServiceProvider { public function register(): void { + $this->app->bind(BaseExport::class, Export::class); $this->app->bind(BaseImport::class, Import::class); $this->app->bind(BaseFailedImportRow::class, FailedImportRow::class); } diff --git a/composer.json b/composer.json index 9bf5ae9983..65f5dca9fa 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "barryvdh/laravel-debugbar": "^3.9", "bvtterfly/model-state-machine": "^0.3.0", "canyon-gbs/advising-app-alert": "*", + "canyon-gbs/advising-app-analytics": "*", "canyon-gbs/advising-app-application": "*", "canyon-gbs/advising-app-assistant": "*", "canyon-gbs/advising-app-audit": "*@dev", @@ -38,11 +39,13 @@ "canyon-gbs/advising-app-integration-microsoft-clarity": "*", "canyon-gbs/advising-app-integration-twilio": "*", "canyon-gbs/advising-app-interaction": "*", + "canyon-gbs/advising-app-inventory-management": "*", "canyon-gbs/advising-app-knowledge-base": "*", "canyon-gbs/advising-app-meeting-center": "*", "canyon-gbs/advising-app-notification": "*", "canyon-gbs/advising-app-portal": "*", "canyon-gbs/advising-app-prospect": "*", + "canyon-gbs/advising-app-report": "*", "canyon-gbs/advising-app-service-management": "*", "canyon-gbs/advising-app-student-data-model": "*", "canyon-gbs/advising-app-survey": "*", @@ -51,8 +54,6 @@ "canyon-gbs/advising-app-theme": "*", "canyon-gbs/advising-app-timeline": "*", "canyon-gbs/advising-app-webhook": "*", - "canyon-gbs/analytics": "*", - "canyon-gbs/inventory-management": "*", "composer/composer": "^2.6.4", "filament/filament": "^3.1", "filament/spatie-laravel-media-library-plugin": "^3.1", diff --git a/composer.lock b/composer.lock index c00dd27891..9a4553b7e3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d4b21060c988298903e42a02149ff481", + "content-hash": "a1d33481f3861afc3a8ed56739a80ad9", "packages": [ { "name": "amphp/amp", @@ -1128,16 +1128,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.298.0", + "version": "3.298.1", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "55536f81006d8721c51e342d638e7ccc3529e754" + "reference": "7c7dd6f596e7f7ba22653a503ae76e8e11702028" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/55536f81006d8721c51e342d638e7ccc3529e754", - "reference": "55536f81006d8721c51e342d638e7ccc3529e754", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7c7dd6f596e7f7ba22653a503ae76e8e11702028", + "reference": "7c7dd6f596e7f7ba22653a503ae76e8e11702028", "shasum": "" }, "require": { @@ -1217,9 +1217,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.298.0" + "source": "https://github.com/aws/aws-sdk-php/tree/3.298.1" }, - "time": "2024-01-31T19:06:05+00:00" + "time": "2024-02-01T19:07:41+00:00" }, { "name": "barryvdh/laravel-debugbar", @@ -1616,6 +1616,41 @@ "relative": true } }, + { + "name": "canyon-gbs/advising-app-analytics", + "version": "1.0", + "dist": { + "type": "path", + "url": "app-modules/analytics", + "reference": "20bf4d89a69ac1f56cfcf0e0395714be2b53e1cd" + }, + "require": { + "filament/filament": "^3.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "AdvisingApp\\Analytics\\Providers\\AnalyticsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "AdvisingApp\\Analytics\\": "src/", + "AdvisingApp\\Analytics\\Tests\\": "tests/", + "AdvisingApp\\Analytics\\Database\\Factories\\": "database/factories/", + "AdvisingApp\\Analytics\\Database\\Seeders\\": "database/seeders/" + } + }, + "license": [ + "proprietary" + ], + "transport-options": { + "symlink": true, + "relative": true + } + }, { "name": "canyon-gbs/advising-app-application", "version": "1.0", @@ -2291,6 +2326,41 @@ "relative": true } }, + { + "name": "canyon-gbs/advising-app-inventory-management", + "version": "1.0", + "dist": { + "type": "path", + "url": "app-modules/inventory-management", + "reference": "4125a058df41583461c17a1538636b1baf92bf48" + }, + "require": { + "filament/filament": "^3.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "AdvisingApp\\InventoryManagement\\Providers\\InventoryManagementServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "AdvisingApp\\InventoryManagement\\": "src/", + "AdvisingApp\\InventoryManagement\\Tests\\": "tests/", + "AdvisingApp\\InventoryManagement\\Database\\Factories\\": "database/factories/", + "AdvisingApp\\InventoryManagement\\Database\\Seeders\\": "database/seeders/" + } + }, + "license": [ + "proprietary" + ], + "transport-options": { + "symlink": true, + "relative": true + } + }, { "name": "canyon-gbs/advising-app-knowledge-base", "version": "1.0", @@ -2469,6 +2539,41 @@ "relative": true } }, + { + "name": "canyon-gbs/advising-app-report", + "version": "1.0", + "dist": { + "type": "path", + "url": "app-modules/report", + "reference": "40ff87de1d9cf2f7932743e9910e1570c732dc04" + }, + "require": { + "filament/filament": "^3.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "AdvisingApp\\Report\\Providers\\ReportServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "AdvisingApp\\Report\\": "src/", + "AdvisingApp\\Report\\Tests\\": "tests/", + "AdvisingApp\\Report\\Database\\Factories\\": "database/factories/", + "AdvisingApp\\Report\\Database\\Seeders\\": "database/seeders/" + } + }, + "license": [ + "proprietary" + ], + "transport-options": { + "symlink": true, + "relative": true + } + }, { "name": "canyon-gbs/advising-app-service-management", "version": "1.0", @@ -2752,76 +2857,6 @@ "relative": true } }, - { - "name": "canyon-gbs/analytics", - "version": "1.0", - "dist": { - "type": "path", - "url": "app-modules/analytics", - "reference": "ee7fa00ee78ce3e31c49566043aaf89cca68ad6b" - }, - "require": { - "filament/filament": "^3.0.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "AdvisingApp\\Analytics\\Providers\\AnalyticsServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "AdvisingApp\\Analytics\\": "src/", - "AdvisingApp\\Analytics\\Tests\\": "tests/", - "AdvisingApp\\Analytics\\Database\\Factories\\": "database/factories/", - "AdvisingApp\\Analytics\\Database\\Seeders\\": "database/seeders/" - } - }, - "license": [ - "proprietary" - ], - "transport-options": { - "symlink": true, - "relative": true - } - }, - { - "name": "canyon-gbs/inventory-management", - "version": "1.0", - "dist": { - "type": "path", - "url": "app-modules/inventory-management", - "reference": "16210c789f3a6d5a4b14ce0db3a17f5cdc4f6e48" - }, - "require": { - "filament/filament": "^3.0.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "AdvisingApp\\InventoryManagement\\Providers\\InventoryManagementServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "AdvisingApp\\InventoryManagement\\": "src/", - "AdvisingApp\\InventoryManagement\\Tests\\": "tests/", - "AdvisingApp\\InventoryManagement\\Database\\Factories\\": "database/factories/", - "AdvisingApp\\InventoryManagement\\Database\\Seeders\\": "database/seeders/" - } - }, - "license": [ - "proprietary" - ], - "transport-options": { - "symlink": true, - "relative": true - } - }, { "name": "carbonphp/carbon-doctrine-types", "version": "2.1.0", @@ -5687,21 +5722,21 @@ }, { "name": "google/auth", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-auth-library-php.git", - "reference": "155daeadfd2f09743f611ea493b828d382519575" + "reference": "6e9c9fd4e2bbd7042f50083076346e4a1eff4e4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/155daeadfd2f09743f611ea493b828d382519575", - "reference": "155daeadfd2f09743f611ea493b828d382519575", + "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/6e9c9fd4e2bbd7042f50083076346e4a1eff4e4b", + "reference": "6e9c9fd4e2bbd7042f50083076346e4a1eff4e4b", "shasum": "" }, "require": { "firebase/php-jwt": "^6.0", - "guzzlehttp/guzzle": "^6.2.1|^7.0", + "guzzlehttp/guzzle": "^6.5.8||^7.4.5", "guzzlehttp/psr7": "^2.4.5", "php": "^7.4||^8.0", "psr/cache": "^1.0||^2.0||^3.0", @@ -5739,9 +5774,9 @@ "support": { "docs": "https://googleapis.github.io/google-auth-library-php/main/", "issues": "https://github.com/googleapis/google-auth-library-php/issues", - "source": "https://github.com/googleapis/google-auth-library-php/tree/v1.34.0" + "source": "https://github.com/googleapis/google-auth-library-php/tree/v1.35.0" }, - "time": "2024-01-03T20:45:15+00:00" + "time": "2024-02-01T20:41:08+00:00" }, { "name": "graham-campbell/result-type", diff --git a/database/migrations/2024_01_30_132008_create_exports_table.php b/database/migrations/2024_01_30_132008_create_exports_table.php new file mode 100644 index 0000000000..13fb6dfb43 --- /dev/null +++ b/database/migrations/2024_01_30_132008_create_exports_table.php @@ -0,0 +1,59 @@ + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +*/ + +use Illuminate\Support\Facades\Schema; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Migrations\Migration; + +return new class () extends Migration { + public function up(): void + { + Schema::create('exports', function (Blueprint $table) { + $table->uuid('id')->primary(); + + $table->timestamp('completed_at')->nullable(); + $table->string('file_disk'); + $table->string('file_name')->nullable(); + $table->string('exporter'); + $table->unsignedInteger('processed_rows')->default(0); + $table->unsignedInteger('total_rows'); + $table->unsignedInteger('successful_rows')->default(0); + $table->foreignUuid('user_id')->constrained()->cascadeOnDelete(); + + $table->timestamps(); + }); + } +}; diff --git a/resources/views/filament/forms/components/table.blade.php b/resources/views/filament/forms/components/table.blade.php new file mode 100644 index 0000000000..9c8071e9c4 --- /dev/null +++ b/resources/views/filament/forms/components/table.blade.php @@ -0,0 +1,36 @@ +{{-- + + + Copyright © 2022-2023, Canyon GBS LLC. All rights reserved. + + Advising App™ is licensed under the Elastic License 2.0. For more details, + see https://github.com/canyongbs/advisingapp/blob/main/LICENSE. + + Notice: + + - You may not provide the software to third parties as a hosted or managed + service, where the service provides users with access to any substantial set of + the features or functionality of the software. + - You may not move, change, disable, or circumvent the license key functionality + in the software, and you may not remove or obscure any functionality in the + software that is protected by the license key. + - You may not alter, remove, or obscure any licensing, copyright, or other notices + of the licensor in the software. Any use of the licensor’s trademarks is subject + to applicable law. + - Canyon GBS LLC respects the intellectual property rights of others and expects the + same in return. Canyon GBS™ and Advising App™ are registered trademarks of + Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks + vigorously. + - The software solution, including services, infrastructure, and code, is offered as a + Software as a Service (SaaS) by Canyon GBS LLC. + - Use of this software implies agreement to the license terms and conditions as stated + in the Elastic License 2.0. + + For more information or inquiries please visit our website at + https://www.canyongbs.com or contact us via email at legal@canyongbs.com. + + +--}} +
+ {{ $this->table }} +