@product7/product7-js 0.6.7 → 0.6.9

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.
@@ -1458,6 +1458,24 @@
1458
1458
  }
1459
1459
  }
1460
1460
 
1461
+ let _cached = null;
1462
+
1463
+ async function getIpInfo() {
1464
+ if (_cached) return _cached;
1465
+
1466
+ try {
1467
+ const res = await fetch('http://ip-api.com/json');
1468
+ if (!res.ok) return null;
1469
+ const data = await res.json();
1470
+ if (data.status === 'success') {
1471
+ _cached = data;
1472
+ }
1473
+ return _cached;
1474
+ } catch {
1475
+ return null;
1476
+ }
1477
+ }
1478
+
1461
1479
  class APIService extends BaseAPIService {
1462
1480
  constructor(config = {}) {
1463
1481
  super(config);
@@ -1561,6 +1579,9 @@
1561
1579
  if (metadata.attributes) payload.attributes = metadata.attributes;
1562
1580
  if (metadata.company) payload.company = metadata.company;
1563
1581
 
1582
+ const ipInfo = await getIpInfo();
1583
+ if (ipInfo) payload.ip_info = ipInfo;
1584
+
1564
1585
  const response = await this._makeRequest('/widget/identify', {
1565
1586
  method: 'POST',
1566
1587
  headers: {
@@ -4282,10 +4303,173 @@
4282
4303
  opacity: 0.7;
4283
4304
  cursor: not-allowed;
4284
4305
  }
4306
+
4307
+ /* ========================================
4308
+ FEEDBACK FORM VIEW
4309
+ ======================================== */
4310
+
4311
+ .liveChat-feedback-view {
4312
+ display: flex;
4313
+ flex-direction: column;
4314
+ height: 100%;
4315
+ overflow: hidden;
4316
+ }
4317
+
4318
+ .liveChat-feedback-header {
4319
+ display: flex;
4320
+ align-items: center;
4321
+ gap: var(--spacing-3);
4322
+ padding: var(--spacing-4) var(--spacing-4);
4323
+ border-bottom: 1px solid var(--border-color);
4324
+ flex-shrink: 0;
4325
+ }
4326
+
4327
+ .liveChat-feedback-title {
4328
+ font-size: var(--font-size-base);
4329
+ font-weight: var(--font-weight-semibold);
4330
+ color: var(--text-primary);
4331
+ }
4332
+
4333
+ .liveChat-feedback-body {
4334
+ display: flex;
4335
+ flex-direction: column;
4336
+ gap: var(--spacing-4);
4337
+ padding: var(--spacing-5) var(--spacing-4);
4338
+ flex: 1;
4339
+ overflow-y: auto;
4340
+ }
4341
+
4342
+ .liveChat-feedback-prompt {
4343
+ margin: 0;
4344
+ font-size: var(--font-size-sm);
4345
+ color: var(--text-secondary);
4346
+ }
4347
+
4348
+
4349
+ .liveChat-feedback-input {
4350
+ width: 100%;
4351
+ border: 1px solid var(--border-color);
4352
+ border-radius: var(--radius-md);
4353
+ padding: var(--spacing-3);
4354
+ font-size: var(--font-size-sm);
4355
+ font-family: inherit;
4356
+ color: var(--text-primary);
4357
+ background: var(--msg-bg);
4358
+ box-sizing: border-box;
4359
+ transition: border-color var(--transition-fast);
4360
+ }
4361
+
4362
+ .liveChat-feedback-input::placeholder {
4363
+ color: var(--msg-text-secondary);
4364
+ }
4365
+
4366
+ .liveChat-feedback-input:focus {
4367
+ outline: none;
4368
+ border-color: var(--color-primary);
4369
+ }
4370
+
4371
+ .liveChat-feedback-textarea {
4372
+ width: 100%;
4373
+ resize: none;
4374
+ border: 1px solid var(--border-color);
4375
+ border-radius: var(--radius-md);
4376
+ padding: var(--spacing-3);
4377
+ font-size: var(--font-size-sm);
4378
+ font-family: inherit;
4379
+ color: var(--text-primary);
4380
+ background: var(--msg-bg);
4381
+ box-sizing: border-box;
4382
+ transition: border-color var(--transition-fast);
4383
+ }
4384
+
4385
+ .liveChat-feedback-textarea::placeholder {
4386
+ color: var(--msg-text-secondary);
4387
+ }
4388
+
4389
+ .liveChat-feedback-textarea:focus {
4390
+ outline: none;
4391
+ border-color: var(--color-primary);
4392
+ }
4393
+
4394
+ .liveChat-feedback-submit {
4395
+ display: flex;
4396
+ align-items: center;
4397
+ justify-content: center;
4398
+ padding: var(--spacing-3);
4399
+ border-radius: var(--radius-md);
4400
+ font-size: var(--font-size-sm);
4401
+ font-weight: var(--font-weight-medium);
4402
+ font-family: inherit;
4403
+ cursor: pointer;
4404
+ border: none;
4405
+ background: var(--color-primary);
4406
+ color: #ffffff;
4407
+ transition: all var(--transition-fast);
4408
+ }
4409
+
4410
+ .liveChat-feedback-submit:hover:not(:disabled) {
4411
+ background: var(--color-primary-hover);
4412
+ }
4413
+
4414
+ .liveChat-feedback-submit:disabled {
4415
+ opacity: 0.5;
4416
+ cursor: not-allowed;
4417
+ }
4418
+
4419
+ .liveChat-feedback-thankyou {
4420
+ display: flex;
4421
+ flex-direction: column;
4422
+ align-items: center;
4423
+ justify-content: center;
4424
+ flex: 1;
4425
+ padding: var(--spacing-6) var(--spacing-4);
4426
+ text-align: center;
4427
+ gap: var(--spacing-3);
4428
+ }
4429
+
4430
+ .liveChat-feedback-thankyou-emoji {
4431
+ font-size: 48px;
4432
+ line-height: 1;
4433
+ }
4434
+
4435
+ .liveChat-feedback-thankyou h3 {
4436
+ margin: 0;
4437
+ font-size: var(--font-size-lg);
4438
+ font-weight: var(--font-weight-semibold);
4439
+ color: var(--text-primary);
4440
+ }
4441
+
4442
+ .liveChat-feedback-thankyou p {
4443
+ margin: 0;
4444
+ font-size: var(--font-size-sm);
4445
+ color: var(--text-secondary);
4446
+ }
4447
+
4448
+ .liveChat-feedback-done-btn {
4449
+ margin-top: var(--spacing-2);
4450
+ padding: var(--spacing-2) var(--spacing-5);
4451
+ border-radius: var(--radius-md);
4452
+ font-size: var(--font-size-sm);
4453
+ font-weight: var(--font-weight-medium);
4454
+ font-family: inherit;
4455
+ cursor: pointer;
4456
+ border: 1px solid var(--border-color);
4457
+ background: transparent;
4458
+ color: var(--text-primary);
4459
+ transition: all var(--transition-fast);
4460
+ }
4461
+
4462
+ .liveChat-feedback-done-btn:hover {
4463
+ background: var(--bg-secondary);
4464
+ }
4285
4465
  `;
4286
4466
 
4287
4467
  const liveChatCoreStyles = `
4288
4468
 
4469
+ .liveChat-widget * {
4470
+ font-weight: 500;
4471
+ }
4472
+
4289
4473
  .liveChat-launcher {
4290
4474
  position: fixed;
4291
4475
  z-index: var(--z-modal);
@@ -9490,11 +9674,12 @@
9490
9674
  </div>`;
9491
9675
  }
9492
9676
 
9493
- // Hide nav in chat and prechat views
9677
+ // Hide nav in chat, prechat and feedback views
9494
9678
  if (navContainer) {
9495
9679
  const hideNav =
9496
9680
  this.state.currentView === 'chat' ||
9497
- this.state.currentView === 'prechat';
9681
+ this.state.currentView === 'prechat' ||
9682
+ this.state.currentView === 'feedback';
9498
9683
  navContainer.style.display = hideNav ? 'none' : '';
9499
9684
  }
9500
9685
  }
@@ -11232,8 +11417,11 @@
11232
11417
  const sendIcon = `<iconify-icon icon="ph:paper-plane-right" width="20" height="20" style="flex-shrink: 0;"></iconify-icon>`;
11233
11418
  const caretIcon = `<iconify-icon icon="ph:caret-right" width="20" height="20" style="flex-shrink: 0;"></iconify-icon>`;
11234
11419
 
11235
- const responseTime =
11236
- this.state.responseTime || 'We typically reply within a few minutes';
11420
+ const isUnavailable = this.state.businessHoursState === 'offline' || this.state.businessHoursState === 'away';
11421
+ const buttonLabel = isUnavailable ? 'Leave us a message' : this.state.startButtonText;
11422
+ const buttonSubtext = isUnavailable
11423
+ ? "We'll get back to you when we're back"
11424
+ : (this.state.responseTime || 'We typically reply within a few minutes');
11237
11425
 
11238
11426
  const recentCardHtml = openConversation
11239
11427
  ? this._renderRecentMessageCard(openConversation)
@@ -11243,8 +11431,8 @@
11243
11431
  ${recentCardHtml}
11244
11432
  <button class="liveChat-home-message-btn">
11245
11433
  <div class="liveChat-home-continue-info">
11246
- <span class="liveChat-home-continue-label">${this.state.startButtonText}</span>
11247
- <span class="liveChat-home-message-subtext">${responseTime}</span>
11434
+ <span class="liveChat-home-continue-label">${buttonLabel}</span>
11435
+ <span class="liveChat-home-message-subtext">${buttonSubtext}</span>
11248
11436
  </div>
11249
11437
  ${sendIcon}
11250
11438
  </button>
@@ -11408,12 +11596,7 @@
11408
11596
  const feedbackBtn = this.element.querySelector('.liveChat-feedback-btn');
11409
11597
  if (feedbackBtn) {
11410
11598
  feedbackBtn.addEventListener('click', () => {
11411
- if (this.options.onFeedbackClick) {
11412
- this.state.setOpen(false);
11413
- this.options.onFeedbackClick();
11414
- } else if (this.state.urls?.feedback) {
11415
- window.open(this.state.urls.feedback, '_blank');
11416
- }
11599
+ this.state.setView('feedback');
11417
11600
  });
11418
11601
  }
11419
11602
 
@@ -11467,6 +11650,111 @@
11467
11650
  }
11468
11651
  }
11469
11652
 
11653
+ class FeedbackFormView {
11654
+ constructor(state, options = {}) {
11655
+ this.state = state;
11656
+ this.options = options;
11657
+ this.element = null;
11658
+ this._isSubmitting = false;
11659
+ this._selectedRating = null;
11660
+ }
11661
+
11662
+ render() {
11663
+ this.element = document.createElement('div');
11664
+ this.element.className = 'liveChat-view liveChat-feedback-view';
11665
+ this._renderForm();
11666
+ return this.element;
11667
+ }
11668
+
11669
+ _renderForm() {
11670
+ this.element.innerHTML = `
11671
+ <div class="liveChat-feedback-header">
11672
+ <button class="sdk-btn-icon liveChat-feedback-back-btn">
11673
+ <iconify-icon icon="ph:arrow-left" width="20" height="20"></iconify-icon>
11674
+ </button>
11675
+ <span class="liveChat-feedback-title">Leave us feedback</span>
11676
+ </div>
11677
+ <div class="liveChat-feedback-body">
11678
+ <p class="liveChat-feedback-prompt">Share your thoughts with us. We read every message.</p>
11679
+ <input
11680
+ type="text"
11681
+ class="liveChat-feedback-input"
11682
+ placeholder="Title"
11683
+ />
11684
+ <textarea
11685
+ class="liveChat-feedback-textarea"
11686
+ placeholder="Your feedback..."
11687
+ rows="5"
11688
+ ></textarea>
11689
+ <button class="liveChat-feedback-submit">Send feedback</button>
11690
+ </div>
11691
+ `;
11692
+ this._attachEvents();
11693
+ }
11694
+
11695
+ _renderThankYou() {
11696
+ this.element.innerHTML = `
11697
+ <div class="liveChat-feedback-header">
11698
+ <button class="sdk-btn-icon liveChat-feedback-back-btn">
11699
+ <iconify-icon icon="ph:arrow-left" width="20" height="20"></iconify-icon>
11700
+ </button>
11701
+ <span class="liveChat-feedback-title">Leave us feedback</span>
11702
+ </div>
11703
+ <div class="liveChat-feedback-thankyou">
11704
+ <span class="liveChat-feedback-thankyou-emoji">🙏</span>
11705
+ <h3>Thanks for your feedback!</h3>
11706
+ <p>We appreciate you taking the time to share your thoughts.</p>
11707
+ <button class="liveChat-feedback-done-btn">Done</button>
11708
+ </div>
11709
+ `;
11710
+ this.element.querySelector('.liveChat-feedback-back-btn').addEventListener('click', () => {
11711
+ this.state.setView('home');
11712
+ });
11713
+ this.element.querySelector('.liveChat-feedback-done-btn').addEventListener('click', () => {
11714
+ this.state.setView('home');
11715
+ });
11716
+ }
11717
+
11718
+ _attachEvents() {
11719
+ this.element.querySelector('.liveChat-feedback-back-btn').addEventListener('click', () => {
11720
+ this.state.setView('home');
11721
+ });
11722
+
11723
+ const submitBtn = this.element.querySelector('.liveChat-feedback-submit');
11724
+ submitBtn.addEventListener('click', async () => {
11725
+ if (this._isSubmitting) return;
11726
+ const title = this.element.querySelector('.liveChat-feedback-input').value.trim();
11727
+ const message = this.element.querySelector('.liveChat-feedback-textarea').value.trim();
11728
+ await this._submit(title, message);
11729
+ });
11730
+ }
11731
+
11732
+ async _submit(title, message) {
11733
+ this._isSubmitting = true;
11734
+ const submitBtn = this.element.querySelector('.liveChat-feedback-submit');
11735
+ if (submitBtn) {
11736
+ submitBtn.disabled = true;
11737
+ submitBtn.textContent = 'Sending...';
11738
+ }
11739
+
11740
+ try {
11741
+ if (this.options.onSubmitFeedback) {
11742
+ await this.options.onSubmitFeedback({ title, message });
11743
+ }
11744
+ } catch (e) {
11745
+ console.warn('[FeedbackFormView] Submit error:', e);
11746
+ }
11747
+
11748
+ this._renderThankYou();
11749
+ }
11750
+
11751
+ destroy() {
11752
+ if (this.element && this.element.parentNode) {
11753
+ this.element.parentNode.removeChild(this.element);
11754
+ }
11755
+ }
11756
+ }
11757
+
11470
11758
  class PreChatFormView {
11471
11759
  constructor(state, options = {}) {
11472
11760
  this.state = state;
@@ -11785,6 +12073,7 @@
11785
12073
  this.panel.registerView('messages', ConversationsView);
11786
12074
  this.panel.registerView('chat', ChatView);
11787
12075
  this.panel.registerView('prechat', PreChatFormView);
12076
+ this.panel.registerView('feedback', FeedbackFormView);
11788
12077
  this.panel.registerView('help', HelpView);
11789
12078
  this.panel.registerView('changelog', ChangelogView);
11790
12079