Anupriya commited on
Commit
6dc3ffe
·
1 Parent(s): 6fb02c5

added footer and logo

Browse files
src/app/app.component.ts CHANGED
@@ -1,19 +1,38 @@
1
- import { Component } from '@angular/core';
2
  import { AuthService } from './auth/auth.service';
 
 
 
3
 
4
  @Component({
5
  selector: 'app-root',
6
  templateUrl: './app.component.html',
7
  styleUrl: './app.component.css'
8
  })
9
- export class AppComponent {
10
- constructor(private authService: AuthService) { }
 
 
 
 
 
11
  title = 'Py-Learn';
12
 
13
  ngOnInit(): void {
 
 
 
 
 
 
 
 
 
 
 
14
  this.authService.checkSession().subscribe((status) => {
15
  if (status) {
16
- this.authService.startAutoRefresh(); // ✅ Start auto-refresh once app loads and session is valid
17
  }
18
  });
19
  }
 
1
+ import { Component, Inject, OnInit } from '@angular/core';
2
  import { AuthService } from './auth/auth.service';
3
+ import { BrandService } from './shared/brand.service';
4
+ import { Title } from '@angular/platform-browser';
5
+ import { DOCUMENT } from '@angular/common';
6
 
7
  @Component({
8
  selector: 'app-root',
9
  templateUrl: './app.component.html',
10
  styleUrl: './app.component.css'
11
  })
12
+ export class AppComponent implements OnInit {
13
+ constructor(
14
+ private authService: AuthService,
15
+ public brandService: BrandService,
16
+ private titleService: Title,
17
+ @Inject(DOCUMENT) private document: Document
18
+ ) { }
19
  title = 'Py-Learn';
20
 
21
  ngOnInit(): void {
22
+ // Set dynamic title
23
+ this.titleService.setTitle(this.brandService.name);
24
+
25
+ // Set dynamic favicon
26
+ const favicon: HTMLLinkElement | null = this.document.querySelector("link[rel*='icon']");
27
+ if (favicon) {
28
+ favicon.href = this.brandService.name === 'Py-Learn'
29
+ ? 'assets/favicon.png'
30
+ : 'assets/majema-favicon.png'; // Make sure this file exists for MJ-Learn
31
+ }
32
+
33
  this.authService.checkSession().subscribe((status) => {
34
  if (status) {
35
+ this.authService.startAutoRefresh();
36
  }
37
  });
38
  }
src/app/app.module.ts CHANGED
@@ -25,6 +25,7 @@ import { RootRedirectComponent } from './root-redirect/root-redirect.component';
25
  import { HeaderComponent } from './shared/header/header.component';
26
  import { SignInComponent } from './sign-in/sign-in.component';
27
  import { PronunciationComponent } from './pronunciation/pronunciation.component';
 
28
 
29
  @NgModule({
30
  declarations: [
@@ -40,7 +41,8 @@ import { PronunciationComponent } from './pronunciation/pronunciation.component'
40
  AuthenticationComponent,
41
  AuthComponent,
42
  RootRedirectComponent,
43
- PronunciationComponent
 
44
 
45
  ],
46
  imports: [
 
25
  import { HeaderComponent } from './shared/header/header.component';
26
  import { SignInComponent } from './sign-in/sign-in.component';
27
  import { PronunciationComponent } from './pronunciation/pronunciation.component';
28
+ import { FooterComponent } from './footer/footer.component';
29
 
30
  @NgModule({
31
  declarations: [
 
41
  AuthenticationComponent,
42
  AuthComponent,
43
  RootRedirectComponent,
44
+ PronunciationComponent,
45
+ FooterComponent
46
 
47
  ],
48
  imports: [
src/app/chat/chat.component.css CHANGED
@@ -1,50 +1,3 @@
1
- /* Header Container */
2
- .header-container {
3
- display: flex;
4
- justify-content: space-between;
5
- align-items: center;
6
- padding: 0vw 1vw;
7
- background-color: #009688;
8
- box-shadow: 0 0.4vw 0.8vw rgba(0, 0, 0, 0.2);
9
- width: 100%;
10
- position: sticky;
11
- top: 0;
12
- z-index: 1000;
13
- }
14
-
15
- .logo img {
16
- max-width: 5vw;
17
- height: auto;
18
- background: #e5e7eb;
19
- border-radius: 1vw;
20
- margin: 0.5vw;
21
- }
22
-
23
- .home-btn img {
24
- width: 5vw;
25
- }
26
-
27
- .home-btn img:hover {
28
- transform: scale(1.1);
29
- }
30
-
31
- h1 {
32
- font-size: 3vw;
33
- color: white;
34
- font-family: Super Cartoon;
35
- position: absolute;
36
- top: 50%;
37
- left: 50%;
38
- transform: translate(-50%, -50%);
39
- }
40
-
41
- @font-face {
42
- font-family: 'Super Cartoon';
43
- src: url('../../assets/font/Super Cartoon.ttf') format('truetype');
44
- font-weight: normal;
45
- font-style: normal;
46
- font-display: swap;
47
- }
48
 
49
  /* Layout */
50
  .chat-container {
@@ -55,16 +8,6 @@ h1 {
55
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
56
  }
57
 
58
- .chat-bg {
59
- position: absolute;
60
- top: 0;
61
- left: 0;
62
- width: 100%;
63
- height: 100%;
64
- object-fit: fill;
65
- z-index: -1;
66
- opacity: 0.2;
67
- }
68
 
69
  .chat-box {
70
  display: flex;
@@ -511,8 +454,7 @@ h1 {
511
  gap: 10px;
512
  align-items: center;
513
  margin-right: 15px;
514
- position: relative;
515
- right: -16vw;
516
  }
517
 
518
  /* Disabled state for toggle buttons */
@@ -575,8 +517,7 @@ h1 {
575
  gap: 18px;
576
  align-items: center;
577
  margin-right: 15px;
578
- position: relative;
579
- right: -16vw;
580
  }
581
 
582
  .toggle-btn.modern {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
 
2
  /* Layout */
3
  .chat-container {
 
8
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
9
  }
10
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  .chat-box {
13
  display: flex;
 
454
  gap: 10px;
455
  align-items: center;
456
  margin-right: 15px;
457
+
 
458
  }
459
 
460
  /* Disabled state for toggle buttons */
 
517
  gap: 18px;
518
  align-items: center;
519
  margin-right: 15px;
520
+
 
521
  }
522
 
523
  .toggle-btn.modern {
src/app/chat/chat.component.html CHANGED
@@ -1,18 +1,8 @@
1
  <!-- Full chat.component.html matching chat.component.ts -->
2
  <div class="chat-container">
3
- <div class="header-container">
4
- <div class="logo">
5
- <a (click)="goToHome()" routerLink="/home" class="brand-link">
6
- <img src="assets/images/pykara-logo.png" alt="Pykara Logo" />
7
- </a>
8
- <span class="product-name">Py-Learn.</span>
9
- </div>
10
-
11
- <div class="header-title">
12
- <h1>Grammar Chat</h1>
13
- </div>
14
 
15
- <div class="toggle-buttons-container modern-toggle">
16
  <button class="toggle-btn modern"
17
  [class.active]="isVoiceEnabled"
18
  [class.muted]="!isVoiceEnabled"
@@ -49,16 +39,11 @@
49
  <img src="assets/images/chat/info.png" alt="User Guide" class="toggle-icon" />
50
  </button>
51
  </div>
 
 
52
 
53
- <div class="home-btn">
54
- <a (click)="goToHome()" routerLink="/home">
55
- <img src="assets/images/home.png" alt="Home" class="home-icon" />
56
- </a>
57
- </div>
58
- </div>
59
 
60
  <div class="chat-box" #chatBox>
61
- <img src="assets/images/chat/chatbg.png" alt="Chat Background" class="chat-bg" />
62
 
63
  <div *ngFor="let message of messages; let i = index">
64
  <!-- User message -->
@@ -73,110 +58,110 @@
73
  </div>
74
 
75
  <!-- AI message -->
76
-
77
  <div *ngIf="message.from === 'ai'" class="message-wrapper ai">
78
- <div class="profile-pic">
79
- <img src="assets/images/chat/natasha.png" alt="AI" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  </div>
81
 
82
- <div class="message structured-response">
83
- <div [innerHTML]="formatStructuredResponse(message.text)"></div>
84
-
85
- <!-- Follow-ups -->
86
- <div class="followups" *ngIf="message.suggestions?.length">
87
- <div class="followups-title">Follow-up suggestions</div>
88
- <button class="followup-chip"
89
- *ngFor="let s of message.suggestions"
90
- (click)="selectHardcodedQuestion(s)"
91
- title="Ask this next">
92
- {{ s }}
93
- </button>
94
- </div>
95
-
96
- <div class="message-timestamp">
97
- {{ message.timestamp }}
98
-
99
- <!-- Copy -->
100
- <button class="icon-btn" (click)="copyToClipboard(message.text, i)"
101
- [attr.aria-label]="copySuccessIndex === i ? 'Copied' : 'Copy message'"
102
- title="Copy message">
103
- <ng-container *ngIf="copySuccessIndex === i; else showCopy">
104
- <span class="copy-tick">&#10003;</span>
105
- </ng-container>
106
- <ng-template #showCopy>
107
- <img src="assets/images/chat/copy.png" alt="Copy" class="meta-icon" />
108
- </ng-template>
109
- </button>
110
-
111
- <!-- Audio: play / stop -->
112
- <button class="icon-btn"
113
- *ngIf="message.audioUrl && isReadingIndex !== i"
114
- (click)="playServerAudioForMessage(i)"
115
- aria-label="Play audio" title="Play audio">
116
- <img src="assets/images/chat/speaker.png" alt="Play audio" class="meta-icon" />
117
- </button>
118
-
119
- <button class="icon-btn"
120
- *ngIf="message.audioUrl && isReadingIndex === i"
121
- (click)="stopReadAloud()"
122
- aria-label="Stop audio" title="Stop audio">
123
- <img src="assets/images/chat/stop-button.png" alt="Stop audio" class="meta-icon" />
124
- </button>
125
-
126
- <!-- Generate audio on demand -->
127
- <button class="icon-btn"
128
- *ngIf="!message.audioUrl"
129
- (click)="synthesizeAudioAndPlay(i)"
130
- [disabled]="message.isSynthesizing"
131
- aria-label="Generate audio"
132
- title="Generate audio">
133
- <ng-container *ngIf="!message.isSynthesizing; else audioSpinner">
134
- <img src="assets/images/chat/speaker.png" alt="Generate audio" class="meta-icon" />
135
- </ng-container>
136
- <ng-template #audioSpinner>
137
- <img src="assets/images/chat/loading-spinner.gif" alt="Generating audio" class="meta-icon" />
138
- </ng-template>
139
- </button>
140
-
141
- <!-- Video: generate, play, stop -->
142
- <!-- Show Generate when no cached video -->
143
- <button class="icon-btn"
144
- *ngIf="!message.videoUrl && !message.isVideoSynthesizing"
145
- (click)="synthesizeVideoAndPlay(i)"
146
- aria-label="Generate video"
147
- title="Generate video">
148
- <img src="assets/images/chat/video.png" alt="Generate video" class="meta-icon" />
149
- </button>
150
-
151
- <!-- Spinner while generating video -->
152
- <button class="icon-btn" *ngIf="!message.videoUrl && message.isVideoSynthesizing" disabled>
153
- <img src="assets/images/chat/loading-spinner.gif" alt="Generating video" class="meta-icon" />
154
- </button>
155
-
156
- <!-- Toggle inline video (single button). Show video.png initially, switches to no-video.png when enabled -->
157
- <button class="icon-btn"
158
- *ngIf="message.videoUrl"
159
- (click)="toggleMessageVideo(i)"
160
- [class.active]="isVideoEnabledIndex[i]"
161
- aria-label="Toggle video"
162
- title="Toggle video">
163
- <img [src]="isVideoEnabledIndex[i] ? 'assets/images/chat/no-video.png' : 'assets/images/chat/video.png'"
164
- alt="Video" class="meta-icon" />
165
- </button>
166
-
167
- </div>
168
-
169
- <!-- Inline video player -->
170
- <video *ngIf="isVideoEnabledIndex[i] && message.playingVideoUrl"
171
- id="inline-video-{{i}}"
172
- class="tutor-video"
173
- [src]="message.playingVideoUrl"
174
- (ended)="onMessageVideoEnded(i)"
175
- controls autoplay>
176
- </video>
177
  </div>
 
 
 
 
 
 
 
 
 
178
  </div>
179
  </div>
 
180
 
181
  <!-- Typing indicator -->
182
  <div *ngIf="isTyping" class="typing-indicator" aria-live="polite">
 
1
  <!-- Full chat.component.html matching chat.component.ts -->
2
  <div class="chat-container">
3
+ <app-header [title]="'Grammar Chat'">
4
+ <div class="toggle-buttons-container modern-toggle" header-right>
 
 
 
 
 
 
 
 
 
5
 
 
6
  <button class="toggle-btn modern"
7
  [class.active]="isVoiceEnabled"
8
  [class.muted]="!isVoiceEnabled"
 
39
  <img src="assets/images/chat/info.png" alt="User Guide" class="toggle-icon" />
40
  </button>
41
  </div>
42
+ </app-header>
43
+ <img src="assets/images/grammar-bg.png" alt="Chat Background" class="grammar-bg" />
44
 
 
 
 
 
 
 
45
 
46
  <div class="chat-box" #chatBox>
 
47
 
48
  <div *ngFor="let message of messages; let i = index">
49
  <!-- User message -->
 
58
  </div>
59
 
60
  <!-- AI message -->
61
+
62
  <div *ngIf="message.from === 'ai'" class="message-wrapper ai">
63
+ <div class="profile-pic">
64
+ <img src="assets/images/chat/natasha.png" alt="AI" />
65
+ </div>
66
+
67
+ <div class="message structured-response">
68
+ <div [innerHTML]="formatStructuredResponse(message.text)"></div>
69
+
70
+ <!-- Follow-ups -->
71
+ <div class="followups" *ngIf="message.suggestions?.length">
72
+ <div class="followups-title">Follow-up suggestions</div>
73
+ <button class="followup-chip"
74
+ *ngFor="let s of message.suggestions"
75
+ (click)="selectHardcodedQuestion(s)"
76
+ title="Ask this next">
77
+ {{ s }}
78
+ </button>
79
  </div>
80
 
81
+ <div class="message-timestamp">
82
+ {{ message.timestamp }}
83
+
84
+ <!-- Copy -->
85
+ <button class="icon-btn" (click)="copyToClipboard(message.text, i)"
86
+ [attr.aria-label]="copySuccessIndex === i ? 'Copied' : 'Copy message'"
87
+ title="Copy message">
88
+ <ng-container *ngIf="copySuccessIndex === i; else showCopy">
89
+ <span class="copy-tick">&#10003;</span>
90
+ </ng-container>
91
+ <ng-template #showCopy>
92
+ <img src="assets/images/chat/copy.png" alt="Copy" class="meta-icon" />
93
+ </ng-template>
94
+ </button>
95
+
96
+ <!-- Audio: play / stop -->
97
+ <button class="icon-btn"
98
+ *ngIf="message.audioUrl && isReadingIndex !== i"
99
+ (click)="playServerAudioForMessage(i)"
100
+ aria-label="Play audio" title="Play audio">
101
+ <img src="assets/images/chat/speaker.png" alt="Play audio" class="meta-icon" />
102
+ </button>
103
+
104
+ <button class="icon-btn"
105
+ *ngIf="message.audioUrl && isReadingIndex === i"
106
+ (click)="stopReadAloud()"
107
+ aria-label="Stop audio" title="Stop audio">
108
+ <img src="assets/images/chat/stop-button.png" alt="Stop audio" class="meta-icon" />
109
+ </button>
110
+
111
+ <!-- Generate audio on demand -->
112
+ <button class="icon-btn"
113
+ *ngIf="!message.audioUrl"
114
+ (click)="synthesizeAudioAndPlay(i)"
115
+ [disabled]="message.isSynthesizing"
116
+ aria-label="Generate audio"
117
+ title="Generate audio">
118
+ <ng-container *ngIf="!message.isSynthesizing; else audioSpinner">
119
+ <img src="assets/images/chat/speaker.png" alt="Generate audio" class="meta-icon" />
120
+ </ng-container>
121
+ <ng-template #audioSpinner>
122
+ <img src="assets/images/chat/loading-spinner.gif" alt="Generating audio" class="meta-icon" />
123
+ </ng-template>
124
+ </button>
125
+
126
+ <!-- Video: generate, play, stop -->
127
+ <!-- Show Generate when no cached video -->
128
+ <button class="icon-btn"
129
+ *ngIf="!message.videoUrl && !message.isVideoSynthesizing"
130
+ (click)="synthesizeVideoAndPlay(i)"
131
+ aria-label="Generate video"
132
+ title="Generate video">
133
+ <img src="assets/images/chat/video.png" alt="Generate video" class="meta-icon" />
134
+ </button>
135
+
136
+ <!-- Spinner while generating video -->
137
+ <button class="icon-btn" *ngIf="!message.videoUrl && message.isVideoSynthesizing" disabled>
138
+ <img src="assets/images/chat/loading-spinner.gif" alt="Generating video" class="meta-icon" />
139
+ </button>
140
+
141
+ <!-- Toggle inline video (single button). Show video.png initially, switches to no-video.png when enabled -->
142
+ <button class="icon-btn"
143
+ *ngIf="message.videoUrl"
144
+ (click)="toggleMessageVideo(i)"
145
+ [class.active]="isVideoEnabledIndex[i]"
146
+ aria-label="Toggle video"
147
+ title="Toggle video">
148
+ <img [src]="isVideoEnabledIndex[i] ? 'assets/images/chat/no-video.png' : 'assets/images/chat/video.png'"
149
+ alt="Video" class="meta-icon" />
150
+ </button>
151
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  </div>
153
+
154
+ <!-- Inline video player -->
155
+ <video *ngIf="isVideoEnabledIndex[i] && message.playingVideoUrl"
156
+ id="inline-video-{{i}}"
157
+ class="tutor-video"
158
+ [src]="message.playingVideoUrl"
159
+ (ended)="onMessageVideoEnded(i)"
160
+ controls autoplay>
161
+ </video>
162
  </div>
163
  </div>
164
+ </div>
165
 
166
  <!-- Typing indicator -->
167
  <div *ngIf="isTyping" class="typing-indicator" aria-live="polite">
src/app/chat/chat.component.ts CHANGED
@@ -7,13 +7,14 @@ import { Subscription } from 'rxjs';
7
  import { finalize } from 'rxjs/operators';
8
  import { HttpClient } from '@angular/common/http';
9
  import { lastValueFrom } from 'rxjs';
 
10
 
11
  type Grade = 'lowergrade' | 'midgrade' | 'highergrade';
12
 
13
  @Component({
14
  selector: 'app-chat',
15
  standalone: true,
16
- imports: [FormsModule, CommonModule, RouterModule],
17
  templateUrl: './chat.component.html',
18
  styleUrls: ['./chat.component.css']
19
  })
 
7
  import { finalize } from 'rxjs/operators';
8
  import { HttpClient } from '@angular/common/http';
9
  import { lastValueFrom } from 'rxjs';
10
+ import { HeaderComponent } from '../shared/header/header.component';
11
 
12
  type Grade = 'lowergrade' | 'midgrade' | 'highergrade';
13
 
14
  @Component({
15
  selector: 'app-chat',
16
  standalone: true,
17
+ imports: [FormsModule, CommonModule, RouterModule, HeaderComponent],
18
  templateUrl: './chat.component.html',
19
  styleUrls: ['./chat.component.css']
20
  })
src/app/footer/footer.component.css ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ .user-guide-overlay {
4
+ position: fixed;
5
+ inset: 0;
6
+ background: rgba(0, 0, 0, 0.35);
7
+ z-index: 2000;
8
+ }
9
+
10
+ .user-guide-modal {
11
+ position: fixed;
12
+ top: 52%;
13
+ left: 50%;
14
+ transform: translate(-50%, -50%);
15
+ height: 38vw;
16
+ width: 50vw;
17
+ background: linear-gradient(135deg, #fff 80%, #e3fcec 100%);
18
+ color: #222;
19
+ box-shadow: 0 12px 40px rgba(93, 145, 195, .22);
20
+ border-radius: 18px;
21
+ padding: 1vw;
22
+ z-index: 2001;
23
+ overflow: visible;
24
+ border: 10px solid #009688;
25
+ box-sizing: border-box;
26
+ font-weight: lighter;
27
+ }
28
+
29
+ .user-guide-modal > ul,
30
+ .user-guide-modal > ol {
31
+ max-height: 34.6vw;
32
+ overflow-y: auto;
33
+ padding-right: 0.5vw;
34
+ margin: 0;
35
+ }
36
+
37
+ .user-guide-close-icon {
38
+ position: absolute;
39
+ top: -22px;
40
+ right: -22px;
41
+ background: #009688;
42
+ border: none;
43
+ width: 44px;
44
+ height: 44px;
45
+ border-radius: 50%;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ font-size: 2vw;
50
+ color: black;
51
+ cursor: pointer;
52
+ z-index: 2010;
53
+ box-shadow: 0 2px 8px rgba(93,145,195,0.18);
54
+ transition: background 0.2s, color 0.2s;
55
+
56
+ }
57
+
58
+ .user-guide-close-icon:hover {
59
+ background: white;
60
+ color: black;
61
+ }
62
+
63
+ .user-guide-modal li {
64
+ line-height: 1.7;
65
+ font-size: 1.1vw;
66
+ background: rgba(93, 145, 195, .07);
67
+ padding: .5vw .5vw;
68
+ box-shadow: 0 2px 8px rgba(93, 145, 195, .06);
69
+ }
70
+
71
+ .user-guide-modal li b {
72
+ color: #2b6296;
73
+ font-size: 1.15vw;
74
+ }
75
+
76
+ .user-guide-modal a {
77
+ color: #137ec4;
78
+ text-decoration: underline;
79
+ font-weight: 600;
80
+ }
81
+
82
+ .user-guide-modal a:hover {
83
+ color: #009688;
84
+ text-decoration: underline;
85
+ }
86
+
87
+ footer {
88
+ background: linear-gradient(to right, #011022, #01030a);
89
+ color: #fff;
90
+ text-align: center;
91
+ padding: 15px 10px;
92
+ text-align: center;
93
+ width: 100%;
94
+ position: relative;
95
+ margin-top: 28vw;
96
+ font-size: 18px;
97
+ font-weight: 700;
98
+ font-family: Inter, Segoe UI, -apple-system, BlinkMacSystemFont, sans-serif;
99
+ }
100
+
101
+ p.footer {
102
+ line-height: 1;
103
+ margin-top: 1vw;
104
+ }
105
+
106
+ footer a {
107
+ color: #fff;
108
+ text-decoration: none;
109
+ margin: 0 5px;
110
+ margin-bottom: 0.5vw;
111
+ display: inline-block;
112
+ }
113
+
114
+ footer .social-icons {
115
+ margin-top: 0.5vw;
116
+ display: flex;
117
+ justify-content: center;
118
+ gap: 28px;
119
+ flex-wrap: wrap;
120
+ }
121
+
122
+ footer .social-icons .social-icon {
123
+ width: 42px;
124
+ height: 42px;
125
+ display: inline-flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ border-radius: 50%;
129
+ background-color: #fff;
130
+ color: #38bdf8;
131
+ font-size: 18px;
132
+ box-shadow: 0 0 0 1px #214055, 0 4px 14px #0006;
133
+ transition: background-color 0.25s, color 0.25s, transform 0.25s, box-shadow 0.25s;
134
+ text-decoration: none;
135
+ }
136
+
137
+ footer .social-icons .social-icon:hover {
138
+ background-color: #38bdf8;
139
+ color: #fff;
140
+ transform: translateY(-4px);
141
+ box-shadow: 0 6px 20px #38bdf8aa, 0 0 0 2px #38bdf8 inset;
142
+ }
143
+
144
+ footer .social-icons .social-icon.facebook {
145
+ color: #1877f2;
146
+ }
147
+
148
+ footer .social-icons .social-icon.youtube {
149
+ color: #ff0000;
150
+ }
151
+
152
+ footer .social-icons .social-icon.linkedin {
153
+ color: #0a66c2;
154
+ }
155
+
156
+ footer .social-icons .social-icon.instagram {
157
+ color: #fd5949;
158
+ }
159
+
160
+ footer .social-icons .social-icon.facebook:hover {
161
+ background-color: #1877f2;
162
+ color: #fff;
163
+ }
164
+
165
+ footer .social-icons .social-icon.youtube:hover {
166
+ background-color: #ff0000;
167
+ color: #fff;
168
+ }
169
+
170
+ footer .social-icons .social-icon.linkedin:hover {
171
+ background-color: #0a66c2;
172
+ color: #fff;
173
+ }
174
+
175
+ footer .social-icons .social-icon.instagram:hover {
176
+ background: radial-gradient(circle at 30% 110%, #fdf497 0%, #fd5949 45%, #d6249f 60%, #285aeb 90%);
177
+ color: #fff;
178
+ filter: brightness(1.15);
179
+ box-shadow: 0 6px 22px rgba(253, 89, 73, .6);
180
+ }
181
+
182
+ @media (max-width: 600px) {
183
+ .user-guide-modal {
184
+ width: 90vw;
185
+ height: 60vw;
186
+ border-radius: 12px;
187
+ padding: 4vw 2vw 2vw 2vw;
188
+ }
189
+
190
+ .user-guide-modal > ul,
191
+ .user-guide-modal > ol {
192
+ max-height: 52vw;
193
+ overflow-y: auto;
194
+ padding-right: 1.5vw;
195
+ }
196
+
197
+ .user-guide-close-icon {
198
+ font-size: 6vw;
199
+ top: 2vw;
200
+ right: 2vw;
201
+ width: 6vw;
202
+ height: 6vw;
203
+ display: flex;
204
+ align-items: center;
205
+ justify-content: center;
206
+ line-height: 1;
207
+ }
208
+ }
src/app/footer/footer.component.html ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <footer *ngIf="brandService.showFooter">
2
+ <ng-container [ngSwitch]="brandService.name">
3
+ <ng-container *ngSwitchCase="'Py-Learn'">
4
+ <p class="footer">© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p>
5
+ <a href="https://pykara.ai/contact-us/" target="blank">Contact Us</a> |
6
+ <a href="#" (click)="openPrivacyPopup($event)">Privacy Policy</a> |
7
+ <a href="#" (click)="openTermsPopup($event)">Terms & Conditions</a>
8
+ <div class="social-icons">
9
+ <a [href]="brandService.socialLinksCurrent.linkedin" target="_blank" class="social-icon linkedin" aria-label="LinkedIn">
10
+ <i class="fab fa-linkedin-in"></i>
11
+ </a>
12
+ <a [href]="brandService.socialLinksCurrent.youtube" target="_blank" class="social-icon youtube" aria-label="YouTube">
13
+ <i class="fab fa-youtube"></i>
14
+ </a>
15
+ <a [href]="brandService.socialLinksCurrent.facebook" target="_blank" class="social-icon facebook" aria-label="Facebook">
16
+ <i class="fab fa-facebook-f"></i>
17
+ </a>
18
+ <a [href]="brandService.socialLinksCurrent.instagram" target="_blank" class="social-icon instagram" aria-label="Instagram">
19
+ <i class="fab fa-instagram"></i>
20
+ </a>
21
+ <a [href]="brandService.socialLinksCurrent.website" target="_blank" class="social-icon website" aria-label="Website">
22
+ <i class="fas fa-globe"></i>
23
+ </a>
24
+ </div>
25
+ <!-- Privacy Policy Popup -->
26
+ <div *ngIf="showPrivacyPopup">
27
+ <div class="user-guide-overlay" (click)="closePrivacyPopup()"></div>
28
+ <div class="user-guide-modal" role="dialog" aria-modal="true" aria-labelledby="privacyTitle">
29
+ <button class="user-guide-close-icon" (click)="closePrivacyPopup()" aria-label="Close">×</button>
30
+ <ul style="text-align: justify; font-size: 15px;">
31
+ <!-- Privacy Policy content (all used) -->
32
+ <li id="privacyTitle">
33
+ <b>Privacy Policy — Py-learn</b><br>
34
+ <ul style="list-style: unset; margin-left: 1.25rem;">
35
+ <li><b>Last Updated:</b> November 2025</li>
36
+ <li><b>Who we are:</b> Py-learn is operated by <b>Pykara Technologies Private Limited</b> (“we”, “us”, “our”), the data controller for personal data processed through the platform.</li>
37
+ <li><b>Contact:</b> <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
38
+ </ul>
39
+ </li>
40
+
41
+ <li>
42
+ <b>Overview</b><br>
43
+ <ul style="list-style: unset; margin-left: 1.25rem;">
44
+ <li>We collect limited personal data to deliver an AI-powered e-learning experience (chatbots, video lessons, quizzes) and to improve learning outcomes.</li>
45
+ <li>We comply with applicable laws, including GDPR where relevant to users in the EEA/UK.</li>
46
+ </ul>
47
+ </li>
48
+
49
+ <li>
50
+ <b>Information We Collect</b><br>
51
+ <ul style="list-style: unset; margin-left: 1.25rem;">
52
+ <li><b>Account Data:</b> name, email, password (hashed), profile details.</li>
53
+ <li><b>Learning Data:</b> enrolled courses, lessons viewed, quiz results, progress analytics, feedback.</li>
54
+ <li><b>Technical Data:</b> device/browser info, IP address, timestamps, logs, approximate location from IP.</li>
55
+ <li><b>Cookies & Similar Tech:</b> preferences, session management, analytics (see “Cookies”).</li>
56
+ </ul>
57
+ </li>
58
+
59
+ <li>
60
+ <b>Purposes of Processing</b><br>
61
+ <ul style="list-style: unset; margin-left: 1.25rem;">
62
+ <li>Provide and secure the service; authenticate users; maintain accounts.</li>
63
+ <li>Personalise content, recommendations, and difficulty levels.</li>
64
+ <li>Measure performance, improve features, and fix issues.</li>
65
+ <li>Send essential service communications (policy changes, security notices).</li>
66
+ <li>Comply with legal obligations and enforce Terms.</li>
67
+ </ul>
68
+ </li>
69
+
70
+ <li>
71
+ <b>Legal Bases (GDPR)</b><br>
72
+ <ul style="list-style: unset; margin-left: 1.25rem;">
73
+ <li><b>Contract necessity:</b> to deliver core features you request.</li>
74
+ <li><b>Legitimate interests:</b> service improvement, security, fraud prevention (balanced against your rights).</li>
75
+ <li><b>Consent:</b> non-essential cookies, optional marketing (you can withdraw at any time).</li>
76
+ <li><b>Legal obligation:</b> records, compliance, and requests from authorities where required by law.</li>
77
+ </ul>
78
+ </li>
79
+
80
+ <li>
81
+ <b>Automated Processing & AI</b><br>
82
+ <ul style="list-style: unset; margin-left: 1.25rem;">
83
+ <li>AI is used to generate explanations, hints, and recommendations. We do not make decisions with legal or similarly significant effects solely by automated means.</li>
84
+ </ul>
85
+ </li>
86
+
87
+ <li>
88
+ <b>Third-Party Processors & Transfers</b><br>
89
+ <ul style="list-style: unset; margin-left: 1.25rem;">
90
+ <li>We use reputable vendors for AI inference, hosting, analytics, email delivery, and content streaming. They act under contracts and process data only on our instructions.</li>
91
+ <li>Where data is transferred outside your country (including outside the EEA/UK), appropriate safeguards are applied (e.g., Standard Contractual Clauses).</li>
92
+ </ul>
93
+ </li>
94
+
95
+ <li>
96
+ <b>Data Sharing</b><br>
97
+ <ul style="list-style: unset; margin-left: 1.25rem;">
98
+ <li>With service providers under confidentiality and security obligations.</li>
99
+ <li>For legal reasons (court orders, to protect users and our rights, prevent fraud or abuse).</li>
100
+ <li>Business changes (merger, acquisition); we will ensure comparable protections or notify you of material changes.</li>
101
+ </ul>
102
+ </li>
103
+
104
+ <li>
105
+ <b>Data Security & Retention</b><br>
106
+ <ul style="list-style: unset; margin-left: 1.25rem;">
107
+ <li>We apply technical and organisational measures (encryption at rest/in transit, access controls, monitoring).</li>
108
+ <li>We retain personal data only as long as needed for the purposes above or as required by law. <b>Inactive accounts may be deleted after 24 months.</b></li>
109
+ </ul>
110
+ </li>
111
+
112
+ <li>
113
+ <b>Cookies & Similar Technologies</b><br>
114
+ <ul style="list-style: unset; margin-left: 1.25rem;">
115
+ <li><b>Essential:</b> login/session, security, load balancing.</li>
116
+ <li><b>Preferences:</b> UI settings, language, playback choices.</li>
117
+ <li><b>Analytics:</b> usage statistics to improve features.</li>
118
+ <li>You may control cookies via your browser. Disabling some cookies may affect functionality.</li>
119
+ </ul>
120
+ </li>
121
+
122
+ <li>
123
+ <b>Children’s Privacy</b><br>
124
+ <ul style="list-style: unset; margin-left: 1.25rem;">
125
+ <li>The service is intended for users aged 13+. If we learn that data from a child under 13 was collected without appropriate consent, we will delete it.</li>
126
+ </ul>
127
+ </li>
128
+
129
+ <li>
130
+ <b>Your Rights (EEA/UK where applicable)</b><br>
131
+ <ul style="list-style: unset; margin-left: 1.25rem;">
132
+ <li>Access, rectify, erase, restrict processing, object, and data portability.</li>
133
+ <li>Withdraw consent (for consent-based processing) without affecting prior lawful processing.</li>
134
+ <li>Lodge a complaint with a supervisory authority. We encourage contacting us first at <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a>.</li>
135
+ </ul>
136
+ </li>
137
+
138
+ <li>
139
+ <b>Data Breach Notification</b><br>
140
+ <ul style="list-style: unset; margin-left: 1.25rem;">
141
+ <li>We will notify affected users and relevant authorities when legally required.</li>
142
+ </ul>
143
+ </li>
144
+
145
+ <li>
146
+ <b>Changes to this Policy</b><br>
147
+ <ul style="list-style: unset; margin-left: 1.25rem;">
148
+ <li>We may update this policy. The “Last Updated” date reflects the latest version; material changes may be additionally notified in-app or by email.</li>
149
+ </ul>
150
+ </li>
151
+
152
+ <li>
153
+ <b>Contact</b><br>
154
+ <ul style="list-style: unset; margin-left: 1.25rem;">
155
+ <li>Email: <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
156
+ <li>Registered Office: Pykara Technologies Private Limited, Chennai, Tamil Nadu, India</li>
157
+ </ul>
158
+ </li>
159
+ </ul>
160
+ </div>
161
+ </div>
162
+ <!-- Terms & Conditions Popup -->
163
+ <div *ngIf="showTermsPopup">
164
+ <div class="user-guide-overlay" (click)="closeTermsPopup()"></div>
165
+ <div class="user-guide-modal" role="dialog" aria-modal="true" aria-labelledby="termsTitle">
166
+ <button class="user-guide-close-icon" (click)="closeTermsPopup()" aria-label="Close">×</button>
167
+ <ul style="text-align: justify; font-size: 15px;">
168
+ <!-- Terms & Conditions content (all used) -->
169
+ <li id="termsTitle">
170
+ <b>Terms & Conditions — Py-learn</b><br>
171
+ <ul style="list-style: unset; margin-left: 1.25rem;">
172
+ <li><b>Last Updated:</b> November 2025</li>
173
+ <li>By using Py-learn, you agree to these Terms and our Privacy Policy.</li>
174
+ </ul>
175
+ </li>
176
+
177
+ <li>
178
+ <b>Eligibility & Accounts</b><br>
179
+ <ul style="list-style: unset; margin-left: 1.25rem;">
180
+ <li>You must be 13+ (or the age of digital consent in your region). Users under 18 require parental/guardian consent.</li>
181
+ <li>You are responsible for the accuracy of your information and for safeguarding your credentials.</li>
182
+ </ul>
183
+ </li>
184
+
185
+ <li>
186
+ <b>Permitted Use & Prohibited Conduct</b><br>
187
+ <ul style="list-style: unset; margin-left: 1.25rem;">
188
+ <li>Use the service for lawful, educational purposes only.</li>
189
+ <li>Do not attempt unauthorised access, disrupt the service, reverse engineer components, or misuse AI features.</li>
190
+ <li>International users must comply with local laws.</li>
191
+ </ul>
192
+ </li>
193
+
194
+ <li>
195
+ <b>Content & Intellectual Property</b><br>
196
+ <ul style="list-style: unset; margin-left: 1.25rem;">
197
+ <li>All software, UI, designs, text, graphics, videos, and datasets are owned by Pykara Technologies or its licensors.</li>
198
+ <li>Copying, redistribution, or commercial use requires prior written consent.</li>
199
+ <li>When you submit content (e.g., answers, uploads), you grant us a non-exclusive, royalty-free licence to host and process it to operate and improve the service (including for AI model quality and safety, where allowed by law and your settings).</li>
200
+ </ul>
201
+ </li>
202
+
203
+ <li>
204
+ <b>AI Outputs & Educational Disclaimer</b><br>
205
+ <ul style="list-style: unset; margin-left: 1.25rem;">
206
+ <li>AI responses support learning but may contain errors. Verify outputs before relying on them; they are not professional advice.</li>
207
+ </ul>
208
+ </li>
209
+
210
+ <li>
211
+ <b>Fees, Trials & Refunds</b><br>
212
+ <ul style="list-style: unset; margin-left: 1.25rem;">
213
+ <li>Some features may be paid (subscriptions or one-off fees). Prices and taxes are shown at checkout.</li>
214
+ <li>Payments are processed securely by third-party providers. Unless required by law, payments are non-refundable.</li>
215
+ </ul>
216
+ </li>
217
+
218
+ <li>
219
+ <b>Third-Party Links & Services</b><br>
220
+ <ul style="list-style: unset; margin-left: 1.25rem;">
221
+ <li>We may link to third-party content or integrate with providers (hosting, analytics, AI). We are not responsible for third-party terms or policies.</li>
222
+ </ul>
223
+ </li>
224
+
225
+ <li>
226
+ <b>Termination & Suspension</b><br>
227
+ <ul style="list-style: unset; margin-left: 1.25rem;">
228
+ <li>We may suspend or terminate access for violations, fraud, or security risks. You may request account closure at any time.</li>
229
+ </ul>
230
+ </li>
231
+
232
+ <li>
233
+ <b>Warranties & Liability</b><br>
234
+ <ul style="list-style: unset; margin-left: 1.25rem;">
235
+ <li>The service is provided “as is” and “as available” without warranties of any kind.</li>
236
+ <li>To the fullest extent permitted by law, we are not liable for indirect, incidental, special, consequential, or punitive damages.</li>
237
+ </ul>
238
+ </li>
239
+
240
+ <li>
241
+ <b>Indemnity</b><br>
242
+ <ul style="list-style: unset; margin-left: 1.25rem;">
243
+ <li>You agree to indemnify and hold us harmless from claims arising from your misuse of the service or breach of these Terms.</li>
244
+ </ul>
245
+ </li>
246
+
247
+ <li>
248
+ <b>Changes to the Service or Terms</b><br>
249
+ <ul style="list-style: unset; margin-left: 1.25rem;">
250
+ <li>We may modify features or these Terms. Continued use after changes indicates acceptance; material updates may be additionally notified in-app or by email.</li>
251
+ </ul>
252
+ </li>
253
+
254
+ <li>
255
+ <b>Governing Law & Disputes</b><br>
256
+ <ul style="list-style: unset; margin-left: 1.25rem;">
257
+ <li>Governing law: India. Exclusive jurisdiction: courts in Chennai, Tamil Nadu, India.</li>
258
+ </ul>
259
+ </li>
260
+
261
+ <li>
262
+ <b>Contact</b><br>
263
+ <ul style="list-style: unset; margin-left: 1.25rem;">
264
+ <li>Support & legal queries: <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
265
+ <li>Registered Office: Pykara Technologies Private Limited, Chennai, Tamil Nadu, India</li>
266
+ </ul>
267
+ </li>
268
+ </ul>
269
+ </div>
270
+ </div>
271
+ </ng-container>
272
+ <!-- MJ-Learn Footer -->
273
+ <ng-container *ngSwitchCase="'MJ-Learn'">
274
+ <p>© 2025 Majema Company. All rights reserved.</p>
275
+ <a href="mailto:kundtjanst@majema.se" target="_blank">Contact Us</a> |
276
+ <a href="#" (click)="openPrivacyPopup($event)">Privacy Policy</a> |
277
+ <a href="#" (click)="openTermsPopup($event)">Terms & Conditions</a>
278
+ <div class="social-icons">
279
+ <a [href]="brandService.socialLinksCurrent.linkedin" target="_blank" class="social-icon linkedin" aria-label="LinkedIn">
280
+ <i class="fab fa-linkedin-in"></i>
281
+ </a>
282
+ <a [href]="brandService.socialLinksCurrent.youtube" target="_blank" class="social-icon youtube" aria-label="YouTube">
283
+ <i class="fab fa-youtube"></i>
284
+ </a>
285
+ <a [href]="brandService.socialLinksCurrent.facebook" target="_blank" class="social-icon facebook" aria-label="Facebook">
286
+ <i class="fab fa-facebook-f"></i>
287
+ </a>
288
+ <a [href]="brandService.socialLinksCurrent.instagram" target="_blank" class="social-icon instagram" aria-label="Instagram">
289
+ <i class="fab fa-instagram"></i>
290
+ </a>
291
+ <a [href]="brandService.socialLinksCurrent.website" target="_blank" class="social-icon website" aria-label="Website">
292
+ <i class="fas fa-globe"></i>
293
+ </a>
294
+ </div>
295
+ <!-- Privacy Policy Popup -->
296
+ <div *ngIf="showPrivacyPopup">
297
+ <div class="user-guide-overlay" (click)="closePrivacyPopup()"></div>
298
+ <div class="user-guide-modal" role="dialog" aria-modal="true" aria-labelledby="privacyTitle">
299
+ <button class="user-guide-close-icon" (click)="closePrivacyPopup()" aria-label="Close">×</button>
300
+ <ul style="text-align: justify; font-size: 15px;">
301
+ <li id="privacyTitle">
302
+ <b>Privacy Policy — Majema</b><br>
303
+ <ul style="list-style: unset; margin-left: 1.25rem;">
304
+ <li><b>What personal data is collected?</b><br>Majemaförlaget AB processes personal data that you provide to us. Personal data that will be processed includes, for example, name, social security number, school affiliation, role, interests related to teaching, address, e-mail address and telephone number. You provide the personal data yourself in connection with ordering, expressions of interest, by telephone or when visiting teaching material exhibitions, school visits and teacher meetings. On our website we use cookies and save your IP address.</li>
305
+ <li>It is voluntary to provide your personal data, but in some cases we cannot provide you with the service and/or product you have ordered if you do not provide your personal data.</li>
306
+ <li>We do not collect any personal information from third-party companies. Advertising may occur on external websites and then we can obtain statistics that are not personal through cookies.</li>
307
+ <li><b>Purposes of personal data processing</b><br>Your personal data will be processed in order for us to fulfill our commitments to you. We may also process your data for the purposes below as well as for our market and customer analyses and statistics in order to provide you with a better offer and better service. We may, unless you have objected to direct marketing in writing, also use your personal data to provide various offers of services and/or products from us. You may unsubscribe from receiving such offers at any time with each marketing message. If we process data for other purposes, we will inform you of this.</li>
308
+ <li><b>Storage of personal data</b><br>We may transfer your data to IT service providers and other service providers who then process the data on our behalf. If we transfer your data outside the EU, we ensure that we have a legal basis for such transfer. This can be done through the EU Commission's standard contractual clauses. Majema takes the greatest possible security measures to protect your personal data. All identifiable personal data is subject to access restrictions to prevent unauthorized access, alteration or misuse. We will store your data for as long as necessary to fulfill our obligations to you.</li>
309
+ <li><b>Our legal basis</b><br>We process your data based on our legitimate interest or to enable us to fulfill our obligations to you.</li>
310
+ <li><b>Cookies – Information collected via www.majema.se</b><br>www.majema.se is managed by Majemaförlaget AB. Majema's website uses cookies to give you access to additional functions, such as previous orders and to simplify logging into your account. You can refuse the use of cookies through your browser settings. Majema uses analysis tools to get a picture of how visitors use our websites, in order to improve the content, navigation and structure of the websites. The analysis tools use JavaScript and cookies and the information generated by these through your use of the website (including your IP address) will be forwarded to the analysis tools' service providers and may be stored on servers outside the EU. The service providers may also transfer this information to third parties if required by law or in cases where a third party processes the information on their behalf. By using Majema's websites without declining third-party cookies, you agree that the service providers process your data in the manner and for the purposes described above. If you do not want your visits to Majema's website to be included in the service providers' statistics, there is an add-on that you can install in your browser.</li>
311
+ <li><b>Your rights</b><br>According to applicable data protection legislation, you have the right to request that incorrect personal data be corrected, blocked, restricted or deleted. In addition, you can also object to processing or request that your data be transferred to another data controller, so-called data portability. You also have the right to request written information, a so-called register extract, about the personal data we process about you once a year, free of charge, by sending a written signed application to our address below. Mark the application with "Request for register extract". If you are dissatisfied with our processing, you have the right to file a complaint with the Swedish Data Protection Authority.</li>
312
+ <li><b>Contact information</b><br>Majemaförlaget AB, 556414-9135, is the data controller for the processing of your personal data that you provide when filling out contact forms on the website. Address: Majemaförlaget AB, Box 4016, 131 04 Nacka</li>
313
+ </ul>
314
+ </li>
315
+ </ul>
316
+ </div>
317
+ </div>
318
+ <!-- Terms & Conditions Popup -->
319
+ <div *ngIf="showTermsPopup">
320
+ <div class="user-guide-overlay" (click)="closeTermsPopup()"></div>
321
+ <div class="user-guide-modal" role="dialog" aria-modal="true" aria-labelledby="termsTitle">
322
+ <button class="user-guide-close-icon" (click)="closeTermsPopup()" aria-label="Close">×</button>
323
+ <ul style="text-align: justify; font-size: 15px;">
324
+ <li id="termsTitle">
325
+ <b>Terms & Conditions — Majema</b><br>
326
+ <ul style="list-style: unset; margin-left: 1.25rem;">
327
+ <li>By using Majema, you agree to these Terms and our Privacy Policy.</li>
328
+ </ul>
329
+ </li>
330
+ <li><b>System requirements</b><br>
331
+ <ul style="list-style: unset; margin-left: 1.25rem;">
332
+ <li>Majema's digital learning materials work on computers, tablets and interactive whiteboards. Our digital learning materials are based on HTML5 and JavaScript. This means that older versions of browsers are not always fully supported. We recommend using an updated version of the Chrome browser to best experience our digital learning materials.</li>
333
+ <li>Here you can download <a href="https://www.google.com/chrome/" target="_blank">Google Chrome for free</a>.</li>
334
+ <li>For the best user experience, you should make sure these four points are met:
335
+ <ol style="margin-left: 1.5rem;">
336
+ <li>Make sure your browser is set to 100% zoom. This is important for the content to look correct on the screen.</li>
337
+ <li>Update your browser.</li>
338
+ <li>Clear your cache. Select: settings, privacy and security, clear browsing data. (If clearing the cache isn't enough, you can open a new incognito window in Chrome and log in from there.)</li>
339
+ <li>If you are using a tablet – update your operating system. This is done under settings.</li>
340
+ </ol>
341
+ </li>
342
+ <li><b>Web browser</b><br>
343
+ We recommend that you always update your browser to the latest version.<br>
344
+ We test our digital learning materials against the following browsers: Chrome, Edge, Safari and Firefox.
345
+ </li>
346
+ <li><b>License management</b><br>
347
+ All of our websites are licensed to apply to one teacher and class for one year from the date of activation.<br>
348
+ If there is a student website, it is activated at the same time as you activate the teacher website.<br>
349
+ To extend a license, you need to make a new purchase, in the webshop, through a retailer or with our customer service.<br>
350
+ Should a license need to be transferred from one teacher to another, please contact our customer service and we will help you with this.<br>
351
+ You can change your account details yourself under the My Account tab when you are logged in.
352
+ </li>
353
+ <li><b>Operating system</b><br>
354
+ We recommend that you always update your operating system to the latest version.<br>
355
+ <ul style="list-style: disc; margin-left: 1.5rem;">
356
+ <li>Windows 7, 8, 10, 11</li>
357
+ <li>Chrome OS (Chromebook)</li>
358
+ <li>Mac OS (11 and later)</li>
359
+ <li>Android tablet 11 or later (with Chrome browser)</li>
360
+ <li>iOS 14 and later (iPad operating system)</li>
361
+ </ul>
362
+ Majema does not guarantee support for devices and operating systems that are no longer updated by the manufacturer.
363
+ </li>
364
+ </ul>
365
+ </li>
366
+ </ul>
367
+ </div>
368
+ </div>
369
+ </ng-container>
370
+ </ng-container>
371
+ </footer>
372
+
src/app/footer/footer.component.ts ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component } from '@angular/core';
2
+ import { BrandService } from '../shared/brand.service';
3
+
4
+ @Component({
5
+ selector: 'app-footer',
6
+ templateUrl: './footer.component.html',
7
+ styleUrls: ['./footer.component.css']
8
+ })
9
+ export class FooterComponent {
10
+ showPrivacyPopup = false;
11
+ showTermsPopup = false;
12
+
13
+ constructor(public brandService: BrandService) {}
14
+
15
+ openPrivacyPopup(event: Event): void {
16
+ event.preventDefault();
17
+ this.showPrivacyPopup = true;
18
+ }
19
+
20
+ closePrivacyPopup(): void {
21
+ this.showPrivacyPopup = false;
22
+ }
23
+
24
+ openTermsPopup(event: Event): void {
25
+ event.preventDefault();
26
+ this.showTermsPopup = true;
27
+ }
28
+
29
+ closeTermsPopup(): void {
30
+ this.showTermsPopup = false;
31
+ }
32
+ }
src/app/home/home.component.css CHANGED
@@ -356,100 +356,6 @@ h1 {
356
  font-size: 1.15vw;
357
  }
358
 
359
- footer {
360
- background: linear-gradient(to right, #011022, #01030a);
361
- color: #fff;
362
- text-align: center;
363
- padding: 15px 10px;
364
- text-align: center;
365
- width: 100%;
366
- position: relative;
367
- margin-top: 28vw;
368
- font-size: 18px;
369
- font-weight: 700;
370
- font-family: Inter, Segoe UI, -apple-system, BlinkMacSystemFont, sans-serif;
371
- }
372
-
373
- p.footer {
374
- line-height: 1;
375
- margin-top: 1vw;
376
- }
377
-
378
- footer a {
379
- color: #fff;
380
- text-decoration: none;
381
- margin: 0 5px;
382
- margin-bottom: 0.5vw;
383
- display: inline-block;
384
- }
385
-
386
- footer .social-icons {
387
- margin-top: 0.5vw;
388
- display: flex;
389
- justify-content: center;
390
- gap: 28px;
391
- flex-wrap: wrap;
392
- }
393
-
394
- footer .social-icons .social-icon {
395
- width: 42px;
396
- height: 42px;
397
- display: inline-flex;
398
- align-items: center;
399
- justify-content: center;
400
- border-radius: 50%;
401
- background-color: #fff;
402
- color: #38bdf8;
403
- font-size: 18px;
404
- box-shadow: 0 0 0 1px #214055, 0 4px 14px #0006;
405
- transition: background-color 0.25s, color 0.25s, transform 0.25s, box-shadow 0.25s;
406
- text-decoration: none;
407
- }
408
-
409
- footer .social-icons .social-icon:hover {
410
- background-color: #38bdf8;
411
- color: #fff;
412
- transform: translateY(-4px);
413
- box-shadow: 0 6px 20px #38bdf8aa, 0 0 0 2px #38bdf8 inset;
414
- }
415
-
416
- footer .social-icons .social-icon.facebook {
417
- color: #1877f2;
418
- }
419
-
420
- footer .social-icons .social-icon.youtube {
421
- color: #ff0000;
422
- }
423
-
424
- footer .social-icons .social-icon.linkedin {
425
- color: #0a66c2;
426
- }
427
-
428
- footer .social-icons .social-icon.instagram {
429
- color: #fd5949;
430
- }
431
-
432
- footer .social-icons .social-icon.facebook:hover {
433
- background-color: #1877f2;
434
- color: #fff;
435
- }
436
-
437
- footer .social-icons .social-icon.youtube:hover {
438
- background-color: #ff0000;
439
- color: #fff;
440
- }
441
-
442
- footer .social-icons .social-icon.linkedin:hover {
443
- background-color: #0a66c2;
444
- color: #fff;
445
- }
446
-
447
- footer .social-icons .social-icon.instagram:hover {
448
- background: radial-gradient(circle at 30% 110%, #fdf497 0%, #fd5949 45%, #d6249f 60%, #285aeb 90%);
449
- color: #fff;
450
- filter: brightness(1.15);
451
- box-shadow: 0 6px 22px rgba(253, 89, 73, .6);
452
- }
453
 
454
  @media (max-width: 600px) {
455
  .user-guide-modal {
 
356
  font-size: 1.15vw;
357
  }
358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
  @media (max-width: 600px) {
361
  .user-guide-modal {
src/app/home/home.component.html CHANGED
@@ -96,32 +96,7 @@
96
  </div>
97
  </div>
98
  </div>
99
- </section>
100
-
101
- <!-- ================= FOOTER ================= -->
102
- <footer>
103
- <p class="footer">© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p>
104
- <a href="https://pykara.ai/contact-us/" target="blank">Contact Us</a> |
105
- <a href="#" (click)="openPrivacyPopup($event)">Privacy Policy</a> |
106
- <a href="#" (click)="openTermsPopup($event)">Terms & Conditions</a>
107
- <div class="social-icons">
108
- <a href="https://www.linkedin.com/in/pykara-technologies" target="_blank" class="social-icon linkedin" aria-label="LinkedIn">
109
- <i class="fab fa-linkedin-in"></i>
110
- </a>
111
- <a href="https://www.youtube.com/@PykaraTechnologies" target="_blank" class="social-icon youtube" aria-label="YouTube">
112
- <i class="fab fa-youtube"></i>
113
- </a>
114
- <a href="https://www.facebook.com/people/Pykara/100087653675803/" target="_blank" class="social-icon facebook" aria-label="Facebook">
115
- <i class="fab fa-facebook-f"></i>
116
- </a>
117
- <a href="https://www.instagram.com/pykaratechnologie/" target="_blank" class="social-icon instagram" aria-label="Instagram">
118
- <i class="fab fa-instagram"></i>
119
- </a>
120
- <a href="https://pykara.ai/" target="_blank" class="social-icon website" aria-label="Website">
121
- <i class="fas fa-globe"></i>
122
- </a>
123
- </div>
124
- </footer>
125
 
126
  <!-- ================= GUIDE POPUP ================= -->
127
  <div *ngIf="showGuidePopup">
@@ -1287,250 +1262,4 @@
1287
  </div>
1288
  </div>
1289
 
1290
- <!-- ================= PRIVACY POLICY POPUP ================= -->
1291
- <div *ngIf="showPrivacyPopup">
1292
- <div class="user-guide-overlay" (click)="closePrivacyPopup()"></div>
1293
- <div class="user-guide-modal" role="dialog" aria-modal="true" aria-labelledby="privacyTitle">
1294
- <button class="user-guide-close-icon" (click)="closePrivacyPopup()" aria-label="Close">×</button>
1295
- <ul style="text-align: justify; font-size: 15px;">
1296
- <!-- Privacy Policy content (all used) -->
1297
- <li id="privacyTitle">
1298
- <b>Privacy Policy — Py-learn</b><br>
1299
- <ul style="list-style: unset; margin-left: 1.25rem;">
1300
- <li><b>Last Updated:</b> November 2025</li>
1301
- <li><b>Who we are:</b> Py-learn is operated by <b>Pykara Technologies Private Limited</b> (“we”, “us”, “our”), the data controller for personal data processed through the platform.</li>
1302
- <li><b>Contact:</b> <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
1303
- </ul>
1304
- </li>
1305
-
1306
- <li>
1307
- <b>Overview</b><br>
1308
- <ul style="list-style: unset; margin-left: 1.25rem;">
1309
- <li>We collect limited personal data to deliver an AI-powered e-learning experience (chatbots, video lessons, quizzes) and to improve learning outcomes.</li>
1310
- <li>We comply with applicable laws, including GDPR where relevant to users in the EEA/UK.</li>
1311
- </ul>
1312
- </li>
1313
-
1314
- <li>
1315
- <b>Information We Collect</b><br>
1316
- <ul style="list-style: unset; margin-left: 1.25rem;">
1317
- <li><b>Account Data:</b> name, email, password (hashed), profile details.</li>
1318
- <li><b>Learning Data:</b> enrolled courses, lessons viewed, quiz results, progress analytics, feedback.</li>
1319
- <li><b>Technical Data:</b> device/browser info, IP address, timestamps, logs, approximate location from IP.</li>
1320
- <li><b>Cookies & Similar Tech:</b> preferences, session management, analytics (see “Cookies”).</li>
1321
- </ul>
1322
- </li>
1323
-
1324
- <li>
1325
- <b>Purposes of Processing</b><br>
1326
- <ul style="list-style: unset; margin-left: 1.25rem;">
1327
- <li>Provide and secure the service; authenticate users; maintain accounts.</li>
1328
- <li>Personalise content, recommendations, and difficulty levels.</li>
1329
- <li>Measure performance, improve features, and fix issues.</li>
1330
- <li>Send essential service communications (policy changes, security notices).</li>
1331
- <li>Comply with legal obligations and enforce Terms.</li>
1332
- </ul>
1333
- </li>
1334
-
1335
- <li>
1336
- <b>Legal Bases (GDPR)</b><br>
1337
- <ul style="list-style: unset; margin-left: 1.25rem;">
1338
- <li><b>Contract necessity:</b> to deliver core features you request.</li>
1339
- <li><b>Legitimate interests:</b> service improvement, security, fraud prevention (balanced against your rights).</li>
1340
- <li><b>Consent:</b> non-essential cookies, optional marketing (you can withdraw at any time).</li>
1341
- <li><b>Legal obligation:</b> records, compliance, and requests from authorities where required by law.</li>
1342
- </ul>
1343
- </li>
1344
-
1345
- <li>
1346
- <b>Automated Processing & AI</b><br>
1347
- <ul style="list-style: unset; margin-left: 1.25rem;">
1348
- <li>AI is used to generate explanations, hints, and recommendations. We do not make decisions with legal or similarly significant effects solely by automated means.</li>
1349
- </ul>
1350
- </li>
1351
-
1352
- <li>
1353
- <b>Third-Party Processors & Transfers</b><br>
1354
- <ul style="list-style: unset; margin-left: 1.25rem;">
1355
- <li>We use reputable vendors for AI inference, hosting, analytics, email delivery, and content streaming. They act under contracts and process data only on our instructions.</li>
1356
- <li>Where data is transferred outside your country (including outside the EEA/UK), appropriate safeguards are applied (e.g., Standard Contractual Clauses).</li>
1357
- </ul>
1358
- </li>
1359
-
1360
- <li>
1361
- <b>Data Sharing</b><br>
1362
- <ul style="list-style: unset; margin-left: 1.25rem;">
1363
- <li>With service providers under confidentiality and security obligations.</li>
1364
- <li>For legal reasons (court orders, to protect users and our rights, prevent fraud or abuse).</li>
1365
- <li>Business changes (merger, acquisition); we will ensure comparable protections or notify you of material changes.</li>
1366
- </ul>
1367
- </li>
1368
-
1369
- <li>
1370
- <b>Data Security & Retention</b><br>
1371
- <ul style="list-style: unset; margin-left: 1.25rem;">
1372
- <li>We apply technical and organisational measures (encryption at rest/in transit, access controls, monitoring).</li>
1373
- <li>We retain personal data only as long as needed for the purposes above or as required by law. <b>Inactive accounts may be deleted after 24 months.</b></li>
1374
- </ul>
1375
- </li>
1376
-
1377
- <li>
1378
- <b>Cookies & Similar Technologies</b><br>
1379
- <ul style="list-style: unset; margin-left: 1.25rem;">
1380
- <li><b>Essential:</b> login/session, security, load balancing.</li>
1381
- <li><b>Preferences:</b> UI settings, language, playback choices.</li>
1382
- <li><b>Analytics:</b> usage statistics to improve features.</li>
1383
- <li>You may control cookies via your browser. Disabling some cookies may affect functionality.</li>
1384
- </ul>
1385
- </li>
1386
-
1387
- <li>
1388
- <b>Children’s Privacy</b><br>
1389
- <ul style="list-style: unset; margin-left: 1.25rem;">
1390
- <li>The service is intended for users aged 13+. If we learn that data from a child under 13 was collected without appropriate consent, we will delete it.</li>
1391
- </ul>
1392
- </li>
1393
-
1394
- <li>
1395
- <b>Your Rights (EEA/UK where applicable)</b><br>
1396
- <ul style="list-style: unset; margin-left: 1.25rem;">
1397
- <li>Access, rectify, erase, restrict processing, object, and data portability.</li>
1398
- <li>Withdraw consent (for consent-based processing) without affecting prior lawful processing.</li>
1399
- <li>Lodge a complaint with a supervisory authority. We encourage contacting us first at <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a>.</li>
1400
- </ul>
1401
- </li>
1402
-
1403
- <li>
1404
- <b>Data Breach Notification</b><br>
1405
- <ul style="list-style: unset; margin-left: 1.25rem;">
1406
- <li>We will notify affected users and relevant authorities when legally required.</li>
1407
- </ul>
1408
- </li>
1409
-
1410
- <li>
1411
- <b>Changes to this Policy</b><br>
1412
- <ul style="list-style: unset; margin-left: 1.25rem;">
1413
- <li>We may update this policy. The “Last Updated” date reflects the latest version; material changes may be additionally notified in-app or by email.</li>
1414
- </ul>
1415
- </li>
1416
-
1417
- <li>
1418
- <b>Contact</b><br>
1419
- <ul style="list-style: unset; margin-left: 1.25rem;">
1420
- <li>Email: <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
1421
- <li>Registered Office: Pykara Technologies Private Limited, Chennai, Tamil Nadu, India</li>
1422
- </ul>
1423
- </li>
1424
- </ul>
1425
- </div>
1426
- </div>
1427
-
1428
- <!-- ================= TERMS & CONDITIONS POPUP ================= -->
1429
- <div *ngIf="showTermsPopup">
1430
- <div class="user-guide-overlay" (click)="closeTermsPopup()"></div>
1431
- <div class="user-guide-modal" role="dialog" aria-modal="true" aria-labelledby="termsTitle">
1432
- <button class="user-guide-close-icon" (click)="closeTermsPopup()" aria-label="Close">×</button>
1433
- <ul style="text-align: justify; font-size: 15px;">
1434
- <!-- Terms & Conditions content (all used) -->
1435
- <li id="termsTitle">
1436
- <b>Terms & Conditions — Py-learn</b><br>
1437
- <ul style="list-style: unset; margin-left: 1.25rem;">
1438
- <li><b>Last Updated:</b> November 2025</li>
1439
- <li>By using Py-learn, you agree to these Terms and our Privacy Policy.</li>
1440
- </ul>
1441
- </li>
1442
-
1443
- <li>
1444
- <b>Eligibility & Accounts</b><br>
1445
- <ul style="list-style: unset; margin-left: 1.25rem;">
1446
- <li>You must be 13+ (or the age of digital consent in your region). Users under 18 require parental/guardian consent.</li>
1447
- <li>You are responsible for the accuracy of your information and for safeguarding your credentials.</li>
1448
- </ul>
1449
- </li>
1450
-
1451
- <li>
1452
- <b>Permitted Use & Prohibited Conduct</b><br>
1453
- <ul style="list-style: unset; margin-left: 1.25rem;">
1454
- <li>Use the service for lawful, educational purposes only.</li>
1455
- <li>Do not attempt unauthorised access, disrupt the service, reverse engineer components, or misuse AI features.</li>
1456
- <li>International users must comply with local laws.</li>
1457
- </ul>
1458
- </li>
1459
-
1460
- <li>
1461
- <b>Content & Intellectual Property</b><br>
1462
- <ul style="list-style: unset; margin-left: 1.25rem;">
1463
- <li>All software, UI, designs, text, graphics, videos, and datasets are owned by Pykara Technologies or its licensors.</li>
1464
- <li>Copying, redistribution, or commercial use requires prior written consent.</li>
1465
- <li>When you submit content (e.g., answers, uploads), you grant us a non-exclusive, royalty-free licence to host and process it to operate and improve the service (including for AI model quality and safety, where allowed by law and your settings).</li>
1466
- </ul>
1467
- </li>
1468
-
1469
- <li>
1470
- <b>AI Outputs & Educational Disclaimer</b><br>
1471
- <ul style="list-style: unset; margin-left: 1.25rem;">
1472
- <li>AI responses support learning but may contain errors. Verify outputs before relying on them; they are not professional advice.</li>
1473
- </ul>
1474
- </li>
1475
-
1476
- <li>
1477
- <b>Fees, Trials & Refunds</b><br>
1478
- <ul style="list-style: unset; margin-left: 1.25rem;">
1479
- <li>Some features may be paid (subscriptions or one-off fees). Prices and taxes are shown at checkout.</li>
1480
- <li>Payments are processed securely by third-party providers. Unless required by law, payments are non-refundable.</li>
1481
- </ul>
1482
- </li>
1483
-
1484
- <li>
1485
- <b>Third-Party Links & Services</b><br>
1486
- <ul style="list-style: unset; margin-left: 1.25rem;">
1487
- <li>We may link to third-party content or integrate with providers (hosting, analytics, AI). We are not responsible for third-party terms or policies.</li>
1488
- </ul>
1489
- </li>
1490
-
1491
- <li>
1492
- <b>Termination & Suspension</b><br>
1493
- <ul style="list-style: unset; margin-left: 1.25rem;">
1494
- <li>We may suspend or terminate access for violations, fraud, or security risks. You may request account closure at any time.</li>
1495
- </ul>
1496
- </li>
1497
-
1498
- <li>
1499
- <b>Warranties & Liability</b><br>
1500
- <ul style="list-style: unset; margin-left: 1.25rem;">
1501
- <li>The service is provided “as is” and “as available” without warranties of any kind.</li>
1502
- <li>To the fullest extent permitted by law, we are not liable for indirect, incidental, special, consequential, or punitive damages.</li>
1503
- </ul>
1504
- </li>
1505
-
1506
- <li>
1507
- <b>Indemnity</b><br>
1508
- <ul style="list-style: unset; margin-left: 1.25rem;">
1509
- <li>You agree to indemnify and hold us harmless from claims arising from your misuse of the service or breach of these Terms.</li>
1510
- </ul>
1511
- </li>
1512
-
1513
- <li>
1514
- <b>Changes to the Service or Terms</b><br>
1515
- <ul style="list-style: unset; margin-left: 1.25rem;">
1516
- <li>We may modify features or these Terms. Continued use after changes indicates acceptance; material updates may be additionally notified in-app or by email.</li>
1517
- </ul>
1518
- </li>
1519
-
1520
- <li>
1521
- <b>Governing Law & Disputes</b><br>
1522
- <ul style="list-style: unset; margin-left: 1.25rem;">
1523
- <li>Governing law: India. Exclusive jurisdiction: courts in Chennai, Tamil Nadu, India.</li>
1524
- </ul>
1525
- </li>
1526
-
1527
- <li>
1528
- <b>Contact</b><br>
1529
- <ul style="list-style: unset; margin-left: 1.25rem;">
1530
- <li>Support & legal queries: <a href="mailto:info@pykara.ai">info&#64;pykara.ai</a></li>
1531
- <li>Registered Office: Pykara Technologies Private Limited, Chennai, Tamil Nadu, India</li>
1532
- </ul>
1533
- </li>
1534
- </ul>
1535
- </div>
1536
- </div>
 
96
  </div>
97
  </div>
98
  </div>
99
+ </section>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  <!-- ================= GUIDE POPUP ================= -->
102
  <div *ngIf="showGuidePopup">
 
1262
  </div>
1263
  </div>
1264
 
1265
+ <app-footer></app-footer>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/shared/brand.service.ts CHANGED
@@ -1,17 +1,89 @@
1
  import { Injectable } from '@angular/core';
2
  import { BehaviorSubject } from 'rxjs';
3
 
 
 
 
4
  @Injectable({ providedIn: 'root' })
5
  export class BrandService {
6
- private nameSubject = new BehaviorSubject<string>('Py-Learn');
7
- private logoSubject = new BehaviorSubject<string>('assets/images/pykara-logo.png');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- readonly name$ = this.nameSubject.asObservable();
10
- readonly logo$ = this.logoSubject.asObservable();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
- get name(): string { return this.nameSubject.value; }
13
- get logo(): string { return this.logoSubject.value; }
14
 
15
- setName(name: string) { this.nameSubject.next(name); }
 
 
 
 
 
 
 
 
 
16
  setLogo(path: string) { this.logoSubject.next(path); }
 
 
 
 
 
17
  }
 
 
 
1
  import { Injectable } from '@angular/core';
2
  import { BehaviorSubject } from 'rxjs';
3
 
4
+ // Add type for brand names
5
+ export type BrandName = 'Py-Learn' | 'MJ-Learn';
6
+
7
  @Injectable({ providedIn: 'root' })
8
  export class BrandService {
9
+ private nameSubject: BehaviorSubject<BrandName>;
10
+ private logoSubject: BehaviorSubject<string>;
11
+
12
+ readonly name$;
13
+ readonly logo$;
14
+
15
+ // Show/hide footer based on brand
16
+ public showFooter: boolean = true;
17
+
18
+ // Social links for each brand
19
+ public socialLinks: Record<BrandName, {
20
+ website: string;
21
+ linkedin: string;
22
+ youtube: string;
23
+ facebook: string;
24
+ instagram: string;
25
+ }> = {
26
+ 'Py-Learn': {
27
+ website: 'https://pykara.ai/',
28
+ linkedin: 'https://www.linkedin.com/in/pykara-technologies',
29
+ youtube: 'https://www.youtube.com/@PykaraTechnologies',
30
+ facebook: 'https://www.facebook.com/people/Pykara/100087653675803/',
31
+ instagram: 'https://www.instagram.com/pykaratechnologie/'
32
+ },
33
+ 'MJ-Learn': {
34
+ website: 'https://www.majema.se/',
35
+ linkedin: 'https://www.linkedin.com/company/majemaforlaget',
36
+ youtube: 'https://www.youtube.com/@majemaforlaget3014',
37
+ facebook: 'https://www.facebook.com/majemaforlaget',
38
+ instagram: 'https://www.instagram.com/majemaforlaget/'
39
+ }
40
+ };
41
+
42
+ constructor() {
43
+ // Detect brand by URL and port
44
+ const url = window.location.href;
45
+ const port = window.location.port;
46
+ let brandName: BrandName = 'Py-Learn'; // default to Py-Learn for type safety
47
+ let logoPath = 'assets/images/pykara-logo.png';
48
 
49
+ if (url.includes('pykara-py-learn')) {
50
+ brandName = 'Py-Learn';
51
+ logoPath = 'assets/images/pykara-logo.png';
52
+ this.showFooter = true;
53
+ } else if (url.includes('majemaai-mj-learn')) {
54
+ brandName = 'MJ-Learn';
55
+ logoPath = 'assets/images/majema-logo.png';
56
+ this.showFooter = false;
57
+ }
58
+ //else if (url.includes('localhost')) {
59
+ // if (port === '4300') {
60
+ // brandName = 'Py-Learn';
61
+ // logoPath = 'assets/images/pykara-logo.png';
62
+ // this.showFooter = true;
63
+ // } else if (port === '4200') {
64
+ // brandName = 'MJ-Learn';
65
+ // logoPath = 'assets/images/Majema.png';
66
+ // this.showFooter = false;
67
+ // }
68
+ //}
69
 
 
 
70
 
71
+ this.nameSubject = new BehaviorSubject<BrandName>(brandName);
72
+ this.logoSubject = new BehaviorSubject<string>(logoPath);
73
+ this.name$ = this.nameSubject.asObservable();
74
+ this.logo$ = this.logoSubject.asObservable();
75
+ }
76
+
77
+ get name(): BrandName { return this.nameSubject.value; }
78
+ get logo(): string { return this.logoSubject.value; }
79
+
80
+ setName(name: BrandName) { this.nameSubject.next(name); }
81
  setLogo(path: string) { this.logoSubject.next(path); }
82
+
83
+ get socialLinksCurrent() {
84
+ // Use 'as BrandName' to ensure type safety
85
+ return this.socialLinks[this.name as BrandName];
86
+ }
87
  }
88
+
89
+
src/app/sign-in/sign-in.component.html CHANGED
@@ -3,27 +3,28 @@
3
  <!-- Added brand logo top-left -->
4
  <div class="signin-brand-logo" role="banner">
5
  <a routerLink="/" aria-label="Pykara Home" class="brand-link">
6
- <img [src]="brand.logo" alt="Pykara Logo" class="brand-logo-img" />
7
 
8
 
9
  </a>
10
  </div>
11
 
 
12
  <!-- Social Media Icons Top-Right -->
13
  <div class="social-icons signin-social-icons">
14
- <a href="https://www.linkedin.com/in/pykara-technologies" target="_blank" class="social-icon linkedin" aria-label="LinkedIn">
15
  <i class="fab fa-linkedin-in"></i>
16
  </a>
17
- <a href="https://www.youtube.com/@PykaraTechnologies" target="_blank" class="social-icon youtube" aria-label="YouTube">
18
  <i class="fab fa-youtube"></i>
19
  </a>
20
- <a href="https://www.facebook.com/people/Pykara/100087653675803/" target="_blank" class="social-icon facebook" aria-label="Facebook">
21
  <i class="fab fa-facebook-f"></i>
22
  </a>
23
- <a href="https://www.instagram.com/pykaratechnologie/" target="_blank" class="social-icon instagram" aria-label="Instagram">
24
  <i class="fab fa-instagram"></i>
25
  </a>
26
- <a href="https://pykara.ai/" target="_blank" class="social-icon website" aria-label="Website">
27
  <i class="fas fa-globe"></i>
28
  </a>
29
  </div>
@@ -166,7 +167,7 @@
166
  <button class="modal-close" (click)="closeForgotModal()">Close</button>
167
  </div>
168
  </div>
169
- <!-- Added bottom-right watermark -->
170
- <!--<div class="signin-watermark" aria-hidden="true">© {{ brand.name }} 2025</div>-->
171
- <a class="signin-watermark" href="https://pykara.ai" target="_blank">www.pykara.ai</a>
172
  </section>
 
3
  <!-- Added brand logo top-left -->
4
  <div class="signin-brand-logo" role="banner">
5
  <a routerLink="/" aria-label="Pykara Home" class="brand-link">
6
+ <img [src]="brand.logo" [alt]="brand.name + ' Logo'" class="brand-logo-img" />
7
 
8
 
9
  </a>
10
  </div>
11
 
12
+
13
  <!-- Social Media Icons Top-Right -->
14
  <div class="social-icons signin-social-icons">
15
+ <a [href]="socialLinks.linkedin" target="_blank" class="social-icon linkedin" aria-label="LinkedIn">
16
  <i class="fab fa-linkedin-in"></i>
17
  </a>
18
+ <a [href]="socialLinks.youtube" target="_blank" class="social-icon youtube" aria-label="YouTube">
19
  <i class="fab fa-youtube"></i>
20
  </a>
21
+ <a [href]="socialLinks.facebook" target="_blank" class="social-icon facebook" aria-label="Facebook">
22
  <i class="fab fa-facebook-f"></i>
23
  </a>
24
+ <a [href]="socialLinks.instagram" target="_blank" class="social-icon instagram" aria-label="Instagram">
25
  <i class="fab fa-instagram"></i>
26
  </a>
27
+ <a [href]="socialLinks.website" target="_blank" class="social-icon website" aria-label="Website">
28
  <i class="fas fa-globe"></i>
29
  </a>
30
  </div>
 
167
  <button class="modal-close" (click)="closeForgotModal()">Close</button>
168
  </div>
169
  </div>
170
+ <a class="signin-watermark" [href]="socialLinks.website" target="_blank">
171
+ {{ websiteDisplay }}
172
+ </a>
173
  </section>
src/app/sign-in/sign-in.component.ts CHANGED
@@ -288,4 +288,17 @@ export class SignInComponent implements AfterViewInit, OnDestroy {
288
  this.cdr.markForCheck();
289
  }, 5000);
290
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  }
 
288
  this.cdr.markForCheck();
289
  }, 5000);
290
  }
291
+
292
+ // In SignInComponent
293
+ get socialLinks() {
294
+ return this.brand.socialLinksCurrent;
295
+ }
296
+
297
+ get websiteDisplay(): string {
298
+ // Remove protocol and any leading www.
299
+ return 'www.' + this.socialLinks.website.replace(/^https?:\/\/(www\.)?/, '');
300
+ }
301
+
302
+
303
+
304
  }
src/app/sign-up/sign-up.component.html CHANGED
@@ -106,30 +106,18 @@
106
  </div>
107
  </div>
108
 
109
- <div class="create-footer"><b>© Pykara Technologies, 2025. All rights reserved.</b></div>
 
 
 
 
110
  </form>
111
  </div>
112
  </div>
113
  </div>
114
  </div>
115
  </div>
116
- <!-- Detached overlay/modal so layout doesn't shift -->
117
- <!--<div class="role-help-overlay" *ngIf="showRoleInfo" (click)="hideRoleInfo()">
118
- <div class="role-help-modal" role="dialog" aria-label="Role descriptions" (click)="$event.stopPropagation()">
119
- <button type="button" class="role-help-close" aria-label="Close role info" (click)="hideRoleInfo()">×</button>
120
- <h3 class="role-help-title">Choose the right role</h3>
121
- <ul class="role-help-list">
122
- <li><strong>Admin:</strong> Full control: users, roles, system settings.</li>
123
- <li><strong>Teachers:</strong> Run assessments / training evaluations.</li>
124
- <li><strong>Student:</strong> Access learning modules, exercises.</li>-->
125
-
126
- <!--<li><strong>Lawyers:</strong> Review analytics for case & witness prep.</li>
127
- <li><strong>Investigators:</strong> Conduct interviews and capture signals.</li>-->
128
- <!--<li><strong>Others:</strong> General or limited access usage.</li>
129
- </ul>
130
- <p class="role-help-tip">Not sure? Select Others; an Admin can upgrade your role later.</p>
131
- </div>
132
- </div>-->
133
 
134
  <!-- Floating Info Popup -->
135
  <div *ngIf="showInfo" class="info-popup-bg">
 
106
  </div>
107
  </div>
108
 
109
+ <div class="create-footer">
110
+ <b>
111
+ © {{ brand.name === 'Py-Learn' ? 'Pykara Technologies' : 'Majema Company' }}, 2025. All rights reserved.
112
+ </b>
113
+ </div>
114
  </form>
115
  </div>
116
  </div>
117
  </div>
118
  </div>
119
  </div>
120
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
  <!-- Floating Info Popup -->
123
  <div *ngIf="showInfo" class="info-popup-bg">
src/app/sign-up/sign-up.component.ts CHANGED
@@ -4,6 +4,7 @@ import { FormBuilder, FormGroup, ReactiveFormsModule, Validators, AbstractContro
4
  import { Router, RouterLink } from '@angular/router';
5
  import { SignUpService } from './sign-up.service'; // Import the SignUpService
6
  import { trigger, transition, style, animate } from '@angular/animations';
 
7
 
8
  export function nameValidator(control: AbstractControl): ValidationErrors | null {
9
  const value = control.value || '';
@@ -71,7 +72,8 @@ export class SignUpComponent implements OnInit, OnDestroy {
71
  private fb: FormBuilder,
72
  private router: Router,
73
  private signUpService: SignUpService,
74
- private cdr: ChangeDetectorRef
 
75
  ) {
76
  this.form = this.fb.group({
77
  name: ['', [Validators.required, Validators.minLength(2), nameValidator]],
 
4
  import { Router, RouterLink } from '@angular/router';
5
  import { SignUpService } from './sign-up.service'; // Import the SignUpService
6
  import { trigger, transition, style, animate } from '@angular/animations';
7
+ import { BrandService } from '../shared/brand.service'; // <-- Add this import
8
 
9
  export function nameValidator(control: AbstractControl): ValidationErrors | null {
10
  const value = control.value || '';
 
72
  private fb: FormBuilder,
73
  private router: Router,
74
  private signUpService: SignUpService,
75
+ private cdr: ChangeDetectorRef,
76
+ public brand: BrandService
77
  ) {
78
  this.form = this.fb.group({
79
  name: ['', [Validators.required, Validators.minLength(2), nameValidator]],
src/assets/images/Majema.png ADDED

Git LFS Details

  • SHA256: 35f2d665d28978a0c24b69454abd7b6dcc633566812df227c70a2242417c5750
  • Pointer size: 132 Bytes
  • Size of remote file: 1.48 MB
src/assets/majema-favicon.png ADDED

Git LFS Details

  • SHA256: b3921d919e2feba4c61910c98303be14c8bc33f4c88a0e6ba6dfbdb4263d854f
  • Pointer size: 129 Bytes
  • Size of remote file: 3.46 kB