From 12dabfa0ab1e040899b43ed41d1c3d9268e129ca Mon Sep 17 00:00:00 2001 From: Rodrigo Quelca Date: Fri, 13 Sep 2024 10:07:38 -0400 Subject: [PATCH 1/7] feature/FOUR-13439: STORY Adding multiple content to the clipboard --- .../Api/V1_1/ClipboardController.php | 106 ++++++++++++++++++ .../Http/Resources/V1_1/ClipboardResource.php | 9 ++ ProcessMaker/Models/Clipboard.php | 32 ++++++ .../ProcessMaker/Models/ClipboardFactory.php | 24 ++++ ...24_09_12_163218_create_clipboard_table.php | 30 +++++ .../js/processes/screen-builder/screen.vue | 9 ++ routes/v1_1/api.php | 15 ++- tests/Feature/ClipboardControllerTest.php | 20 ++++ 8 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 ProcessMaker/Http/Controllers/Api/V1_1/ClipboardController.php create mode 100644 ProcessMaker/Http/Resources/V1_1/ClipboardResource.php create mode 100644 ProcessMaker/Models/Clipboard.php create mode 100644 database/factories/ProcessMaker/Models/ClipboardFactory.php create mode 100644 database/migrations/2024_09_12_163218_create_clipboard_table.php create mode 100644 tests/Feature/ClipboardControllerTest.php diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/ClipboardController.php b/ProcessMaker/Http/Controllers/Api/V1_1/ClipboardController.php new file mode 100644 index 0000000000..96a62cb40a --- /dev/null +++ b/ProcessMaker/Http/Controllers/Api/V1_1/ClipboardController.php @@ -0,0 +1,106 @@ +findClipboardOrFail($clipboardId); + + return new Resource($clipboard); + } + + /** + * Show clipboard for the authenticated user + */ + public function showByUserId(): Resource + { + $userId = Auth::id(); + $clipboard = Clipboard::where('user_id', $userId)->firstOrFail(); + + return new Resource($clipboard); + } + + public function createOrUpdateForUser(Request $request): \Illuminate\Http\Response + { + $userId = Auth::id(); + $data = $request->all(); + $data['user_id'] = $userId; + // Check if a clipboard already exists for the user + $clipboard = Clipboard::where('user_id', $userId)->first(); + + if ($clipboard) { + $clipboard->fill($data); + } else { + $clipboard = new Clipboard($data); + $clipboard->fill($data); + } + + // Save the clipboard (either newly created or updated) + $clipboard->saveOrFail(); + + return response(new Resource($clipboard), 201); + } + + /** + * Update an existing clipboard for the authenticated user + */ + public function update(int $clipboardId, Request $request): \Illuminate\Http\Response + { + $clipboard = $this->findClipboardOrFail($clipboardId); + $this->authorizeUser($clipboard); + + $data = $request->all(); + $data['user_id'] = Auth::id(); + + $clipboard->fill($data); + $clipboard->saveOrFail(); + + return response([], 204); + } + + /** + * Delete a clipboard for the authenticated user + */ + public function destroy(int $clipboardId): \Illuminate\Http\Response + { + $clipboard = $this->findClipboardOrFail($clipboardId); + $this->authorizeUser($clipboard); + + $clipboard->delete(); + + return response([], 204); + } + + /** + * Helper method to find a clipboard by ID or fail + */ + protected function findClipboardOrFail(int $clipboardId): Clipboard + { + return Clipboard::findOrFail($clipboardId); + } + + /** + * Helper method to authorize a user for clipboard actions + */ + protected function authorizeUser(Clipboard $clipboard): void + { + if ($clipboard->user_id !== Auth::id()) { + abort(403, 'Unauthorized action.'); + } + } +} diff --git a/ProcessMaker/Http/Resources/V1_1/ClipboardResource.php b/ProcessMaker/Http/Resources/V1_1/ClipboardResource.php new file mode 100644 index 0000000000..bbbfafd892 --- /dev/null +++ b/ProcessMaker/Http/Resources/V1_1/ClipboardResource.php @@ -0,0 +1,9 @@ +belongsTo(User::class); + } +} diff --git a/database/factories/ProcessMaker/Models/ClipboardFactory.php b/database/factories/ProcessMaker/Models/ClipboardFactory.php new file mode 100644 index 0000000000..b1d4d5ffe1 --- /dev/null +++ b/database/factories/ProcessMaker/Models/ClipboardFactory.php @@ -0,0 +1,24 @@ + + */ +class ClipboardFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'config' => null, + 'user_id' => 1, + ]; + } +} diff --git a/database/migrations/2024_09_12_163218_create_clipboard_table.php b/database/migrations/2024_09_12_163218_create_clipboard_table.php new file mode 100644 index 0000000000..23ea30fda2 --- /dev/null +++ b/database/migrations/2024_09_12_163218_create_clipboard_table.php @@ -0,0 +1,30 @@ +id(); + $table->unsignedInteger('user_id')->nullable(); + $table->json('config')->nullable(); + $table->timestamps(); + // Foreign keys + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('clipboard'); + } +}; diff --git a/resources/js/processes/screen-builder/screen.vue b/resources/js/processes/screen-builder/screen.vue index 11d6a96274..072553bbbc 100644 --- a/resources/js/processes/screen-builder/screen.vue +++ b/resources/js/processes/screen-builder/screen.vue @@ -716,6 +716,15 @@ export default { ProcessMaker.EventBus.$on("show-create-template-modal", () => { this.$refs["create-template-modal"].show(); }); + ProcessMaker.EventBus.$on( + "save-clipboard", + (items) => { + console.log('save-clipboard',items); + ProcessMaker.apiClient.post('/api/1.1/clipboard/create_or_update', { + config: JSON.stringify(items), + }); + }, + ); }, methods: { ...mapMutations("globalErrorsModule", { setStoreMode: "setMode" }), diff --git a/routes/v1_1/api.php b/routes/v1_1/api.php index fe1b6a01c7..d645fc10a5 100644 --- a/routes/v1_1/api.php +++ b/routes/v1_1/api.php @@ -1,6 +1,7 @@ name('show') - ->middleware(['bindings','can:view,task']); + ->middleware(['bindings', 'can:view,task']); // Route to show the screen of a task Route::get('/{taskId}/screen', [TaskController::class, 'showScreen']) @@ -26,4 +27,16 @@ Route::get('/{taskId}/interstitial', [TaskController::class, 'showInterstitial']) ->name('show.interstitial'); }); + // Clipboard Endpoints + Route::name('clipboard.')->prefix('clipboard')->group(function () { + // Get clipboard by user + Route::get('/get_by_user', [ClipboardController::class, 'showByUserId']) + ->name('user'); + + Route::get('/{clipboard}', [ClipboardController::class, 'show']) + ->name('show'); + + Route::post('/create_or_update', [ClipboardController::class, 'createOrUpdateForUser']) + ->name('clipboard.createOrUpdateForUser'); + }); }); diff --git a/tests/Feature/ClipboardControllerTest.php b/tests/Feature/ClipboardControllerTest.php new file mode 100644 index 0000000000..50e9711750 --- /dev/null +++ b/tests/Feature/ClipboardControllerTest.php @@ -0,0 +1,20 @@ +get('/clipboard'); + + // $response->assertStatus(200); + } +} From 4709eeb3027097a4cbf9e962a0a3baadef49d57b Mon Sep 17 00:00:00 2001 From: Rodrigo Quelca Date: Fri, 13 Sep 2024 13:58:32 -0400 Subject: [PATCH 2/7] retrieve clipboard data --- .../processes/screen-builder/screen.blade.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/resources/views/processes/screen-builder/screen.blade.php b/resources/views/processes/screen-builder/screen.blade.php index 2fb337ab88..91534a61da 100644 --- a/resources/views/processes/screen-builder/screen.blade.php +++ b/resources/views/processes/screen-builder/screen.blade.php @@ -99,6 +99,29 @@ class="border-0 bg-white p-0" console.warn("Screen builder version does not have watchers"); } }); + window.ProcessMaker.EventBus.$on("screen-renderer-init", (screen) => { + + ProcessMaker.apiClient.get('/api/1.1/clipboard/get_by_user') + .then(response => { + if (response && response.data && response.data.config) { + try { + const clipboardData = JSON.parse(response.data.config); + if (clipboardData && typeof clipboardData === 'object') { + screen.$store.dispatch("clipboardModule/addToClipboard", clipboardData); + } else { + console.error("Clipboard data is not in the expected format."); + } + } catch (e) { + console.error("Failed to parse clipboard config data: ", e); + } + } else { + console.error("No valid clipboard config data in response."); + } + }) + .catch(error => { + console.error("Error fetching clipboard data: ", error); + }); + }); window.Processmaker.user = @json($currentUser); From 84bc35233ad89492ab1d3aeecec14721bc86ac84 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelca Date: Fri, 13 Sep 2024 14:29:26 -0400 Subject: [PATCH 3/7] remove incomplete test --- tests/Feature/ClipboardControllerTest.php | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 tests/Feature/ClipboardControllerTest.php diff --git a/tests/Feature/ClipboardControllerTest.php b/tests/Feature/ClipboardControllerTest.php deleted file mode 100644 index 50e9711750..0000000000 --- a/tests/Feature/ClipboardControllerTest.php +++ /dev/null @@ -1,20 +0,0 @@ -get('/clipboard'); - - // $response->assertStatus(200); - } -} From dc8f68b5ae93ffe29dc637f28ae73e76be8e32dc Mon Sep 17 00:00:00 2001 From: Rodrigo Quelca Date: Fri, 13 Sep 2024 16:12:10 -0400 Subject: [PATCH 4/7] promote local storage --- public/images/wizard-icon.svg | 10 ++-- .../processes/screen-builder/screen.blade.php | 57 ++++++++++++++----- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/public/images/wizard-icon.svg b/public/images/wizard-icon.svg index a9e83791b8..a2be69d748 100644 --- a/public/images/wizard-icon.svg +++ b/public/images/wizard-icon.svg @@ -1,7 +1,7 @@ - - - - - + + + + + \ No newline at end of file diff --git a/resources/views/processes/screen-builder/screen.blade.php b/resources/views/processes/screen-builder/screen.blade.php index 91534a61da..1ad18e10d9 100644 --- a/resources/views/processes/screen-builder/screen.blade.php +++ b/resources/views/processes/screen-builder/screen.blade.php @@ -100,28 +100,55 @@ class="border-0 bg-white p-0" } }); window.ProcessMaker.EventBus.$on("screen-renderer-init", (screen) => { - + const savedClipboard = localStorage.getItem("savedClipboard"); + + if (savedClipboard) { + addClipboardToStore(savedClipboard); + return; + } + ProcessMaker.apiClient.get('/api/1.1/clipboard/get_by_user') - .then(response => { - if (response && response.data && response.data.config) { - try { - const clipboardData = JSON.parse(response.data.config); - if (clipboardData && typeof clipboardData === 'object') { - screen.$store.dispatch("clipboardModule/addToClipboard", clipboardData); - } else { - console.error("Clipboard data is not in the expected format."); - } - } catch (e) { - console.error("Failed to parse clipboard config data: ", e); + .then(handleClipboardResponse) + .catch(handleClipboardError); + + /** + * Helper function to add clipboard data to the store + * @param {string|object} clipboardData + */ + function addClipboardToStore(clipboardData) { + try { + const parsedData = typeof clipboardData === 'string' ? JSON.parse(clipboardData) : clipboardData; + if (parsedData && typeof parsedData === 'object') { + screen.$store.dispatch("clipboardModule/addToClipboard", parsedData); + } else { + console.error("Clipboard data is not in the expected format."); } + } catch (error) { + console.error("Failed to parse clipboard data: ", error); + } + } + + /** + * Handle clipboard API response + * @param {object} response + */ + function handleClipboardResponse(response) { + if (response && response.data && response.data.config) { + addClipboardToStore(response.data.config); } else { console.error("No valid clipboard config data in response."); } - }) - .catch(error => { + } + + /** + * Handle clipboard API error + * @param {Error} error + */ + function handleClipboardError(error) { console.error("Error fetching clipboard data: ", error); - }); + } }); + window.Processmaker.user = @json($currentUser); From 8ef73317157f4562d23f653a48a4821b736c1cac Mon Sep 17 00:00:00 2001 From: Rodrigo Quelca Date: Fri, 13 Sep 2024 16:18:52 -0400 Subject: [PATCH 5/7] restore wizard icon --- public/images/wizard-icon.svg | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/images/wizard-icon.svg b/public/images/wizard-icon.svg index a2be69d748..a9e83791b8 100644 --- a/public/images/wizard-icon.svg +++ b/public/images/wizard-icon.svg @@ -1,7 +1,7 @@ - - - - - + + + + + \ No newline at end of file From f8e883f8bc17719c38e1f71c38a5b86e39c5cf21 Mon Sep 17 00:00:00 2001 From: Rodrigo Quelca Date: Fri, 13 Sep 2024 16:59:57 -0400 Subject: [PATCH 6/7] fix CR notes --- .../migrations/2024_09_12_163218_create_clipboard_table.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/migrations/2024_09_12_163218_create_clipboard_table.php b/database/migrations/2024_09_12_163218_create_clipboard_table.php index 23ea30fda2..7814e5d8f2 100644 --- a/database/migrations/2024_09_12_163218_create_clipboard_table.php +++ b/database/migrations/2024_09_12_163218_create_clipboard_table.php @@ -13,7 +13,8 @@ public function up(): void Schema::create('clipboards', function (Blueprint $table) { $table->id(); $table->unsignedInteger('user_id')->nullable(); - $table->json('config')->nullable(); + $table->mediumText('config'); + $table->string('type', 20)->default('FORM'); $table->timestamps(); // Foreign keys $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); From 642c97774c259b877419759d5baa2ca2152eb8aa Mon Sep 17 00:00:00 2001 From: Rodrigo Quelca Date: Fri, 13 Sep 2024 17:02:08 -0400 Subject: [PATCH 7/7] update model and factory --- ProcessMaker/Models/Clipboard.php | 1 + database/factories/ProcessMaker/Models/ClipboardFactory.php | 1 + 2 files changed, 2 insertions(+) diff --git a/ProcessMaker/Models/Clipboard.php b/ProcessMaker/Models/Clipboard.php index 1e5518e4f6..e6754e75e6 100644 --- a/ProcessMaker/Models/Clipboard.php +++ b/ProcessMaker/Models/Clipboard.php @@ -12,6 +12,7 @@ class Clipboard extends ProcessMakerModel protected $fillable = [ 'config', 'user_id', + 'type', ]; /** diff --git a/database/factories/ProcessMaker/Models/ClipboardFactory.php b/database/factories/ProcessMaker/Models/ClipboardFactory.php index b1d4d5ffe1..c37175dfa9 100644 --- a/database/factories/ProcessMaker/Models/ClipboardFactory.php +++ b/database/factories/ProcessMaker/Models/ClipboardFactory.php @@ -19,6 +19,7 @@ public function definition(): array return [ 'config' => null, 'user_id' => 1, + 'type' => 'FORM', ]; } }