collavre_notion 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d998584e2f1dab6b3fc6232c70f5990d7d1f5e832e2b82d88d20ae3831adbb7
4
- data.tar.gz: 807c15cac07e50ecd25ff7f7ad308dd550693d20a514c012c6460b24df0e290f
3
+ metadata.gz: 79c6d353c5d4bb7cceebb0f7d4ed7f1f4a3c81be1df39305aa4ecced9a305f75
4
+ data.tar.gz: 49d5d8b36ac8fd0ff3e03d32ff58bb8c5395dbea3cc1974db95b1b4d7a0aeb16
5
5
  SHA512:
6
- metadata.gz: 3ca865c81a3beffe888deaed1f252c301502416e669bdade8d4506deb1566bc057fd2afcffa4015f38c8de7c2bf0f23c182d756db8729a98b52795da22a1fb24
7
- data.tar.gz: a81bcbf00b5bc22e524c37967a6a54af0ac54d90e149f8fabcee5137f9130801ce9fa0791f805594c21587346fae51790ffa53b6ad5e87b781350bc3d6764023
6
+ metadata.gz: 6a69db91cc2c0694d5925f349c414880fed6d69df2cb4e6a8855228a125fc975b9ae6065ca88700082b2da8c5e999e1fa5d9d199134b781d3704037301c36063
7
+ data.tar.gz: 88c045ced22013630fab5ab674bc6c3e81de16038c51b4d39a42ad23ee0a83f2699609e8dbe873774729d1a92bbf520ba2124d5a7e7bc2b7f720a0979e1841fa
@@ -1,6 +1,9 @@
1
1
  module CollavreNotion
2
2
  module Creatives
3
3
  class NotionIntegrationsController < ApplicationController
4
+ include Collavre::IntegrationSetup
5
+ include Collavre::IntegrationPermission
6
+
4
7
  before_action :set_creative
5
8
  before_action :ensure_read_permission
6
9
  before_action :ensure_admin_permission, only: [ :show, :update ]
@@ -78,7 +81,7 @@ module CollavreNotion
78
81
 
79
82
  def destroy
80
83
  unless @creative.has_permission?(Current.user, :write)
81
- render json: { error: "forbidden" }, status: :forbidden
84
+ render json: { error: integration_forbidden_message }, status: :forbidden
82
85
  return
83
86
  end
84
87
 
@@ -120,22 +123,6 @@ module CollavreNotion
120
123
 
121
124
  private
122
125
 
123
- def set_creative
124
- @creative = Collavre::Creative.find(params[:creative_id])
125
- end
126
-
127
- def ensure_read_permission
128
- return if @creative.has_permission?(Current.user, :read)
129
-
130
- render json: { error: "forbidden" }, status: :forbidden
131
- end
132
-
133
- def ensure_admin_permission
134
- return if @creative.has_permission?(Current.user, :admin)
135
-
136
- render json: { error: "forbidden" }, status: :forbidden
137
- end
138
-
139
126
  def linked_page_links(account)
140
127
  return CollavreNotion::NotionPageLink.none unless account
141
128
 
@@ -22,24 +22,8 @@ module CollavreNotion
22
22
  # If opened in popup, close it and notify parent window
23
23
  if params[:popup] || request.referer&.include?("popup=true") || session[:oauth_popup]
24
24
  session.delete(:oauth_popup)
25
- render html: <<-HTML.html_safe
26
- <!DOCTYPE html>
27
- <html>
28
- <head><title>Notion Connected</title></head>
29
- <body>
30
- <script>
31
- if (window.opener) {
32
- window.opener.postMessage({ type: 'notion_oauth_success', connected: true }, '*');
33
- window.close();
34
- } else {
35
- window.location.href = '#{collavre.creatives_path}';
36
- }
37
- </script>
38
- <p>#{I18n.t("collavre_notion.notion_auth.connected")}</p>
39
- <p>This window should close automatically. If not, you can close it manually.</p>
40
- </body>
41
- </html>
42
- HTML
25
+ @fallback_path = collavre.creatives_path
26
+ render template: "collavre_notion/notion_auth/callback_success", layout: false
43
27
  else
44
28
  redirect_to collavre.creatives_path, notice: I18n.t("collavre_notion.notion_auth.connected")
45
29
  end
@@ -1,3 +1,5 @@
1
+ import { csrfToken, showError, clearError, updateStepVisibility, openOAuthPopup, fetchWithCsrf, setupModalClose } from 'collavre/modules/integration_wizard';
2
+
1
3
  let notionIntegrationInitialized = false;
2
4
 
3
5
  if (!notionIntegrationInitialized) {
@@ -43,10 +45,6 @@ if (!notionIntegrationInitialized) {
43
45
  let exportType = 'new-page';
44
46
  let selectedParentPage = null;
45
47
 
46
- function csrfToken() {
47
- return document.querySelector('meta[name="csrf-token"]')?.content;
48
- }
49
-
50
48
  function resetWizard() {
51
49
  currentStep = 'connect';
52
50
  hasExistingIntegration = false;
@@ -55,8 +53,7 @@ if (!notionIntegrationInitialized) {
55
53
  exportType = 'new-page';
56
54
  selectedParentPage = null;
57
55
  statusEl.textContent = '';
58
- errorEl.style.display = 'none';
59
- errorEl.textContent = '';
56
+ clearError(errorEl);
60
57
  if (existingContainer) {
61
58
  existingContainer.style.display = 'none';
62
59
  }
@@ -74,12 +71,7 @@ if (!notionIntegrationInitialized) {
74
71
  }
75
72
 
76
73
  function updateStep() {
77
- ['connect', 'workspace', 'summary']
78
- .forEach(function (step) {
79
- const el = document.getElementById(`notion-step-${step}`);
80
- if (!el) return;
81
- el.style.display = (step === currentStep) ? 'block' : 'none';
82
- });
74
+ updateStepVisibility(currentStep, ['connect', 'workspace', 'summary'], 'notion-step');
83
75
 
84
76
  if (currentStep === 'connect') {
85
77
  prevBtn.style.display = 'none';
@@ -105,29 +97,13 @@ if (!notionIntegrationInitialized) {
105
97
  }
106
98
  }
107
99
 
108
- function showError(message) {
109
- errorEl.textContent = message;
110
- errorEl.style.display = 'block';
111
- }
112
-
113
- function clearError() {
114
- errorEl.style.display = 'none';
115
- errorEl.textContent = '';
116
- }
117
-
118
100
  function loadIntegrationStatus() {
119
101
  if (!creativeId) return;
120
102
 
121
103
  statusEl.textContent = modal.dataset.loading;
122
- clearError();
104
+ clearError(errorEl);
123
105
 
124
- fetch(`/notion/creatives/${creativeId}/notion_integration`, {
125
- method: 'GET',
126
- headers: {
127
- 'X-CSRF-Token': csrfToken(),
128
- 'Content-Type': 'application/json'
129
- }
130
- })
106
+ fetchWithCsrf(`/notion/creatives/${creativeId}/notion_integration`)
131
107
  .then(response => response.json())
132
108
  .then(data => {
133
109
  console.log('Notion integration status:', data);
@@ -168,7 +144,7 @@ if (!notionIntegrationInitialized) {
168
144
  .catch(error => {
169
145
  console.error('Error loading integration status:', error);
170
146
  statusEl.textContent = '';
171
- showError(modal.dataset.loadFailed);
147
+ showError(errorEl, modal.dataset.loadFailed);
172
148
  });
173
149
  }
174
150
 
@@ -285,13 +261,13 @@ if (!notionIntegrationInitialized) {
285
261
 
286
262
  function performExport() {
287
263
  if (!creativeId) {
288
- showError(modal.dataset.noCreative);
264
+ showError(errorEl, modal.dataset.noCreative);
289
265
  return;
290
266
  }
291
267
 
292
268
  exportBtn.disabled = true;
293
269
  exportBtn.textContent = modal.dataset.exporting;
294
- clearError();
270
+ clearError(errorEl);
295
271
 
296
272
  const requestData = {
297
273
  action: 'export'
@@ -304,13 +280,9 @@ if (!notionIntegrationInitialized) {
304
280
  console.log('Sending export request:', requestData);
305
281
  console.log('Export type:', exportType, 'Selected parent page:', selectedParentPage);
306
282
 
307
- fetch(`/notion/creatives/${creativeId}/notion_integration`, {
283
+ fetchWithCsrf(`/notion/creatives/${creativeId}/notion_integration`, {
308
284
  method: 'PATCH',
309
- headers: {
310
- 'X-CSRF-Token': csrfToken(),
311
- 'Content-Type': 'application/json'
312
- },
313
- body: JSON.stringify(requestData)
285
+ body: requestData
314
286
  })
315
287
  .then(response => response.json())
316
288
  .then(data => {
@@ -322,12 +294,12 @@ if (!notionIntegrationInitialized) {
322
294
  resetWizard();
323
295
  }, 2000);
324
296
  } else {
325
- showError(data.message || modal.dataset.exportFailed);
297
+ showError(errorEl, data.message || modal.dataset.exportFailed);
326
298
  }
327
299
  })
328
300
  .catch(error => {
329
301
  console.error('Export error:', error);
330
- showError(modal.dataset.exportFailed);
302
+ showError(errorEl, modal.dataset.exportFailed);
331
303
  })
332
304
  .finally(() => {
333
305
  exportBtn.disabled = false;
@@ -340,15 +312,11 @@ if (!notionIntegrationInitialized) {
340
312
 
341
313
  syncBtn.disabled = true;
342
314
  syncBtn.textContent = modal.dataset.syncing;
343
- clearError();
315
+ clearError(errorEl);
344
316
 
345
- fetch(`/notion/creatives/${creativeId}/notion_integration`, {
317
+ fetchWithCsrf(`/notion/creatives/${creativeId}/notion_integration`, {
346
318
  method: 'PATCH',
347
- headers: {
348
- 'X-CSRF-Token': csrfToken(),
349
- 'Content-Type': 'application/json'
350
- },
351
- body: JSON.stringify({ action: 'sync' })
319
+ body: { action: 'sync' }
352
320
  })
353
321
  .then(response => response.json())
354
322
  .then(data => {
@@ -356,12 +324,12 @@ if (!notionIntegrationInitialized) {
356
324
  statusEl.textContent = modal.dataset.syncSuccess || 'Sync completed successfully';
357
325
  statusEl.style.color = 'green';
358
326
  } else {
359
- showError(data.message || modal.dataset.syncFailed);
327
+ showError(errorEl, data.message || modal.dataset.syncFailed);
360
328
  }
361
329
  })
362
330
  .catch(error => {
363
331
  console.error('Sync error:', error);
364
- showError(modal.dataset.syncFailed);
332
+ showError(errorEl, modal.dataset.syncFailed);
365
333
  })
366
334
  .finally(() => {
367
335
  syncBtn.disabled = false;
@@ -374,15 +342,9 @@ if (!notionIntegrationInitialized) {
374
342
 
375
343
  deleteBtn.disabled = true;
376
344
  deleteBtn.textContent = modal.dataset.removing;
377
- clearError();
345
+ clearError(errorEl);
378
346
 
379
- fetch(`/notion/creatives/${creativeId}/notion_integration`, {
380
- method: 'DELETE',
381
- headers: {
382
- 'X-CSRF-Token': csrfToken(),
383
- 'Content-Type': 'application/json'
384
- }
385
- })
347
+ fetchWithCsrf(`/notion/creatives/${creativeId}/notion_integration`, { method: 'DELETE' })
386
348
  .then(response => response.json())
387
349
  .then(data => {
388
350
  if (data.success) {
@@ -393,12 +355,12 @@ if (!notionIntegrationInitialized) {
393
355
  resetWizard();
394
356
  }, 2000);
395
357
  } else {
396
- showError(data.message || modal.dataset.deleteFailed);
358
+ showError(errorEl, data.message || modal.dataset.deleteFailed);
397
359
  }
398
360
  })
399
361
  .catch(error => {
400
362
  console.error('Delete error:', error);
401
- showError(modal.dataset.deleteFailed);
363
+ showError(errorEl, modal.dataset.deleteFailed);
402
364
  })
403
365
  .finally(() => {
404
366
  deleteBtn.disabled = false;
@@ -418,10 +380,7 @@ if (!notionIntegrationInitialized) {
418
380
  loadIntegrationStatus();
419
381
  });
420
382
 
421
- closeBtn.addEventListener('click', function () {
422
- modal.style.display = 'none';
423
- resetWizard();
424
- });
383
+ setupModalClose(modal, closeBtn, resetWizard);
425
384
 
426
385
  // Listen for OAuth success message from popup window
427
386
  window.addEventListener('message', function(event) {
@@ -436,26 +395,19 @@ if (!notionIntegrationInitialized) {
436
395
 
437
396
  loginBtn.addEventListener('click', function () {
438
397
  console.log('Notion login button clicked');
439
- const width = parseInt(this.dataset.windowWidth) || 600;
440
- const height = parseInt(this.dataset.windowHeight) || 700;
441
- const left = (screen.width - width) / 2;
442
- const top = (screen.height - height) / 2;
398
+ const authWindow = openOAuthPopup('notion-auth-window', {
399
+ width: parseInt(this.dataset.windowWidth) || 600,
400
+ height: parseInt(this.dataset.windowHeight) || 700,
401
+ onClose: function () {
402
+ console.log('Auth window closed, reloading integration status');
403
+ loadIntegrationStatus();
404
+ }
405
+ });
443
406
 
444
- const authWindow = window.open('', 'notion-auth-window',
445
- `width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`);
446
-
447
407
  if (authWindow) {
448
408
  loginForm.target = 'notion-auth-window';
449
409
  loginForm.submit();
450
410
  console.log('Auth form submitted to popup window');
451
-
452
- const checkClosed = setInterval(() => {
453
- if (authWindow.closed) {
454
- clearInterval(checkClosed);
455
- console.log('Auth window closed, reloading integration status');
456
- setTimeout(() => loadIntegrationStatus(), 1000);
457
- }
458
- }, 1000);
459
411
  } else {
460
412
  loginForm.target = '_blank';
461
413
  loginForm.submit();
@@ -472,7 +424,7 @@ if (!notionIntegrationInitialized) {
472
424
  });
473
425
 
474
426
  nextBtn.addEventListener('click', function () {
475
- clearError();
427
+ clearError(errorEl);
476
428
  if (currentStep === 'connect') {
477
429
  currentStep = 'workspace';
478
430
  updateParentPageOptions();
@@ -501,11 +453,5 @@ if (!notionIntegrationInitialized) {
501
453
  });
502
454
  }
503
455
 
504
- modal.addEventListener('click', function (e) {
505
- if (e.target === modal) {
506
- modal.style.display = 'none';
507
- resetWizard();
508
- }
509
- });
510
456
  });
511
457
  }
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head><title>Notion Connected</title></head>
4
+ <body>
5
+ <script>
6
+ if (window.opener) {
7
+ window.opener.postMessage({ type: 'notion_oauth_success', connected: true }, window.location.origin);
8
+ window.close();
9
+ } else {
10
+ window.location.href = '<%= @fallback_path %>';
11
+ }
12
+ </script>
13
+ <p><%= t("collavre_notion.notion_auth.connected") %></p>
14
+ <p>This window should close automatically. If not, you can close it manually.</p>
15
+ </body>
16
+ </html>
@@ -1,3 +1,3 @@
1
1
  module CollavreNotion
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collavre_notion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Collavre
@@ -74,6 +74,7 @@ files:
74
74
  - app/services/collavre_notion/notion_creative_exporter.rb
75
75
  - app/services/collavre_notion/notion_service.rb
76
76
  - app/views/collavre_notion/integrations/_modal.html.erb
77
+ - app/views/collavre_notion/notion_auth/callback_success.html.erb
77
78
  - config/locales/en.yml
78
79
  - config/locales/ko.yml
79
80
  - config/routes.rb