@hotosm/hanko-auth 0.3.5 → 0.4.1
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/README.md +39 -6
- package/dist/hanko-auth.esm.js +1857 -1671
- package/dist/hanko-auth.iife.js +464 -368
- package/dist/hanko-auth.umd.js +464 -368
- package/package.json +1 -5
- package/src/hanko-auth.styles.ts +406 -0
- package/src/hanko-auth.ts +77 -344
- package/src/translations.ts +87 -0
package/src/hanko-auth.ts
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
import { LitElement, html, css } from "lit";
|
|
13
13
|
import { customElement, property, state } from "lit/decorators.js";
|
|
14
14
|
import { register } from "@teamhanko/hanko-elements";
|
|
15
|
+
import { styles } from "./hanko-auth.styles";
|
|
16
|
+
import { translations } from "./translations";
|
|
15
17
|
//Icons
|
|
16
18
|
import accountIcon from "../assets/icon-account.svg";
|
|
17
19
|
import logoutIcon from "../assets/icon-logout.svg";
|
|
@@ -50,6 +52,7 @@ interface OSMData {
|
|
|
50
52
|
|
|
51
53
|
@customElement("hotosm-auth")
|
|
52
54
|
export class HankoAuth extends LitElement {
|
|
55
|
+
static styles = styles;
|
|
53
56
|
// Properties (from attributes)
|
|
54
57
|
@property({ type: String, attribute: "hanko-url" }) hankoUrlAttr = "";
|
|
55
58
|
@property({ type: String, attribute: "base-path" }) basePath = "";
|
|
@@ -74,6 +77,18 @@ export class HankoAuth extends LitElement {
|
|
|
74
77
|
@property({ type: String, attribute: "app-id" }) appId = "";
|
|
75
78
|
// Custom login page URL (for standalone mode - overrides ${hankoUrl}/app)
|
|
76
79
|
@property({ type: String, attribute: "login-url" }) loginUrl = "";
|
|
80
|
+
// Language code (en, es, fr, pt, etc.)
|
|
81
|
+
@property({ type: String }) lang = "en";
|
|
82
|
+
// Button variant (filled, outline, plain)
|
|
83
|
+
@property({ type: String, attribute: "button-variant" }) buttonVariant:
|
|
84
|
+
| "filled"
|
|
85
|
+
| "outline"
|
|
86
|
+
| "plain" = "plain";
|
|
87
|
+
// Button color (primary, neutral, danger)
|
|
88
|
+
@property({ type: String, attribute: "button-color" }) buttonColor:
|
|
89
|
+
| "primary"
|
|
90
|
+
| "neutral"
|
|
91
|
+
| "danger" = "primary";
|
|
77
92
|
|
|
78
93
|
// Internal state
|
|
79
94
|
@state() private user: UserState | null = null;
|
|
@@ -84,15 +99,26 @@ export class HankoAuth extends LitElement {
|
|
|
84
99
|
@state() private error: string | null = null;
|
|
85
100
|
@state() private profileDisplayName: string = "";
|
|
86
101
|
@state() private hasAppMapping = false; // True if user has mapping in the app
|
|
102
|
+
@state() private userProfileLanguage: string | null = null; // Language from user profile
|
|
87
103
|
// dropdown
|
|
88
104
|
@state() private isOpen = false;
|
|
89
105
|
|
|
90
106
|
private toggleDropdown() {
|
|
91
107
|
this.isOpen = !this.isOpen;
|
|
108
|
+
if (this.isOpen) {
|
|
109
|
+
// Add listener when dropdown opens
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
document.addEventListener("click", this.handleOutsideClick);
|
|
112
|
+
}, 0);
|
|
113
|
+
} else {
|
|
114
|
+
// Remove listener when dropdown closes
|
|
115
|
+
document.removeEventListener("click", this.handleOutsideClick);
|
|
116
|
+
}
|
|
92
117
|
}
|
|
93
118
|
|
|
94
119
|
private closeDropdown() {
|
|
95
120
|
this.isOpen = false;
|
|
121
|
+
document.removeEventListener("click", this.handleOutsideClick);
|
|
96
122
|
}
|
|
97
123
|
private handleOutsideClick = (event: MouseEvent) => {
|
|
98
124
|
if (!this.contains(event.target as Node)) {
|
|
@@ -107,332 +133,6 @@ export class HankoAuth extends LitElement {
|
|
|
107
133
|
private _hanko: any = null;
|
|
108
134
|
private _isPrimary = false; // Is this the primary instance?
|
|
109
135
|
|
|
110
|
-
static styles = css`
|
|
111
|
-
:host {
|
|
112
|
-
display: block;
|
|
113
|
-
font-family: var(--hot-font-sans);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.container {
|
|
117
|
-
max-width: 400px;
|
|
118
|
-
margin: 0 auto;
|
|
119
|
-
padding: var(--hot-spacing-large);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.loading {
|
|
123
|
-
text-align: center;
|
|
124
|
-
padding: var(--hot-spacing-3x-large);
|
|
125
|
-
color: var(--hot-color-gray-600);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.osm-connecting {
|
|
129
|
-
display: flex;
|
|
130
|
-
flex-direction: column;
|
|
131
|
-
align-items: center;
|
|
132
|
-
gap: var(--hot-spacing-small);
|
|
133
|
-
padding: var(--hot-spacing-large);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.spinner {
|
|
137
|
-
width: var(--hot-spacing-3x-large);
|
|
138
|
-
height: var(--hot-spacing-3x-large);
|
|
139
|
-
border: var(--hot-spacing-2x-small) solid var(--hot-color-gray-50);
|
|
140
|
-
border-top: var(--hot-spacing-2x-small) solid var(--hot-color-red-600);
|
|
141
|
-
border-radius: 50%;
|
|
142
|
-
animation: spin 1s linear infinite;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
@keyframes spin {
|
|
146
|
-
0% {
|
|
147
|
-
transform: rotate(0deg);
|
|
148
|
-
}
|
|
149
|
-
100% {
|
|
150
|
-
transform: rotate(360deg);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.connecting-text {
|
|
155
|
-
font-size: var(--hot-font-size-small);
|
|
156
|
-
color: var(--hot-color-gray-600);
|
|
157
|
-
font-weight: var(--hot-font-weight-semibold);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
button {
|
|
161
|
-
width: 100%;
|
|
162
|
-
padding: 12px 20px;
|
|
163
|
-
border: none;
|
|
164
|
-
border-radius: 6px;
|
|
165
|
-
font-size: 14px;
|
|
166
|
-
font-weight: 500;
|
|
167
|
-
cursor: pointer;
|
|
168
|
-
transition: all 0.2s;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
.btn-primary {
|
|
172
|
-
background: var(--hot-color-gray-700);
|
|
173
|
-
color: white;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.btn-primary:hover {
|
|
177
|
-
background: var(--hot-color-gray-600);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.btn-secondary {
|
|
181
|
-
border: 1px solid var(--hot-color-gray-700);
|
|
182
|
-
border-radius: var(--hot-border-radius-medium);
|
|
183
|
-
background-color: white;
|
|
184
|
-
color: var(--hot-color-gray-700);
|
|
185
|
-
margin-top: 8px;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.btn-secondary:hover {
|
|
189
|
-
background: var(--hot-color-gray-50);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.error {
|
|
193
|
-
background: var(--hot-color-red-50);
|
|
194
|
-
border: var(--hot-border-width, 1px) solid var(--hot-color-red-200);
|
|
195
|
-
border-radius: var(--hot-border-radius-medium);
|
|
196
|
-
padding: var(--hot-spacing-small);
|
|
197
|
-
color: var(--hot-color-red-700);
|
|
198
|
-
margin-bottom: var(--hot-spacing-medium);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
.profile {
|
|
202
|
-
background: var(--hot-color-gray-50);
|
|
203
|
-
border-radius: var(--hot-border-radius-large);
|
|
204
|
-
padding: var(--hot-spacing-large);
|
|
205
|
-
margin-bottom: var(--hot-spacing-medium);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.profile-header {
|
|
209
|
-
display: flex;
|
|
210
|
-
align-items: center;
|
|
211
|
-
gap: var(--hot-spacing-small);
|
|
212
|
-
margin-bottom: var(--hot-spacing-medium);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
.profile-avatar {
|
|
216
|
-
width: var(--hot-spacing-3x-large);
|
|
217
|
-
height: var(--hot-spacing-3x-large);
|
|
218
|
-
border-radius: 50%;
|
|
219
|
-
background: var(--hot-color-gray-200);
|
|
220
|
-
display: flex;
|
|
221
|
-
align-items: center;
|
|
222
|
-
justify-content: center;
|
|
223
|
-
font-size: var(--hot-font-size-large);
|
|
224
|
-
font-weight: var(--hot-font-weight-bold);
|
|
225
|
-
color: var(--hot-color-gray-600);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.profile-info {
|
|
229
|
-
padding: var(--hot-spacing-x-small) var(--hot-spacing-medium);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.profile-email {
|
|
233
|
-
font-size: var(--hot-font-size-small);
|
|
234
|
-
font-weight: var(--hot-font-weight-bold);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.osm-section {
|
|
238
|
-
border-top: var(--hot-border-width, 1px) solid var(--hot-color-gray-100);
|
|
239
|
-
padding-top: var(--hot-spacing-medium);
|
|
240
|
-
padding-bottom: var(--hot-spacing-small);
|
|
241
|
-
margin-top: var(--hot-spacing-medium);
|
|
242
|
-
text-align: center;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
.osm-connected {
|
|
246
|
-
display: flex;
|
|
247
|
-
align-items: center;
|
|
248
|
-
justify-content: center;
|
|
249
|
-
padding: var(--hot-spacing-small);
|
|
250
|
-
background: linear-gradient(
|
|
251
|
-
135deg,
|
|
252
|
-
var(--hot-color-success-50) 0%,
|
|
253
|
-
var(--hot-color-success-50) 100%
|
|
254
|
-
);
|
|
255
|
-
border-radius: var(--hot-border-radius-large);
|
|
256
|
-
border: var(--hot-border-width, 1px) solid var(--hot-color-success-200);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
.osm-badge {
|
|
260
|
-
display: flex;
|
|
261
|
-
align-items: center;
|
|
262
|
-
gap: var(--hot-spacing-x-small);
|
|
263
|
-
color: var(--hot-color-success-800);
|
|
264
|
-
font-weight: var(--hot-font-weight-semibold);
|
|
265
|
-
font-size: var(--hot-font-size-small);
|
|
266
|
-
text-align: left;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.osm-badge-icon {
|
|
270
|
-
font-size: var(--hot-font-size-medium);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
.osm-username {
|
|
274
|
-
font-size: var(--hot-font-size-x-small);
|
|
275
|
-
color: var(--hot-color-success-700);
|
|
276
|
-
margin-top: var(--hot-spacing-2x-small);
|
|
277
|
-
}
|
|
278
|
-
.osm-prompt {
|
|
279
|
-
background: var(--hot-color-warning-50);
|
|
280
|
-
border: var(--hot-border-width, 1px) solid var(--hot-color-warning-200);
|
|
281
|
-
border-radius: var(--hot-border-radius-large);
|
|
282
|
-
padding: var(--hot-spacing-large);
|
|
283
|
-
margin-bottom: var(--hot-spacing-medium);
|
|
284
|
-
text-align: center;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
.osm-prompt-title {
|
|
288
|
-
font-weight: var(--hot-font-weight-semibold);
|
|
289
|
-
font-size: var(--hot-font-size-medium);
|
|
290
|
-
margin-bottom: var(--hot-spacing-small);
|
|
291
|
-
color: var(--hot-color-gray-900);
|
|
292
|
-
text-align: center;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
.osm-prompt-text {
|
|
296
|
-
font-size: var(--hot-font-size-small);
|
|
297
|
-
color: var(--hot-color-gray-600);
|
|
298
|
-
margin-bottom: var(--hot-spacing-medium);
|
|
299
|
-
line-height: var(--hot-line-height-normal);
|
|
300
|
-
text-align: center;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.osm-status-badge {
|
|
304
|
-
position: absolute;
|
|
305
|
-
top: 2px;
|
|
306
|
-
right: 2px;
|
|
307
|
-
width: var(--hot-font-size-small);
|
|
308
|
-
height: var(--hot-font-size-small);
|
|
309
|
-
border-radius: 50%;
|
|
310
|
-
border: var(--hot-spacing-3x-small) solid white;
|
|
311
|
-
display: flex;
|
|
312
|
-
align-items: center;
|
|
313
|
-
justify-content: center;
|
|
314
|
-
font-size: var(--hot-font-size-2x-small);
|
|
315
|
-
color: white;
|
|
316
|
-
font-weight: var(--hot-font-weight-bold);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
.osm-status-badge.connected {
|
|
320
|
-
background-color: var(--hot-color-success-600);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.osm-status-badge.required {
|
|
324
|
-
background-color: var(--hot-color-warning-600);
|
|
325
|
-
}
|
|
326
|
-
.header-avatar {
|
|
327
|
-
width: var(--hot-spacing-2x-large);
|
|
328
|
-
height: var(--hot-spacing-2x-large);
|
|
329
|
-
border-radius: 50%;
|
|
330
|
-
background: var(--hot-color-gray-800);
|
|
331
|
-
display: inline-flex;
|
|
332
|
-
align-items: center;
|
|
333
|
-
justify-content: center;
|
|
334
|
-
font-size: var(--hot-font-size-small);
|
|
335
|
-
font-weight: var(--hot-font-weight-semibold);
|
|
336
|
-
color: white;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.login-link {
|
|
340
|
-
color: var(--hot-color-neutral-950);
|
|
341
|
-
font-size: var(--hot-font-size-small);
|
|
342
|
-
border-radius: var(--hot-border-radius-medium);
|
|
343
|
-
text-decoration: none;
|
|
344
|
-
padding: 14px;
|
|
345
|
-
}
|
|
346
|
-
.login-link:hover {
|
|
347
|
-
background: var(--hot-color-gray-50);
|
|
348
|
-
}
|
|
349
|
-
/* Dropdown styles */
|
|
350
|
-
.dropdown {
|
|
351
|
-
position: relative;
|
|
352
|
-
display: inline-block;
|
|
353
|
-
}
|
|
354
|
-
.dropdown-trigger {
|
|
355
|
-
background: none;
|
|
356
|
-
border: none;
|
|
357
|
-
padding: var(--hot-spacing-x-small);
|
|
358
|
-
cursor: pointer;
|
|
359
|
-
position: relative;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.dropdown-trigger.no-hover:hover,
|
|
363
|
-
.dropdown-trigger.no-hover:active,
|
|
364
|
-
.dropdown-trigger.no-hover:focus {
|
|
365
|
-
background: none;
|
|
366
|
-
outline: none;
|
|
367
|
-
}
|
|
368
|
-
.dropdown-content {
|
|
369
|
-
position: absolute;
|
|
370
|
-
right: 0;
|
|
371
|
-
background: white;
|
|
372
|
-
border: 1px solid var(--hot-color-gray-100);
|
|
373
|
-
border-radius: var(--hot-border-radius-medium);
|
|
374
|
-
z-index: 1000;
|
|
375
|
-
opacity: 0;
|
|
376
|
-
visibility: hidden;
|
|
377
|
-
transform: translateY(-10px);
|
|
378
|
-
transition:
|
|
379
|
-
opacity 0.2s ease,
|
|
380
|
-
visibility 0.2s ease,
|
|
381
|
-
transform 0.2s ease;
|
|
382
|
-
}
|
|
383
|
-
@media (max-width: 768px) {
|
|
384
|
-
.dropdown-content {
|
|
385
|
-
position: fixed;
|
|
386
|
-
width: 100%;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
.dropdown-content.open {
|
|
391
|
-
opacity: 1;
|
|
392
|
-
visibility: visible;
|
|
393
|
-
transform: translateY(0);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.dropdown-content button {
|
|
397
|
-
display: flex;
|
|
398
|
-
align-items: center;
|
|
399
|
-
width: 100%;
|
|
400
|
-
padding: var(--hot-spacing-small) var(--hot-spacing-medium);
|
|
401
|
-
background: none;
|
|
402
|
-
border: none;
|
|
403
|
-
cursor: pointer;
|
|
404
|
-
text-align: left;
|
|
405
|
-
transition: background-color 0.2s ease;
|
|
406
|
-
gap: var(--hot-spacing-small);
|
|
407
|
-
font-size: var(--hot-font-size-small);
|
|
408
|
-
color: var(--hot-color-gray-900);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
.dropdown-content button:hover {
|
|
412
|
-
background-color: var(--hot-color-gray-50);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
.dropdown-content button:focus {
|
|
416
|
-
background-color: var(--hot-color-gray-50);
|
|
417
|
-
outline: 2px solid var(--hot-color-gray-500);
|
|
418
|
-
outline-offset: -2px;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
.dropdown-content .profile-info {
|
|
422
|
-
padding: var(--hot-spacing-small) var(--hot-spacing-medium);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
.dropdown-content .profile-email {
|
|
426
|
-
font-size: var(--hot-font-size-small);
|
|
427
|
-
font-weight: var(--hot-font-weight-bold);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
.icon {
|
|
431
|
-
width: 20px;
|
|
432
|
-
height: 20px;
|
|
433
|
-
}
|
|
434
|
-
`;
|
|
435
|
-
|
|
436
136
|
// Get computed hankoUrl (priority: attribute > meta tag > window.HANKO_URL > origin)
|
|
437
137
|
get hankoUrl(): string {
|
|
438
138
|
if (this.hankoUrlAttr) {
|
|
@@ -619,6 +319,21 @@ export class HankoAuth extends LitElement {
|
|
|
619
319
|
}
|
|
620
320
|
}
|
|
621
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Get translated string for the current language
|
|
324
|
+
* Falls back to English if translation not found
|
|
325
|
+
* When user is logged in, uses their profile language instead of the lang prop
|
|
326
|
+
*/
|
|
327
|
+
private t(key: keyof typeof translations.en): string {
|
|
328
|
+
// When user is logged in, use their profile language
|
|
329
|
+
const effectiveLang =
|
|
330
|
+
this.user && this.userProfileLanguage
|
|
331
|
+
? this.userProfileLanguage
|
|
332
|
+
: this.lang;
|
|
333
|
+
const langTranslations = translations[effectiveLang] || translations.en;
|
|
334
|
+
return langTranslations[key] || translations.en[key] || key;
|
|
335
|
+
}
|
|
336
|
+
|
|
622
337
|
private warn(...args: any[]) {
|
|
623
338
|
console.warn(...args);
|
|
624
339
|
}
|
|
@@ -648,6 +363,7 @@ export class HankoAuth extends LitElement {
|
|
|
648
363
|
return path;
|
|
649
364
|
}
|
|
650
365
|
|
|
366
|
+
// styles injected to ensure global availability
|
|
651
367
|
private injectHotStyles() {
|
|
652
368
|
const stylesheets = [
|
|
653
369
|
{
|
|
@@ -1072,7 +788,7 @@ export class HankoAuth extends LitElement {
|
|
|
1072
788
|
}
|
|
1073
789
|
}
|
|
1074
790
|
|
|
1075
|
-
// Fetch profile display name from login backend
|
|
791
|
+
// Fetch profile display name and language from login backend
|
|
1076
792
|
private async fetchProfileDisplayName() {
|
|
1077
793
|
try {
|
|
1078
794
|
const profileUrl = `${this.hankoUrl}/api/profile/me`;
|
|
@@ -1091,6 +807,12 @@ export class HankoAuth extends LitElement {
|
|
|
1091
807
|
`${profile.first_name || ""} ${profile.last_name || ""}`.trim();
|
|
1092
808
|
this.log("👤 Display name set to:", this.profileDisplayName);
|
|
1093
809
|
}
|
|
810
|
+
|
|
811
|
+
// Set language from user profile if available
|
|
812
|
+
if (profile.language) {
|
|
813
|
+
this.userProfileLanguage = profile.language;
|
|
814
|
+
this.log("🌐 Language set from profile:", this.userProfileLanguage);
|
|
815
|
+
}
|
|
1094
816
|
}
|
|
1095
817
|
} catch (error) {
|
|
1096
818
|
this.log("⚠️ Could not fetch profile:", error);
|
|
@@ -1426,6 +1148,7 @@ export class HankoAuth extends LitElement {
|
|
|
1426
1148
|
this.osmConnected = false;
|
|
1427
1149
|
this.osmData = null;
|
|
1428
1150
|
this.hasAppMapping = false;
|
|
1151
|
+
this.userProfileLanguage = null; // Clear user's language preference
|
|
1429
1152
|
|
|
1430
1153
|
// Broadcast state changes to other instances
|
|
1431
1154
|
if (this._isPrimary) {
|
|
@@ -1590,7 +1313,7 @@ export class HankoAuth extends LitElement {
|
|
|
1590
1313
|
);
|
|
1591
1314
|
|
|
1592
1315
|
if (this.loading) {
|
|
1593
|
-
return html
|
|
1316
|
+
return html`<div class="spinner-small"></div>`;
|
|
1594
1317
|
}
|
|
1595
1318
|
|
|
1596
1319
|
if (this.error) {
|
|
@@ -1631,7 +1354,9 @@ export class HankoAuth extends LitElement {
|
|
|
1631
1354
|
${this.osmRequired && this.osmLoading
|
|
1632
1355
|
? html`
|
|
1633
1356
|
<div class="osm-section">
|
|
1634
|
-
<div class="loading">
|
|
1357
|
+
<div class="loading">
|
|
1358
|
+
${this.t("checkingOsmConnection")}
|
|
1359
|
+
</div>
|
|
1635
1360
|
</div>
|
|
1636
1361
|
`
|
|
1637
1362
|
: this.osmRequired && this.osmConnected
|
|
@@ -1641,7 +1366,7 @@ export class HankoAuth extends LitElement {
|
|
|
1641
1366
|
<div class="osm-badge">
|
|
1642
1367
|
<span class="osm-badge-icon">🗺️</span>
|
|
1643
1368
|
<div>
|
|
1644
|
-
<div
|
|
1369
|
+
<div>${this.t("connectedToOpenStreetMap")}</div>
|
|
1645
1370
|
${this.osmData?.osm_username
|
|
1646
1371
|
? html`
|
|
1647
1372
|
<div class="osm-username">
|
|
@@ -1663,20 +1388,22 @@ export class HankoAuth extends LitElement {
|
|
|
1663
1388
|
<div class="osm-connecting">
|
|
1664
1389
|
<div class="spinner"></div>
|
|
1665
1390
|
<div class="connecting-text">
|
|
1666
|
-
🗺️
|
|
1391
|
+
🗺️ ${this.t("connectingToOpenStreetMap")}
|
|
1667
1392
|
</div>
|
|
1668
1393
|
</div>
|
|
1669
1394
|
`
|
|
1670
1395
|
: html`
|
|
1671
|
-
<div class="osm-prompt-title"
|
|
1396
|
+
<div class="osm-prompt-title">
|
|
1397
|
+
🌍 ${this.t("osmRequired")}
|
|
1398
|
+
</div>
|
|
1672
1399
|
<div class="osm-prompt-text">
|
|
1673
|
-
|
|
1400
|
+
${this.t("osmRequiredText")}
|
|
1674
1401
|
</div>
|
|
1675
1402
|
<button
|
|
1676
1403
|
@click=${this.handleOSMConnect}
|
|
1677
1404
|
class="btn-primary"
|
|
1678
1405
|
>
|
|
1679
|
-
|
|
1406
|
+
${this.t("connectOsmAccount")}
|
|
1680
1407
|
</button>
|
|
1681
1408
|
`}
|
|
1682
1409
|
</div>
|
|
@@ -1684,7 +1411,7 @@ export class HankoAuth extends LitElement {
|
|
|
1684
1411
|
: ""}
|
|
1685
1412
|
|
|
1686
1413
|
<button @click=${this.handleLogout} class="btn-secondary">
|
|
1687
|
-
|
|
1414
|
+
${this.t("logOut")}
|
|
1688
1415
|
</button>
|
|
1689
1416
|
</div>
|
|
1690
1417
|
</div>
|
|
@@ -1695,7 +1422,7 @@ export class HankoAuth extends LitElement {
|
|
|
1695
1422
|
<div class="dropdown">
|
|
1696
1423
|
<button
|
|
1697
1424
|
@click=${this.toggleDropdown}
|
|
1698
|
-
aria-label="
|
|
1425
|
+
aria-label="${this.t("openAccountMenu")}"
|
|
1699
1426
|
aria-expanded=${this.isOpen}
|
|
1700
1427
|
aria-haspopup="true"
|
|
1701
1428
|
class="dropdown-trigger"
|
|
@@ -1706,7 +1433,8 @@ export class HankoAuth extends LitElement {
|
|
|
1706
1433
|
? html`
|
|
1707
1434
|
<span
|
|
1708
1435
|
class="osm-status-badge connected"
|
|
1709
|
-
title="
|
|
1436
|
+
title="${this.t("connectedToOsmAs")} @${this.osmData
|
|
1437
|
+
?.osm_username}"
|
|
1710
1438
|
>✓</span
|
|
1711
1439
|
>
|
|
1712
1440
|
`
|
|
@@ -1714,7 +1442,7 @@ export class HankoAuth extends LitElement {
|
|
|
1714
1442
|
? html`
|
|
1715
1443
|
<span
|
|
1716
1444
|
class="osm-status-badge required"
|
|
1717
|
-
title="
|
|
1445
|
+
title="${this.t("osmConnectionRequired")}"
|
|
1718
1446
|
>!</span
|
|
1719
1447
|
>
|
|
1720
1448
|
`
|
|
@@ -1728,14 +1456,15 @@ export class HankoAuth extends LitElement {
|
|
|
1728
1456
|
</div>
|
|
1729
1457
|
<button data-action="profile" @click=${this.handleDropdownSelect}>
|
|
1730
1458
|
<img src="${accountIcon}" class="icon" alt="Account icon" />
|
|
1731
|
-
|
|
1459
|
+
${this.t("myHotAccount")}
|
|
1732
1460
|
</button>
|
|
1733
1461
|
${this.osmRequired
|
|
1734
1462
|
? this.osmConnected
|
|
1735
1463
|
? html`
|
|
1736
1464
|
<button class="osm-connected" disabled>
|
|
1737
1465
|
<img src="${checkIcon}" alt="Check icon" class="icon" />
|
|
1738
|
-
|
|
1466
|
+
${this.t("connectedToOsm")}
|
|
1467
|
+
(@${this.osmData?.osm_username})
|
|
1739
1468
|
</button>
|
|
1740
1469
|
`
|
|
1741
1470
|
: html`
|
|
@@ -1744,13 +1473,13 @@ export class HankoAuth extends LitElement {
|
|
|
1744
1473
|
@click=${this.handleDropdownSelect}
|
|
1745
1474
|
>
|
|
1746
1475
|
<img src="${mapIcon}" alt="Check icon" class="icon" />
|
|
1747
|
-
|
|
1476
|
+
${this.t("connectToOsm")}
|
|
1748
1477
|
</button>
|
|
1749
1478
|
`
|
|
1750
1479
|
: ""}
|
|
1751
1480
|
<button data-action="logout" @click=${this.handleDropdownSelect}>
|
|
1752
1481
|
<img src="${logoutIcon}" alt="Log out icon" class="icon" />
|
|
1753
|
-
|
|
1482
|
+
${this.t("logOut")}
|
|
1754
1483
|
</button>
|
|
1755
1484
|
</div>
|
|
1756
1485
|
</div>
|
|
@@ -1810,9 +1539,13 @@ export class HankoAuth extends LitElement {
|
|
|
1810
1539
|
const loginBase = this.loginUrl || `${baseUrl}/app`;
|
|
1811
1540
|
const loginUrl = `${loginBase}?return_to=${encodeURIComponent(
|
|
1812
1541
|
returnTo,
|
|
1813
|
-
)}${this.osmRequired ? "&osm_required=true" : ""}${autoConnectParam}`;
|
|
1542
|
+
)}${this.osmRequired ? "&osm_required=true" : ""}${autoConnectParam}&lang=${this.lang}`;
|
|
1814
1543
|
|
|
1815
|
-
return html`<a
|
|
1544
|
+
return html`<a
|
|
1545
|
+
class="login-link ${this.buttonVariant} ${this.buttonColor}"
|
|
1546
|
+
href="${loginUrl}"
|
|
1547
|
+
>${this.t("logIn")}</a
|
|
1548
|
+
> `;
|
|
1816
1549
|
}
|
|
1817
1550
|
}
|
|
1818
1551
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translations for the hotosm-auth web component
|
|
3
|
+
**/
|
|
4
|
+
|
|
5
|
+
export interface Translations {
|
|
6
|
+
logIn: string;
|
|
7
|
+
logOut: string;
|
|
8
|
+
myHotAccount: string;
|
|
9
|
+
connectToOsm: string;
|
|
10
|
+
connectedToOsm: string;
|
|
11
|
+
connectedToOpenStreetMap: string;
|
|
12
|
+
connectingToOpenStreetMap: string;
|
|
13
|
+
checkingOsmConnection: string;
|
|
14
|
+
osmRequired: string;
|
|
15
|
+
osmRequiredText: string;
|
|
16
|
+
connectOsmAccount: string;
|
|
17
|
+
openAccountMenu: string;
|
|
18
|
+
connectedToOsmAs: string;
|
|
19
|
+
osmConnectionRequired: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const translations: Record<string, Translations> = {
|
|
23
|
+
en: {
|
|
24
|
+
logIn: "Log in",
|
|
25
|
+
logOut: "Log Out",
|
|
26
|
+
myHotAccount: "My HOT Account",
|
|
27
|
+
connectToOsm: "Connect to OSM",
|
|
28
|
+
connectedToOsm: "Connected to OSM",
|
|
29
|
+
connectedToOpenStreetMap: "Connected to OpenStreetMap",
|
|
30
|
+
connectingToOpenStreetMap: "Connecting to OpenStreetMap...",
|
|
31
|
+
checkingOsmConnection: "Checking OSM connection...",
|
|
32
|
+
osmRequired: "OSM Required",
|
|
33
|
+
osmRequiredText: "This endpoint requires OSM connection.",
|
|
34
|
+
connectOsmAccount: "Connect OSM Account",
|
|
35
|
+
openAccountMenu: "Open account menu",
|
|
36
|
+
connectedToOsmAs: "Connected to OSM as",
|
|
37
|
+
osmConnectionRequired: "OSM connection required",
|
|
38
|
+
},
|
|
39
|
+
es: {
|
|
40
|
+
logIn: "Iniciar sesión",
|
|
41
|
+
logOut: "Cerrar sesión",
|
|
42
|
+
myHotAccount: "Mi cuenta HOT",
|
|
43
|
+
connectToOsm: "Conectar a OSM",
|
|
44
|
+
connectedToOsm: "Conectado a OSM",
|
|
45
|
+
connectedToOpenStreetMap: "Conectado a OpenStreetMap",
|
|
46
|
+
connectingToOpenStreetMap: "Conectando a OpenStreetMap...",
|
|
47
|
+
checkingOsmConnection: "Verificando conexión OSM...",
|
|
48
|
+
osmRequired: "OSM Requerido",
|
|
49
|
+
osmRequiredText: "Este endpoint requiere conexión OSM.",
|
|
50
|
+
connectOsmAccount: "Conectar cuenta OSM",
|
|
51
|
+
openAccountMenu: "Abrir menú de cuenta",
|
|
52
|
+
connectedToOsmAs: "Conectado a OSM como",
|
|
53
|
+
osmConnectionRequired: "Se requiere conexión OSM",
|
|
54
|
+
},
|
|
55
|
+
fr: {
|
|
56
|
+
logIn: "Se connecter",
|
|
57
|
+
logOut: "Se déconnecter",
|
|
58
|
+
myHotAccount: "Mon compte HOT",
|
|
59
|
+
connectToOsm: "Connecter à OSM",
|
|
60
|
+
connectedToOsm: "Connecté à OSM",
|
|
61
|
+
connectedToOpenStreetMap: "Connecté à OpenStreetMap",
|
|
62
|
+
connectingToOpenStreetMap: "Connexion à OpenStreetMap...",
|
|
63
|
+
checkingOsmConnection: "Vérification de la connexion OSM...",
|
|
64
|
+
osmRequired: "OSM requis",
|
|
65
|
+
osmRequiredText: "Ce point de terminaison nécessite une connexion OSM.",
|
|
66
|
+
connectOsmAccount: "Connecter le compte OSM",
|
|
67
|
+
openAccountMenu: "Ouvrir le menu du compte",
|
|
68
|
+
connectedToOsmAs: "Connecté à OSM en tant que",
|
|
69
|
+
osmConnectionRequired: "Connexion OSM requise",
|
|
70
|
+
},
|
|
71
|
+
pt: {
|
|
72
|
+
logIn: "Entrar",
|
|
73
|
+
logOut: "Sair",
|
|
74
|
+
myHotAccount: "Minha conta HOT",
|
|
75
|
+
connectToOsm: "Conectar ao OSM",
|
|
76
|
+
connectedToOsm: "Conectado ao OSM",
|
|
77
|
+
connectedToOpenStreetMap: "Conectado ao OpenStreetMap",
|
|
78
|
+
connectingToOpenStreetMap: "Conectando ao OpenStreetMap...",
|
|
79
|
+
checkingOsmConnection: "Verificando conexão OSM...",
|
|
80
|
+
osmRequired: "OSM Necessário",
|
|
81
|
+
osmRequiredText: "Este endpoint requer conexão OSM.",
|
|
82
|
+
connectOsmAccount: "Conectar conta OSM",
|
|
83
|
+
openAccountMenu: "Abrir menu da conta",
|
|
84
|
+
connectedToOsmAs: "Conectado ao OSM como",
|
|
85
|
+
osmConnectionRequired: "Conexão OSM necessária",
|
|
86
|
+
},
|
|
87
|
+
};
|