l-min-components 1.0.747 → 1.0.753

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.0.747",
3
+ "version": "1.0.753",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/assets",
@@ -24,6 +24,7 @@
24
24
  "lottie-web": "^5.12.2",
25
25
  "moment": "^2.29.4",
26
26
  "npm": "^10.2.5",
27
+ "papaparse": "^5.4.1",
27
28
  "prop-types": "^15.8.1",
28
29
  "rc-pagination": "^4.0.4",
29
30
  "rc-progress": "^3.5.1",
Binary file
@@ -28,6 +28,7 @@ import PersonalRightBar from "../fileRightBar/personalRightBar";
28
28
  import useHeader from "../header/getHeaderDetails";
29
29
  import GracePeriod from "../deactivated";
30
30
  import MobileLayout from "../mobileLayout";
31
+ import useTranslation from "../../hooks/useTranslation";
31
32
 
32
33
  const AppMainLayout = () => {
33
34
  const [isOpen, setIsOpen] = useState(true);
@@ -152,141 +153,156 @@ const AppMainLayout = () => {
152
153
  const currentPlan = userPlanData?.data?.current_plan;
153
154
  const planState = userPlanData?.data?.state;
154
155
 
156
+ const { textSchema, setDefaultLang, findText } = useTranslation();
157
+ // console.log(findText());
155
158
  return (
156
- <OutletContext.Provider
157
- value={{
158
- setRightComponent,
159
- setRightLayout,
160
- generalData,
161
- setGeneralData,
162
- coming,
163
- hasLayoutBackgroundImage,
164
- setHasLayoutBackgroundImage,
165
- setSideMenuLayout,
166
- activePage,
167
- setActivePage,
168
- studyTab,
169
- setStudyTab,
170
- page,
171
- setPage,
172
- selectedCourseId,
173
- setSelectedCourseId,
174
- centerLayoutStyle,
175
- setCenterLayoutStyle,
176
- // return true if instructor affiliates is Active
177
- setHideAffilicates,
178
- affiliatesActive,
179
- accessToken,
180
- envType,
181
- newNotifications,
182
- setNewNotifications,
183
- notificationMarkReadData,
184
- handleGetNotificationMarkRead,
185
- }}
186
- >
187
- {/* display mobile layout on device width*/}
188
- {deviceWidth < 1200 ? (
189
- <MobileLayout />
190
- ) : (
191
- <Layout
192
- coming={coming}
193
- hasLayoutBackgroundImage={hasLayoutBackgroundImage}
159
+ textSchema && (
160
+ <OutletContext.Provider
161
+ value={{
162
+ textSchema,
163
+ findText,
164
+ setDefaultLang,
165
+ setRightComponent,
166
+ setRightLayout,
167
+ generalData,
168
+ setGeneralData,
169
+ coming,
170
+ hasLayoutBackgroundImage,
171
+ setHasLayoutBackgroundImage,
172
+ setSideMenuLayout,
173
+ activePage,
174
+ setActivePage,
175
+ studyTab,
176
+ setStudyTab,
177
+ page,
178
+ setPage,
179
+ selectedCourseId,
180
+ setSelectedCourseId,
181
+ centerLayoutStyle,
182
+ setCenterLayoutStyle,
183
+ // return true if instructor affiliates is Active
184
+ setHideAffilicates,
185
+ affiliatesActive,
186
+ accessToken,
187
+ envType,
188
+ newNotifications,
189
+ setNewNotifications,
190
+ notificationMarkReadData,
191
+ handleGetNotificationMarkRead,
192
+ }}
194
193
  >
195
- <HeaderComponent setNewNotifications={setNewNotifications} />
196
- <MainLayout coming={coming}>
197
- <LeftLayout>
198
- <SideBar routes={leftNavMenu} />
199
- {!coming && (
200
- <>
201
- {sideMenuLayout && (
202
- <SideMenu
203
- user={user}
204
- routes={sideMenuOptions}
205
- affiliatesActive={affiliatesActive}
206
- userType={generalData?.selectedAccount?.type?.toLowerCase()}
207
- isOpen={isOpen}
208
- setIsOpen={setIsOpen}
209
- setRightComponent={setRightComponent}
210
- planState={planState}
211
- />
194
+ {/* display mobile layout on device width*/}
195
+ {deviceWidth < 1200 ? (
196
+ <MobileLayout />
197
+ ) : (
198
+ <Layout
199
+ coming={coming}
200
+ hasLayoutBackgroundImage={hasLayoutBackgroundImage}
201
+ >
202
+ <HeaderComponent setNewNotifications={setNewNotifications} />
203
+ <MainLayout coming={coming}>
204
+ <LeftLayout>
205
+ <SideBar routes={leftNavMenu} />
206
+ {!coming && (
207
+ <>
208
+ {sideMenuLayout && (
209
+ <SideMenu
210
+ user={user}
211
+ routes={sideMenuOptions}
212
+ affiliatesActive={affiliatesActive}
213
+ userType={generalData?.selectedAccount?.type?.toLowerCase()}
214
+ isOpen={isOpen}
215
+ setIsOpen={setIsOpen}
216
+ setRightComponent={setRightComponent}
217
+ planState={planState}
218
+ />
219
+ )}
220
+ </>
212
221
  )}
213
- </>
214
- )}
215
- {
216
- // window.location.pathname.includes("enterprise")
217
- // ? "enterprise"
218
- // : window.location.pathname.includes("personal")
219
- // ? "personal"
220
- // : window.location.pathname.includes("instructor")
221
- // ? "instructor"
222
- // : window.location.pathname.includes("developer") ||
223
- // window.location.hostname.includes("developer")
224
- // ? "developer"
225
- // : "developer"
226
- }
227
- </LeftLayout>
228
- <CenterLayout isOpen={isOpen} style={centerLayoutStyle}>
229
- {window.location.pathname.includes("instructor") &&
230
- !window.location.pathname.includes("enterprise") &&
231
- !window.location.pathname.includes("manage-teams") &&
232
- !hideAffilicates && (
233
- <InstructorAccountSwitcher
234
- setAccountType={setAffiliatesActive}
235
- />
236
- )}
237
-
238
- {window.location.pathname.includes("enterprise") &&
239
- planState === "GRACE PERIOD" ? (
240
- <GracePeriod
241
- getCurrentSubscriptionData={getCurrentSubscriptionData}
242
- handleCurrentSubscription={handleCurrentSubscription}
243
- gracePeriod={gracePeriod}
244
- />
245
- ) : (
246
- <Outlet />
247
- )}
248
- </CenterLayout>
222
+ {
223
+ // window.location.pathname.includes("enterprise")
224
+ // ? "enterprise"
225
+ // : window.location.pathname.includes("personal")
226
+ // ? "personal"
227
+ // : window.location.pathname.includes("instructor")
228
+ // ? "instructor"
229
+ // : window.location.pathname.includes("developer") ||
230
+ // window.location.hostname.includes("developer")
231
+ // ? "developer"
232
+ // : "developer"
233
+ }
234
+ </LeftLayout>
235
+ <CenterLayout isOpen={isOpen} style={centerLayoutStyle}>
236
+ {window.location.pathname.includes("instructor") &&
237
+ !window.location.pathname.includes("enterprise") &&
238
+ !window.location.pathname.includes("manage-teams") &&
239
+ !hideAffilicates && (
240
+ <InstructorAccountSwitcher
241
+ setAccountType={setAffiliatesActive}
242
+ />
243
+ )}
249
244
 
250
- {rightLayout && !coming && (
251
- <RightLayout>
252
- {rightComponent ??
253
- (window.location.pathname.includes("enterprise") &&
254
- !window.location.pathname.includes("file-manager") ? (
255
- <EnterpriseRightBar
245
+ {window.location.pathname.includes("enterprise") &&
246
+ planState === "GRACE PERIOD" ? (
247
+ <GracePeriod
248
+ getCurrentSubscriptionData={getCurrentSubscriptionData}
249
+ handleCurrentSubscription={handleCurrentSubscription}
256
250
  gracePeriod={gracePeriod}
257
- setGracePeriod={setGracePeriod}
258
- planState={planState}
259
251
  />
260
- ) : window.location.pathname.includes("personal/dashboard") ? (
261
- <PersonalRightBar />
262
- ) : window.location.pathname.includes("personal/addons") ? (
263
- <InstructorRightBar personal />
264
- ) : window.location.pathname.includes("personal/courses") ? (
265
- <InstructorRightBar personal />
266
- ) : window.location.pathname.includes(
267
- "personal/library/selectlanguage",
268
- ) ? (
269
- <InstructorRightBar personal />
270
- ) : window.location.pathname.includes("personal/library") ? (
271
- <PersonalRightBar personalLibrary />
272
- ) : window.location.pathname.includes("personal/reports") ? (
273
- <PersonalRightBar personalReport />
274
- ) : window.location.pathname.includes("instructor") &&
275
- !window.location.pathname.includes("manage-teams") &&
276
- !window.location.pathname.includes("file-manager") ? (
277
- <InstructorRightBar />
278
- ) : window.location.pathname.includes("developer") ? (
279
- <Banner />
280
252
  ) : (
281
- <Banner />
282
- ))}
283
- {/* {rightComponent ?? <Banner />} */}
284
- </RightLayout>
285
- )}
286
- </MainLayout>
287
- </Layout>
288
- )}
289
- </OutletContext.Provider>
253
+ <Outlet />
254
+ )}
255
+ </CenterLayout>
256
+
257
+ {rightLayout && !coming && (
258
+ <RightLayout>
259
+ {rightComponent ??
260
+ (window.location.pathname.includes("enterprise") &&
261
+ !window.location.pathname.includes("file-manager") ? (
262
+ <EnterpriseRightBar
263
+ gracePeriod={gracePeriod}
264
+ setGracePeriod={setGracePeriod}
265
+ planState={planState}
266
+ />
267
+ ) : window.location.pathname.includes(
268
+ "personal/dashboard"
269
+ ) ? (
270
+ <PersonalRightBar />
271
+ ) : window.location.pathname.includes("personal/addons") ? (
272
+ <InstructorRightBar personal />
273
+ ) : window.location.pathname.includes(
274
+ "personal/courses"
275
+ ) ? (
276
+ <InstructorRightBar personal />
277
+ ) : window.location.pathname.includes(
278
+ "personal/library/selectlanguage"
279
+ ) ? (
280
+ <InstructorRightBar personal />
281
+ ) : window.location.pathname.includes(
282
+ "personal/library"
283
+ ) ? (
284
+ <PersonalRightBar personalLibrary />
285
+ ) : window.location.pathname.includes(
286
+ "personal/reports"
287
+ ) ? (
288
+ <PersonalRightBar personalReport />
289
+ ) : window.location.pathname.includes("instructor") &&
290
+ !window.location.pathname.includes("manage-teams") &&
291
+ !window.location.pathname.includes("file-manager") ? (
292
+ <InstructorRightBar />
293
+ ) : window.location.pathname.includes("developer") ? (
294
+ <Banner />
295
+ ) : (
296
+ <Banner />
297
+ ))}
298
+ {/* {rightComponent ?? <Banner />} */}
299
+ </RightLayout>
300
+ )}
301
+ </MainLayout>
302
+ </Layout>
303
+ )}
304
+ </OutletContext.Provider>
305
+ )
290
306
  );
291
307
  };
292
308
 
@@ -61,7 +61,7 @@ const HeaderComponent = (props) => {
61
61
  handleGetNotification,
62
62
  } = useHeader();
63
63
  const { pathname } = useLocation();
64
- const { setGeneralData, generalData, notificationMarkReadData } =
64
+ const { setGeneralData, generalData, notificationMarkReadData, findText } =
65
65
  useContext(OutletContext);
66
66
  const [selectedAccount, setSelectedAccount] = useState();
67
67
  const { setDefaultAccount, handleSetDefaultAccount } = useHeader();
@@ -158,25 +158,25 @@ const HeaderComponent = (props) => {
158
158
  // Checking for delete account
159
159
  useEffect(() => {
160
160
  if (userAccountsDetail?.data) {
161
- const validAccounts = userAccountsDetail.data.results.filter(account => !account.pending_delete);
161
+ const validAccounts = userAccountsDetail.data.results.filter(
162
+ (account) => !account.pending_delete
163
+ );
162
164
 
163
- if (generalData?.selectedAccount && generalData?.selectedAccount?.pending_delete) {
164
- const nextValidAccount = validAccounts[0];
165
+ if (
166
+ generalData?.selectedAccount &&
167
+ generalData?.selectedAccount?.pending_delete
168
+ ) {
169
+ const nextValidAccount = validAccounts[0];
165
170
  if (nextValidAccount) {
166
171
  setSelectedAccount(nextValidAccount);
167
- } else {
168
- // Fallback to personal account or some default setting
169
- setSelectedAccount(personalAccountData[0]);
170
- }
172
+ } else {
173
+ // Fallback to personal account or some default setting
174
+ setSelectedAccount(personalAccountData[0]);
175
+ }
171
176
  }
172
-
173
177
  }
174
- }, [
175
- generalData?.selectedAccount,
176
- userAccountsDetail?.data,
178
+ }, [generalData?.selectedAccount, userAccountsDetail?.data]);
177
179
 
178
- ]);
179
-
180
180
  const containerRef = useRef(null);
181
181
  const secondContainerRef = useRef(null);
182
182
  const acctDropdownContainerRef = useRef(null);
@@ -266,7 +266,7 @@ const HeaderComponent = (props) => {
266
266
  if (path.includes("duet")) return "Duet";
267
267
  if (path.includes("speech")) return "Speech";
268
268
  if (path.includes("dictionary")) return "Dictionary";
269
- return "Learning";
269
+ return findText("Learning");
270
270
  };
271
271
 
272
272
  // websocket for notifications initialization
@@ -371,8 +371,7 @@ const HeaderComponent = (props) => {
371
371
  onClick={() => {
372
372
  setLanguageDropdown();
373
373
  setIsOpen();
374
- }}
375
- >
374
+ }}>
376
375
  <li>
377
376
  <a className={isHomePage() ? "active" : ""}>
378
377
  <BookIcon /> {getNavLinkLabel()}
@@ -397,9 +396,8 @@ const HeaderComponent = (props) => {
397
396
  className={
398
397
  window.location.pathname.includes("settings") ? "active" : ""
399
398
  }
400
- style={{ cursor: "pointer" }}
401
- >
402
- <SettingIcon /> Settings
399
+ style={{ cursor: "pointer" }}>
400
+ <SettingIcon /> {findText("Settings")}
403
401
  </a>
404
402
  </li>
405
403
  <li>
@@ -412,14 +410,13 @@ const HeaderComponent = (props) => {
412
410
  className={
413
411
  window.location.pathname.includes("notifications") ? "active" : ""
414
412
  }
415
- style={{ cursor: "pointer" }}
416
- >
413
+ style={{ cursor: "pointer" }}>
417
414
  {unreadNotificationData?.data?.count > 0 ? (
418
415
  <NotificationIcon />
419
416
  ) : (
420
417
  <NotificationEmptyIcon />
421
418
  )}
422
- Notifications
419
+ {findText("Notifications")}
423
420
  </a>
424
421
  </li>
425
422
  </Nav>
@@ -428,8 +425,7 @@ const HeaderComponent = (props) => {
428
425
  onClick={() => {
429
426
  setLanguageDropdown();
430
427
  setIsOpen();
431
- }}
432
- >
428
+ }}>
433
429
  <SearchInputGroup>
434
430
  <SearchIcon />
435
431
  <SearchInput
@@ -446,7 +442,7 @@ const HeaderComponent = (props) => {
446
442
  {searchResultOpen && (
447
443
  <div className="search-result-wrapper">
448
444
  <p>
449
- Website <span>UIUX </span> Design
445
+ {/* Website <span>UIUX </span> Design */}
450
446
  </p>
451
447
  </div>
452
448
  )}
@@ -458,8 +454,7 @@ const HeaderComponent = (props) => {
458
454
  onClick={() => {
459
455
  setLanguageDropdown(!languageDropdown);
460
456
  setIsOpen();
461
- }}
462
- >
457
+ }}>
463
458
  <img src={usFlag} alt="" />
464
459
  <ArrowDownIcon />
465
460
  </div>
@@ -477,8 +472,7 @@ const HeaderComponent = (props) => {
477
472
  setIsOpen(!isOpen);
478
473
  setLanguageDropdown();
479
474
  }}
480
- ref={secondContainerRef}
481
- >
475
+ ref={secondContainerRef}>
482
476
  <div className="user-img-container">
483
477
  <img
484
478
  src={selectedAccount?.profile_photo?.url || avatar}
@@ -719,9 +719,14 @@ export const LanguageDropdownContainer = styled.div`
719
719
  margin: 16px 0;
720
720
  display: flex;
721
721
  align-items: center;
722
+ cursor: pointer;
722
723
  span {
723
724
  margin-left: 8px;
724
725
  }
726
+
727
+ img {
728
+ width: 20px;
729
+ }
725
730
  }
726
731
  }
727
732
  `;
@@ -4,6 +4,7 @@ import useHeader from "./getHeaderDetails";
4
4
  import { OutletContext } from "..";
5
5
  import { useLocation } from "react-router-dom";
6
6
  import usFlag from "../../assets/images/usFlag.png";
7
+ import koreanFlag from "../../assets/images/koreaFlag.png";
7
8
  import { LanguageDropdownContainer } from "./index.styled";
8
9
  import { RiCloseCircleFill } from "react-icons/ri";
9
10
  import { HiSearch } from "react-icons/hi";
@@ -20,7 +21,8 @@ import { DebounceInput } from "react-debounce-input";
20
21
  *
21
22
  */
22
23
  const LanguageDropdown = ({ languageDropdown, setLanguageDropdown }) => {
23
- const { setGeneralData, generalData } = useContext(OutletContext);
24
+ const { setGeneralData, generalData, setDefaultLang, findText } =
25
+ useContext(OutletContext);
24
26
  const [filteredData, setFilteredData] = useState([]);
25
27
  const [isSearch, setIsSearch] = useState(false);
26
28
  const containerRef = useRef(null);
@@ -29,8 +31,8 @@ const LanguageDropdown = ({ languageDropdown, setLanguageDropdown }) => {
29
31
 
30
32
  const handleFilter = (event) => {
31
33
  const searchWord = event.target.value;
32
- const newFilter = sampleSuggestions.filter((item) => {
33
- return item?.toLowerCase().includes(searchWord.toLowerCase());
34
+ const newFilter = languagesData.filter((item) => {
35
+ return item?.name?.toLowerCase().includes(searchWord.toLowerCase());
34
36
  });
35
37
  console.log("newFilter", newFilter);
36
38
  if (searchWord === "") {
@@ -60,10 +62,23 @@ const LanguageDropdown = ({ languageDropdown, setLanguageDropdown }) => {
60
62
  };
61
63
  }, [setLanguageDropdown]);
62
64
 
65
+ const languagesData = [
66
+ {
67
+ name: "English",
68
+ flag: usFlag,
69
+ slug: "en",
70
+ },
71
+ {
72
+ name: "Korean",
73
+ flag: koreanFlag,
74
+ slug: "ko",
75
+ },
76
+ ];
77
+
63
78
  return (
64
79
  <LanguageDropdownContainer ref={containerRef}>
65
80
  <div className="ldc_header">
66
- <p className="ldc_title"> Whats your language</p>
81
+ <p className="ldc_title"> {findText("Whats your language")}</p>
67
82
  <span onClick={() => setLanguageDropdown(false)}>
68
83
  <RiCloseCircleFill />
69
84
  </span>
@@ -91,21 +106,31 @@ const LanguageDropdown = ({ languageDropdown, setLanguageDropdown }) => {
91
106
  filteredData?.map((item, index) => (
92
107
  <p
93
108
  className="language_dropdown_item"
94
- onClick={() => setLanguageDropdown()}
109
+ onClick={() => {
110
+ setLanguageDropdown();
111
+ setDefaultLang(item?.slug);
112
+ localStorage.setItem("defaultLang", item?.slug);
113
+ }}
95
114
  >
96
- <img src={usFlag} alt="" /> <span>{item}</span>
115
+ <img src={item?.flag} alt="" /> <span>{item?.name}</span>
97
116
  </p>
98
117
  ))}
99
118
 
100
119
  {!isSearch && (
101
120
  <>
102
-
103
- <p
104
- className="language_dropdown_item"
105
- onClick={() => setLanguageDropdown()}
106
- >
107
- <img src={usFlag} alt="" /> <span>English</span>
108
- </p>
121
+ {languagesData?.map((item, idx) => (
122
+ <p
123
+ className="language_dropdown_item"
124
+ onClick={() => {
125
+ setLanguageDropdown();
126
+ setDefaultLang(item?.slug);
127
+ localStorage.setItem("defaultLang", item?.slug);
128
+ }}
129
+ >
130
+ <img src={item?.flag} alt="" />{" "}
131
+ <span>{findText(`${item?.name}`)}</span>
132
+ </p>
133
+ ))}
109
134
  </>
110
135
  )}
111
136
  </div>
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useRef, useCallback } from "react";
1
+ import React, { useState, useEffect, useRef, useCallback, useContext } from "react";
2
2
  import ReactFlagsSelect from "react-flags-select";
3
3
  import logo from "./assets/images/logo.png";
4
4
  import { Navbar2, NavGroup2, Nav2, CountryFlagGroup2 } from "./index.styled";
@@ -6,6 +6,7 @@ import { ArrowDownIcon } from "./assets/svg/arrow-down";
6
6
  import ButtonComponent from "../button";
7
7
  import { useLocation, useNavigate } from "react-router-dom";
8
8
  import usFlag from "../../assets/images/usFlag.png";
9
+ import { OutletContext } from "..";
9
10
 
10
11
  /**
11
12
  * @param {{
@@ -17,6 +18,8 @@ import usFlag from "../../assets/images/usFlag.png";
17
18
  *
18
19
  */
19
20
  const HeaderComponentTwo = (props) => {
21
+ const { setGeneralData, generalData, setDefaultLang, findText } =
22
+ useContext(OutletContext);
20
23
  const [selected, setSelected] = useState("ES");
21
24
  const [isOpen, setIsOpen] = useState(false);
22
25
  const [searchResultOpen, setSearchResultOpen] = useState(false);
@@ -38,7 +41,7 @@ const HeaderComponentTwo = (props) => {
38
41
  const containerRef = useRef(null);
39
42
 
40
43
  const handleLogoClick = () => {
41
- window.location.href = 'https://learngual.com/';
44
+ window.location.href = "https://learngual.com/";
42
45
  };
43
46
 
44
47
  useEffect(() => {
@@ -57,6 +60,19 @@ const HeaderComponentTwo = (props) => {
57
60
  document.removeEventListener("click", handleClickOutside);
58
61
  };
59
62
  }, [setLanguageDropdown]);
63
+
64
+ const languagesData = [
65
+ {
66
+ name: "English",
67
+ flag: usFlag,
68
+ slug: "en",
69
+ },
70
+ {
71
+ name: "Korean",
72
+ flag: koreanFlag,
73
+ slug: "ko",
74
+ },
75
+ ];
60
76
  return (
61
77
  <Navbar2
62
78
  onClick={() => {
@@ -92,9 +108,17 @@ const HeaderComponentTwo = (props) => {
92
108
  </div>
93
109
  {languageDropdown && (
94
110
  <ul className="dropdown_list" ref={containerRef}>
95
- <li onClick={() => setLanguageDropdown()}>
96
- <img src={usFlag} alt="" /> <span>English</span>
97
- </li>
111
+ {languagesData?.map((item, idx) => (
112
+ <li
113
+ onClick={() => {
114
+ setLanguageDropdown();
115
+ setDefaultLang(item?.slug);
116
+ localStorage.setItem("defaultLang", item?.slug);
117
+ }}
118
+ >
119
+ <img src={item?.flag} alt="" /> <span>{item?.name}</span>
120
+ </li>
121
+ ))}
98
122
  </ul>
99
123
  )}
100
124
  </div>
@@ -0,0 +1,24 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import loadTranslations from "./utils/translation";
3
+
4
+ const useTranslation = () => {
5
+ const value = localStorage.getItem("defaultLang");
6
+ const [textSchema, setTextSchema] = useState(null);
7
+
8
+ const [defaultLang, setDefaultLang] = useState(value || "en");
9
+ const findText = useCallback((str) => textSchema[str] ?? str, [textSchema]);
10
+
11
+ useEffect(() => {
12
+ loadTranslations(undefined, defaultLang).then((res) => setTextSchema(res));
13
+ }, [defaultLang]);
14
+
15
+ return {
16
+ textSchema,
17
+ setTextSchema,
18
+ setDefaultLang,
19
+ defaultLang,
20
+ findText,
21
+ };
22
+ };
23
+
24
+ export default useTranslation;
@@ -0,0 +1,65 @@
1
+ // import Tabletop from "tabletop";
2
+ import Papa from "papaparse";
3
+
4
+ /**
5
+ * Loads translation data from a Google Sheet URL into a structured object.
6
+ *
7
+ * @param {string} sheetUrl - The URL of the published Google Sheet.
8
+ * @param {string[]} languages - Array of language codes (e.g., ['es', 'fr']) to include in the result.
9
+ * @returns {Promise<Object>} A Promise that resolves to an object with English keys and translated values.
10
+ */
11
+
12
+ const protoUrl =
13
+ "https://docs.google.com/spreadsheets/d/e/2PACX-1vSvUIA_1eqpANaRTXafwYkh0y7ZYfbgvlHeLNjti21yjdtJjy6Fjk7pAuQjODROqT3uALC2Aq_odZy4/pub?gid=0&single=true&output=csv";
14
+
15
+ async function loadTranslations(sheetUrl = protoUrl, language = "") {
16
+ try {
17
+ // 1. Fetch data from the Google Sheet
18
+ const response = await fetch(sheetUrl);
19
+
20
+ // Check if the request was successful
21
+ if (!response.ok) {
22
+ throw new Error(
23
+ `Failed to fetch from Google Sheet: ${response.status} ${response.statusText}`
24
+ );
25
+ }
26
+
27
+ const csvData = await response.text();
28
+
29
+ // 2. Parse CSV data (handling quoted fields with commas)
30
+ const parseResult = Papa.parse(csvData, { header: true });
31
+
32
+ // Check for parsing errors
33
+ if (parseResult.errors.length > 0) {
34
+ throw new Error(`Failed to parse CSV data: ${parseResult.errors}`);
35
+ }
36
+
37
+ const rows = parseResult.data;
38
+
39
+ // 3. Identify the English column and language columns
40
+ const headers = Object.keys(rows[0]);
41
+
42
+ const englishColumnIndex = headers.indexOf("en"); // Using 'en' instead of 'English' based on your feedback
43
+
44
+ // Ensure English column exists
45
+ if (englishColumnIndex === -1) {
46
+ throw new Error("English column not found in Google Sheet");
47
+ }
48
+
49
+ // if language exists
50
+ if (!headers.includes(language))
51
+ throw new Error(language.concat(" translation not found"));
52
+
53
+ const translations = {};
54
+ for (let row of rows) {
55
+ translations[row.en.trim().replace(/\r\n|\r/g, "\n")] = row[language];
56
+ }
57
+ return translations;
58
+ } catch (error) {
59
+ console.error("Error loading translations:", error);
60
+ return {};
61
+ // throw error; // Rethrow for handling elsewhere
62
+ }
63
+ }
64
+
65
+ export default loadTranslations;