From a09eebc9b837bd4c10033bd4abd2d8cd6edca640 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 11:34:34 -0500 Subject: [PATCH 001/126] Add all Interaction types Signed-off-by: Kevin Ullyott --- app-modules/division/graphql/division.graphql | 8 +++ .../src/Providers/DivisionServiceProvider.php | 4 ++ .../graphql/interaction-campaign.graphql | 22 +++++++ .../graphql/interaction-driver.graphql | 22 +++++++ .../graphql/interaction-outcome.graphql | 22 +++++++ .../graphql/interaction-relation.graphql | 22 +++++++ .../graphql/interaction-status.graphql | 25 ++++++++ .../graphql/interaction-type.graphql | 22 +++++++ .../interaction/graphql/interaction.graphql | 61 +++++++++++++++++++ .../interaction/src/Models/Interaction.php | 6 ++ .../Providers/InteractionServiceProvider.php | 8 +++ app/Concerns/GraphSchemaDiscovery.php | 14 +++++ 12 files changed, 236 insertions(+) create mode 100644 app-modules/division/graphql/division.graphql create mode 100644 app-modules/interaction/graphql/interaction-campaign.graphql create mode 100644 app-modules/interaction/graphql/interaction-driver.graphql create mode 100644 app-modules/interaction/graphql/interaction-outcome.graphql create mode 100644 app-modules/interaction/graphql/interaction-relation.graphql create mode 100644 app-modules/interaction/graphql/interaction-status.graphql create mode 100644 app-modules/interaction/graphql/interaction-type.graphql create mode 100644 app-modules/interaction/graphql/interaction.graphql diff --git a/app-modules/division/graphql/division.graphql b/app-modules/division/graphql/division.graphql new file mode 100644 index 0000000000..21e6fc4847 --- /dev/null +++ b/app-modules/division/graphql/division.graphql @@ -0,0 +1,8 @@ +type Division @model(class: "AdvisingApp\\Division\\Models\\Division") { + "Unique primary key." + id: ID! + + # TODO: Finish fields +} + +# TODO: Query and Mutate diff --git a/app-modules/division/src/Providers/DivisionServiceProvider.php b/app-modules/division/src/Providers/DivisionServiceProvider.php index bdf2954ca8..90a7e9de24 100644 --- a/app-modules/division/src/Providers/DivisionServiceProvider.php +++ b/app-modules/division/src/Providers/DivisionServiceProvider.php @@ -37,6 +37,7 @@ namespace AdvisingApp\Division\Providers; use Filament\Panel; +use App\Concerns\GraphSchemaDiscovery; use Illuminate\Support\ServiceProvider; use AdvisingApp\Division\DivisionPlugin; use AdvisingApp\Division\Models\Division; @@ -47,6 +48,8 @@ class DivisionServiceProvider extends ServiceProvider { + use GraphSchemaDiscovery; + public function register(): void { Panel::configureUsing(fn (Panel $panel) => $panel->plugin(new DivisionPlugin())); @@ -61,6 +64,7 @@ public function boot(): void $this->registerRolesAndPermissions(); $this->registerObservers(); + $this->discoverSchema(__DIR__ . '/../../graphql/division.graphql'); } protected function registerRolesAndPermissions(): void diff --git a/app-modules/interaction/graphql/interaction-campaign.graphql b/app-modules/interaction/graphql/interaction-campaign.graphql new file mode 100644 index 0000000000..066d042807 --- /dev/null +++ b/app-modules/interaction/graphql/interaction-campaign.graphql @@ -0,0 +1,22 @@ +type InteractionCampaign + @model(class: "AdvisingApp\\Interaction\\Models\\InteractionCampaign") { + "Unique primary key." + id: ID! + + "The name of the interaction campaign." + name: String! + + "Interactions related to this interaction campaign." + interactions: [Interaction!] @hasMany + + "The created date of the interaction campaign." + created_at: DateTime + + "The updated date of the interaction campaign." + updated_at: DateTime + + "The deleted date of the interaction campaign." + deleted_at: DateTime +} + +# TODO: Query and Mutate diff --git a/app-modules/interaction/graphql/interaction-driver.graphql b/app-modules/interaction/graphql/interaction-driver.graphql new file mode 100644 index 0000000000..f23014ccc3 --- /dev/null +++ b/app-modules/interaction/graphql/interaction-driver.graphql @@ -0,0 +1,22 @@ +type InteractionDriver + @model(class: "AdvisingApp\\Interaction\\Models\\InteractionDriver") { + "Unique primary key." + id: ID! + + "The name of the interaction driver." + name: String! + + "Interactions related to this interaction driver." + interactions: [Interaction!] @hasMany + + "The created date of the interaction driver." + created_at: DateTime + + "The updated date of the interaction driver." + updated_at: DateTime + + "The deleted date of the interaction driver." + deleted_at: DateTime +} + +# TODO: Query and Mutate diff --git a/app-modules/interaction/graphql/interaction-outcome.graphql b/app-modules/interaction/graphql/interaction-outcome.graphql new file mode 100644 index 0000000000..388ae190c0 --- /dev/null +++ b/app-modules/interaction/graphql/interaction-outcome.graphql @@ -0,0 +1,22 @@ +type InteractionOutcome + @model(class: "AdvisingApp\\Interaction\\Models\\InteractionOutcome") { + "Unique primary key." + id: ID! + + "The name of the interaction outcome." + name: String! + + "Interactions related to this interaction outcome." + interactions: [Interaction!] @hasMany + + "The created date of the interaction outcome." + created_at: DateTime + + "The updated date of the interaction outcome." + updated_at: DateTime + + "The deleted date of the interaction outcome." + deleted_at: DateTime +} + +# TODO: Query and Mutate diff --git a/app-modules/interaction/graphql/interaction-relation.graphql b/app-modules/interaction/graphql/interaction-relation.graphql new file mode 100644 index 0000000000..e401f3d5b9 --- /dev/null +++ b/app-modules/interaction/graphql/interaction-relation.graphql @@ -0,0 +1,22 @@ +type InteractionRelation + @model(class: "AdvisingApp\\Interaction\\Models\\InteractionRelation") { + "Unique primary key." + id: ID! + + "The name of the interaction relation." + name: String! + + "Interactions related to this interaction relation." + interactions: [Interaction!] @hasMany + + "The created date of the interaction relation." + created_at: DateTime + + "The updated date of the interaction relation." + updated_at: DateTime + + "The deleted date of the interaction relation." + deleted_at: DateTime +} + +# TODO: Query and Mutate diff --git a/app-modules/interaction/graphql/interaction-status.graphql b/app-modules/interaction/graphql/interaction-status.graphql new file mode 100644 index 0000000000..5ef757436b --- /dev/null +++ b/app-modules/interaction/graphql/interaction-status.graphql @@ -0,0 +1,25 @@ +type InteractionStatus + @model(class: "AdvisingApp\\Interaction\\Models\\InteractionStatus") { + "Unique primary key." + id: ID! + + "The name of the interaction driver." + name: String! + + "Interactions related to this interaction driver." + interactions: [Interaction!] @hasMany + + "The color of the interaction driver." + color: InteractionStatusColorOptions! + + "The created date of the interaction driver." + created_at: DateTime + + "The updated date of the interaction driver." + updated_at: DateTime + + "The deleted date of the interaction driver." + deleted_at: DateTime +} + +# TODO: Query and Mutate diff --git a/app-modules/interaction/graphql/interaction-type.graphql b/app-modules/interaction/graphql/interaction-type.graphql new file mode 100644 index 0000000000..89d6b8457f --- /dev/null +++ b/app-modules/interaction/graphql/interaction-type.graphql @@ -0,0 +1,22 @@ +type InteractionType + @model(class: "AdvisingApp\\Interaction\\Models\\InteractionType") { + "Unique primary key." + id: ID! + + "The name of the interaction type." + name: String! + + "Interactions related to this interaction type." + interactions: [Interaction!] @hasMany + + "The created date of the interaction type." + created_at: DateTime + + "The updated date of the interaction type." + updated_at: DateTime + + "The deleted date of the interaction type." + deleted_at: DateTime +} + +# TODO: Query and Mutate diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql new file mode 100644 index 0000000000..eb4747378f --- /dev/null +++ b/app-modules/interaction/graphql/interaction.graphql @@ -0,0 +1,61 @@ +union Interactable = Student | Prospect + +type Interaction + @model(class: "AdvisingApp\\Interaction\\Models\\Interaction") { + "Unique primary key." + id: ID! + + "The subject of the interaction." + subject: String! + + "The description of the interaction." + description: String! + + "The User related to the interaction." + user: User @belongsTo + + "The Interactable related to the interaction." + interactable: Interactable @morphTo + + "The type of interaction." + type: InteractionType @belongsTo + + "The relation of the interaction." + relation: InteractionRelation @belongsTo + + "The campaign of the interaction." + campaign: InteractionCampaign @belongsTo + + "The driver of the interaction." + driver: InteractionDriver @belongsTo + + "The status of the interaction." + status: InteractionStatus @belongsTo + + "The outcome of the interaction." + outcome: InteractionOutcome @belongsTo + + "The division of the interaction." + division: Division @belongsTo + + "The start datetime of the interaction." + start_datetime: DateTime! + + "The end datetime of the interaction." + end_datetime: DateTime + + "The created datetime of the interaction." + created_at: DateTime + + "The updated datetime of the interaction." + updated_at: DateTime +} + +# TODO: Query and Mutate + +#import ./interaction-campaign.graphql +#import ./interaction-driver.graphql +#import ./interaction-outcome.graphql +#import ./interaction-relation.graphql +#import ./interaction-status.graphql +#import ./interaction-type.graphql diff --git a/app-modules/interaction/src/Models/Interaction.php b/app-modules/interaction/src/Models/Interaction.php index a5f686911f..d79f0259e9 100644 --- a/app-modules/interaction/src/Models/Interaction.php +++ b/app-modules/interaction/src/Models/Interaction.php @@ -37,6 +37,7 @@ namespace AdvisingApp\Interaction\Models; use Exception; +use App\Models\User; use App\Models\BaseModel; use Illuminate\Support\Collection; use OwenIt\Auditing\Contracts\Auditable; @@ -79,6 +80,11 @@ class Interaction extends BaseModel implements Auditable, CanTriggerAutoSubscrip 'end_datetime' => 'datetime', ]; + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id'); + } + public function getWebPermissions(): Collection { return collect(['import', ...$this->webPermissions()]); diff --git a/app-modules/interaction/src/Providers/InteractionServiceProvider.php b/app-modules/interaction/src/Providers/InteractionServiceProvider.php index 0965250e62..578100739f 100644 --- a/app-modules/interaction/src/Providers/InteractionServiceProvider.php +++ b/app-modules/interaction/src/Providers/InteractionServiceProvider.php @@ -37,6 +37,7 @@ namespace AdvisingApp\Interaction\Providers; use Filament\Panel; +use App\Concerns\GraphSchemaDiscovery; use Illuminate\Support\ServiceProvider; use AdvisingApp\Interaction\InteractionPlugin; use AdvisingApp\Interaction\Models\Interaction; @@ -50,9 +51,12 @@ use AdvisingApp\Authorization\AuthorizationRoleRegistry; use AdvisingApp\Interaction\Observers\InteractionObserver; use AdvisingApp\Authorization\AuthorizationPermissionRegistry; +use AdvisingApp\Interaction\Enums\InteractionStatusColorOptions; class InteractionServiceProvider extends ServiceProvider { + use GraphSchemaDiscovery; + public function register() { Panel::configureUsing(fn (Panel $panel) => $panel->plugin(new InteractionPlugin())); @@ -72,6 +76,10 @@ public function boot() $this->registerRolesAndPermissions(); $this->registerObservers(); + + $this->discoverSchema(__DIR__ . '/../../graphql/interaction.graphql'); + + $this->registerEnum(InteractionStatusColorOptions::class); } protected function registerRolesAndPermissions() diff --git a/app/Concerns/GraphSchemaDiscovery.php b/app/Concerns/GraphSchemaDiscovery.php index 8244f9a31a..0756fb9811 100644 --- a/app/Concerns/GraphSchemaDiscovery.php +++ b/app/Concerns/GraphSchemaDiscovery.php @@ -37,8 +37,11 @@ namespace App\Concerns; use Illuminate\Support\Facades\Event; +use GraphQL\Type\Definition\PhpEnumType; +use Nuwave\Lighthouse\Schema\TypeRegistry; use Nuwave\Lighthouse\Events\BuildSchemaString; use Nuwave\Lighthouse\Schema\Source\SchemaStitcher; +use Nuwave\Lighthouse\Exceptions\DefinitionException; trait GraphSchemaDiscovery { @@ -48,4 +51,15 @@ public function discoverSchema(string $path): void return (new SchemaStitcher($path))->getSchemaString(); }); } + + /** + * @param class-string $enumClass + * + * @throws DefinitionException + */ + public function registerEnum(string $enumClass): void + { + $typeRegistry = app(TypeRegistry::class); + $typeRegistry->register(new PhpEnumType($enumClass)); + } } From ad38587a01f835148341555fd793fda3b72a0315 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 12:23:45 -0500 Subject: [PATCH 002/126] Start on query Signed-off-by: Kevin Ullyott --- .../interaction/graphql/interaction.graphql | 15 +++++++++++++++ package.json | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index eb4747378f..3dbb2bf2f4 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -53,6 +53,21 @@ type Interaction # TODO: Query and Mutate +extend type Query { + interaction( + "Search by primary key." + id: ID @eq @rules(apply: ["prohibits:email", "required_without:email"]) + + "Search by email address." + email: String + @eq + @rules(apply: ["prohibits:id", "required_without:id", "email"]) + ): User @find + + "List multiple interactions." + interactions: [Interaction!]! @paginate +} + #import ./interaction-campaign.graphql #import ./interaction-driver.graphql #import ./interaction-outcome.graphql diff --git a/package.json b/package.json index 7a0d81df99..9c89c5957b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "build": "npm run build:js-compile && npm run build:vite && npm run build:filament && npm run build:application && npm run build:form", "build:application": "(cd widgets/application && vite build)", "build:form": "(cd widgets/form && vite build)", - "api-docs:generate": "env-cmd spectaql spectaql.yml" + "api-docs:generate": "env-cmd spectaql spectaql.yml" }, "devDependencies": { "@headlessui/vue": "^1.7.16", From ec3d77ae4fdccd6e1218082ff709e6af249ec1a4 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 12:25:45 -0500 Subject: [PATCH 003/126] Ignore ide helpers Signed-off-by: Kevin Ullyott --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index ec9fa66b12..008f36801c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ app-modules/**/resources/js/dist/* /.phpactor.json **/.DS_Store public/api-docs/* +/_lighthouse_ide_helper.php +/programmatic-types.graphql +/schema-directives.graphql From adfd76d6a27903c23f2c03ebddd66603c13f21a5 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 13:08:01 -0500 Subject: [PATCH 004/126] Setup searching Signed-off-by: Kevin Ullyott --- .../interaction/graphql/interaction.graphql | 17 ++--- .../src/Policies/InteractionPolicy.php | 30 ++++----- composer.json | 1 + composer.lock | 63 ++++++++++++++++++- config/app.php | 2 + 5 files changed, 89 insertions(+), 24 deletions(-) diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index 3dbb2bf2f4..db7a78df5d 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -56,16 +56,17 @@ type Interaction extend type Query { interaction( "Search by primary key." - id: ID @eq @rules(apply: ["prohibits:email", "required_without:email"]) - - "Search by email address." - email: String - @eq - @rules(apply: ["prohibits:id", "required_without:id", "email"]) - ): User @find + id: ID @eq + ): User @find @canResolved(ability: "view") "List multiple interactions." - interactions: [Interaction!]! @paginate + interactions( + "Filters by name. Accepts SQL LIKE wildcards `%` and `_`." + subject: String @where(operator: "like") + hasType: _ @whereHasConditions + ): [Interaction!]! + @paginate + @canModel(ability: "viewAny") } #import ./interaction-campaign.graphql diff --git a/app-modules/interaction/src/Policies/InteractionPolicy.php b/app-modules/interaction/src/Policies/InteractionPolicy.php index fbb95865b6..929427cddf 100644 --- a/app-modules/interaction/src/Policies/InteractionPolicy.php +++ b/app-modules/interaction/src/Policies/InteractionPolicy.php @@ -36,63 +36,63 @@ namespace AdvisingApp\Interaction\Policies; -use App\Models\User; +use App\Models\Authenticatable; use Illuminate\Auth\Access\Response; use AdvisingApp\Interaction\Models\Interaction; class InteractionPolicy { - public function viewAny(User $user): Response + public function viewAny(Authenticatable $authenticatable): Response { - return $user->canOrElse( + return $authenticatable->canOrElse( abilities: 'interaction.view-any', denyResponse: 'You do not have permission to view interactions.' ); } - public function view(User $user, Interaction $interaction): Response + public function view(Authenticatable $authenticatable, Interaction $interaction): Response { - return $user->canOrElse( + return $authenticatable->canOrElse( abilities: ['interaction.*.view', "interaction.{$interaction->id}.view"], denyResponse: 'You do not have permission to view this interaction.' ); } - public function create(User $user): Response + public function create(Authenticatable $authenticatable): Response { - return $user->canOrElse( + return $authenticatable->canOrElse( abilities: 'interaction.create', denyResponse: 'You do not have permission to create interactions.' ); } - public function update(User $user, Interaction $interaction): Response + public function update(Authenticatable $authenticatable, Interaction $interaction): Response { - return $user->canOrElse( + return $authenticatable->canOrElse( abilities: ['interaction.*.update', "interaction.{$interaction->id}.update"], denyResponse: 'You do not have permission to update this interaction.' ); } - public function delete(User $user, Interaction $interaction): Response + public function delete(Authenticatable $authenticatable, Interaction $interaction): Response { - return $user->canOrElse( + return $authenticatable->canOrElse( abilities: ['interaction.*.delete', "interaction.{$interaction->id}.delete"], denyResponse: 'You do not have permission to delete this interaction.' ); } - public function restore(User $user, Interaction $interaction): Response + public function restore(Authenticatable $authenticatable, Interaction $interaction): Response { - return $user->canOrElse( + return $authenticatable->canOrElse( abilities: ['interaction.*.restore', "interaction.{$interaction->id}.restore"], denyResponse: 'You do not have permission to restore this interaction.' ); } - public function forceDelete(User $user, Interaction $interaction): Response + public function forceDelete(Authenticatable $authenticatable, Interaction $interaction): Response { - return $user->canOrElse( + return $authenticatable->canOrElse( abilities: ['interaction.*.force-delete', "interaction.{$interaction->id}.force-delete"], denyResponse: 'You do not have permission to permanently delete this interaction.' ); diff --git a/composer.json b/composer.json index 2721152d95..9477b6a447 100644 --- a/composer.json +++ b/composer.json @@ -72,6 +72,7 @@ "lomkit/laravel-rest-api": "^2.3", "maatwebsite/excel": "^3.1", "microsoft/microsoft-graph": "^1.109", + "mll-lab/graphql-php-scalars": "^6.2", "nuwave/lighthouse": "^6.28", "owen-it/laravel-auditing": "dev-feature/queued-auditing@dev", "saade/filament-fullcalendar": "^3.1", diff --git a/composer.lock b/composer.lock index b5876dcc02..168aa67183 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": "9bfaad2414272ba1599b5e9c467e60eb", + "content-hash": "114ab3eac3c7a0e2278d79df819b4371", "packages": [ { "name": "awcodes/filament-tiptap-editor", @@ -7715,6 +7715,67 @@ }, "time": "2023-12-01T09:41:49+00:00" }, + { + "name": "mll-lab/graphql-php-scalars", + "version": "v6.2.0", + "source": { + "type": "git", + "url": "https://github.com/mll-lab/graphql-php-scalars.git", + "reference": "f0922636f090bc1e56f6a9efb3ebb8272aa5cff3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mll-lab/graphql-php-scalars/zipball/f0922636f090bc1e56f6a9efb3ebb8272aa5cff3", + "reference": "f0922636f090bc1e56f6a9efb3ebb8272aa5cff3", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.17 || ^3 || ^4", + "ext-json": "*", + "php": "^8", + "spatie/regex": "^1.4 || ^2 || ^3", + "thecodingmachine/safe": "^1.3 || ^2", + "webonyx/graphql-php": "^15" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.16", + "mll-lab/php-cs-fixer-config": "^5", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^1", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1", + "phpunit/phpunit": "^9 || ^10", + "symfony/var-dumper": "^5.4 || ^6", + "thecodingmachine/phpstan-safe-rule": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "MLL\\GraphQLScalars\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Benedikt Franke", + "email": "benedikt@franke.tech" + } + ], + "description": "A collection of custom scalar types for usage with https://github.com/webonyx/graphql-php", + "keywords": [ + "graphql", + "php" + ], + "support": { + "issues": "https://github.com/mll-lab/graphql-php-scalars/issues", + "source": "https://github.com/mll-lab/graphql-php-scalars/tree/v6.2.0" + }, + "time": "2023-05-09T13:20:50+00:00" + }, { "name": "monolog/monolog", "version": "3.5.0", diff --git a/config/app.php b/config/app.php index 3766ace6d2..19e1923db0 100644 --- a/config/app.php +++ b/config/app.php @@ -36,6 +36,7 @@ use Illuminate\Support\Facades\Facade; use App\Providers\MultiConnectionParallelTestingServiceProvider; +use Nuwave\Lighthouse\WhereConditions\WhereConditionsServiceProvider; return [ /* @@ -233,6 +234,7 @@ App\Providers\HealthServiceProvider::class, App\Providers\FilamentServiceProvider::class, MultiConnectionParallelTestingServiceProvider::class, + WhereConditionsServiceProvider::class, ], /* From b35146910e321dbc2fe3e349912d6036f75e70a3 Mon Sep 17 00:00:00 2001 From: Matthew Myers Date: Thu, 14 Dec 2023 15:32:28 -0500 Subject: [PATCH 005/126] wip --- app-modules/alert/graphql/alert.graphql | 30 +++++++++++++++++++ .../src/Providers/AlertServiceProvider.php | 5 ++++ 2 files changed, 35 insertions(+) create mode 100644 app-modules/alert/graphql/alert.graphql diff --git a/app-modules/alert/graphql/alert.graphql b/app-modules/alert/graphql/alert.graphql new file mode 100644 index 0000000000..153ac1f8b9 --- /dev/null +++ b/app-modules/alert/graphql/alert.graphql @@ -0,0 +1,30 @@ +type Alert + @model(class: "AdvisingApp\\Alert\\Models\\Alert") { + "Unique primary key." + id: ID! + "The concern of the alert." + concern: Educatable! @morphTo + description: String! + severity: String! + status: String! + suggested_intervention: String!, + "The created date of the alert." + created_at: DateTime + "The updated date of the alert." + updated_at: DateTime + "The deleted date of the alert." + updated_at: DateTime +} + +extend type Query { + "Get an alert by its primary key." + alert( + "Search by primary key." + id: ID! @whereKey + ): Alert @find @canResolved(ability: "view") + + "Get all alerts." + alerts: [Alert!]! + @paginate + @canModel(ability: "viewAny") +} diff --git a/app-modules/alert/src/Providers/AlertServiceProvider.php b/app-modules/alert/src/Providers/AlertServiceProvider.php index 97223383a5..080ffb547b 100644 --- a/app-modules/alert/src/Providers/AlertServiceProvider.php +++ b/app-modules/alert/src/Providers/AlertServiceProvider.php @@ -36,6 +36,7 @@ namespace AdvisingApp\Alert\Providers; +use App\Concerns\GraphSchemaDiscovery; use Filament\Panel; use AdvisingApp\Alert\AlertPlugin; use AdvisingApp\Alert\Models\Alert; @@ -50,6 +51,8 @@ class AlertServiceProvider extends ServiceProvider { + use GraphSchemaDiscovery; + public function register(): void { Panel::configureUsing(fn (Panel $panel) => $panel->plugin(new AlertPlugin())); @@ -66,6 +69,8 @@ public function boot(): void $this->registerObservers(); $this->registerEvents(); + + $this->discoverSchema(__DIR__ . '/../../graphql/alert.graphql'); } protected function registerRolesAndPermissions() From 965fc6d3542b9388d51aff103a2eb8b842176803 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 16:16:50 -0500 Subject: [PATCH 006/126] Fix issues Signed-off-by: Kevin Ullyott --- .../interaction/graphql/interaction.graphql | 41 +++++++++++++++---- package.json | 2 +- spectaql.yml | 2 +- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index db7a78df5d..6f30458d02 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -14,6 +14,7 @@ type Interaction "The User related to the interaction." user: User @belongsTo + # TODO: A ServiceRequest "The Interactable related to the interaction." interactable: Interactable @morphTo @@ -53,20 +54,42 @@ type Interaction # TODO: Query and Mutate +enum QueryInteractionsWhereColumn { + subject + description + start_datetime + end_datetime + created_at + updated_at +} + +enum QueryInteractionsHasUserColumn { + id + email +} + +enum QueryInteractionsHasTypeColumn { + id + name +} + extend type Query { + "Find a single interaction by an identifying attribute." interaction( - "Search by primary key." - id: ID @eq - ): User @find @canResolved(ability: "view") + "The value of the attribute to match." + id: ID! @whereKey + ): Interaction @find @canResolved(ability: "view") "List multiple interactions." interactions( - "Filters by name. Accepts SQL LIKE wildcards `%` and `_`." - subject: String @where(operator: "like") - hasType: _ @whereHasConditions - ): [Interaction!]! - @paginate - @canModel(ability: "viewAny") + where: _ @whereConditions(columnsEnum: QueryInteractionsWhereColumn) + + "Filter by the user relationship." + hasUser: _ @whereHasConditions(columnsEnum: QueryInteractionsHasUserColumn) + + "Filter by the type relationship." + hasType: _ @whereHasConditions(columnsEnum: QueryInteractionsHasTypeColumn) + ): [Interaction!]! @paginate @canModel(ability: "viewAny") } #import ./interaction-campaign.graphql diff --git a/package.json b/package.json index 9c89c5957b..8392030b03 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "build": "npm run build:js-compile && npm run build:vite && npm run build:filament && npm run build:application && npm run build:form", "build:application": "(cd widgets/application && vite build)", "build:form": "(cd widgets/form && vite build)", - "api-docs:generate": "env-cmd spectaql spectaql.yml" + "api-docs:generate": "export NODE_OPTIONS=--max_old_space_size=4096 && env-cmd spectaql spectaql.yml" }, "devDependencies": { "@headlessui/vue": "^1.7.16", diff --git a/spectaql.yml b/spectaql.yml index f147ea7357..a466d3997b 100644 --- a/spectaql.yml +++ b/spectaql.yml @@ -12,7 +12,7 @@ introspection: # metadataFile: ./examples/data/metadata.json # dynamicExamplesProcessingModule: ./examples/customizations/examples queryNameStrategy: capitalizeFirst - fieldExpansionDepth: 20 + fieldExpansionDepth: 3 spectaqlDirective: enable: true From e3667c22c1db9549623909f58be81da9777b1ec2 Mon Sep 17 00:00:00 2001 From: Matthew Myers Date: Thu, 14 Dec 2023 16:39:54 -0500 Subject: [PATCH 007/126] wip --- app-modules/alert/graphql/alert.graphql | 33 +++++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/app-modules/alert/graphql/alert.graphql b/app-modules/alert/graphql/alert.graphql index 153ac1f8b9..2242ae5cea 100644 --- a/app-modules/alert/graphql/alert.graphql +++ b/app-modules/alert/graphql/alert.graphql @@ -5,15 +5,15 @@ type Alert "The concern of the alert." concern: Educatable! @morphTo description: String! - severity: String! - status: String! +# severity: String! +# status: String! suggested_intervention: String!, "The created date of the alert." - created_at: DateTime + created_at: DateTime! "The updated date of the alert." - updated_at: DateTime + updated_at: DateTime! "The deleted date of the alert." - updated_at: DateTime + deleted_at: DateTime } extend type Query { @@ -28,3 +28,26 @@ extend type Query { @paginate @canModel(ability: "viewAny") } + +extend type Mutation { + "Create a new alert." + createAlert( + "The id of the concern of the alert." + concern_id: ID! @rules(apply: ["required"]) + + "The type of concern of the alert." + concern_type: String! @rules(apply: ["required", "in:student,prospect"]) + + "The description of the alert." + description: String! @rules(apply: ["required", "string"]) + + "The suggested intervention for the alert." + suggested_intervention: String! @rules(apply: ["required", "string"]) + ): Alert! @create @canModel(ability: "create") + + "Delete an existing alert." + deleteAlert( + "The primary key of the alert." + id: ID! @whereKey + ): Alert @delete @canFind(ability: "delete", find: "id") +} From a5f399798b2cd2944a68003f3110bae5574df2be Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 16:54:04 -0500 Subject: [PATCH 008/126] Write the queries Signed-off-by: Kevin Ullyott --- .../interaction/graphql/interaction.graphql | 73 ++++++++++++++++++- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index 6f30458d02..b1722de001 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -73,6 +73,40 @@ enum QueryInteractionsHasTypeColumn { name } +enum QueryInteractionsHasInteractableEnum { + full_name +} + +enum QueryInteractionsHasRelationColumn { + id + name +} + +enum QueryInteractionsHasCampaignColumn { + id + name +} + +enum QueryInteractionsHasDriverColumn { + id + name +} + +enum QueryInteractionsHasStatusColumn { + id + name + color +} + +enum QueryInteractionsHasOutcomeColumn { + id + name +} + +enum QueryDivisionsWhereColumn{ + id +} + extend type Query { "Find a single interaction by an identifying attribute." interaction( @@ -82,13 +116,46 @@ extend type Query { "List multiple interactions." interactions( + "Filter by the interactions attributes." where: _ @whereConditions(columnsEnum: QueryInteractionsWhereColumn) - + "Filter by the user relationship." - hasUser: _ @whereHasConditions(columnsEnum: QueryInteractionsHasUserColumn) + hasUser: _ + @whereHasConditions(columnsEnum: QueryInteractionsHasUserColumn) + + # TODO: Look into improving Polymorphic whereHas + hasInteractable: _ + @whereHasConditions( + columnsEnum: QueryInteractionsHasInteractableEnum + ) "Filter by the type relationship." - hasType: _ @whereHasConditions(columnsEnum: QueryInteractionsHasTypeColumn) + hasType: _ + @whereHasConditions(columnsEnum: QueryInteractionsHasTypeColumn) + + "Filter by the relation relationship." + hasRelation: _ + @whereHasConditions(columnsEnum: QueryInteractionsHasRelationColumn) + + "Filter by the campaign relationship." + hasCampaign: _ + @whereHasConditions(columnsEnum: QueryInteractionsHasCampaignColumn) + + "Filter by the driver relationship." + hasDriver: _ + @whereHasConditions(columnsEnum: QueryInteractionsHasDriverColumn) + + "Filter by the status relationship." + hasStatus: _ + @whereHasConditions(columnsEnum: QueryInteractionsHasStatusColumn) + + "Filter by the outcome relationship." + hasOutcome: _ + @whereHasConditions(columnsEnum: QueryInteractionsHasOutcomeColumn) + + "Filter by the division relationship." + hasDivision: _ + @whereHasConditions(columnsEnum: QueryDivisionsWhereColumn) ): [Interaction!]! @paginate @canModel(ability: "viewAny") } From 20c5508036670255888556839ba02c80da3e58b0 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 17:33:27 -0500 Subject: [PATCH 009/126] More work on interaction Signed-off-by: Kevin Ullyott --- .../interaction/graphql/interaction.graphql | 15 +++++++++++++-- .../graphql/service-management.graphql | 8 ++++++++ .../ServiceManagementServiceProvider.php | 5 +++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 app-modules/service-management/graphql/service-management.graphql diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index b1722de001..3c5f74f9a9 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -14,7 +14,6 @@ type Interaction "The User related to the interaction." user: User @belongsTo - # TODO: A ServiceRequest "The Interactable related to the interaction." interactable: Interactable @morphTo @@ -103,7 +102,7 @@ enum QueryInteractionsHasOutcomeColumn { name } -enum QueryDivisionsWhereColumn{ +enum QueryDivisionsWhereColumn { id } @@ -159,6 +158,18 @@ extend type Query { ): [Interaction!]! @paginate @canModel(ability: "viewAny") } +extend type Mutation { + "Create a interaction." + createInteraction( + "The subject of the interaction." + subject: String! + + "The description of the interaction." + description: String! + + ): Interaction! @create @canModel(ability: "create") +} + #import ./interaction-campaign.graphql #import ./interaction-driver.graphql #import ./interaction-outcome.graphql diff --git a/app-modules/service-management/graphql/service-management.graphql b/app-modules/service-management/graphql/service-management.graphql new file mode 100644 index 0000000000..62725b0ebf --- /dev/null +++ b/app-modules/service-management/graphql/service-management.graphql @@ -0,0 +1,8 @@ +type ServiceRequest @model(class: "AdvisingApp\\ServiceManagement\\Models\\ServiceRequest") { + "Unique primary key." + id: ID! + + # TODO: Finish fields +} + +# TODO: Query and Mutate \ No newline at end of file diff --git a/app-modules/service-management/src/Providers/ServiceManagementServiceProvider.php b/app-modules/service-management/src/Providers/ServiceManagementServiceProvider.php index f0ec97d350..6cd39f6c19 100644 --- a/app-modules/service-management/src/Providers/ServiceManagementServiceProvider.php +++ b/app-modules/service-management/src/Providers/ServiceManagementServiceProvider.php @@ -37,6 +37,7 @@ namespace AdvisingApp\ServiceManagement\Providers; use Filament\Panel; +use App\Concerns\GraphSchemaDiscovery; use Illuminate\Support\ServiceProvider; use Illuminate\Database\Eloquent\Relations\Relation; use AdvisingApp\Authorization\AuthorizationRoleRegistry; @@ -58,6 +59,8 @@ class ServiceManagementServiceProvider extends ServiceProvider { + use GraphSchemaDiscovery; + public function register(): void { Panel::configureUsing(fn (Panel $panel) => $panel->plugin(new ServiceManagementPlugin())); @@ -79,6 +82,8 @@ public function boot(): void $this->registerRolesAndPermissions(); $this->registerObservers(); + + $this->discoverSchema(__DIR__ . '/../../graphql/service-management.graphql'); } protected function registerObservers(): void From 26f5879ccb44be6322ac6ca5c255e79c8dc59577 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 17:58:35 -0500 Subject: [PATCH 010/126] more work Signed-off-by: Kevin Ullyott --- app-modules/interaction/graphql/interaction.graphql | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index 3c5f74f9a9..3f2e198f44 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -167,6 +167,15 @@ extend type Mutation { "The description of the interaction." description: String! + "The User related to the interaction." + user_id: ID! @rules(apply: ["required", "exists:users,id"]) + + "The Interactable related to the interaction." + interactable_id: ID! @rules(apply: ["required", "exists:students,id"]) + + "The type of interaction." + interactable_type: String + ): Interaction! @create @canModel(ability: "create") } From f0cb54af9b409a9ff41b6ed1260e1a1ceea3dac7 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 18:01:11 -0500 Subject: [PATCH 011/126] updates Signed-off-by: Kevin Ullyott --- app-modules/care-team/graphql/care-team.graphql | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app-modules/care-team/graphql/care-team.graphql b/app-modules/care-team/graphql/care-team.graphql index 635b180d0d..45de4d9667 100644 --- a/app-modules/care-team/graphql/care-team.graphql +++ b/app-modules/care-team/graphql/care-team.graphql @@ -28,8 +28,8 @@ extend type Mutation { user_id: ID! @rules( apply: [ - "required" - "exists:users,id" + "required", + "exists:users,id", "AdvisingApp\\CareTeam\\Rules\\UniqueCareTeamRule" ] ) @@ -38,8 +38,7 @@ extend type Mutation { educatable_id: ID! @rules( apply: [ - "required" - "AdvisingApp\\CareTeam\\Rules\\EducatableIdExistsRule" + "required", "AdvisingApp\\CareTeam\\Rules\\EducatableIdExistsRule" ] ) From 0a3cbd652003b65c994950bec79c7a8d9470447a Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 14 Dec 2023 19:03:28 -0500 Subject: [PATCH 012/126] Define update mutation Signed-off-by: Kevin Ullyott --- .../interaction/graphql/interaction.graphql | 103 +++++++++++++++++- .../src/Rules/InteractableIdExistsRules.php | 31 ++++++ 2 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 app-modules/interaction/src/Rules/InteractableIdExistsRules.php diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index 3f2e198f44..f4bc922c10 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -158,24 +158,119 @@ extend type Query { ): [Interaction!]! @paginate @canModel(ability: "viewAny") } +input UpdateInteractionInput { + "The subject of the interaction." + subject: String @rules(apply: ["string", "max:255"]) + + "The description of the interaction." + description: String @rules(apply: ["string"]) + + "The User related to the interaction." + user_id: ID @rules(apply: ["exists:users,id"]) + + "The Interactable related to the interaction." + interactable_id: ID + @rules( + apply: [ + "AdvisingApp\\Interaction\\Rules\\InteractableIdExistsRules" + "required_with:interactable_type" + ] + ) + + "The type of Interactable related to the interaction." + interactable_type: String @rules( + apply: [ + "in:student,prospect,service_request", + "required_with:interactable_id" + ] + ) + + "The type of interaction." + interaction_type_id: ID @rules(apply: ["exists:interaction_types,id"]) + + "The relation of the interaction." + interaction_relation_id: ID @rules(apply: ["exists:interaction_relations,id"]) + + "The campaign of the interaction." + interaction_campaign_id: ID @rules(apply: ["exists:interaction_campaigns,id"]) + + "The driver of the interaction." + interaction_driver_id: ID @rules(apply: ["exists:interaction_drivers,id"]) + + "The status of the interaction." + interaction_status_id: ID @rules(apply: ["exists:interaction_statuses,id"]) + + "The outcome of the interaction." + interaction_outcome_id: ID @rules(apply: ["exists:interaction_outcomes,id"]) + + "The division of the interaction." + division_id: ID @rules(apply: ["exists:divisions,id"]) + + "The start datetime of the interaction." + start_datetime: DateTime @rules(apply: ["date_format:Y-m-d H:i:s"]) + + "The end datetime of the interaction." + end_datetime: DateTime @rules(apply: ["nullable", "date_format:Y-m-d H:i:s"]) +} + extend type Mutation { "Create a interaction." createInteraction( "The subject of the interaction." - subject: String! + subject: String! @rules(apply: ["required", "string", "max:255"]) "The description of the interaction." - description: String! + description: String! @rules(apply: ["required", "string"]) "The User related to the interaction." user_id: ID! @rules(apply: ["required", "exists:users,id"]) "The Interactable related to the interaction." - interactable_id: ID! @rules(apply: ["required", "exists:students,id"]) + interactable_id: ID! + @rules( + apply: [ + "required", + "AdvisingApp\\Interaction\\Rules\\InteractableIdExistsRules" + ] + ) + + "The type of Interactable related to the interaction." + interactable_type: String! @rules(apply: ["required", "in:student,prospect,service_request"]) "The type of interaction." - interactable_type: String + interaction_type_id: ID! @rules(apply: ["required", "exists:interaction_types,id"]) + + "The relation of the interaction." + interaction_relation_id: ID! @rules(apply: ["required", "exists:interaction_relations,id"]) + + "The campaign of the interaction." + interaction_campaign_id: ID! @rules(apply: ["required", "exists:interaction_campaigns,id"]) + + "The driver of the interaction." + interaction_driver_id: ID! @rules(apply: ["required", "exists:interaction_drivers,id"]) + + "The status of the interaction." + interaction_status_id: ID! @rules(apply: ["required", "exists:interaction_statuses,id"]) + + "The outcome of the interaction." + interaction_outcome_id: ID! @rules(apply: ["required", "exists:interaction_outcomes,id"]) + + "The division of the interaction." + division_id: ID! @rules(apply: ["required", "exists:divisions,id"]) + + "The start datetime of the interaction." + start_datetime: DateTime! @rules(apply: ["required", "date_format:Y-m-d H:i:s"]) + + "The end datetime of the interaction." + end_datetime: DateTime @rules(apply: ["nullable", "date_format:Y-m-d H:i:s"]) + ): Interaction! @create @canModel(ability: "create") + + "Update a interaction." + updateInteraction( + "The identifier of the interaction you would like to update." + id: ID! @rules(apply: ["required", "exists:interactions,id"]) + input: UpdateInteractionInput! ): Interaction! @create @canModel(ability: "create") } diff --git a/app-modules/interaction/src/Rules/InteractableIdExistsRules.php b/app-modules/interaction/src/Rules/InteractableIdExistsRules.php new file mode 100644 index 0000000000..7ba7591bf7 --- /dev/null +++ b/app-modules/interaction/src/Rules/InteractableIdExistsRules.php @@ -0,0 +1,31 @@ +data['interactable_type'])::query() + ->whereKey($value) + ->exists() + ) { + $fail('The interactable does not exist.'); + } + } + + public function setData(array $data): static + { + $this->data = $data; + + return $this; + } +} From e8ccd50cb6637d5acf6b1cf0bea8be05dc1c0737 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 14 Dec 2023 20:06:34 -0500 Subject: [PATCH 013/126] Create survey module. --- app-modules/form/src/FormPlugin.php | 4 - app-modules/survey/composer.json | 26 ++ .../survey/config/permissions/api/custom.php | 37 +++ .../survey/config/permissions/web/custom.php | 37 +++ .../survey/config/roles/api/survey_roles.php | 37 +++ .../survey/config/roles/web/survey_roles.php | 37 +++ .../survey/database/factories/.gitkeep | 0 ...2023_12_14_194215_create_surveys_table.php | 61 +++++ ...12_14_194216_create_survey_steps_table.php | 55 ++++ ...2_14_194217_create_survey_fields_table.php | 58 +++++ ...18_create_survey_authentications_table.php | 57 ++++ ...194219_create_survey_submissions_table.php | 62 +++++ ...0_create_survey_field_submission_table.php | 54 ++++ app-modules/survey/database/seeders/.gitkeep | 0 .../views/livewire/render-survey.blade.php | 42 +++ .../resources/views/submission.blade.php | 55 ++++ app-modules/survey/routes/api.php | 64 +++++ .../routes/web.php} | 26 +- .../src/Filament/Resources/SurveyResource.php | 82 ++++++ .../Concerns/HasSharedFormConfiguration.php | 241 +++++++++++++++++ .../SurveyResource/Pages/CreateSurvey.php | 55 ++++ .../SurveyResource/Pages/EditSurvey.php | 98 +++++++ .../SurveyResource/Pages/ListSurveys.php | 85 ++++++ .../Pages/ManageSurveySubmissions.php | 148 +++++++++++ .../Controllers/SurveyWidgetController.php | 244 ++++++++++++++++++ .../EnsureSurveysFeatureIsActive.php | 54 ++++ .../survey/src/Livewire/RenderSurvey.php | 60 +++++ app-modules/survey/src/Models/Survey.php | 83 ++++++ .../src/Models/SurveyAuthentication.php | 54 ++++ app-modules/survey/src/Models/SurveyField.php | 71 +++++ app-modules/survey/src/Models/SurveyStep.php | 71 +++++ .../survey/src/Models/SurveySubmission.php | 134 ++++++++++ .../src/Providers/SurveyServiceProvider.php | 97 +++++++ app-modules/survey/src/SurveyPlugin.php | 59 +++++ app-modules/survey/tests/.gitkeep | 0 composer.json | 1 + composer.lock | 37 ++- 37 files changed, 2365 insertions(+), 21 deletions(-) create mode 100644 app-modules/survey/composer.json create mode 100644 app-modules/survey/config/permissions/api/custom.php create mode 100644 app-modules/survey/config/permissions/web/custom.php create mode 100644 app-modules/survey/config/roles/api/survey_roles.php create mode 100644 app-modules/survey/config/roles/web/survey_roles.php create mode 100644 app-modules/survey/database/factories/.gitkeep create mode 100644 app-modules/survey/database/migrations/2023_12_14_194215_create_surveys_table.php create mode 100644 app-modules/survey/database/migrations/2023_12_14_194216_create_survey_steps_table.php create mode 100644 app-modules/survey/database/migrations/2023_12_14_194217_create_survey_fields_table.php create mode 100644 app-modules/survey/database/migrations/2023_12_14_194218_create_survey_authentications_table.php create mode 100644 app-modules/survey/database/migrations/2023_12_14_194219_create_survey_submissions_table.php create mode 100644 app-modules/survey/database/migrations/2023_12_14_194220_create_survey_field_submission_table.php create mode 100644 app-modules/survey/database/seeders/.gitkeep create mode 100644 app-modules/survey/resources/views/livewire/render-survey.blade.php create mode 100644 app-modules/survey/resources/views/submission.blade.php create mode 100644 app-modules/survey/routes/api.php rename app-modules/{form/src/Filament/Pages/ManageSurveys.php => survey/routes/web.php} (78%) create mode 100644 app-modules/survey/src/Filament/Resources/SurveyResource.php create mode 100644 app-modules/survey/src/Filament/Resources/SurveyResource/Pages/Concerns/HasSharedFormConfiguration.php create mode 100644 app-modules/survey/src/Filament/Resources/SurveyResource/Pages/CreateSurvey.php create mode 100644 app-modules/survey/src/Filament/Resources/SurveyResource/Pages/EditSurvey.php create mode 100644 app-modules/survey/src/Filament/Resources/SurveyResource/Pages/ListSurveys.php create mode 100644 app-modules/survey/src/Filament/Resources/SurveyResource/Pages/ManageSurveySubmissions.php create mode 100644 app-modules/survey/src/Http/Controllers/SurveyWidgetController.php create mode 100644 app-modules/survey/src/Http/Middleware/EnsureSurveysFeatureIsActive.php create mode 100644 app-modules/survey/src/Livewire/RenderSurvey.php create mode 100644 app-modules/survey/src/Models/Survey.php create mode 100644 app-modules/survey/src/Models/SurveyAuthentication.php create mode 100644 app-modules/survey/src/Models/SurveyField.php create mode 100644 app-modules/survey/src/Models/SurveyStep.php create mode 100644 app-modules/survey/src/Models/SurveySubmission.php create mode 100644 app-modules/survey/src/Providers/SurveyServiceProvider.php create mode 100644 app-modules/survey/src/SurveyPlugin.php create mode 100644 app-modules/survey/tests/.gitkeep diff --git a/app-modules/form/src/FormPlugin.php b/app-modules/form/src/FormPlugin.php index cef599ecfe..e3134ecd1d 100644 --- a/app-modules/form/src/FormPlugin.php +++ b/app-modules/form/src/FormPlugin.php @@ -52,10 +52,6 @@ public function register(Panel $panel): void ->discoverResources( in: __DIR__ . '/Filament/Resources', for: 'AdvisingApp\\Form\\Filament\\Resources' - ) - ->discoverPages( - in: __DIR__ . '/Filament/Pages', - for: 'AdvisingApp\\Form\\Filament\\Pages' ); } diff --git a/app-modules/survey/composer.json b/app-modules/survey/composer.json new file mode 100644 index 0000000000..07d5e5394a --- /dev/null +++ b/app-modules/survey/composer.json @@ -0,0 +1,26 @@ +{ + "name": "canyon-gbs/advising-app-survey", + "description": "", + "type": "library", + "version": "1.0", + "license": "proprietary", + "require": { + "filament/filament": "^3.0.0" + }, + "autoload": { + "psr-4": { + "AdvisingApp\\Survey\\": "src/", + "AdvisingApp\\Survey\\Tests\\": "tests/", + "AdvisingApp\\Survey\\Database\\Factories\\": "database/factories/", + "AdvisingApp\\Survey\\Database\\Seeders\\": "database/seeders/" + } + }, + "minimum-stability": "dev", + "extra": { + "laravel": { + "providers": [ + "AdvisingApp\\Survey\\Providers\\SurveyServiceProvider" + ] + } + } +} diff --git a/app-modules/survey/config/permissions/api/custom.php b/app-modules/survey/config/permissions/api/custom.php new file mode 100644 index 0000000000..2c194089ba --- /dev/null +++ b/app-modules/survey/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/survey/config/permissions/web/custom.php b/app-modules/survey/config/permissions/web/custom.php new file mode 100644 index 0000000000..2c194089ba --- /dev/null +++ b/app-modules/survey/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/survey/config/roles/api/survey_roles.php b/app-modules/survey/config/roles/api/survey_roles.php new file mode 100644 index 0000000000..2c194089ba --- /dev/null +++ b/app-modules/survey/config/roles/api/survey_roles.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/survey/config/roles/web/survey_roles.php b/app-modules/survey/config/roles/web/survey_roles.php new file mode 100644 index 0000000000..2c194089ba --- /dev/null +++ b/app-modules/survey/config/roles/web/survey_roles.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/survey/database/factories/.gitkeep b/app-modules/survey/database/factories/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app-modules/survey/database/migrations/2023_12_14_194215_create_surveys_table.php b/app-modules/survey/database/migrations/2023_12_14_194215_create_surveys_table.php new file mode 100644 index 0000000000..cd9aeb0fbf --- /dev/null +++ b/app-modules/survey/database/migrations/2023_12_14_194215_create_surveys_table.php @@ -0,0 +1,61 @@ + + + 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('surveys', function (Blueprint $table) { + $table->uuid('id')->primary(); + + $table->string('name')->unique(); + $table->text('description')->nullable(); + $table->boolean('embed_enabled')->default(false); + $table->json('allowed_domains')->nullable(); + $table->string('primary_color')->nullable(); + $table->string('rounding')->nullable(); + $table->boolean('is_authenticated')->default(false); + $table->boolean('is_wizard')->default(false); + $table->json('content')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + }); + } +}; diff --git a/app-modules/survey/database/migrations/2023_12_14_194216_create_survey_steps_table.php b/app-modules/survey/database/migrations/2023_12_14_194216_create_survey_steps_table.php new file mode 100644 index 0000000000..c0c413c48b --- /dev/null +++ b/app-modules/survey/database/migrations/2023_12_14_194216_create_survey_steps_table.php @@ -0,0 +1,55 @@ + + + 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('survey_steps', function (Blueprint $table) { + $table->uuid('id')->primary(); + + $table->text('label'); + $table->json('content')->nullable(); + $table->foreignUuid('survey_id')->constrained('surveys')->cascadeOnDelete(); + $table->integer('sort'); + + $table->timestamps(); + }); + } +}; diff --git a/app-modules/survey/database/migrations/2023_12_14_194217_create_survey_fields_table.php b/app-modules/survey/database/migrations/2023_12_14_194217_create_survey_fields_table.php new file mode 100644 index 0000000000..7292eac5cc --- /dev/null +++ b/app-modules/survey/database/migrations/2023_12_14_194217_create_survey_fields_table.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. + + +*/ + +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('survey_fields', function (Blueprint $table) { + $table->uuid('id')->primary(); + + $table->text('label'); + $table->text('type'); + $table->boolean('is_required'); + $table->json('config'); + + $table->foreignUuid('survey_id')->constrained('surveys')->cascadeOnDelete(); + $table->foreignUuid('step_id')->nullable()->constrained('survey_steps')->cascadeOnDelete(); + + $table->timestamps(); + }); + } +}; diff --git a/app-modules/survey/database/migrations/2023_12_14_194218_create_survey_authentications_table.php b/app-modules/survey/database/migrations/2023_12_14_194218_create_survey_authentications_table.php new file mode 100644 index 0000000000..f3f08ad7ad --- /dev/null +++ b/app-modules/survey/database/migrations/2023_12_14_194218_create_survey_authentications_table.php @@ -0,0 +1,57 @@ + + + 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('survey_authentications', function (Blueprint $table) { + $table->uuid('id')->primary(); + + $table->string('author_id')->nullable(); + $table->string('author_type')->nullable(); + $table->string('code')->nullable(); + $table->foreignUuid('survey_id')->constrained('surveys')->cascadeOnDelete(); + + $table->timestamps(); + + $table->index(['author_type', 'author_id']); + }); + } +}; diff --git a/app-modules/survey/database/migrations/2023_12_14_194219_create_survey_submissions_table.php b/app-modules/survey/database/migrations/2023_12_14_194219_create_survey_submissions_table.php new file mode 100644 index 0000000000..022869380d --- /dev/null +++ b/app-modules/survey/database/migrations/2023_12_14_194219_create_survey_submissions_table.php @@ -0,0 +1,62 @@ + + + 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('survey_submissions', function (Blueprint $table) { + $table->uuid('id')->primary(); + + $table->foreignUuid('survey_id')->constrained('surveys')->cascadeOnDelete(); + $table->string('author_id')->nullable(); + $table->string('author_type')->nullable(); + $table->timestamp('submitted_at')->nullable(); + $table->timestamp('canceled_at')->nullable(); + $table->string('request_method')->nullable(); + $table->text('request_note')->nullable(); + $table->foreignUuid('requester_id')->nullable()->constrained('users')->nullOnDelete(); + + $table->timestamps(); + $table->softDeletes(); + + $table->index(['author_type', 'author_id']); + }); + } +}; diff --git a/app-modules/survey/database/migrations/2023_12_14_194220_create_survey_field_submission_table.php b/app-modules/survey/database/migrations/2023_12_14_194220_create_survey_field_submission_table.php new file mode 100644 index 0000000000..7f1b904061 --- /dev/null +++ b/app-modules/survey/database/migrations/2023_12_14_194220_create_survey_field_submission_table.php @@ -0,0 +1,54 @@ + + + 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('survey_field_submission', function (Blueprint $table) { + $table->uuid('id')->primary(); + + $table->longText('response'); + $table->foreignUuid('field_id')->constrained('survey_fields')->cascadeOnDelete(); + $table->foreignUuid('submission_id')->constrained('survey_submissions')->cascadeOnDelete(); + + $table->timestamps(); + }); + } +}; diff --git a/app-modules/survey/database/seeders/.gitkeep b/app-modules/survey/database/seeders/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app-modules/survey/resources/views/livewire/render-survey.blade.php b/app-modules/survey/resources/views/livewire/render-survey.blade.php new file mode 100644 index 0000000000..55dce312fa --- /dev/null +++ b/app-modules/survey/resources/views/livewire/render-survey.blade.php @@ -0,0 +1,42 @@ +{{-- + + + 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. + + +--}} +@php + use AdvisingApp\Form\Actions\GenerateSubmissibleEmbedCode; +@endphp + +
+
+ {!! resolve(GenerateSubmissibleEmbedCode::class)->handle($this->survey) !!} +
+
diff --git a/app-modules/survey/resources/views/submission.blade.php b/app-modules/survey/resources/views/submission.blade.php new file mode 100644 index 0000000000..2756746379 --- /dev/null +++ b/app-modules/survey/resources/views/submission.blade.php @@ -0,0 +1,55 @@ +{{-- + + + 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. + + +--}} + +
+ @if ($submission->submissible->is_wizard) + @foreach ($submission->submissible->steps as $step) + + + {{ $step->label }} + + + + + @endforeach + @else + + @endif +
diff --git a/app-modules/survey/routes/api.php b/app-modules/survey/routes/api.php new file mode 100644 index 0000000000..bc3ac292b0 --- /dev/null +++ b/app-modules/survey/routes/api.php @@ -0,0 +1,64 @@ + + + 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 AdvisingApp\Survey\Http\Controllers\SurveyWidgetController; +use AdvisingApp\Application\Http\Middleware\EnsureSurveysFeatureIsActive; +use AdvisingApp\Form\Http\Middleware\EnsureSubmissibleIsEmbeddableAndAuthorized; + +Route::prefix('api') + ->middleware([ + 'api', + EnsureSurveysFeatureIsActive::class, + EnsureSubmissibleIsEmbeddableAndAuthorized::class . ':survey', + ]) + ->group(function () { + Route::prefix('surveys') + ->name('surveys.') + ->group(function () { + Route::get('/{survey}', [SurveyWidgetController::class, 'view']) + ->middleware(['signed']) + ->name('define'); + Route::post('/{survey}/authenticate/request', [SurveyWidgetController::class, 'requestAuthentication']) + ->middleware(['signed']) + ->name('request-authentication'); + Route::post('/{survey}/authenticate/{authentication}', [SurveyWidgetController::class, 'authenticate']) + ->middleware(['signed']) + ->name('authenticate'); + Route::post('/{survey}/submit', [SurveyWidgetController::class, 'store']) + ->middleware(['signed']) + ->name('submit'); + }); + }); diff --git a/app-modules/form/src/Filament/Pages/ManageSurveys.php b/app-modules/survey/routes/web.php similarity index 78% rename from app-modules/form/src/Filament/Pages/ManageSurveys.php rename to app-modules/survey/routes/web.php index 9f6a82e6a0..6020cda464 100644 --- a/app-modules/form/src/Filament/Pages/ManageSurveys.php +++ b/app-modules/survey/routes/web.php @@ -34,19 +34,13 @@ */ -namespace AdvisingApp\Form\Filament\Pages; - -use Filament\Pages\Page; - -class ManageSurveys extends Page -{ - protected static ?string $navigationIcon = 'heroicon-o-clipboard-document'; - - protected static string $view = 'filament.pages.coming-soon'; - - protected static ?string $navigationGroup = 'Forms and Surveys'; - - protected static ?int $navigationSort = 3; - - protected static ?string $navigationLabel = 'Manage Surveys'; -} +use Illuminate\Support\Facades\Route; +use AdvisingApp\Survey\Livewire\RenderSurvey; + +Route::middleware('web') + ->prefix('surveys') + ->name('surveys.') + ->group(function () { + Route::get('/{survey}/respond', RenderSurvey::class) + ->name('show'); + }); diff --git a/app-modules/survey/src/Filament/Resources/SurveyResource.php b/app-modules/survey/src/Filament/Resources/SurveyResource.php new file mode 100644 index 0000000000..16659c840d --- /dev/null +++ b/app-modules/survey/src/Filament/Resources/SurveyResource.php @@ -0,0 +1,82 @@ + + + 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\Survey\Filament\Resources; + +use Filament\Resources\Resource; +use Filament\Resources\Pages\Page; +use AdvisingApp\Survey\Models\Survey; +use Illuminate\Database\Eloquent\Builder; +use AdvisingApp\Survey\Filament\Resources\FormResource\Pages\EditSurvey; +use AdvisingApp\Survey\Filament\Resources\FormResource\Pages\ListSurveys; +use AdvisingApp\Survey\Filament\Resources\FormResource\Pages\CreateSurvey; +use AdvisingApp\Form\Filament\Resources\FormResource\Pages\ManageSurveySubmissions; + +class SurveyResource extends Resource +{ + protected static ?string $model = Survey::class; + + protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack'; + + protected static ?string $navigationGroup = 'Forms and Surveys'; + + protected static ?int $navigationSort = 2; + + protected static ?string $navigationLabel = 'Manage Surveys'; + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery()->with(['fields']); + } + + public static function getRecordSubNavigation(Page $page): array + { + return $page->generateNavigationItems([ + EditSurvey::class, + ManageSurveySubmissions::class, + ]); + } + + public static function getPages(): array + { + return [ + 'index' => ListSurveys::route('/'), + 'create' => CreateSurvey::route('/create'), + 'edit' => EditSurvey::route('/{record}/edit'), + 'manage-submissions' => ManageSurveySubmissions::route('/{record}/submissions'), + ]; + } +} diff --git a/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/Concerns/HasSharedFormConfiguration.php b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/Concerns/HasSharedFormConfiguration.php new file mode 100644 index 0000000000..f8ec2e4656 --- /dev/null +++ b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/Concerns/HasSharedFormConfiguration.php @@ -0,0 +1,241 @@ + + + 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\Survey\Filament\Resources\SurveyResource\Pages\Concerns; + +use Filament\Forms\Get; +use Filament\Forms\Components\Grid; +use AdvisingApp\Form\Enums\Rounding; +use AdvisingApp\Form\Rules\IsDomain; +use AdvisingApp\Survey\Models\Survey; +use App\Forms\Components\ColorSelect; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\Toggle; +use Filament\Forms\Components\Section; +use FilamentTiptapEditor\TiptapEditor; +use Filament\Forms\Components\Repeater; +use Filament\Forms\Components\Textarea; +use Filament\Forms\Components\TagsInput; +use Filament\Forms\Components\TextInput; +use AdvisingApp\Survey\Models\SurveyStep; +use AdvisingApp\Survey\Models\SurveyField; +use FilamentTiptapEditor\Enums\TiptapOutput; +use AdvisingApp\Form\Filament\Blocks\FormFieldBlockRegistry; +use Illuminate\Database\Eloquent\Builder as EloquentBuilder; + +// This isn't actually shared between any of the form related entities (Form, Survey, Application) +trait HasSharedFormConfiguration +{ + public function fields(): array + { + return [ + TextInput::make('name') + ->required() + ->string() + ->maxLength(255) + ->unique(ignoreRecord: true) + ->autocomplete(false) + ->columnSpanFull(), + Textarea::make('description') + ->string() + ->columnSpanFull(), + Grid::make() + ->schema([ + Toggle::make('embed_enabled') + ->label('Embed Enabled') + ->live() + ->helperText('If enabled, this survey can be embedded on other websites.'), + TagsInput::make('allowed_domains') + ->label('Allowed Domains') + ->helperText('Only these domains will be allowed to embed this survey.') + ->placeholder('example.com') + ->hidden(fn (Get $get) => ! $get('embed_enabled')) + ->disabled(fn (Get $get) => ! $get('embed_enabled')) + ->nestedRecursiveRules( + [ + 'string', + new IsDomain(), + ] + ), + ]) + ->columnSpanFull(), + Toggle::make('is_authenticated') + ->label('Requires authentication') + ->helperText('If enabled, only students and prospects can submit this survey, and they must verify their email address first.'), + Toggle::make('is_wizard') + ->label('Multi-step survey') + ->live() + ->disabled(fn (?Survey $record) => $record?->submissions()->submitted()->exists()), + Section::make('Fields') + ->schema([ + $this->fieldBuilder(), + ]) + ->hidden(fn (Get $get) => $get('is_wizard')) + ->disabled(fn (?Survey $record) => $record?->submissions()->submitted()->exists()), + Repeater::make('steps') + ->schema([ + TextInput::make('label') + ->required() + ->string() + ->maxLength(255) + ->autocomplete(false) + ->columnSpanFull() + ->lazy(), + $this->fieldBuilder(), + ]) + ->addActionLabel('New step') + ->itemLabel(fn (array $state): ?string => $state['label'] ?? null) + ->visible(fn (Get $get) => $get('is_wizard')) + ->disabled(fn (?Survey $record) => $record?->submissions()->submitted()->exists()) + ->relationship() + ->reorderable() + ->columnSpanFull(), + Section::make('Appearance') + ->schema([ + ColorSelect::make('primary_color'), + Select::make('rounding') + ->options(Rounding::class), + ]) + ->columns(), + ]; + } + + public function fieldBuilder(): TiptapEditor + { + return TiptapEditor::make('content') + ->output(TiptapOutput::Json) + ->blocks(FormFieldBlockRegistry::get()) + ->tools(['bold', 'italic', 'small', '|', 'heading', 'bullet-list', 'ordered-list', 'hr', '|', 'link', 'grid', 'blocks']) + ->placeholder('Drag blocks here to build your survey') + ->hiddenLabel() + ->saveRelationshipsUsing(function (TiptapEditor $component, Survey | SurveyStep $record) { + if ($component->isDisabled()) { + return; + } + + $survey = $record instanceof Survey ? $record : $record->submissible; + $surveyStep = $record instanceof SurveyStep ? $record : null; + + SurveyField::query() + ->whereBelongsTo($survey, 'submissible') + ->when($surveyStep, fn (EloquentBuilder $query) => $query->whereBelongsTo($surveyStep, 'step')) + ->delete(); + + $content = $component->decodeBlocksBeforeSave($component->getJSON(decoded: true)); + $content['content'] = $this->saveFieldsFromComponents( + $survey, + $content['content'] ?? [], + $surveyStep, + ); + + $record->content = $content; + $record->save(); + }) + ->dehydrated(false) + ->columnSpanFull() + ->extraInputAttributes(['style' => 'min-height: 12rem;']); + } + + public function saveFieldsFromComponents(Survey $survey, array $components, ?SurveyStep $surveyStep): array + { + foreach ($components as $componentKey => $component) { + if (array_key_exists('content', $component)) { + $components[$componentKey]['content'] = $this->saveFieldsFromComponents($survey, $component['content'], $surveyStep); + + continue; + } + + if ($component['type'] !== 'tiptapBlock') { + continue; + } + + $componentAttributes = $component['attrs'] ?? []; + + if (array_key_exists('id', $componentAttributes)) { + $id = $componentAttributes['id'] ?? null; + unset($componentAttributes['id']); + } + + if (array_key_exists('label', $componentAttributes['data'])) { + $label = $componentAttributes['data']['label'] ?? null; + unset($componentAttributes['data']['label']); + } + + if (array_key_exists('isRequired', $componentAttributes['data'])) { + $isRequired = $componentAttributes['data']['isRequired'] ?? null; + unset($componentAttributes['data']['isRequired']); + } + + /** @var SurveyField $field */ + $field = $survey->fields()->findOrNew($id ?? null); + $field->step()->associate($surveyStep); + $field->label = $label ?? $componentAttributes['type']; + $field->is_required = $isRequired ?? false; + $field->type = $componentAttributes['type']; + $field->config = $componentAttributes['data']; + $field->save(); + + $components[$componentKey]['attrs']['id'] = $field->id; + } + + return $components; + } + + protected function afterCreate(): void + { + $this->clearFormContentForWizard(); + } + + protected function afterSave(): void + { + $this->clearFormContentForWizard(); + } + + protected function clearFormContentForWizard(): void + { + /** @var Survey $record */ + $record = $this->record; + + if ($record->is_wizard) { + $record->content = null; + $record->save(); + + return; + } + + $record->steps()->delete(); + } +} diff --git a/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/CreateSurvey.php b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/CreateSurvey.php new file mode 100644 index 0000000000..df871668ce --- /dev/null +++ b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/CreateSurvey.php @@ -0,0 +1,55 @@ + + + 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\Survey\Filament\Resources\FormResource\Pages; + +use Filament\Forms\Form; +use Filament\Resources\Pages\CreateRecord; +use AdvisingApp\Survey\Filament\Resources\SurveyResource; +use AdvisingApp\Survey\Filament\Resources\SurveyResource\Pages\Concerns\HasSharedFormConfiguration; + +class CreateSurvey extends CreateRecord +{ + use HasSharedFormConfiguration; + + protected static string $resource = SurveyResource::class; + + public function form(Form $form): Form + { + return $form + ->schema($this->fields()); + } +} diff --git a/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/EditSurvey.php b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/EditSurvey.php new file mode 100644 index 0000000000..090b94afa6 --- /dev/null +++ b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/EditSurvey.php @@ -0,0 +1,98 @@ + + + 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\Survey\Filament\Resources\FormResource\Pages; + +use Filament\Actions\Action; +use Filament\Actions\DeleteAction; +use AdvisingApp\Survey\Models\Survey; +use Filament\Forms\Form as FilamentForm; +use Filament\Resources\Pages\EditRecord; +use Filament\Infolists\Components\TextEntry; +use AdvisingApp\Survey\Filament\Resources\SurveyResource; +use AdvisingApp\Form\Actions\GenerateSubmissibleEmbedCode; +use AdvisingApp\Survey\Filament\Resources\SurveyResource\Pages\Concerns\HasSharedFormConfiguration; + +class EditSurvey extends EditRecord +{ + use HasSharedFormConfiguration; + + protected static string $resource = SurveyResource::class; + + protected static ?string $navigationLabel = 'Edit'; + + public function form(FilamentForm $form): FilamentForm + { + return $form + ->schema($this->fields()); + } + + protected function getHeaderActions(): array + { + return [ + Action::make('view') + ->url(fn (Survey $survey) => route('surveys.show', ['survey' => $survey])) + ->icon('heroicon-m-arrow-top-right-on-square') + ->openUrlInNewTab(), + Action::make('embed_snippet') + ->label('Embed Snippet') + ->infolist( + [ + TextEntry::make('snippet') + ->label('Click to Copy') + ->state(function (Survey $survey) { + $code = resolve(GenerateSubmissibleEmbedCode::class)->handle($survey); + + return <<markdown() + ->copyable() + ->copyableState(fn (Survey $survey) => resolve(GenerateSubmissibleEmbedCode::class)->handle($survey)) + ->copyMessage('Copied!') + ->copyMessageDuration(1500), + ] + ) + ->modalSubmitAction(false) + ->modalCancelActionLabel('Close') + ->hidden(fn (Survey $survey) => ! $survey->embed_enabled), + DeleteAction::make(), + ]; + } +} diff --git a/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/ListSurveys.php b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/ListSurveys.php new file mode 100644 index 0000000000..bd4ec55fb9 --- /dev/null +++ b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/ListSurveys.php @@ -0,0 +1,85 @@ + + + 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\Survey\Filament\Resources\FormResource\Pages; + +use Filament\Tables\Table; +use App\Filament\Columns\IdColumn; +use Filament\Actions\CreateAction; +use Filament\Tables\Actions\Action; +use AdvisingApp\Survey\Models\Survey; +use Filament\Tables\Actions\EditAction; +use Filament\Tables\Columns\TextColumn; +use Filament\Resources\Pages\ListRecords; +use Filament\Tables\Actions\BulkActionGroup; +use Filament\Tables\Actions\DeleteBulkAction; +use AdvisingApp\Survey\Filament\Resources\SurveyResource; + +class ListSurveys extends ListRecords +{ + protected static string $resource = SurveyResource::class; + + public function table(Table $table): Table + { + return $table + ->columns([ + IdColumn::make(), + TextColumn::make('name'), + ]) + ->filters([ + ]) + ->actions([ + Action::make('Respond') + ->url(fn (Survey $survey) => route('surveys.show', ['survey' => $survey])) + ->icon('heroicon-m-arrow-top-right-on-square') + ->openUrlInNewTab() + ->color('gray'), + EditAction::make(), + ]) + ->bulkActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } + + protected function getHeaderActions(): array + { + return [ + CreateAction::make(), + ]; + } +} diff --git a/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/ManageSurveySubmissions.php b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/ManageSurveySubmissions.php new file mode 100644 index 0000000000..034de64ea2 --- /dev/null +++ b/app-modules/survey/src/Filament/Resources/SurveyResource/Pages/ManageSurveySubmissions.php @@ -0,0 +1,148 @@ + + + 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\Form\Filament\Resources\FormResource\Pages; + +use Filament\Tables\Table; +use Carbon\CarbonInterface; +use App\Filament\Columns\IdColumn; +use Filament\Tables\Actions\Action; +use Maatwebsite\Excel\Facades\Excel; +use Filament\Tables\Actions\BulkAction; +use Filament\Tables\Actions\ViewAction; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Actions\DeleteAction; +use Filament\Infolists\Components\Section; +use Filament\Infolists\Components\TextEntry; +use Filament\Tables\Actions\BulkActionGroup; +use Filament\Tables\Actions\DeleteBulkAction; +use AdvisingApp\Survey\Models\SurveySubmission; +use AdvisingApp\Form\Enums\FormSubmissionStatus; +use App\Filament\Filters\OpenSearch\SelectFilter; +use AdvisingApp\Form\Exports\FormSubmissionExport; +use Filament\Resources\Pages\ManageRelatedRecords; +use AdvisingApp\Survey\Filament\Resources\SurveyResource; +use AdvisingApp\Form\Filament\Tables\Filters\FormSubmissionStatusFilter; + +class ManageSurveySubmissions extends ManageRelatedRecords +{ + protected static string $resource = SurveyResource::class; + + // TODO: Obsolete when there is no table, remove from Filament + protected static string $relationship = 'submissions'; + + protected static ?string $navigationLabel = 'Submissions'; + + protected static ?string $breadcrumb = 'Submissions'; + + protected static ?string $navigationIcon = 'heroicon-o-document-text'; + + public function table(Table $table): Table + { + return $table + ->columns([ + IdColumn::make(), + TextColumn::make('status') + ->badge() + ->getStateUsing(fn (SurveySubmission $record): FormSubmissionStatus => $record->getStatus()), + TextColumn::make('submitted_at') + ->dateTime() + ->sortable(), + TextColumn::make('author.email') + ->searchable(), + TextColumn::make('author_type') + ->badge() + ->formatStateUsing(fn (?string $state): ?string => filled($state) ? ucfirst($state) : null) + ->color('success'), + TextColumn::make('requester.name'), + TextColumn::make('requested_at') + ->dateTime() + ->getStateUsing(fn (SurveySubmission $record): ?CarbonInterface => $record->requester ? $record->created_at : null), + ]) + ->filters([ + FormSubmissionStatusFilter::make(), + SelectFilter::make('author_type') + ->options([ + 'student' => 'Student', + 'prospect' => 'Prospect', + ]), + ]) + ->headerActions([ + Action::make('export') + ->icon('heroicon-o-arrow-down-tray') + ->action(function () { + $filename = str("form-submissions-{$this->getOwnerRecord()->name}-") + ->append(now()->format('Y-m-d-Hisv')) + ->slug() + ->append('.csv'); + + return Excel::download(new FormSubmissionExport($this->getOwnerRecord()->submissions), $filename); + }), + ]) + ->actions([ + ViewAction::make() + ->modalHeading(fn (SurveySubmission $record) => 'Submission Details: ' . $record->submitted_at->format('M j, Y H:i:s')) + ->infolist(fn (SurveySubmission $record): ?array => ($record->author && $record->submissible->is_authenticated) ? [ + Section::make('Authenticated author') + ->schema([ + TextEntry::make('author.' . $record->author::displayNameKey()) + ->label('Name'), + TextEntry::make('author.email') + ->label('Email address'), + ]) + ->columns(2), + ] : null) + ->modalContent(fn (SurveySubmission $record) => view('survey::submission', ['submission' => $record])) + ->visible(fn (SurveySubmission $record) => $record->submitted_at), + DeleteAction::make(), + ]) + ->bulkActions([ + BulkActionGroup::make([ + BulkAction::make('Export') + ->icon('heroicon-o-arrow-down-tray') + ->action(function ($records) { + $filename = str("selected-form-submissions-{$this->getOwnerRecord()->name}-") + ->append(now()->format('Y-m-d-Hisv')) + ->slug() + ->append('.csv'); + + return Excel::download(new FormSubmissionExport($records), $filename); + }), + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app-modules/survey/src/Http/Controllers/SurveyWidgetController.php b/app-modules/survey/src/Http/Controllers/SurveyWidgetController.php new file mode 100644 index 0000000000..87ab714c63 --- /dev/null +++ b/app-modules/survey/src/Http/Controllers/SurveyWidgetController.php @@ -0,0 +1,244 @@ + + + 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\Survey\Http\Controllers; + +use Closure; +use Illuminate\Support\Str; +use Illuminate\Http\Request; +use Illuminate\Http\JsonResponse; +use Filament\Support\Colors\Color; +use Illuminate\Support\Facades\URL; +use App\Http\Controllers\Controller; +use Illuminate\Support\Facades\Hash; +use AdvisingApp\Survey\Models\Survey; +use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Facades\Notification; +use Illuminate\Validation\ValidationException; +use Symfony\Component\HttpFoundation\Response; +use AdvisingApp\Form\Actions\GenerateFormKitSchema; +use AdvisingApp\Application\Models\ApplicationSubmission; +use AdvisingApp\Form\Actions\GenerateSubmissibleValidation; +use AdvisingApp\Application\Models\ApplicationAuthentication; +use AdvisingApp\Form\Actions\ResolveSubmissionAuthorFromEmail; +use AdvisingApp\Form\Notifications\AuthenticateFormNotification; +use AdvisingApp\Form\Filament\Blocks\EducatableEmailFormFieldBlock; + +class SurveyWidgetController extends Controller +{ + public function view(GenerateFormKitSchema $generateSchema, Survey $survey): JsonResponse + { + return response()->json( + [ + 'name' => $survey->name, + 'description' => $survey->description, + 'authentication_url' => URL::signedRoute('survey.request-authentication', ['survey' => $survey]), + 'schema' => $generateSchema($survey), + 'primary_color' => Color::all()[$survey->primary_color ?? 'blue'], + 'rounding' => $survey->rounding, + ], + ); + } + + public function requestAuthentication(Request $request, ResolveSubmissionAuthorFromEmail $resolveSubmissionAuthorFromEmail, Survey $survey): JsonResponse + { + $data = $request->validate([ + 'email' => ['required', 'email'], + ]); + + $author = $resolveSubmissionAuthorFromEmail($data['email']); + + if (! $author) { + throw ValidationException::withMessages([ + 'email' => 'A student with that email address could not be found. Please contact your system administrator.', + ]); + } + + $code = random_int(100000, 999999); + + $authentication = new ApplicationAuthentication(); + $authentication->author()->associate($author); + $authentication->submissible()->associate($survey); + $authentication->code = Hash::make($code); + $authentication->save(); + + Notification::route('mail', [ + $data['email'] => $author->getAttributeValue($author::displayNameKey()), + ])->notify(new AuthenticateFormNotification($authentication, $code)); + + return response()->json([ + 'message' => "We've sent an authentication code to {$data['email']}.", + 'authentication_url' => URL::signedRoute('applications.authenticate', [ + 'application' => $survey, + 'authentication' => $authentication, + ]), + ]); + } + + public function authenticate(Request $request, Survey $survey, ApplicationAuthentication $authentication): JsonResponse + { + if ($authentication->isExpired()) { + return response()->json([ + 'is_expired' => true, + ]); + } + + $request->validate([ + 'code' => ['required', 'integer', 'digits:6', function (string $attribute, int $value, Closure $fail) use ($authentication) { + if (Hash::check($value, $authentication->code)) { + return; + } + + $fail('The provided code is invalid.'); + }], + ]); + + return response()->json([ + 'submission_url' => URL::signedRoute('applications.submit', [ + 'authentication' => $authentication, + 'application' => $authentication->submissible, + ]), + ]); + } + + public function store( + Request $request, + GenerateSubmissibleValidation $generateValidation, + ResolveSubmissionAuthorFromEmail $resolveSubmissionAuthorFromEmail, + Survey $survey, + ): JsonResponse { + $authentication = $request->query('authentication'); + + if (filled($authentication)) { + $authentication = ApplicationAuthentication::findOrFail($authentication); + } + + if ( + ($authentication?->isExpired() ?? true) + ) { + abort(Response::HTTP_UNAUTHORIZED); + } + + $validator = Validator::make( + $request->all(), + $generateValidation($survey) + ); + + if ($validator->fails()) { + return response()->json( + [ + 'errors' => (object) $validator->errors(), + ], + Response::HTTP_UNPROCESSABLE_ENTITY + ); + } + + /** @var ApplicationSubmission $submission */ + $submission = $survey->submissions()->make(); + + if ($authentication) { + $submission->author()->associate($authentication->author); + + $authentication->delete(); + } + + $submission->save(); + + $data = $validator->validated(); + + if ($survey->is_wizard) { + foreach ($survey->steps as $step) { + $stepFields = $step->fields()->pluck('type', 'id')->all(); + + foreach ($data[$step->label] as $fieldId => $response) { + $submission->fields()->attach( + $fieldId, + ['id' => Str::orderedUuid(), 'response' => $response], + ); + + if ($submission->author) { + continue; + } + + if ($stepFields[$fieldId] !== EducatableEmailFormFieldBlock::type()) { + continue; + } + + $author = $resolveSubmissionAuthorFromEmail($response); + + if (! $author) { + continue; + } + + $submission->author()->associate($author); + } + } + } else { + $applicationFields = $survey->fields()->pluck('type', 'id')->all(); + + foreach ($data as $fieldId => $response) { + $submission->fields()->attach( + $fieldId, + ['id' => Str::orderedUuid(), 'response' => $response], + ); + + if ($submission->author) { + continue; + } + + if ($applicationFields[$fieldId] !== EducatableEmailFormFieldBlock::type()) { + continue; + } + + $author = $resolveSubmissionAuthorFromEmail($response); + + if (! $author) { + continue; + } + + $submission->author()->associate($author); + } + } + + $submission->save(); + + return response()->json( + [ + 'message' => 'Application submitted successfully.', + ] + ); + } +} diff --git a/app-modules/survey/src/Http/Middleware/EnsureSurveysFeatureIsActive.php b/app-modules/survey/src/Http/Middleware/EnsureSurveysFeatureIsActive.php new file mode 100644 index 0000000000..27bdadb012 --- /dev/null +++ b/app-modules/survey/src/Http/Middleware/EnsureSurveysFeatureIsActive.php @@ -0,0 +1,54 @@ + + + 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\Application\Http\Middleware; + +use Closure; +use Illuminate\Http\Request; +use App\Settings\LicenseSettings; +use Symfony\Component\HttpFoundation\Response; + +class EnsureSurveysFeatureIsActive +{ + public function handle(Request $request, Closure $next): Response + { + if (! app(LicenseSettings::class)->data->addons->conductSurveys) { + return response()->json(['error' => 'Surveys is not enabled.'], 403); + } + + return $next($request); + } +} diff --git a/app-modules/survey/src/Livewire/RenderSurvey.php b/app-modules/survey/src/Livewire/RenderSurvey.php new file mode 100644 index 0000000000..500e60622f --- /dev/null +++ b/app-modules/survey/src/Livewire/RenderSurvey.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\Survey\Livewire; + +use Livewire\Component; +use Illuminate\Contracts\View\View; +use AdvisingApp\Survey\Models\Survey; +use Filament\Forms\Contracts\HasForms; +use Filament\Forms\Concerns\InteractsWithForms; + +class RenderSurvey extends Component implements HasForms +{ + use InteractsWithForms; + + public bool $show = true; + + public Survey $survey; + + public ?array $data = []; + + public function render(): View + { + return view('survey::livewire.render-survey') + ->title($this->survey->name); + } +} diff --git a/app-modules/survey/src/Models/Survey.php b/app-modules/survey/src/Models/Survey.php new file mode 100644 index 0000000000..9b313f2939 --- /dev/null +++ b/app-modules/survey/src/Models/Survey.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. + + +*/ + +namespace AdvisingApp\Survey\Models; + +use AdvisingApp\Form\Enums\Rounding; +use AdvisingApp\Form\Models\Submissible; +use Illuminate\Database\Eloquent\Relations\HasMany; + +/** + * @mixin IdeHelperForm + */ +class Survey extends Submissible +{ + protected $fillable = [ + 'name', + 'description', + 'embed_enabled', + 'allowed_domains', + 'is_authenticated', + 'is_wizard', + 'primary_color', + 'rounding', + 'content', + ]; + + protected $casts = [ + 'content' => 'array', + 'embed_enabled' => 'boolean', + 'allowed_domains' => 'array', + 'is_authenticated' => 'boolean', + 'is_wizard' => 'boolean', + 'rounding' => Rounding::class, + ]; + + public function fields(): HasMany + { + return $this->hasMany(SurveyField::class); + } + + public function steps(): HasMany + { + return $this->hasMany(SurveyStep::class); + } + + public function submissions(): HasMany + { + return $this->hasMany(SurveySubmission::class); + } +} diff --git a/app-modules/survey/src/Models/SurveyAuthentication.php b/app-modules/survey/src/Models/SurveyAuthentication.php new file mode 100644 index 0000000000..6f2936bf66 --- /dev/null +++ b/app-modules/survey/src/Models/SurveyAuthentication.php @@ -0,0 +1,54 @@ + + + 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\Survey\Models; + +use App\Models\Attributes\NoPermissions; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use AdvisingApp\Form\Models\SubmissibleAuthentication; + +/** + * @mixin IdeHelperFormAuthentication + */ +#[NoPermissions] +class SurveyAuthentication extends SubmissibleAuthentication +{ + public function submissible(): BelongsTo + { + return $this + ->belongsTo(Survey::class, 'survey_id'); + } +} diff --git a/app-modules/survey/src/Models/SurveyField.php b/app-modules/survey/src/Models/SurveyField.php new file mode 100644 index 0000000000..5bb1680daa --- /dev/null +++ b/app-modules/survey/src/Models/SurveyField.php @@ -0,0 +1,71 @@ + + + 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\Survey\Models; + +use AdvisingApp\Form\Models\SubmissibleField; +use Illuminate\Database\Eloquent\Relations\BelongsTo; + +/** + * @mixin IdeHelperFormField + */ +class SurveyField extends SubmissibleField +{ + protected $fillable = [ + 'config', + 'label', + 'type', + 'is_required', + 'survey_id', + ]; + + protected $casts = [ + 'config' => 'array', + 'is_required' => 'bool', + ]; + + public function submissible(): BelongsTo + { + return $this + ->belongsTo(Survey::class, 'survey_id'); + } + + public function step(): BelongsTo + { + return $this + ->belongsTo(SurveyStep::class, 'step_id'); + } +} diff --git a/app-modules/survey/src/Models/SurveyStep.php b/app-modules/survey/src/Models/SurveyStep.php new file mode 100644 index 0000000000..0f7b053d61 --- /dev/null +++ b/app-modules/survey/src/Models/SurveyStep.php @@ -0,0 +1,71 @@ + + + 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\Survey\Models; + +use App\Models\Attributes\NoPermissions; +use AdvisingApp\Form\Models\SubmissibleStep; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; + +/** + * @mixin IdeHelperFormStep + */ +#[NoPermissions] +class SurveyStep extends SubmissibleStep +{ + protected $fillable = [ + 'label', + 'content', + 'sort', + ]; + + protected $casts = [ + 'content' => 'array', + 'sort' => 'integer', + ]; + + public function submissible(): BelongsTo + { + return $this + ->belongsTo(Survey::class, 'survey_id'); + } + + public function fields(): HasMany + { + return $this->hasMany(SurveyField::class, 'step_id'); + } +} diff --git a/app-modules/survey/src/Models/SurveySubmission.php b/app-modules/survey/src/Models/SurveySubmission.php new file mode 100644 index 0000000000..80b07c2db4 --- /dev/null +++ b/app-modules/survey/src/Models/SurveySubmission.php @@ -0,0 +1,134 @@ + + + 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\Survey\Models; + +use App\Models\User; +use AdvisingApp\Form\Models\Submission; +use AdvisingApp\Prospect\Models\Prospect; +use Illuminate\Database\Eloquent\Builder; +use AdvisingApp\Form\Enums\FormSubmissionStatus; +use AdvisingApp\StudentDataModel\Models\Student; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use AdvisingApp\Form\Enums\FormSubmissionRequestDeliveryMethod; + +/** + * @property Student|Prospect|null $author + * + * @mixin IdeHelperFormSubmission + */ +class SurveySubmission extends Submission +{ + protected $fillable = [ + 'canceled_at', + 'survey_id', + 'request_method', + 'request_note', + 'submitted_at', + ]; + + protected $casts = [ + 'submitted_at' => 'immutable_datetime', + 'canceled_at' => 'immutable_datetime', + 'request_method' => FormSubmissionRequestDeliveryMethod::class, + ]; + + public function submissible(): BelongsTo + { + return $this + ->belongsTo(Survey::class, 'survey_id'); + } + + public function requester(): BelongsTo + { + return $this->belongsTo(User::class, 'requester_id'); + } + + public function fields(): BelongsToMany + { + return $this->belongsToMany( + SurveyField::class, + 'survey_field_submission', + 'submission_id', + 'field_id', + ) + ->withPivot(['id', 'response']); + } + + public function deliverRequest(): void + { + $this->request_method->deliver($this); + } + + public function scopeRequested(Builder $query): Builder + { + return $query->notSubmitted()->notCanceled(); + } + + public function scopeSubmitted(Builder $query): Builder + { + return $query->whereNotNull('submitted_at'); + } + + public function scopeCanceled(Builder $query): Builder + { + return $query->notSubmitted()->whereNotNull('canceled_at'); + } + + public function scopeNotSubmitted(Builder $query): Builder + { + return $query->whereNull('submitted_at'); + } + + public function scopeNotCanceled(Builder $query): Builder + { + return $query->whereNull('canceled_at'); + } + + public function getStatus(): FormSubmissionStatus + { + if ($this->submitted_at) { + return FormSubmissionStatus::Submitted; + } + + if ($this->canceled_at) { + return FormSubmissionStatus::Canceled; + } + + return FormSubmissionStatus::Requested; + } +} diff --git a/app-modules/survey/src/Providers/SurveyServiceProvider.php b/app-modules/survey/src/Providers/SurveyServiceProvider.php new file mode 100644 index 0000000000..eb847d3028 --- /dev/null +++ b/app-modules/survey/src/Providers/SurveyServiceProvider.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\Survey\Providers; + +use Filament\Panel; +use AdvisingApp\Survey\SurveyPlugin; +use AdvisingApp\Survey\Models\Survey; +use Illuminate\Support\ServiceProvider; +use AdvisingApp\Survey\Models\SurveyStep; +use AdvisingApp\Survey\Models\SurveyField; +use AdvisingApp\Survey\Models\SurveySubmission; +use AdvisingApp\Survey\Models\SurveyAuthentication; +use Illuminate\Database\Eloquent\Relations\Relation; +use AdvisingApp\Authorization\AuthorizationRoleRegistry; +use AdvisingApp\Authorization\AuthorizationPermissionRegistry; + +class SurveyServiceProvider extends ServiceProvider +{ + public function register() + { + Panel::configureUsing(fn (Panel $panel) => $panel->plugin(new SurveyPlugin())); + } + + public function boot() + { + Relation::morphMap([ + 'survey_authentication' => SurveyAuthentication::class, + 'survey_field' => SurveyField::class, + 'survey_step' => SurveyStep::class, + 'survey_submission' => SurveySubmission::class, + 'survey' => Survey::class, + ]); + + $this->registerRolesAndPermissions(); + } + + protected function registerRolesAndPermissions() + { + $permissionRegistry = app(AuthorizationPermissionRegistry::class); + + $permissionRegistry->registerApiPermissions( + module: 'survey', + path: 'permissions/api/custom' + ); + + $permissionRegistry->registerWebPermissions( + module: 'survey', + path: 'permissions/web/custom' + ); + + $roleRegistry = app(AuthorizationRoleRegistry::class); + + $roleRegistry->registerApiRoles( + module: 'survey', + path: 'roles/api' + ); + + $roleRegistry->registerWebRoles( + module: 'survey', + path: 'roles/web' + ); + } +} diff --git a/app-modules/survey/src/SurveyPlugin.php b/app-modules/survey/src/SurveyPlugin.php new file mode 100644 index 0000000000..e8c3bf8f17 --- /dev/null +++ b/app-modules/survey/src/SurveyPlugin.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. + + +*/ + +namespace AdvisingApp\Survey; + +use Filament\Panel; +use Filament\Contracts\Plugin; + +class SurveyPlugin implements Plugin +{ + public function getId(): string + { + return 'survey'; + } + + public function register(Panel $panel): void + { + $panel + ->discoverResources( + in: __DIR__ . '/Filament/Resources', + for: 'AdvisingApp\\Survey\\Filament\\Resources' + ); + } + + public function boot(Panel $panel): void {} +} diff --git a/app-modules/survey/tests/.gitkeep b/app-modules/survey/tests/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/composer.json b/composer.json index 2721152d95..e2140939f3 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,7 @@ "canyon-gbs/advising-app-theme": "*", "canyon-gbs/advising-app-timeline": "*", "canyon-gbs/advising-app-webhook": "*", + "canyon-gbs/advising-app-survey": "*", "composer/composer": "^2.6.4", "filament/filament": "^3.1", "filament/spatie-laravel-media-library-plugin": "^3.0", diff --git a/composer.lock b/composer.lock index b5876dcc02..d51295ee2a 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": "9bfaad2414272ba1599b5e9c467e60eb", + "content-hash": "3d735a6d68ae848d9f7a7f610349697f", "packages": [ { "name": "awcodes/filament-tiptap-editor", @@ -1546,6 +1546,41 @@ "relative": true } }, + { + "name": "canyon-gbs/advising-app-survey", + "version": "1.0", + "dist": { + "type": "path", + "url": "app-modules/survey", + "reference": "f018d995e19cedeacf5d5b73b30c9a192b1b531d" + }, + "require": { + "filament/filament": "^3.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "AdvisingApp\\Survey\\Providers\\SurveyServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "AdvisingApp\\Survey\\": "src/", + "AdvisingApp\\Survey\\Tests\\": "tests/", + "AdvisingApp\\Survey\\Database\\Factories\\": "database/factories/", + "AdvisingApp\\Survey\\Database\\Seeders\\": "database/seeders/" + } + }, + "license": [ + "proprietary" + ], + "transport-options": { + "symlink": true, + "relative": true + } + }, { "name": "canyon-gbs/advising-app-task", "version": "1.0", From e48288554575f67b553b44916a09ed6df05c4a59 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 14 Dec 2023 20:14:01 -0500 Subject: [PATCH 014/126] Add embed case. --- app-modules/application/routes/api.php | 1 + .../form/src/Actions/GenerateSubmissibleEmbedCode.php | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app-modules/application/routes/api.php b/app-modules/application/routes/api.php index 418827fda8..e28ecbb6c5 100644 --- a/app-modules/application/routes/api.php +++ b/app-modules/application/routes/api.php @@ -34,6 +34,7 @@ */ +use Illuminate\Support\Facades\Route; use AdvisingApp\Application\Http\Controllers\ApplicationWidgetController; use AdvisingApp\Form\Http\Middleware\EnsureSubmissibleIsEmbeddableAndAuthorized; use AdvisingApp\Application\Http\Middleware\EnsureOnlineAdmissionsFeatureIsActive; diff --git a/app-modules/form/src/Actions/GenerateSubmissibleEmbedCode.php b/app-modules/form/src/Actions/GenerateSubmissibleEmbedCode.php index 88f949029d..6d534cd948 100644 --- a/app-modules/form/src/Actions/GenerateSubmissibleEmbedCode.php +++ b/app-modules/form/src/Actions/GenerateSubmissibleEmbedCode.php @@ -39,6 +39,7 @@ use Exception; use AdvisingApp\Form\Models\Form; use Illuminate\Support\Facades\URL; +use AdvisingApp\Survey\Models\Survey; use AdvisingApp\Form\Models\Submissible; use AdvisingApp\Application\Models\Application; @@ -57,7 +58,6 @@ public function handle(Submissible $submissible): string EOD; })(), Application::class => (function () use ($submissible) { - // TODO: Eventually we will want to change this to create a separate widget for applications. $scriptUrl = url('js/widgets/application/advising-app-application-widget.js?'); $applicationDefinitionUrl = URL::signedRoute('applications.define', ['application' => $submissible]); @@ -66,6 +66,15 @@ public function handle(Submissible $submissible): string EOD; })(), + Survey::class => (function () use ($submissible) { + $scriptUrl = url('js/widgets/application/advising-app-application-widget.js?'); + $surveyDefinitionUrl = URL::signedRoute('surveys.define', ['survey' => $submissible]); + + return << + + EOD; + })(), default => throw new Exception('Unsupported submissible type.'), }; } From 45c62a080ae012fea6e253249afe3de4323ee8a6 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Fri, 15 Dec 2023 01:18:06 -0500 Subject: [PATCH 015/126] Fix API issues Signed-off-by: Kevin Ullyott --- app-modules/audit/config/audit.php | 1 + .../care-team/graphql/care-team.graphql | 7 +- .../interaction/graphql/interaction.graphql | 75 ++++++++++++------- .../graphql/service-management.graphql | 5 +- app/Providers/AppServiceProvider.php | 6 ++ config/lighthouse.php | 6 +- spectaql.yml | 2 +- 7 files changed, 66 insertions(+), 36 deletions(-) diff --git a/app-modules/audit/config/audit.php b/app-modules/audit/config/audit.php index a8f099609e..d85ae45545 100644 --- a/app-modules/audit/config/audit.php +++ b/app-modules/audit/config/audit.php @@ -65,6 +65,7 @@ 'guards' => [ 'web', 'api', + 'sanctum', ], 'resolver' => OwenIt\Auditing\Resolvers\UserResolver::class, ], diff --git a/app-modules/care-team/graphql/care-team.graphql b/app-modules/care-team/graphql/care-team.graphql index 45de4d9667..635b180d0d 100644 --- a/app-modules/care-team/graphql/care-team.graphql +++ b/app-modules/care-team/graphql/care-team.graphql @@ -28,8 +28,8 @@ extend type Mutation { user_id: ID! @rules( apply: [ - "required", - "exists:users,id", + "required" + "exists:users,id" "AdvisingApp\\CareTeam\\Rules\\UniqueCareTeamRule" ] ) @@ -38,7 +38,8 @@ extend type Mutation { educatable_id: ID! @rules( apply: [ - "required", "AdvisingApp\\CareTeam\\Rules\\EducatableIdExistsRule" + "required" + "AdvisingApp\\CareTeam\\Rules\\EducatableIdExistsRule" ] ) diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index f4bc922c10..ae077e3bcc 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -170,29 +170,32 @@ input UpdateInteractionInput { "The Interactable related to the interaction." interactable_id: ID - @rules( - apply: [ - "AdvisingApp\\Interaction\\Rules\\InteractableIdExistsRules" - "required_with:interactable_type" - ] - ) + @rules( + apply: [ + "AdvisingApp\\Interaction\\Rules\\InteractableIdExistsRules" + "required_with:interactable_type" + ] + ) "The type of Interactable related to the interaction." - interactable_type: String @rules( - apply: [ - "in:student,prospect,service_request", - "required_with:interactable_id" - ] - ) + interactable_type: String + @rules( + apply: [ + "in:student,prospect,service_request" + "required_with:interactable_id" + ] + ) "The type of interaction." interaction_type_id: ID @rules(apply: ["exists:interaction_types,id"]) "The relation of the interaction." - interaction_relation_id: ID @rules(apply: ["exists:interaction_relations,id"]) + interaction_relation_id: ID + @rules(apply: ["exists:interaction_relations,id"]) "The campaign of the interaction." - interaction_campaign_id: ID @rules(apply: ["exists:interaction_campaigns,id"]) + interaction_campaign_id: ID + @rules(apply: ["exists:interaction_campaigns,id"]) "The driver of the interaction." interaction_driver_id: ID @rules(apply: ["exists:interaction_drivers,id"]) @@ -210,7 +213,8 @@ input UpdateInteractionInput { start_datetime: DateTime @rules(apply: ["date_format:Y-m-d H:i:s"]) "The end datetime of the interaction." - end_datetime: DateTime @rules(apply: ["nullable", "date_format:Y-m-d H:i:s"]) + end_datetime: DateTime + @rules(apply: ["nullable", "date_format:Y-m-d H:i:s"]) } extend type Mutation { @@ -229,49 +233,64 @@ extend type Mutation { interactable_id: ID! @rules( apply: [ - "required", + "required" "AdvisingApp\\Interaction\\Rules\\InteractableIdExistsRules" ] ) "The type of Interactable related to the interaction." - interactable_type: String! @rules(apply: ["required", "in:student,prospect,service_request"]) + interactable_type: String! + @rules(apply: ["required", "in:student,prospect,service_request"]) "The type of interaction." - interaction_type_id: ID! @rules(apply: ["required", "exists:interaction_types,id"]) + interaction_type_id: ID! + @rules(apply: ["required", "exists:interaction_types,id"]) "The relation of the interaction." - interaction_relation_id: ID! @rules(apply: ["required", "exists:interaction_relations,id"]) + interaction_relation_id: ID! + @rules(apply: ["required", "exists:interaction_relations,id"]) "The campaign of the interaction." - interaction_campaign_id: ID! @rules(apply: ["required", "exists:interaction_campaigns,id"]) + interaction_campaign_id: ID! + @rules(apply: ["required", "exists:interaction_campaigns,id"]) "The driver of the interaction." - interaction_driver_id: ID! @rules(apply: ["required", "exists:interaction_drivers,id"]) + interaction_driver_id: ID! + @rules(apply: ["required", "exists:interaction_drivers,id"]) "The status of the interaction." - interaction_status_id: ID! @rules(apply: ["required", "exists:interaction_statuses,id"]) + interaction_status_id: ID! + @rules(apply: ["required", "exists:interaction_statuses,id"]) "The outcome of the interaction." - interaction_outcome_id: ID! @rules(apply: ["required", "exists:interaction_outcomes,id"]) + interaction_outcome_id: ID! + @rules(apply: ["required", "exists:interaction_outcomes,id"]) "The division of the interaction." division_id: ID! @rules(apply: ["required", "exists:divisions,id"]) "The start datetime of the interaction." - start_datetime: DateTime! @rules(apply: ["required", "date_format:Y-m-d H:i:s"]) + start_datetime: DateTime! + @rules(apply: ["required", "date_format:Y-m-d H:i:s"]) "The end datetime of the interaction." - end_datetime: DateTime @rules(apply: ["nullable", "date_format:Y-m-d H:i:s"]) + end_datetime: DateTime + @rules(apply: ["nullable", "date_format:Y-m-d H:i:s"]) ): Interaction! @create @canModel(ability: "create") "Update a interaction." updateInteraction( "The identifier of the interaction you would like to update." - id: ID! @rules(apply: ["required", "exists:interactions,id"]) + id: ID! @whereKey - input: UpdateInteractionInput! - ): Interaction! @create @canModel(ability: "create") + input: UpdateInteractionInput! @spread + ): Interaction! @canFind(ability: "update", find: "id") @update + + "Delete a interaction." + deleteInteraction( + "The identifier of the interaction you would like to delete." + id: ID! @whereKey + ): Interaction @canFind(ability: "delete", find: "id") @delete } #import ./interaction-campaign.graphql diff --git a/app-modules/service-management/graphql/service-management.graphql b/app-modules/service-management/graphql/service-management.graphql index 62725b0ebf..3884a5afb2 100644 --- a/app-modules/service-management/graphql/service-management.graphql +++ b/app-modules/service-management/graphql/service-management.graphql @@ -1,8 +1,9 @@ -type ServiceRequest @model(class: "AdvisingApp\\ServiceManagement\\Models\\ServiceRequest") { +type ServiceRequest + @model(class: "AdvisingApp\\ServiceManagement\\Models\\ServiceRequest") { "Unique primary key." id: ID! # TODO: Finish fields } -# TODO: Query and Mutate \ No newline at end of file +# TODO: Query and Mutate diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index ec01244508..c33508824a 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -36,11 +36,13 @@ namespace App\Providers; +use App\Models\SystemUser; use Illuminate\Console\Application; use Illuminate\Support\ServiceProvider; use Illuminate\Console\Application as Artisan; use Lomkit\Rest\Console\Commands\ResourceCommand; use Lomkit\Rest\Console\Commands\ControllerCommand; +use Illuminate\Database\Eloquent\Relations\Relation; use Lomkit\Rest\Console\Commands\DocumentationCommand; use OpenSearch\Migrations\Filesystem\MigrationStorage; @@ -69,5 +71,9 @@ public function boot(): void resolve(MigrationStorage::class)->registerPaths([ 'app-modules/prospect/opensearch/migrations', ]); + + Relation::morphMap([ + 'system_user' => SystemUser::class, + ]); } } diff --git a/config/lighthouse.php b/config/lighthouse.php index a05a8ccf70..36c3d25867 100644 --- a/config/lighthouse.php +++ b/config/lighthouse.php @@ -36,6 +36,8 @@ declare(strict_types = 1); +use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful; + return [ /* |-------------------------------------------------------------------------- @@ -64,7 +66,7 @@ * make sure to return spec-compliant responses in case an error is thrown. */ 'middleware' => [ - //EnsureFrontendRequestsAreStateful::class, + EnsureFrontendRequestsAreStateful::class, // Ensures the request is not vulnerable to cross-site request forgery. // Nuwave\Lighthouse\Http\Middleware\EnsureXHR::class, @@ -343,7 +345,7 @@ | */ - 'transactional_mutations' => true, + 'transactional_mutations' => false, /* |-------------------------------------------------------------------------- diff --git a/spectaql.yml b/spectaql.yml index a466d3997b..388be662ea 100644 --- a/spectaql.yml +++ b/spectaql.yml @@ -12,7 +12,7 @@ introspection: # metadataFile: ./examples/data/metadata.json # dynamicExamplesProcessingModule: ./examples/customizations/examples queryNameStrategy: capitalizeFirst - fieldExpansionDepth: 3 + fieldExpansionDepth: 2 spectaqlDirective: enable: true From c767f576c93cf86537848f8550571cc9d3aeaf7d Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 15 Dec 2023 13:25:44 +0000 Subject: [PATCH 016/126] [ADVISING-1105]: Introduce Spatie GDPR Cookie Package --- composer.json | 1 + composer.lock | 122 ++++++++++++++---- .../views/components/layouts/app.blade.php | 2 + .../vendor/cookie-consent/dialog.blade.php | 19 +++ .../vendor/cookie-consent/index.blade.php | 52 ++++++++ tailwind.config.js | 2 +- 6 files changed, 175 insertions(+), 23 deletions(-) create mode 100644 resources/views/vendor/cookie-consent/dialog.blade.php create mode 100644 resources/views/vendor/cookie-consent/index.blade.php diff --git a/composer.json b/composer.json index 1ed9fec25a..ec74d29216 100644 --- a/composer.json +++ b/composer.json @@ -80,6 +80,7 @@ "shuvroroy/filament-spatie-laravel-health": "^2.0", "socialiteproviders/google": "^4.1", "socialiteproviders/microsoft-azure": "^5.1", + "spatie/laravel-cookie-consent": "^3.2", "spatie/laravel-data": "^3.10", "spatie/laravel-medialibrary": "^10.15", "spatie/laravel-settings": "^3.2", diff --git a/composer.lock b/composer.lock index 358e4650e8..46632e9628 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": "138e312c2af2093f49ef9641a68bd054", + "content-hash": "91271f79b0c4a7d9b632e9498dbc2a01", "packages": [ { "name": "awcodes/filament-tiptap-editor", @@ -3725,7 +3725,7 @@ }, { "name": "filament/spatie-laravel-media-library-plugin", - "version": "v3.1.21", + "version": "v3.1.22", "source": { "type": "git", "url": "https://github.com/filamentphp/spatie-laravel-media-library-plugin.git", @@ -8377,16 +8377,16 @@ }, { "name": "nuwave/lighthouse", - "version": "v6.28.0", + "version": "v6.29.0", "source": { "type": "git", "url": "https://github.com/nuwave/lighthouse.git", - "reference": "76018af1019d97739a71eb47c9008f3e69273fbc" + "reference": "e1b36601ad798449dec04d5ab7bd81587bd90eb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nuwave/lighthouse/zipball/76018af1019d97739a71eb47c9008f3e69273fbc", - "reference": "76018af1019d97739a71eb47c9008f3e69273fbc", + "url": "https://api.github.com/repos/nuwave/lighthouse/zipball/e1b36601ad798449dec04d5ab7bd81587bd90eb6", + "reference": "e1b36601ad798449dec04d5ab7bd81587bd90eb6", "shasum": "" }, "require": { @@ -8508,7 +8508,7 @@ "type": "patreon" } ], - "time": "2023-12-11T13:03:36+00:00" + "time": "2023-12-15T12:47:21+00:00" }, { "name": "openai-php/client", @@ -11251,6 +11251,84 @@ ], "time": "2023-07-19T18:55:36+00:00" }, + { + "name": "spatie/laravel-cookie-consent", + "version": "3.2.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-cookie-consent.git", + "reference": "5a5b2f0a03cc66d8ffede42037a7f3e00f95aaa9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-cookie-consent/zipball/5a5b2f0a03cc66d8ffede42037a7f3e00f95aaa9", + "reference": "5a5b2f0a03cc66d8ffede42037a7f3e00f95aaa9", + "shasum": "" + }, + "require": { + "illuminate/cookie": "^8.0|^9.0|^10.0", + "illuminate/support": "^8.0|^9.0|^10.0", + "illuminate/view": "^8.0|^9.0|^10.0", + "php": "^8.0", + "spatie/laravel-package-tools": "^1.9" + }, + "require-dev": { + "fakerphp/faker": "^1.9", + "orchestra/testbench": "^6.0|^7.0|^8.0", + "pestphp/pest": "^1.22" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\CookieConsent\\CookieConsentServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\CookieConsent\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Willem Van Bockstal", + "email": "willem@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Make your Laravel app comply with the crazy EU cookie law", + "homepage": "https://github.com/spatie/cookie-consent", + "keywords": [ + "comply", + "cookie", + "cookie-consent", + "eu", + "law", + "spatie" + ], + "support": { + "source": "https://github.com/spatie/laravel-cookie-consent/tree/3.2.4" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2023-01-25T09:18:22+00:00" + }, { "name": "spatie/laravel-data", "version": "3.10.1", @@ -16472,16 +16550,16 @@ }, { "name": "pestphp/pest", - "version": "v2.28.0", + "version": "v2.28.1", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "9a8f6e64149592b2555a2519237abb39e9e4f1fe" + "reference": "9ee41910201ef8fc5f5b6d1390e5ec4558222927" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/9a8f6e64149592b2555a2519237abb39e9e4f1fe", - "reference": "9a8f6e64149592b2555a2519237abb39e9e4f1fe", + "url": "https://api.github.com/repos/pestphp/pest/zipball/9ee41910201ef8fc5f5b6d1390e5ec4558222927", + "reference": "9ee41910201ef8fc5f5b6d1390e5ec4558222927", "shasum": "" }, "require": { @@ -16491,10 +16569,10 @@ "pestphp/pest-plugin": "^2.1.1", "pestphp/pest-plugin-arch": "^2.5.0", "php": "^8.1.0", - "phpunit/phpunit": "^10.5.2" + "phpunit/phpunit": "^10.5.3" }, "conflict": { - "phpunit/phpunit": ">10.5.2", + "phpunit/phpunit": ">10.5.3", "sebastian/exporter": "<5.1.0", "webmozart/assert": "<1.11.0" }, @@ -16564,7 +16642,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v2.28.0" + "source": "https://github.com/pestphp/pest/tree/v2.28.1" }, "funding": [ { @@ -16576,7 +16654,7 @@ "type": "github" } ], - "time": "2023-12-05T19:06:22+00:00" + "time": "2023-12-15T11:42:34+00:00" }, { "name": "pestphp/pest-plugin", @@ -17559,16 +17637,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.2", + "version": "10.5.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5aedff46afba98dddecaa12349ec044d9103d4fe" + "reference": "6fce887c71076a73f32fd3e0774a6833fc5c7f19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5aedff46afba98dddecaa12349ec044d9103d4fe", - "reference": "5aedff46afba98dddecaa12349ec044d9103d4fe", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6fce887c71076a73f32fd3e0774a6833fc5c7f19", + "reference": "6fce887c71076a73f32fd3e0774a6833fc5c7f19", "shasum": "" }, "require": { @@ -17640,7 +17718,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.2" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.3" }, "funding": [ { @@ -17656,7 +17734,7 @@ "type": "tidelift" } ], - "time": "2023-12-05T14:54:33+00:00" + "time": "2023-12-13T07:25:23+00:00" }, { "name": "pimple/pimple", @@ -19887,5 +19965,5 @@ "ext-pdo": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php index 621d496dd9..5d16cd7091 100644 --- a/resources/views/components/layouts/app.blade.php +++ b/resources/views/components/layouts/app.blade.php @@ -65,6 +65,8 @@ {{ $slot }} + @include('cookie-consent::index') + @filamentScripts @vite('resources/js/app.js') diff --git a/resources/views/vendor/cookie-consent/dialog.blade.php b/resources/views/vendor/cookie-consent/dialog.blade.php new file mode 100644 index 0000000000..da29e8ae13 --- /dev/null +++ b/resources/views/vendor/cookie-consent/dialog.blade.php @@ -0,0 +1,19 @@ + diff --git a/resources/views/vendor/cookie-consent/index.blade.php b/resources/views/vendor/cookie-consent/index.blade.php new file mode 100644 index 0000000000..617966df6d --- /dev/null +++ b/resources/views/vendor/cookie-consent/index.blade.php @@ -0,0 +1,52 @@ +@if ($cookieConsentConfig['enabled'] && ! $alreadyConsentedWithCookies) + @include('cookie-consent::dialog') + + +@endif diff --git a/tailwind.config.js b/tailwind.config.js index 87ddadbda7..3060b86506 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -45,8 +45,8 @@ export default { './app-modules/**/resources/views/**/*.php', './resources/views/**/*.blade.php', './resources/views/filament/**/*.blade.php', - './vendor/filament/**/*.blade.php', './vendor/awcodes/filament-tiptap-editor/resources/**/*.blade.php', + './vendor/filament/**/*.blade.php', './node_modules/flowbite/**/*.js', ], theme: { From 1411bbbbbb68efb703eee0773c5591974492cee4 Mon Sep 17 00:00:00 2001 From: danharrin Date: Fri, 15 Dec 2023 13:29:07 +0000 Subject: [PATCH 017/126] chore: fix enforcement of copyright on all files --- .../vendor/cookie-consent/dialog.blade.php | 33 +++++++++++++++++++ .../vendor/cookie-consent/index.blade.php | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/resources/views/vendor/cookie-consent/dialog.blade.php b/resources/views/vendor/cookie-consent/dialog.blade.php index da29e8ae13..81fe7837c3 100644 --- a/resources/views/vendor/cookie-consent/dialog.blade.php +++ b/resources/views/vendor/cookie-consent/dialog.blade.php @@ -1,3 +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. + + +--}}
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('m/d/Y H:i:s') }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
diff --git a/resources/views/filament/forms/components/campaigns/actions/task.blade.php b/resources/views/filament/forms/components/campaigns/actions/task.blade.php index 1a831679e8..822ca18fff 100644 --- a/resources/views/filament/forms/components/campaigns/actions/task.blade.php +++ b/resources/views/filament/forms/components/campaigns/actions/task.blade.php @@ -34,6 +34,7 @@ @php use Carbon\Carbon; use App\Models\User; + use AdvisingApp\Campaign\Settings\CampaignSettings; @endphp @@ -52,7 +53,7 @@
Due
-
{{ Carbon::parse($action['due'])->format('m/d/Y H:i:s') }}
+
{{ Carbon::parse($action['due'])->format('M j, Y H:i:s') }}
Assigned To
@@ -60,7 +61,7 @@
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('m/d/Y H:i:s') }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
From 9567a1abf6b8b599241d4caa4d32674ae4c9ba5f Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 15:37:47 +0000 Subject: [PATCH 096/126] update deps --- .../Filament/Blocks/CampaignActionBlock.php | 2 +- .../src/Filament/Blocks/CareTeamBlock.php | 2 +- .../Filament/Blocks/EngagementBatchBlock.php | 3 +- .../src/Filament/Blocks/InteractionBlock.php | 4 +- .../Filament/Blocks/ProactiveAlertBlock.php | 2 +- .../Filament/Blocks/ServiceRequestBlock.php | 4 +- .../src/Filament/Blocks/SubscriptionBlock.php | 2 +- .../src/Filament/Blocks/TaskBlock.php | 5 +- .../Filament/Pages/ManageCampaignSettings.php | 15 +- .../CampaignActionsRelationManager.php | 2 +- .../src/Settings/CampaignSettings.php | 1 - app-modules/prospect/graphql/prospect.graphql | 15 +- app/Filament/Pages/EmailConfiguration.php | 2 +- composer.json | 5 +- composer.lock | 879 +++++++++++++++--- .../actions/bulk-engagement.blade.php | 3 +- .../campaigns/actions/care-team.blade.php | 3 +- .../campaigns/actions/interaction.blade.php | 6 +- .../actions/proactive-alert.blade.php | 3 +- .../actions/service-request.blade.php | 3 +- .../campaigns/actions/subscription.blade.php | 3 +- .../campaigns/actions/task.blade.php | 3 +- 22 files changed, 797 insertions(+), 170 deletions(-) diff --git a/app-modules/campaign/src/Filament/Blocks/CampaignActionBlock.php b/app-modules/campaign/src/Filament/Blocks/CampaignActionBlock.php index 471bc52297..79a674ca46 100644 --- a/app-modules/campaign/src/Filament/Blocks/CampaignActionBlock.php +++ b/app-modules/campaign/src/Filament/Blocks/CampaignActionBlock.php @@ -36,9 +36,9 @@ namespace AdvisingApp\Campaign\Filament\Blocks; -use AdvisingApp\Campaign\Settings\CampaignSettings; use Carbon\CarbonInterface; use Filament\Forms\Components\Builder\Block; +use AdvisingApp\Campaign\Settings\CampaignSettings; abstract class CampaignActionBlock extends Block { diff --git a/app-modules/campaign/src/Filament/Blocks/CareTeamBlock.php b/app-modules/campaign/src/Filament/Blocks/CareTeamBlock.php index 75b92aefa8..ef22b7f937 100644 --- a/app-modules/campaign/src/Filament/Blocks/CareTeamBlock.php +++ b/app-modules/campaign/src/Filament/Blocks/CareTeamBlock.php @@ -36,12 +36,12 @@ namespace AdvisingApp\Campaign\Filament\Blocks; -use AdvisingApp\Campaign\Settings\CampaignSettings; use App\Models\User; use Carbon\CarbonImmutable; use Filament\Forms\Components\Select; use Filament\Forms\Components\Toggle; use Filament\Forms\Components\DateTimePicker; +use AdvisingApp\Campaign\Settings\CampaignSettings; class CareTeamBlock extends CampaignActionBlock { diff --git a/app-modules/campaign/src/Filament/Blocks/EngagementBatchBlock.php b/app-modules/campaign/src/Filament/Blocks/EngagementBatchBlock.php index 32215caa89..0940b13c6e 100644 --- a/app-modules/campaign/src/Filament/Blocks/EngagementBatchBlock.php +++ b/app-modules/campaign/src/Filament/Blocks/EngagementBatchBlock.php @@ -36,14 +36,13 @@ namespace AdvisingApp\Campaign\Filament\Blocks; -use AdvisingApp\Campaign\Settings\CampaignSettings; use Carbon\CarbonImmutable; use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\DateTimePicker; +use AdvisingApp\Campaign\Settings\CampaignSettings; use AdvisingApp\Engagement\Enums\EngagementDeliveryMethod; -use Illuminate\Support\Carbon; class EngagementBatchBlock extends CampaignActionBlock { diff --git a/app-modules/campaign/src/Filament/Blocks/InteractionBlock.php b/app-modules/campaign/src/Filament/Blocks/InteractionBlock.php index a9ee91bd65..7869ab6ea7 100644 --- a/app-modules/campaign/src/Filament/Blocks/InteractionBlock.php +++ b/app-modules/campaign/src/Filament/Blocks/InteractionBlock.php @@ -36,9 +36,8 @@ namespace AdvisingApp\Campaign\Filament\Blocks; -use AdvisingApp\Campaign\Settings\CampaignSettings; -use Carbon\CarbonImmutable; use Closure; +use Carbon\CarbonImmutable; use Filament\Forms\Components\Select; use Filament\Forms\Components\Fieldset; use Filament\Forms\Components\Textarea; @@ -47,6 +46,7 @@ use AdvisingApp\Division\Models\Division; use Filament\Forms\Components\DateTimePicker; use AdvisingApp\Interaction\Models\Interaction; +use AdvisingApp\Campaign\Settings\CampaignSettings; use AdvisingApp\Interaction\Models\InteractionType; use AdvisingApp\Interaction\Models\InteractionDriver; use AdvisingApp\Interaction\Models\InteractionStatus; diff --git a/app-modules/campaign/src/Filament/Blocks/ProactiveAlertBlock.php b/app-modules/campaign/src/Filament/Blocks/ProactiveAlertBlock.php index 3454485e66..4c9783abae 100644 --- a/app-modules/campaign/src/Filament/Blocks/ProactiveAlertBlock.php +++ b/app-modules/campaign/src/Filament/Blocks/ProactiveAlertBlock.php @@ -36,13 +36,13 @@ namespace AdvisingApp\Campaign\Filament\Blocks; -use AdvisingApp\Campaign\Settings\CampaignSettings; use Carbon\CarbonImmutable; use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; use AdvisingApp\Alert\Enums\AlertStatus; use AdvisingApp\Alert\Enums\AlertSeverity; use Filament\Forms\Components\DateTimePicker; +use AdvisingApp\Campaign\Settings\CampaignSettings; class ProactiveAlertBlock extends CampaignActionBlock { diff --git a/app-modules/campaign/src/Filament/Blocks/ServiceRequestBlock.php b/app-modules/campaign/src/Filament/Blocks/ServiceRequestBlock.php index 37e5ce9365..c5828cbff1 100644 --- a/app-modules/campaign/src/Filament/Blocks/ServiceRequestBlock.php +++ b/app-modules/campaign/src/Filament/Blocks/ServiceRequestBlock.php @@ -36,15 +36,15 @@ namespace AdvisingApp\Campaign\Filament\Blocks; -use AdvisingApp\Campaign\Settings\CampaignSettings; -use Carbon\CarbonImmutable; use Closure; use App\Models\User; +use Carbon\CarbonImmutable; use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; use Illuminate\Database\Eloquent\Model; use AdvisingApp\Division\Models\Division; use Filament\Forms\Components\DateTimePicker; +use AdvisingApp\Campaign\Settings\CampaignSettings; use Illuminate\Contracts\Database\Eloquent\Builder; use AdvisingApp\ServiceManagement\Models\ServiceRequest; use AdvisingApp\ServiceManagement\Models\ServiceRequestType; diff --git a/app-modules/campaign/src/Filament/Blocks/SubscriptionBlock.php b/app-modules/campaign/src/Filament/Blocks/SubscriptionBlock.php index 35c6aa1b2b..08a1fca8e2 100644 --- a/app-modules/campaign/src/Filament/Blocks/SubscriptionBlock.php +++ b/app-modules/campaign/src/Filament/Blocks/SubscriptionBlock.php @@ -36,12 +36,12 @@ namespace AdvisingApp\Campaign\Filament\Blocks; -use AdvisingApp\Campaign\Settings\CampaignSettings; use App\Models\User; use Carbon\CarbonImmutable; use Filament\Forms\Components\Select; use Filament\Forms\Components\Toggle; use Filament\Forms\Components\DateTimePicker; +use AdvisingApp\Campaign\Settings\CampaignSettings; class SubscriptionBlock extends CampaignActionBlock { diff --git a/app-modules/campaign/src/Filament/Blocks/TaskBlock.php b/app-modules/campaign/src/Filament/Blocks/TaskBlock.php index 89a7633f3e..6e1147825a 100644 --- a/app-modules/campaign/src/Filament/Blocks/TaskBlock.php +++ b/app-modules/campaign/src/Filament/Blocks/TaskBlock.php @@ -36,15 +36,14 @@ namespace AdvisingApp\Campaign\Filament\Blocks; -use AdvisingApp\Campaign\Settings\CampaignSettings; -use App\Models\User; -use AdvisingApp\Task\Models\Task; use Carbon\CarbonImmutable; +use AdvisingApp\Task\Models\Task; use Filament\Forms\Components\Select; use Filament\Forms\Components\Fieldset; use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\DateTimePicker; +use AdvisingApp\Campaign\Settings\CampaignSettings; class TaskBlock extends CampaignActionBlock { diff --git a/app-modules/campaign/src/Filament/Pages/ManageCampaignSettings.php b/app-modules/campaign/src/Filament/Pages/ManageCampaignSettings.php index 124c266e25..80da9539db 100644 --- a/app-modules/campaign/src/Filament/Pages/ManageCampaignSettings.php +++ b/app-modules/campaign/src/Filament/Pages/ManageCampaignSettings.php @@ -36,21 +36,10 @@ namespace AdvisingApp\Campaign\Filament\Pages; -use AdvisingApp\Campaign\Settings\CampaignSettings; -use App\Filament\Pages\EmailConfiguration; -use App\Models\User; -use App\Enums\Feature; use Filament\Forms\Form; -use App\Models\SettingsProperty; use Filament\Pages\SettingsPage; -use Illuminate\Support\Facades\Gate; -use App\Filament\Fields\TiptapEditor; -use Filament\Forms\Components\Toggle; -use Filament\Forms\Components\Section; -use Filament\Forms\Components\ColorPicker; -use FilamentTiptapEditor\Enums\TiptapOutput; -use AdvisingApp\Portal\Settings\PortalSettings; -use Filament\Forms\Components\SpatieMediaLibraryFileUpload; +use App\Filament\Pages\EmailConfiguration; +use AdvisingApp\Campaign\Settings\CampaignSettings; use Tapp\FilamentTimezoneField\Forms\Components\TimezoneSelect; class ManageCampaignSettings extends SettingsPage diff --git a/app-modules/campaign/src/Filament/Resources/CampaignResource/RelationManagers/CampaignActionsRelationManager.php b/app-modules/campaign/src/Filament/Resources/CampaignResource/RelationManagers/CampaignActionsRelationManager.php index ca87fc2edb..0abe1fc083 100644 --- a/app-modules/campaign/src/Filament/Resources/CampaignResource/RelationManagers/CampaignActionsRelationManager.php +++ b/app-modules/campaign/src/Filament/Resources/CampaignResource/RelationManagers/CampaignActionsRelationManager.php @@ -36,7 +36,6 @@ namespace AdvisingApp\Campaign\Filament\Resources\CampaignResource\RelationManagers; -use AdvisingApp\Campaign\Settings\CampaignSettings; use Filament\Forms\Form; use Filament\Tables\Table; use Filament\Forms\Components\Builder; @@ -49,6 +48,7 @@ use Filament\Tables\Actions\DeleteBulkAction; use AdvisingApp\Campaign\Models\CampaignAction; use AdvisingApp\Campaign\Enums\CampaignActionType; +use AdvisingApp\Campaign\Settings\CampaignSettings; use App\Filament\Resources\RelationManagers\RelationManager; class CampaignActionsRelationManager extends RelationManager diff --git a/app-modules/campaign/src/Settings/CampaignSettings.php b/app-modules/campaign/src/Settings/CampaignSettings.php index b4afa4d389..b919f534f7 100644 --- a/app-modules/campaign/src/Settings/CampaignSettings.php +++ b/app-modules/campaign/src/Settings/CampaignSettings.php @@ -36,7 +36,6 @@ namespace AdvisingApp\Campaign\Settings; -use App\Filament\Pages\EmailConfiguration; use Spatie\LaravelSettings\Settings; class CampaignSettings extends Settings diff --git a/app-modules/prospect/graphql/prospect.graphql b/app-modules/prospect/graphql/prospect.graphql index 32ea71fdee..4ddafa6442 100644 --- a/app-modules/prospect/graphql/prospect.graphql +++ b/app-modules/prospect/graphql/prospect.graphql @@ -1,5 +1,4 @@ -type Prospect - @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { +type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { "Unique primary key." id: UUID! @@ -23,8 +22,8 @@ input ProspectQuery { extend type Query { "List multiple prospects." prospects(where: ProspectQuery @searchBy): [Prospect!]! - @paginate - @canModel(ability: "viewAny") + @paginate + @canModel(ability: "viewAny") } input UpdateProspectInput { @@ -52,8 +51,8 @@ input CreateProspectInput { extend type Mutation { "Create a prospect." createProspect(input: CreateProspectInput! @spread): Prospect! - @create - @canModel(ability: "create") + @create + @canModel(ability: "create") "Update a prospect." updateProspect( @@ -65,7 +64,7 @@ extend type Mutation { "Delete a prospect." deleteProspect( - "The identifier of the prospect you would like to delete." - id: UUID! @whereKey + "The identifier of the prospect you would like to delete." + id: UUID! @whereKey ): Prospect @canFind(ability: "delete", find: "id") @delete } diff --git a/app/Filament/Pages/EmailConfiguration.php b/app/Filament/Pages/EmailConfiguration.php index c22d56f4de..73aa66cd49 100644 --- a/app/Filament/Pages/EmailConfiguration.php +++ b/app/Filament/Pages/EmailConfiguration.php @@ -36,11 +36,11 @@ namespace App\Filament\Pages; -use AdvisingApp\Campaign\Filament\Pages\ManageCampaignSettings; use App\Models\User; use Filament\Pages\Page; use Filament\Navigation\NavigationItem; use Symfony\Component\HttpFoundation\Response; +use AdvisingApp\Campaign\Filament\Pages\ManageCampaignSettings; use App\Filament\Resources\NotificationSettingResource\Pages\ListNotificationSettings; use AdvisingApp\Engagement\Filament\Resources\SmsTemplateResource\Pages\ListSmsTemplates; use AdvisingApp\Engagement\Filament\Resources\EmailTemplateResource\Pages\ListEmailTemplates; diff --git a/composer.json b/composer.json index e5a439570a..9a72934610 100644 --- a/composer.json +++ b/composer.json @@ -53,8 +53,8 @@ "canyon-gbs/advising-app-survey": "*", "composer/composer": "^2.6.4", "filament/filament": "^3.1", - "filament/spatie-laravel-media-library-plugin": "^3.0", - "filament/spatie-laravel-settings-plugin": "^3.0", + "filament/spatie-laravel-media-library-plugin": "^3.1", + "filament/spatie-laravel-settings-plugin": "^3.1", "friendsofcat/opensearch-migrations": "^2.0", "friendsofcat/opensearch-scout-driver": "^2.1", "friendsofcat/opensearch-scout-driver-plus": "^2.1", @@ -69,7 +69,6 @@ "laravel/tinker": "^2.8", "laravel/ui": "^4.2", "lastdragon-ru/lara-asp-graphql": "^5.3", - "league/csv": "^9.12", "league/flysystem-aws-s3-v3": "^3.22", "league/oauth2-client": "^2.7", "livewire/livewire": "^3.3", diff --git a/composer.lock b/composer.lock index 38e5758987..93a0900a98 100644 --- a/composer.lock +++ b/composer.lock @@ -4,24 +4,574 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "93858d8980e23de662dd916028747e6d", + "content-hash": "7c3eae12459225a880ae0d4729d641e4", "packages": [ + { + "name": "amphp/amp", + "version": "v2.6.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-20T17:52:18+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "amphp/parallel", + "version": "v1.4.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/3aac213ba7858566fd83d38ccb85b91b2d652cb0", + "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.6.1", + "amphp/parser": "^1", + "amphp/process": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^1.0.1", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.1", + "phpunit/phpunit": "^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Context/functions.php", + "lib/Sync/functions.php", + "lib/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "support": { + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v1.4.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2023-03-23T08:04:23+00:00" + }, + { + "name": "amphp/parallel-functions", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel-functions.git", + "reference": "04e92fcacfc921a56dfe12c23b3265e62593a7cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel-functions/zipball/04e92fcacfc921a56dfe12c23b3265e62593a7cb", + "reference": "04e92fcacfc921a56dfe12c23b3265e62593a7cb", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.0.3", + "amphp/parallel": "^1.4", + "amphp/serialization": "^1.0", + "laravel/serializable-closure": "^1.0", + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "v2.x-dev", + "amphp/phpunit-util": "^2.0", + "phpunit/phpunit": "^9.5.11" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\ParallelFunctions\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Parallel processing made simple.", + "support": { + "issues": "https://github.com/amphp/parallel-functions/issues", + "source": "https://github.com/amphp/parallel-functions/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-03T19:32:41+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/ff1de4144726c5dad5fab97f66692ebe8de3e151", + "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-12-30T18:08:47+00:00" + }, + { + "name": "amphp/process", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/76e9495fd6818b43a20167cb11d8a67f7744ee0f", + "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.4", + "php": ">=7" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous process manager.", + "homepage": "https://github.com/amphp/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v1.1.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-07-06T23:50:12+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/sync", + "version": "v1.4.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/85ab06764f4f36d63b1356b466df6111cf4b89cf", + "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.1", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/ConcurrentIterator/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Mutex, Semaphore, and other synchronization tools for Amp.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v1.4.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-10-25T18:29:10+00:00" + }, { "name": "awcodes/filament-tiptap-editor", - "version": "v3.2.11", + "version": "v3.2.14", "source": { "type": "git", "url": "https://github.com/awcodes/filament-tiptap-editor.git", - "reference": "251611b8c6fe1898c80f70cbd545c06f533d5072" + "reference": "60d5840516a862e693c7de86d2f69d5f4d3b85c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/awcodes/filament-tiptap-editor/zipball/251611b8c6fe1898c80f70cbd545c06f533d5072", - "reference": "251611b8c6fe1898c80f70cbd545c06f533d5072", + "url": "https://api.github.com/repos/awcodes/filament-tiptap-editor/zipball/60d5840516a862e693c7de86d2f69d5f4d3b85c1", + "reference": "60d5840516a862e693c7de86d2f69d5f4d3b85c1", "shasum": "" }, "require": { - "intervention/image": "^2.7", "php": "^8.1", "spatie/laravel-package-tools": "^1.9.2", "ueberdosis/tiptap-php": "^1.1" @@ -80,7 +630,7 @@ ], "support": { "issues": "https://github.com/awcodes/filament-tiptap-editor/issues", - "source": "https://github.com/awcodes/filament-tiptap-editor/tree/v3.2.11" + "source": "https://github.com/awcodes/filament-tiptap-editor/tree/v3.2.14" }, "funding": [ { @@ -88,7 +638,7 @@ "type": "github" } ], - "time": "2023-12-16T21:26:51+00:00" + "time": "2023-12-21T15:23:55+00:00" }, { "name": "aws/aws-crt-php", @@ -205,16 +755,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.294.3", + "version": "3.294.4", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "05761093c61ca7a02c1b5ae9be279bf69360e060" + "reference": "4f59bf50aa445fc3ec0b10648b205dd2465e9bec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/05761093c61ca7a02c1b5ae9be279bf69360e060", - "reference": "05761093c61ca7a02c1b5ae9be279bf69360e060", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4f59bf50aa445fc3ec0b10648b205dd2465e9bec", + "reference": "4f59bf50aa445fc3ec0b10648b205dd2465e9bec", "shasum": "" }, "require": { @@ -294,9 +844,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.294.3" + "source": "https://github.com/aws/aws-sdk-php/tree/3.294.4" }, - "time": "2023-12-19T19:07:14+00:00" + "time": "2023-12-20T19:21:19+00:00" }, { "name": "barryvdh/laravel-debugbar", @@ -3521,16 +4071,16 @@ }, { "name": "filament/actions", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/actions.git", - "reference": "94528865f7a6666a3125da0cb872b17814437dbf" + "reference": "be3a8c110aa69f3f4856816eefd477b600bc06e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/actions/zipball/94528865f7a6666a3125da0cb872b17814437dbf", - "reference": "94528865f7a6666a3125da0cb872b17814437dbf", + "url": "https://api.github.com/repos/filamentphp/actions/zipball/be3a8c110aa69f3f4856816eefd477b600bc06e2", + "reference": "be3a8c110aa69f3f4856816eefd477b600bc06e2", "shasum": "" }, "require": { @@ -3541,7 +4091,7 @@ "illuminate/contracts": "^10.0", "illuminate/database": "^10.0", "illuminate/support": "^10.0", - "league/csv": "^9.11", + "league/csv": "9.11.0", "php": "^8.1", "spatie/laravel-package-tools": "^1.9" }, @@ -3568,20 +4118,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-12-02T22:24:03+00:00" + "time": "2023-12-21T12:38:03+00:00" }, { "name": "filament/filament", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/panels.git", - "reference": "d857640aa5cc4f55f448edf1c13488fb882c0979" + "reference": "ab343c8f688207d6e5e28d19c9205087a17fc892" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/panels/zipball/d857640aa5cc4f55f448edf1c13488fb882c0979", - "reference": "d857640aa5cc4f55f448edf1c13488fb882c0979", + "url": "https://api.github.com/repos/filamentphp/panels/zipball/ab343c8f688207d6e5e28d19c9205087a17fc892", + "reference": "ab343c8f688207d6e5e28d19c9205087a17fc892", "shasum": "" }, "require": { @@ -3633,20 +4183,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-12-02T22:24:06+00:00" + "time": "2023-12-21T12:38:10+00:00" }, { "name": "filament/forms", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/forms.git", - "reference": "6a26439d7ff7b81ba00d2ca88a27a57f8e22fb14" + "reference": "810f3826c767b4eab2b8952fe7fb0d3971a51da5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/forms/zipball/6a26439d7ff7b81ba00d2ca88a27a57f8e22fb14", - "reference": "6a26439d7ff7b81ba00d2ca88a27a57f8e22fb14", + "url": "https://api.github.com/repos/filamentphp/forms/zipball/810f3826c767b4eab2b8952fe7fb0d3971a51da5", + "reference": "810f3826c767b4eab2b8952fe7fb0d3971a51da5", "shasum": "" }, "require": { @@ -3689,20 +4239,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-12-02T22:24:00+00:00" + "time": "2023-12-21T15:19:45+00:00" }, { "name": "filament/infolists", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/infolists.git", - "reference": "c99cef616da4b87640cb62e4869df9c5a8b306ce" + "reference": "4f0e981dc59340d5c5af4b309a8483d1d294dcc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/infolists/zipball/c99cef616da4b87640cb62e4869df9c5a8b306ce", - "reference": "c99cef616da4b87640cb62e4869df9c5a8b306ce", + "url": "https://api.github.com/repos/filamentphp/infolists/zipball/4f0e981dc59340d5c5af4b309a8483d1d294dcc0", + "reference": "4f0e981dc59340d5c5af4b309a8483d1d294dcc0", "shasum": "" }, "require": { @@ -3740,20 +4290,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-12-02T22:24:00+00:00" + "time": "2023-12-21T12:38:08+00:00" }, { "name": "filament/notifications", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/notifications.git", - "reference": "b0a917a041bee3819a3b58c543d82ba463ffb20c" + "reference": "5b9255c0793751534461cace12febdc0d4cc05bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/notifications/zipball/b0a917a041bee3819a3b58c543d82ba463ffb20c", - "reference": "b0a917a041bee3819a3b58c543d82ba463ffb20c", + "url": "https://api.github.com/repos/filamentphp/notifications/zipball/5b9255c0793751534461cace12febdc0d4cc05bb", + "reference": "5b9255c0793751534461cace12febdc0d4cc05bb", "shasum": "" }, "require": { @@ -3792,11 +4342,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-12-02T22:24:00+00:00" + "time": "2023-12-17T22:25:42+00:00" }, { "name": "filament/spatie-laravel-media-library-plugin", - "version": "v3.1.24", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/spatie-laravel-media-library-plugin.git", @@ -3833,16 +4383,16 @@ }, { "name": "filament/spatie-laravel-settings-plugin", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/spatie-laravel-settings-plugin.git", - "reference": "7a2bce9ccb18703f867a1a1da1ef07f03cc2eca4" + "reference": "7119f3f714dfa0ce098cd90aa2a741147a5508f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/spatie-laravel-settings-plugin/zipball/7a2bce9ccb18703f867a1a1da1ef07f03cc2eca4", - "reference": "7a2bce9ccb18703f867a1a1da1ef07f03cc2eca4", + "url": "https://api.github.com/repos/filamentphp/spatie-laravel-settings-plugin/zipball/7119f3f714dfa0ce098cd90aa2a741147a5508f6", + "reference": "7119f3f714dfa0ce098cd90aa2a741147a5508f6", "shasum": "" }, "require": { @@ -3876,30 +4426,30 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-11-14T12:07:17+00:00" + "time": "2023-12-19T13:24:28+00:00" }, { "name": "filament/support", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/support.git", - "reference": "2bdf07a954c9873e6b4c6f2654798d4b38f62a27" + "reference": "d41cd962c9a76790aa1a34bae09a9e645a69506d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/support/zipball/2bdf07a954c9873e6b4c6f2654798d4b38f62a27", - "reference": "2bdf07a954c9873e6b4c6f2654798d4b38f62a27", + "url": "https://api.github.com/repos/filamentphp/support/zipball/d41cd962c9a76790aa1a34bae09a9e645a69506d", + "reference": "d41cd962c9a76790aa1a34bae09a9e645a69506d", "shasum": "" }, "require": { - "blade-ui-kit/blade-heroicons": "^2.0", + "blade-ui-kit/blade-heroicons": "^2.2.1", "doctrine/dbal": "^3.2", "ext-intl": "*", "illuminate/contracts": "^10.0", "illuminate/support": "^10.0", "illuminate/view": "^10.0", - "livewire/livewire": "^3.0.8", + "livewire/livewire": "^3.2.3", "php": "^8.1", "ryangjchandler/blade-capture-directive": "^0.2|^0.3", "spatie/color": "^1.5", @@ -3933,20 +4483,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-12-02T22:24:03+00:00" + "time": "2023-12-21T12:38:29+00:00" }, { "name": "filament/tables", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/tables.git", - "reference": "6a9a952aefc372e77eb07a44e4eb558d94f621b3" + "reference": "ba549030218f299d19e4b3ce8ef18176f783eb6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/tables/zipball/6a9a952aefc372e77eb07a44e4eb558d94f621b3", - "reference": "6a9a952aefc372e77eb07a44e4eb558d94f621b3", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/ba549030218f299d19e4b3ce8ef18176f783eb6f", + "reference": "ba549030218f299d19e4b3ce8ef18176f783eb6f", "shasum": "" }, "require": { @@ -3986,20 +4536,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-12-02T22:24:03+00:00" + "time": "2023-12-21T12:38:38+00:00" }, { "name": "filament/widgets", - "version": "v3.1.8", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/widgets.git", - "reference": "64c4e7074908426840f4b16f82822b178dc21afa" + "reference": "290184b9dfb99436df94a9a0b8511b21811d9e64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/widgets/zipball/64c4e7074908426840f4b16f82822b178dc21afa", - "reference": "64c4e7074908426840f4b16f82822b178dc21afa", + "url": "https://api.github.com/repos/filamentphp/widgets/zipball/290184b9dfb99436df94a9a0b8511b21811d9e64", + "reference": "290184b9dfb99436df94a9a0b8511b21811d9e64", "shasum": "" }, "require": { @@ -4030,7 +4580,7 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2023-11-29T15:29:52+00:00" + "time": "2023-12-17T22:26:03+00:00" }, { "name": "firebase/php-jwt", @@ -5632,16 +6182,16 @@ }, { "name": "laravel/framework", - "version": "v10.38.0", + "version": "v10.38.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "531732a17e4d0fa4fc4fb987a72abbdb93537d3a" + "reference": "ced4689f495213e9d23995b36098f12a802cc15b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/531732a17e4d0fa4fc4fb987a72abbdb93537d3a", - "reference": "531732a17e4d0fa4fc4fb987a72abbdb93537d3a", + "url": "https://api.github.com/repos/laravel/framework/zipball/ced4689f495213e9d23995b36098f12a802cc15b", + "reference": "ced4689f495213e9d23995b36098f12a802cc15b", "shasum": "" }, "require": { @@ -5687,6 +6237,8 @@ "voku/portable-ascii": "^2.0" }, "conflict": { + "carbonphp/carbon-doctrine-types": ">=3.0", + "doctrine/dbal": ">=4.0", "tightenco/collect": "<5.5.33" }, "provide": { @@ -5798,6 +6350,7 @@ "files": [ "src/Illuminate/Collections/helpers.php", "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Support/helpers.php" ], @@ -5830,7 +6383,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-12-19T14:59:00+00:00" + "time": "2023-12-20T14:52:12+00:00" }, { "name": "laravel/prompts", @@ -6789,36 +7342,35 @@ }, { "name": "league/csv", - "version": "9.13.0", + "version": "9.11.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "3690cc71bfe8dc3b6daeef356939fac95348f0a8" + "reference": "33149c4bea4949aa4fa3d03fb11ed28682168b39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/3690cc71bfe8dc3b6daeef356939fac95348f0a8", - "reference": "3690cc71bfe8dc3b6daeef356939fac95348f0a8", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/33149c4bea4949aa4fa3d03fb11ed28682168b39", + "reference": "33149c4bea4949aa4fa3d03fb11ed28682168b39", "shasum": "" }, "require": { - "ext-filter": "*", "ext-json": "*", "ext-mbstring": "*", "php": "^8.1.2" }, "require-dev": { - "doctrine/collections": "^2.1.4", + "doctrine/collections": "^2.1.3", "ext-dom": "*", "ext-xdebug": "*", "friendsofphp/php-cs-fixer": "^v3.22.0", - "phpbench/phpbench": "^1.2.15", - "phpstan/phpstan": "^1.10.50", - "phpstan/phpstan-deprecation-rules": "^1.1.4", - "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5.2", - "phpunit/phpunit": "^10.5.3", - "symfony/var-dumper": "^6.4.0" + "phpbench/phpbench": "^1.2.14", + "phpstan/phpstan": "^1.10.26", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.13", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^10.3.1", + "symfony/var-dumper": "^6.3.3" }, "suggest": { "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", @@ -6874,7 +7426,7 @@ "type": "github" } ], - "time": "2023-12-16T11:03:20+00:00" + "time": "2023-09-23T10:09:54+00:00" }, { "name": "league/flysystem", @@ -7535,16 +8087,16 @@ }, { "name": "livewire/livewire", - "version": "v3.3.2", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "cfda4d16fdd63052cff3030f066deeb2b6f97c9b" + "reference": "6dd3bec8c711cd792742be4620057637e261e6f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/cfda4d16fdd63052cff3030f066deeb2b6f97c9b", - "reference": "cfda4d16fdd63052cff3030f066deeb2b6f97c9b", + "url": "https://api.github.com/repos/livewire/livewire/zipball/6dd3bec8c711cd792742be4620057637e261e6f7", + "reference": "6dd3bec8c711cd792742be4620057637e261e6f7", "shasum": "" }, "require": { @@ -7597,7 +8149,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v3.3.2" + "source": "https://github.com/livewire/livewire/tree/v3.3.3" }, "funding": [ { @@ -7605,7 +8157,7 @@ "type": "github" } ], - "time": "2023-12-19T18:02:00+00:00" + "time": "2023-12-20T05:34:05+00:00" }, { "name": "lomkit/laravel-rest-api", @@ -11710,28 +12262,30 @@ }, { "name": "spatie/laravel-data", - "version": "3.10.1", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-data.git", - "reference": "23dd20812d3ba829233081679b5bf0f97645d300" + "reference": "e9cb66136974b6a6e290d6d2e2d484bac1727537" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-data/zipball/23dd20812d3ba829233081679b5bf0f97645d300", - "reference": "23dd20812d3ba829233081679b5bf0f97645d300", + "url": "https://api.github.com/repos/spatie/laravel-data/zipball/e9cb66136974b6a6e290d6d2e2d484bac1727537", + "reference": "e9cb66136974b6a6e290d6d2e2d484bac1727537", "shasum": "" }, "require": { "illuminate/contracts": "^9.30|^10.0", "php": "^8.1", "phpdocumentor/type-resolver": "^1.5", - "spatie/laravel-package-tools": "^1.9.0" + "spatie/laravel-package-tools": "^1.9.0", + "spatie/php-structure-discoverer": "^2.0" }, "require-dev": { "fakerphp/faker": "^1.14", "friendsofphp/php-cs-fixer": "^3.0", "inertiajs/inertia-laravel": "^0.6.3", + "mockery/mockery": "^1.6", "nesbot/carbon": "^2.63", "nette/php-generator": "^3.5", "nunomaduro/larastan": "^2.0", @@ -11781,7 +12335,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-data/issues", - "source": "https://github.com/spatie/laravel-data/tree/3.10.1" + "source": "https://github.com/spatie/laravel-data/tree/3.11.0" }, "funding": [ { @@ -11789,7 +12343,7 @@ "type": "github" } ], - "time": "2023-12-04T14:59:58+00:00" + "time": "2023-12-21T12:31:34+00:00" }, { "name": "spatie/laravel-health", @@ -12220,6 +12774,87 @@ ], "time": "2023-12-04T11:38:23+00:00" }, + { + "name": "spatie/php-structure-discoverer", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/php-structure-discoverer.git", + "reference": "3e532f0952d37bb10cddd544800eb659499cda3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/php-structure-discoverer/zipball/3e532f0952d37bb10cddd544800eb659499cda3d", + "reference": "3e532f0952d37bb10cddd544800eb659499cda3d", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.6.2", + "amphp/parallel": "^1.4.1", + "amphp/parallel-functions": "^1.1", + "illuminate/collections": "^9.30|^10.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.4.3", + "symfony/finder": "^6.0|^7.0" + }, + "require-dev": { + "illuminate/console": "^9.30|^10.0", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^6.0", + "nunomaduro/larastan": "^2.0.1", + "orchestra/testbench": "^7.0|^8.0", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5", + "spatie/laravel-ray": "^1.26" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\StructureDiscoverer\\StructureDiscovererServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\StructureDiscoverer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ruben Van Assche", + "email": "ruben@spatie.be", + "role": "Developer" + } + ], + "description": "Automatically discover structures within your PHP application", + "homepage": "https://github.com/spatie/php-structure-discoverer", + "keywords": [ + "discover", + "laravel", + "php", + "php-structure-discoverer" + ], + "support": { + "issues": "https://github.com/spatie/php-structure-discoverer/issues", + "source": "https://github.com/spatie/php-structure-discoverer/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/LaravelAutoDiscoverer", + "type": "github" + } + ], + "time": "2023-12-21T12:04:07+00:00" + }, { "name": "spatie/regex", "version": "3.1.1", @@ -18428,16 +19063,16 @@ }, { "name": "rector/rector", - "version": "0.18.12", + "version": "0.18.13", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "ed8d5352a3faa69e4a5e315896abffd4bc29c828" + "reference": "f8011a76d36aa4f839f60f3b4f97707d97176618" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/ed8d5352a3faa69e4a5e315896abffd4bc29c828", - "reference": "ed8d5352a3faa69e4a5e315896abffd4bc29c828", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/f8011a76d36aa4f839f60f3b4f97707d97176618", + "reference": "f8011a76d36aa4f839f60f3b4f97707d97176618", "shasum": "" }, "require": { @@ -18472,7 +19107,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/0.18.12" + "source": "https://github.com/rectorphp/rector/tree/0.18.13" }, "funding": [ { @@ -18480,7 +19115,7 @@ "type": "github" } ], - "time": "2023-12-04T08:47:30+00:00" + "time": "2023-12-20T16:08:01+00:00" }, { "name": "sebastian/cli-parser", @@ -18728,20 +19363,20 @@ }, { "name": "sebastian/complexity", - "version": "3.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -18750,7 +19385,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -18774,7 +19409,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -18782,7 +19417,7 @@ "type": "github" } ], - "time": "2023-09-28T11:50:59+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", @@ -19057,20 +19692,20 @@ }, { "name": "sebastian/lines-of-code", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -19103,7 +19738,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -19111,7 +19746,7 @@ "type": "github" } ], - "time": "2023-08-31T09:25:50+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", @@ -19614,16 +20249,16 @@ }, { "name": "spatie/laravel-ignition", - "version": "2.3.2", + "version": "2.3.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "4800661a195e15783477d99f7f8f669a49793996" + "reference": "66499cd3c858642ded56dafb8fa0352057ca20dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/4800661a195e15783477d99f7f8f669a49793996", - "reference": "4800661a195e15783477d99f7f8f669a49793996", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/66499cd3c858642ded56dafb8fa0352057ca20dd", + "reference": "66499cd3c858642ded56dafb8fa0352057ca20dd", "shasum": "" }, "require": { @@ -19702,7 +20337,7 @@ "type": "github" } ], - "time": "2023-12-15T13:44:49+00:00" + "time": "2023-12-21T09:43:05+00:00" }, { "name": "spatie/laravel-ray", @@ -20602,5 +21237,5 @@ "ext-pdo": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/resources/views/filament/forms/components/campaigns/actions/bulk-engagement.blade.php b/resources/views/filament/forms/components/campaigns/actions/bulk-engagement.blade.php index e14fed9a7f..8f496a3214 100644 --- a/resources/views/filament/forms/components/campaigns/actions/bulk-engagement.blade.php +++ b/resources/views/filament/forms/components/campaigns/actions/bulk-engagement.blade.php @@ -60,7 +60,8 @@
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} + {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
diff --git a/resources/views/filament/forms/components/campaigns/actions/care-team.blade.php b/resources/views/filament/forms/components/campaigns/actions/care-team.blade.php index 84224cf3bb..ebd373c844 100644 --- a/resources/views/filament/forms/components/campaigns/actions/care-team.blade.php +++ b/resources/views/filament/forms/components/campaigns/actions/care-team.blade.php @@ -55,7 +55,8 @@
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} + {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
diff --git a/resources/views/filament/forms/components/campaigns/actions/interaction.blade.php b/resources/views/filament/forms/components/campaigns/actions/interaction.blade.php index ca2d4c9395..6e7b223b21 100644 --- a/resources/views/filament/forms/components/campaigns/actions/interaction.blade.php +++ b/resources/views/filament/forms/components/campaigns/actions/interaction.blade.php @@ -84,7 +84,8 @@
Start Time
-
{{ Carbon::parse($action['start_datetime'])->format('M j, Y H:i:s') }}
+
{{ Carbon::parse($action['start_datetime'])->format('M j, Y H:i:s') }} +
End Time
@@ -100,7 +101,8 @@
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} + {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
diff --git a/resources/views/filament/forms/components/campaigns/actions/proactive-alert.blade.php b/resources/views/filament/forms/components/campaigns/actions/proactive-alert.blade.php index f4fbaca258..a892badb62 100644 --- a/resources/views/filament/forms/components/campaigns/actions/proactive-alert.blade.php +++ b/resources/views/filament/forms/components/campaigns/actions/proactive-alert.blade.php @@ -60,7 +60,8 @@
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} + {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
diff --git a/resources/views/filament/forms/components/campaigns/actions/service-request.blade.php b/resources/views/filament/forms/components/campaigns/actions/service-request.blade.php index d58ed99765..e70ea90ee2 100644 --- a/resources/views/filament/forms/components/campaigns/actions/service-request.blade.php +++ b/resources/views/filament/forms/components/campaigns/actions/service-request.blade.php @@ -77,7 +77,8 @@
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} + {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
diff --git a/resources/views/filament/forms/components/campaigns/actions/subscription.blade.php b/resources/views/filament/forms/components/campaigns/actions/subscription.blade.php index 5db266ba47..a9f6d67db3 100644 --- a/resources/views/filament/forms/components/campaigns/actions/subscription.blade.php +++ b/resources/views/filament/forms/components/campaigns/actions/subscription.blade.php @@ -55,7 +55,8 @@
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} + {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
diff --git a/resources/views/filament/forms/components/campaigns/actions/task.blade.php b/resources/views/filament/forms/components/campaigns/actions/task.blade.php index 822ca18fff..9b38104f25 100644 --- a/resources/views/filament/forms/components/campaigns/actions/task.blade.php +++ b/resources/views/filament/forms/components/campaigns/actions/task.blade.php @@ -61,7 +61,8 @@
Execute At
-
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
+
{{ Carbon::parse($action['execute_at'])->format('M j, Y H:i:s') }} + {{ app(CampaignSettings::class)->getActionExecutionTimezoneLabel() }}
From 0f82a3e970516498f3295331c7df7c16f04eba98 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 15:39:52 +0000 Subject: [PATCH 097/126] add all operations --- app-modules/prospect/graphql/prospect.graphql | 68 ++++++++++++++++--- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/app-modules/prospect/graphql/prospect.graphql b/app-modules/prospect/graphql/prospect.graphql index e96557a0e7..53a9ce8cc8 100644 --- a/app-modules/prospect/graphql/prospect.graphql +++ b/app-modules/prospect/graphql/prospect.graphql @@ -1,24 +1,70 @@ -type Prospect { +type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { "Unique primary key." id: UUID! - status_id: UUID + "The first name of the prospect." + first_name: String! - source_id: UUID + "The last name of the prospect." + last_name: String! - first_name: String + "The full name of the prospect." + full_name: String! +} - last_name: String +input ProspectQuery { + id: UUID! + first_name: String! + last_name: String! + full_name: String! +} - full_name: String +extend type Query { + "List multiple prospects." + prospects(where: ProspectQuery @searchBy): [Prospect!]! + @paginate + @canModel(ability: "viewAny") } -input ProspectQuery { - id: UUID +input UpdateProspectInput { + "The first name of the prospect." + first_name: String @rules(apply: ["string", "max:255"]) + + "The last name of the prospect." + last_name: String @rules(apply: ["string", "max:255"]) + + "The full name of the prospect." + full_name: String @rules(apply: ["string", "max:255"]) +} + +input CreateProspectInput { + "The first name of the prospect." + first_name: String! @rules(apply: ["required", "string", "max:255"]) + + "The last name of the prospect." + last_name: String! @rules(apply: ["required", "string", "max:255"]) + + "The full name of the prospect." + full_name: String! @rules(apply: ["required", "string", "max:255"]) +} + +extend type Mutation { + "Create a prospect." + createProspect(input: CreateProspectInput! @spread): Prospect! + @create + @canModel(ability: "create") - first_name: String + "Update a prospect." + updateProspect( + "The identifier of the prospect you would like to update." + id: UUID! @whereKey - last_name: String + input: UpdateProspectInput! @spread + ): Prospect! @canFind(ability: "update", find: "id") @update - full_name: String + "Delete a prospect." + deleteProspect( + "The identifier of the prospect you would like to delete." + id: UUID! @whereKey + ): Prospect @canFind(ability: "delete", find: "id") @delete } From bb959cc53e3b24258ccd19599b15486c2b42cfcc Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 15:40:11 +0000 Subject: [PATCH 098/126] revert --- app-modules/prospect/graphql/prospect.graphql | 68 +++---------------- 1 file changed, 11 insertions(+), 57 deletions(-) diff --git a/app-modules/prospect/graphql/prospect.graphql b/app-modules/prospect/graphql/prospect.graphql index 4ddafa6442..e96557a0e7 100644 --- a/app-modules/prospect/graphql/prospect.graphql +++ b/app-modules/prospect/graphql/prospect.graphql @@ -1,70 +1,24 @@ -type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { +type Prospect { "Unique primary key." id: UUID! - "The first name of the prospect." - first_name: String! + status_id: UUID - "The last name of the prospect." - last_name: String! + source_id: UUID - "The full name of the prospect." - full_name: String! -} + first_name: String -input ProspectQuery { - id: UUID! - first_name: String! - last_name: String! - full_name: String! -} + last_name: String -extend type Query { - "List multiple prospects." - prospects(where: ProspectQuery @searchBy): [Prospect!]! - @paginate - @canModel(ability: "viewAny") + full_name: String } -input UpdateProspectInput { - "The first name of the prospect." - first_name: String @rules(apply: ["string", "max:255"]) - - "The last name of the prospect." - last_name: String @rules(apply: ["string", "max:255"]) - - "The full name of the prospect." - full_name: String @rules(apply: ["string", "max:255"]) -} - -input CreateProspectInput { - "The first name of the prospect." - first_name: String! @rules(apply: ["required", "string", "max:255"]) - - "The last name of the prospect." - last_name: String! @rules(apply: ["required", "string", "max:255"]) - - "The full name of the prospect." - full_name: String! @rules(apply: ["required", "string", "max:255"]) -} - -extend type Mutation { - "Create a prospect." - createProspect(input: CreateProspectInput! @spread): Prospect! - @create - @canModel(ability: "create") +input ProspectQuery { + id: UUID - "Update a prospect." - updateProspect( - "The identifier of the prospect you would like to update." - id: UUID! @whereKey + first_name: String - input: UpdateProspectInput! @spread - ): Prospect! @canFind(ability: "update", find: "id") @update + last_name: String - "Delete a prospect." - deleteProspect( - "The identifier of the prospect you would like to delete." - id: UUID! @whereKey - ): Prospect @canFind(ability: "delete", find: "id") @delete + full_name: String } From 59c4bf624ce5e414c87a78f8feed8a1761bbc6d7 Mon Sep 17 00:00:00 2001 From: Matthew Myers Date: Thu, 21 Dec 2023 10:41:42 -0500 Subject: [PATCH 099/126] Feedback. --- app-modules/interaction/graphql/interaction.graphql | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index f14d03958a..b6d1f151f1 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -218,10 +218,12 @@ input CreateInteractionInput { division_id: UUID! @rules(apply: ["required", "exists:divisions,id"]) "The start datetime of the interaction." - start_datetime: DateTime! @rules(apply: ["required", "date:Y-m-d H:i:s"]) + start_datetime: DateTime! + @rules(apply: ["required", "date_format:Y-m-d H:i:s"]) "The end datetime of the interaction." - end_datetime: DateTime @rules(apply: ["nullable", "date:Y-m-d H:i:s"]) + end_datetime: DateTime + @rules(apply: ["nullable", "date_format:Y-m-d H:i:s"]) } type InteractionMutations { From cd70dbbdf638add2d04e3fa8029d8503f64c4f76 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 15:42:45 +0000 Subject: [PATCH 100/126] update deps --- composer.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.lock b/composer.lock index 93a0900a98..812cc05701 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": "7c3eae12459225a880ae0d4729d641e4", + "content-hash": "76b62f75042752e87277f492a1212ec7", "packages": [ { "name": "amphp/amp", @@ -18588,23 +18588,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.10", + "version": "10.1.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "599109c8ca6bae97b23482d557d2874c25a65e59" + "reference": "78c3b7625965c2513ee96569a4dbb62601784145" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/599109c8ca6bae97b23482d557d2874c25a65e59", - "reference": "599109c8ca6bae97b23482d557d2874c25a65e59", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145", + "reference": "78c3b7625965c2513ee96569a4dbb62601784145", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1", "phpunit/php-file-iterator": "^4.0", "phpunit/php-text-template": "^3.0", @@ -18654,7 +18654,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.10" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11" }, "funding": [ { @@ -18662,7 +18662,7 @@ "type": "github" } ], - "time": "2023-12-11T06:28:43+00:00" + "time": "2023-12-21T15:38:30+00:00" }, { "name": "phpunit/php-file-iterator", @@ -21232,7 +21232,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.2", + "php": "8.2.*", "ext-gd": "*", "ext-pdo": "*" }, From 123b0b2e2a928f1c8d3f40cfaa160a6de66bded5 Mon Sep 17 00:00:00 2001 From: Matthew Myers Date: Thu, 21 Dec 2023 10:46:52 -0500 Subject: [PATCH 101/126] Fix MyTasks widget status color. --- app/Filament/Widgets/MyTasks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Filament/Widgets/MyTasks.php b/app/Filament/Widgets/MyTasks.php index 100bbd993c..dcf3278870 100644 --- a/app/Filament/Widgets/MyTasks.php +++ b/app/Filament/Widgets/MyTasks.php @@ -72,7 +72,7 @@ public function table(Table $table): Table TextColumn::make('status') ->formatStateUsing(fn (TaskStatus $state): string => str($state->value)->title()->headline()) ->badge() - ->color(fn (Task $record) => $record->status->getTableColor()), + ->color(fn (Task $record) => $record->status->getColor()), TextColumn::make('due') ->label('Due Date') ->sortable(), From b71df21f8dfe5ccd7481331fba0da89cbb908309 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 15:52:30 +0000 Subject: [PATCH 102/126] [ADVAPP-110]: Form submission request query issue --- .../form/src/Filament/Actions/RequestFormSubmission.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app-modules/form/src/Filament/Actions/RequestFormSubmission.php b/app-modules/form/src/Filament/Actions/RequestFormSubmission.php index 2c2c53c32f..b8f8ecc515 100644 --- a/app-modules/form/src/Filament/Actions/RequestFormSubmission.php +++ b/app-modules/form/src/Filament/Actions/RequestFormSubmission.php @@ -42,6 +42,7 @@ use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; use Filament\Notifications\Notification; +use Illuminate\Database\Query\Expression; use Filament\Forms\Components\Wizard\Step; use Filament\Resources\Pages\ManageRelatedRecords; use AdvisingApp\Form\Enums\FormSubmissionRequestDeliveryMethod; @@ -64,7 +65,7 @@ protected function setUp(): void ->all()) ->getSearchResultsUsing(fn (string $search): array => Form::query() ->where('is_authenticated', true) - ->where('lower(name)', 'like', '%' . Str::lower($search) . '%') + ->where(new Expression('lower(name)'), 'like', '%' . Str::lower($search) . '%') ->limit(50) ->pluck('name', 'id') ->all()) From 998e09825c046c34fcd7df732fce8c5c485d2b58 Mon Sep 17 00:00:00 2001 From: Matthew Myers Date: Thu, 21 Dec 2023 11:02:18 -0500 Subject: [PATCH 103/126] Remove missed old api file. --- public/vendor/rest/openapi.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 public/vendor/rest/openapi.json diff --git a/public/vendor/rest/openapi.json b/public/vendor/rest/openapi.json deleted file mode 100644 index 418a578b6d..0000000000 --- a/public/vendor/rest/openapi.json +++ /dev/null @@ -1 +0,0 @@ -{"openapi":"3.1.0","info":{"title":"Advising App\u2122 REST API","summary":"","description":"Advising App\u2122 created by Canyon GBS\u2122 API Documentation","contact":{"name":"","url":"","email":""},"license":{"name":"Elastic License 2.0","identifier":"Elastic-2.0"},"version":"0.1.0"},"paths":{"\/api\/v1\/prospects":{"get":{"tags":["Prospects"],"summary":"Get the resource detail","responses":{"default":{"description":"","content":{"application\/json":{"example":{"data":{"actions":[],"instructions":[],"fields":["id","first_name","last_name","full_name","preferred","description","email","email_2","mobile","sms_opt_out","email_bounce","phone","address","address_2","birthdate","hsgrad","created_at","updated_at"],"limits":[10,25,50],"scopes":[],"relations":[{"resources":["Assist\\Prospect\\Rest\\Resources\\ProspectStatusResource"],"relation":"status","constraints":{"required_on_creation":true,"prohibited_on_creation":false,"required_on_update":false,"prohibited_on_update":false},"name":"BelongsTo"},{"resources":["Assist\\Prospect\\Rest\\Resources\\ProspectSourceResource"],"relation":"source","constraints":{"required_on_creation":true,"prohibited_on_creation":false,"required_on_update":false,"prohibited_on_update":false},"name":"BelongsTo"}],"rules":{"all":[],"create":{"id":["missing"],"first_name":["required","string","max:255"],"last_name":["required","string","max:255"],"full_name":["required","string","max:255"],"preferred":["nullable","string","max:255"],"description":["nullable","string","max:65535"],"email":["nullable","email","max:255"],"email_2":["nullable","email","max:255"],"mobile":["nullable","string","max:255"],"sms_opt_out":["boolean"],"email_bounce":["boolean"],"phone":["nullable","string","max:255"],"address":["nullable","string","max:255"],"address_2":["nullable","string","max:255"],"birthdate":["nullable","date","date_format:Y-m-d"],"hsgrad":["nullable","integer","digits:4"],"created_at":["missing"],"updated_at":["missing"]},"update":{"id":["missing"],"first_name":["string","max:255"],"last_name":["string","max:255"],"full_name":["string","max:255"],"preferred":["nullable","string","max:255"],"description":["nullable","string","max:65535"],"email":["nullable","email","max:255"],"email_2":["nullable","email","max:255"],"mobile":["nullable","string","max:255"],"sms_opt_out":["boolean"],"email_bounce":["boolean"],"phone":["nullable","string","max:255"],"address":["nullable","string","max:255"],"address_2":["nullable","string","max:255"],"birthdate":["nullable","date","date_format:Y-m-d"],"hsgrad":["nullable","integer","digits:4"],"created_at":["missing"],"updated_at":["missing"]}}}}}}}},"description":"Get every detail about the resource according to the current user connected"},"delete":{"tags":["Prospects"],"summary":"Perform a destroy request","responses":{"default":{"description":"","content":{"application\/json":{"example":{"data":{"first_name":"Shayna","last_name":"Pouros","full_name":"Shayna Pouros","preferred":"Imelda","description":"Quia velit natus recusandae rerum molestiae earum cum. Deleniti facere est accusantium similique quos. Animi nihil velit necessitatibus tempora autem exercitationem. Debitis ipsam aut dolor in quidem magnam neque.","email":"kobe27@feil.com","email_2":"toy.bednar@hotmail.com","mobile":"(845) 376-8789","sms_opt_out":true,"email_bounce":true,"phone":"820.594.2178","address":"113 Wisoky Port\nPagacside, DE 16947","address_2":"7323 Jamal Summit\nWatsicahaven, CA 61393","birthdate":"2002-02-15","hsgrad":"1999"},"meta":[]}}}}},"description":"Delete database records using primary key","requestBody":{"content":{"application\/json":{"example":{"resources":[1,5,6]}}}}}},"\/api\/v1\/prospects\/search":{"post":{"tags":["Prospects"],"summary":"Perform a search request","responses":{"default":{"description":"","content":{"application\/json":{"example":{"data":{"first_name":"Assunta","last_name":"Auer","full_name":"Assunta Auer","preferred":"Meggie","description":"Tempore placeat magni architecto aliquam est est id. Nulla asperiores id at vel. Ut amet recusandae eveniet dolorem est culpa. Beatae quam aliquid fugiat qui. Dolorem dolor nisi doloremque magnam consequuntur laudantium.","email":"ledner.dallin@stroman.org","email_2":"dee.stroman@yahoo.com","mobile":"(463) 623-5099","sms_opt_out":false,"email_bounce":true,"phone":"1-718-925-4641","address":"29084 Windler Hill Apt. 702\nHermistonborough, NY 68531-9928","address_2":"51390 Zena Falls\nLake Yasmeenberg, NY 57334-5298","birthdate":"1970-09-17","hsgrad":"1991"},"meta":[]}}}}},"description":"Crunch the Api's data with multiple attributes","requestBody":{"content":{"application\/json":{"example":{"search":{"scopes":[],"filters":[{"field":"id","operator":"=","value":""},{"field":"first_name","operator":"=","value":""},{"field":"last_name","operator":"=","value":""},{"field":"full_name","operator":"=","value":""},{"field":"preferred","operator":"=","value":""},{"field":"description","operator":"=","value":""},{"field":"email","operator":"=","value":""},{"field":"email_2","operator":"=","value":""},{"field":"mobile","operator":"=","value":""},{"field":"sms_opt_out","operator":"=","value":""},{"field":"email_bounce","operator":"=","value":""},{"field":"phone","operator":"=","value":""},{"field":"address","operator":"=","value":""},{"field":"address_2","operator":"=","value":""},{"field":"birthdate","operator":"=","value":""},{"field":"hsgrad","operator":"=","value":""},{"field":"created_at","operator":"=","value":""},{"field":"updated_at","operator":"=","value":""}],"sorts":[{"field":"id","direction":"desc"},{"field":"first_name","direction":"desc"},{"field":"last_name","direction":"desc"},{"field":"full_name","direction":"desc"},{"field":"preferred","direction":"desc"},{"field":"description","direction":"desc"},{"field":"email","direction":"desc"},{"field":"email_2","direction":"desc"},{"field":"mobile","direction":"desc"},{"field":"sms_opt_out","direction":"desc"},{"field":"email_bounce","direction":"desc"},{"field":"phone","direction":"desc"},{"field":"address","direction":"desc"},{"field":"address_2","direction":"desc"},{"field":"birthdate","direction":"desc"},{"field":"hsgrad","direction":"desc"},{"field":"created_at","direction":"desc"},{"field":"updated_at","direction":"desc"}],"selects":[{"field":"id"},{"field":"first_name"},{"field":"last_name"},{"field":"full_name"},{"field":"preferred"},{"field":"description"},{"field":"email"},{"field":"email_2"},{"field":"mobile"},{"field":"sms_opt_out"},{"field":"email_bounce"},{"field":"phone"},{"field":"address"},{"field":"address_2"},{"field":"birthdate"},{"field":"hsgrad"},{"field":"created_at"},{"field":"updated_at"}],"includes":[{"relation":"status"},{"relation":"source"}],"aggregates":[],"instructions":[],"gates":["create","update","delete"],"page":1,"limit":10}}}}}}},"\/api\/v1\/prospects\/mutate":{"post":{"tags":["Prospects"],"summary":"Perform a mutate request","responses":{"default":{"description":"","content":{"application\/json":{"example":{"created":[1],"updated":[2,3]}}}}},"description":"Create \/ Modify the database data with multiple options","requestBody":{"content":{"application\/json":{"example":{"mutate":[{"operation":"create","attributes":{"id":"","first_name":"","last_name":"","full_name":"","preferred":"","description":"","email":"","email_2":"","mobile":"","sms_opt_out":"","email_bounce":"","phone":"","address":"","address_2":"","birthdate":"","hsgrad":"","created_at":"","updated_at":""},"relations":{"status":{"operation":"update","key":1},"source":{"operation":"update","key":1}}}]}}}}}},"\/api\/v1\/prospects\/actions\/{action}":{"parameters":[{"name":"action","in":"path","description":"The action uriKey","required":true,"schema":{"type":"string"}}],"post":{"tags":["Prospects"],"summary":"Perform an action request","responses":{"default":{"description":"","content":{"application\/json":{"example":{"data":{"impacted":2}}}}}},"description":"Launch actions","requestBody":{"content":{"application\/json":{"example":{"search":{"filters":[{"field":"has_received_welcome_notification","value":false}]},"fields":[{"name":"expires_at","value":"2023-04-29"}]}}}}}}},"servers":[],"security":[]} \ No newline at end of file From a1d1339f22b531cb8e80a493c781fd7f5b502169 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 16:37:27 +0000 Subject: [PATCH 104/126] add fields to model --- .../interaction/graphql/interaction.graphql | 4 +- app-modules/prospect/graphql/prospect.graphql | 89 +++++++++++++++---- .../graphql/student.graphql | 2 +- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index 3b2c95418c..e66da9e4fc 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -56,8 +56,8 @@ type Interaction } input InteractionInteractablesQuery { - student: StudentQuery - prospect: ProspectQuery + student: StudentsQuery + prospect: ProspectsQuery service_request: ServiceRequestQuery } diff --git a/app-modules/prospect/graphql/prospect.graphql b/app-modules/prospect/graphql/prospect.graphql index 53a9ce8cc8..71a6fcea30 100644 --- a/app-modules/prospect/graphql/prospect.graphql +++ b/app-modules/prospect/graphql/prospect.graphql @@ -10,20 +10,68 @@ type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { "The full name of the prospect." full_name: String! + + "The preferred name of the prospect." + preferred: String + + "The description of the prospect." + description: String + + "The email of the prospect." + email: String + + "The email 2 of the prospect." + email_2: String + + "The mobile number of the prospect." + mobile: String + + "The phone number of the prospect." + phone: String + + "If the prospect is opted out of SMS messages." + sms_opt_out: Boolean + + "If the prospect's email bounces." + email_bounce: Boolean + + "The address of the prospect." + address: String + + "The address 2 of the prospect." + address_2: String + + "The birthdate of the prospect." + birthdate: DateTime + + "The High School graduation year of the prospect." + hs_grad_year: String } -input ProspectQuery { +input ProspectsQuery { id: UUID! first_name: String! last_name: String! full_name: String! } -extend type Query { +type ProspectQueries { + "Find a single prospect by an identifying attribute." + find( + "The value of the attribute to match." + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospects"]) + ): Prospect @find @canResolved(ability: "view") + "List multiple prospects." - prospects(where: ProspectQuery @searchBy): [Prospect!]! - @paginate - @canModel(ability: "viewAny") + list(where: ProspectsQuery @searchBy): [Prospect!]! + @paginate + @canModel(ability: "viewAny") +} + +extend type Query { + prospect: ProspectQueries! @namespaced } input UpdateProspectInput { @@ -48,23 +96,32 @@ input CreateProspectInput { full_name: String! @rules(apply: ["required", "string", "max:255"]) } -extend type Mutation { - "Create a prospect." - createProspect(input: CreateProspectInput! @spread): Prospect! - @create - @canModel(ability: "create") +type ProspectMutations { + "Create an prospect." + create(input: CreateProspectInput! @spread): Prospect! + @create + @canModel(ability: "create") - "Update a prospect." - updateProspect( + "Update an prospect." + update( "The identifier of the prospect you would like to update." - id: UUID! @whereKey + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospects"]) + "The fields you would like to update." input: UpdateProspectInput! @spread ): Prospect! @canFind(ability: "update", find: "id") @update - "Delete a prospect." - deleteProspect( + "Delete an prospect." + delete( "The identifier of the prospect you would like to delete." - id: UUID! @whereKey + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospects"]) ): Prospect @canFind(ability: "delete", find: "id") @delete } + +extend type Mutation { + prospect: ProspectMutations! @namespaced +} diff --git a/app-modules/student-data-model/graphql/student.graphql b/app-modules/student-data-model/graphql/student.graphql index dc04c972c0..5e8a850d66 100644 --- a/app-modules/student-data-model/graphql/student.graphql +++ b/app-modules/student-data-model/graphql/student.graphql @@ -10,7 +10,7 @@ type Student { full_name: String } -input StudentQuery { +input StudentsQuery { sisid: ID otherid: String From 5bf075d4930f9625f2a08c3328da0243277b59fa Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 17:10:55 +0000 Subject: [PATCH 105/126] start status --- .../graphql/interaction-campaign.graphql | 2 +- .../graphql/interaction-driver.graphql | 2 +- .../graphql/interaction-outcome.graphql | 2 +- .../graphql/interaction-relation.graphql | 2 +- .../graphql/interaction-status.graphql | 14 +- .../graphql/interaction-type.graphql | 2 +- .../prospect/graphql/prospect-status.graphql | 123 ++++++++++++++++++ app-modules/prospect/graphql/prospect.graphql | 12 ++ 8 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 app-modules/prospect/graphql/prospect-status.graphql diff --git a/app-modules/interaction/graphql/interaction-campaign.graphql b/app-modules/interaction/graphql/interaction-campaign.graphql index cda799661d..ca98052c8b 100644 --- a/app-modules/interaction/graphql/interaction-campaign.graphql +++ b/app-modules/interaction/graphql/interaction-campaign.graphql @@ -40,7 +40,7 @@ input InteractionCampaignQuery { } extend type Query { - "Get a specific interaction campaign by id." + "Get a specific interaction campaign by ID." interactionCampaign(id: UUID! @eq): InteractionCampaign @find @softDeletes diff --git a/app-modules/interaction/graphql/interaction-driver.graphql b/app-modules/interaction/graphql/interaction-driver.graphql index a4ca724b8a..93fa8fecf3 100644 --- a/app-modules/interaction/graphql/interaction-driver.graphql +++ b/app-modules/interaction/graphql/interaction-driver.graphql @@ -40,7 +40,7 @@ input InteractionDriverQuery { } extend type Query { - "Get a specific interaction driver by id." + "Get a specific interaction driver by ID." interactionDriver(id: UUID! @eq): InteractionDriver @find @softDeletes diff --git a/app-modules/interaction/graphql/interaction-outcome.graphql b/app-modules/interaction/graphql/interaction-outcome.graphql index 370c0dbaac..be2277d912 100644 --- a/app-modules/interaction/graphql/interaction-outcome.graphql +++ b/app-modules/interaction/graphql/interaction-outcome.graphql @@ -40,7 +40,7 @@ input InteractionOutcomeQuery { } extend type Query { - "Get a specific interaction outcome by id." + "Get a specific interaction outcome by ID." interactionOutcome(id: UUID! @eq): InteractionOutcome @find @softDeletes diff --git a/app-modules/interaction/graphql/interaction-relation.graphql b/app-modules/interaction/graphql/interaction-relation.graphql index d1cb975660..854b291a61 100644 --- a/app-modules/interaction/graphql/interaction-relation.graphql +++ b/app-modules/interaction/graphql/interaction-relation.graphql @@ -40,7 +40,7 @@ input InteractionRelationQuery { } extend type Query { - "Get a specific interaction relation by id." + "Get a specific interaction relation by ID." interactionRelation(id: UUID! @eq): InteractionRelation @find @softDeletes diff --git a/app-modules/interaction/graphql/interaction-status.graphql b/app-modules/interaction/graphql/interaction-status.graphql index 5a5e4295b3..2d50ca0f2a 100644 --- a/app-modules/interaction/graphql/interaction-status.graphql +++ b/app-modules/interaction/graphql/interaction-status.graphql @@ -3,22 +3,22 @@ type InteractionStatus "Unique primary key." id: UUID! - "The name of the interaction driver." + "The name of the interaction status." name: String! - "Interactions related to this interaction driver." + "Interactions related to this interaction status." interactions: [Interaction!] @hasMany - "The color of the interaction driver." + "The color of the interaction status." color: InteractionStatusColorOptions! - "The created date of the interaction driver." + "The created date of the interaction status." created_at: DateTime - "The updated date of the interaction driver." + "The updated date of the interaction status." updated_at: DateTime - "The deleted date of the interaction driver." + "The deleted date of the interaction status." deleted_at: DateTime } @@ -46,7 +46,7 @@ input InteractionStatusesQuery { } extend type Query { - "Get a specific interaction status by id." + "Get a specific interaction status by ID." interactionStatus(id: UUID! @eq): InteractionStatus @find @softDeletes diff --git a/app-modules/interaction/graphql/interaction-type.graphql b/app-modules/interaction/graphql/interaction-type.graphql index 05e3c8ea7c..a8efd4dd6e 100644 --- a/app-modules/interaction/graphql/interaction-type.graphql +++ b/app-modules/interaction/graphql/interaction-type.graphql @@ -40,7 +40,7 @@ input InteractionTypeQuery { } extend type Query { - "Get a specific interaction type by id." + "Get a specific interaction type by ID." interactionType(id: UUID! @eq): InteractionType @find @softDeletes diff --git a/app-modules/prospect/graphql/prospect-status.graphql b/app-modules/prospect/graphql/prospect-status.graphql new file mode 100644 index 0000000000..a142368163 --- /dev/null +++ b/app-modules/prospect/graphql/prospect-status.graphql @@ -0,0 +1,123 @@ +type ProspectStatus + @model(class: "AdvisingApp\\Prospect\\Models\\ProspectStatus") { + "Unique primary key." + id: UUID! + + "The name of the prospect driver." + name: String! + + "Prospects related to this prospect driver." + prospects: [Prospect!] @hasMany + + "The classification of the prospect status." + classification: ProspectStatusClassificationOptions! + + "The color of the prospect status." + color: ProspectStatusColorOptions! + + "The created date of the prospect driver." + created_at: DateTime + + "The updated date of the prospect driver." + updated_at: DateTime + + "The deleted date of the prospect driver." + deleted_at: DateTime +} + +input ProspectStatusesQuery { + "The unique primary key of the prospect status." + id: UUID + + "The name of the prospect status." + name: String + + "The prospects related to this prospect status." + prospects: ProspectsQuery + + "The classification of the prospect status." + classification: ProspectStatusClassificationOptions + + "The color of the prospect status." + color: ProspectStatusColorOptions + + "The created date of the prospect status." + created_at: DateTime + + "The updated date of the prospect status." + updated_at: DateTime + + "The deleted date of the prospect status." + deleted_at: DateTime +} + +type ProspectStatusQueries { + "Get a specific prospect status by ID." + find( + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) + ): ProspectStatus @find @softDeletes @canResolved(ability: "view") + + "List multiple prospects statuses." + list( + "Filter by the prospect statuses attributes and relations." + where: ProspectStatusesQuery @searchBy + ): [ProspectStatus!]! + @paginate + @softDeletes + @canModel(ability: "viewAny") +} + +extend type Query { + prospectStatus: ProspectStatusQueries! @namespaced +} + +input CreateProspectStatusInput { + "The name of the prospect status." + name: String! + @rules( + apply: [ + "required" + "string" + "max:255" + "unique:prospect_statuses,name" + ] + ) +} + +input UpdateProspectStatusInput { + "The name of the prospect status." + name: String + @rules(apply: ["string", "max:255", "unique:prospect_statuses,name"]) +} + +type ProspectStatusMutations { + "Create an prospect status." + create(input: CreateProspectStatusInput! @spread): ProspectStatus! + @create + @canModel(ability: "create") + + "Update an existing prospect status." + update( + "The identifier of the prospect status you would like to update." + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) + + "The fields you would like to update." + input: UpdateProspectStatusInput! @spread + ): ProspectStatus! @canFind(ability: "update", find: "id") @update + + "Delete an existing prospect status." + delete( + "The identifier of the prospect status you would like to delete." + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) + ): ProspectStatus @canFind(ability: "delete", find: "id") @delete +} + +extend type Mutation { + prospectStatus: ProspectStatusMutations! @namespaced +} diff --git a/app-modules/prospect/graphql/prospect.graphql b/app-modules/prospect/graphql/prospect.graphql index 71a6fcea30..b570475b6a 100644 --- a/app-modules/prospect/graphql/prospect.graphql +++ b/app-modules/prospect/graphql/prospect.graphql @@ -2,6 +2,9 @@ type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { "Unique primary key." id: UUID! + "The status of the prospect." + status: String! + "The first name of the prospect." first_name: String! @@ -46,6 +49,15 @@ type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { "The High School graduation year of the prospect." hs_grad_year: String + + "The created date of the prospect." + created_at: DateTime + + "The updated date of the prospect." + updated_at: DateTime + + "The deleted date of the prospect." + deleted_at: DateTime } input ProspectsQuery { From c3f72fb8bd5a8412e8886536aee7a91decdeca56 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 17:11:51 +0000 Subject: [PATCH 106/126] capitalise --- app-modules/interaction/graphql/interaction-campaign.graphql | 2 +- app-modules/interaction/graphql/interaction-driver.graphql | 2 +- app-modules/interaction/graphql/interaction-outcome.graphql | 2 +- app-modules/interaction/graphql/interaction-relation.graphql | 2 +- app-modules/interaction/graphql/interaction-status.graphql | 2 +- app-modules/interaction/graphql/interaction-type.graphql | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app-modules/interaction/graphql/interaction-campaign.graphql b/app-modules/interaction/graphql/interaction-campaign.graphql index 4aa97c5f3c..bfd9a7fe9b 100644 --- a/app-modules/interaction/graphql/interaction-campaign.graphql +++ b/app-modules/interaction/graphql/interaction-campaign.graphql @@ -40,7 +40,7 @@ input InteractionCampaignQuery { } type InteractionCampaignQueries { - "Get a specific interaction campaign by id." + "Get a specific interaction campaign by ID." find( id: UUID! @whereKey diff --git a/app-modules/interaction/graphql/interaction-driver.graphql b/app-modules/interaction/graphql/interaction-driver.graphql index 1f3de641cf..8ecc177346 100644 --- a/app-modules/interaction/graphql/interaction-driver.graphql +++ b/app-modules/interaction/graphql/interaction-driver.graphql @@ -40,7 +40,7 @@ input InteractionDriverQuery { } type InteractionDriverQueries { - "Get a specific interaction driver by id." + "Get a specific interaction driver by ID." find( id: UUID! @whereKey diff --git a/app-modules/interaction/graphql/interaction-outcome.graphql b/app-modules/interaction/graphql/interaction-outcome.graphql index b282249c09..6d35e3363b 100644 --- a/app-modules/interaction/graphql/interaction-outcome.graphql +++ b/app-modules/interaction/graphql/interaction-outcome.graphql @@ -40,7 +40,7 @@ input InteractionOutcomeQuery { } type InteractionOutcomeQueries { - "Get a specific interaction outcome by id." + "Get a specific interaction outcome by ID." find( id: UUID! @whereKey diff --git a/app-modules/interaction/graphql/interaction-relation.graphql b/app-modules/interaction/graphql/interaction-relation.graphql index 81bbe4a267..c5d911b707 100644 --- a/app-modules/interaction/graphql/interaction-relation.graphql +++ b/app-modules/interaction/graphql/interaction-relation.graphql @@ -40,7 +40,7 @@ input InteractionRelationQuery { } type InteractionRelationQueries { - "Get a specific interaction relation by id." + "Get a specific interaction relation by ID." find( id: UUID! @whereKey diff --git a/app-modules/interaction/graphql/interaction-status.graphql b/app-modules/interaction/graphql/interaction-status.graphql index 07b064e199..10eca1dda8 100644 --- a/app-modules/interaction/graphql/interaction-status.graphql +++ b/app-modules/interaction/graphql/interaction-status.graphql @@ -46,7 +46,7 @@ input InteractionStatusesQuery { } type InteractionStatusQueries { - "Get a specific interaction status by id." + "Get a specific interaction status by ID." find( id: UUID! @whereKey diff --git a/app-modules/interaction/graphql/interaction-type.graphql b/app-modules/interaction/graphql/interaction-type.graphql index 1a3b36e242..64efb73dea 100644 --- a/app-modules/interaction/graphql/interaction-type.graphql +++ b/app-modules/interaction/graphql/interaction-type.graphql @@ -40,7 +40,7 @@ input InteractionTypeQuery { } type InteractionTypeQueries { - "Get a specific interaction type by id." + "Get a specific interaction type by ID." find( id: UUID! @whereKey From 834253d27f30816bdf277b8fea8546047d9e01ff Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 12:40:39 -0500 Subject: [PATCH 107/126] Add support for database notifications. --- .../Actions/EngagementSmsChannelDelivery.php | 2 - .../EngagementEmailSentNotification.php | 24 ++++----- .../src/Actions/StatusCallback.php | 3 ++ .../src/Actions/CreateOutboundDeliverable.php | 5 +- .../DatabaseChannelResultData.php | 49 +++++++++++++++++ .../src/Listeners/HandleNotificationSent.php | 6 ++- .../src/Notifications/BaseNotification.php | 54 +++++++++---------- .../Channels/DatabaseChannel.php | 40 ++++++++++++++ .../src/Notifications/Channels/SmsChannel.php | 2 +- .../Concerns/DatabaseChannelTrait.php | 15 ++++++ .../Concerns/EmailChannelTrait.php | 5 +- .../Concerns/SmsChannelTrait.php | 5 +- .../Notifications/DatabaseNotification.php | 42 +++++++++++++++ .../src/Notifications/EmailNotification.php | 6 --- .../src/Notifications/SmsNotification.php | 6 --- .../Providers/NotificationServiceProvider.php | 9 +--- 16 files changed, 203 insertions(+), 70 deletions(-) create mode 100644 app-modules/notification/src/DataTransferObjects/DatabaseChannelResultData.php create mode 100644 app-modules/notification/src/Notifications/Channels/DatabaseChannel.php create mode 100644 app-modules/notification/src/Notifications/Concerns/DatabaseChannelTrait.php create mode 100644 app-modules/notification/src/Notifications/DatabaseNotification.php diff --git a/app-modules/engagement/src/Actions/EngagementSmsChannelDelivery.php b/app-modules/engagement/src/Actions/EngagementSmsChannelDelivery.php index 924ab1d226..7109d8b2a9 100644 --- a/app-modules/engagement/src/Actions/EngagementSmsChannelDelivery.php +++ b/app-modules/engagement/src/Actions/EngagementSmsChannelDelivery.php @@ -38,8 +38,6 @@ use AdvisingApp\Engagement\Notifications\EngagementSmsNotification; -// TODO We can probably do away with these classes altogether and just queue up the -// notification from where these are currently sent. class EngagementSmsChannelDelivery extends QueuedEngagementDelivery { public function deliver(): void diff --git a/app-modules/engagement/src/Notifications/EngagementEmailSentNotification.php b/app-modules/engagement/src/Notifications/EngagementEmailSentNotification.php index 5c9186ab9c..f94719d571 100644 --- a/app-modules/engagement/src/Notifications/EngagementEmailSentNotification.php +++ b/app-modules/engagement/src/Notifications/EngagementEmailSentNotification.php @@ -37,36 +37,34 @@ namespace AdvisingApp\Engagement\Notifications; use App\Models\User; -use Illuminate\Bus\Queueable; use App\Models\NotificationSetting; -use Illuminate\Notifications\Notification; -use Illuminate\Contracts\Queue\ShouldQueue; use AdvisingApp\Engagement\Models\Engagement; +use AdvisingApp\Notification\Notifications\BaseNotification; +use AdvisingApp\Notification\Notifications\EmailNotification; +use AdvisingApp\Notification\Notifications\DatabaseNotification; use AdvisingApp\Notification\Notifications\Messages\MailMessage; use Filament\Notifications\Notification as FilamentNotification; +use AdvisingApp\Notification\Notifications\Concerns\EmailChannelTrait; +use AdvisingApp\Notification\Notifications\Concerns\DatabaseChannelTrait; -class EngagementEmailSentNotification extends Notification implements ShouldQueue +class EngagementEmailSentNotification extends BaseNotification implements EmailNotification, DatabaseNotification { - use Queueable; + use EmailChannelTrait; + use DatabaseChannelTrait; public function __construct( public Engagement $engagement ) {} - public function via(User $notifiable): array - { - return ['mail', 'database']; - } - - public function toMail(User $notifiable): MailMessage + public function toEmail(object $notifiable): MailMessage { return MailMessage::make() ->settings($this->resolveNotificationSetting($notifiable)) - ->subject('Your Engagement Email has successfully been delivered.') + ->subject('Your Engagement email has successfully been delivered.') ->line("Your engagement was successfully delivered to {$this->engagement->recipient->display_name}."); } - public function toDatabase(User $notifiable): array + public function toDatabase(object $notifiable): array { return FilamentNotification::make() ->success() diff --git a/app-modules/integration-twilio/src/Actions/StatusCallback.php b/app-modules/integration-twilio/src/Actions/StatusCallback.php index dd9c92b820..4968fd4906 100644 --- a/app-modules/integration-twilio/src/Actions/StatusCallback.php +++ b/app-modules/integration-twilio/src/Actions/StatusCallback.php @@ -58,6 +58,7 @@ public function __construct( public function handle(): void { // TODO Update this to be the OutboundDeliverable model and hand off functionality to the "related" model if applicable + // https://canyongbs.atlassian.net/browse/ADVAPP-111 $deliverable = EngagementDeliverable::where('external_reference_id', $this->data['MessageSid'])->first(); if (is_null($deliverable)) { @@ -68,11 +69,13 @@ public function handle(): void // TODO We should implement some sort of process that checks to see if a deliverable has been updated to the "delivered" or "undelivered" // status after a certain period of time. This is to handle an edge case where the webhook is not received for some reason, and in this // situation we can simply poll Twilio for the data related to this deliverable. It can be a simple process implemented through the Kernel + // https://canyongbs.atlassian.net/browse/ADVAPP-112 // TODO In order to potentially reduce the amount of noise from jobs, we might want to introduce a "screener" that eliminates certain jobs based on their status // And only run the update if it's a status that we want to run some type of update against. For instance, we will receive callbacks for // queued, sending, sent, etc... but we don't actually want/need to do anything during these lifecycle hooks. We only really care about // delivered, undelivered, failed, etc... statuses. + // https://canyongbs.atlassian.net/browse/ADVAPP-113 UpdateEngagementDeliverableStatus::dispatch($deliverable, $this->data); } } diff --git a/app-modules/notification/src/Actions/CreateOutboundDeliverable.php b/app-modules/notification/src/Actions/CreateOutboundDeliverable.php index a169b1aa07..77c580f7a2 100644 --- a/app-modules/notification/src/Actions/CreateOutboundDeliverable.php +++ b/app-modules/notification/src/Actions/CreateOutboundDeliverable.php @@ -42,6 +42,7 @@ use AdvisingApp\Notification\Notifications\BaseNotification; use AdvisingApp\Notification\Notifications\Channels\SmsChannel; use AdvisingApp\Notification\Notifications\Channels\EmailChannel; +use AdvisingApp\Notification\Notifications\Channels\DatabaseChannel; class CreateOutboundDeliverable { @@ -50,14 +51,14 @@ public function handle(BaseNotification $notification, object $notifiable, strin $channel = match ($channel) { SmsChannel::class => NotificationChannel::Sms, EmailChannel::class => NotificationChannel::Email, - // DatabaseChannel::class => $deliverable->channel = NotificationChannel::Database, + DatabaseChannel::class => NotificationChannel::Database, default => throw new Exception('Invalid notification channel.'), }; $content = match ($channel) { NotificationChannel::Sms => $notification->toSms($notifiable)->toArray(), NotificationChannel::Email => $notification->toMail($notifiable)->toArray(), - // NotificationChannel::Database => $notification->toDatabase($notifiable)->toArray(), + NotificationChannel::Database => $notification->toDatabase($notifiable), default => throw new Exception('Invalid notification channel.'), }; diff --git a/app-modules/notification/src/DataTransferObjects/DatabaseChannelResultData.php b/app-modules/notification/src/DataTransferObjects/DatabaseChannelResultData.php new file mode 100644 index 0000000000..d02024a47b --- /dev/null +++ b/app-modules/notification/src/DataTransferObjects/DatabaseChannelResultData.php @@ -0,0 +1,49 @@ + + + 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\Notification\DataTransferObjects; + +use Twilio\Rest\Api\V2010\Account\MessageInstance; + +class DatabaseChannelResultData extends NotificationResultData +{ + public function __construct( + public bool $success, + // TODO Is there something we can add here? + // public ?MessageInstance $message = null, + public ?string $error = null, + ) {} +} diff --git a/app-modules/notification/src/Listeners/HandleNotificationSent.php b/app-modules/notification/src/Listeners/HandleNotificationSent.php index d4c66ca87a..0f99e7d636 100644 --- a/app-modules/notification/src/Listeners/HandleNotificationSent.php +++ b/app-modules/notification/src/Listeners/HandleNotificationSent.php @@ -45,8 +45,10 @@ class HandleNotificationSent implements ShouldQueue { public function handle(NotificationSent $event): void { - match ($event->notification) { - EngagementEmailNotification::class => HandleEngagementEmailNotificationSent::dispatch($event), + $notification = $event->notification; + + match (true) { + $notification instanceof EngagementEmailNotification => HandleEngagementEmailNotificationSent::dispatch($event), default => null, }; } diff --git a/app-modules/notification/src/Notifications/BaseNotification.php b/app-modules/notification/src/Notifications/BaseNotification.php index c313a5b9e8..092574fd47 100644 --- a/app-modules/notification/src/Notifications/BaseNotification.php +++ b/app-modules/notification/src/Notifications/BaseNotification.php @@ -36,52 +36,53 @@ namespace AdvisingApp\Notification\Notifications; -use ReflectionClass; -use ReflectionProperty; +use Illuminate\Support\Str; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use AdvisingApp\Notification\Models\OutboundDeliverable; -use AdvisingApp\Notification\Enums\NotificationDeliveryStatus; use AdvisingApp\Notification\Actions\CreateOutboundDeliverable; use AdvisingApp\Notification\Notifications\Channels\SmsChannel; use AdvisingApp\Notification\Notifications\Channels\EmailChannel; use AdvisingApp\Notification\Notifications\Concerns\ChannelTrait; +use AdvisingApp\Notification\Notifications\Channels\DatabaseChannel; use AdvisingApp\Notification\DataTransferObjects\SmsChannelResultData; use AdvisingApp\Notification\DataTransferObjects\EmailChannelResultData; use AdvisingApp\Notification\DataTransferObjects\NotificationResultData; +use AdvisingApp\Notification\DataTransferObjects\DatabaseChannelResultData; abstract class BaseNotification extends Notification implements ShouldQueue { use Queueable; use Dispatchable; - public array $channels = []; - protected array $metadata = []; public function via(object $notifiable): array { - $reflectionClass = new ReflectionClass($this); + $traits = collect(class_uses_recursive(static::class)); - return collect($reflectionClass->getTraits()) - ->filter(function (ReflectionClass $trait) { - return collect($trait->getTraits()) - ->contains(fn (ReflectionClass $nestedTrait) => $nestedTrait->getName() === ChannelTrait::class); + $channels = $traits + ->filter(function ($traitName) { + return in_array(ChannelTrait::class, class_uses($traitName)); }) + ->map(function ($traitName) { + $channelName = Str::before(class_basename($traitName), 'ChannelTrait'); + $methodName = 'get' . Str::studly($channelName) . 'Channel'; + + if (method_exists($traitName, $methodName)) { + return forward_static_call([$traitName, $methodName]); + } - ->map(function (ReflectionClass $trait) { - return collect($trait->getProperties()) - ->filter(function (ReflectionProperty $property) { - return $property->getName() === 'channel'; - }) - ->map(function (ReflectionProperty $property) { - return $property->getValue(); - }); + return null; }) - ->flatten() + ->filter() + ->unique() + ->values() ->toArray(); + + return $channels; } public function beforeSend(object $notifiable, string $channel): OutboundDeliverable|false @@ -94,16 +95,10 @@ public function beforeSend(object $notifiable, string $channel): OutboundDeliver $this->beforeSendHook($notifiable, $deliverable, $channel); - // TODO Implement some kind of check against the rate limits for a particular channel - // Reminder that we'll need to consider "must send" notifications, which may be those sent - // To a sending party letting them know they've hit a quota or been throttled, etc... - // if (! $this->checkRateLimitsFor($channel)) { - // $deliverable->update([ - // 'delivery_status' => NotificationDeliveryStatus::RateLimited, - // ]); - - // return false; - // } + // TODO Check License Limits / update deliverable status / etc... + // This will be completed in: + // https://canyongbs.atlassian.net/browse/ADVAPP-1 + // https://canyongbs.atlassian.net/browse/ADVAPP-2 return $deliverable; } @@ -113,6 +108,7 @@ public function afterSend(object $notifiable, OutboundDeliverable $deliverable, match (true) { $result instanceof SmsChannelResultData => SmsChannel::afterSending($notifiable, $deliverable, $result), $result instanceof EmailChannelResultData => EmailChannel::afterSending($notifiable, $deliverable, $result), + $result instanceof DatabaseChannelResultData => DatabaseChannel::afterSending($notifiable, $deliverable, $result), default => throw new \Exception('Invalid notification result data.'), }; diff --git a/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php b/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php new file mode 100644 index 0000000000..91b723aa37 --- /dev/null +++ b/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php @@ -0,0 +1,40 @@ +beforeSend($notifiable, DatabaseChannel::class); + + if ($deliverable === false) { + // Do anything else we need to to notify sending party that notification was not sent + return; + } + + $result = $this->handle($notifiable, $notification); + + $notification->afterSend($notifiable, $deliverable, $result); + } + + public function handle(object $notifiable, BaseNotification $notification): NotificationResultData + { + parent::send($notifiable, $notification); + + return new DatabaseChannelResultData( + success: true, + ); + } + + public static function afterSending(object $notifiable, OutboundDeliverable $deliverable, EmailChannelResultData $result): void {} +} diff --git a/app-modules/notification/src/Notifications/Channels/SmsChannel.php b/app-modules/notification/src/Notifications/Channels/SmsChannel.php index 04fb07b4e2..1f66dbcc24 100644 --- a/app-modules/notification/src/Notifications/Channels/SmsChannel.php +++ b/app-modules/notification/src/Notifications/Channels/SmsChannel.php @@ -30,8 +30,8 @@ public function send(object $notifiable, BaseNotification $notification): void public function handle(object $notifiable, BaseNotification $notification): NotificationResultData { - // TODO Figure out if we can remove this easily/nicely... /** @var SmsNotification $notification */ + /** @var TwilioMessage $twilioMessage */ $twilioMessage = $notification->toSms($notifiable); diff --git a/app-modules/notification/src/Notifications/Concerns/DatabaseChannelTrait.php b/app-modules/notification/src/Notifications/Concerns/DatabaseChannelTrait.php new file mode 100644 index 0000000000..5207f8e960 --- /dev/null +++ b/app-modules/notification/src/Notifications/Concerns/DatabaseChannelTrait.php @@ -0,0 +1,15 @@ + + + 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\Notification\Notifications; + +interface DatabaseNotification +{ + public function toDatabase(object $notifiable): array; +} diff --git a/app-modules/notification/src/Notifications/EmailNotification.php b/app-modules/notification/src/Notifications/EmailNotification.php index ef3a2224be..b500f7b14e 100644 --- a/app-modules/notification/src/Notifications/EmailNotification.php +++ b/app-modules/notification/src/Notifications/EmailNotification.php @@ -36,15 +36,9 @@ namespace AdvisingApp\Notification\Notifications; -use AdvisingApp\Notification\Models\OutboundDeliverable; use AdvisingApp\Notification\Notifications\Messages\MailMessage; -use AdvisingApp\Notification\DataTransferObjects\NotificationResultData; interface EmailNotification { public function toEmail(object $notifiable): MailMessage; - - public function beforeSend(object $notifiable, string $channel): OutboundDeliverable|false; - - public function afterSend(object $notifiable, OutboundDeliverable $deliverable, NotificationResultData $result): void; } diff --git a/app-modules/notification/src/Notifications/SmsNotification.php b/app-modules/notification/src/Notifications/SmsNotification.php index 81100914c4..3bd996a3f3 100644 --- a/app-modules/notification/src/Notifications/SmsNotification.php +++ b/app-modules/notification/src/Notifications/SmsNotification.php @@ -36,15 +36,9 @@ namespace AdvisingApp\Notification\Notifications; -use AdvisingApp\Notification\Models\OutboundDeliverable; use AdvisingApp\Notification\Notifications\Messages\TwilioMessage; -use AdvisingApp\Notification\DataTransferObjects\NotificationResultData; interface SmsNotification { public function toSms(object $notifiable): TwilioMessage; - - public function beforeSend(object $notifiable, string $channel): OutboundDeliverable|false; - - public function afterSend(object $notifiable, OutboundDeliverable $deliverable, NotificationResultData $result): void; } diff --git a/app-modules/notification/src/Providers/NotificationServiceProvider.php b/app-modules/notification/src/Providers/NotificationServiceProvider.php index 00e341c73c..b90d180391 100644 --- a/app-modules/notification/src/Providers/NotificationServiceProvider.php +++ b/app-modules/notification/src/Providers/NotificationServiceProvider.php @@ -91,13 +91,8 @@ protected function registerEvents(): void HandleNotificationFailed::class ); - // TODO We should probably introduce a listener for the NotificationSending class as well - // The corresponding listener could effectively serve as a middleware to prevent sending in situations - // Where the notification should not be sent (for instance, quota is reached, recipient declined to receive, etc.) - // Event::listen( - // NotificationSending::class, - // ShouldSendNotification::class - // ) + // TODO Listen to the MessageSent event in order to update email statuses + // as best as we can without the SES information... // TODO Should subscriptions exist in their own module??? Event::listen( From 71fcabbaf9e365a5d2eddeb786862855545debf6 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 12:47:05 -0500 Subject: [PATCH 108/126] Add logic post database send. --- .../src/Notifications/Channels/DatabaseChannel.php | 10 ++++++++-- .../src/Notifications/Channels/EmailChannel.php | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php b/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php index 91b723aa37..b586b867e9 100644 --- a/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php +++ b/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php @@ -5,7 +5,6 @@ use Illuminate\Notifications\Notification; use AdvisingApp\Notification\Models\OutboundDeliverable; use AdvisingApp\Notification\Notifications\BaseNotification; -use AdvisingApp\Notification\DataTransferObjects\EmailChannelResultData; use AdvisingApp\Notification\DataTransferObjects\NotificationResultData; use AdvisingApp\Notification\DataTransferObjects\DatabaseChannelResultData; use Illuminate\Notifications\Channels\DatabaseChannel as BaseDatabaseChannel; @@ -36,5 +35,12 @@ public function handle(object $notifiable, BaseNotification $notification): Noti ); } - public static function afterSending(object $notifiable, OutboundDeliverable $deliverable, EmailChannelResultData $result): void {} + public static function afterSending(object $notifiable, OutboundDeliverable $deliverable, DatabaseChannelResultData $result): void + { + if ($result->success) { + $deliverable->markDeliverySuccessful(); + } else { + $deliverable->markDeliveryFailed('Failed to send notification'); + } + } } diff --git a/app-modules/notification/src/Notifications/Channels/EmailChannel.php b/app-modules/notification/src/Notifications/Channels/EmailChannel.php index 2f8a75c49a..92b4a630a4 100644 --- a/app-modules/notification/src/Notifications/Channels/EmailChannel.php +++ b/app-modules/notification/src/Notifications/Channels/EmailChannel.php @@ -41,5 +41,9 @@ public function handle(object $notifiable, BaseNotification $notification): Noti return $result; } - public static function afterSending(object $notifiable, OutboundDeliverable $deliverable, EmailChannelResultData $result): void {} + public static function afterSending(object $notifiable, OutboundDeliverable $deliverable, EmailChannelResultData $result): void + { + // TODO Do we want to add any updating of the deliverable here? + // Or do we want to leave it all for SES events? + } } From 39685a88fc2bc28e9407c157be45b445c2ebb0b6 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 12:50:57 -0500 Subject: [PATCH 109/126] Update lock. --- composer.lock | 752 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 693 insertions(+), 59 deletions(-) diff --git a/composer.lock b/composer.lock index c3d6a68876..9d7584f3c0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,571 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "06a9456c89725f4b69a04f6371e0ca65", + "content-hash": "1eba33b3cd016e6d48f91a58a13e6a37", "packages": [ + { + "name": "amphp/amp", + "version": "v2.6.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-20T17:52:18+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "amphp/parallel", + "version": "v1.4.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/3aac213ba7858566fd83d38ccb85b91b2d652cb0", + "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.6.1", + "amphp/parser": "^1", + "amphp/process": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^1.0.1", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.1", + "phpunit/phpunit": "^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Context/functions.php", + "lib/Sync/functions.php", + "lib/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "support": { + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v1.4.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2023-03-23T08:04:23+00:00" + }, + { + "name": "amphp/parallel-functions", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel-functions.git", + "reference": "04e92fcacfc921a56dfe12c23b3265e62593a7cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel-functions/zipball/04e92fcacfc921a56dfe12c23b3265e62593a7cb", + "reference": "04e92fcacfc921a56dfe12c23b3265e62593a7cb", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.0.3", + "amphp/parallel": "^1.4", + "amphp/serialization": "^1.0", + "laravel/serializable-closure": "^1.0", + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "v2.x-dev", + "amphp/phpunit-util": "^2.0", + "phpunit/phpunit": "^9.5.11" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\ParallelFunctions\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Parallel processing made simple.", + "support": { + "issues": "https://github.com/amphp/parallel-functions/issues", + "source": "https://github.com/amphp/parallel-functions/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-03T19:32:41+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/ff1de4144726c5dad5fab97f66692ebe8de3e151", + "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-12-30T18:08:47+00:00" + }, + { + "name": "amphp/process", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/76e9495fd6818b43a20167cb11d8a67f7744ee0f", + "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.4", + "php": ">=7" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous process manager.", + "homepage": "https://github.com/amphp/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v1.1.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-07-06T23:50:12+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/sync", + "version": "v1.4.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/85ab06764f4f36d63b1356b466df6111cf4b89cf", + "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.1", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/ConcurrentIterator/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Mutex, Semaphore, and other synchronization tools for Amp.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v1.4.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-10-25T18:29:10+00:00" + }, { "name": "awcodes/filament-tiptap-editor", - "version": "v3.2.12", + "version": "v3.2.14", "source": { "type": "git", "url": "https://github.com/awcodes/filament-tiptap-editor.git", - "reference": "5d83fba2346577d9733c8ad89786bba49bf79016" + "reference": "60d5840516a862e693c7de86d2f69d5f4d3b85c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/awcodes/filament-tiptap-editor/zipball/5d83fba2346577d9733c8ad89786bba49bf79016", - "reference": "5d83fba2346577d9733c8ad89786bba49bf79016", + "url": "https://api.github.com/repos/awcodes/filament-tiptap-editor/zipball/60d5840516a862e693c7de86d2f69d5f4d3b85c1", + "reference": "60d5840516a862e693c7de86d2f69d5f4d3b85c1", "shasum": "" }, "require": { @@ -79,7 +630,7 @@ ], "support": { "issues": "https://github.com/awcodes/filament-tiptap-editor/issues", - "source": "https://github.com/awcodes/filament-tiptap-editor/tree/v3.2.12" + "source": "https://github.com/awcodes/filament-tiptap-editor/tree/v3.2.14" }, "funding": [ { @@ -87,7 +638,7 @@ "type": "github" } ], - "time": "2023-12-19T21:51:03+00:00" + "time": "2023-12-21T15:23:55+00:00" }, { "name": "aws/aws-crt-php", @@ -204,16 +755,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.294.3", + "version": "3.294.4", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "05761093c61ca7a02c1b5ae9be279bf69360e060" + "reference": "4f59bf50aa445fc3ec0b10648b205dd2465e9bec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/05761093c61ca7a02c1b5ae9be279bf69360e060", - "reference": "05761093c61ca7a02c1b5ae9be279bf69360e060", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4f59bf50aa445fc3ec0b10648b205dd2465e9bec", + "reference": "4f59bf50aa445fc3ec0b10648b205dd2465e9bec", "shasum": "" }, "require": { @@ -293,9 +844,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.294.3" + "source": "https://github.com/aws/aws-sdk-php/tree/3.294.4" }, - "time": "2023-12-19T19:07:14+00:00" + "time": "2023-12-20T19:21:19+00:00" }, { "name": "barryvdh/laravel-debugbar", @@ -1441,12 +1992,12 @@ } }, { - "name": "canyon-gbs/advising-app-notifications", + "name": "canyon-gbs/advising-app-notification", "version": "1.0", "dist": { "type": "path", - "url": "app-modules/notifications", - "reference": "7dbec344f693b6973f93539b31c53d03f5891f3e" + "url": "app-modules/notification", + "reference": "5062ea2b82f5998318e06e77dcb7836661b3a412" }, "require": { "filament/filament": "^3.0" @@ -1455,16 +2006,16 @@ "extra": { "laravel": { "providers": [ - "AdvisingApp\\Notifications\\Providers\\NotificationsServiceProvider" + "AdvisingApp\\Notification\\Providers\\NotificationServiceProvider" ] } }, "autoload": { "psr-4": { - "AdvisingApp\\Notifications\\": "src/", - "AdvisingApp\\Notifications\\Tests\\": "tests/", - "AdvisingApp\\Notifications\\Database\\Factories\\": "database/factories/", - "AdvisingApp\\Notifications\\Database\\Seeders\\": "database/seeders/" + "AdvisingApp\\Notification\\": "src/", + "AdvisingApp\\Notification\\Tests\\": "tests/", + "AdvisingApp\\Notification\\Database\\Factories\\": "database/factories/", + "AdvisingApp\\Notification\\Database\\Seeders\\": "database/seeders/" } }, "license": [ @@ -1586,10 +2137,10 @@ "dist": { "type": "path", "url": "app-modules/student-data-model", - "reference": "bae8ef0016aab67de5ed247f19915690e93cbd21" + "reference": "97787a2aedc5da23f7907add6646513bb8f39a97" }, "require": { - "canyon-gbs/advising-app-notifications": "^1.0", + "canyon-gbs/advising-app-notification": "*", "filament/filament": "^3.0" }, "type": "library", @@ -3795,7 +4346,7 @@ }, { "name": "filament/spatie-laravel-media-library-plugin", - "version": "v3.1.24", + "version": "v3.1.26", "source": { "type": "git", "url": "https://github.com/filamentphp/spatie-laravel-media-library-plugin.git", @@ -11712,28 +12263,30 @@ }, { "name": "spatie/laravel-data", - "version": "3.10.1", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-data.git", - "reference": "23dd20812d3ba829233081679b5bf0f97645d300" + "reference": "e9cb66136974b6a6e290d6d2e2d484bac1727537" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-data/zipball/23dd20812d3ba829233081679b5bf0f97645d300", - "reference": "23dd20812d3ba829233081679b5bf0f97645d300", + "url": "https://api.github.com/repos/spatie/laravel-data/zipball/e9cb66136974b6a6e290d6d2e2d484bac1727537", + "reference": "e9cb66136974b6a6e290d6d2e2d484bac1727537", "shasum": "" }, "require": { "illuminate/contracts": "^9.30|^10.0", "php": "^8.1", "phpdocumentor/type-resolver": "^1.5", - "spatie/laravel-package-tools": "^1.9.0" + "spatie/laravel-package-tools": "^1.9.0", + "spatie/php-structure-discoverer": "^2.0" }, "require-dev": { "fakerphp/faker": "^1.14", "friendsofphp/php-cs-fixer": "^3.0", "inertiajs/inertia-laravel": "^0.6.3", + "mockery/mockery": "^1.6", "nesbot/carbon": "^2.63", "nette/php-generator": "^3.5", "nunomaduro/larastan": "^2.0", @@ -11783,7 +12336,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-data/issues", - "source": "https://github.com/spatie/laravel-data/tree/3.10.1" + "source": "https://github.com/spatie/laravel-data/tree/3.11.0" }, "funding": [ { @@ -11791,7 +12344,7 @@ "type": "github" } ], - "time": "2023-12-04T14:59:58+00:00" + "time": "2023-12-21T12:31:34+00:00" }, { "name": "spatie/laravel-health", @@ -12222,6 +12775,87 @@ ], "time": "2023-12-04T11:38:23+00:00" }, + { + "name": "spatie/php-structure-discoverer", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/php-structure-discoverer.git", + "reference": "3e532f0952d37bb10cddd544800eb659499cda3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/php-structure-discoverer/zipball/3e532f0952d37bb10cddd544800eb659499cda3d", + "reference": "3e532f0952d37bb10cddd544800eb659499cda3d", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.6.2", + "amphp/parallel": "^1.4.1", + "amphp/parallel-functions": "^1.1", + "illuminate/collections": "^9.30|^10.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.4.3", + "symfony/finder": "^6.0|^7.0" + }, + "require-dev": { + "illuminate/console": "^9.30|^10.0", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^6.0", + "nunomaduro/larastan": "^2.0.1", + "orchestra/testbench": "^7.0|^8.0", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5", + "spatie/laravel-ray": "^1.26" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\StructureDiscoverer\\StructureDiscovererServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\StructureDiscoverer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ruben Van Assche", + "email": "ruben@spatie.be", + "role": "Developer" + } + ], + "description": "Automatically discover structures within your PHP application", + "homepage": "https://github.com/spatie/php-structure-discoverer", + "keywords": [ + "discover", + "laravel", + "php", + "php-structure-discoverer" + ], + "support": { + "issues": "https://github.com/spatie/php-structure-discoverer/issues", + "source": "https://github.com/spatie/php-structure-discoverer/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/LaravelAutoDiscoverer", + "type": "github" + } + ], + "time": "2023-12-21T12:04:07+00:00" + }, { "name": "spatie/regex", "version": "3.1.1", @@ -17955,23 +18589,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.10", + "version": "10.1.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "599109c8ca6bae97b23482d557d2874c25a65e59" + "reference": "78c3b7625965c2513ee96569a4dbb62601784145" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/599109c8ca6bae97b23482d557d2874c25a65e59", - "reference": "599109c8ca6bae97b23482d557d2874c25a65e59", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145", + "reference": "78c3b7625965c2513ee96569a4dbb62601784145", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1", "phpunit/php-file-iterator": "^4.0", "phpunit/php-text-template": "^3.0", @@ -18021,7 +18655,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.10" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11" }, "funding": [ { @@ -18029,7 +18663,7 @@ "type": "github" } ], - "time": "2023-12-11T06:28:43+00:00" + "time": "2023-12-21T15:38:30+00:00" }, { "name": "phpunit/php-file-iterator", @@ -18730,20 +19364,20 @@ }, { "name": "sebastian/complexity", - "version": "3.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -18752,7 +19386,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -18776,7 +19410,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -18784,7 +19418,7 @@ "type": "github" } ], - "time": "2023-09-28T11:50:59+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", @@ -19059,20 +19693,20 @@ }, { "name": "sebastian/lines-of-code", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -19105,7 +19739,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -19113,7 +19747,7 @@ "type": "github" } ], - "time": "2023-08-31T09:25:50+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", @@ -19616,16 +20250,16 @@ }, { "name": "spatie/laravel-ignition", - "version": "2.3.2", + "version": "2.3.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "4800661a195e15783477d99f7f8f669a49793996" + "reference": "66499cd3c858642ded56dafb8fa0352057ca20dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/4800661a195e15783477d99f7f8f669a49793996", - "reference": "4800661a195e15783477d99f7f8f669a49793996", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/66499cd3c858642ded56dafb8fa0352057ca20dd", + "reference": "66499cd3c858642ded56dafb8fa0352057ca20dd", "shasum": "" }, "require": { @@ -19704,7 +20338,7 @@ "type": "github" } ], - "time": "2023-12-15T13:44:49+00:00" + "time": "2023-12-21T09:43:05+00:00" }, { "name": "spatie/laravel-ray", @@ -20604,5 +21238,5 @@ "ext-pdo": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } From d82c8c467ec52399dcd3a7d1b651dc2ddf5ecaa5 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 12:58:15 -0500 Subject: [PATCH 110/126] Ensure metadata is set. --- .../src/Listeners/AddSesMessageTagsToEmailHeaders.php | 2 +- .../notification/src/Notifications/Channels/EmailChannel.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app-modules/integration-aws-ses-event-handling/src/Listeners/AddSesMessageTagsToEmailHeaders.php b/app-modules/integration-aws-ses-event-handling/src/Listeners/AddSesMessageTagsToEmailHeaders.php index 4a421e4037..0836d781f7 100644 --- a/app-modules/integration-aws-ses-event-handling/src/Listeners/AddSesMessageTagsToEmailHeaders.php +++ b/app-modules/integration-aws-ses-event-handling/src/Listeners/AddSesMessageTagsToEmailHeaders.php @@ -42,7 +42,7 @@ class AddSesMessageTagsToEmailHeaders { public function handle(MessageSending $event): void { - if ($event->message->metadata) { + if (property_exists($event->message, 'metadata') && is_array($event->message->metadata)) { foreach ($event->message->metadata as $key => $value) { $event->message->getHeaders()->addTextHeader('X-SES-MESSAGE-TAGS', $key . '=' . $value); } diff --git a/app-modules/notification/src/Notifications/Channels/EmailChannel.php b/app-modules/notification/src/Notifications/Channels/EmailChannel.php index 92b4a630a4..087beebb1d 100644 --- a/app-modules/notification/src/Notifications/Channels/EmailChannel.php +++ b/app-modules/notification/src/Notifications/Channels/EmailChannel.php @@ -44,6 +44,6 @@ public function handle(object $notifiable, BaseNotification $notification): Noti public static function afterSending(object $notifiable, OutboundDeliverable $deliverable, EmailChannelResultData $result): void { // TODO Do we want to add any updating of the deliverable here? - // Or do we want to leave it all for SES events? + // Or do we want to leave it all for SES events handled via webhook? } } From bca6aa49bcaa0f4eb548d256a2adcd29f2b37ea6 Mon Sep 17 00:00:00 2001 From: dgoetzit Date: Thu, 21 Dec 2023 18:02:46 +0000 Subject: [PATCH 111/126] chore: fix enforcement of copyright on all files --- _ide_helper_models.php | 34 +++++++++++++++++++ .../factories/OutboundDeliverableFactory.php | 34 +++++++++++++++++++ ...721_create_outbound_deliverables_table.php | 34 +++++++++++++++++++ .../src/Models/OutboundDeliverable.php | 34 +++++++++++++++++++ .../Channels/DatabaseChannel.php | 34 +++++++++++++++++++ .../Notifications/Channels/EmailChannel.php | 34 +++++++++++++++++++ .../src/Notifications/Channels/SmsChannel.php | 34 +++++++++++++++++++ .../Notifications/Concerns/ChannelTrait.php | 34 +++++++++++++++++++ .../Concerns/DatabaseChannelTrait.php | 34 +++++++++++++++++++ .../Concerns/EmailChannelTrait.php | 34 +++++++++++++++++++ .../Concerns/SmsChannelTrait.php | 34 +++++++++++++++++++ 11 files changed, 374 insertions(+) diff --git a/_ide_helper_models.php b/_ide_helper_models.php index bee16e70c2..8414883ba6 100644 --- a/_ide_helper_models.php +++ b/_ide_helper_models.php @@ -1,5 +1,39 @@ + + 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. + + +*/ + // @formatter:off /** * A helper file for your Eloquent Models diff --git a/app-modules/notification/database/factories/OutboundDeliverableFactory.php b/app-modules/notification/database/factories/OutboundDeliverableFactory.php index dee9627fb9..e077767637 100644 --- a/app-modules/notification/database/factories/OutboundDeliverableFactory.php +++ b/app-modules/notification/database/factories/OutboundDeliverableFactory.php @@ -1,5 +1,39 @@ + + 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\Notification\Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; diff --git a/app-modules/notification/database/migrations/2023_12_20_160721_create_outbound_deliverables_table.php b/app-modules/notification/database/migrations/2023_12_20_160721_create_outbound_deliverables_table.php index e3c74ca3e2..29211fe3d3 100644 --- a/app-modules/notification/database/migrations/2023_12_20_160721_create_outbound_deliverables_table.php +++ b/app-modules/notification/database/migrations/2023_12_20_160721_create_outbound_deliverables_table.php @@ -1,5 +1,39 @@ + + 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; diff --git a/app-modules/notification/src/Models/OutboundDeliverable.php b/app-modules/notification/src/Models/OutboundDeliverable.php index e88f231fd6..54fa380a60 100644 --- a/app-modules/notification/src/Models/OutboundDeliverable.php +++ b/app-modules/notification/src/Models/OutboundDeliverable.php @@ -1,5 +1,39 @@ + + 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\Notification\Models; use App\Models\BaseModel; diff --git a/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php b/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php index b586b867e9..d0adeeda05 100644 --- a/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php +++ b/app-modules/notification/src/Notifications/Channels/DatabaseChannel.php @@ -1,5 +1,39 @@ + + 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\Notification\Notifications\Channels; use Illuminate\Notifications\Notification; diff --git a/app-modules/notification/src/Notifications/Channels/EmailChannel.php b/app-modules/notification/src/Notifications/Channels/EmailChannel.php index 087beebb1d..61c5f72363 100644 --- a/app-modules/notification/src/Notifications/Channels/EmailChannel.php +++ b/app-modules/notification/src/Notifications/Channels/EmailChannel.php @@ -1,5 +1,39 @@ + + 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\Notification\Notifications\Channels; use Illuminate\Notifications\Notification; diff --git a/app-modules/notification/src/Notifications/Channels/SmsChannel.php b/app-modules/notification/src/Notifications/Channels/SmsChannel.php index 1f66dbcc24..51944f710e 100644 --- a/app-modules/notification/src/Notifications/Channels/SmsChannel.php +++ b/app-modules/notification/src/Notifications/Channels/SmsChannel.php @@ -1,5 +1,39 @@ + + 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\Notification\Notifications\Channels; use Twilio\Rest\Client; diff --git a/app-modules/notification/src/Notifications/Concerns/ChannelTrait.php b/app-modules/notification/src/Notifications/Concerns/ChannelTrait.php index 9ce1ec69a8..0aab8d9127 100644 --- a/app-modules/notification/src/Notifications/Concerns/ChannelTrait.php +++ b/app-modules/notification/src/Notifications/Concerns/ChannelTrait.php @@ -1,5 +1,39 @@ + + 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\Notification\Notifications\Concerns; trait ChannelTrait {} diff --git a/app-modules/notification/src/Notifications/Concerns/DatabaseChannelTrait.php b/app-modules/notification/src/Notifications/Concerns/DatabaseChannelTrait.php index 5207f8e960..88fd874397 100644 --- a/app-modules/notification/src/Notifications/Concerns/DatabaseChannelTrait.php +++ b/app-modules/notification/src/Notifications/Concerns/DatabaseChannelTrait.php @@ -1,5 +1,39 @@ + + 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\Notification\Notifications\Concerns; use AdvisingApp\Notification\Notifications\Channels\DatabaseChannel; diff --git a/app-modules/notification/src/Notifications/Concerns/EmailChannelTrait.php b/app-modules/notification/src/Notifications/Concerns/EmailChannelTrait.php index 85d26e3c7a..9dbfb3a12d 100644 --- a/app-modules/notification/src/Notifications/Concerns/EmailChannelTrait.php +++ b/app-modules/notification/src/Notifications/Concerns/EmailChannelTrait.php @@ -1,5 +1,39 @@ + + 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\Notification\Notifications\Concerns; use AdvisingApp\Notification\Notifications\Messages\MailMessage; diff --git a/app-modules/notification/src/Notifications/Concerns/SmsChannelTrait.php b/app-modules/notification/src/Notifications/Concerns/SmsChannelTrait.php index 0b1d9d8239..3fe3f0c260 100644 --- a/app-modules/notification/src/Notifications/Concerns/SmsChannelTrait.php +++ b/app-modules/notification/src/Notifications/Concerns/SmsChannelTrait.php @@ -1,5 +1,39 @@ + + 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\Notification\Notifications\Concerns; use AdvisingApp\Notification\Notifications\Channels\SmsChannel; From 33ac098083232e4c83bbdf6749bfc435a47f2612 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 21 Dec 2023 13:21:35 -0500 Subject: [PATCH 112/126] Update to include a specific SQS connection Signed-off-by: Kevin Ullyott --- .env.example | 3 +++ config/queue.php | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 03a1520542..8d133b2ce1 100644 --- a/.env.example +++ b/.env.example @@ -56,6 +56,9 @@ AWS_USE_PATH_STYLE_ENDPOINT=true AWS_URL=http://localhost:9000/local +AWS_SQS_ACCESS_KEY_ID= +AWS_SQS_SECRET_ACCESS_KEY= + PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= diff --git a/config/queue.php b/config/queue.php index f330fb2847..3babb008c4 100644 --- a/config/queue.php +++ b/config/queue.php @@ -85,8 +85,8 @@ 'sqs' => [ 'driver' => 'sqs', - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'key' => env('AWS_SQS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SQS_SECRET_ACCESS_KEY'), 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 'queue' => env('SQS_QUEUE', 'default'), 'suffix' => env('SQS_SUFFIX'), From ac0b1fe4d1acd5ecab14be2812741e0089a9d0b8 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 18:29:24 +0000 Subject: [PATCH 113/126] finish prospects --- app-modules/alert/graphql/alert.graphql | 4 +- .../care-team/graphql/care-team.graphql | 4 +- .../graphql/interaction-status.graphql | 18 +- .../interaction/graphql/interaction.graphql | 96 ++++----- .../graphql/subscription.graphql | 4 +- .../prospect/graphql/prospect-source.graphql | 108 ++++++++++ .../prospect/graphql/prospect-status.graphql | 62 +++--- app-modules/prospect/graphql/prospect.graphql | 184 ++++++++++++++++-- app-modules/prospect/src/Models/Prospect.php | 1 + .../src/Providers/ProspectServiceProvider.php | 4 + graphql/schema.graphql | 2 + 11 files changed, 389 insertions(+), 98 deletions(-) create mode 100644 app-modules/prospect/graphql/prospect-source.graphql diff --git a/app-modules/alert/graphql/alert.graphql b/app-modules/alert/graphql/alert.graphql index 50e37bf00a..59bbe2995d 100644 --- a/app-modules/alert/graphql/alert.graphql +++ b/app-modules/alert/graphql/alert.graphql @@ -28,8 +28,8 @@ type Alert @model(class: "AdvisingApp\\Alert\\Models\\Alert") { } input AlertConcernsQuery { - student: StudentQuery - prospect: ProspectQuery + student: StudentsQuery + prospect: ProspectsQuery } input AlertsQuery { diff --git a/app-modules/care-team/graphql/care-team.graphql b/app-modules/care-team/graphql/care-team.graphql index 604c2e8a14..95ca0b5118 100644 --- a/app-modules/care-team/graphql/care-team.graphql +++ b/app-modules/care-team/graphql/care-team.graphql @@ -16,8 +16,8 @@ type CareTeam @model(class: "AdvisingApp\\CareTeam\\Models\\CareTeam") { } input CareTeamEducatablesQuery { - student: StudentQuery - prospect: ProspectQuery + student: StudentsQuery + prospect: ProspectsQuery } input CareTeamsQuery { diff --git a/app-modules/interaction/graphql/interaction-status.graphql b/app-modules/interaction/graphql/interaction-status.graphql index 10eca1dda8..52edfa9638 100644 --- a/app-modules/interaction/graphql/interaction-status.graphql +++ b/app-modules/interaction/graphql/interaction-status.graphql @@ -3,22 +3,22 @@ type InteractionStatus "Unique primary key." id: UUID! - "The name of the interaction driver." + "The name of the interaction status." name: String! - "Interactions related to this interaction driver." + "Interactions related to this interaction status." interactions: [Interaction!] @hasMany - "The color of the interaction driver." + "The color of the interaction status." color: InteractionStatusColorOptions! - "The created date of the interaction driver." + "The created date of the interaction status." created_at: DateTime - "The updated date of the interaction driver." + "The updated date of the interaction status." updated_at: DateTime - "The deleted date of the interaction driver." + "The deleted date of the interaction status." deleted_at: DateTime } @@ -78,12 +78,18 @@ input CreateInteractionStatusInput { "unique:interaction_statuses,name" ] ) + + "The color of the interaction status." + color: InteractionStatusColorOptions! } input UpdateInteractionStatusInput { "The name of the interaction status." name: String @rules(apply: ["string", "max:255", "unique:interaction_statuses,name"]) + + "The color of the interaction status." + color: InteractionStatusColorOptions } type InteractionStatusMutations { diff --git a/app-modules/interaction/graphql/interaction.graphql b/app-modules/interaction/graphql/interaction.graphql index ac3cb1a9ab..7aded1faf5 100644 --- a/app-modules/interaction/graphql/interaction.graphql +++ b/app-modules/interaction/graphql/interaction.graphql @@ -106,120 +106,120 @@ extend type Query { interaction: InteractionQueries! @namespaced } -input UpdateInteractionInput { +input CreateInteractionInput { "The subject of the interaction." - subject: String @rules(apply: ["string", "max:255"]) + subject: String! @rules(apply: ["required", "string", "max:255"]) "The description of the interaction." - description: String @rules(apply: ["string"]) + description: String! @rules(apply: ["required", "string"]) "The User related to the interaction." - user_id: UUID @rules(apply: ["exists:users,id"]) + user_id: UUID! @rules(apply: ["required", "exists:users,id"]) "The Interactable related to the interaction." - interactable_id: InteractableId + interactable_id: InteractableId! @rules( apply: [ + "required" "AdvisingApp\\Interaction\\Rules\\InteractableIdExistsRule" - "required_with:interactable_type" ] ) "The type of Interactable related to the interaction." - interactable_type: InteractableType - @rules( - apply: [ - "in:student,prospect,service_request" - "required_with:interactable_id" - ] - ) + interactable_type: InteractableType! + @rules(apply: ["required", "in:student,prospect,service_request"]) "The type of interaction." - interaction_type_id: UUID @rules(apply: ["exists:interaction_types,id"]) + interaction_type_id: UUID! + @rules(apply: ["required", "exists:interaction_types,id"]) "The relation of the interaction." - interaction_relation_id: UUID - @rules(apply: ["exists:interaction_relations,id"]) + interaction_relation_id: UUID! + @rules(apply: ["required", "exists:interaction_relations,id"]) "The campaign of the interaction." - interaction_campaign_id: UUID - @rules(apply: ["exists:interaction_campaigns,id"]) + interaction_campaign_id: UUID! + @rules(apply: ["required", "exists:interaction_campaigns,id"]) "The driver of the interaction." - interaction_driver_id: UUID @rules(apply: ["exists:interaction_drivers,id"]) + interaction_driver_id: UUID! + @rules(apply: ["required", "exists:interaction_drivers,id"]) "The status of the interaction." - interaction_status_id: UUID - @rules(apply: ["exists:interaction_statuses,id"]) + interaction_status_id: UUID! + @rules(apply: ["required", "exists:interaction_statuses,id"]) "The outcome of the interaction." - interaction_outcome_id: UUID - @rules(apply: ["exists:interaction_outcomes,id"]) + interaction_outcome_id: UUID! + @rules(apply: ["required", "exists:interaction_outcomes,id"]) "The division of the interaction." - division_id: UUID @rules(apply: ["exists:divisions,id"]) + division_id: UUID! @rules(apply: ["required", "exists:divisions,id"]) "The start datetime of the interaction." - start_datetime: DateTime @rules(apply: ["date_format:Y-m-d H:i:s"]) + start_datetime: DateTime! + @rules(apply: ["required", "date_format:Y-m-d H:i:s"]) "The end datetime of the interaction." end_datetime: DateTime @rules(apply: ["nullable", "date_format:Y-m-d H:i:s"]) } -input CreateInteractionInput { +input UpdateInteractionInput { "The subject of the interaction." - subject: String! @rules(apply: ["required", "string", "max:255"]) + subject: String @rules(apply: ["string", "max:255"]) "The description of the interaction." - description: String! @rules(apply: ["required", "string"]) + description: String @rules(apply: ["string"]) "The User related to the interaction." - user_id: UUID! @rules(apply: ["required", "exists:users,id"]) + user_id: UUID @rules(apply: ["exists:users,id"]) "The Interactable related to the interaction." - interactable_id: InteractableId! + interactable_id: InteractableId @rules( apply: [ - "required" "AdvisingApp\\Interaction\\Rules\\InteractableIdExistsRule" + "required_with:interactable_type" ] ) "The type of Interactable related to the interaction." - interactable_type: InteractableType! - @rules(apply: ["required", "in:student,prospect,service_request"]) + interactable_type: InteractableType + @rules( + apply: [ + "in:student,prospect,service_request" + "required_with:interactable_id" + ] + ) "The type of interaction." - interaction_type_id: UUID! - @rules(apply: ["required", "exists:interaction_types,id"]) + interaction_type_id: UUID @rules(apply: ["exists:interaction_types,id"]) "The relation of the interaction." - interaction_relation_id: UUID! - @rules(apply: ["required", "exists:interaction_relations,id"]) + interaction_relation_id: UUID + @rules(apply: ["exists:interaction_relations,id"]) "The campaign of the interaction." - interaction_campaign_id: UUID! - @rules(apply: ["required", "exists:interaction_campaigns,id"]) + interaction_campaign_id: UUID + @rules(apply: ["exists:interaction_campaigns,id"]) "The driver of the interaction." - interaction_driver_id: UUID! - @rules(apply: ["required", "exists:interaction_drivers,id"]) + interaction_driver_id: UUID @rules(apply: ["exists:interaction_drivers,id"]) "The status of the interaction." - interaction_status_id: UUID! - @rules(apply: ["required", "exists:interaction_statuses,id"]) + interaction_status_id: UUID + @rules(apply: ["exists:interaction_statuses,id"]) "The outcome of the interaction." - interaction_outcome_id: UUID! - @rules(apply: ["required", "exists:interaction_outcomes,id"]) + interaction_outcome_id: UUID + @rules(apply: ["exists:interaction_outcomes,id"]) "The division of the interaction." - division_id: UUID! @rules(apply: ["required", "exists:divisions,id"]) + division_id: UUID @rules(apply: ["exists:divisions,id"]) "The start datetime of the interaction." - start_datetime: DateTime! - @rules(apply: ["required", "date_format:Y-m-d H:i:s"]) + start_datetime: DateTime @rules(apply: ["date_format:Y-m-d H:i:s"]) "The end datetime of the interaction." end_datetime: DateTime diff --git a/app-modules/notifications/graphql/subscription.graphql b/app-modules/notifications/graphql/subscription.graphql index 27ffc88b3f..31d3a7d22d 100644 --- a/app-modules/notifications/graphql/subscription.graphql +++ b/app-modules/notifications/graphql/subscription.graphql @@ -13,8 +13,8 @@ type UserSubscription } input UserSubscriptionSubscribablesQuery { - student: StudentQuery - prospect: ProspectQuery + student: StudentsQuery + prospect: ProspectsQuery } input SubscriptionsQuery { diff --git a/app-modules/prospect/graphql/prospect-source.graphql b/app-modules/prospect/graphql/prospect-source.graphql new file mode 100644 index 0000000000..8da9468bff --- /dev/null +++ b/app-modules/prospect/graphql/prospect-source.graphql @@ -0,0 +1,108 @@ +type ProspectSource + @model(class: "AdvisingApp\\Prospect\\Models\\ProspectSource") { + "Unique primary key." + id: UUID! + + "The name of the prospect driver." + name: String! + + "Prospects related to this prospect driver." + prospects: [Prospect!] @hasMany + + "The created date of the prospect driver." + created_at: DateTime + + "The updated date of the prospect driver." + updated_at: DateTime + + "The deleted date of the prospect driver." + deleted_at: DateTime +} + +input ProspectSourcesQuery { + "The unique primary key of the prospect source." + id: UUID + + "The name of the prospect source." + name: String + + "The prospects related to this prospect source." + prospects: ProspectsQuery + + "The created date of the prospect source." + created_at: DateTime + + "The updated date of the prospect source." + updated_at: DateTime + + "The deleted date of the prospect source." + deleted_at: DateTime +} + +type ProspectSourceQueries { + "Get a specific prospect source by ID." + find( + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_sources"]) + ): ProspectSource @find @softDeletes @canResolved(ability: "view") + + "List multiple prospects sources." + list( + "Filter by the prospect sources attributes and relations." + where: ProspectSourcesQuery @searchBy + ): [ProspectSource!]! @paginate @softDeletes @canModel(ability: "viewAny") +} + +extend type Query { + prospectSource: ProspectSourceQueries! @namespaced +} + +input CreateProspectSourceInput { + "The name of the prospect source." + name: String! + @rules( + apply: [ + "required" + "string" + "max:255" + "unique:prospect_sources,name" + ] + ) +} + +input UpdateProspectSourceInput { + "The name of the prospect source." + name: String + @rules(apply: ["string", "max:255", "unique:prospect_sources,name"]) +} + +type ProspectSourceMutations { + "Create an prospect source." + create(input: CreateProspectSourceInput! @spread): ProspectSource! + @create + @canModel(ability: "create") + + "Update an existing prospect source." + update( + "The identifier of the prospect source you would like to update." + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_sources"]) + + "The fields you would like to update." + input: UpdateProspectSourceInput! @spread + ): ProspectSource! @canFind(ability: "update", find: "id") @update + + "Delete an existing prospect source." + delete( + "The identifier of the prospect source you would like to delete." + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_sources"]) + ): ProspectSource @canFind(ability: "delete", find: "id") @delete +} + +extend type Mutation { + prospectSource: ProspectSourceMutations! @namespaced +} diff --git a/app-modules/prospect/graphql/prospect-status.graphql b/app-modules/prospect/graphql/prospect-status.graphql index a142368163..aac791b41a 100644 --- a/app-modules/prospect/graphql/prospect-status.graphql +++ b/app-modules/prospect/graphql/prospect-status.graphql @@ -10,7 +10,7 @@ type ProspectStatus prospects: [Prospect!] @hasMany "The classification of the prospect status." - classification: ProspectStatusClassificationOptions! + classification: SystemProspectClassification! "The color of the prospect status." color: ProspectStatusColorOptions! @@ -36,7 +36,7 @@ input ProspectStatusesQuery { prospects: ProspectsQuery "The classification of the prospect status." - classification: ProspectStatusClassificationOptions + classification: SystemProspectClassification "The color of the prospect status." color: ProspectStatusColorOptions @@ -55,18 +55,15 @@ type ProspectStatusQueries { "Get a specific prospect status by ID." find( id: UUID! - @whereKey - @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) ): ProspectStatus @find @softDeletes @canResolved(ability: "view") "List multiple prospects statuses." list( "Filter by the prospect statuses attributes and relations." where: ProspectStatusesQuery @searchBy - ): [ProspectStatus!]! - @paginate - @softDeletes - @canModel(ability: "viewAny") + ): [ProspectStatus!]! @paginate @softDeletes @canModel(ability: "viewAny") } extend type Query { @@ -76,34 +73,53 @@ extend type Query { input CreateProspectStatusInput { "The name of the prospect status." name: String! - @rules( - apply: [ - "required" - "string" - "max:255" - "unique:prospect_statuses,name" - ] - ) + @rules( + apply: [ + "required" + "string" + "max:255" + "unique:prospect_statuses,name" + ] + ) + + "The classification of the prospect status." + classification: SystemProspectClassification! + + "The color of the prospect status." + color: ProspectStatusColorOptions! } input UpdateProspectStatusInput { "The name of the prospect status." name: String - @rules(apply: ["string", "max:255", "unique:prospect_statuses,name"]) + @rules( + apply: [ + "required" + "string" + "max:255" + "unique:prospect_statuses,name" + ] + ) + + "The classification of the prospect status." + classification: SystemProspectClassification + + "The color of the prospect status." + color: ProspectStatusColorOptions } type ProspectStatusMutations { "Create an prospect status." create(input: CreateProspectStatusInput! @spread): ProspectStatus! - @create - @canModel(ability: "create") + @create + @canModel(ability: "create") "Update an existing prospect status." update( "The identifier of the prospect status you would like to update." id: UUID! - @whereKey - @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) "The fields you would like to update." input: UpdateProspectStatusInput! @spread @@ -113,8 +129,8 @@ type ProspectStatusMutations { delete( "The identifier of the prospect status you would like to delete." id: UUID! - @whereKey - @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) + @whereKey + @rules(apply: ["required", "uuid", "exists:prospect_statuses"]) ): ProspectStatus @canFind(ability: "delete", find: "id") @delete } diff --git a/app-modules/prospect/graphql/prospect.graphql b/app-modules/prospect/graphql/prospect.graphql index b570475b6a..3ee6265c04 100644 --- a/app-modules/prospect/graphql/prospect.graphql +++ b/app-modules/prospect/graphql/prospect.graphql @@ -3,7 +3,10 @@ type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { id: UUID! "The status of the prospect." - status: String! + status: ProspectStatus! @belongsTo + + "The source of the prospect." + source: ProspectSource! @belongsTo "The first name of the prospect." first_name: String! @@ -45,10 +48,10 @@ type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { address_2: String "The birthdate of the prospect." - birthdate: DateTime + birthdate: Date "The High School graduation year of the prospect." - hs_grad_year: String + hsgrad: String "The created date of the prospect." created_at: DateTime @@ -61,10 +64,74 @@ type Prospect @model(class: "AdvisingApp\\Prospect\\Models\\Prospect") { } input ProspectsQuery { - id: UUID! - first_name: String! - last_name: String! - full_name: String! + "Unique primary key." + id: UUID + + "The status ID of the prospect." + status_id: UUID + + "The status of the prospect." + status: ProspectStatusesQuery + + "The source ID of the prospect." + source_id: UUID + + "The source of the prospect." + source: ProspectSourcesQuery + + "The first name of the prospect." + first_name: String + + "The last name of the prospect." + last_name: String + + "The full name of the prospect." + full_name: String + + "The preferred name of the prospect." + preferred: String + + "The description of the prospect." + description: String + + "The email of the prospect." + email: String + + "The email 2 of the prospect." + email_2: String + + "The mobile number of the prospect." + mobile: String + + "The phone number of the prospect." + phone: String + + "If the prospect is opted out of SMS messages." + sms_opt_out: Boolean + + "If the prospect's email bounces." + email_bounce: Boolean + + "The address of the prospect." + address: String + + "The address 2 of the prospect." + address_2: String + + "The birthdate of the prospect." + birthdate: Date + + "The High School graduation year of the prospect." + hsgrad: String + + "The created date of the prospect." + created_at: DateTime + + "The updated date of the prospect." + updated_at: DateTime + + "The deleted date of the prospect." + deleted_at: DateTime } type ProspectQueries { @@ -86,26 +153,110 @@ extend type Query { prospect: ProspectQueries! @namespaced } -input UpdateProspectInput { +input CreateProspectInput { + "The status of the prospect." + status_id: UUID! @rules(apply: ["required", "exists:prospect_statuses,id"]) + + "The source of the prospect." + source_id: UUID! @rules(apply: ["required", "exists:prospect_sources,id"]) + "The first name of the prospect." - first_name: String @rules(apply: ["string", "max:255"]) + first_name: String! @rules(apply: ["required", "string", "max:255"]) "The last name of the prospect." - last_name: String @rules(apply: ["string", "max:255"]) + last_name: String! @rules(apply: ["required", "string", "max:255"]) "The full name of the prospect." - full_name: String @rules(apply: ["string", "max:255"]) + full_name: String! @rules(apply: ["required", "string", "max:255"]) + + "The preferred name of the prospect." + preferred: String @rules(apply: ["string", "max:255"]) + + "The description of the prospect." + description: String @rules(apply: ["string", "max:4294967295"]) + + "The email of the prospect." + email: String @rules(apply: ["string", "email", "max:255"]) + + "The email 2 of the prospect." + email_2: String @rules(apply: ["string", "email", "max:255"]) + + "The mobile number of the prospect." + mobile: String @rules(apply: ["string", "max:255"]) + + "The phone number of the prospect." + phone: String @rules(apply: ["string", "max:255"]) + + "If the prospect is opted out of SMS messages." + sms_opt_out: Boolean @rules(apply: ["boolean"]) + + "If the prospect's email bounces." + email_bounce: Boolean @rules(apply: ["boolean"]) + + "The address of the prospect." + address: String @rules(apply: ["string", "max:255"]) + + "The address 2 of the prospect." + address_2: String @rules(apply: ["string", "max:255"]) + + "The birthdate of the prospect." + birthdate: Date @rules(apply: ["date_format:Y-m-d"]) + + "The High School graduation year of the prospect." + hsgrad: String @rules(apply: ["string", "max:255"]) } -input CreateProspectInput { +input UpdateProspectInput { + "The status of the prospect." + status_id: UUID @rules(apply: ["exists:prospect_statuses,id"]) + + "The source of the prospect." + source_id: UUID @rules(apply: ["exists:prospect_sources,id"]) + "The first name of the prospect." - first_name: String! @rules(apply: ["required", "string", "max:255"]) + first_name: String @rules(apply: ["string", "max:255"]) "The last name of the prospect." - last_name: String! @rules(apply: ["required", "string", "max:255"]) + last_name: String @rules(apply: ["string", "max:255"]) "The full name of the prospect." - full_name: String! @rules(apply: ["required", "string", "max:255"]) + full_name: String @rules(apply: ["string", "max:255"]) + + "The preferred name of the prospect." + preferred: String @rules(apply: ["string", "max:255"]) + + "The description of the prospect." + description: String @rules(apply: ["string", "max:4294967295"]) + + "The email of the prospect." + email: String @rules(apply: ["string", "email", "max:255"]) + + "The email 2 of the prospect." + email_2: String @rules(apply: ["string", "email", "max:255"]) + + "The mobile number of the prospect." + mobile: String @rules(apply: ["string", "max:255"]) + + "The phone number of the prospect." + phone: String @rules(apply: ["string", "max:255"]) + + "If the prospect is opted out of SMS messages." + sms_opt_out: Boolean @rules(apply: ["boolean"]) + + "If the prospect's email bounces." + email_bounce: Boolean @rules(apply: ["boolean"]) + + "The address of the prospect." + address: String @rules(apply: ["string", "max:255"]) + + "The address 2 of the prospect." + address_2: String @rules(apply: ["string", "max:255"]) + + "The birthdate of the prospect." + birthdate: Date @rules(apply: ["date_format:Y-m-d"]) + + "The High School graduation year of the prospect." + hsgrad: String @rules(apply: ["string", "max:255"]) } type ProspectMutations { @@ -137,3 +288,6 @@ type ProspectMutations { extend type Mutation { prospect: ProspectMutations! @namespaced } + +#import ./prospect-source.graphql +#import ./prospect-status.graphql diff --git a/app-modules/prospect/src/Models/Prospect.php b/app-modules/prospect/src/Models/Prospect.php index 21bc9e0a58..0b9a21d71b 100644 --- a/app-modules/prospect/src/Models/Prospect.php +++ b/app-modules/prospect/src/Models/Prospect.php @@ -110,6 +110,7 @@ class Prospect extends BaseModel implements Auditable, Subscribable, Educatable, protected $casts = [ 'sms_opt_out' => 'boolean', 'email_bounce' => 'boolean', + 'birthdate' => 'date', ]; public function searchableAs(): string diff --git a/app-modules/prospect/src/Providers/ProspectServiceProvider.php b/app-modules/prospect/src/Providers/ProspectServiceProvider.php index 46f6c14742..ae73c5fc3f 100644 --- a/app-modules/prospect/src/Providers/ProspectServiceProvider.php +++ b/app-modules/prospect/src/Providers/ProspectServiceProvider.php @@ -46,6 +46,8 @@ use AdvisingApp\Prospect\Observers\ProspectObserver; use Illuminate\Database\Eloquent\Relations\Relation; use AdvisingApp\Authorization\AuthorizationRoleRegistry; +use AdvisingApp\Prospect\Enums\ProspectStatusColorOptions; +use AdvisingApp\Prospect\Enums\SystemProspectClassification; use AdvisingApp\Authorization\AuthorizationPermissionRegistry; class ProspectServiceProvider extends ServiceProvider @@ -70,6 +72,8 @@ public function boot(): void Prospect::observe(ProspectObserver::class); $this->discoverSchema(__DIR__ . '/../../graphql/prospect.graphql'); + $this->registerEnum(ProspectStatusColorOptions::class); + $this->registerEnum(SystemProspectClassification::class); } public function registerRolesAndPermissions(): void diff --git a/graphql/schema.graphql b/graphql/schema.graphql index 40c3c66b57..9459185a5f 100644 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -1,4 +1,6 @@ "A datetime string with format `Y-m-d H:i:s`, e.g. `2018-05-23 13:43:32`." +scalar Date @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date") + scalar DateTime @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime") From 0bd28a02f8cfa29b7fd32fc11b4b67b996692948 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 13:46:27 -0500 Subject: [PATCH 114/126] Add test, import class. --- .../src/Notifications/BaseNotification.php | 3 +- .../Features/NotificationSendingTest.php | 107 ++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 app-modules/notification/tests/Features/NotificationSendingTest.php diff --git a/app-modules/notification/src/Notifications/BaseNotification.php b/app-modules/notification/src/Notifications/BaseNotification.php index 092574fd47..6d32c7640f 100644 --- a/app-modules/notification/src/Notifications/BaseNotification.php +++ b/app-modules/notification/src/Notifications/BaseNotification.php @@ -36,6 +36,7 @@ namespace AdvisingApp\Notification\Notifications; +use Exception; use Illuminate\Support\Str; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; @@ -109,7 +110,7 @@ public function afterSend(object $notifiable, OutboundDeliverable $deliverable, $result instanceof SmsChannelResultData => SmsChannel::afterSending($notifiable, $deliverable, $result), $result instanceof EmailChannelResultData => EmailChannel::afterSending($notifiable, $deliverable, $result), $result instanceof DatabaseChannelResultData => DatabaseChannel::afterSending($notifiable, $deliverable, $result), - default => throw new \Exception('Invalid notification result data.'), + default => throw new Exception('Invalid notification result data.'), }; $this->afterSendHook($notifiable, $deliverable); diff --git a/app-modules/notification/tests/Features/NotificationSendingTest.php b/app-modules/notification/tests/Features/NotificationSendingTest.php new file mode 100644 index 0000000000..5afa529714 --- /dev/null +++ b/app-modules/notification/tests/Features/NotificationSendingTest.php @@ -0,0 +1,107 @@ + + + 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 App\Models\User; +use AdvisingApp\Notification\Enums\NotificationChannel; +use AdvisingApp\Notification\Models\OutboundDeliverable; +use AdvisingApp\Notification\Notifications\BaseNotification; +use AdvisingApp\Notification\Notifications\EmailNotification; +use AdvisingApp\Notification\Notifications\DatabaseNotification; +use AdvisingApp\Notification\Notifications\Messages\MailMessage; +use Filament\Notifications\Notification as FilamentNotification; +use AdvisingApp\Notification\Notifications\Concerns\EmailChannelTrait; +use AdvisingApp\Notification\Notifications\Concerns\DatabaseChannelTrait; + +it('will create an outbound deliverable for the outbound notification', function () { + $notifiable = User::factory()->create(); + + $notification = new TestEmailNotification(); + + $notifiable->notify($notification); + + expect(OutboundDeliverable::count())->toBe(1); + expect(OutboundDeliverable::first()->notification_class)->toBe(TestEmailNotification::class); +}); + +it('will create an outbound deliverable for each of the channels that the notification specifies', function () { + $notifiable = User::factory()->create(); + + $notification = new TestMultipleChannelNotification(); + + $notifiable->notify($notification); + + expect(OutboundDeliverable::count())->toBe(2); + expect(OutboundDeliverable::where('channel', NotificationChannel::Email)->count())->toBe(1); + expect(OutboundDeliverable::where('channel', NotificationChannel::Database)->count())->toBe(1); +}); + +class TestEmailNotification extends BaseNotification implements EmailNotification +{ + use EmailChannelTrait; + + public function toEmail(object $notifiable): MailMessage + { + return MailMessage::make() + ->subject('Test Subject') + ->greeting('Test Greeting') + ->content('This is a test email') + ->salutation('Test Salutation'); + } +} + +class TestMultipleChannelNotification extends BaseNotification implements EmailNotification, DatabaseNotification +{ + use EmailChannelTrait; + use DatabaseChannelTrait; + + public function toEmail(object $notifiable): MailMessage + { + return MailMessage::make() + ->subject('Test Subject') + ->greeting('Test Greeting') + ->content('This is a test email') + ->salutation('Test Salutation'); + } + + public function toDatabase(object $notifiable): array + { + return FilamentNotification::make() + ->success() + ->title('This is a test database notification.') + ->body('This is the content of your test database notification') + ->getDatabaseMessage(); + } +} From acaa6f22ed1ff45521412c500793ea62674389c3 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 13:53:35 -0500 Subject: [PATCH 115/126] Fix namespace for graphql file. --- .../notification/graphql/subscription.graphql | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/app-modules/notification/graphql/subscription.graphql b/app-modules/notification/graphql/subscription.graphql index 1c0dc60882..9e65b5b0fe 100644 --- a/app-modules/notification/graphql/subscription.graphql +++ b/app-modules/notification/graphql/subscription.graphql @@ -1,60 +1,60 @@ type UserSubscription - @model(class: "AdvisingApp\\Notifications\\Models\\Subscription") { - "Unique primary key." - id: UUID! - "The user related to this subscription." - user: User! @belongsTo - "The subscribable the user is subscribed to." - subscribable: Educatable! @morphTo - "The created date of the subscription." - created_at: DateTime - "The updated date of the subscription." - updated_at: DateTime + @model(class: "AdvisingApp\\Notification\\Models\\Subscription") { + "Unique primary key." + id: UUID! + "The user related to this subscription." + user: User! @belongsTo + "The subscribable the user is subscribed to." + subscribable: Educatable! @morphTo + "The created date of the subscription." + created_at: DateTime + "The updated date of the subscription." + updated_at: DateTime } extend type Query { - "Get a subscription by its primary key." - userSubscription( - "Search by primary key." - id: UUID! @whereKey - ): UserSubscription @find @canResolved(ability: "view") + "Get a subscription by its primary key." + userSubscription( + "Search by primary key." + id: UUID! @whereKey + ): UserSubscription @find @canResolved(ability: "view") - "Get all subscriptions." - userSubscriptions: [UserSubscription!]! - @paginate - @canModel(ability: "viewAny") + "Get all subscriptions." + userSubscriptions: [UserSubscription!]! + @paginate + @canModel(ability: "viewAny") } extend type Mutation { - "Create a new subscription." - createUserSubscription( - "The user to subscribe." - user_id: UUID! - @rules( - apply: [ - "required" - "exists:users,id" - "AdvisingApp\\Notifications\\Rules\\UniqueSubscriptionRule" - ] - ) + "Create a new subscription." + createUserSubscription( + "The user to subscribe." + user_id: UUID! + @rules( + apply: [ + "required" + "exists:users,id" + "AdvisingApp\\Notification\\Rules\\UniqueSubscriptionRule" + ] + ) - "The subscribable to subscribe to." - subscribable_id: EducatableId! - @rules( - apply: [ - "required" - "AdvisingApp\\Notifications\\Rules\\SubscribableIdExistsRule" - ] - ) + "The subscribable to subscribe to." + subscribable_id: EducatableId! + @rules( + apply: [ + "required" + "AdvisingApp\\Notification\\Rules\\SubscribableIdExistsRule" + ] + ) - "The type of subscribable to subscribe to." - subscribable_type: String! - @rules(apply: ["required", "in:student,prospect"]) - ): UserSubscription! @create @canModel(ability: "create") + "The type of subscribable to subscribe to." + subscribable_type: String! + @rules(apply: ["required", "in:student,prospect"]) + ): UserSubscription! @create @canModel(ability: "create") - "Delete an existing subscription." - deleteUserSubscription( - "The primary key of the subscription." - id: UUID! @whereKey - ): UserSubscription @delete @canFind(ability: "delete", find: "id") + "Delete an existing subscription." + deleteUserSubscription( + "The primary key of the subscription." + id: UUID! @whereKey + ): UserSubscription @delete @canFind(ability: "delete", find: "id") } From 33d8973574332634e2cda6560f8df7e6449105b9 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 14:01:57 -0500 Subject: [PATCH 116/126] update lock --- composer.lock | 93 +++++++-------------------------------------------- 1 file changed, 12 insertions(+), 81 deletions(-) diff --git a/composer.lock b/composer.lock index 812cc05701..8ea7aa3fc8 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": "76b62f75042752e87277f492a1212ec7", + "content-hash": "06a7cf58a79008fb80e6a9abff643c00", "packages": [ { "name": "amphp/amp", @@ -1992,12 +1992,12 @@ } }, { - "name": "canyon-gbs/advising-app-notifications", + "name": "canyon-gbs/advising-app-notification", "version": "1.0", "dist": { "type": "path", - "url": "app-modules/notifications", - "reference": "7dbec344f693b6973f93539b31c53d03f5891f3e" + "url": "app-modules/notification", + "reference": "5062ea2b82f5998318e06e77dcb7836661b3a412" }, "require": { "filament/filament": "^3.0" @@ -2006,16 +2006,16 @@ "extra": { "laravel": { "providers": [ - "AdvisingApp\\Notifications\\Providers\\NotificationsServiceProvider" + "AdvisingApp\\Notification\\Providers\\NotificationServiceProvider" ] } }, "autoload": { "psr-4": { - "AdvisingApp\\Notifications\\": "src/", - "AdvisingApp\\Notifications\\Tests\\": "tests/", - "AdvisingApp\\Notifications\\Database\\Factories\\": "database/factories/", - "AdvisingApp\\Notifications\\Database\\Seeders\\": "database/seeders/" + "AdvisingApp\\Notification\\": "src/", + "AdvisingApp\\Notification\\Tests\\": "tests/", + "AdvisingApp\\Notification\\Database\\Factories\\": "database/factories/", + "AdvisingApp\\Notification\\Database\\Seeders\\": "database/seeders/" } }, "license": [ @@ -2137,10 +2137,10 @@ "dist": { "type": "path", "url": "app-modules/student-data-model", - "reference": "bae8ef0016aab67de5ed247f19915690e93cbd21" + "reference": "97787a2aedc5da23f7907add6646513bb8f39a97" }, "require": { - "canyon-gbs/advising-app-notifications": "^1.0", + "canyon-gbs/advising-app-notification": "*", "filament/filament": "^3.0" }, "type": "library", @@ -8159,75 +8159,6 @@ ], "time": "2023-12-20T05:34:05+00:00" }, - { - "name": "lomkit/laravel-rest-api", - "version": "v2.3.3", - "source": { - "type": "git", - "url": "https://github.com/Lomkit/laravel-rest-api.git", - "reference": "6f631445a8a2dbc449cdc6c80590c1585a42a476" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Lomkit/laravel-rest-api/zipball/6f631445a8a2dbc449cdc6c80590c1585a42a476", - "reference": "6f631445a8a2dbc449cdc6c80590c1585a42a476", - "shasum": "" - }, - "require": { - "ext-json": "*", - "illuminate/contracts": "^10.0", - "illuminate/http": "^10.0", - "illuminate/support": "^10.0", - "php": "^8.0" - }, - "require-dev": { - "guzzlehttp/guzzle": "^6.0|^7.0", - "orchestra/testbench": "^8.5", - "phpunit/phpunit": "^8.0|^9.0|^10.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Lomkit\\Rest\\RestServiceProvider" - ], - "aliases": { - "Rest": "Lomkit\\Rest\\Facades\\Rest" - } - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "Lomkit\\Rest\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gautier Deleglise", - "email": "gautier@lomkit.com" - } - ], - "description": "A package to build quick and robust rest api for the Laravel framework.", - "keywords": [ - "api", - "graphql", - "laravel", - "lomkit", - "rest" - ], - "support": { - "issues": "https://github.com/Lomkit/laravel-rest-api/issues", - "source": "https://github.com/Lomkit/laravel-rest-api/tree/v2.3.3" - }, - "time": "2023-11-29T12:00:56+00:00" - }, { "name": "maatwebsite/excel", "version": "3.1.51", @@ -21237,5 +21168,5 @@ "ext-pdo": "*" }, "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } From 3c596ed301a0d4afd7330a3c8f7153ad51db6e3c Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 14:12:40 -0500 Subject: [PATCH 117/126] remove todo, add interface. --- .../form/src/Actions/DeliverFormSubmissionRequestBySms.php | 1 - .../src/Notifications/FormSubmissionRequestSmsNotification.php | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app-modules/form/src/Actions/DeliverFormSubmissionRequestBySms.php b/app-modules/form/src/Actions/DeliverFormSubmissionRequestBySms.php index c31d79a3f7..b86232de4c 100644 --- a/app-modules/form/src/Actions/DeliverFormSubmissionRequestBySms.php +++ b/app-modules/form/src/Actions/DeliverFormSubmissionRequestBySms.php @@ -38,7 +38,6 @@ use AdvisingApp\Form\Notifications\FormSubmissionRequestSmsNotification; -// TODO Turn this into a notification class DeliverFormSubmissionRequestBySms extends DeliverFormSubmissionRequest { public function handle(): void diff --git a/app-modules/form/src/Notifications/FormSubmissionRequestSmsNotification.php b/app-modules/form/src/Notifications/FormSubmissionRequestSmsNotification.php index 165307e9ce..4440b94a0f 100644 --- a/app-modules/form/src/Notifications/FormSubmissionRequestSmsNotification.php +++ b/app-modules/form/src/Notifications/FormSubmissionRequestSmsNotification.php @@ -37,11 +37,12 @@ namespace AdvisingApp\Form\Notifications; use AdvisingApp\Form\Models\FormSubmission; +use AdvisingApp\Notification\Notifications\SmsNotification; use AdvisingApp\Notification\Notifications\BaseNotification; use AdvisingApp\Notification\Notifications\Messages\TwilioMessage; use AdvisingApp\Notification\Notifications\Concerns\SmsChannelTrait; -class FormSubmissionRequestSmsNotification extends BaseNotification +class FormSubmissionRequestSmsNotification extends BaseNotification implements SmsNotification { use SmsChannelTrait; From bd41de2cbfc554fae85963d3b5dab18de6967d40 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 15:12:07 -0500 Subject: [PATCH 118/126] Update graphql reference. --- .../graphql/notifications.graphql | 1 - .../notification/graphql/subscription.graphql | 96 +++++++++---------- .../Providers/NotificationServiceProvider.php | 2 +- 3 files changed, 49 insertions(+), 50 deletions(-) delete mode 100644 app-modules/notification/graphql/notifications.graphql diff --git a/app-modules/notification/graphql/notifications.graphql b/app-modules/notification/graphql/notifications.graphql deleted file mode 100644 index 26d9db2a89..0000000000 --- a/app-modules/notification/graphql/notifications.graphql +++ /dev/null @@ -1 +0,0 @@ -#import subscription.graphql diff --git a/app-modules/notification/graphql/subscription.graphql b/app-modules/notification/graphql/subscription.graphql index 9e65b5b0fe..7761106a58 100644 --- a/app-modules/notification/graphql/subscription.graphql +++ b/app-modules/notification/graphql/subscription.graphql @@ -1,60 +1,60 @@ type UserSubscription - @model(class: "AdvisingApp\\Notification\\Models\\Subscription") { - "Unique primary key." - id: UUID! - "The user related to this subscription." - user: User! @belongsTo - "The subscribable the user is subscribed to." - subscribable: Educatable! @morphTo - "The created date of the subscription." - created_at: DateTime - "The updated date of the subscription." - updated_at: DateTime + @model(class: "AdvisingApp\\Notification\\Models\\Subscription") { + "Unique primary key." + id: UUID! + "The user related to this subscription." + user: User! @belongsTo + "The subscribable the user is subscribed to." + subscribable: Educatable! @morphTo + "The created date of the subscription." + created_at: DateTime + "The updated date of the subscription." + updated_at: DateTime } extend type Query { - "Get a subscription by its primary key." - userSubscription( - "Search by primary key." - id: UUID! @whereKey - ): UserSubscription @find @canResolved(ability: "view") + "Get a subscription by its primary key." + userSubscription( + "Search by primary key." + id: UUID! @whereKey + ): UserSubscription @find @canResolved(ability: "view") - "Get all subscriptions." - userSubscriptions: [UserSubscription!]! - @paginate - @canModel(ability: "viewAny") + "Get all subscriptions." + userSubscriptions: [UserSubscription!]! + @paginate + @canModel(ability: "viewAny") } extend type Mutation { - "Create a new subscription." - createUserSubscription( - "The user to subscribe." - user_id: UUID! - @rules( - apply: [ - "required" - "exists:users,id" - "AdvisingApp\\Notification\\Rules\\UniqueSubscriptionRule" - ] - ) + "Create a new subscription." + createUserSubscription( + "The user to subscribe." + user_id: UUID! + @rules( + apply: [ + "required" + "exists:users,id" + "AdvisingApp\\Notification\\Rules\\UniqueSubscriptionRule" + ] + ) - "The subscribable to subscribe to." - subscribable_id: EducatableId! - @rules( - apply: [ - "required" - "AdvisingApp\\Notification\\Rules\\SubscribableIdExistsRule" - ] - ) + "The subscribable to subscribe to." + subscribable_id: EducatableId! + @rules( + apply: [ + "required" + "AdvisingApp\\Notification\\Rules\\SubscribableIdExistsRule" + ] + ) - "The type of subscribable to subscribe to." - subscribable_type: String! - @rules(apply: ["required", "in:student,prospect"]) - ): UserSubscription! @create @canModel(ability: "create") + "The type of subscribable to subscribe to." + subscribable_type: String! + @rules(apply: ["required", "in:student,prospect"]) + ): UserSubscription! @create @canModel(ability: "create") - "Delete an existing subscription." - deleteUserSubscription( - "The primary key of the subscription." - id: UUID! @whereKey - ): UserSubscription @delete @canFind(ability: "delete", find: "id") + "Delete an existing subscription." + deleteUserSubscription( + "The primary key of the subscription." + id: UUID! @whereKey + ): UserSubscription @delete @canFind(ability: "delete", find: "id") } diff --git a/app-modules/notification/src/Providers/NotificationServiceProvider.php b/app-modules/notification/src/Providers/NotificationServiceProvider.php index b90d180391..45cf9ac70a 100644 --- a/app-modules/notification/src/Providers/NotificationServiceProvider.php +++ b/app-modules/notification/src/Providers/NotificationServiceProvider.php @@ -71,7 +71,7 @@ public function boot(): void $this->registerObservers(); $this->registerEvents(); - $this->discoverSchema(__DIR__ . '/../../graphql/notifications.graphql'); + $this->discoverSchema(__DIR__ . '/../../graphql/subscription.graphql'); } protected function registerObservers(): void From 6c2127daebd511bf1af5a14878b5eeaa1861048f Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 15:29:36 -0500 Subject: [PATCH 119/126] Add dispatched delivery status. --- .../engagement/src/Actions/DeliverEngagements.php | 1 + .../src/Enums/EngagementDeliveryStatus.php | 4 ++++ app-modules/engagement/src/Models/Engagement.php | 8 ++++++++ .../factories/OutboundDeliverableFactory.php | 5 ----- .../src/Enums/NotificationDeliveryStatus.php | 9 +++++++-- .../src/Notifications/Channels/EmailChannel.php | 12 ++++++++++-- .../src/Notifications/Channels/SmsChannel.php | 4 ++-- 7 files changed, 32 insertions(+), 11 deletions(-) diff --git a/app-modules/engagement/src/Actions/DeliverEngagements.php b/app-modules/engagement/src/Actions/DeliverEngagements.php index c36acc4919..579f8a126d 100644 --- a/app-modules/engagement/src/Actions/DeliverEngagements.php +++ b/app-modules/engagement/src/Actions/DeliverEngagements.php @@ -55,6 +55,7 @@ public function handle(): void Engagement::query() ->where('deliver_at', '<=', now()) ->isScheduled() + ->isAwaitingDelivery() ->isNotPartOfABatch() ->hasNotBeenDelivered() ->cursor() diff --git a/app-modules/engagement/src/Enums/EngagementDeliveryStatus.php b/app-modules/engagement/src/Enums/EngagementDeliveryStatus.php index fd950d83ec..60d8d500a4 100644 --- a/app-modules/engagement/src/Enums/EngagementDeliveryStatus.php +++ b/app-modules/engagement/src/Enums/EngagementDeliveryStatus.php @@ -39,6 +39,10 @@ enum EngagementDeliveryStatus: string { case Awaiting = 'awaiting'; + case Dispatched = 'dispatched'; + case DispatchFailed = 'failed_dispatch'; + case RateLimited = 'rate_limited'; + case Successful = 'successful'; case Failed = 'failed'; } diff --git a/app-modules/engagement/src/Models/Engagement.php b/app-modules/engagement/src/Models/Engagement.php index 8acb2c5822..e4fdff3509 100644 --- a/app-modules/engagement/src/Models/Engagement.php +++ b/app-modules/engagement/src/Models/Engagement.php @@ -50,6 +50,7 @@ use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\Relations\BelongsTo; use AdvisingApp\Timeline\Timelines\EngagementTimeline; +use AdvisingApp\Engagement\Enums\EngagementDeliveryStatus; use AdvisingApp\Notification\Models\Contracts\Subscribable; use AdvisingApp\Timeline\Models\Contracts\ProvidesATimeline; use AdvisingApp\StudentDataModel\Models\Contracts\Educatable; @@ -143,6 +144,13 @@ public function scopeIsScheduled(Builder $query): void $query->where('scheduled', true); } + public function scopeIsAwaitingDelivery(Builder $query): void + { + $query->whereHas('engagementDeliverable', function (Builder $query) { + $query->where('status', EngagementDeliveryStatus::Awaiting); + }); + } + public function scopeHasBeenDelivered(Builder $query): void { $query->whereDoesntHave('engagementDeliverable', function (Builder $query) { diff --git a/app-modules/notification/database/factories/OutboundDeliverableFactory.php b/app-modules/notification/database/factories/OutboundDeliverableFactory.php index e077767637..3f22a5368b 100644 --- a/app-modules/notification/database/factories/OutboundDeliverableFactory.php +++ b/app-modules/notification/database/factories/OutboundDeliverableFactory.php @@ -43,11 +43,6 @@ */ class OutboundDeliverableFactory extends Factory { - /** - * Define the model's default state. - * - * @return array - */ public function definition(): array { return [ diff --git a/app-modules/notification/src/Enums/NotificationDeliveryStatus.php b/app-modules/notification/src/Enums/NotificationDeliveryStatus.php index 5265ab3450..4472e3669d 100644 --- a/app-modules/notification/src/Enums/NotificationDeliveryStatus.php +++ b/app-modules/notification/src/Enums/NotificationDeliveryStatus.php @@ -40,11 +40,16 @@ enum NotificationDeliveryStatus: string implements HasLabel { + // Internal case Awaiting = 'awaiting'; - case Successful = 'successful'; - case Failed = 'failed'; + case Dispatched = 'dispatched'; + case DispatchFailed = 'failed_dispatch'; case RateLimited = 'rate_limited'; + // External + case Failed = 'failed'; + case Successful = 'successful'; + public function getLabel(): ?string { return $this->name; diff --git a/app-modules/notification/src/Notifications/Channels/EmailChannel.php b/app-modules/notification/src/Notifications/Channels/EmailChannel.php index 61c5f72363..40e4e6c4ca 100644 --- a/app-modules/notification/src/Notifications/Channels/EmailChannel.php +++ b/app-modules/notification/src/Notifications/Channels/EmailChannel.php @@ -40,6 +40,7 @@ use Illuminate\Notifications\Channels\MailChannel; use AdvisingApp\Notification\Models\OutboundDeliverable; use AdvisingApp\Notification\Notifications\BaseNotification; +use AdvisingApp\Notification\Enums\NotificationDeliveryStatus; use AdvisingApp\Notification\DataTransferObjects\EmailChannelResultData; use AdvisingApp\Notification\DataTransferObjects\NotificationResultData; @@ -77,7 +78,14 @@ public function handle(object $notifiable, BaseNotification $notification): Noti public static function afterSending(object $notifiable, OutboundDeliverable $deliverable, EmailChannelResultData $result): void { - // TODO Do we want to add any updating of the deliverable here? - // Or do we want to leave it all for SES events handled via webhook? + if ($result->success) { + $deliverable->update([ + 'status' => NotificationDeliveryStatus::Dispatched, + ]); + } else { + $deliverable->update([ + 'status' => NotificationDeliveryStatus::DispatchFailed, + ]); + } } } diff --git a/app-modules/notification/src/Notifications/Channels/SmsChannel.php b/app-modules/notification/src/Notifications/Channels/SmsChannel.php index 51944f710e..e5138ea173 100644 --- a/app-modules/notification/src/Notifications/Channels/SmsChannel.php +++ b/app-modules/notification/src/Notifications/Channels/SmsChannel.php @@ -105,11 +105,11 @@ public static function afterSending(object $notifiable, OutboundDeliverable $del $deliverable->update([ 'external_reference_id' => $result->message->sid, 'external_status' => $result->message->status, - 'delivery_status' => NotificationDeliveryStatus::Successful, + 'delivery_status' => NotificationDeliveryStatus::Dispatched, ]); } else { $deliverable->update([ - 'delivery_status' => NotificationDeliveryStatus::Failed, + 'delivery_status' => NotificationDeliveryStatus::DispatchFailed, 'delivery_response' => $result->error, ]); } From 7670962244c8ae71bbda8d84f6a613dca71fe01e Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 15:37:39 -0500 Subject: [PATCH 120/126] Update column. --- app-modules/engagement/src/Models/Engagement.php | 2 +- .../notification/src/Notifications/Channels/EmailChannel.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app-modules/engagement/src/Models/Engagement.php b/app-modules/engagement/src/Models/Engagement.php index e4fdff3509..1db65d748c 100644 --- a/app-modules/engagement/src/Models/Engagement.php +++ b/app-modules/engagement/src/Models/Engagement.php @@ -147,7 +147,7 @@ public function scopeIsScheduled(Builder $query): void public function scopeIsAwaitingDelivery(Builder $query): void { $query->whereHas('engagementDeliverable', function (Builder $query) { - $query->where('status', EngagementDeliveryStatus::Awaiting); + $query->where('delivery_status', EngagementDeliveryStatus::Awaiting); }); } diff --git a/app-modules/notification/src/Notifications/Channels/EmailChannel.php b/app-modules/notification/src/Notifications/Channels/EmailChannel.php index 40e4e6c4ca..ca59b49066 100644 --- a/app-modules/notification/src/Notifications/Channels/EmailChannel.php +++ b/app-modules/notification/src/Notifications/Channels/EmailChannel.php @@ -80,11 +80,11 @@ public static function afterSending(object $notifiable, OutboundDeliverable $del { if ($result->success) { $deliverable->update([ - 'status' => NotificationDeliveryStatus::Dispatched, + 'delivery_status' => NotificationDeliveryStatus::Dispatched, ]); } else { $deliverable->update([ - 'status' => NotificationDeliveryStatus::DispatchFailed, + 'delivery_status' => NotificationDeliveryStatus::DispatchFailed, ]); } } From eda5b144339e49ac9238595c974f4d6cb34e25ed Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 15:39:32 -0500 Subject: [PATCH 121/126] Remove comment. --- app-modules/engagement/src/Drivers/EmailDriver.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app-modules/engagement/src/Drivers/EmailDriver.php b/app-modules/engagement/src/Drivers/EmailDriver.php index 9ec9844149..855aa99a3e 100644 --- a/app-modules/engagement/src/Drivers/EmailDriver.php +++ b/app-modules/engagement/src/Drivers/EmailDriver.php @@ -54,13 +54,6 @@ public function jobForDelivery(): QueuedEngagementDelivery return new EngagementEmailChannelDelivery($this->deliverable); } - // Currently, deliverables are created at the point of Engagement creation - // But, if we move in the direction of normalization of "deliverables" as a concept across any notification (including engagements) - // We probably don't want to create "deliverables" until we _actually_ attempt to deliver them. This would take place in the underlying notification - // class that every other notification would extend - // This way, we could introduce hooks that allow us to perform actions before/after send of a notification, and the normalized deliverables would be - // Created and tracked on demand - // At least initially, let's created a duplicated data source for the normalized outbound deliverables that will serve as a layer on top of engagement deliverables for now public function deliver(): void { EngagementEmailChannelDelivery::dispatch($this->deliverable); From b3e559344de97daa2ac73dd4b3af853efe37d54a Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Thu, 21 Dec 2023 15:43:21 -0500 Subject: [PATCH 122/126] Update outbound deliverable notifiable key. --- .../notification/src/Actions/CreateOutboundDeliverable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-modules/notification/src/Actions/CreateOutboundDeliverable.php b/app-modules/notification/src/Actions/CreateOutboundDeliverable.php index 77c580f7a2..30fe6dedbf 100644 --- a/app-modules/notification/src/Actions/CreateOutboundDeliverable.php +++ b/app-modules/notification/src/Actions/CreateOutboundDeliverable.php @@ -66,7 +66,7 @@ public function handle(BaseNotification $notification, object $notifiable, strin 'channel' => $channel, 'notification_class' => get_class($notification), 'content' => json_encode($content), - 'recipient_id' => $notifiable->id, + 'recipient_id' => $notifiable->getKey(), 'recipient_type' => $notifiable->getMorphClass(), ]); From 4057e7519f2adab00b3b387d825bf310b0b4355c Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 21 Dec 2023 20:45:22 +0000 Subject: [PATCH 123/126] Update EditProspectTest.php --- app-modules/prospect/tests/Prospect/EditProspectTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-modules/prospect/tests/Prospect/EditProspectTest.php b/app-modules/prospect/tests/Prospect/EditProspectTest.php index 5ce215723c..5afffddf31 100644 --- a/app-modules/prospect/tests/Prospect/EditProspectTest.php +++ b/app-modules/prospect/tests/Prospect/EditProspectTest.php @@ -102,7 +102,7 @@ ->and($prospect->fresh()->phone)->toEqual($request->get('phone')) ->and($prospect->fresh()->address)->toEqual($request->get('address')) ->and($prospect->fresh()->address_2)->toEqual($request->get('address_2')) - ->and($prospect->fresh()->birthdate)->toEqual($request->get('birthdate')) + ->and($prospect->fresh()->birthdate->toDateString())->toEqual($request->get('birthdate')) ->and($prospect->fresh()->hsgrad)->toEqual($request->get('hsgrad')) ->and($prospect->fresh()->assigned_to_id)->toEqual($request->get('assigned_to_id')) ->and($prospect->fresh()->created_by_id)->toEqual($request->get('created_by_id')); From 901423ee40226a08422d11af9f11222bdaa6ff66 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 21 Dec 2023 16:21:56 -0500 Subject: [PATCH 124/126] Make sure the audit queue is specified Signed-off-by: Kevin Ullyott --- app-modules/audit/config/audit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-modules/audit/config/audit.php b/app-modules/audit/config/audit.php index d85ae45545..181469257d 100644 --- a/app-modules/audit/config/audit.php +++ b/app-modules/audit/config/audit.php @@ -205,7 +205,7 @@ 'queue' => [ 'dispatch_listener' => ProcessDispatchAudit::class, 'connection' => env('AUDIT_QUEUE_CONNECTION', 'sync'), - 'queue' => 'default', + 'queue' => env('SQS_QUEUE', 'default'), 'delay' => 0, ], From 8b719877af77d85a3a711d0f3f2a4297f8346d18 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Thu, 21 Dec 2023 16:58:53 -0500 Subject: [PATCH 125/126] Merge branch 'develop' into advapp-20 Signed-off-by: Kevin Ullyott # Conflicts: # app-modules/notifications/graphql/subscription.graphql --- .../graphql/subscription.graphql | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 app-modules/notifications/graphql/subscription.graphql diff --git a/app-modules/notifications/graphql/subscription.graphql b/app-modules/notifications/graphql/subscription.graphql new file mode 100644 index 0000000000..31d3a7d22d --- /dev/null +++ b/app-modules/notifications/graphql/subscription.graphql @@ -0,0 +1,91 @@ +type UserSubscription + @model(class: "AdvisingApp\\Notifications\\Models\\Subscription") { + "Unique primary key." + id: UUID! + "The User related to this subscription." + user: User! @belongsTo + "The Subscribable the User is subscribed to." + subscribable: Educatable! @morphTo + "The created date of the subscription." + created_at: DateTime + "The updated date of the subscription." + updated_at: DateTime +} + +input UserSubscriptionSubscribablesQuery { + student: StudentsQuery + prospect: ProspectsQuery +} + +input SubscriptionsQuery { + id: UUID + user: UserQuery + subscribable: UserSubscriptionSubscribablesQuery @morphToRelation + subscribable_id: EducatableId + subscribable_type: EducatableType + created_at: DateTime + updated_at: DateTime +} + +type UserSubscriptionQueries { + "Find a single subscription by an identifying attribute." + find( + "The value of the attribute to match." + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:subscriptions"]) + ): UserSubscription @find @canResolved(ability: "view") + + "List multiple subscriptions." + list(where: SubscriptionsQuery @searchBy): [UserSubscription!]! + @paginate + @canModel(ability: "viewAny") +} + +extend type Query { + subscription: UserSubscriptionQueries! @namespaced +} + +input CreateUserSubscriptionInput { + "The user to subscribe." + user_id: UUID! + @rules( + apply: [ + "required" + "exists:users,id" + "AdvisingApp\\Notifications\\Rules\\UniqueSubscriptionRule" + ] + ) + + "The subscribable to subscribe to." + subscribable_id: EducatableId! + @rules( + apply: [ + "required" + "AdvisingApp\\Notifications\\Rules\\SubscribableIdExistsRule" + ] + ) + + "The type of subscribable to subscribe to." + subscribable_type: EducatableType! + @rules(apply: ["required", "in:student,prospect"]) +} + +type UserSubscriptionMutations { + "Subscribe a User to a Subscribable." + subscribe(input: CreateUserSubscriptionInput! @spread): UserSubscription! + @create + @canModel(ability: "create") + + "Unsubscribe a User from a Subscribable." + unsubscribe( + "The primary key of the subscription." + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:subscriptions"]) + ): UserSubscription @delete @canFind(ability: "delete", find: "id") +} + +extend type Mutation { + subscription: UserSubscriptionMutations! @namespaced +} From ff01a71c97b86a9aebae41a45641d542bfd407da Mon Sep 17 00:00:00 2001 From: Matthew Myers Date: Thu, 21 Dec 2023 18:13:58 -0500 Subject: [PATCH 126/126] Fix. --- .../notification/graphql/subscription.graphql | 109 +++++++++++------- .../graphql/subscription.graphql | 91 --------------- 2 files changed, 70 insertions(+), 130 deletions(-) delete mode 100644 app-modules/notifications/graphql/subscription.graphql diff --git a/app-modules/notification/graphql/subscription.graphql b/app-modules/notification/graphql/subscription.graphql index 7761106a58..5b8447c974 100644 --- a/app-modules/notification/graphql/subscription.graphql +++ b/app-modules/notification/graphql/subscription.graphql @@ -2,9 +2,9 @@ type UserSubscription @model(class: "AdvisingApp\\Notification\\Models\\Subscription") { "Unique primary key." id: UUID! - "The user related to this subscription." + "The User related to this subscription." user: User! @belongsTo - "The subscribable the user is subscribed to." + "The Subscribable the User is subscribed to." subscribable: Educatable! @morphTo "The created date of the subscription." created_at: DateTime @@ -12,49 +12,80 @@ type UserSubscription updated_at: DateTime } -extend type Query { - "Get a subscription by its primary key." - userSubscription( - "Search by primary key." - id: UUID! @whereKey +input UserSubscriptionSubscribablesQuery { + student: StudentsQuery + prospect: ProspectsQuery +} + +input SubscriptionsQuery { + id: UUID + user: UserQuery + subscribable: UserSubscriptionSubscribablesQuery @morphToRelation + subscribable_id: EducatableId + subscribable_type: EducatableType + created_at: DateTime + updated_at: DateTime +} + +type UserSubscriptionQueries { + "Find a single subscription by an identifying attribute." + find( + "The value of the attribute to match." + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:subscriptions"]) ): UserSubscription @find @canResolved(ability: "view") - "Get all subscriptions." - userSubscriptions: [UserSubscription!]! + "List multiple subscriptions." + list(where: SubscriptionsQuery @searchBy): [UserSubscription!]! @paginate @canModel(ability: "viewAny") } -extend type Mutation { - "Create a new subscription." - createUserSubscription( - "The user to subscribe." - user_id: UUID! - @rules( - apply: [ - "required" - "exists:users,id" - "AdvisingApp\\Notification\\Rules\\UniqueSubscriptionRule" - ] - ) - - "The subscribable to subscribe to." - subscribable_id: EducatableId! - @rules( - apply: [ - "required" - "AdvisingApp\\Notification\\Rules\\SubscribableIdExistsRule" - ] - ) - - "The type of subscribable to subscribe to." - subscribable_type: String! - @rules(apply: ["required", "in:student,prospect"]) - ): UserSubscription! @create @canModel(ability: "create") - - "Delete an existing subscription." - deleteUserSubscription( +extend type Query { + subscription: UserSubscriptionQueries! @namespaced +} + +input CreateUserSubscriptionInput { + "The user to subscribe." + user_id: UUID! + @rules( + apply: [ + "required" + "exists:users,id" + "AdvisingApp\\Notification\\Rules\\UniqueSubscriptionRule" + ] + ) + + "The subscribable to subscribe to." + subscribable_id: EducatableId! + @rules( + apply: [ + "required" + "AdvisingApp\\Notification\\Rules\\SubscribableIdExistsRule" + ] + ) + + "The type of subscribable to subscribe to." + subscribable_type: EducatableType! + @rules(apply: ["required", "in:student,prospect"]) +} + +type UserSubscriptionMutations { + "Subscribe a User to a Subscribable." + subscribe(input: CreateUserSubscriptionInput! @spread): UserSubscription! + @create + @canModel(ability: "create") + + "Unsubscribe a User from a Subscribable." + unsubscribe( "The primary key of the subscription." - id: UUID! @whereKey + id: UUID! + @whereKey + @rules(apply: ["required", "uuid", "exists:subscriptions"]) ): UserSubscription @delete @canFind(ability: "delete", find: "id") } + +extend type Mutation { + subscription: UserSubscriptionMutations! @namespaced +} diff --git a/app-modules/notifications/graphql/subscription.graphql b/app-modules/notifications/graphql/subscription.graphql deleted file mode 100644 index 31d3a7d22d..0000000000 --- a/app-modules/notifications/graphql/subscription.graphql +++ /dev/null @@ -1,91 +0,0 @@ -type UserSubscription - @model(class: "AdvisingApp\\Notifications\\Models\\Subscription") { - "Unique primary key." - id: UUID! - "The User related to this subscription." - user: User! @belongsTo - "The Subscribable the User is subscribed to." - subscribable: Educatable! @morphTo - "The created date of the subscription." - created_at: DateTime - "The updated date of the subscription." - updated_at: DateTime -} - -input UserSubscriptionSubscribablesQuery { - student: StudentsQuery - prospect: ProspectsQuery -} - -input SubscriptionsQuery { - id: UUID - user: UserQuery - subscribable: UserSubscriptionSubscribablesQuery @morphToRelation - subscribable_id: EducatableId - subscribable_type: EducatableType - created_at: DateTime - updated_at: DateTime -} - -type UserSubscriptionQueries { - "Find a single subscription by an identifying attribute." - find( - "The value of the attribute to match." - id: UUID! - @whereKey - @rules(apply: ["required", "uuid", "exists:subscriptions"]) - ): UserSubscription @find @canResolved(ability: "view") - - "List multiple subscriptions." - list(where: SubscriptionsQuery @searchBy): [UserSubscription!]! - @paginate - @canModel(ability: "viewAny") -} - -extend type Query { - subscription: UserSubscriptionQueries! @namespaced -} - -input CreateUserSubscriptionInput { - "The user to subscribe." - user_id: UUID! - @rules( - apply: [ - "required" - "exists:users,id" - "AdvisingApp\\Notifications\\Rules\\UniqueSubscriptionRule" - ] - ) - - "The subscribable to subscribe to." - subscribable_id: EducatableId! - @rules( - apply: [ - "required" - "AdvisingApp\\Notifications\\Rules\\SubscribableIdExistsRule" - ] - ) - - "The type of subscribable to subscribe to." - subscribable_type: EducatableType! - @rules(apply: ["required", "in:student,prospect"]) -} - -type UserSubscriptionMutations { - "Subscribe a User to a Subscribable." - subscribe(input: CreateUserSubscriptionInput! @spread): UserSubscription! - @create - @canModel(ability: "create") - - "Unsubscribe a User from a Subscribable." - unsubscribe( - "The primary key of the subscription." - id: UUID! - @whereKey - @rules(apply: ["required", "uuid", "exists:subscriptions"]) - ): UserSubscription @delete @canFind(ability: "delete", find: "id") -} - -extend type Mutation { - subscription: UserSubscriptionMutations! @namespaced -}