diff --git a/app/Jobs/SetupAccount.php b/app/Jobs/SetupAccount.php index 871d5031f40..4c56af3ab10 100644 --- a/app/Jobs/SetupAccount.php +++ b/app/Jobs/SetupAccount.php @@ -442,6 +442,22 @@ private function addTemplatePageInformation(): void 'module_id' => $module->id, ]); + // Documents + $module = (new CreateModule())->execute([ + 'account_id' => $this->user->account_id, + 'author_id' => $this->user->id, + 'name' => trans('app.module_photos'), + 'type' => Module::TYPE_PHOTOS, + 'can_be_deleted' => false, + ]); + (new AssociateModuleToTemplatePage())->execute([ + 'account_id' => $this->user->account_id, + 'author_id' => $this->user->id, + 'template_id' => $this->template->id, + 'template_page_id' => $templatePageInformation->id, + 'module_id' => $module->id, + ]); + // Notes $module = (new CreateModule())->execute([ 'account_id' => $this->user->account_id, diff --git a/app/Models/Module.php b/app/Models/Module.php index c60ce018e62..39bd71365fc 100644 --- a/app/Models/Module.php +++ b/app/Models/Module.php @@ -35,6 +35,7 @@ class Module extends Model public const TYPE_GROUPS = 'groups'; public const TYPE_CONTACT_INFORMATION = 'contact_information'; public const TYPE_DOCUMENTS = 'documents'; + public const TYPE_PHOTOS = 'photos'; /** * The attributes that are mass assignable. diff --git a/composer.lock b/composer.lock index e950a834a97..085d8010f7d 100644 --- a/composer.lock +++ b/composer.lock @@ -1357,16 +1357,16 @@ }, { "name": "laravel/framework", - "version": "v9.21.6", + "version": "v9.22.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "569d7a2e361895f90e66bb4e02db89c27691a0e7" + "reference": "b3b3dd43b9899f23df6d1d3e5390bd4662947a46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/569d7a2e361895f90e66bb4e02db89c27691a0e7", - "reference": "569d7a2e361895f90e66bb4e02db89c27691a0e7", + "url": "https://api.github.com/repos/laravel/framework/zipball/b3b3dd43b9899f23df6d1d3e5390bd4662947a46", + "reference": "b3b3dd43b9899f23df6d1d3e5390bd4662947a46", "shasum": "" }, "require": { @@ -1533,7 +1533,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-07-22T14:16:44+00:00" + "time": "2022-07-26T16:16:33+00:00" }, { "name": "laravel/sanctum", @@ -8143,22 +8143,23 @@ }, { "name": "laravel/breeze", - "version": "v1.11.1", + "version": "v1.11.2", "source": { "type": "git", "url": "https://github.com/laravel/breeze.git", - "reference": "416c0923860dcef96b977bd4d385c681f65e896a" + "reference": "0568bd257f0985645d5f4bb10f9f37ad9e46d414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/breeze/zipball/416c0923860dcef96b977bd4d385c681f65e896a", - "reference": "416c0923860dcef96b977bd4d385c681f65e896a", + "url": "https://api.github.com/repos/laravel/breeze/zipball/0568bd257f0985645d5f4bb10f9f37ad9e46d414", + "reference": "0568bd257f0985645d5f4bb10f9f37ad9e46d414", "shasum": "" }, "require": { - "illuminate/filesystem": "^9.0", - "illuminate/support": "^9.0", - "illuminate/validation": "^9.0", + "illuminate/console": "^9.21", + "illuminate/filesystem": "^9.21", + "illuminate/support": "^9.21", + "illuminate/validation": "^9.21", "php": "^8.0.2" }, "conflict": { @@ -8199,7 +8200,7 @@ "issues": "https://github.com/laravel/breeze/issues", "source": "https://github.com/laravel/breeze" }, - "time": "2022-07-19T14:03:23+00:00" + "time": "2022-07-20T16:08:11+00:00" }, { "name": "laravel/pint", diff --git a/domains/Contact/ManageContact/Web/ViewHelpers/ContactShowViewHelper.php b/domains/Contact/ManageContact/Web/ViewHelpers/ContactShowViewHelper.php index cc82c087cd5..6d65582e03c 100644 --- a/domains/Contact/ManageContact/Web/ViewHelpers/ContactShowViewHelper.php +++ b/domains/Contact/ManageContact/Web/ViewHelpers/ContactShowViewHelper.php @@ -18,6 +18,7 @@ use App\Contact\ManageLoans\Web\ViewHelpers\ModuleLoanViewHelper; use App\Contact\ManageNotes\Web\ViewHelpers\ModuleNotesViewHelper; use App\Contact\ManagePets\Web\ViewHelpers\ModulePetsViewHelper; +use App\Contact\ManagePhotos\Web\ViewHelpers\ModulePhotosViewHelper; use App\Contact\ManagePronouns\Web\ViewHelpers\ModuleGenderPronounViewHelper; use App\Contact\ManageRelationships\Web\ViewHelpers\ModuleFamilySummaryViewHelper; use App\Contact\ManageRelationships\Web\ViewHelpers\ModuleRelationshipViewHelper; @@ -242,6 +243,10 @@ public static function modules(TemplatePage $page, Contact $contact, User $user) $data = ModuleDocumentsViewHelper::data($contact); } + if ($module->type == Module::TYPE_PHOTOS) { + $data = ModulePhotosViewHelper::data($contact); + } + $modulesCollection->push([ 'id' => $module->id, 'type' => $module->type, diff --git a/domains/Contact/ManageDocuments/Web/ViewHelpers/ModuleDocumentsViewHelper.php b/domains/Contact/ManageDocuments/Web/ViewHelpers/ModuleDocumentsViewHelper.php index b3325ad6a3e..7a390be21de 100644 --- a/domains/Contact/ManageDocuments/Web/ViewHelpers/ModuleDocumentsViewHelper.php +++ b/domains/Contact/ManageDocuments/Web/ViewHelpers/ModuleDocumentsViewHelper.php @@ -13,6 +13,7 @@ public static function data(Contact $contact): array { $documentsCollection = $contact->files() ->where('type', File::TYPE_DOCUMENT) + ->orderBy('created_at', 'desc') ->get() ->map(function (File $file) use ($contact) { return self::dto($file, $contact); @@ -35,11 +36,11 @@ public static function dto(File $file, Contact $contact): array { return [ 'id' => $file->id, - 'download_url' => $file->cdn_url, 'name' => $file->name, 'mime_type' => $file->mime_type, 'size' => FileHelper::formatFileSize($file->size), 'url' => [ + 'download' => $file->cdn_url, 'destroy' => route('contact.document.destroy', [ 'vault' => $contact->vault_id, 'contact' => $contact->id, diff --git a/domains/Contact/ManagePhotos/Services/DestroyPhoto.php b/domains/Contact/ManagePhotos/Services/DestroyPhoto.php new file mode 100644 index 00000000000..bcabb67490f --- /dev/null +++ b/domains/Contact/ManagePhotos/Services/DestroyPhoto.php @@ -0,0 +1,74 @@ + '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', + 'file_id' => 'required|integer|exists:files,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', + 'contact_must_belong_to_vault', + 'author_must_be_vault_editor', + ]; + } + + /** + * Destroy a file of the photo type. + * + * @param array $data + */ + public function execute(array $data): void + { + $this->data = $data; + $this->validate(); + + $this->file->delete(); + + $this->updateLastEditedDate(); + } + + private function validate(): void + { + $this->validateRules($this->data); + + $this->file = File::where('contact_id', $this->contact->id) + ->where('type', File::TYPE_PHOTO) + ->findOrFail($this->data['file_id']); + } + + private function updateLastEditedDate(): void + { + $this->contact->last_updated_at = Carbon::now(); + $this->contact->save(); + } +} diff --git a/domains/Contact/ManagePhotos/Web/Controllers/ContactModulePhotoController.php b/domains/Contact/ManagePhotos/Web/Controllers/ContactModulePhotoController.php new file mode 100644 index 00000000000..8d5e0f4d23b --- /dev/null +++ b/domains/Contact/ManagePhotos/Web/Controllers/ContactModulePhotoController.php @@ -0,0 +1,60 @@ + Auth::user()->account_id, + 'author_id' => Auth::user()->id, + 'vault_id' => $vaultId, + 'contact_id' => $contactId, + 'uuid' => $request->input('uuid'), + 'name' => $request->input('name'), + 'original_url' => $request->input('original_url'), + 'cdn_url' => $request->input('cdn_url'), + 'mime_type' => $request->input('mime_type'), + 'size' => $request->input('size'), + 'type' => File::TYPE_PHOTO, + ]; + + $file = (new UploadFile())->execute($data); + + $contact = Contact::findOrFail($contactId); + + return response()->json([ + 'data' => ModulePhotosViewHelper::dto($file, $contact), + ], 201); + } + + public function destroy(Request $request, int $vaultId, int $contactId, int $fileId) + { + $data = [ + 'account_id' => Auth::user()->account_id, + 'author_id' => Auth::user()->id, + 'vault_id' => $vaultId, + 'contact_id' => $contactId, + 'file_id' => $fileId, + ]; + + (new DestroyPhoto())->execute($data); + + return response()->json([ + 'data' => route('contact.photo.index', [ + 'vault' => $vaultId, + 'contact' => $contactId, + ]), + ], 200); + } +} diff --git a/domains/Contact/ManagePhotos/Web/Controllers/ContactPhotoController.php b/domains/Contact/ManagePhotos/Web/Controllers/ContactPhotoController.php new file mode 100644 index 00000000000..dff5d1548a1 --- /dev/null +++ b/domains/Contact/ManagePhotos/Web/Controllers/ContactPhotoController.php @@ -0,0 +1,49 @@ +where('type', File::TYPE_PHOTO) + ->orderBy('created_at', 'desc') + ->paginate(30); + + return Inertia::render('Vault/Contact/Photos/Index', [ + 'layoutData' => VaultIndexViewHelper::layoutData($vault), + 'data' => ContactPhotosIndexViewHelper::data($files, $contact), + 'paginator' => PaginatorHelper::getData($files), + ]); + } + + public function show(Request $request, int $vaultId, int $contactId, int $photoId) + { + $vault = Vault::findOrFail($vaultId); + $contact = Contact::findOrFail($contactId); + + $photo = File::where('contact_id', $contactId) + ->where('type', File::TYPE_PHOTO) + ->findOrFail($photoId); + + return Inertia::render('Vault/Contact/Photos/Show', [ + 'layoutData' => VaultIndexViewHelper::layoutData($vault), + 'data' => ContactPhotosShowViewHelper::data($photo, $contact), + ]); + } +} diff --git a/domains/Contact/ManagePhotos/Web/ViewHelpers/ContactPhotosIndexViewHelper.php b/domains/Contact/ManagePhotos/Web/ViewHelpers/ContactPhotosIndexViewHelper.php new file mode 100644 index 00000000000..cf1fc82cf2c --- /dev/null +++ b/domains/Contact/ManagePhotos/Web/ViewHelpers/ContactPhotosIndexViewHelper.php @@ -0,0 +1,61 @@ +map(function (File $file) use ($contact) { + return self::dto($file, $contact); + }); + + return [ + 'contact' => [ + 'name' => $contact->name, + ], + 'photos' => $photosCollection, + 'uploadcarePublicKey' => config('services.uploadcare.public_key'), + 'canUploadFile' => StorageHelper::canUploadFile($contact->vault->account), + 'url' => [ + 'show' => route('contact.show', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + 'store' => route('contact.photo.store', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + ], + ]; + } + + public static function dto(File $file, Contact $contact): array + { + return [ + 'id' => $file->id, + 'name' => $file->name, + 'mime_type' => $file->mime_type, + 'size' => FileHelper::formatFileSize($file->size), + 'url' => [ + 'display' => 'https://ucarecdn.com/' . $file->uuid . '/-/scale_crop/400x400/smart/-/format/auto/-/quality/smart_retina/', + 'download' => $file->cdn_url, + 'show' => route('contact.photo.show', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + 'photo' => $file->id, + ]), + 'destroy' => route('contact.photo.destroy', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + 'photo' => $file->id, + ]), + ], + ]; + } +} diff --git a/domains/Contact/ManagePhotos/Web/ViewHelpers/ContactPhotosShowViewHelper.php b/domains/Contact/ManagePhotos/Web/ViewHelpers/ContactPhotosShowViewHelper.php new file mode 100644 index 00000000000..4e497155f43 --- /dev/null +++ b/domains/Contact/ManagePhotos/Web/ViewHelpers/ContactPhotosShowViewHelper.php @@ -0,0 +1,40 @@ + [ + 'name' => $contact->name, + ], + 'id' => $file->id, + 'name' => $file->name, + 'mime_type' => $file->mime_type, + 'size' => FileHelper::formatFileSize($file->size), + 'url' => [ + 'display' => 'https://ucarecdn.com/' . $file->uuid . '/-/resize/1700x/-/format/auto/-/quality/smart_retina/', + 'download' => $file->cdn_url, + 'show' => route('contact.show', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + 'index' => route('contact.photo.index', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + 'destroy' => route('contact.photo.destroy', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + 'photo' => $file->id, + ]), + ], + ]; + } +} diff --git a/domains/Contact/ManagePhotos/Web/ViewHelpers/ModulePhotosViewHelper.php b/domains/Contact/ManagePhotos/Web/ViewHelpers/ModulePhotosViewHelper.php new file mode 100644 index 00000000000..1f7f6cb3929 --- /dev/null +++ b/domains/Contact/ManagePhotos/Web/ViewHelpers/ModulePhotosViewHelper.php @@ -0,0 +1,57 @@ +files() + ->where('type', File::TYPE_PHOTO) + ->take(6) + ->get() + ->map(function (File $file) use ($contact) { + return self::dto($file, $contact); + }); + + return [ + 'photos' => $photosCollection, + 'uploadcarePublicKey' => config('services.uploadcare.public_key'), + 'canUploadFile' => StorageHelper::canUploadFile($contact->vault->account), + 'url' => [ + 'index' => route('contact.photo.index', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + 'store' => route('contact.photo.store', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + ]), + ], + ]; + } + + public static function dto(File $file, Contact $contact): array + { + return [ + 'id' => $file->id, + 'name' => $file->name, + 'mime_type' => $file->mime_type, + 'size' => FileHelper::formatFileSize($file->size), + 'url' => [ + 'display' => 'https://ucarecdn.com/' . $file->uuid . '/-/scale_crop/300x300/smart/-/format/auto/-/quality/smart_retina/', + 'download' => $file->cdn_url, + 'destroy' => route('contact.photo.destroy', [ + 'vault' => $contact->vault_id, + 'contact' => $contact->id, + 'photo' => $file->id, + ]), + ], + ]; + } +} diff --git a/domains/Vault/ManageFiles/Web/ViewHelpers/VaultFileIndexViewHelper.php b/domains/Vault/ManageFiles/Web/ViewHelpers/VaultFileIndexViewHelper.php index 74f1bb11cd5..e7b5c28c3f6 100644 --- a/domains/Vault/ManageFiles/Web/ViewHelpers/VaultFileIndexViewHelper.php +++ b/domains/Vault/ManageFiles/Web/ViewHelpers/VaultFileIndexViewHelper.php @@ -19,7 +19,6 @@ public static function data($files, User $user, Vault $vault): array $filesCollection->push([ 'id' => $file->id, - 'download_url' => $file->cdn_url, 'name' => $file->name, 'mime_type' => $file->mime_type, 'size' => FileHelper::formatFileSize($file->size), @@ -36,6 +35,7 @@ public static function data($files, User $user, Vault $vault): array ], ], 'url' => [ + 'download' => $file->cdn_url, 'destroy' => route('contact.document.destroy', [ 'vault' => $contact->vault_id, 'contact' => $contact->id, diff --git a/lang/en/app.php b/lang/en/app.php index 6a79245b596..8880a721f11 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -24,6 +24,7 @@ 'breadcrumb_contact_show' => 'Profile of :name', 'breadcrumb_contact_create' => 'Create a contact', 'breadcrumb_contact_note_index' => 'All the notes', + 'breadcrumb_contact_photo' => 'All the photos', 'breadcrumb_settings' => 'Settings', 'breadcrumb_settings_preferences' => 'User preferences', 'breadcrumb_settings_notification_channels' => 'Notification channels', @@ -99,6 +100,7 @@ 'module_groups' => 'Groups', 'module_contact_information' => 'Contact information', 'module_documents' => 'Documents', + 'module_photos' => 'Photos', '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 6f86ce29984..3530f2bdee6 100644 --- a/lang/en/contact.php +++ b/lang/en/contact.php @@ -89,4 +89,17 @@ 'documents_new_success' => 'The document has been added', 'documents_delete_confirm' => 'Are you sure? This will delete the document permanently.', 'documents_delete_success' => 'The document has been deleted', + + /*************************************************************** + * MODULE: PHOTOS + **************************************************************/ + + 'photos_title' => 'Photos', + 'photos_cta' => 'Add a photo', + 'photos_blank' => 'There are no photos yet.', + 'photos_not_enough_storage' => 'You don’t have enough space left in your account.', + 'photos_key_missing' => 'The keys to manage uploads have not been set in this Monica instance.', + 'photos_new_success' => 'The photo has been added', + 'photos_delete_confirm' => 'Are you sure? This will delete the photo permanently.', + 'photos_delete_success' => 'The photo has been deleted', ]; diff --git a/lang/fr/app.php b/lang/fr/app.php index 44064622f7d..bf36b41bf0f 100644 --- a/lang/fr/app.php +++ b/lang/fr/app.php @@ -24,6 +24,7 @@ 'breadcrumb_contact_show' => 'Profil de :name', 'breadcrumb_contact_create' => 'Créer un contact', 'breadcrumb_contact_note_index' => 'Toutes les notes', + 'breadcrumb_contact_photo' => 'Toutes les photos', 'breadcrumb_settings' => 'Paramètres', 'breadcrumb_settings_preferences' => 'Préférences de l’utilisateur', 'breadcrumb_settings_notification_channels' => 'Chaînes de notification', @@ -99,6 +100,7 @@ 'module_groups' => 'Groupes', 'module_contact_information' => 'Information de contact', 'module_documents' => 'Documents', + 'module_photos' => 'Photos', 'module_option_default_number_of_items_to_display' => 'Nombre par défaut d’éléments à afficher', diff --git a/lang/fr/contact.php b/lang/fr/contact.php index 34f167b802f..f22a07a2120 100644 --- a/lang/fr/contact.php +++ b/lang/fr/contact.php @@ -83,9 +83,21 @@ 'documents_title' => 'Documents', 'documents_cta' => 'Ajouter un document', - 'documents_blank' => 'Il n’y a pas encore d’information de documents.', + 'documents_blank' => 'Il n’y a pas encore de documents.', 'documents_key_missing' => 'Les clés pour gérer les uploads n’ont pas été définies sur cette instance de Monica.', 'documents_new_success' => 'Le document a été ajouté', 'documents_delete_confirm' => 'Êtes vous sûr ? Cela va supprimer le document de contact de façon permanente.', 'documents_delete_success' => 'Le document a été supprimé', + + /*************************************************************** + * MODULE: PHOTOS + **************************************************************/ + + 'photos_title' => 'Photos', + 'photos_cta' => 'Ajouter une photo', + 'photos_blank' => 'Il n’y a pas encore de photos.', + 'photos_key_missing' => 'Les clés pour gérer les uploads n’ont pas été définies sur cette instance de Monica.', + 'photos_new_success' => 'La photo a été ajoutée', + 'photos_delete_confirm' => 'Êtes vous sûr ? Cela va supprimer la photo de contact de façon permanente.', + 'photos_delete_success' => 'La photo a été supprimée', ]; diff --git a/resources/js/Pages/Auth/Login.vue b/resources/js/Pages/Auth/Login.vue index f00a584d486..4771dd15baa 100644 --- a/resources/js/Pages/Auth/Login.vue +++ b/resources/js/Pages/Auth/Login.vue @@ -62,9 +62,7 @@
-

- 👋 {{ $t('Please choose a product first') }} -

+

👋 Sign in to your account

diff --git a/resources/js/Pages/Vault/Contact/Photos/Index.vue b/resources/js/Pages/Vault/Contact/Photos/Index.vue new file mode 100644 index 00000000000..0fb06740043 --- /dev/null +++ b/resources/js/Pages/Vault/Contact/Photos/Index.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/resources/js/Pages/Vault/Contact/Photos/Show.vue b/resources/js/Pages/Vault/Contact/Photos/Show.vue new file mode 100644 index 00000000000..15d0567c917 --- /dev/null +++ b/resources/js/Pages/Vault/Contact/Photos/Show.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/resources/js/Pages/Vault/Contact/Show.vue b/resources/js/Pages/Vault/Contact/Show.vue index de4a73c5d99..d37c254a3a4 100644 --- a/resources/js/Pages/Vault/Contact/Show.vue +++ b/resources/js/Pages/Vault/Contact/Show.vue @@ -156,6 +156,8 @@ + +
@@ -187,6 +189,7 @@ import Addresses from '@/Shared/Modules/Addresses'; import Groups from '@/Shared/Modules/Groups'; import ContactInformation from '@/Shared/Modules/ContactInformation'; import Documents from '@/Shared/Modules/Documents'; +import Photos from '@/Shared/Modules/Photos'; export default { components: { @@ -211,6 +214,7 @@ export default { Groups, ContactInformation, Documents, + Photos, }, props: { @@ -246,6 +250,7 @@ export default { groups: [], contactInformation: [], documents: [], + photos: [], }; }, @@ -347,6 +352,10 @@ export default { if (this.data.modules.findIndex((x) => x.type == 'documents') > -1) { this.documents = this.data.modules[this.data.modules.findIndex((x) => x.type == 'documents')].data; } + + if (this.data.modules.findIndex((x) => x.type == 'photos') > -1) { + this.photos = this.data.modules[this.data.modules.findIndex((x) => x.type == 'photos')].data; + } } }, diff --git a/resources/js/Pages/Vault/Files/Index.vue b/resources/js/Pages/Vault/Files/Index.vue index 98d9da98615..39be23d112f 100644 --- a/resources/js/Pages/Vault/Files/Index.vue +++ b/resources/js/Pages/Vault/Files/Index.vue @@ -46,7 +46,7 @@

{{ $t('vault.files_filter_or') }}