l-min-components 1.7.1306 → 1.7.1307

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "l-min-components",
3
- "version": "1.7.1306",
3
+ "version": "1.7.1307",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/assets",
@@ -131,10 +131,10 @@ const AppMainLayout = ({ children }) => {
131
131
  handleCurrentSubscription,
132
132
  getDefaultAccount,
133
133
  handleGetDefaultAccount,
134
- } = useHeader();
135
-
136
- // get current default account and store in cookie (from api)
137
-
134
+ user,
135
+ userDetails,
136
+ } = useHeader({ default: true });
137
+ // get current default account and store in cookie (from api);
138
138
  useEffect(() => {
139
139
  if (getDefaultAccount?.data) {
140
140
  const date = new Date();
@@ -298,15 +298,17 @@ const AppMainLayout = ({ children }) => {
298
298
  setIsTranslationsLoading,
299
299
  } = useTranslation(wordBank);
300
300
 
301
- const messageKit = useMessageKit({ affiliateAccount }); // useMessageKit
301
+ const messageKit = useMessageKit(); // useMessageKit
302
302
 
303
303
  return (
304
304
  <OutletContext.Provider
305
305
  value={{
306
306
  messageKit,
307
307
  affiliateAccount,
308
+ userDetails,
308
309
  findAccountNames,
309
310
  accountName,
311
+ user,
310
312
  setRightComponent,
311
313
  setRightLayout,
312
314
  generalData,
@@ -45,7 +45,7 @@ export const MainLayout = styled.div`
45
45
  width: 100%;
46
46
  padding: 84px 20px 20px 0;
47
47
  /* max-height: 100vh; */
48
- min-height: 100vh;
48
+ max-height: 100vh;
49
49
  `;
50
50
 
51
51
  export const LeftLayout = styled.div`
@@ -0,0 +1,681 @@
1
+ import React, {
2
+ useState,
3
+ useRef,
4
+ useEffect,
5
+ forwardRef,
6
+ useImperativeHandle,
7
+ } from "react";
8
+
9
+ // Main React component for the Emoji Input
10
+ // Manages its own state internally (uncontrolled behavior)
11
+ const EmojiInput = forwardRef(
12
+ (
13
+ {
14
+ containerStyle,
15
+ inputStyle,
16
+ buttonStyle,
17
+ pickerStyle,
18
+ onChange, // Exposed onChange prop
19
+ onPaste, // Exposed onPaste prop
20
+ onFocus, // Exposed onFocus prop
21
+ onBlur, // Exposed onBlur prop
22
+ onKeyDown, // Exposed onKeyDown prop
23
+ onKeyUp, // Exposed onKeyUp prop
24
+ onKeyPress, // Exposed onKeyPress prop
25
+ onEnter, // Exposed onEnter prop for handling single Enter key press
26
+ disabled = false, // Disabled prop
27
+ readOnly = false, // ReadOnly prop
28
+ fontSize = "16px", // Font size prop
29
+ lineHeight = "20px", // Line height prop
30
+ // Removed 'value' prop for uncontrolled behavior
31
+ },
32
+ ref
33
+ ) => {
34
+ // Internal state is the source of truth for the input value
35
+ const [internalValue, setInternalValue] = useState("");
36
+
37
+ // State to manage the visibility of the emoji picker
38
+ const [showPicker, setShowPicker] = useState(false);
39
+ // State to manage the position of the emoji picker ('top' or 'bottom')
40
+ const [pickerPosition, setPickerPosition] = useState("bottom"); // Default to bottom (above input)
41
+ // State to manage the emoji search term
42
+ const [searchTerm, setSearchTerm] = useState("");
43
+
44
+ // Ref to access the textarea element directly
45
+ const textareaRef = useRef(null);
46
+ // Ref to access the input container element
47
+ const containerRef = useRef(null);
48
+ // Ref to access the emoji picker element
49
+ const emojiPickerRef = useRef(null);
50
+ // Ref to access the emoji toggle button element
51
+ const emojiToggleRef = useRef(null);
52
+
53
+ // Expose imperative methods to the parent component via the ref
54
+ useImperativeHandle(ref, () => ({
55
+ // Method to get the current input value (from internal state)
56
+ getValue: () => internalValue,
57
+ // Method to set the input value (updates internal state and calls onChange)
58
+ setValue: (newValue) => {
59
+ setInternalValue(newValue);
60
+ // Manually trigger onChange with the new value
61
+ if (onChange) {
62
+ const event = { target: { value: newValue } };
63
+ onChange(event);
64
+ }
65
+ },
66
+ // Method to focus the textarea
67
+ focus: () => {
68
+ if (textareaRef.current) {
69
+ textareaRef.current.focus();
70
+ }
71
+ },
72
+ // Method to clear the input (updates internal state to empty and calls onChange)
73
+ clear: () => {
74
+ setInternalValue("");
75
+ // Manually trigger onChange with empty string
76
+ if (onChange) {
77
+ const event = { target: { value: "" } };
78
+ onChange(event);
79
+ }
80
+ },
81
+ // Method to toggle the emoji picker visibility
82
+ toggleEmojiPicker: () => togglePicker(),
83
+ // Method to show the emoji picker
84
+ showEmojiPicker: () => setShowPicker(true),
85
+ // Method to hide the emoji picker
86
+ hideEmojiPicker: () => setShowPicker(false),
87
+ // You can expose other methods as needed
88
+ }));
89
+
90
+ // Expanded list of emojis with names/keywords for search
91
+ const allEmojisWithNames = [
92
+ { emoji: "😊", name: "Smiling Face with Smiling Eyes" },
93
+ { emoji: "😂", name: "Face with Tears of Joy" },
94
+ { emoji: "😍", name: "Smiling Face with Heart-Eyes" },
95
+ { emoji: "🥰", name: "Smiling Face with Hearts" },
96
+ { emoji: "😘", name: "Face Blowing a Kiss" },
97
+ { emoji: "😗", name: "Kissing Face" },
98
+ { emoji: "😙", name: "Kissing Face with Smiling Eyes" },
99
+ { emoji: "😚", name: "Kissing Face with Closed Eyes" },
100
+ { emoji: "😋", name: "Face Savoring Food" },
101
+ { emoji: "😛", name: "Face with Tongue" },
102
+ { emoji: "😜", name: "Winking Face with Tongue" },
103
+ { emoji: "🤪", name: "Zany Face" },
104
+ { emoji: "🤨", name: "Face with Raised Eyebrow" },
105
+ { emoji: "🧐", name: "Face with Monocle" },
106
+ { emoji: "🤓", name: "Nerd Face" },
107
+ { emoji: "😎", name: "Smiling Face with Sunglasses" },
108
+ { emoji: "🤩", name: "Star-Struck" },
109
+ { emoji: "🥳", name: "Partying Face" },
110
+ { emoji: "😏", name: "Smirking Face" },
111
+ { emoji: "😒", name: "Unamused Face" },
112
+ { emoji: "😞", name: "Disappointed Face" },
113
+ { emoji: "😔", name: "Pensive Face" },
114
+ { emoji: "😟", name: "Worried Face" },
115
+ { emoji: "😕", name: "Confused Face" },
116
+ { emoji: "🙁", name: "Slightly Frowning Face" },
117
+ { emoji: "☹️", name: "Frowning Face" },
118
+ { emoji: "😣", name: "Persevering Face" },
119
+ { emoji: "😖", name: "Confounded Face" },
120
+ { emoji: "😫", name: "Tired Face" },
121
+ { emoji: "😩", name: "Weary Face" },
122
+ { emoji: "🥺", name: "Pleading Face" },
123
+ { emoji: "😢", name: "Crying Face" },
124
+ { emoji: "😭", name: "Loudly Crying Face" },
125
+ { emoji: "😤", name: "Face with Steam From Nose" },
126
+ { emoji: "😠", name: "Angry Face" },
127
+ { emoji: "😡", name: "Pouting Face" },
128
+ { emoji: "🤬", name: "Face with Symbols on Mouth" },
129
+ { emoji: "😈", name: "Smiling Face with Horns" },
130
+ { emoji: "👿", name: "Angry Face with Horns" },
131
+ { emoji: "💀", name: "Skull" },
132
+ { emoji: "👻", name: "Ghost" },
133
+ { emoji: "👽", name: "Alien" },
134
+ { emoji: "🤖", name: "Robot" },
135
+ { emoji: "🎃", name: "Jack-O-Lantern" },
136
+ { emoji: "😺", name: "Grinning Cat" },
137
+ { emoji: "😸", name: "Grinning Cat with Smiling Eyes" },
138
+ { emoji: "😹", name: "Cat with Tears of Joy" },
139
+ { emoji: "😻", name: "Smiling Cat with Heart-Eyes" },
140
+ { emoji: "😼", name: "Cat with Wry Smile" },
141
+ { emoji: "😽", name: "Kissing Cat" },
142
+ { emoji: "🙀", name: "Weary Cat" },
143
+ { emoji: "😿", name: "Crying Cat" },
144
+ { emoji: "😾", name: "Pouting Cat" },
145
+ { emoji: "👍", name: "Thumbs Up" },
146
+ { emoji: "👎", name: "Thumbs Down" },
147
+ { emoji: "👏", name: "Clapping Hands" },
148
+ { emoji: "🙌", name: "Raising Hands" },
149
+ { emoji: "🤲", name: "Palms Up Together" },
150
+ { emoji: "🤝", name: "Handshake" },
151
+ { emoji: "🙏", name: "Folded Hands" },
152
+ { emoji: "💪", name: "Flexed Biceps" },
153
+ { emoji: "👈", name: "Backhand Index Pointing Left" },
154
+ { emoji: "👉", name: "Backhand Index Pointing Right" },
155
+ { emoji: "☝️", name: "Index Pointing Up" },
156
+ { emoji: "👆", name: "Index Pointing Up" }, // Duplicate, can be refined
157
+ { emoji: "👇", name: "Index Pointing Down" },
158
+ { emoji: "✌️", name: "Victory Hand" },
159
+ { emoji: "🤞", name: "Crossed Fingers" },
160
+ { emoji: "🖖", name: "Vulcan Salute" },
161
+ { emoji: "🤘", name: "Sign of the Horns" },
162
+ { emoji: "🤙", name: "Call Me Hand" },
163
+ { emoji: "🖐️", name: "Hand with Fingers Splayed" },
164
+ { emoji: "✋", name: "Raised Hand" },
165
+ { emoji: "👌", name: "OK Hand" },
166
+ { emoji: "🤏", name: "Pinched Fingers" },
167
+ { emoji: "👋", name: "Waving Hand" },
168
+ { emoji: "🤚", name: "Raised Back of Hand" },
169
+ { emoji: "❤️", name: "Red Heart" },
170
+ { emoji: "🧡", name: "Orange Heart" },
171
+ { emoji: "💛", name: "Yellow Heart" },
172
+ { emoji: "💚", name: "Green Heart" },
173
+ { emoji: "💙", name: "Blue Heart" },
174
+ { emoji: "💜", name: "Purple Heart" },
175
+ { emoji: "🖤", name: "Black Heart" },
176
+ { emoji: "🤍", name: "White Heart" },
177
+ { emoji: "🤎", name: "Brown Heart" },
178
+ { emoji: "💔", name: "Broken Heart" },
179
+ { emoji: "❣️", name: "Heart Exclamation" },
180
+ { emoji: "💕", name: "Two Hearts" },
181
+ { emoji: "💞", name: "Revolving Hearts" },
182
+ { emoji: "💓", name: "Beating Heart" },
183
+ { emoji: "💗", name: "Growing Heart" },
184
+ { emoji: "💖", name: "Sparkling Heart" },
185
+ { emoji: "💘", name: "Heart with Arrow" },
186
+ { emoji: "💝", name: "Heart with Ribbon" },
187
+ { emoji: "💯", name: "Hundred Points" },
188
+ { emoji: "💢", name: "Anger Symbol" },
189
+ { emoji: "💥", name: "Collision" },
190
+ { emoji: "💫", name: "Dizzy" },
191
+ { emoji: "💦", name: "Sweat Droplets" },
192
+ { emoji: "💨", name: "Dashing Away" },
193
+ { emoji: "💩", name: "Pile of Poo" },
194
+ { emoji: "🍎", name: "Red Apple" },
195
+ { emoji: "🍌", name: "Banana" },
196
+ { emoji: "🍒", name: "Cherries" },
197
+ { emoji: "🍓", name: "Strawberry" },
198
+ { emoji: "🍔", name: "Hamburger" },
199
+ { emoji: "🍕", name: "Pizza" },
200
+ { emoji: "🍟", name: "French Fries" },
201
+ { emoji: "🍦", name: "Soft Ice Cream" },
202
+ { emoji: "🍩", name: "Doughnut" },
203
+ { emoji: "☕", name: "Hot Beverage" },
204
+ { emoji: "🍵", name: "Teacup Without Handle" },
205
+ { emoji: "🍺", name: "Beer Bottle" },
206
+ { emoji: "🍷", name: "Wine Glass" },
207
+ { emoji: "🍸", name: "Cocktail Glass" },
208
+ { emoji: "🍹", name: "Tropical Drink" },
209
+ { emoji: "🍾", name: "Bottle with Popping Cork" },
210
+ { emoji: "🌍", name: "Earth Globe Europe-Africa" },
211
+ { emoji: "🌎", name: "Earth Globe Americas" },
212
+ { emoji: "🌏", name: "Earth Globe Asia-Australia" },
213
+ { emoji: "🌞", name: "Sun with Face" },
214
+ { emoji: "🌝", name: "Full Moon Face" },
215
+ { emoji: "🌚", name: "New Moon Face" },
216
+ { emoji: "🌙", name: "Crescent Moon" },
217
+ { emoji: "⭐", name: "Star" },
218
+ { emoji: "🌟", name: "Glowing Star" },
219
+ { emoji: "✨", name: "Sparkles" },
220
+ { emoji: "⚡", name: "High Voltage" },
221
+ { emoji: "🔥", name: "Fire" },
222
+ { emoji: "💧", name: "Droplet" },
223
+ { emoji: "🌊", name: "Water Wave" },
224
+ { emoji: "🌈", name: "Rainbow" },
225
+ { emoji: "☁️", name: "Cloud" },
226
+ { emoji: "🐶", name: "Dog Face" },
227
+ { emoji: "🐱", name: "Cat Face" },
228
+ { emoji: "🐭", name: "Mouse Face" },
229
+ { emoji: "🐹", name: "Hamster Face" },
230
+ { emoji: "🐰", name: "Rabbit Face" },
231
+ { emoji: "🦊", name: "Fox Face" },
232
+ { emoji: "🐻", name: "Bear" },
233
+ { emoji: "🐼", name: "Panda" },
234
+ { emoji: "🐨", name: "Koala" },
235
+ { emoji: "🐯", name: "Tiger Face" },
236
+ { emoji: "🦁", name: "Lion" },
237
+ { emoji: "🐮", name: "Cow Face" },
238
+ { emoji: "🐷", name: "Pig Face" },
239
+ { emoji: "🐸", name: "Frog" },
240
+ { emoji: "🐵", name: "Monkey Face" },
241
+ { emoji: "🐔", name: "Chicken" },
242
+ { emoji: "⚽", name: "Soccer Ball" },
243
+ { emoji: "🏀", name: "Basketball" },
244
+ { emoji: "🏈", name: "American Football" },
245
+ { emoji: "⚾", name: "Baseball" },
246
+ { emoji: "🎾", name: "Tennis Racquet and Ball" },
247
+ { emoji: "🏐", name: "Volleyball" },
248
+ { emoji: "🏉", name: "Rugby Football" },
249
+ { emoji: "🎱", name: "Pool 8 Ball" },
250
+ { emoji: "🏓", name: "Table Tennis Paddle and Ball" },
251
+ { emoji: "🏸", name: "Badminton Racquet and Shuttlecock" },
252
+ { emoji: "🏒", name: "Ice Hockey Stick and Puck" },
253
+ { emoji: "🥅", name: "Goal Net" },
254
+ { emoji: "⛳", name: "Flag in Hole" },
255
+ { emoji: "🏹", name: "Bow and Arrow" },
256
+ { emoji: "🎣", name: "Fishing Pole" },
257
+ { emoji: "🥊", name: "Boxing Glove" },
258
+ ];
259
+
260
+ // Filter emojis based on the search term (case-insensitive)
261
+ const filteredEmojis = allEmojisWithNames.filter((item) => {
262
+ const lowerCaseSearchTerm = searchTerm.toLowerCase();
263
+ // Check if the search term is included in the emoji's name
264
+ return (
265
+ searchTerm === "" ||
266
+ item.name.toLowerCase().includes(lowerCaseSearchTerm)
267
+ );
268
+ });
269
+
270
+ // Function to insert an emoji at the current cursor position
271
+ const insertEmoji = (emoji) => {
272
+ const textarea = textareaRef.current;
273
+ if (!textarea) return; // Ensure textarea is available
274
+
275
+ const cursorPosition = textarea.selectionStart;
276
+ const textBefore = internalValue.substring(0, cursorPosition);
277
+ const textAfter = internalValue.substring(cursorPosition);
278
+
279
+ // Calculate the new value
280
+ const newValue = textBefore + emoji + textAfter;
281
+
282
+ // Update internal state
283
+ setInternalValue(newValue);
284
+
285
+ // Manually trigger onChange with the new value
286
+ if (onChange) {
287
+ const event = { target: { value: newValue } };
288
+ onChange(event);
289
+ }
290
+
291
+ // Move cursor to after the inserted emoji
292
+ const newCursorPosition = cursorPosition + emoji.length;
293
+
294
+ // Use a timeout to ensure the state updates before setting selection
295
+ // This is a common pattern in React for DOM manipulations after state updates
296
+ setTimeout(() => {
297
+ textarea.selectionStart = newCursorPosition;
298
+ textarea.selectionEnd = newCursorPosition;
299
+ textarea.focus(); // Focus the textarea after inserting
300
+ }, 0);
301
+
302
+ // Optionally hide the picker after selection
303
+ // setShowPicker(false);
304
+ };
305
+
306
+ // Effect to handle clicks outside the emoji picker to close it
307
+ useEffect(() => {
308
+ const handleClickOutside = (event) => {
309
+ // Check if the click was outside the picker and not on the toggle button or the picker itself
310
+ if (
311
+ emojiPickerRef.current &&
312
+ !emojiPickerRef.current.contains(event.target) &&
313
+ event.target !== emojiToggleRef.current &&
314
+ !emojiToggleRef.current.contains(event.target)
315
+ ) {
316
+ setShowPicker(false);
317
+ setSearchTerm(""); // Clear search term when closing
318
+ }
319
+ };
320
+
321
+ // Add the event listener
322
+ document.addEventListener("click", handleClickOutside);
323
+
324
+ // Cleanup the event listener on component unmount
325
+ return () => {
326
+ document.removeEventListener("click", handleClickOutside);
327
+ };
328
+ }, [showPicker]); // Re-run effect if showPicker changes
329
+
330
+ // Function to determine picker position based on available space
331
+ const determinePickerPosition = () => {
332
+ const container = containerRef.current;
333
+ if (!container) return "bottom"; // Default to bottom if container not found
334
+
335
+ const rect = container.getBoundingClientRect();
336
+ const viewportHeight = window.innerHeight;
337
+
338
+ // Calculate space above and below the input container
339
+ const spaceAbove = rect.top;
340
+ const spaceBelow = viewportHeight - rect.bottom;
341
+
342
+ // Assume picker needs at least 200px height (based on max-height)
343
+ const requiredSpace = 200; // Match max-height of picker
344
+
345
+ // If there's enough space below, position below. Otherwise, position above.
346
+ // Prioritize below if space is sufficient, otherwise check above.
347
+ if (spaceBelow >= requiredSpace) {
348
+ return "top"; // Position below the input (top: 100%)
349
+ } else if (spaceAbove >= requiredSpace) {
350
+ return "bottom"; // Position above the input (bottom: 100%)
351
+ } else {
352
+ // If neither has enough space, position where there is more space
353
+ return spaceBelow > spaceAbove ? "top" : "bottom";
354
+ }
355
+ };
356
+
357
+ // Toggle emoji picker visibility and determine position
358
+ const togglePicker = () => {
359
+ if (!showPicker) {
360
+ // If about to show, determine the best position
361
+ setPickerPosition(determinePickerPosition());
362
+ } else {
363
+ setSearchTerm(""); // Clear search term when hiding
364
+ }
365
+ setShowPicker(!showPicker);
366
+ };
367
+
368
+ return (
369
+ // Apply containerStyle prop
370
+ <div
371
+ className="input-container"
372
+ style={containerStyle}
373
+ ref={containerRef}>
374
+ {/* Textarea for input */}
375
+ {/* Apply inputStyle prop */}
376
+ <textarea
377
+ ref={textareaRef} // Attach ref to textarea
378
+ className="text-input"
379
+ rows="1" // Start with 1 row, max-height will control size
380
+ placeholder="Start typing..." // Updated placeholder text
381
+ // Use internal state as the source of truth
382
+ value={internalValue}
383
+ onChange={(e) => {
384
+ // Update internal state
385
+ setInternalValue(e.target.value);
386
+ // Always call the passed-in onChange handler if it exists
387
+ if (onChange) {
388
+ onChange(e);
389
+ }
390
+ }}
391
+ onPaste={(e) => {
392
+ if (onPaste) {
393
+ onPaste(e); // Call the passed-in onPaste handler
394
+ }
395
+ // onChange handler will be triggered by paste, updating internal state
396
+ }}
397
+ onFocus={(e) => {
398
+ // Added onFocus handler
399
+ if (onFocus) {
400
+ onFocus(e);
401
+ }
402
+ }}
403
+ onBlur={(e) => {
404
+ // Added onBlur handler
405
+ if (onBlur) {
406
+ onBlur(e);
407
+ }
408
+ }}
409
+ onKeyDown={(e) => {
410
+ // Added onKeyDown handler
411
+ // Check for Enter key press
412
+ if (e.key === "Enter") {
413
+ // If Shift is NOT pressed, prevent default and call onEnter
414
+ if (!e.shiftKey) {
415
+ e.preventDefault();
416
+ if (onEnter) {
417
+ onEnter(internalValue); // Pass current internal value to onEnter
418
+ }
419
+ }
420
+ // If Shift IS pressed, allow default (new line)
421
+ }
422
+
423
+ // Always call the passed-in onKeyDown handler if it exists
424
+ if (onKeyDown) {
425
+ onKeyDown(e);
426
+ }
427
+ }}
428
+ onKeyUp={(e) => {
429
+ // Added onKeyUp handler
430
+ if (onKeyUp) {
431
+ onKeyUp(e);
432
+ }
433
+ }}
434
+ onKeyPress={(e) => {
435
+ // Added onKeyPress handler
436
+ if (onKeyPress) {
437
+ onKeyPress(e);
438
+ }
439
+ }}
440
+ disabled={disabled} // Apply disabled prop
441
+ readOnly={readOnly} // Apply readOnly prop
442
+ style={{
443
+ maxHeight: "52px",
444
+ overflowY: "auto",
445
+ fontSize: fontSize, // Apply fontSize prop
446
+ lineHeight: lineHeight, // Apply lineHeight prop
447
+ ...inputStyle, // Apply custom inputStyle prop
448
+ }} // Set max height and overflow, apply inputStyle
449
+ ></textarea>
450
+
451
+ {/* Button to toggle the emoji picker */}
452
+ {/* Apply buttonStyle prop */}
453
+ <button
454
+ ref={emojiToggleRef} // Attach ref to toggle button
455
+ className="emoji-toggle-button"
456
+ onClick={togglePicker} // Use the new togglePicker function
457
+ // Disable button if input is disabled or readOnly
458
+ disabled={disabled || readOnly}
459
+ style={buttonStyle} // Apply buttonStyle
460
+ >
461
+ {/* SVG Icon */}
462
+ <svg
463
+ width="24"
464
+ height="25"
465
+ viewBox="0 0 24 25"
466
+ fill="none"
467
+ xmlns="http://www.w3.org/2000/svg">
468
+ <path
469
+ d="M19.0668 5.51844C23.0368 9.48844 22.9668 15.9684 18.8668 19.8584C15.0768 23.4484 8.92679 23.4484 5.12679 19.8584C1.01679 15.9684 0.946776 9.48844 4.92678 5.51844C8.82678 1.60844 15.1668 1.60844 19.0668 5.51844Z"
470
+ stroke="currentColor"
471
+ strokeWidth="1.5"
472
+ strokeLinecap="round"
473
+ strokeLinejoin="round"
474
+ />
475
+ <path
476
+ d="M15.834 16.6367C13.714 18.6367 10.2741 18.6367 8.16406 16.6367"
477
+ stroke="currentColor"
478
+ strokeWidth="1.5"
479
+ strokeLinecap="round"
480
+ strokeLinejoin="round"
481
+ />
482
+ <path
483
+ d="M8.66638 9.55859H8.67536"
484
+ stroke="currentColor"
485
+ strokeWidth="2.5"
486
+ strokeLinecap="round"
487
+ strokeLinejoin="round"
488
+ />
489
+ <path
490
+ d="M15.8383 9.55859H15.8472"
491
+ stroke="currentColor"
492
+ strokeWidth="2.5"
493
+ strokeLinecap="round"
494
+ strokeLinejoin="round"
495
+ />
496
+ </svg>
497
+ </button>
498
+
499
+ {/* Emoji picker container, shown conditionally */}
500
+ {showPicker && (
501
+ // Apply pickerStyle prop and dynamic position class
502
+ <div
503
+ ref={emojiPickerRef} // Attach ref to picker container
504
+ className={`emoji-picker-container show ${pickerPosition}`} // Add pickerPosition class
505
+ style={pickerStyle} // Apply pickerStyle
506
+ >
507
+ {/* Emoji Search Input */}
508
+ <input
509
+ type="text"
510
+ placeholder="Search emojis..."
511
+ className="emoji-search-input"
512
+ value={searchTerm}
513
+ onChange={(e) => setSearchTerm(e.target.value)}
514
+ />
515
+
516
+ {/* Emoji Grid */}
517
+ <div className="emoji-grid">
518
+ {/* Map over filtered emojis and create buttons */}
519
+ {filteredEmojis.map((item, index) => (
520
+ <button
521
+ key={index} // Use index as key (safe for static list)
522
+ className="emoji-button"
523
+ onClick={() => insertEmoji(item.emoji)} // Insert emoji on click
524
+ title={item.name} // Add title for accessibility
525
+ >
526
+ {item.emoji}
527
+ </button>
528
+ ))}
529
+ {filteredEmojis.length === 0 && (
530
+ <p className="no-results">No emojis found.</p>
531
+ )}
532
+ </div>
533
+ </div>
534
+ )}
535
+ {/* Plain CSS styles */}
536
+ <style>{`
537
+ .input-container {
538
+ position: relative;
539
+ width: 100%;
540
+ max-width: 400px; /* Adjusted max-width slightly for a common input size */
541
+ margin: 20px auto;
542
+ /* Updated default border color */
543
+ border: 1px solid rgba(229, 229, 234, 1);
544
+ border-radius: 0.375rem; /* Default rounded-md */
545
+ /* Removed overflow: hidden; */
546
+ /* Default background, can be overridden by prop */
547
+ background-color: #ffffff;
548
+ }
549
+
550
+ .text-input {
551
+ display: block; /* Make it a block element */
552
+ width: 100%;
553
+ padding: 8px; /* p-2 */
554
+ padding-right: 40px; /* Adjust padding to make space for the button */
555
+ border: none; /* Removed border */
556
+ outline: none; /* Removed default outline */
557
+ resize: none; /* Disable resize handle */
558
+ max-height: 52px; /* Set the maximum height */
559
+ overflow-y: auto; /* Add scrollbar if content exceeds max-height */
560
+ font-family: sans-serif; /* Use a common sans-serif font */
561
+ box-sizing: border-box; /* Include padding and border in element's total width and height */
562
+ /* Default background, can be overridden by prop */
563
+ background-color: transparent; /* Make textarea background transparent to show container background */
564
+ color: #374151; /* Set text color to a shade of gray */
565
+ /* Removed hardcoded font-size and line-height here */
566
+ }
567
+
568
+ .text-input:disabled,
569
+ .text-input[readOnly] {
570
+ background-color: #f3f4f6; /* gray-100 */
571
+ cursor: not-allowed;
572
+ }
573
+
574
+
575
+ .emoji-toggle-button {
576
+ position: absolute;
577
+ top: 50%; /* Center vertically */
578
+ right: 8px; /* Position from the right */
579
+ transform: translateY(-50%); /* Adjust for vertical centering */
580
+ display: inline-flex;
581
+ align-items: center;
582
+ justify-content: center;
583
+ padding: 4px; /* p-1 */
584
+ color: #6b7280; /* gray-500 */
585
+ cursor: pointer;
586
+ background: none; /* No background */
587
+ border: none; /* No border */
588
+ transition: color 0.2s ease-in-out;
589
+ }
590
+
591
+ .emoji-toggle-button:hover:not(:disabled) {
592
+ color: #4b5563; /* gray-700 */
593
+ }
594
+
595
+ .emoji-toggle-button:disabled {
596
+ cursor: not-allowed;
597
+ opacity: 0.6;
598
+ }
599
+
600
+
601
+ .emoji-toggle-button svg {
602
+ stroke: currentColor; /* Make SVG stroke color match text color */
603
+ }
604
+
605
+ .emoji-picker-container {
606
+ display: none; /* Hidden by default */
607
+ position: absolute;
608
+ left: 0;
609
+ right: 0;
610
+ z-index: 10;
611
+ max-height: 192px; /* max-h-48 */
612
+ overflow-y: hidden; /* Hide overflow for the container itself */
613
+ background-color: #ffffff; /* bg-white */
614
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* shadow-md */
615
+ border: 1px solid #e5e7eb; /* gray-200 */
616
+ border-radius: 0.375rem; /* rounded-md */
617
+ padding: 8px; /* p-2 */
618
+ flex-direction: column; /* Stack search and grid vertically */
619
+ }
620
+
621
+ .emoji-picker-container.show {
622
+ display: flex; /* Show as flex container when 'show' class is present */
623
+ }
624
+
625
+ .emoji-search-input {
626
+ width: 100%;
627
+ padding: 8px;
628
+ margin-bottom: 8px; /* Space below search input */
629
+ border: 1px solid #d1d5db;
630
+ border-radius: 0.25rem;
631
+ outline: none;
632
+ box-sizing: border-box;
633
+ }
634
+
635
+ .emoji-grid {
636
+ display: grid; /* Use grid for layout */
637
+ grid-template-columns: repeat(auto-fill, minmax(30px, 1fr)); /* Responsive columns */
638
+ gap: 4px; /* gap-1 */
639
+ overflow-y: auto; /* Add scrollbar to the grid */
640
+ max-height: calc(100% - 40px); /* Calculate remaining height for grid (adjust 40px based on search input height + margin) */
641
+ padding-right: 8px; /* Add padding for scrollbar */
642
+ }
643
+
644
+
645
+ /* Positioning classes */
646
+ .emoji-picker-container.bottom {
647
+ bottom: 100%; /* Position above the input */
648
+ }
649
+
650
+ .emoji-picker-container.top {
651
+ top: 100%; /* Position below the input */
652
+ }
653
+
654
+
655
+ .emoji-button {
656
+ cursor: pointer;
657
+ font-size: 1.25rem; /* text-xl */
658
+ text-align: center;
659
+ padding: 4px; /* p-1 */
660
+ border-radius: 0.25rem; /* rounded */
661
+ background: none; /* No background */
662
+ border: none; /* No border */
663
+ transition: background-color 0.2s ease-in-out;
664
+ }
665
+
666
+ .emoji-button:hover {
667
+ background-color: #f3f4f6; /* gray-100 */
668
+ }
669
+
670
+ .no-results {
671
+ text-align: center;
672
+ color: #6b7280; /* gray-500 */
673
+ grid-column: 1 / -1; /* Span across all columns in the grid */
674
+ }
675
+ `}</style>
676
+ </div>
677
+ );
678
+ }
679
+ );
680
+
681
+ export default EmojiInput; // Export the component with the new name
@@ -86,7 +86,7 @@ const InstructorRightBar = ({ planState }) => {
86
86
  marginBottom: "40px",
87
87
  }}
88
88
  onClick={() => {
89
- window.location.href = `${protocol}//${host}${port}/enterprise/courses/create-course`;
89
+ window.location.href = `${protocol}//${host}${port}/instructor/courses/create-course`;
90
90
  }}
91
91
  />
92
92
  </RecentAddedEnterprie>
@@ -1,9 +1,17 @@
1
1
  import useAxios from "axios-hooks";
2
2
  import { useEffect } from "react";
3
3
 
4
- const useHeader = () => {
4
+ const useHeader = (props = { default: false }) => {
5
5
  // get user details
6
- const [{ ...userDetails }, getUserDetails] = useAxios();
6
+ const [userDetails, getUserDetails] = useAxios(
7
+ {
8
+ method: "get",
9
+ url: "/iam/v1/users/me/",
10
+ },
11
+ {
12
+ manual: !props.default,
13
+ }
14
+ );
7
15
 
8
16
  const handleGetUserDetails = async () => {
9
17
  await getUserDetails({
@@ -369,15 +377,11 @@ const useHeader = () => {
369
377
  }
370
378
  };
371
379
 
372
- //defaults
373
- // get user info
374
- useEffect(() => {
375
- handleGetUserDetails();
376
- }, []);
377
-
378
380
  return {
379
381
  handleGetUserDetails,
380
382
  userDetails,
383
+ user: userDetails.data,
384
+ getUserDetails,
381
385
  handleGetUserAccountsDetail,
382
386
  userAccountsDetail,
383
387
  handleGetDefaultAccount,
@@ -398,7 +402,7 @@ const useHeader = () => {
398
402
  handleGetAllAffiliate,
399
403
  handleSetDefaultAffiliate,
400
404
  setDefaultAffiliateData,
401
-
405
+ plan: userPlanData?.data,
402
406
  userPlanData,
403
407
  handleGetUserPlan,
404
408
  getCurrentSubscriptionData,
@@ -56,9 +56,7 @@ const HeaderComponent = (props) => {
56
56
  // in general data find user info and replace where user details is being used
57
57
  const {
58
58
  handleGetUserAccountsDetail,
59
- handleGetUserDetails,
60
59
  userAccountsDetail,
61
- userDetails,
62
60
  // getDefaultAccount,
63
61
  // handleGetDefaultAccount,
64
62
  unreadNotificationData,
@@ -75,8 +73,13 @@ const HeaderComponent = (props) => {
75
73
  handleSetDefaultAccount,
76
74
  } = useHeader();
77
75
  const { pathname } = useLocation();
78
- const { setGeneralData, generalData, notificationMarkReadData, accountName } =
79
- useContext(OutletContext);
76
+ const {
77
+ setGeneralData,
78
+ generalData,
79
+ notificationMarkReadData,
80
+ accountName,
81
+ userDetails,
82
+ } = useContext(OutletContext);
80
83
  const [accountType, setAccountType] = useState("");
81
84
  const currentAccountType = props?.selectedAccount?.type
82
85
  ? props?.selectedAccount?.type?.toLowerCase()
@@ -91,7 +94,6 @@ const HeaderComponent = (props) => {
91
94
  useEffect(() => {
92
95
  setIsOpen(false);
93
96
  handleGetUserAccountsDetail();
94
- handleGetUserDetails();
95
97
  props?.handleGetDefaultAccount();
96
98
  }, []);
97
99
 
@@ -56,3 +56,4 @@ export { default as DeveloperBanner } from "./banner/developerBanner";
56
56
  export { default as VideoPlayer } from "./video-player";
57
57
  export { default as useAudioPlayer } from "./useAudioPlayer";
58
58
  export { default as AudioWaveComponent } from "./useAudioPlayer/audioWave";
59
+ export { default as InputEmoji } from "./InputEmoji";
@@ -98,7 +98,6 @@ const InstructorAccountSwitcher = ({ generalData, onChange }) => {
98
98
  };
99
99
 
100
100
  const { accountName, planState } = useContext(OutletContext);
101
-
102
101
  // handle if account does not have subscription but is an affiliate
103
102
  const prioritizedAffiliateId = useMemo(() => {
104
103
  let prioritizedAffiliateId = null;
@@ -107,11 +106,13 @@ const InstructorAccountSwitcher = ({ generalData, onChange }) => {
107
106
  // Priority 1: Use cookie value if it exists
108
107
  prioritizedAffiliateId = cookieValue;
109
108
  } else if (
110
- !planState && // Check for NO subscription
109
+ planState !== "ACTIVE" &&
110
+ planState !== "GRACE PERIOD" && // Check for NO subscription
111
111
  getAllAffiliateData?.data?.results?.length > 0 // Check if the list exists and is not empty
112
112
  ) {
113
113
  // Priority 2: If no cookie, no subscription, and list exists...
114
114
  // Assign the ID of the *first* affiliate directly from the results array
115
+ setCookie(getAllAffiliateData.data.results[0]?.id);
115
116
  prioritizedAffiliateId = getAllAffiliateData.data.results[0]?.id; // Optional chaining on 'id'
116
117
  }
117
118
  return prioritizedAffiliateId;
@@ -188,28 +189,30 @@ const InstructorAccountSwitcher = ({ generalData, onChange }) => {
188
189
  <div className="left">
189
190
  {switchValue !== "affiliates" && <p>{accountName}</p>}
190
191
  {switchValue === "affiliates" && (
191
- <AffiliatesDropDown>
192
- {!selectedValue ? (
193
- <div
194
- className="placeholder"
195
- onClick={() => setDropdown(!dropdown)}>
196
- <p>Select affiliate</p>
197
- <ArrowDown />
198
- </div>
199
- ) : (
200
- <div
201
- className="selected"
202
- onClick={() => setDropdown(!dropdown)}>
203
- <div>
204
- <img src={selectedValue?.image} alt="" />
205
- <p>{selectedValue?.name}</p>
192
+ <OutsideAlerter handleClick={() => setDropdown(false)}>
193
+ <AffiliatesDropDown>
194
+ {!selectedValue ? (
195
+ <div
196
+ className="placeholder"
197
+ onClick={() => setDropdown(!dropdown)}
198
+ >
199
+ <p>Select affiliate</p>
200
+ <ArrowDown />
201
+ </div>
202
+ ) : (
203
+ <div
204
+ className="selected"
205
+ onClick={() => setDropdown(!dropdown)}
206
+ >
207
+ <div>
208
+ <img src={selectedValue?.image} alt="" />
209
+ <p>{selectedValue?.name}</p>
210
+ </div>
211
+ <ArrowDown />
206
212
  </div>
207
- <ArrowDown />
208
- </div>
209
- )}
213
+ )}
210
214
 
211
- {dropdown && switchValue === "affiliates" ? (
212
- <OutsideAlerter handleClick={() => setDropdown(false)}>
215
+ {dropdown && switchValue === "affiliates" ? (
213
216
  <AffiliatesDropDownWrapper>
214
217
  <div className="search_wrapper">
215
218
  <Search
@@ -223,7 +226,8 @@ const InstructorAccountSwitcher = ({ generalData, onChange }) => {
223
226
  key={item?.id}
224
227
  onClick={() => {
225
228
  handleSelection(item);
226
- }}>
229
+ }}
230
+ >
227
231
  <div className="left">
228
232
  <img src={item?.image} alt="" />
229
233
  <p>{item?.name}</p>
@@ -233,24 +237,26 @@ const InstructorAccountSwitcher = ({ generalData, onChange }) => {
233
237
  ))}
234
238
  </ul>
235
239
  </AffiliatesDropDownWrapper>
236
- </OutsideAlerter>
237
- ) : null}
238
- </AffiliatesDropDown>
240
+ ) : null}
241
+ </AffiliatesDropDown>
242
+ </OutsideAlerter>
239
243
  )}
240
244
  </div>
241
245
  {
242
246
  <div className="btn__wrapper">
243
- {!!!prioritizedAffiliateId && (
247
+ {(planState === "ACTIVE" || planState === "GRACE PERIOD") && (
244
248
  <button
245
249
  className={switchValue !== "affiliates" ? "active" : ""}
246
- onClick={() => handleSwitch(1)}>
250
+ onClick={() => handleSwitch(1)}
251
+ >
247
252
  <span>My Account</span>
248
253
  <span className="circle"></span>
249
254
  </button>
250
255
  )}
251
256
  <button
252
257
  className={switchValue === "affiliates" ? "active" : ""}
253
- onClick={() => handleSwitch(2)}>
258
+ onClick={() => handleSwitch(2)}
259
+ >
254
260
  <span>Affiliates</span>
255
261
  <span className="circle"></span>
256
262
  </button>
@@ -354,6 +360,7 @@ const AffiliatesDropDown = styled.div`
354
360
  img {
355
361
  width: 36px;
356
362
  height: 36px;
363
+ border-radius: 50%;
357
364
  }
358
365
  p {
359
366
  color: #00c2c2;
@@ -407,6 +414,8 @@ const AffiliatesDropDownWrapper = styled.div`
407
414
  img {
408
415
  width: 20px;
409
416
  height: 20px;
417
+ border-radius: 50%;
418
+ object-fit: cover;
410
419
  }
411
420
  }
412
421
  span {
@@ -288,7 +288,7 @@ const SideMenu = ({
288
288
  }
289
289
  }}
290
290
  key={index}
291
- className={cx(`${route.text} ${active && "active"}`)}
291
+ className={cx({ active, [route.text]: true, disabled: route.disabled })}
292
292
  minimal={isOpen}>
293
293
  <IconContainer active={active}>
294
294
  {active ? iconActive : icon}
@@ -348,6 +348,7 @@ SideMenu.propTypes = {
348
348
  icon: PropTypes.element.isRequired,
349
349
  text: PropTypes.string.isRequired,
350
350
  notifications: PropTypes.number,
351
+ disabled: PropTypes.bool,
351
352
  })
352
353
  ).isRequired,
353
354
  })
@@ -120,6 +120,10 @@ export const NavigationItemContainer = styled.p`
120
120
  background: #949999;
121
121
  }
122
122
  }
123
+ &.disabled {
124
+ pointer-events: none;
125
+ opacity: 0.5;
126
+ }
123
127
  `;
124
128
 
125
129
  export const IconContainer = styled.div`
@@ -12,14 +12,12 @@ import Loader from "../../loader";
12
12
  import avatar from "../../../assets/images/avatar.png";
13
13
  import { OutletContext } from "../../AppMainLayout";
14
14
 
15
- const UserCard = ({ user, isOpen, findText }) => {
16
- const { handleGetUserDetails, userDetails, handleSetDefaultAccount } =
17
- useHeader();
15
+ const UserCard = ({ isOpen, findText }) => {
16
+ const { handleSetDefaultAccount } = useHeader();
18
17
  const [organizationName, setOrganizationName] = useState();
19
- const { generalData, accountName } = useContext(OutletContext);
18
+ const { generalData, accountName, userDetails } = useContext(OutletContext);
20
19
 
21
20
  useEffect(() => {
22
- handleGetUserDetails();
23
21
  if (generalData?.selectedAccount) {
24
22
  const date = new Date();
25
23
  date.setDate(date.getDate() + 28);
@@ -71,9 +69,9 @@ const UserCard = ({ user, isOpen, findText }) => {
71
69
  src={generalData?.selectedAccount?.profile_photo?.url || avatar}
72
70
  isOpen={isOpen}
73
71
  onClick={() => {
74
- if (window.location.pathname.includes("personal")) {
75
- window.location.href = `${window.location.protocol}//${window.location.hostname}/personal/profile`;
76
- }
72
+ // if (window.location.pathname.includes("personal")) {
73
+ // window.location.href = `${window.location.protocol}//${window.location.hostname}/personal/profile`;
74
+ // }
77
75
  if (window.location.pathname.includes("enterprise")) {
78
76
  window.location.href = `${window.location.protocol}//${window.location.hostname}/enterprise/profile`;
79
77
  }
@@ -179,6 +179,7 @@ export const sideMenuOptions = [
179
179
  icon: <AwardIcon />,
180
180
  iconActive: <AwardIconActive />,
181
181
  text: "Manage report",
182
+ // disabled: true,
182
183
  },
183
184
  {
184
185
  path: "/instructor/courses",
@@ -228,12 +229,14 @@ export const sideMenuOptions = [
228
229
  icon: <DashboardIcon />,
229
230
  iconActive: <DashboardIconActive />,
230
231
  text: "Dashboard",
232
+ disabled: true,
231
233
  },
232
234
  {
233
235
  path: "/personal/library",
234
236
  icon: <LibraryIcon />,
235
237
  iconActive: <LibraryIconActive />,
236
238
  text: "Library",
239
+ disabled: true,
237
240
  },
238
241
  {
239
242
  path: "/personal/courses",
@@ -259,6 +262,7 @@ export const sideMenuOptions = [
259
262
  icon: <AddOnIcon />,
260
263
  iconActive: <AddOnIconActive />,
261
264
  text: "Add on",
265
+ disabled: true,
262
266
  },
263
267
  ],
264
268
  },
@@ -1,6 +1,7 @@
1
1
  import useAxios from "axios-hooks";
2
2
  import { useCallback, useEffect, useMemo, useState, useRef } from "react";
3
3
  import sound from "./new-notification-7-210334.mp3";
4
+ import { cookieGrabber, useCookiePolling } from "../utils/cookiePolling";
4
5
 
5
6
  /**
6
7
  * Represents the details of the last message in a chat room overview.
@@ -236,38 +237,28 @@ import sound from "./new-notification-7-210334.mp3";
236
237
  * @property {any | null} media - The media data (e.g., File object, FormData details). Needs proper handling before API call.
237
238
  */
238
239
 
239
- const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
240
- const cookieGrabber = (key = "") => {
241
- const cookies = document.cookie;
242
- const string = cookies.split(";").find((cookie) => {
243
- cookie = cookie.trim();
244
- if (cookie.startsWith(key.trim() + "=")) return true;
245
- });
246
-
247
- if (!string) return "";
248
- return string.split("=")[1].trim();
240
+ const useMessageKit = () => {
241
+ const selectedAccountId = useCookiePolling("defaultAccountID");
242
+ const defaultAccountType = useCookiePolling("defaultAccountType");
243
+ const selectedAccount = {
244
+ id: selectedAccountId,
245
+ type: defaultAccountType,
249
246
  };
250
- const selectedAccount = useMemo(
251
- () => ({
252
- id: cookieGrabber("defaultAccountID"),
253
- type: cookieGrabber("defaultAccountType"),
254
- }),
255
- []
256
- );
247
+ const token = useCookiePolling("access");
257
248
  const buildSocketUrl = useCallback(() => {
258
249
  const baseUrl =
259
250
  window.location.hostname.includes("staging") ||
260
251
  window.location.hostname.includes("localhost")
261
252
  ? "https://dev-117782726-api.learngual.com"
262
253
  : "https://api.learngual.com";
263
- const token = cookieGrabber("access");
254
+
264
255
  return (
265
256
  baseUrl.replace("http", "ws") +
266
257
  `/notify/v1/ws/connect/?authorization=${token}&_account=${selectedAccount.id}`
267
258
  );
268
259
  }, []);
269
260
 
270
- const affiliateAccount = cookieGrabber("affiliateAccount");
261
+ const affiliateAccount = useCookiePolling("affiliateAccount", 1000);
271
262
  // console.log(affiliateAccount, affiliatesActive, "affiliate");
272
263
  // get account type
273
264
  const [, request] = useAxios(
@@ -370,14 +361,13 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
370
361
  });
371
362
  }
372
363
 
373
- if (
374
- selectedAccount.type.toLowerCase() === "instructor" &&
375
- affiliateAccount
376
- // &&
377
- // affiliatesActive
378
- ) {
364
+ if (selectedAccount.type.toLowerCase() === "instructor") {
379
365
  response = await request({
380
- url: nextPage || `/notify/v1/instructor/${affiliateAccount}/rooms/`,
366
+ url:
367
+ nextPage ||
368
+ `/notify/v1/instructor${
369
+ affiliateAccount ? "/".concat(affiliateAccount) : ""
370
+ }/rooms/`,
381
371
  params: {
382
372
  limit,
383
373
  _account: selectedAccount.id,
@@ -438,14 +428,13 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
438
428
  });
439
429
  }
440
430
 
441
- if (
442
- String(selectedAccount.type).toLowerCase() === "instructor" &&
443
- affiliateAccount
444
- // &&
445
- // affiliatesActive
446
- ) {
431
+ if (String(selectedAccount.type).toLowerCase() === "instructor") {
447
432
  response = await request({
448
- url: nextPage || `/notify/v1/instructor/${affiliateAccount}/chats/`,
433
+ url:
434
+ nextPage ||
435
+ `/notify/v1/instructor${
436
+ affiliateAccount ? "/".concat(affiliateAccount) : ""
437
+ }/chats/`,
449
438
  params: {
450
439
  _account: selectedAccount.id,
451
440
  room_id: roomId,
@@ -585,12 +574,11 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
585
574
  url: `/notify/v1/enterprise/chats/${latestMessageId}/read_message/`,
586
575
  });
587
576
  }
588
- if (
589
- String(selectedAccount.type).toLowerCase() === "instructor" &&
590
- affiliateAccount
591
- ) {
577
+ if (String(selectedAccount.type).toLowerCase() === "instructor") {
592
578
  await request({
593
- url: `/notify/v1/instructor/${affiliateAccount}/chats/${latestMessageId}/read_message/`,
579
+ url: `/notify/v1/instructor${
580
+ affiliateAccount ? "/".concat(affiliateAccount) : ""
581
+ }/chats/${latestMessageId}/read_message/`,
594
582
  });
595
583
  }
596
584
  if (String(selectedAccount.type).toLowerCase() === "personal") {
@@ -885,11 +873,12 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
885
873
  method: "Post",
886
874
  });
887
875
  } else if (
888
- String(selectedAccount.type).toLowerCase() === "instructor" &&
889
- affiliateAccount
876
+ String(selectedAccount.type).toLowerCase() === "instructor"
890
877
  ) {
891
878
  response = await request({
892
- url: `/notify/v1/instructor/${affiliateAccount}/chats/`,
879
+ url: `/notify/v1/instructor${
880
+ affiliateAccount ? "/".concat(affiliateAccount) : ""
881
+ }/chats/`,
893
882
  params: requestParams,
894
883
  data: requestData,
895
884
  method: "Post",
@@ -1295,14 +1284,11 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
1295
1284
  method: "patch",
1296
1285
  });
1297
1286
  }
1298
- if (
1299
- selectedAccount.type.toLowerCase() === "instructor" &&
1300
- affiliateAccount
1301
- // &&
1302
- // affiliatesActive
1303
- ) {
1287
+ if (selectedAccount.type.toLowerCase() === "instructor") {
1304
1288
  response = await request({
1305
- url: `/notify/v1/instructor/${affiliateAccount}/rooms/${roomId}/change_pin_status/`,
1289
+ url: `/notify/v1/instructor${
1290
+ affiliateAccount ? "/".concat(affiliateAccount) : ""
1291
+ }/rooms/${roomId}/change_pin_status/`,
1306
1292
  method: "patch",
1307
1293
  });
1308
1294
  } // incomplete
@@ -1375,14 +1361,11 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
1375
1361
  method: "delete",
1376
1362
  });
1377
1363
  }
1378
- if (
1379
- selectedAccount.type.toLowerCase() === "instructor" &&
1380
- affiliateAccount
1381
- // &&
1382
- // affiliatesActive
1383
- ) {
1364
+ if (selectedAccount.type.toLowerCase() === "instructor") {
1384
1365
  await request({
1385
- url: `/notify/v1/instructor/${affiliateAccount}/rooms/${roomId}/`,
1366
+ url: `/notify/v1/instructor${
1367
+ affiliateAccount ? "/".concat(affiliateAccount) : ""
1368
+ }/rooms/${roomId}/`,
1386
1369
  method: "delete",
1387
1370
  });
1388
1371
  } // incomplete
@@ -1419,12 +1402,11 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
1419
1402
  data,
1420
1403
  });
1421
1404
  }
1422
- if (
1423
- selectedAccount.type.toLowerCase() === "instructor" &&
1424
- affiliateAccount
1425
- ) {
1405
+ if (selectedAccount.type.toLowerCase() === "instructor") {
1426
1406
  response = await request({
1427
- url: `/notify/v1/instructor/${affiliateAccount}/chats/reports/`,
1407
+ url: `/notify/v1/instructor${
1408
+ affiliateAccount ? "/".concat(affiliateAccount) : ""
1409
+ }/chats/reports/`,
1428
1410
  method: "post",
1429
1411
  data,
1430
1412
  });
@@ -1441,6 +1423,7 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
1441
1423
  // Initial data fetch effect
1442
1424
  useEffect(() => {
1443
1425
  (async () => {
1426
+ if (!selectedAccountId || !defaultAccountType || !token) return;
1444
1427
  await getMessageRoomsByCourses();
1445
1428
  // reportChat({
1446
1429
  // reasons: ["Verbal harassment"],
@@ -1462,7 +1445,7 @@ const useMessageKit = ({ affiliateAccount: AffiliateAccount }) => {
1462
1445
  // accountId: "24dbaeede1",
1463
1446
  // });
1464
1447
  })();
1465
- }, [AffiliateAccount]); // Rerun when selectedAccount changes
1448
+ }, [affiliateAccount, token, defaultAccountType, selectedAccountId]); // Rerun when selectedAccount changes
1466
1449
  // console.log(
1467
1450
  // getMessagesForRoom("5be9b48281ac4c2885d3b719654ed59d", "878", "24dbaeede1"),
1468
1451
  // "hold versions"
@@ -0,0 +1,44 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ //helper function to get cookie
4
+ export const cookieGrabber = (key = "") => {
5
+ const cookies = document.cookie;
6
+ const string = cookies.split(";").find((cookie) => {
7
+ cookie = cookie.trim();
8
+ if (cookie.startsWith(key.trim() + "=")) return true;
9
+ });
10
+
11
+ if (!string) return "";
12
+ return string.split("=")[1].trim();
13
+ };
14
+
15
+ /**
16
+ * Custom hook to poll a specific cookie and update state on change.
17
+ *
18
+ * @param {string} cookieName The name of the cookie to monitor.
19
+ * @param {number} intervalMs The polling interval in milliseconds (e.g., 1000 for 1 second).
20
+ * @returns {string | null} The current value of the cookie.
21
+ */
22
+ export function useCookiePolling(cookieName, intervalMs = 500) {
23
+ const [cookieValue, setCookieValue] = useState(() =>
24
+ cookieGrabber(cookieName)
25
+ );
26
+
27
+ useEffect(() => {
28
+ const intervalId = setInterval(() => {
29
+ const currentValue = cookieGrabber(cookieName);
30
+ // Update state only if the value has actually changed
31
+ if (currentValue !== cookieValue) {
32
+ console.log(
33
+ `Cookie '${cookieName}' changed from '${cookieValue}' to '${currentValue}'`
34
+ );
35
+ setCookieValue(currentValue);
36
+ }
37
+ }, intervalMs);
38
+
39
+ // Cleanup: clear the interval when the component unmounts or dependencies change
40
+ return () => clearInterval(intervalId);
41
+ }, [cookieName, intervalMs, cookieValue]); // Re-run effect if cookieName or interval changes, or if cookieValue was updated
42
+
43
+ return cookieValue;
44
+ }