livechat-assistant 0.0.8 → 0.0.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.
Files changed (64) hide show
  1. package/ng-package.json +10 -0
  2. package/package.json +19 -23
  3. package/src/lib/components/chat-history/chat-history.component.html +83 -0
  4. package/src/lib/components/chat-history/chat-history.component.scss +0 -0
  5. package/src/lib/components/chat-history/chat-history.component.spec.ts +23 -0
  6. package/src/lib/components/chat-history/chat-history.component.ts +97 -0
  7. package/src/lib/components/chat-widget/chat-widget.component.html +87 -0
  8. package/src/lib/components/chat-widget/chat-widget.component.scss +77 -0
  9. package/src/lib/components/chat-widget/chat-widget.component.spec.ts +23 -0
  10. package/src/lib/components/chat-widget/chat-widget.component.ts +84 -0
  11. package/src/lib/components/help-support/help-support.component.html +106 -0
  12. package/src/lib/components/help-support/help-support.component.scss +0 -0
  13. package/src/lib/components/help-support/help-support.component.spec.ts +23 -0
  14. package/src/lib/components/help-support/help-support.component.ts +46 -0
  15. package/src/lib/components/home/home.component.html +64 -0
  16. package/src/lib/components/home/home.component.scss +0 -0
  17. package/src/lib/components/home/home.component.spec.ts +23 -0
  18. package/src/lib/components/home/home.component.ts +18 -0
  19. package/src/lib/components/index.ts +2 -0
  20. package/src/lib/components/message/message.component.html +119 -0
  21. package/src/lib/components/message/message.component.scss +0 -0
  22. package/src/lib/components/message/message.component.spec.ts +23 -0
  23. package/src/lib/components/message/message.component.ts +48 -0
  24. package/src/lib/components/success-message/success-message.component.html +33 -0
  25. package/src/lib/components/success-message/success-message.component.scss +65 -0
  26. package/src/lib/components/success-message/success-message.component.spec.ts +23 -0
  27. package/src/lib/components/success-message/success-message.component.ts +34 -0
  28. package/src/lib/components/support-buttons/support-buttons.component.html +42 -0
  29. package/src/lib/components/support-buttons/support-buttons.component.scss +49 -0
  30. package/src/lib/components/support-buttons/support-buttons.component.spec.ts +21 -0
  31. package/src/lib/components/support-buttons/support-buttons.component.ts +33 -0
  32. package/src/lib/environment/environment.test.ts +15 -0
  33. package/src/lib/livechat-assistant.component.spec.ts +23 -0
  34. package/src/lib/livechat-assistant.component.ts +15 -0
  35. package/src/lib/livechat-assistant.service.spec.ts +16 -0
  36. package/src/lib/livechat-assistant.service.ts +9 -0
  37. package/src/lib/services/index.ts +1 -0
  38. package/src/lib/services/request.ts +58 -0
  39. package/src/lib/utilities/helper.ts +23 -0
  40. package/src/lib/utilities/index.ts +1 -0
  41. package/src/public-api.ts +10 -0
  42. package/src/styles.css +5 -0
  43. package/tsconfig.lib.json +15 -0
  44. package/tsconfig.lib.prod.json +11 -0
  45. package/tsconfig.spec.json +15 -0
  46. package/fesm2022/livechat-assistant.mjs +0 -445
  47. package/fesm2022/livechat-assistant.mjs.map +0 -1
  48. package/index.d.ts +0 -5
  49. package/lib/components/chat-history/chat-history.component.d.ts +0 -94
  50. package/lib/components/chat-widget/chat-widget.component.d.ts +0 -17
  51. package/lib/components/help-support/help-support.component.d.ts +0 -15
  52. package/lib/components/home/home.component.d.ts +0 -8
  53. package/lib/components/index.d.ts +0 -2
  54. package/lib/components/message/message.component.d.ts +0 -16
  55. package/lib/components/success-message/success-message.component.d.ts +0 -15
  56. package/lib/components/support-buttons/support-buttons.component.d.ts +0 -12
  57. package/lib/environment/environment.test.d.ts +0 -15
  58. package/lib/livechat-assistant.component.d.ts +0 -5
  59. package/lib/livechat-assistant.service.d.ts +0 -6
  60. package/lib/services/index.d.ts +0 -1
  61. package/lib/services/request.d.ts +0 -12
  62. package/lib/utilities/helper.d.ts +0 -9
  63. package/lib/utilities/index.d.ts +0 -1
  64. package/public-api.d.ts +0 -5
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/livechat-assistant",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ },
7
+ "allowedNonPeerDependencies": [
8
+ "preline"
9
+ ]
10
+ }
package/package.json CHANGED
@@ -1,23 +1,19 @@
1
- {
2
- "name": "livechat-assistant",
3
- "version": "0.0.8",
4
- "peerDependencies": {
5
- "@angular/common": "^19.2.0",
6
- "@angular/core": "^19.2.0"
7
- },
8
- "dependencies": {
9
- "tslib": "^2.3.0"
10
- },
11
- "sideEffects": false,
12
- "module": "fesm2022/livechat-assistant.mjs",
13
- "typings": "index.d.ts",
14
- "exports": {
15
- "./package.json": {
16
- "default": "./package.json"
17
- },
18
- ".": {
19
- "types": "./index.d.ts",
20
- "default": "./fesm2022/livechat-assistant.mjs"
21
- }
22
- }
23
- }
1
+ {
2
+ "name": "livechat-assistant",
3
+ "version": "0.0.9",
4
+ "description": "Angular AI live chat widget component",
5
+ "author": "sleekpenny",
6
+ "license": "MIT",
7
+ "main": "index.js",
8
+ "module": "fesm2022/ai-livechat-assistants.mjs",
9
+ "types": "index.d.ts",
10
+ "peerDependencies": {
11
+ "@angular/common": "^19.2.0",
12
+ "@angular/core": "^19.2.0"
13
+ },
14
+ "dependencies": {
15
+ "preline": "^4.0.1",
16
+ "tslib": "^2.3.0"
17
+ },
18
+ "sideEffects": false
19
+ }
@@ -0,0 +1,83 @@
1
+ <div class="max-h-[600px] rounded-xl w-full flex flex-col justify-center items-center">
2
+ <div
3
+ class="flex items-center justify-between px-4 py-5 dark:bg-neutral-700/20 shadow-xl bg-neutral-50 hover:bg-neutral-800/50 rounded-xl w-full">
4
+ <h3 class="font-semibold text-neutral-800 dark:text-neutral-200 fs-16">Messages</h3>
5
+ </div>
6
+
7
+ <!-- <div class="flex gap-2">
8
+ <button (click)="navigateToTab(2)" class="mt-4 mb-2 bg-red-600 text-white rounded-md hover:bg-blue-700 transition w-fit">
9
+ <p class="text-center px-4 py-1.5 text-sm m-0">Ask AI question</p>
10
+ </button>
11
+ <button (click)="navigateToTab(2)" class="mt-4 mb-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition w-fit">
12
+ <p class="text-center px-4 py-1.5 text-sm m-0">Chat with Live Agent</p>
13
+ </button>
14
+ </div> -->
15
+
16
+ <div class="flex-1 overflow-y-auto hideScroll">
17
+ @for (conversation of conversations; track $index) {
18
+
19
+ <div (click)="selectConversation(conversation.id, 2)"
20
+ class="flex items-center gap-3 px-4 py-3 hover:bg-neutral-800 cursor-pointer transition-colors">
21
+
22
+ <!-- Avatar -->
23
+ <div class="relative flex-shrink-0">
24
+ @if (conversation.avatar) {
25
+ <div [class]="'w-12 h-12 flex items-center font-semibold justify-center rounded-full overflow-hidden' + conversation.color">
26
+ <h1 class="text-white text-xl">AI</h1>
27
+ </div>
28
+ }
29
+
30
+ @if (!conversation.avatar && conversation.initials) {
31
+ <div [class]="'w-12 h-12 rounded-full flex items-center justify-center text-white font-semibold text-xl' + (conversation.color || 'bg-gray-500')">
32
+ {{ conversation.initials }}
33
+ </div>
34
+ }
35
+
36
+ @if (conversation.online) {
37
+ <span class="absolute bottom-0 right-0 w-3 h-3 bg-green-500 border-2 border-white rounded-full"></span>
38
+ }
39
+ </div>
40
+
41
+ <div class="flex-1 min-w-0">
42
+ <div class="flex items-center justify-between mb-1">
43
+ <h3 class="font-semibold text-neutral-800 dark:text-white fs-16 truncate">{{ conversation.name }}</h3>
44
+ <span class="text-neutral-800 dark:text-neutral-400 text-xs">Date </span>
45
+ </div>
46
+ <div class="flex items-center justify-between">
47
+ <p class="text-sm text-neutral-400 truncate flex items-center gap-1">
48
+ @if (conversation.hasAttachment) {
49
+ <span class="text-neutral-400">📎</span>
50
+ }
51
+ {{ conversation.lastMessage }}
52
+ </p>
53
+ <div class="flex items-center gap-2 flex-shrink-0 ml-2">
54
+
55
+ @if (conversation.status === 'read') {
56
+ <svg class="w-4 h-4 text-green-400" fill="currentColor"
57
+ viewBox="0 0 24 24">
58
+ <path
59
+ d="M18 7l-1.41-1.41-6.34 6.34 1.41 1.41L18 7zm4.24-1.41L11.66 16.17 7.48 12l-1.41 1.41L11.66 19l12-12-1.42-1.41zM.41 13.41L6 19l1.41-1.41L1.83 12 .41 13.41z" />
60
+ </svg>
61
+ }
62
+
63
+ @if (conversation.status === 'delivered') {
64
+ <svg class="w-4 h-4 text-blue-600" fill="currentColor"
65
+ viewBox="0 0 24 24">
66
+ <path
67
+ d="M18 7l-1.41-1.41-6.34 6.34 1.41 1.41L18 7zm4.24-1.41L11.66 16.17 7.48 12l-1.41 1.41L11.66 19l12-12-1.42-1.41zM.41 13.41L6 19l1.41-1.41L1.83 12 .41 13.41z" />
68
+ </svg>
69
+ }
70
+
71
+ @if (conversation.unreadCount) {
72
+ <span
73
+ class="bg-blue-600 text-white text-xs font-semibold rounded-full w-5 h-5 flex items-center justify-center">
74
+ {{ conversation.unreadCount }}
75
+ </span>
76
+ }
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ }
82
+ </div>
83
+ </div>
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { ChatHistoryComponent } from './chat-history.component';
4
+
5
+ describe('ChatHistoryComponent', () => {
6
+ let component: ChatHistoryComponent;
7
+ let fixture: ComponentFixture<ChatHistoryComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [ChatHistoryComponent]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(ChatHistoryComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,97 @@
1
+ import { Component, ChangeDetectionStrategy, EventEmitter, Output, } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'app-chat-history',
5
+ imports: [],
6
+ templateUrl: './chat-history.component.html',
7
+ styleUrl: './chat-history.component.scss',
8
+ standalone: true,
9
+ changeDetection: ChangeDetectionStrategy.OnPush,
10
+ })
11
+ export class ChatHistoryComponent {
12
+ @Output() navigate= new EventEmitter<number>();
13
+
14
+ conversations = [
15
+ {
16
+ id: 1,
17
+ name: 'Costa Quinn',
18
+ avatar: true,
19
+ lastMessage: 'Yes, you can!',
20
+ time: '11M',
21
+ status: 'read',
22
+ color: 'bg-red-600',
23
+ online: true,
24
+ initials: ''
25
+ },
26
+ {
27
+ id: 2,
28
+ name: 'Rachel Doe',
29
+ initials: 'R',
30
+ color: 'bg-blue-600',
31
+ lastMessage: 'When using open method, const select = n...',
32
+ time: '14M',
33
+ unreadCount: 1,
34
+ online: false,
35
+ avatar:false
36
+ },
37
+ {
38
+ id: 3,
39
+ name: 'Lewis Clarke',
40
+ avatar: true,
41
+ lastMessage: 'Have a great all free! 😊',
42
+ time: '15M',
43
+ online: true,
44
+ status: 'read',
45
+ color: 'bg-blue-400'
46
+ },
47
+ {
48
+ id: 4,
49
+ name: 'Technical issues',
50
+ initials: 'T',
51
+ color: 'bg-orange-500',
52
+ lastMessage: 'Great!',
53
+ time: '35M',
54
+ status: 'read',
55
+ hasAttachment: true,
56
+ online:false
57
+ },
58
+ {
59
+ id: 5,
60
+ name: 'Bob Dean',
61
+ initials: 'B',
62
+ color: 'bg-pink-500',
63
+ lastMessage: 'Hey Preline team, I got an p3p48 while using...',
64
+ time: '1H',
65
+ unreadCount: 1,
66
+ online:true
67
+ },
68
+ {
69
+ id: 6,
70
+ name: 'Mark Colbert',
71
+ initials: 'M',
72
+ color: 'bg-teal-500',
73
+ lastMessage: 'Voice message',
74
+ time: '6DM',
75
+ status: 'delivered',
76
+ online: false
77
+ },
78
+ {
79
+ id: 7,
80
+ name: 'Ella Lauda',
81
+ avatar: true,
82
+ lastMessage: 'I am really impressed! Can\'t wait...',
83
+ time: '37M',
84
+ online: false
85
+ }
86
+ ];
87
+
88
+ selectConversation(id: number, tabIndex:number) {
89
+ console.log('Selected conversation:', id);
90
+ this.navigate.emit(tabIndex);
91
+ }
92
+
93
+ navigateToTab(tabIndex:number) {
94
+ this.navigate.emit(tabIndex);
95
+ }
96
+
97
+ }
@@ -0,0 +1,87 @@
1
+ <div class="chatbot bg-white dark:bg-neutral-900 ">
2
+
3
+ <div>
4
+ @if (activeTab === 1) {
5
+ <div role="tabpanel">
6
+ <app-home (navigate)="navigateToTab($event)" />
7
+ </div>
8
+ }
9
+
10
+ @if (activeTab === 2) {
11
+ <div role="tabpanel">
12
+ <app-message [messages]="messages" [isTyping]="isTyping" (sendMessage)="sendMessage($event)" (navigate)="navigateToTab($event)"/>
13
+ </div>
14
+ }
15
+
16
+ @if (activeTab === 3) {
17
+ <div role="tabpanel">
18
+ <app-help-support (navigate)="navigateToTab($event)"/>
19
+ </div>
20
+ }
21
+
22
+ @if (activeTab === 4) {
23
+ <div role="tabpanel">
24
+ <app-chat-history (navigate)="navigateToTab($event)"/>
25
+ </div>
26
+ }
27
+
28
+ @if (activeTab === 5) {
29
+ <div role="tabpanel">
30
+ <app-success-message (navigate)="navigateToTab($event)" />
31
+ </div>
32
+ }
33
+
34
+ </div>
35
+
36
+ <div class="">
37
+ <nav class="flex justify-evenly" aria-label="Tabs" role="tablist">
38
+ <button type="button" (click)="setActiveTab(1)"
39
+ [class.text-blue-500]="activeTab === 1"
40
+ [class.text-neutral-300]="activeTab !== 1"
41
+ class="py-2 px-1 inline-flex items-center gap-x-2 border-b-2 border-transparent text-sm whitespace-nowrap transition-colors focus:outline-none flex flex-col gap-2">
42
+
43
+ <div [ngClass]="activeTab === 1 ? 'bg-blue-100 rounded-full dark:font-semibold transition-colors' : 'bg-transparent'" class="px-4 py-2.5">
44
+ <svg class="shrink-0 size-5" [class.text-black]="activeTab === 1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"
45
+ viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
46
+ stroke-linejoin="round">
47
+ <path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
48
+ <polyline points="9 22 9 12 15 12 15 22"></polyline>
49
+ </svg>
50
+ </div>
51
+
52
+ <span class="text-lg fw-4 hover:text-blue-200">Home</span>
53
+ </button>
54
+
55
+ <button type="button" (click)="setActiveTab(2)" [class.font-bold]="activeTab === 2"
56
+ [class.text-blue-500]="activeTab === 2" [class.text-neutral-300]="activeTab !== 2"
57
+ class="py-2 px-1 inline-flex items-center gap-x-2 border-b-2 border-transparent text-sm whitespace-nowrap hover:text-blue-200 focus:outline-none flex flex-col gap-2">
58
+ <div [ngClass]="activeTab === 2 ? 'bg-blue-100 rounded-full dark:font-semibold transition-colors' : 'bg-transparent'" class="px-4 py-2.5">
59
+ <svg class="shrink-0 size-5" [class.text-black]="activeTab === 2" xmlns="http://www.w3.org/2000/svg" width="24" height="24"
60
+ viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
61
+ stroke-linejoin="round">
62
+ <circle cx="12" cy="12" r="10"></circle>
63
+ <circle cx="12" cy="10" r="3"></circle>
64
+ <path d="M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662"></path>
65
+ </svg>
66
+ </div>
67
+ <span class="text-lg fw-6 hover:text-blue-200">Message</span>
68
+ </button>
69
+
70
+ <button type="button" (click)="setActiveTab(3)" [class.font-bold]="activeTab === 3"
71
+ [class.text-blue-500]="activeTab === 3" [class.text-neutral-300]="activeTab !== 3"
72
+ class="py-2 px-1 inline-flex items-center gap-x-2 border-b-2 border-transparent text-sm whitespace-nowrap hover:text-blue-200 focus:outline-none flex flex-col gap-2">
73
+ <div [ngClass]="activeTab === 3 ? 'bg-blue-100 rounded-full dark:font-semibold transition-colors' : 'bg-transparent'" class="px-4 py-2.5">
74
+ <svg class="shrink-0 size-5" [class.text-black]="activeTab === 3" xmlns="http://www.w3.org/2000/svg" width="24" height="24"
75
+ viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
76
+ stroke-linejoin="round">
77
+ <path
78
+ d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z">
79
+ </path>
80
+ <circle cx="12" cy="12" r="3"></circle>
81
+ </svg>
82
+ </div>
83
+ <span class="text-lg fw-6 hover:text-blue-200">Help</span>
84
+ </button>
85
+ </nav>
86
+ </div>
87
+ </div>
@@ -0,0 +1,77 @@
1
+ :host {
2
+
3
+ .chat-container {
4
+ width: 400px !important;
5
+ }
6
+ .btn-round {
7
+ border-radius: 100% !important;
8
+ width: 40px !important;
9
+ height: 40px !important;
10
+ &.btn-fab {
11
+ position: fixed !important;
12
+ bottom: 20px !important;
13
+ right: 30px !important;
14
+ width: 60px !important;
15
+ height: 60px !important;
16
+ border-radius: 100%;
17
+ box-shadow: 0 6px 20px rgba(0,0,0,0.2);
18
+ i {
19
+ position: relative !important;
20
+ top: 0px !important;
21
+ right: -18px !important;
22
+ // margin: 10px auto !important;
23
+ }
24
+ }
25
+ }
26
+ #send-btn {
27
+ margin-bottom: 10px !important;
28
+ margin-right: 0px !important;
29
+ }
30
+
31
+ .chatbot header {
32
+ padding: 10px 10px !important;
33
+ position: relative;
34
+ text-align: center;
35
+ }
36
+
37
+ .chatbot .chatbox {
38
+ overflow-y: auto;
39
+ height: 550px;
40
+ margin-bottom: 40px !important;
41
+ }
42
+
43
+ .chatbot :where(.chatbox, textarea)::-webkit-scrollbar {
44
+ width: 6px;
45
+ }
46
+
47
+ .chatbot :where(.chatbox, textarea)::-webkit-scrollbar-track {
48
+ border-radius: 25px;
49
+ }
50
+
51
+ .chatbot :where(.chatbox, textarea)::-webkit-scrollbar-thumb {
52
+ border-radius: 25px;
53
+ }
54
+ @media (max-width: 490px) {
55
+ .chatbot-toggler {
56
+ right: 20px;
57
+ bottom: 20px;
58
+ }
59
+
60
+ .chatbot {
61
+ right: 0;
62
+ bottom: 0;
63
+ height: 100%;
64
+ border-radius: 0;
65
+ width: 100%;
66
+ }
67
+
68
+ .chatbot .chatbox {
69
+ height: 90%;
70
+ padding: 25px 15px 100px;
71
+ }
72
+
73
+ .chatbot header span {
74
+ display: block;
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { ChatWidgetComponent } from './chat-widget.component';
4
+
5
+ describe('ChatWidgetComponent', () => {
6
+ let component: ChatWidgetComponent;
7
+ let fixture: ComponentFixture<ChatWidgetComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [ChatWidgetComponent]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(ChatWidgetComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,84 @@
1
+ import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core';
2
+ import { FormsModule } from '@angular/forms';
3
+ import { NgClass } from '@angular/common';
4
+ import { ChatHistoryComponent } from '../chat-history/chat-history.component';
5
+ import { HelpSupportComponent } from '../help-support/help-support.component';
6
+ import { HomeComponent } from '../home/home.component';
7
+ import { MessageComponent } from '../message/message.component';
8
+ import { SuccessMessageComponent } from '../success-message/success-message.component';
9
+ @Component({
10
+ selector: 'app-chat-widget',
11
+ standalone: true,
12
+ imports: [
13
+ FormsModule,
14
+ HomeComponent,
15
+ MessageComponent,
16
+ HelpSupportComponent,
17
+ FormsModule,
18
+ ChatHistoryComponent,
19
+ NgClass,
20
+ SuccessMessageComponent
21
+ ],
22
+ changeDetection: ChangeDetectionStrategy.OnPush,
23
+ templateUrl: './chat-widget.component.html',
24
+ styleUrl: './chat-widget.component.scss',
25
+ encapsulation: ViewEncapsulation.None
26
+ })
27
+ export class ChatWidgetComponent {
28
+ isTyping = false;
29
+ @Input() isOpen = false;
30
+ activeTab = 1;
31
+
32
+ setActiveTab(tab: number) {
33
+ this.activeTab = tab;
34
+ }
35
+
36
+ navigateToTab(tab:number) {
37
+ this.setActiveTab(tab);
38
+ }
39
+
40
+ messages = [
41
+ {
42
+ id: 1,
43
+ sender: 'agent',
44
+ text: 'Hi, I\'d like to ask some questions. Can I use Preline UI on a client project?',
45
+ time: '9:40 AM'
46
+ },
47
+ {
48
+ id: 2,
49
+ sender: 'agent',
50
+ text: 'https://preline.co/',
51
+ time: '9:41 AM',
52
+ },
53
+ {
54
+ id: 3,
55
+ sender: 'customer',
56
+ text: 'Hi, I\'d like to ask some questions. Can I use Preline UI on a client project?',
57
+ time: '11:27 AM'
58
+ },
59
+ {
60
+ id: 4,
61
+ sender: 'customer',
62
+ text: 'Yes, you can! 😊',
63
+ time: '11:28 AM'
64
+ }
65
+ ];
66
+
67
+ sendMessage(message: string) {
68
+ if (message.trim()) {
69
+ this.messages.push({
70
+ id: this.messages.length + 1,
71
+ sender: 'customer',
72
+ text: message,
73
+ time: new Date().toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })
74
+ });
75
+ console.log(message)
76
+ // Simulate agent typing
77
+ this.isTyping = true;
78
+ setTimeout(() => {
79
+ this.isTyping = false;
80
+ }, 2000);
81
+ }
82
+ }
83
+
84
+ }
@@ -0,0 +1,106 @@
1
+ <div class="rounded-lg flex flex-col overflow-hidden max-h-[600px]">
2
+ <div class="relative h-32 bg-gradient-to-br from-cyan-300 via-blue-600 to-orange-400 overflow-hidden">
3
+ <div class="absolute inset-0">
4
+ <div class="absolute top-0 left-0 w-24 h-24 bg-blue-700 transform rotate-45 -translate-x-8 -translate-y-8"></div>
5
+ <div class="absolute top-4 right-0 w-32 h-32 bg-orange-400 transform rotate-12 translate-x-12"></div>
6
+ <div class="absolute bottom-0 left-8 w-28 h-28 bg-cyan-400 transform -rotate-12 translate-y-12"></div>
7
+ </div>
8
+
9
+
10
+ <!-- Logo -->
11
+ <div class="absolute bottom-4 left-4 w-10 h-10 bg-white rounded-full flex items-center justify-center shadow-lg">
12
+ <svg class="w-6 h-6 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
13
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"/>
14
+ </svg>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="p-4 overflow-y-auto hideScroll">
19
+
20
+ <h2 class="text-lg font-semibold text-neutral-800 dark:text-neutral-200 mb-1">Send a message</h2>
21
+ <p class="text-sm text-neutral-500 dark:text-neutral-200 mb-6">We'll get back to you in a few hours.</p>
22
+
23
+ <form [formGroup]="contactForm" (ngSubmit)="onSubmit()" class="space-y-4">
24
+
25
+ <div>
26
+ <label for="name" class="block text-sm font-medium text-neutral-700 dark:text-neutral-200 mb-1">Name</label>
27
+ <input
28
+ type="text"
29
+ id="name"
30
+ formControlName="name"
31
+ placeholder="John Doe"
32
+ class="w-full px-4 py-2.5 border border-neutral-500 dark:text-neutral-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
33
+ [class.border-red-500]="contactForm.get('name')?.invalid && contactForm.get('name')?.touched">
34
+ <p *ngIf="contactForm.get('name')?.invalid && contactForm.get('name')?.touched"
35
+ class="text-xs text-red-500 mt-1">Name is required</p>
36
+ </div>
37
+
38
+ <!-- Email Field -->
39
+ <div class="relative">
40
+ <label for="email" class="block text-sm font-medium text-neutral-700 dark:text-neutral-200 mb-1">Email</label>
41
+ <input
42
+ type="email"
43
+ id="email"
44
+ formControlName="email"
45
+ placeholder="john@site.co"
46
+ (focus)="showTooltip = true"
47
+ (blur)="showTooltip = false"
48
+ class="w-full px-4 py-2.5 border border-neutral-500 dark:text-neutral-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
49
+ [class.border-red-500]="contactForm.get('email')?.invalid && contactForm.get('email')?.touched">
50
+
51
+ <!-- Tooltip -->
52
+ <div *ngIf="showTooltip"
53
+ class="absolute right-0 top-0 mt-1 mr-2 bg-neutral-900 dark:text-neutral-200 text-white text-xs px-3 py-2 rounded shadow-lg z-10 whitespace-nowrap">
54
+ Existing accounts should use <br>your account to access the <br>source code.
55
+ <div class="absolute top-1/2 -right-1 transform -translate-y-1/2 w-2 h-2 bg-neutral-900 dark:text-neutral-200 rotate-45"></div>
56
+ </div>
57
+
58
+ <p *ngIf="contactForm.get('email')?.invalid && contactForm.get('email')?.touched"
59
+ class="text-xs text-red-500 mt-1">
60
+ <span *ngIf="contactForm.get('email')?.errors?.['required']">Email is required</span>
61
+ <span *ngIf="contactForm.get('email')?.errors?.['email']">Invalid email format</span>
62
+ </p>
63
+ </div>
64
+
65
+ <!-- Subject Field -->
66
+ <div>
67
+ <label for="subject" class="block text-sm font-medium text-neutral-700 dark:text-neutral-200 mb-1">Subject</label>
68
+ <input
69
+ type="text"
70
+ id="subject"
71
+ formControlName="subject"
72
+ placeholder="Preline Pro"
73
+ class="w-full px-4 py-2.5 border border-neutral-500 dark:text-neutral-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
74
+ [class.border-red-500]="contactForm.get('subject')?.invalid && contactForm.get('subject')?.touched">
75
+ <p *ngIf="contactForm.get('subject')?.invalid && contactForm.get('subject')?.touched"
76
+ class="text-xs text-red-500 mt-1">Subject is required</p>
77
+ </div>
78
+
79
+ <!-- Message Field -->
80
+ <div>
81
+ <label for="message" class="block text-sm font-medium text-neutral-700 dark:text-neutral-200 mb-1">How can we help?</label>
82
+ <textarea
83
+ id="message"
84
+ formControlName="message"
85
+ rows="4"
86
+ placeholder="Message..."
87
+ class="w-full px-4 py-2.5 border border-neutral-500 dark:text-neutral-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
88
+ [class.border-red-500]="contactForm.get('message')?.invalid && contactForm.get('message')?.touched"></textarea>
89
+ <p *ngIf="contactForm.get('message')?.invalid && contactForm.get('message')?.touched"
90
+ class="text-xs text-red-500 mt-1">Message is required</p>
91
+ </div>
92
+
93
+ <!-- Submit Button -->
94
+ <button
95
+ type="submit"
96
+ [disabled]="contactForm.invalid"
97
+ (click)="navigateToTab(5)"
98
+ class="w-full bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition disabled:opacity-50 disabled:cursor-not-allowed">
99
+ Send message
100
+ </button>
101
+
102
+ </form>
103
+
104
+ </div>
105
+ </div>
106
+
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { HelpSupportComponent } from './help-support.component';
4
+
5
+ describe('HelpSupportComponent', () => {
6
+ let component: HelpSupportComponent;
7
+ let fixture: ComponentFixture<HelpSupportComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [HelpSupportComponent]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(HelpSupportComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });