From 3052e96c6df5377a6dfc641cd5257b02bfc4a949 Mon Sep 17 00:00:00 2001 From: Mazarin Date: Sun, 6 Nov 2022 07:32:55 -0500 Subject: [PATCH] feat: add religion on contact profile (monicahq/chandler#283) --- .../Web/ViewHelpers/ContactShowViewHelper.php | 5 + .../Services/UpdateReligion.php | 79 ++++++++++++ .../ContactModuleReligionController.php | 30 +++++ .../ViewHelpers/ModuleReligionViewHelper.php | 45 +++++++ .../CreateAccount/Jobs/SetupAccount.php | 17 +++ app/Models/Contact.php | 11 ++ app/Models/ContactFeedItem.php | 2 + app/Models/Module.php | 2 + ...2_11_01_174411_add_religion_to_contact.php | 33 +++++ lang/en/app.php | 1 + lang/en/contact.php | 9 ++ resources/js/Pages/Vault/Contact/Show.vue | 10 ++ resources/js/Shared/Modules/Feed.vue | 5 + resources/js/Shared/Modules/Religion.vue | 119 ++++++++++++++++++ routes/web.php | 4 + .../Services/UpdateReligionTest.php | 118 +++++++++++++++++ .../ModuleReligionViewHelperTest.php | 78 ++++++++++++ .../Services/SetupAccountTest.php | 4 + tests/Unit/Models/ContactTest.php | 12 ++ 19 files changed, 584 insertions(+) create mode 100644 app/Domains/Contact/ManageReligion/Services/UpdateReligion.php create mode 100644 app/Domains/Contact/ManageReligion/Web/Controllers/ContactModuleReligionController.php create mode 100644 app/Domains/Contact/ManageReligion/Web/ViewHelpers/ModuleReligionViewHelper.php create mode 100644 database/migrations/2022_11_01_174411_add_religion_to_contact.php create mode 100644 resources/js/Shared/Modules/Religion.vue create mode 100644 tests/Unit/Domains/Contact/ManageReligion/Services/UpdateReligionTest.php create mode 100644 tests/Unit/Domains/Contact/ManageReligion/Web/ViewHelpers/ModuleReligionViewHelperTest.php diff --git a/app/Domains/Contact/ManageContact/Web/ViewHelpers/ContactShowViewHelper.php b/app/Domains/Contact/ManageContact/Web/ViewHelpers/ContactShowViewHelper.php index b3e83fa7270..59af6d50574 100644 --- a/app/Domains/Contact/ManageContact/Web/ViewHelpers/ContactShowViewHelper.php +++ b/app/Domains/Contact/ManageContact/Web/ViewHelpers/ContactShowViewHelper.php @@ -21,6 +21,7 @@ use App\Domains\Contact\ManagePronouns\Web\ViewHelpers\ModuleGenderPronounViewHelper; use App\Domains\Contact\ManageRelationships\Web\ViewHelpers\ModuleFamilySummaryViewHelper; use App\Domains\Contact\ManageRelationships\Web\ViewHelpers\ModuleRelationshipViewHelper; +use App\Domains\Contact\ManageReligion\Web\ViewHelpers\ModuleReligionViewHelper; use App\Domains\Contact\ManageReminders\Web\ViewHelpers\ModuleRemindersViewHelper; use App\Domains\Contact\ManageTasks\Web\ViewHelpers\ModuleContactTasksViewHelper; use App\Helpers\StorageHelper; @@ -199,6 +200,10 @@ private static function getContactInformation(EloquentCollection $templatePages, $data = ModuleFamilySummaryViewHelper::data($contact, $user); } + if ($module->type == Module::TYPE_RELIGIONS) { + $data = ModuleReligionViewHelper::data($contact); + } + $modulesCollection->push([ 'id' => $module->id, 'type' => $module->type, diff --git a/app/Domains/Contact/ManageReligion/Services/UpdateReligion.php b/app/Domains/Contact/ManageReligion/Services/UpdateReligion.php new file mode 100644 index 00000000000..e492291a4ba --- /dev/null +++ b/app/Domains/Contact/ManageReligion/Services/UpdateReligion.php @@ -0,0 +1,79 @@ + 'required|integer|exists:accounts,id', + 'vault_id' => 'required|integer|exists:vaults,id', + 'author_id' => 'required|integer|exists:users,id', + 'contact_id' => 'required|integer|exists:contacts,id', + 'religion_id' => 'nullable|integer|exists:religions,id', + ]; + } + + /** + * Get the permissions that apply to the user calling the service. + * + * @return array + */ + public function permissions(): array + { + return [ + 'author_must_belong_to_account', + 'vault_must_belong_to_account', + 'author_must_be_vault_editor', + 'contact_must_belong_to_vault', + ]; + } + + /** + * Update religion of the given contact. + * + * @param array $data + * @return Contact + */ + public function execute(array $data): Contact + { + $this->validateRules($data); + + $this->religion = Religion::where('account_id', $data['account_id']) + ->findOrFail($data['religion_id']); + + $this->contact->religion_id = $data['religion_id'] ? $this->religion->id : null; + $this->contact->save(); + + $this->updateLastEditedDate(); + + ContactFeedItem::create([ + 'author_id' => $this->author->id, + 'contact_id' => $this->contact->id, + 'action' => ContactFeedItem::ACTION_RELIGION_UPDATED, + ]); + + return $this->contact; + } + + private function updateLastEditedDate(): void + { + $this->contact->last_updated_at = Carbon::now(); + $this->contact->save(); + } +} diff --git a/app/Domains/Contact/ManageReligion/Web/Controllers/ContactModuleReligionController.php b/app/Domains/Contact/ManageReligion/Web/Controllers/ContactModuleReligionController.php new file mode 100644 index 00000000000..a7819af0f75 --- /dev/null +++ b/app/Domains/Contact/ManageReligion/Web/Controllers/ContactModuleReligionController.php @@ -0,0 +1,30 @@ +execute([ + 'account_id' => Auth::user()->account_id, + 'author_id' => Auth::user()->id, + 'vault_id' => $vaultId, + 'contact_id' => $contactId, + 'religion_id' => $request->input('religion_id'), + ]); + + $contact = Contact::findOrFail($contactId); + + return response()->json([ + 'data' => ModuleReligionViewHelper::data($contact), + ], 200); + } +} diff --git a/app/Domains/Contact/ManageReligion/Web/ViewHelpers/ModuleReligionViewHelper.php b/app/Domains/Contact/ManageReligion/Web/ViewHelpers/ModuleReligionViewHelper.php new file mode 100644 index 00000000000..d3598496641 --- /dev/null +++ b/app/Domains/Contact/ManageReligion/Web/ViewHelpers/ModuleReligionViewHelper.php @@ -0,0 +1,45 @@ + $contact->religion ? [ + 'id' => $contact->religion->id, + 'name' => $contact->religion->name, + ] : null, + 'religions' => self::list($contact), + 'url' => [ + 'update' => route('contact.religion.update', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + ], + ]; + } + + public static function list(Contact $contact): Collection + { + return $contact->vault->account + ->religions() + ->orderBy('position', 'asc') + ->get() + ->map(fn (Religion $religion) => self::dto($religion, $contact)); + } + + public static function dto(Religion $religion, Contact $contact): array + { + return [ + 'id' => $religion->id, + 'name' => $religion->name, + 'selected' => $religion->id === $contact->religion_id, + ]; + } +} diff --git a/app/Domains/Settings/CreateAccount/Jobs/SetupAccount.php b/app/Domains/Settings/CreateAccount/Jobs/SetupAccount.php index dc3d23a1918..d2cf83351e0 100644 --- a/app/Domains/Settings/CreateAccount/Jobs/SetupAccount.php +++ b/app/Domains/Settings/CreateAccount/Jobs/SetupAccount.php @@ -266,6 +266,23 @@ private function addTemplatePageContactInformation(): void 'template_page_id' => $templatePageContact->id, 'module_id' => $module->id, ]); + + // religions + $module = (new CreateModule())->execute([ + 'account_id' => $this->author->account_id, + 'author_id' => $this->author->id, + 'name' => trans('app.module_religions'), + 'type' => Module::TYPE_RELIGIONS, + 'can_be_deleted' => false, + 'reserved_to_contact_information' => true, + ]); + (new AssociateModuleToTemplatePage())->execute([ + 'account_id' => $this->author->account_id, + 'author_id' => $this->author->id, + 'template_id' => $this->template->id, + 'template_page_id' => $templatePageContact->id, + 'module_id' => $module->id, + ]); } private function addTemplatePageFeed(): void diff --git a/app/Models/Contact.php b/app/Models/Contact.php index 805dce807a7..88ad8d99caa 100644 --- a/app/Models/Contact.php +++ b/app/Models/Contact.php @@ -51,6 +51,7 @@ class Contact extends Model 'job_position', 'listed', 'file_id', + 'religion_id', ]; /** @@ -334,6 +335,16 @@ public function groups(): BelongsToMany return $this->belongsToMany(Group::class, 'contact_group'); } + /** + * Get the religion associated with the contact. + * + * @return BelongsTo + */ + public function religion(): BelongsTo + { + return $this->belongsTo(Religion::class); + } + /** * Get the name of the contact, according to the user preference. * diff --git a/app/Models/ContactFeedItem.php b/app/Models/ContactFeedItem.php index 95ebb373822..0d9f64f64af 100644 --- a/app/Models/ContactFeedItem.php +++ b/app/Models/ContactFeedItem.php @@ -28,6 +28,8 @@ class ContactFeedItem extends Model public const ACTION_JOB_INFORMATION_UPDATED = 'job_information_updated'; + public const ACTION_RELIGION_UPDATED = 'religion_updated'; + public const ACTION_NOTE_CREATED = 'note_created'; public const ACTION_NOTE_UPDATED = 'note_updated'; diff --git a/app/Models/Module.php b/app/Models/Module.php index ef4ac094d6d..4ae23598eca 100644 --- a/app/Models/Module.php +++ b/app/Models/Module.php @@ -57,6 +57,8 @@ class Module extends Model public const TYPE_PHOTOS = 'photos'; + public const TYPE_RELIGIONS = 'religions'; + /** * The attributes that are mass assignable. * diff --git a/database/migrations/2022_11_01_174411_add_religion_to_contact.php b/database/migrations/2022_11_01_174411_add_religion_to_contact.php new file mode 100644 index 00000000000..85ee79762bb --- /dev/null +++ b/database/migrations/2022_11_01_174411_add_religion_to_contact.php @@ -0,0 +1,33 @@ +unsignedBigInteger('religion_id')->nullable()->after('file_id'); + $table->foreign('religion_id')->references('id')->on('religions')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('contacts', function (Blueprint $table) { + $table->dropColumn('religion_id'); + }); + } +}; diff --git a/lang/en/app.php b/lang/en/app.php index 9bdf339361a..f9888a52c78 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -115,6 +115,7 @@ 'module_contact_information' => 'Contact information', 'module_documents' => 'Documents', 'module_photos' => 'Photos', + 'module_religions' => 'Religions', 'module_option_default_number_of_items_to_display' => 'Default number of items to display', diff --git a/lang/en/contact.php b/lang/en/contact.php index a902c366337..fe47755033a 100644 --- a/lang/en/contact.php +++ b/lang/en/contact.php @@ -39,6 +39,7 @@ 'feed_item_note_updated' => 'edited a note', 'feed_item_note_destroyed' => 'deleted a note', 'feed_item_job_information_updated' => 'updated the job information', + 'feed_item_religion_updated' => 'updated the religion', 'feed_item_goal_created' => 'created a goal', 'feed_item_goal_updated' => 'updated a goal', 'feed_item_goal_destroyed' => 'deleted a goal', @@ -123,4 +124,12 @@ 'goals_delete_confirm' => 'Are you sure? This will delete the goal and all the streaks permanently.', 'goals_delete_success' => 'The goal has been deleted', 'goals_update_success' => 'The goal has been edited', + + /*************************************************************** + * MODULE: RELIGIONS + **************************************************************/ + + 'religions_title' => 'Religion', + 'religions_delete_success' => 'The goal has been deleted', + 'religions_update_success' => 'The goal has been edited', ]; diff --git a/resources/js/Pages/Vault/Contact/Show.vue b/resources/js/Pages/Vault/Contact/Show.vue index 83ce85d4c72..57866544cc1 100644 --- a/resources/js/Pages/Vault/Contact/Show.vue +++ b/resources/js/Pages/Vault/Contact/Show.vue @@ -59,6 +59,8 @@ + + @@ -207,6 +209,7 @@ import Groups from '@/Shared/Modules/Groups.vue'; import ContactInformation from '@/Shared/Modules/ContactInformation.vue'; import Documents from '@/Shared/Modules/Documents.vue'; import Photos from '@/Shared/Modules/Photos.vue'; +import Religion from '@/Shared/Modules/Religion.vue'; import Uploadcare from '@/Components/Uploadcare.vue'; export default { @@ -233,6 +236,7 @@ export default { ContactInformation, Documents, Photos, + Religion, Uploadcare, }, @@ -270,6 +274,7 @@ export default { contactInformation: [], documents: [], photos: [], + religions: [], form: { searchTerm: null, uuid: null, @@ -325,6 +330,11 @@ export default { this.data.contact_information.findIndex((x) => x.type == 'family_summary') ].data; } + + if (this.data.contact_information.findIndex((x) => x.type == 'religions') > -1) { + this.religions = + this.data.contact_information[this.data.contact_information.findIndex((x) => x.type == 'religions')].data; + } } // active page diff --git a/resources/js/Shared/Modules/Feed.vue b/resources/js/Shared/Modules/Feed.vue index 813bbf3eb2a..7159408553e 100644 --- a/resources/js/Shared/Modules/Feed.vue +++ b/resources/js/Shared/Modules/Feed.vue @@ -81,6 +81,11 @@ :data="feedItem.data" :contact-view-mode="contactViewMode" /> + + +import Errors from '@/Shared/Form/Errors.vue'; +import PrettyButton from '@/Shared/Form/PrettyButton.vue'; +import PrettySpan from '@/Shared/Form/PrettySpan.vue'; +import Dropdown from '@/Shared/Form/Dropdown.vue'; +import { useForm } from '@inertiajs/inertia-vue3'; +import { onMounted, ref } from 'vue'; + +const props = defineProps({ + data: Object, +}); + +const form = useForm({ + religion_id: '', +}); + +const loadingState = ref(false); +const editReligion = ref(false); +const localReligions = ref([]); +const religion = ref(''); + +onMounted(() => { + form.religion_id = props.data.religion ? props.data.religion.id : null; + religion.value = props.data.religion ? props.data.religion.name : null; + localReligions.value = props.data.religions; +}); + +const update = () => { + loadingState.value = 'loading'; + + axios + .put(props.data.url.update, form) + .then((response) => { + editReligion.value = false; + loadingState.value = ''; + religion.value = response.data.data.religion.name; + }) + .catch(() => { + loadingState.value = ''; + }); +}; + +const showEditModal = () => { + editReligion.value = true; +}; + + + + + diff --git a/routes/web.php b/routes/web.php index bc762317754..b6ef672b45f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -30,6 +30,7 @@ use App\Domains\Contact\ManagePhotos\Web\Controllers\ContactModulePhotoController; use App\Domains\Contact\ManagePhotos\Web\Controllers\ContactPhotoController; use App\Domains\Contact\ManageRelationships\Web\Controllers\ContactRelationshipsController; +use App\Domains\Contact\ManageReligion\Web\Controllers\ContactModuleReligionController; use App\Domains\Contact\ManageReminders\Web\Controllers\ContactModuleReminderController; use App\Domains\Contact\ManageTasks\Web\Controllers\ContactModuleTaskController; use App\Domains\Settings\CancelAccount\Web\Controllers\CancelAccountController; @@ -243,6 +244,9 @@ Route::get('companies/list', [ContactModuleJobInformationController::class, 'index'])->name('contact.companies.list.index'); Route::put('jobInformation', [ContactModuleJobInformationController::class, 'update'])->name('contact.job_information.update'); + // religion + Route::put('religion', [ContactModuleReligionController::class, 'update'])->name('contact.religion.update'); + // relationships Route::get('relationships/create', [ContactRelationshipsController::class, 'create'])->name('contact.relationships.create'); Route::post('relationships', [ContactRelationshipsController::class, 'store'])->name('contact.relationships.store'); diff --git a/tests/Unit/Domains/Contact/ManageReligion/Services/UpdateReligionTest.php b/tests/Unit/Domains/Contact/ManageReligion/Services/UpdateReligionTest.php new file mode 100644 index 00000000000..a620a091a7d --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageReligion/Services/UpdateReligionTest.php @@ -0,0 +1,118 @@ +createUser(); + $vault = $this->createVault($regis->account); + $vault = $this->setPermissionInVault($regis, Vault::PERMISSION_EDIT, $vault); + $contact = Contact::factory()->create(['vault_id' => $vault->id]); + $religion = Religion::factory()->create(['account_id' => $regis->account_id]); + + $this->executeService($regis, $regis->account, $vault, $contact, $religion); + } + + /** @test */ + public function it_fails_if_wrong_parameters_are_given(): void + { + $request = [ + 'title' => 'Ross', + ]; + + $this->expectException(ValidationException::class); + (new UpdateReligion())->execute($request); + } + + /** @test */ + public function it_fails_if_user_doesnt_belong_to_account(): void + { + $this->expectException(ModelNotFoundException::class); + + $regis = $this->createUser(); + $account = Account::factory()->create(); + $vault = $this->createVault($regis->account); + $vault = $this->setPermissionInVault($regis, Vault::PERMISSION_EDIT, $vault); + $contact = Contact::factory()->create(['vault_id' => $vault->id]); + $religion = Religion::factory()->create(['account_id' => $regis->account_id]); + + $this->executeService($regis, $account, $vault, $contact, $religion); + } + + /** @test */ + public function it_fails_if_contact_doesnt_belong_to_vault(): void + { + $this->expectException(ModelNotFoundException::class); + + $regis = $this->createUser(); + $vault = $this->createVault($regis->account); + $vault = $this->setPermissionInVault($regis, Vault::PERMISSION_EDIT, $vault); + $contact = Contact::factory()->create(); + $religion = Religion::factory()->create(['account_id' => $regis->account_id]); + + $this->executeService($regis, $regis->account, $vault, $contact, $religion); + } + + /** @test */ + public function it_fails_if_user_doesnt_have_right_permission_in_initial_vault(): void + { + $this->expectException(NotEnoughPermissionException::class); + + $regis = $this->createUser(); + $vault = $this->createVault($regis->account); + $vault = $this->setPermissionInVault($regis, Vault::PERMISSION_VIEW, $vault); + $contact = Contact::factory()->create(['vault_id' => $vault->id]); + $religion = Religion::factory()->create(['account_id' => $regis->account_id]); + + $this->executeService($regis, $regis->account, $vault, $contact, $religion); + } + + /** @test */ + public function it_fails_if_religion_is_not_in_the_account(): void + { + $this->expectException(ModelNotFoundException::class); + + $regis = $this->createUser(); + $vault = $this->createVault($regis->account); + $vault = $this->setPermissionInVault($regis, Vault::PERMISSION_EDIT, $vault); + $contact = Contact::factory()->create(['vault_id' => $vault->id]); + $religion = Religion::factory()->create(); + + $this->executeService($regis, $regis->account, $vault, $contact, $religion); + } + + private function executeService(User $author, Account $account, Vault $vault, Contact $contact, Religion $religion): void + { + $request = [ + 'account_id' => $account->id, + 'vault_id' => $vault->id, + 'author_id' => $author->id, + 'contact_id' => $contact->id, + 'religion_id' => $religion->id, + ]; + + $contact = (new UpdateReligion())->execute($request); + + $this->assertDatabaseHas('contacts', [ + 'id' => $contact->id, + 'religion_id' => $religion->id, + ]); + } +} diff --git a/tests/Unit/Domains/Contact/ManageReligion/Web/ViewHelpers/ModuleReligionViewHelperTest.php b/tests/Unit/Domains/Contact/ManageReligion/Web/ViewHelpers/ModuleReligionViewHelperTest.php new file mode 100644 index 00000000000..ebcbc0c5125 --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageReligion/Web/ViewHelpers/ModuleReligionViewHelperTest.php @@ -0,0 +1,78 @@ +create(); + + $array = ModuleReligionViewHelper::data($contact); + + $this->assertEquals( + 3, + count($array) + ); + + $this->assertArrayHasKey('religion', $array); + $this->assertArrayHasKey('religions', $array); + $this->assertArrayHasKey('url', $array); + + $this->assertEquals( + [ + 'update' => env('APP_URL').'/vaults/'.$contact->vault->id.'/contacts/'.$contact->id.'/religion', + ], + $array['url'] + ); + } + + /** @test */ + public function it_gets_all_the_religions(): void + { + $contact = Contact::factory()->create(); + $religion = Religion::factory()->create([ + 'account_id' => $contact->vault->account->id, + ]); + + $collection = ModuleReligionViewHelper::list($contact); + $this->assertEquals( + [ + 0 => [ + 'id' => $religion->id, + 'name' => $religion->name, + 'selected' => false, + ], + ], + $collection->toArray() + ); + } + + /** @test */ + public function it_gets_the_data_transfer_object(): void + { + $contact = Contact::factory()->create(); + $religion = Religion::factory()->create([ + 'account_id' => $contact->vault->account->id, + ]); + + $collection = ModuleReligionViewHelper::dto($religion, $contact); + $this->assertEquals( + [ + 'id' => $religion->id, + 'name' => $religion->name, + 'selected' => false, + ], + $collection + ); + } +} diff --git a/tests/Unit/Domains/Settings/CreateAccount/Services/SetupAccountTest.php b/tests/Unit/Domains/Settings/CreateAccount/Services/SetupAccountTest.php index c7c080a0f81..02e435e1829 100644 --- a/tests/Unit/Domains/Settings/CreateAccount/Services/SetupAccountTest.php +++ b/tests/Unit/Domains/Settings/CreateAccount/Services/SetupAccountTest.php @@ -93,6 +93,10 @@ public function it_sets_an_account_up(): void 'account_id' => $user->account_id, 'name' => trans('app.module_companies'), ]); + $this->assertDatabaseHas('modules', [ + 'account_id' => $user->account_id, + 'name' => trans('app.module_religions'), + ]); $this->assertDatabaseHas('modules', [ 'account_id' => $user->account_id, 'name' => trans('app.module_tasks'), diff --git a/tests/Unit/Models/ContactTest.php b/tests/Unit/Models/ContactTest.php index 47c96291336..04db7ebe299 100644 --- a/tests/Unit/Models/ContactTest.php +++ b/tests/Unit/Models/ContactTest.php @@ -21,6 +21,7 @@ use App\Models\Pet; use App\Models\Pronoun; use App\Models\RelationshipType; +use App\Models\Religion; use App\Models\Template; use App\Models\User; use Carbon\Carbon; @@ -259,6 +260,17 @@ public function it_has_many_groups(): void $this->assertTrue($contact->groups()->exists()); } + /** @test */ + public function it_has_one_religion(): void + { + $contact = Contact::factory()->create(); + $religion = Religion::factory()->create(); + $contact->religion_id = $religion->id; + $contact->save(); + + $this->assertTrue($contact->religion()->exists()); + } + /** @test */ public function it_gets_the_name(): void {