ultimate-jekyll-manager 0.0.270 → 0.0.271

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.
@@ -64,21 +64,38 @@
64
64
  }
65
65
  }
66
66
 
67
- // Delete account section styles
67
+ // Shared styles for data-request and delete sections
68
+ #data-request-section,
68
69
  #delete-section {
69
- .card.border-danger {
70
+ .card-form-border {
70
71
  border-width: 2px;
71
72
  }
72
73
 
74
+ .form-check-label {
75
+ user-select: none;
76
+ }
77
+
78
+ .accordion-trigger {
79
+ background: none;
80
+ border: none;
81
+ padding: 0;
82
+ font: inherit;
83
+ color: inherit;
84
+ cursor: pointer;
85
+
86
+ &:focus {
87
+ outline: none;
88
+ }
89
+ }
90
+ }
91
+
92
+ // Delete account section styles
93
+ #delete-section {
73
94
  #delete-account-btn {
74
95
  &:disabled {
75
96
  opacity: 0.5;
76
97
  cursor: not-allowed;
77
98
  }
78
99
  }
79
-
80
- .form-check-label {
81
- user-select: none;
82
- }
83
100
  }
84
101
 
@@ -114,6 +114,7 @@ export default function (Manager) {
114
114
  formManager = new FormManager('#auth-form', {
115
115
  autoReady: false, // We'll call ready() after checking redirect result
116
116
  allowResubmit: false,
117
+ warnOnUnsavedChanges: false,
117
118
  submittingText: 'Signing in...',
118
119
  submittedText: 'Signed In!',
119
120
  });
@@ -128,6 +129,7 @@ export default function (Manager) {
128
129
  formManager = new FormManager('#auth-form', {
129
130
  autoReady: false, // We'll call ready() after checking redirect result
130
131
  allowResubmit: false,
132
+ warnOnUnsavedChanges: false,
131
133
  submittingText: 'Creating account...',
132
134
  submittedText: 'Account Created!',
133
135
  });
@@ -142,6 +144,7 @@ export default function (Manager) {
142
144
  formManager = new FormManager('#auth-form', {
143
145
  autoReady: false, // We'll call ready() after checking redirect result
144
146
  allowResubmit: false,
147
+ warnOnUnsavedChanges: false,
145
148
  submittingText: 'Sending...',
146
149
  submittedText: 'Email Sent!',
147
150
  });
@@ -8,6 +8,7 @@ import * as teamSection from './sections/team.js';
8
8
  import * as referralsSection from './sections/referrals.js';
9
9
  import * as apiKeysSection from './sections/api-keys.js';
10
10
  import * as deleteSection from './sections/delete.js';
11
+ import * as dataRequestSection from './sections/data-request.js';
11
12
  import * as connectionsSection from './sections/connections.js';
12
13
  let webManager = null;
13
14
 
@@ -47,6 +48,7 @@ const sectionModules = {
47
48
  referrals: referralsSection,
48
49
  'api-keys': apiKeysSection,
49
50
  delete: deleteSection,
51
+ 'data-request': dataRequestSection,
50
52
  connections: connectionsSection
51
53
  };
52
54
 
@@ -62,10 +64,13 @@ async function initializeAccount() {
62
64
  // Setup navigation
63
65
  setupNavigation();
64
66
 
65
- // Check if delete hash is present on initial load
67
+ // Check if delete/data-request hash is present on initial load
66
68
  if (window.location.hash === '#delete') {
67
69
  showDeleteOption();
68
70
  }
71
+ if (window.location.hash === '#data-request') {
72
+ showDataRequestOption();
73
+ }
69
74
 
70
75
  // Initialize all section modules
71
76
  Object.values(sectionModules).forEach(module => {
@@ -180,6 +185,10 @@ function loadAllSectionData(authState) {
180
185
  sectionModules.delete.loadData(account);
181
186
  }
182
187
 
188
+ if (sectionModules['data-request'] && sectionModules['data-request'].loadData) {
189
+ sectionModules['data-request'].loadData(account);
190
+ }
191
+
183
192
  if (sectionModules.connections.loadData) {
184
193
  sectionModules.connections.loadData(account, appData);
185
194
  }
@@ -219,10 +228,13 @@ function setupNavigation() {
219
228
  function handleHashChange() {
220
229
  const hash = window.location.hash.slice(1);
221
230
 
222
- // Show delete nav item and mobile option if hash is delete
231
+ // Show hidden nav items based on hash
223
232
  if (hash === 'delete') {
224
233
  showDeleteOption();
225
234
  }
235
+ if (hash === 'data-request') {
236
+ showDataRequestOption();
237
+ }
226
238
 
227
239
  if (hash) {
228
240
  // Check if the section exists
@@ -264,6 +276,27 @@ function showDeleteOption() {
264
276
  }
265
277
  }
266
278
 
279
+ // Show data request option in navigation
280
+ function showDataRequestOption() {
281
+ // Show desktop nav item
282
+ const $dataRequestNavItem = document.getElementById('data-request-nav-item');
283
+ if ($dataRequestNavItem) {
284
+ $dataRequestNavItem.classList.remove('d-none');
285
+ }
286
+
287
+ // Add mobile dropdown option if not exists
288
+ const $mobileNavSelect = document.getElementById('mobile-nav-select');
289
+ if ($mobileNavSelect) {
290
+ const dataRequestOption = $mobileNavSelect.querySelector('option[value="data-request"]');
291
+ if (!dataRequestOption) {
292
+ const option = document.createElement('option');
293
+ option.value = 'data-request';
294
+ option.textContent = 'Data Request';
295
+ $mobileNavSelect.appendChild(option);
296
+ }
297
+ }
298
+ }
299
+
267
300
  // Hide all sections except loading
268
301
  function hideAllSectionsExceptLoading() {
269
302
  $sections.forEach(section => {
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Data Request Section JavaScript
3
+ */
4
+
5
+ // Libraries
6
+ import { FormManager } from '__main_assets__/js/libs/form-manager.js';
7
+ import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
8
+
9
+ let webManager = null;
10
+ let formManager = null;
11
+ let downloadFormManager = null;
12
+ let cancelFormManager = null;
13
+
14
+ // Initialize data-request section
15
+ export async function init(wm) {
16
+ webManager = wm;
17
+ setupDataRequestForm();
18
+ setupDownloadButton();
19
+ setupCancelButton();
20
+ }
21
+
22
+ // Setup data request form
23
+ function setupDataRequestForm() {
24
+ const $form = document.getElementById('data-request-form');
25
+ const $checkbox1 = document.getElementById('data-request-confirm-checkbox');
26
+ const $checkbox2 = document.getElementById('data-request-deletion-checkbox');
27
+ const $submitBtn = document.getElementById('data-request-submit-btn');
28
+
29
+ if (!$form || !$checkbox1 || !$checkbox2 || !$submitBtn) {
30
+ return;
31
+ }
32
+
33
+ formManager = new FormManager('#data-request-form', {
34
+ allowResubmit: false,
35
+ submittingText: 'Submitting request...',
36
+ submittedText: 'Request Submitted',
37
+ });
38
+
39
+ // Enable/disable submit button based on both checkboxes
40
+ function updateSubmitState() {
41
+ $submitBtn.disabled = !($checkbox1.checked && $checkbox2.checked);
42
+ }
43
+
44
+ $checkbox1.addEventListener('change', updateSubmitState);
45
+ $checkbox2.addEventListener('change', updateSubmitState);
46
+
47
+ formManager.on('submit', async ({ data }) => {
48
+ if (!$checkbox1.checked || !$checkbox2.checked) {
49
+ throw new Error('Please confirm all acknowledgments before submitting.');
50
+ }
51
+
52
+ trackDataRequest('submit');
53
+
54
+ const response = await authorizedFetch(`${webManager.getApiUrl()}/backend-manager/user/data-request`, {
55
+ method: 'POST',
56
+ timeout: 30000,
57
+ response: 'json',
58
+ tries: 2,
59
+ body: {
60
+ confirmed: true,
61
+ reason: data.reason || '',
62
+ },
63
+ });
64
+
65
+ if (response.error) {
66
+ throw new Error(response.message || 'Failed to submit data request. Please try again later.');
67
+ }
68
+
69
+ formManager.showSuccess('Your data request has been submitted. Please check back in up to 14 business days.');
70
+
71
+ showRequestStatus(response.request);
72
+ });
73
+ }
74
+
75
+ // Setup download form
76
+ function setupDownloadButton() {
77
+ const $form = document.getElementById('data-request-download-form');
78
+
79
+ if (!$form) {
80
+ return;
81
+ }
82
+
83
+ downloadFormManager = new FormManager('#data-request-download-form', {
84
+ allowResubmit: false,
85
+ submittingText: 'Downloading...',
86
+ submittedText: 'Downloaded!',
87
+ });
88
+
89
+ downloadFormManager.on('submit', async () => {
90
+ const response = await authorizedFetch(`${webManager.getApiUrl()}/backend-manager/user/data-request?action=download`, {
91
+ method: 'GET',
92
+ timeout: 60000,
93
+ response: 'json',
94
+ });
95
+
96
+ if (response.error || !response.data) {
97
+ throw new Error(response.message || 'Failed to download data.');
98
+ }
99
+
100
+ trackDataRequest('download');
101
+
102
+ // Trigger file download
103
+ const blob = new Blob([JSON.stringify(response.data, null, 2)], { type: 'application/json' });
104
+ const url = URL.createObjectURL(blob);
105
+ const $a = document.createElement('a');
106
+ $a.href = url;
107
+ $a.download = `my-data-${new Date().toISOString().split('T')[0]}.json`;
108
+ document.body.appendChild($a);
109
+ $a.click();
110
+ document.body.removeChild($a);
111
+ URL.revokeObjectURL(url);
112
+ });
113
+ }
114
+
115
+ // Setup cancel form
116
+ function setupCancelButton() {
117
+ const $form = document.getElementById('data-request-cancel-form');
118
+
119
+ if (!$form) {
120
+ return;
121
+ }
122
+
123
+ cancelFormManager = new FormManager('#data-request-cancel-form', {
124
+ allowResubmit: true,
125
+ submittingText: 'Withdrawing...',
126
+ });
127
+
128
+ cancelFormManager.on('submit', async () => {
129
+ const response = await authorizedFetch(`${webManager.getApiUrl()}/backend-manager/user/data-request`, {
130
+ method: 'DELETE',
131
+ timeout: 30000,
132
+ response: 'json',
133
+ });
134
+
135
+ if (response.error) {
136
+ throw new Error(response.message || 'Failed to withdraw request.');
137
+ }
138
+
139
+ trackDataRequest('cancel');
140
+
141
+ showRequestForm();
142
+ });
143
+ }
144
+
145
+ // Load data (called when auth state resolves)
146
+ export async function loadData() {
147
+ // Status is checked lazily in onShow
148
+ }
149
+
150
+ // Called when section is shown
151
+ export async function onShow() {
152
+ await checkRequestStatus();
153
+ }
154
+
155
+ // Check request status from backend
156
+ async function checkRequestStatus() {
157
+ try {
158
+ const response = await authorizedFetch(`${webManager.getApiUrl()}/backend-manager/user/data-request`, {
159
+ method: 'GET',
160
+ timeout: 30000,
161
+ response: 'json',
162
+ });
163
+
164
+ if (response.request) {
165
+ showRequestStatus(response.request);
166
+ } else {
167
+ showRequestForm();
168
+ }
169
+ } catch (error) {
170
+ // No active request or error — show form
171
+ showRequestForm();
172
+ }
173
+ }
174
+
175
+ // Show request status UI
176
+ function showRequestStatus(request) {
177
+ const $status = document.getElementById('data-request-status');
178
+ const $statusTitle = document.getElementById('data-request-status-title');
179
+ const $statusMessage = document.getElementById('data-request-status-message');
180
+ const $download = document.getElementById('data-request-download');
181
+ const $cancel = document.getElementById('data-request-cancel');
182
+ const $formContainer = document.getElementById('data-request-form-container');
183
+ const $formTrigger = document.getElementById('data-request-form-trigger');
184
+
185
+ if (!$status) {
186
+ return;
187
+ }
188
+
189
+ if (request.status === 'expired') {
190
+ showRequestForm();
191
+ return;
192
+ }
193
+
194
+ $status.classList.remove('d-none');
195
+ $formContainer.classList.add('d-none');
196
+ if ($formTrigger) {
197
+ $formTrigger.classList.add('d-none');
198
+ }
199
+
200
+ const createdDate = new Date(request.createdAt).toLocaleDateString();
201
+
202
+ if (request.status === 'complete') {
203
+ $statusTitle.textContent = 'Your data is ready';
204
+ $statusMessage.textContent = `Your data request from ${createdDate} has been processed. Click below to download your data package. This download will expire 30 days after your data became available.`;
205
+ $download.classList.remove('d-none');
206
+ if (downloadFormManager) {
207
+ downloadFormManager.reset();
208
+ }
209
+ $cancel.classList.add('d-none');
210
+
211
+ trackDataRequest('download_available');
212
+ } else {
213
+ $statusTitle.textContent = 'Request pending';
214
+ $statusMessage.textContent = `Your data request was submitted on ${createdDate}. Processing may take up to 14 business days. Please check back later.`;
215
+ $download.classList.add('d-none');
216
+ $cancel.classList.remove('d-none');
217
+ if (cancelFormManager) {
218
+ cancelFormManager.reset();
219
+ }
220
+ }
221
+ }
222
+
223
+ // Show request form UI
224
+ function showRequestForm() {
225
+ const $status = document.getElementById('data-request-status');
226
+ const $formContainer = document.getElementById('data-request-form-container');
227
+ const $formTrigger = document.getElementById('data-request-form-trigger');
228
+
229
+ if (!$status || !$formContainer) {
230
+ return;
231
+ }
232
+
233
+ $status.classList.add('d-none');
234
+ $formContainer.classList.remove('d-none');
235
+ if ($formTrigger) {
236
+ $formTrigger.classList.remove('d-none');
237
+ }
238
+ }
239
+
240
+ // Tracking
241
+ function trackDataRequest(action) {
242
+ gtag('event', 'data_request', {
243
+ action: action,
244
+ });
245
+ fbq('trackCustom', 'DataRequest', {
246
+ action: action,
247
+ });
248
+ ttq.track('ViewContent', {
249
+ content_id: `data-request-${action}`,
250
+ content_type: 'product',
251
+ content_name: `Data Request ${action}`,
252
+ });
253
+ }
@@ -18,11 +18,11 @@ export async function init(wm) {
18
18
  // Setup delete account form
19
19
  function setupDeleteAccountForm() {
20
20
  const $form = document.getElementById('delete-account-form');
21
- const $checkbox = document.getElementById('delete-confirm-checkbox');
21
+ const $checkbox1 = document.getElementById('delete-confirm-checkbox');
22
+ const $checkbox2 = document.getElementById('delete-data-request-checkbox');
22
23
  const $deleteBtn = document.getElementById('delete-account-btn');
23
- const $cancelBtn = document.getElementById('cancel-delete-btn');
24
24
 
25
- if (!$form || !$checkbox || !$deleteBtn) {
25
+ if (!$form || !$checkbox1 || !$checkbox2 || !$deleteBtn) {
26
26
  return;
27
27
  }
28
28
 
@@ -32,23 +32,18 @@ function setupDeleteAccountForm() {
32
32
  submittedText: 'Account Deleted!',
33
33
  });
34
34
 
35
- // Enable/disable delete button based on checkbox
36
- $checkbox.addEventListener('change', (e) => {
37
- $deleteBtn.disabled = !e.target.checked;
38
- });
39
-
40
- // Handle cancel button
41
- if ($cancelBtn) {
42
- $cancelBtn.addEventListener('click', () => {
43
- // Navigate back to profile
44
- window.location.hash = 'profile';
45
- });
35
+ // Enable/disable delete button based on both checkboxes
36
+ function updateDeleteState() {
37
+ $deleteBtn.disabled = !($checkbox1.checked && $checkbox2.checked);
46
38
  }
47
39
 
40
+ $checkbox1.addEventListener('change', updateDeleteState);
41
+ $checkbox2.addEventListener('change', updateDeleteState);
42
+
48
43
  formManager.on('submit', async ({ data }) => {
49
- // Check if checkbox is checked
50
- if (!$checkbox.checked) {
51
- throw new Error('Please confirm that you understand this action is permanent.');
44
+ // Check if both checkboxes are checked
45
+ if (!$checkbox1.checked || !$checkbox2.checked) {
46
+ throw new Error('Please confirm all acknowledgments before proceeding.');
52
47
  }
53
48
 
54
49
  // 1ms wait for dialog to appear properly
@@ -70,6 +70,7 @@ In the event of a change of control, if we sell or otherwise transfer part or th
70
70
 
71
71
  ### Your Choices About Your Information:
72
72
  Your account information and profile privacy settings can be updated or changed by visiting your account profile at [{{ site.url }}/account]({{ site.url }}/account) or by contacting {{ brand }} directly at [{{ site.url }}/contact]({{ site.url }}/contact).
73
+ - You may request a copy of your personal data by visiting [{{ site.url }}/account]({{ site.url }}/account#data-request).
73
74
  - You may request to have your account deleted by visiting your account profile at [{{ site.url }}/account]({{ site.url }}/account#delete).
74
75
  - You may request to unsubscribe from emails by clicking the "unsubscribe" link inside the email.
75
76
 
@@ -80,7 +81,27 @@ When it comes to the collection of personal information from children under 13,
80
81
  By opting in to receive SMS communications from {{ brand }}, you agree to receive marketing text messages, such as promotions and cart reminders, from us. Consent to receive marketing text messages is not a condition of any purchase. Message and data rates may apply, and the frequency of messages may vary. You may unsubscribe from receiving SMS messages at any time by replying “STOP” to any message or by clicking the unsubscribe link provided in our communications. For more information about our privacy practices, please refer to this Privacy Policy or our [Terms of Service]({{ site.url }}/terms/).
81
82
 
82
83
  ## Request Your Data to Be Removed or Deleted
83
- If you would like your personally identifiable information to be deleted from our database, you can request deletion here: [{{ site.url }}/account]({{ site.url }}/account#delete).
84
+ Under applicable data protection regulations including the General Data Protection Regulation (GDPR) and the California Consumer Privacy Act (CCPA), you have the right to request the deletion of the personal data we hold about you. Account deletion is a permanent, irreversible action that cannot be undone under any circumstances.
85
+
86
+ To request deletion, visit your account page here: [{{ site.url }}/account]({{ site.url }}/account#delete). Please read the following important information before proceeding:
87
+
88
+ * **Permanent and irreversible:** Once your account is deleted, all personal data associated with your account will be permanently removed from our systems. This includes your profile information, subscription and billing history, activity logs, API keys, OAuth2 connections, referral data, and all other stored information. There is no mechanism to recover any data after deletion.
89
+ * **Active subscriptions:** You must cancel any active paid subscriptions before deleting your account. Accounts with active or suspended paid subscriptions cannot be deleted until the subscription is cancelled or expires.
90
+ * **Pending data requests:** If you have a pending data request (Subject Access Request), it will be permanently cancelled upon account deletion. We will not be able to fulfill data requests after your account has been deleted. If you wish to obtain a copy of your data, you must complete the data request and download process **before** initiating account deletion.
91
+ * **Immediate effect:** Account deletion takes effect immediately. You will be signed out of all active sessions and will no longer be able to access any services associated with your account.
92
+ * **Third-party services:** While we will remove your data from our systems, we cannot guarantee the removal of data that has already been shared with or collected by third-party services you may have used in connection with your account. Please refer to the privacy policies of those services for information about their data retention practices.
93
+
94
+ ## Request a Copy of Your Data
95
+ Under applicable data protection regulations including the General Data Protection Regulation (GDPR), the California Consumer Privacy Act (CCPA), and other similar legislative frameworks, you may have the right to request a copy of the personal data we hold about you. This is commonly referred to as a Subject Access Request (SAR) or a Data Portability Request.
96
+
97
+ To submit a data request, visit your account page here: [{{ site.url }}/account]({{ site.url }}/account#data-request). Please read the following important information before submitting your request:
98
+
99
+ * **Processing time:** Data requests may take up to **14 business days** to process. The compilation and verification of your data is performed manually by our data processing team to ensure accuracy, completeness, and security.
100
+ * **Download only — no email delivery:** For security purposes, your data will **not** be sent via email or any other communication channel. You must return to your account page to download your data once it is ready. We do not consider email to be a sufficiently secure medium for the transmission of personally identifiable information.
101
+ * **Data unavailable after account deletion:** If you delete your account before downloading your data, all personal information will be permanently and irreversibly removed from our systems. We will not be able to fulfill any pending data requests after account deletion. If you wish to both request your data and delete your account, you must download your data **before** initiating account deletion.
102
+ * **Download expiration:** Completed data requests will be available for download for 30 days after processing is complete. After this period, the request will expire and you will need to submit a new request.
103
+ * **Request limits:** You are limited to one active data request at a time. After a completed request, there is a 30-day cooldown period before a new request can be submitted.
104
+ * **Data format:** Your data will be provided as a downloadable JSON file containing your account information, authentication records, subscription history, activity data, and other personally identifiable information associated with your account.
84
105
 
85
106
  ## Acceptance of This Policy
86
107
  Use of our site signifies your acceptance of this policy. If you do not accept the policy then please do not use this site. When registering, we will further request your explicit acceptance of the privacy policy.
@@ -48,6 +48,9 @@ sections:
48
48
  - id: "api-keys"
49
49
  name: "API Keys"
50
50
  icon: "key"
51
+ - id: "data-request"
52
+ name: "Data Request"
53
+ icon: "file-export"
51
54
  - id: "delete"
52
55
  name: "Delete Account"
53
56
  icon: "trash"
@@ -133,7 +136,7 @@ badges:
133
136
  <div class="d-flex align-items-center justify-content-between py-3">
134
137
  <!-- Left: Brand -->
135
138
  <a href="/" class="d-flex align-items-center text-decoration-none text-body">
136
- <span class="avatar avatar-md filter-adaptive me-3">
139
+ <span class="avatar avatar-md me-3">
137
140
  <img src="{{ site.brand.images.brandmark }}?cb={{ site.uj.cache_breaker }}" alt="{{ site.brand.name }}"/>
138
141
  </span>
139
142
  <h1 class="h5 mb-0 fw-semibold">{{ site.brand.name }} Account</h1>
@@ -153,13 +156,13 @@ badges:
153
156
  <div class="d-flex gap-2">
154
157
  <select class="form-select flex-grow-1" id="mobile-nav-select" aria-label="Select account section">
155
158
  {% for section in page.resolved.sections %}
156
- {% unless section.id == 'delete' %}
159
+ {% unless section.id == 'delete' or section.id == 'data-request' %}
157
160
  <option value="{{ section.id }}" {% if forloop.first %}selected{% endif %}>
158
161
  {{ section.name }}
159
162
  </option>
160
163
  {% endunless %}
161
164
  {% endfor %}
162
- <!-- Delete option will be added dynamically by JavaScript when needed -->
165
+ <!-- Delete and Data Request options will be added dynamically by JavaScript when needed -->
163
166
  </select>
164
167
  <button class="btn btn-danger auth-signout-btn">
165
168
  {% uj_icon "arrow-right-from-bracket", "fa-sm" %}
@@ -181,7 +184,7 @@ badges:
181
184
  <div class="card-body p-3">
182
185
  <ul class="nav nav-pills flex-column" id="account-nav" role="tablist" aria-label="Account sections">
183
186
  {% for section in page.resolved.sections %}
184
- <li class="nav-item {% if section.id == 'delete' %}d-none{% endif %}" id="{{ section.id }}-nav-item">
187
+ <li class="nav-item {% if section.id == 'delete' or section.id == 'data-request' %}d-none{% endif %}" id="{{ section.id }}-nav-item">
185
188
  <a class="nav-link d-flex align-items-center {% if forloop.first %}active{% endif %}"
186
189
  href="#{{ section.id }}"
187
190
  data-section="{{ section.id }}"
@@ -1126,54 +1129,228 @@ badges:
1126
1129
  </div>
1127
1130
  </section>
1128
1131
 
1132
+ <!-- Data Request Section (Hidden by default) -->
1133
+ <section id="data-request-section" class="account-section d-none">
1134
+ <h2 class="h3 mb-4">Request your data</h2>
1135
+
1136
+ <!-- Info card (always shown) -->
1137
+ <div class="card mb-4">
1138
+ <div class="card-body">
1139
+ <h5 class="card-title text-primary">
1140
+ {% uj_icon "file-shield", "fa-lg me-2" %}
1141
+ About Data Subject Access Requests
1142
+ </h5>
1143
+ <p class="card-text small text-muted">
1144
+ Under applicable data protection regulations including the General Data Protection Regulation (GDPR), the California Consumer Privacy Act (CCPA), and other similar legislative frameworks enacted by governmental bodies around the world, you may have the right to request a copy of the personal data we hold about you. This process is commonly referred to as a Subject Access Request (SAR) or a Data Portability Request. It is important that you understand the full scope, limitations, and procedural requirements of this request before proceeding. Please read the following information carefully and in its entirety before submitting a request.
1145
+ </p>
1146
+ <p class="card-text small text-muted">
1147
+ When you submit a data request, our data processing team will begin the process of compiling the personal information associated with your account. This may include, but is not limited to: your name, email address, account creation date, account activity logs, subscription and billing history (including payment processor identifiers and transaction records), geolocation data collected during your use of our services (such as IP address, approximate location derived from IP, continent, country, region, and city), device and browser information (including user agent strings, platform identifiers, and viewport characteristics), API usage statistics and rate limiting data, referral and affiliate information, OAuth2 connection records, and any other data points stored in connection with your account in our systems.
1148
+ </p>
1149
+ <p class="card-text small text-muted">
1150
+ The compilation and verification process is performed by our data processing team to ensure the accuracy, completeness, and security of the information provided to you. Due to the manual verification steps involved in this process and the need to ensure that data is being released to the correct account holder, this process may take up to <strong>14 business days</strong> from the date of your request submission to complete. We appreciate your patience during this time and ask that you do not submit duplicate requests, as this will not expedite the process and may in fact delay it.
1151
+ </p>
1152
+ <p class="card-text small text-muted">
1153
+ Once your data has been compiled, verified, and is ready for retrieval, you will need to <strong>return to this page</strong> to download it. For security purposes, we do not transmit personal data via email, SMS, or any other communication channel, as these methods are not considered sufficiently secure mediums for the transmission of personally identifiable information. The data will be made available exclusively through this authenticated account interface to ensure that only the verified account holder can access it. Your data package will be provided as a JSON file containing all of the aforementioned data categories.
1154
+ </p>
1155
+ <p class="card-text small text-muted">
1156
+ <strong>Important notice regarding account deletion:</strong> If you choose to delete your account at any point, all personal data associated with your account will be permanently and irreversibly removed from our systems in accordance with our data retention policy. Once your account has been deleted, we will no longer be able to process any pending data requests, nor will we be able to provide you with a copy of your data retroactively. There is no mechanism to recover data after account deletion. If you are considering both requesting your data and deleting your account, you must complete the data request process in full and download your data package <strong>before</strong> initiating the account deletion process. We strongly recommend downloading and verifying your data before taking any irreversible actions with your account.
1157
+ </p>
1158
+ <p class="card-text small text-muted">
1159
+ You are limited to one active data request at a time. If you have previously submitted a request that is still being processed, you will not be able to submit a new request until the current one has been completed or has expired. Additionally, after a completed data request, there is a 30-day cooldown period before a new request can be submitted. These limitations exist to prevent abuse of the system and to ensure that our data processing team can handle all requests in a timely and thorough manner. Excessive or abusive use of this feature may result in additional restrictions being applied to your account in accordance with our Terms of Service.
1160
+ </p>
1161
+
1162
+ <hr>
1163
+
1164
+ <h6 class="text-muted">Summary of key points:</h6>
1165
+ <ul class="small text-muted">
1166
+ <li>Processing time: up to <strong>14 business days</strong></li>
1167
+ <li>You must return to this page to download your data</li>
1168
+ <li>Data is <strong>not</strong> sent via email for security reasons</li>
1169
+ <li>Data is permanently unavailable after account deletion</li>
1170
+ <li>Only one active request at a time</li>
1171
+ <li>30-day cooldown between completed requests</li>
1172
+ <li>Data is provided as a downloadable JSON file</li>
1173
+ </ul>
1174
+
1175
+ <p id="data-request-form-trigger" class="card-text small text-muted mb-0">
1176
+ If you have read and understood all of the information above and still wish to proceed with your data subject access request, you may proceed to <button type="button" class="accordion-trigger small text-muted text-decoration-underline" data-bs-toggle="collapse" data-bs-target="#data-request-form-accordion" aria-expanded="false" aria-controls="data-request-form-accordion">submit your request &rarr;</button>.
1177
+ </p>
1178
+ </div>
1179
+ </div>
1180
+
1181
+ <!-- Status area (shown when a request exists) -->
1182
+ <div id="data-request-status" class="d-none mb-4">
1183
+ <div class="card">
1184
+ <div class="card-body">
1185
+ <h5 class="card-title">
1186
+ {% uj_icon "clock", "fa-lg me-2" %}
1187
+ <span id="data-request-status-title">Request Status</span>
1188
+ </h5>
1189
+ <p id="data-request-status-message" class="card-text text-muted"></p>
1190
+ <div id="data-request-download" class="d-none">
1191
+ <form id="data-request-download-form" onsubmit="return false">
1192
+ <button type="submit" class="btn btn-primary btn-sm" id="data-request-download-btn">
1193
+ {% uj_icon "download", "fa-sm me-2" %}
1194
+ <span class="button-text">Download your data</span>
1195
+ </button>
1196
+ </form>
1197
+ </div>
1198
+ <div id="data-request-cancel" class="d-none">
1199
+ <form id="data-request-cancel-form" onsubmit="return false">
1200
+ <button type="submit" class="btn btn-primary btn-sm" id="data-request-cancel-btn">
1201
+ {% uj_icon "rotate-left", "fa-sm me-2" %}
1202
+ <span class="button-text">Request withdrawal</span>
1203
+ </button>
1204
+ </form>
1205
+ </div>
1206
+ </div>
1207
+ </div>
1208
+ </div>
1209
+
1210
+ <!-- Request form (hidden when a request is pending) -->
1211
+ <div id="data-request-form-container">
1212
+ <div class="collapse" id="data-request-form-accordion">
1213
+ <div class="card card-form-border border-primary mt-4">
1214
+ <div class="card-body">
1215
+ <h5 class="card-title text-primary">Confirm data request</h5>
1216
+
1217
+ <form id="data-request-form" novalidate onsubmit="return false">
1218
+ <div class="mb-3">
1219
+ <div class="form-check">
1220
+ <input class="form-check-input" type="checkbox" id="data-request-confirm-checkbox" name="confirm_processing_time" required>
1221
+ <label class="form-check-label small" for="data-request-confirm-checkbox">
1222
+ I understand that this request may take up to 14 business days to process and that I must return to this page to download my data. I acknowledge that data will not be sent to me via email or any other channel.
1223
+ </label>
1224
+ </div>
1225
+ </div>
1226
+
1227
+ <div class="mb-3">
1228
+ <div class="form-check">
1229
+ <input class="form-check-input" type="checkbox" id="data-request-deletion-checkbox" name="confirm_deletion_warning" required>
1230
+ <label class="form-check-label small" for="data-request-deletion-checkbox">
1231
+ I understand that if I delete my account before downloading my data, the data will be permanently lost and cannot be recovered under any circumstances.
1232
+ </label>
1233
+ </div>
1234
+ </div>
1235
+
1236
+ <div class="mb-3">
1237
+ <label for="data-request-reason" class="form-label">Reason for request</label>
1238
+ <textarea class="form-control" id="data-request-reason" name="reason" rows="3" placeholder="Help us understand why you're requesting your data..."></textarea>
1239
+ </div>
1240
+
1241
+ <div class="row g-2">
1242
+ <div class="col-12 col-md-6 order-md-2">
1243
+ <button type="submit" class="btn btn-outline-adaptive w-100" id="data-request-submit-btn" disabled>
1244
+ {% uj_icon "file-export", "fa-sm me-2" %}
1245
+ <span class="button-text">Submit Data Request</span>
1246
+ </button>
1247
+ </div>
1248
+ <div class="col-12 col-md-6 order-md-1">
1249
+ <a href="/account" class="btn btn-primary w-100">
1250
+ {% uj_icon "arrow-left", "fa-sm me-2" %}
1251
+ <span>Return to Profile</span>
1252
+ </a>
1253
+ </div>
1254
+ </div>
1255
+ </form>
1256
+ </div>
1257
+ </div>
1258
+ </div>
1259
+ </div>
1260
+ </section>
1261
+
1129
1262
  <!-- Delete Account Section (Hidden by default) -->
1130
1263
  <section id="delete-section" class="account-section d-none">
1131
- <h2 class="h3 mb-4 text-danger" >Delete account</h2>
1132
-
1133
- <div class="alert alert-danger">
1134
- <h5 class="alert-heading">
1135
- {% uj_icon "triangle-exclamation", "fa-lg" %}
1136
- Warning: This action cannot be undone
1137
- </h5>
1138
- <p>Deleting your account will permanently remove:</p>
1139
- <ul class="mb-2">
1140
- <li>All your personal information</li>
1141
- <li>Your subscription and billing history</li>
1142
- <li>Access to all services and data</li>
1143
- <li>Any stored content or settings</li>
1144
- </ul>
1145
- <p class="mb-0"><strong>This action is immediate and irreversible.</strong></p>
1146
- </div>
1264
+ <h2 class="h3 mb-4">Delete account</h2>
1147
1265
 
1148
- <div class="card border-danger">
1266
+ <div class="card mb-4">
1149
1267
  <div class="card-body">
1150
- <h5 class="card-title text-danger">Confirm account deletion</h5>
1151
-
1152
- <form id="delete-account-form" novalidate onsubmit="return false">
1153
- <div class="mb-3">
1154
- <div class="form-check">
1155
- <input class="form-check-input" type="checkbox" id="delete-confirm-checkbox" required>
1156
- <label class="form-check-label" for="delete-confirm-checkbox">
1157
- I understand that deleting my account is permanent and cannot be reversed. All my data will be lost forever.
1158
- </label>
1268
+ <h5 class="card-title text-danger">
1269
+ {% uj_icon "triangle-exclamation", "fa-lg me-2" %}
1270
+ About Account Deletion
1271
+ </h5>
1272
+ <p class="card-text small text-muted">
1273
+ Under applicable data protection regulations including the General Data Protection Regulation (GDPR) and the California Consumer Privacy Act (CCPA), you have the right to request the deletion of the personal data we hold about you. Account deletion is a permanent, irreversible action that cannot be undone under any circumstances. Please read the following important information carefully before proceeding.
1274
+ </p>
1275
+ <p class="card-text small text-muted">
1276
+ <strong>Permanent and irreversible:</strong> Once your account is deleted, all personal data associated with your account will be permanently removed from our systems. This includes, but is not limited to, your profile information, subscription and billing history, activity logs, API keys, OAuth2 connections, referral data, saved preferences, and all other stored information. There is no mechanism to recover any data after deletion. Our support team cannot restore deleted accounts or retrieve any associated data, regardless of the circumstances.
1277
+ </p>
1278
+ <p class="card-text small text-muted">
1279
+ <strong>Active subscriptions:</strong> You must cancel any active paid subscriptions before deleting your account. Accounts with active or suspended paid subscriptions cannot be deleted until the subscription is cancelled or expires. If you have a subscription that is currently in a billing cycle, you will need to wait for the cycle to complete or cancel the subscription first. No refunds will be issued for unused portions of cancelled subscriptions in connection with account deletion.
1280
+ </p>
1281
+ <p class="card-text small text-muted">
1282
+ <strong>Pending data requests:</strong> If you have a pending data request (Subject Access Request), it will be permanently cancelled upon account deletion. We will not be able to fulfill data requests after your account has been deleted. If you wish to obtain a copy of your data, you must complete the data request and download process <strong>before</strong> initiating account deletion. You can request a copy of your data from the <a href="#data-request">data request section</a>.
1283
+ </p>
1284
+ <p class="card-text small text-muted">
1285
+ <strong>Immediate effect:</strong> Account deletion takes effect immediately upon confirmation. You will be signed out of all active sessions across all devices and platforms, and will no longer be able to access any services, features, content, or data associated with your account. Any active API integrations or third-party connections will cease to function immediately.
1286
+ </p>
1287
+ <p class="card-text small text-muted">
1288
+ <strong>Third-party services:</strong> While we will remove your data from our systems, we cannot guarantee the removal of data that has already been shared with or collected by third-party services you may have used in connection with your account. This includes, but is not limited to, analytics providers, payment processors, authentication providers, and any external services you authorized. Please refer to the privacy policies of those services for information about their data retention and deletion practices.
1289
+ </p>
1290
+
1291
+ <hr>
1292
+
1293
+ <h6 class="text-muted">Summary of consequences:</h6>
1294
+ <ul class="small text-muted">
1295
+ <li>All personal information, account data, and stored content will be permanently erased</li>
1296
+ <li>Your subscription and complete billing history will be permanently removed</li>
1297
+ <li>All API keys, OAuth2 connections, and integrations will be revoked</li>
1298
+ <li>Access to all services, features, and platforms will be terminated immediately</li>
1299
+ <li>Any pending data requests will be cancelled and cannot be fulfilled</li>
1300
+ <li>This action cannot be reversed, appealed, or undone by our support team</li>
1301
+ </ul>
1302
+
1303
+ <p class="card-text small text-muted mb-0">
1304
+ If you have read and understood all of the information above and still wish to proceed with deleting your account, you may proceed to <button type="button" class="accordion-trigger small text-muted text-decoration-underline" data-bs-toggle="collapse" data-bs-target="#delete-form-accordion" aria-expanded="false" aria-controls="delete-form-accordion">confirm account deletion &rarr;</button>.
1305
+ </p>
1306
+ </div>
1307
+ </div>
1308
+
1309
+ <div class="collapse" id="delete-form-accordion">
1310
+ <div class="card card-form-border border-danger mt-4">
1311
+ <div class="card-body">
1312
+ <h5 class="card-title text-danger">Confirm account deletion</h5>
1313
+
1314
+ <form id="delete-account-form" novalidate onsubmit="return false">
1315
+ <div class="mb-3">
1316
+ <div class="form-check">
1317
+ <input class="form-check-input" type="checkbox" id="delete-confirm-checkbox" required>
1318
+ <label class="form-check-label" for="delete-confirm-checkbox">
1319
+ I understand that deleting my account is permanent and cannot be reversed. All my data, including personal information, subscription history, API keys, and stored content, will be permanently and irreversibly erased.
1320
+ </label>
1321
+ </div>
1159
1322
  </div>
1160
- </div>
1161
1323
 
1162
- <div class="mb-3">
1163
- <label for="delete-reason" class="form-label">Reason for leaving (optional)</label>
1164
- <textarea class="form-control" id="delete-reason" rows="3" placeholder="Help us improve by sharing why you're leaving..."></textarea>
1165
- </div>
1324
+ <div class="mb-3">
1325
+ <div class="form-check">
1326
+ <input class="form-check-input" type="checkbox" id="delete-data-request-checkbox" required>
1327
+ <label class="form-check-label" for="delete-data-request-checkbox">
1328
+ I acknowledge that any pending data requests will be cancelled and I will not be able to request or download a copy of my data after my account is deleted.
1329
+ </label>
1330
+ </div>
1331
+ </div>
1166
1332
 
1167
- <div class="d-grid gap-2">
1168
- <button type="submit" class="btn btn-danger" id="delete-account-btn" disabled>
1169
- {% uj_icon "trash", "fa-sm" %}
1170
- <span class="button-text">Delete My Account Permanently</span>
1171
- </button>
1172
- <button type="button" class="btn btn-outline-secondary" id="cancel-delete-btn">
1173
- Cancel
1174
- </button>
1175
- </div>
1176
- </form>
1333
+ <div class="mb-3">
1334
+ <label for="delete-reason" class="form-label">Reason for leaving</label>
1335
+ <textarea class="form-control" id="delete-reason" name="reason" rows="3" placeholder="Help us improve by sharing why you're leaving..."></textarea>
1336
+ </div>
1337
+
1338
+ <div class="row g-2">
1339
+ <div class="col-12 col-md-6 order-md-2">
1340
+ <button type="submit" class="btn btn-outline-adaptive w-100" id="delete-account-btn" disabled>
1341
+ {% uj_icon "trash", "fa-sm me-2" %}
1342
+ <span class="button-text">Delete My Account Permanently</span>
1343
+ </button>
1344
+ </div>
1345
+ <div class="col-12 col-md-6 order-md-1">
1346
+ <a href="/account" class="btn btn-danger w-100" id="cancel-delete-btn">
1347
+ {% uj_icon "arrow-left", "fa-sm me-2" %}
1348
+ <span>Return to Profile</span>
1349
+ </a>
1350
+ </div>
1351
+ </div>
1352
+ </form>
1353
+ </div>
1177
1354
  </div>
1178
1355
  </div>
1179
1356
  </section>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-jekyll-manager",
3
- "version": "0.0.270",
3
+ "version": "0.0.271",
4
4
  "description": "Ultimate Jekyll dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {