@kengcom202/kcore-ui 0.4.0 → 0.6.0
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 +1 -1
- package/fesm2022/kengcom202-kcore-ui.mjs +487 -96
- package/fesm2022/kengcom202-kcore-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/kengcom202-kcore-ui.d.ts +192 -28
|
@@ -5,6 +5,7 @@ import { FormBuilder, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, Valid
|
|
|
5
5
|
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
|
|
6
6
|
import { ActivatedRoute, Router, RouterLink } from "@angular/router";
|
|
7
7
|
import { catchError, throwError } from "rxjs";
|
|
8
|
+
import { toSignal } from "@angular/core/rxjs-interop";
|
|
8
9
|
var AnchoredPopup = class {
|
|
9
10
|
anchor;
|
|
10
11
|
component;
|
|
@@ -1395,7 +1396,8 @@ const KC_AUTH_CONFIG = new InjectionToken("KC_AUTH_CONFIG");
|
|
|
1395
1396
|
const KC_AUTH_PATHS = {
|
|
1396
1397
|
login: "/login",
|
|
1397
1398
|
register: "/register",
|
|
1398
|
-
ssoCallback: "/sso-callback"
|
|
1399
|
+
ssoCallback: "/sso-callback",
|
|
1400
|
+
thaidCallback: "/thaid-callback"
|
|
1399
1401
|
};
|
|
1400
1402
|
function resolveKcAuthConfig(o) {
|
|
1401
1403
|
return {
|
|
@@ -1404,8 +1406,10 @@ function resolveKcAuthConfig(o) {
|
|
|
1404
1406
|
apiBase: (o.apiBase ?? "/api").replace(/\/+$/, ""),
|
|
1405
1407
|
homeUrl: o.homeUrl || "/",
|
|
1406
1408
|
sso: o.sso ? { label: o.sso.label || "SSO" } : null,
|
|
1407
|
-
|
|
1409
|
+
thaid: o.thaid ? { label: o.thaid.label || "ThaiD" } : null,
|
|
1410
|
+
passwordLogin: o.passwordLogin !== false || !o.sso && !o.thaid,
|
|
1408
1411
|
register: o.register ?? "none",
|
|
1412
|
+
profileOrgChange: o.profileOrgChange === true,
|
|
1409
1413
|
orgLabel: o.orgLabel || "หน่วยงาน",
|
|
1410
1414
|
adminRoles: o.adminRoles ?? ["admin"],
|
|
1411
1415
|
passwordMinLength: o.passwordMinLength ?? 4,
|
|
@@ -1418,6 +1422,12 @@ function provideKcAuth(options) {
|
|
|
1418
1422
|
useValue: resolveKcAuthConfig(options)
|
|
1419
1423
|
}]);
|
|
1420
1424
|
}
|
|
1425
|
+
const KC_LOGIN_LOCK_SECONDS = 300;
|
|
1426
|
+
function kcLoginRetryAfter(err) {
|
|
1427
|
+
if (!(err instanceof HttpErrorResponse) || err.status !== 429) return 0;
|
|
1428
|
+
const s = Number(err.error?.retryAfter);
|
|
1429
|
+
return Number.isFinite(s) && s > 0 ? Math.ceil(s) : 300;
|
|
1430
|
+
}
|
|
1421
1431
|
function kcErrorMessage(err, fallback) {
|
|
1422
1432
|
if (err instanceof HttpErrorResponse) {
|
|
1423
1433
|
const m = err.error?.message;
|
|
@@ -1429,6 +1439,7 @@ function kcErrorMessage(err, fallback) {
|
|
|
1429
1439
|
const SESSION_KEY = "kc_session";
|
|
1430
1440
|
const KC_SSO_STATE_KEY = "kc_sso_state";
|
|
1431
1441
|
const KC_SSO_TICKET_KEY = "kc_sso_ticket";
|
|
1442
|
+
const KC_THAID_STATE_KEY = "kc_thaid_state";
|
|
1432
1443
|
const KC_RETURN_URL_KEY = "kc_return_url";
|
|
1433
1444
|
function kcTokenExpired(token, now = Date.now()) {
|
|
1434
1445
|
const part = token.split(".")[1];
|
|
@@ -1492,6 +1503,7 @@ var KcAuthService = class KcAuthService {
|
|
|
1492
1503
|
for (const k of [
|
|
1493
1504
|
KC_SSO_STATE_KEY,
|
|
1494
1505
|
KC_SSO_TICKET_KEY,
|
|
1506
|
+
KC_THAID_STATE_KEY,
|
|
1495
1507
|
KC_RETURN_URL_KEY
|
|
1496
1508
|
]) this.storage("sessionStorage")?.removeItem(k);
|
|
1497
1509
|
}
|
|
@@ -1719,6 +1731,9 @@ const AUTH_CSS = `
|
|
|
1719
1731
|
.kc-auth-field-error { color: var(--kc-auth-danger, #c62828); font-size: 0.86em; }
|
|
1720
1732
|
.kc-auth-hint { margin: 2px 0 0; color: var(--kc-auth-muted, #806f80); font-size: 0.86em; }
|
|
1721
1733
|
.kc-auth-check { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; }
|
|
1734
|
+
.kc-auth-check:has(input:disabled) { cursor: not-allowed; opacity: 0.7; }
|
|
1735
|
+
.kc-auth-methods { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 22px; }
|
|
1736
|
+
.kc-auth-methods > .kc-auth-btn { margin-left: auto; min-width: 110px; }
|
|
1722
1737
|
|
|
1723
1738
|
/* ---- ปุ่ม ---- */
|
|
1724
1739
|
.kc-auth-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 6px; }
|
|
@@ -1760,7 +1775,7 @@ const AUTH_CSS = `
|
|
|
1760
1775
|
.kc-auth-btn-row { display: flex; gap: 10px; margin-top: 6px; }
|
|
1761
1776
|
.kc-auth-btn-row > .kc-auth-btn { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
1762
1777
|
/* ปิดรหัสผ่าน: ปุ่ม SSO ปุ่มเดียวตรงกลาง */
|
|
1763
|
-
.kc-auth-sso-only { display: flex;
|
|
1778
|
+
.kc-auth-sso-only { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 8px 0 4px; }
|
|
1764
1779
|
.kc-auth-sso-only > .kc-auth-btn { min-width: 75%; height: 46px; padding: 0 24px; font-size: 1.05em; }
|
|
1765
1780
|
.kc-auth-btn.block { display: flex; width: 100%; }
|
|
1766
1781
|
.kc-auth-btn.sm { height: 30px; padding: 0 10px; font-size: 0.93em; }
|
|
@@ -1981,6 +1996,23 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1981
1996
|
}],
|
|
1982
1997
|
ctorParameters: () => []
|
|
1983
1998
|
});
|
|
1999
|
+
const KC_LOGIN_LOCK_KEY = "kc_login_lock";
|
|
2000
|
+
const lockKey = (username) => username.trim().toLowerCase();
|
|
2001
|
+
function readLocks(storage) {
|
|
2002
|
+
try {
|
|
2003
|
+
const raw = JSON.parse(storage?.getItem("kc_login_lock") ?? "{}");
|
|
2004
|
+
const now = Date.now();
|
|
2005
|
+
return Object.fromEntries(Object.entries(raw && typeof raw === "object" ? raw : {}).filter((e) => typeof e[1] === "number" && e[1] > now));
|
|
2006
|
+
} catch {
|
|
2007
|
+
return {};
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
function writeLocks(storage, locks) {
|
|
2011
|
+
try {
|
|
2012
|
+
if (Object.keys(locks).length) storage?.setItem(KC_LOGIN_LOCK_KEY, JSON.stringify(locks));
|
|
2013
|
+
else storage?.removeItem(KC_LOGIN_LOCK_KEY);
|
|
2014
|
+
} catch {}
|
|
2015
|
+
}
|
|
1984
2016
|
var KcLoginComponent = class KcLoginComponent {
|
|
1985
2017
|
config = inject(KC_AUTH_CONFIG);
|
|
1986
2018
|
paths = KC_AUTH_PATHS;
|
|
@@ -1988,19 +2020,57 @@ var KcLoginComponent = class KcLoginComponent {
|
|
|
1988
2020
|
http = inject(HttpClient);
|
|
1989
2021
|
router = inject(Router);
|
|
1990
2022
|
route = inject(ActivatedRoute);
|
|
2023
|
+
doc = inject(DOCUMENT);
|
|
1991
2024
|
busy = signal("", ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
|
|
2025
|
+
methods = signal({
|
|
2026
|
+
password: true,
|
|
2027
|
+
sso: true,
|
|
2028
|
+
thaid: true
|
|
2029
|
+
}, ...ngDevMode ? [{ debugName: "methods" }] : /* istanbul ignore next */ []);
|
|
2030
|
+
external = computed(() => [...this.config.sso && this.methods().sso ? [{
|
|
2031
|
+
key: "sso",
|
|
2032
|
+
label: this.config.sso.label,
|
|
2033
|
+
stateKey: KC_SSO_STATE_KEY
|
|
2034
|
+
}] : [], ...this.config.thaid && this.methods().thaid ? [{
|
|
2035
|
+
key: "thaid",
|
|
2036
|
+
label: this.config.thaid.label,
|
|
2037
|
+
stateKey: KC_THAID_STATE_KEY
|
|
2038
|
+
}] : []], ...ngDevMode ? [{ debugName: "external" }] : /* istanbul ignore next */ []);
|
|
2039
|
+
showPassword = computed(() => this.config.passwordLogin && this.methods().password || !this.external().length, ...ngDevMode ? [{ debugName: "showPassword" }] : /* istanbul ignore next */ []);
|
|
1992
2040
|
message = signal(null, ...ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []);
|
|
1993
2041
|
form = inject(FormBuilder).nonNullable.group({
|
|
1994
2042
|
username: ["", Validators.required],
|
|
1995
2043
|
password: ["", Validators.required]
|
|
1996
2044
|
});
|
|
2045
|
+
locks = signal(readLocks(this.storage()), ...ngDevMode ? [{ debugName: "locks" }] : /* istanbul ignore next */ []);
|
|
2046
|
+
now = signal(Date.now(), ...ngDevMode ? [{ debugName: "now" }] : /* istanbul ignore next */ []);
|
|
2047
|
+
username = toSignal(this.form.controls.username.valueChanges, { initialValue: "" });
|
|
2048
|
+
lockLeft = computed(() => {
|
|
2049
|
+
const until = this.locks()[lockKey(this.username())] ?? 0;
|
|
2050
|
+
return Math.max(0, Math.ceil((until - this.now()) / 1e3));
|
|
2051
|
+
}, ...ngDevMode ? [{ debugName: "lockLeft" }] : /* istanbul ignore next */ []);
|
|
2052
|
+
lockText = computed(() => {
|
|
2053
|
+
const s = this.lockLeft();
|
|
2054
|
+
return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`;
|
|
2055
|
+
}, ...ngDevMode ? [{ debugName: "lockText" }] : /* istanbul ignore next */ []);
|
|
2056
|
+
timer;
|
|
1997
2057
|
constructor() {
|
|
1998
2058
|
ensureAuthStyles();
|
|
2059
|
+
inject(DestroyRef).onDestroy(() => this.stopTimer());
|
|
2060
|
+
if (Object.keys(this.locks()).length) this.startTimer();
|
|
1999
2061
|
}
|
|
2000
2062
|
get returnUrl() {
|
|
2001
2063
|
return this.auth.safeReturnUrl(this.route.snapshot.queryParamMap.get("returnUrl"));
|
|
2002
2064
|
}
|
|
2003
2065
|
ngOnInit() {
|
|
2066
|
+
this.http.get(`${this.config.apiBase}/user/login-methods`).subscribe({
|
|
2067
|
+
next: (m) => this.methods.set({
|
|
2068
|
+
password: m?.password !== false,
|
|
2069
|
+
sso: m?.sso !== false,
|
|
2070
|
+
thaid: m?.thaid !== false
|
|
2071
|
+
}),
|
|
2072
|
+
error: () => {}
|
|
2073
|
+
});
|
|
2004
2074
|
if (this.route.snapshot.queryParamMap.get("expired") === "1") this.message.set({
|
|
2005
2075
|
text: "หมดเวลาการใช้งาน กรุณาเข้าสู่ระบบใหม่",
|
|
2006
2076
|
error: false
|
|
@@ -2008,7 +2078,7 @@ var KcLoginComponent = class KcLoginComponent {
|
|
|
2008
2078
|
else if (this.auth.isLoggedIn()) this.router.navigateByUrl(this.returnUrl);
|
|
2009
2079
|
}
|
|
2010
2080
|
submit() {
|
|
2011
|
-
if (this.busy()) return;
|
|
2081
|
+
if (this.busy() || this.lockLeft()) return;
|
|
2012
2082
|
const v = this.form.getRawValue();
|
|
2013
2083
|
if (!v.username.trim() || !v.password) {
|
|
2014
2084
|
this.message.set({
|
|
@@ -2024,33 +2094,66 @@ var KcLoginComponent = class KcLoginComponent {
|
|
|
2024
2094
|
password: v.password
|
|
2025
2095
|
}).subscribe({
|
|
2026
2096
|
next: (res) => {
|
|
2097
|
+
this.setLock(v.username, 0);
|
|
2027
2098
|
this.auth.start(res);
|
|
2028
2099
|
this.router.navigateByUrl(this.returnUrl);
|
|
2029
2100
|
},
|
|
2030
2101
|
error: (err) => {
|
|
2031
2102
|
this.busy.set("");
|
|
2032
|
-
|
|
2103
|
+
const wait = kcLoginRetryAfter(err);
|
|
2104
|
+
if (wait) this.setLock(v.username, Date.now() + wait * 1e3);
|
|
2105
|
+
else this.message.set({
|
|
2033
2106
|
text: kcErrorMessage(err, "เข้าสู่ระบบไม่สำเร็จ"),
|
|
2034
2107
|
error: true
|
|
2035
2108
|
});
|
|
2036
2109
|
}
|
|
2037
2110
|
});
|
|
2038
2111
|
}
|
|
2039
|
-
|
|
2040
|
-
const
|
|
2041
|
-
if (
|
|
2042
|
-
|
|
2112
|
+
setLock(username, until) {
|
|
2113
|
+
const next = { ...this.locks() };
|
|
2114
|
+
if (until) next[lockKey(username)] = until;
|
|
2115
|
+
else delete next[lockKey(username)];
|
|
2116
|
+
this.locks.set(next);
|
|
2117
|
+
this.now.set(Date.now());
|
|
2118
|
+
writeLocks(this.storage(), next);
|
|
2119
|
+
if (until) this.startTimer();
|
|
2120
|
+
}
|
|
2121
|
+
startTimer() {
|
|
2122
|
+
this.timer ??= setInterval(() => {
|
|
2123
|
+
this.now.set(Date.now());
|
|
2124
|
+
const live = Object.fromEntries(Object.entries(this.locks()).filter(([, until]) => until > this.now()));
|
|
2125
|
+
if (Object.keys(live).length === Object.keys(this.locks()).length) return;
|
|
2126
|
+
this.locks.set(live);
|
|
2127
|
+
writeLocks(this.storage(), live);
|
|
2128
|
+
if (!Object.keys(live).length) this.stopTimer();
|
|
2129
|
+
}, 1e3);
|
|
2130
|
+
}
|
|
2131
|
+
stopTimer() {
|
|
2132
|
+
clearInterval(this.timer);
|
|
2133
|
+
this.timer = void 0;
|
|
2134
|
+
}
|
|
2135
|
+
storage() {
|
|
2136
|
+
try {
|
|
2137
|
+
return this.doc.defaultView?.localStorage ?? null;
|
|
2138
|
+
} catch {
|
|
2139
|
+
return null;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
startExternal(key) {
|
|
2143
|
+
const p = this.external().find((x) => x.key === key);
|
|
2144
|
+
if (!p || this.busy()) return;
|
|
2145
|
+
this.busy.set(key);
|
|
2043
2146
|
this.message.set(null);
|
|
2044
|
-
this.http.get(`${this.config.apiBase}/user/
|
|
2147
|
+
this.http.get(`${this.config.apiBase}/user/${key}/authorize-url`).subscribe({
|
|
2045
2148
|
next: ({ url, state }) => {
|
|
2046
|
-
this.auth.setTemp(
|
|
2149
|
+
this.auth.setTemp(p.stateKey, state);
|
|
2047
2150
|
this.auth.setTemp(KC_RETURN_URL_KEY, this.returnUrl);
|
|
2048
2151
|
this.auth.redirect(url);
|
|
2049
2152
|
},
|
|
2050
2153
|
error: (err) => {
|
|
2051
2154
|
this.busy.set("");
|
|
2052
2155
|
this.message.set({
|
|
2053
|
-
text: err.status === 404 ? `ระบบยังไม่ได้ตั้งค่าการเข้าสู่ระบบด้วย ${
|
|
2156
|
+
text: err.status === 404 ? `ระบบยังไม่ได้ตั้งค่าการเข้าสู่ระบบด้วย ${p.label}` : kcErrorMessage(err, `ไม่สามารถเชื่อมต่อ ${p.label} ได้ กรุณาลองใหม่อีกครั้ง`),
|
|
2054
2157
|
error: true
|
|
2055
2158
|
});
|
|
2056
2159
|
}
|
|
@@ -2080,11 +2183,15 @@ var KcLoginComponent = class KcLoginComponent {
|
|
|
2080
2183
|
<h1 class="kc-auth-title">{{ config.appName }}</h1>
|
|
2081
2184
|
<p class="kc-auth-subtitle">ลงชื่อเข้าสู่ระบบ</p>
|
|
2082
2185
|
|
|
2083
|
-
@if (
|
|
2186
|
+
@if (lockLeft()) {
|
|
2187
|
+
<div class="kc-auth-alert error" role="alert">
|
|
2188
|
+
เข้าสู่ระบบไม่สำเร็จเกินจำนวนครั้งที่กำหนด กรุณารอ {{ lockText() }} นาที แล้วลองใหม่อีกครั้ง
|
|
2189
|
+
</div>
|
|
2190
|
+
} @else if (message(); as m) {
|
|
2084
2191
|
<div class="kc-auth-alert" [class.error]="m.error" [class.info]="!m.error" role="alert">{{ m.text }}</div>
|
|
2085
2192
|
}
|
|
2086
2193
|
|
|
2087
|
-
@if (
|
|
2194
|
+
@if (showPassword()) {
|
|
2088
2195
|
<form [formGroup]="form" (ngSubmit)="submit()" novalidate>
|
|
2089
2196
|
<label class="kc-auth-field">
|
|
2090
2197
|
<span>ชื่อผู้ใช้</span>
|
|
@@ -2094,32 +2201,37 @@ var KcLoginComponent = class KcLoginComponent {
|
|
|
2094
2201
|
<span>รหัสผ่าน</span>
|
|
2095
2202
|
<kc-password><input class="kc-auth-input" type="password" formControlName="password" autocomplete="current-password" /></kc-password>
|
|
2096
2203
|
</label>
|
|
2097
|
-
<!-- ปุ่มเข้าสู่ระบบกับ SSO อยู่บรรทัดเดียวกัน
|
|
2204
|
+
<!-- ปุ่มเข้าสู่ระบบกับ SSO / ThaiD อยู่บรรทัดเดียวกัน แบ่งเท่ากัน — ปุ่มภายนอกใช้แค่ชื่อเพื่อให้พอดี -->
|
|
2098
2205
|
<div class="kc-auth-btn-row">
|
|
2099
|
-
<button type="submit" class="kc-auth-btn primary" [disabled]="!!busy()">
|
|
2100
|
-
{{ busy() === 'password' ? 'กำลังเข้าสู่ระบบ...' : 'เข้าสู่ระบบ' }}
|
|
2206
|
+
<button type="submit" class="kc-auth-btn primary" [disabled]="!!busy() || !!lockLeft()">
|
|
2207
|
+
{{ busy() === 'password' ? 'กำลังเข้าสู่ระบบ...' : lockLeft() ? 'รอ ' + lockText() : 'เข้าสู่ระบบ' }}
|
|
2101
2208
|
</button>
|
|
2102
|
-
@
|
|
2103
|
-
<button type="button" class="kc-auth-btn outline" [disabled]="!!busy()" (click)="
|
|
2104
|
-
[attr.title]="'เข้าสู่ระบบด้วย ' +
|
|
2105
|
-
{{ busy() ===
|
|
2209
|
+
@for (p of external(); track p.key) {
|
|
2210
|
+
<button type="button" class="kc-auth-btn outline" [disabled]="!!busy()" (click)="startExternal(p.key)"
|
|
2211
|
+
[attr.title]="'เข้าสู่ระบบด้วย ' + p.label">
|
|
2212
|
+
{{ busy() === p.key ? 'กำลังเชื่อมต่อ...' : p.label }}
|
|
2106
2213
|
</button>
|
|
2107
2214
|
}
|
|
2108
2215
|
</div>
|
|
2109
2216
|
</form>
|
|
2110
|
-
} @else
|
|
2111
|
-
<!-- ปิดรหัสผ่าน: มีแต่ปุ่ม SSO ตรงกลาง -->
|
|
2217
|
+
} @else {
|
|
2218
|
+
<!-- ปิดรหัสผ่าน: มีแต่ปุ่ม SSO / ThaiD ตรงกลาง -->
|
|
2112
2219
|
<div class="kc-auth-sso-only">
|
|
2113
|
-
|
|
2114
|
-
|
|
2220
|
+
@for (p of external(); track p.key) {
|
|
2221
|
+
<button type="button" class="kc-auth-btn" [class.primary]="$first" [class.outline]="!$first" [disabled]="!!busy()"
|
|
2222
|
+
(click)="startExternal(p.key)">
|
|
2223
|
+
{{ busy() === p.key ? 'กำลังเชื่อมต่อ...' : 'เข้าสู่ระบบด้วย ' + p.label }}
|
|
2115
2224
|
</button>
|
|
2225
|
+
}
|
|
2116
2226
|
</div>
|
|
2117
2227
|
}
|
|
2118
2228
|
|
|
2119
2229
|
@if (config.register === 'form') {
|
|
2120
2230
|
<p class="kc-auth-footer">ยังไม่มีบัญชี? <a class="kc-auth-link" [routerLink]="paths.register">ลงทะเบียน</a></p>
|
|
2121
|
-
} @else if (config.register === 'sso' && config.sso) {
|
|
2231
|
+
} @else if (config.register === 'sso' && config.sso && methods().sso) {
|
|
2122
2232
|
<p class="kc-auth-footer">ผู้ใช้ใหม่ กดปุ่ม {{ config.sso.label }} เพื่อลงทะเบียน</p>
|
|
2233
|
+
} @else if (config.register === 'thaid' && config.thaid && methods().thaid) {
|
|
2234
|
+
<p class="kc-auth-footer">ผู้ใช้ใหม่ กดปุ่ม {{ config.thaid.label }} เพื่อลงทะเบียน</p>
|
|
2123
2235
|
}
|
|
2124
2236
|
<ng-content />
|
|
2125
2237
|
</div>
|
|
@@ -2221,11 +2333,15 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2221
2333
|
<h1 class="kc-auth-title">{{ config.appName }}</h1>
|
|
2222
2334
|
<p class="kc-auth-subtitle">ลงชื่อเข้าสู่ระบบ</p>
|
|
2223
2335
|
|
|
2224
|
-
@if (
|
|
2336
|
+
@if (lockLeft()) {
|
|
2337
|
+
<div class="kc-auth-alert error" role="alert">
|
|
2338
|
+
เข้าสู่ระบบไม่สำเร็จเกินจำนวนครั้งที่กำหนด กรุณารอ {{ lockText() }} นาที แล้วลองใหม่อีกครั้ง
|
|
2339
|
+
</div>
|
|
2340
|
+
} @else if (message(); as m) {
|
|
2225
2341
|
<div class="kc-auth-alert" [class.error]="m.error" [class.info]="!m.error" role="alert">{{ m.text }}</div>
|
|
2226
2342
|
}
|
|
2227
2343
|
|
|
2228
|
-
@if (
|
|
2344
|
+
@if (showPassword()) {
|
|
2229
2345
|
<form [formGroup]="form" (ngSubmit)="submit()" novalidate>
|
|
2230
2346
|
<label class="kc-auth-field">
|
|
2231
2347
|
<span>ชื่อผู้ใช้</span>
|
|
@@ -2235,32 +2351,37 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2235
2351
|
<span>รหัสผ่าน</span>
|
|
2236
2352
|
<kc-password><input class="kc-auth-input" type="password" formControlName="password" autocomplete="current-password" /></kc-password>
|
|
2237
2353
|
</label>
|
|
2238
|
-
<!-- ปุ่มเข้าสู่ระบบกับ SSO อยู่บรรทัดเดียวกัน
|
|
2354
|
+
<!-- ปุ่มเข้าสู่ระบบกับ SSO / ThaiD อยู่บรรทัดเดียวกัน แบ่งเท่ากัน — ปุ่มภายนอกใช้แค่ชื่อเพื่อให้พอดี -->
|
|
2239
2355
|
<div class="kc-auth-btn-row">
|
|
2240
|
-
<button type="submit" class="kc-auth-btn primary" [disabled]="!!busy()">
|
|
2241
|
-
{{ busy() === 'password' ? 'กำลังเข้าสู่ระบบ...' : 'เข้าสู่ระบบ' }}
|
|
2356
|
+
<button type="submit" class="kc-auth-btn primary" [disabled]="!!busy() || !!lockLeft()">
|
|
2357
|
+
{{ busy() === 'password' ? 'กำลังเข้าสู่ระบบ...' : lockLeft() ? 'รอ ' + lockText() : 'เข้าสู่ระบบ' }}
|
|
2242
2358
|
</button>
|
|
2243
|
-
@
|
|
2244
|
-
<button type="button" class="kc-auth-btn outline" [disabled]="!!busy()" (click)="
|
|
2245
|
-
[attr.title]="'เข้าสู่ระบบด้วย ' +
|
|
2246
|
-
{{ busy() ===
|
|
2359
|
+
@for (p of external(); track p.key) {
|
|
2360
|
+
<button type="button" class="kc-auth-btn outline" [disabled]="!!busy()" (click)="startExternal(p.key)"
|
|
2361
|
+
[attr.title]="'เข้าสู่ระบบด้วย ' + p.label">
|
|
2362
|
+
{{ busy() === p.key ? 'กำลังเชื่อมต่อ...' : p.label }}
|
|
2247
2363
|
</button>
|
|
2248
2364
|
}
|
|
2249
2365
|
</div>
|
|
2250
2366
|
</form>
|
|
2251
|
-
} @else
|
|
2252
|
-
<!-- ปิดรหัสผ่าน: มีแต่ปุ่ม SSO ตรงกลาง -->
|
|
2367
|
+
} @else {
|
|
2368
|
+
<!-- ปิดรหัสผ่าน: มีแต่ปุ่ม SSO / ThaiD ตรงกลาง -->
|
|
2253
2369
|
<div class="kc-auth-sso-only">
|
|
2254
|
-
|
|
2255
|
-
|
|
2370
|
+
@for (p of external(); track p.key) {
|
|
2371
|
+
<button type="button" class="kc-auth-btn" [class.primary]="$first" [class.outline]="!$first" [disabled]="!!busy()"
|
|
2372
|
+
(click)="startExternal(p.key)">
|
|
2373
|
+
{{ busy() === p.key ? 'กำลังเชื่อมต่อ...' : 'เข้าสู่ระบบด้วย ' + p.label }}
|
|
2256
2374
|
</button>
|
|
2375
|
+
}
|
|
2257
2376
|
</div>
|
|
2258
2377
|
}
|
|
2259
2378
|
|
|
2260
2379
|
@if (config.register === 'form') {
|
|
2261
2380
|
<p class="kc-auth-footer">ยังไม่มีบัญชี? <a class="kc-auth-link" [routerLink]="paths.register">ลงทะเบียน</a></p>
|
|
2262
|
-
} @else if (config.register === 'sso' && config.sso) {
|
|
2381
|
+
} @else if (config.register === 'sso' && config.sso && methods().sso) {
|
|
2263
2382
|
<p class="kc-auth-footer">ผู้ใช้ใหม่ กดปุ่ม {{ config.sso.label }} เพื่อลงทะเบียน</p>
|
|
2383
|
+
} @else if (config.register === 'thaid' && config.thaid && methods().thaid) {
|
|
2384
|
+
<p class="kc-auth-footer">ผู้ใช้ใหม่ กดปุ่ม {{ config.thaid.label }} เพื่อลงทะเบียน</p>
|
|
2264
2385
|
}
|
|
2265
2386
|
<ng-content />
|
|
2266
2387
|
</div>
|
|
@@ -2293,7 +2414,7 @@ function kcContactProblem(email, mobile) {
|
|
|
2293
2414
|
var KcRegisterComponent = class KcRegisterComponent {
|
|
2294
2415
|
config = inject(KC_AUTH_CONFIG);
|
|
2295
2416
|
paths = KC_AUTH_PATHS;
|
|
2296
|
-
ssoLabel = this.config.sso?.label ?? "SSO";
|
|
2417
|
+
ssoLabel = this.config.register === "thaid" ? this.config.thaid?.label ?? "ThaiD" : this.config.sso?.label ?? "SSO";
|
|
2297
2418
|
auth = inject(KcAuthService);
|
|
2298
2419
|
http = inject(HttpClient);
|
|
2299
2420
|
router = inject(Router);
|
|
@@ -2326,7 +2447,7 @@ var KcRegisterComponent = class KcRegisterComponent {
|
|
|
2326
2447
|
this.error.set("ระบบนี้ไม่เปิดให้ลงทะเบียนเอง กรุณาติดต่อผู้ดูแลระบบ");
|
|
2327
2448
|
return;
|
|
2328
2449
|
}
|
|
2329
|
-
if (mode === "sso") {
|
|
2450
|
+
if (mode === "sso" || mode === "thaid") {
|
|
2330
2451
|
this.ticket = this.auth.getTemp(KC_SSO_TICKET_KEY);
|
|
2331
2452
|
if (!this.ticket) {
|
|
2332
2453
|
this.error.set(`ยังไม่ได้ยืนยันตัวตน กรุณากลับไปกด "เข้าสู่ระบบด้วย ${this.ssoLabel}" ที่หน้าเข้าสู่ระบบก่อน`);
|
|
@@ -2678,22 +2799,35 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2678
2799
|
}],
|
|
2679
2800
|
ctorParameters: () => []
|
|
2680
2801
|
});
|
|
2681
|
-
|
|
2802
|
+
const CALLBACK_TEMPLATE = `
|
|
2803
|
+
<div class="kc-auth-screen">
|
|
2804
|
+
<div class="kc-auth-card center">
|
|
2805
|
+
<h1 class="kc-auth-title">{{ label }}</h1>
|
|
2806
|
+
@if (failed()) {
|
|
2807
|
+
<div class="kc-auth-alert error" role="alert" style="margin-top: 16px">{{ message() }}</div>
|
|
2808
|
+
<a class="kc-auth-btn" [routerLink]="paths.login">กลับไปหน้าเข้าสู่ระบบ</a>
|
|
2809
|
+
} @else {
|
|
2810
|
+
<p class="kc-auth-subtitle" style="margin-bottom: 0">{{ message() }}</p>
|
|
2811
|
+
}
|
|
2812
|
+
</div>
|
|
2813
|
+
</div>
|
|
2814
|
+
`;
|
|
2815
|
+
var KcExternalCallbackBase = class KcExternalCallbackBase {
|
|
2682
2816
|
config = inject(KC_AUTH_CONFIG);
|
|
2683
2817
|
auth = inject(KcAuthService);
|
|
2684
2818
|
http = inject(HttpClient);
|
|
2685
|
-
route = inject(ActivatedRoute);
|
|
2686
2819
|
router = inject(Router);
|
|
2820
|
+
route = inject(ActivatedRoute);
|
|
2687
2821
|
paths = KC_AUTH_PATHS;
|
|
2688
|
-
|
|
2689
|
-
message = signal(`กำลังเข้าสู่ระบบผ่าน ${this.label}...`, ...ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []);
|
|
2822
|
+
message = signal("", ...ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []);
|
|
2690
2823
|
failed = signal(false, ...ngDevMode ? [{ debugName: "failed" }] : /* istanbul ignore next */ []);
|
|
2691
2824
|
constructor() {
|
|
2692
2825
|
ensureAuthStyles();
|
|
2693
2826
|
}
|
|
2694
2827
|
ngOnInit() {
|
|
2828
|
+
this.message.set(`กำลังเข้าสู่ระบบผ่าน ${this.label}...`);
|
|
2695
2829
|
const params = this.route.snapshot.queryParamMap;
|
|
2696
|
-
const expectedState = this.auth.takeTemp(
|
|
2830
|
+
const expectedState = this.auth.takeTemp(this.stateKey);
|
|
2697
2831
|
const code = params.get("code");
|
|
2698
2832
|
if (params.get("error")) {
|
|
2699
2833
|
this.fail(`${this.label} ปฏิเสธการเข้าสู่ระบบ กรุณาลองใหม่อีกครั้ง`);
|
|
@@ -2701,33 +2835,83 @@ var KcSsoCallbackComponent = class KcSsoCallbackComponent {
|
|
|
2701
2835
|
}
|
|
2702
2836
|
if (!code) {
|
|
2703
2837
|
this.message.set(`กำลังเชื่อมต่อกับ ${this.label}...`);
|
|
2704
|
-
this.
|
|
2705
|
-
next: ({ url, state }) => {
|
|
2706
|
-
this.auth.setTemp(KC_SSO_STATE_KEY, state);
|
|
2707
|
-
this.auth.redirect(url);
|
|
2708
|
-
},
|
|
2709
|
-
error: () => this.fail(`ไม่สามารถเชื่อมต่อ ${this.label} ได้ กรุณาเริ่มเข้าสู่ระบบใหม่`)
|
|
2710
|
-
});
|
|
2838
|
+
this.startFlow(this.endpoint, this.stateKey, this.label);
|
|
2711
2839
|
return;
|
|
2712
2840
|
}
|
|
2713
2841
|
if (!expectedState || expectedState !== params.get("state")) {
|
|
2714
2842
|
this.fail("คำขอเข้าสู่ระบบไม่ถูกต้อง (state ไม่ตรงกัน) กรุณาเริ่มเข้าสู่ระบบใหม่");
|
|
2715
2843
|
return;
|
|
2716
2844
|
}
|
|
2845
|
+
this.exchange(code);
|
|
2846
|
+
}
|
|
2847
|
+
startFlow(endpoint, stateKey, label) {
|
|
2848
|
+
this.http.get(`${this.config.apiBase}/user/${endpoint}/authorize-url`).subscribe({
|
|
2849
|
+
next: ({ url, state }) => {
|
|
2850
|
+
this.auth.setTemp(stateKey, state);
|
|
2851
|
+
this.auth.redirect(url);
|
|
2852
|
+
},
|
|
2853
|
+
error: () => this.fail(`ไม่สามารถเชื่อมต่อ ${label} ได้ กรุณาเริ่มเข้าสู่ระบบใหม่`)
|
|
2854
|
+
});
|
|
2855
|
+
}
|
|
2856
|
+
toRegister(ticket) {
|
|
2857
|
+
this.auth.setTemp(KC_SSO_TICKET_KEY, ticket);
|
|
2858
|
+
this.message.set("ยังไม่มีบัญชีในระบบ กำลังไปหน้าลงทะเบียน...");
|
|
2859
|
+
this.router.navigateByUrl(KC_AUTH_PATHS.register, { replaceUrl: true });
|
|
2860
|
+
}
|
|
2861
|
+
signIn(session) {
|
|
2862
|
+
this.auth.start(session);
|
|
2863
|
+
this.router.navigateByUrl(this.auth.safeReturnUrl(this.auth.takeTemp(KC_RETURN_URL_KEY)), { replaceUrl: true });
|
|
2864
|
+
}
|
|
2865
|
+
fail(message) {
|
|
2866
|
+
this.message.set(message);
|
|
2867
|
+
this.failed.set(true);
|
|
2868
|
+
}
|
|
2869
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
2870
|
+
minVersion: "12.0.0",
|
|
2871
|
+
version: "22.2.1",
|
|
2872
|
+
ngImport: i0,
|
|
2873
|
+
type: KcExternalCallbackBase,
|
|
2874
|
+
deps: [],
|
|
2875
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
2876
|
+
});
|
|
2877
|
+
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
2878
|
+
minVersion: "14.0.0",
|
|
2879
|
+
version: "22.2.1",
|
|
2880
|
+
type: KcExternalCallbackBase,
|
|
2881
|
+
isStandalone: true,
|
|
2882
|
+
ngImport: i0
|
|
2883
|
+
});
|
|
2884
|
+
};
|
|
2885
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
2886
|
+
minVersion: "12.0.0",
|
|
2887
|
+
version: "22.2.1",
|
|
2888
|
+
ngImport: i0,
|
|
2889
|
+
type: KcExternalCallbackBase,
|
|
2890
|
+
decorators: [{ type: Directive }],
|
|
2891
|
+
ctorParameters: () => []
|
|
2892
|
+
});
|
|
2893
|
+
var KcSsoCallbackComponent = class KcSsoCallbackComponent extends KcExternalCallbackBase {
|
|
2894
|
+
label = this.config.sso?.label ?? "SSO";
|
|
2895
|
+
endpoint = "sso";
|
|
2896
|
+
stateKey = KC_SSO_STATE_KEY;
|
|
2897
|
+
exchange(code) {
|
|
2717
2898
|
this.http.post(`${this.config.apiBase}/user/sso/callback`, { code }).subscribe({
|
|
2718
2899
|
next: (res) => {
|
|
2719
2900
|
if (res.mode === "signed-in" && res.token && res.user) {
|
|
2720
|
-
this.
|
|
2901
|
+
this.signIn({
|
|
2721
2902
|
token: res.token,
|
|
2722
2903
|
user: res.user
|
|
2723
2904
|
});
|
|
2724
|
-
this.router.navigateByUrl(this.auth.safeReturnUrl(this.auth.takeTemp(KC_RETURN_URL_KEY)), { replaceUrl: true });
|
|
2725
2905
|
return;
|
|
2726
2906
|
}
|
|
2727
2907
|
if (res.mode === "register" && res.ticket) {
|
|
2728
|
-
this.
|
|
2729
|
-
|
|
2730
|
-
|
|
2908
|
+
this.toRegister(res.ticket);
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
if (res.mode === "thaid" && this.config.register === "thaid" && this.config.thaid) {
|
|
2912
|
+
const thaid = this.config.thaid.label;
|
|
2913
|
+
this.message.set(`ไม่พบบัญชีผู้ใช้ในระบบ กำลังไปยืนยันตัวตนด้วย ${thaid} เพื่อลงทะเบียน...`);
|
|
2914
|
+
this.startFlow("thaid", KC_THAID_STATE_KEY, thaid);
|
|
2731
2915
|
return;
|
|
2732
2916
|
}
|
|
2733
2917
|
this.fail("ไม่สามารถเข้าสู่ระบบได้ กรุณาลองใหม่อีกครั้ง");
|
|
@@ -2735,16 +2919,12 @@ var KcSsoCallbackComponent = class KcSsoCallbackComponent {
|
|
|
2735
2919
|
error: (err) => this.fail(kcErrorMessage(err, `ไม่สามารถเข้าสู่ระบบผ่าน ${this.label} ได้`))
|
|
2736
2920
|
});
|
|
2737
2921
|
}
|
|
2738
|
-
fail(message) {
|
|
2739
|
-
this.message.set(message);
|
|
2740
|
-
this.failed.set(true);
|
|
2741
|
-
}
|
|
2742
2922
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
2743
2923
|
minVersion: "12.0.0",
|
|
2744
2924
|
version: "22.2.1",
|
|
2745
2925
|
ngImport: i0,
|
|
2746
2926
|
type: KcSsoCallbackComponent,
|
|
2747
|
-
deps:
|
|
2927
|
+
deps: null,
|
|
2748
2928
|
target: i0.ɵɵFactoryTarget.Component
|
|
2749
2929
|
});
|
|
2750
2930
|
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
@@ -2753,20 +2933,9 @@ var KcSsoCallbackComponent = class KcSsoCallbackComponent {
|
|
|
2753
2933
|
type: KcSsoCallbackComponent,
|
|
2754
2934
|
isStandalone: true,
|
|
2755
2935
|
selector: "kc-sso-callback",
|
|
2936
|
+
usesInheritance: true,
|
|
2756
2937
|
ngImport: i0,
|
|
2757
|
-
template:
|
|
2758
|
-
<div class="kc-auth-screen">
|
|
2759
|
-
<div class="kc-auth-card center">
|
|
2760
|
-
<h1 class="kc-auth-title">{{ label }}</h1>
|
|
2761
|
-
@if (failed()) {
|
|
2762
|
-
<div class="kc-auth-alert error" role="alert" style="margin-top: 16px">{{ message() }}</div>
|
|
2763
|
-
<a class="kc-auth-btn" [routerLink]="paths.login">กลับไปหน้าเข้าสู่ระบบ</a>
|
|
2764
|
-
} @else {
|
|
2765
|
-
<p class="kc-auth-subtitle" style="margin-bottom: 0">{{ message() }}</p>
|
|
2766
|
-
}
|
|
2767
|
-
</div>
|
|
2768
|
-
</div>
|
|
2769
|
-
`,
|
|
2938
|
+
template: "\n <div class=\"kc-auth-screen\">\n <div class=\"kc-auth-card center\">\n <h1 class=\"kc-auth-title\">{{ label }}</h1>\n @if (failed()) {\n <div class=\"kc-auth-alert error\" role=\"alert\" style=\"margin-top: 16px\">{{ message() }}</div>\n <a class=\"kc-auth-btn\" [routerLink]=\"paths.login\">กลับไปหน้าเข้าสู่ระบบ</a>\n } @else {\n <p class=\"kc-auth-subtitle\" style=\"margin-bottom: 0\">{{ message() }}</p>\n }\n </div>\n </div>\n",
|
|
2770
2939
|
isInline: true,
|
|
2771
2940
|
dependencies: [{
|
|
2772
2941
|
kind: "directive",
|
|
@@ -2800,22 +2969,83 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2800
2969
|
selector: "kc-sso-callback",
|
|
2801
2970
|
standalone: true,
|
|
2802
2971
|
imports: [RouterLink],
|
|
2803
|
-
template:
|
|
2804
|
-
<div class="kc-auth-screen">
|
|
2805
|
-
<div class="kc-auth-card center">
|
|
2806
|
-
<h1 class="kc-auth-title">{{ label }}</h1>
|
|
2807
|
-
@if (failed()) {
|
|
2808
|
-
<div class="kc-auth-alert error" role="alert" style="margin-top: 16px">{{ message() }}</div>
|
|
2809
|
-
<a class="kc-auth-btn" [routerLink]="paths.login">กลับไปหน้าเข้าสู่ระบบ</a>
|
|
2810
|
-
} @else {
|
|
2811
|
-
<p class="kc-auth-subtitle" style="margin-bottom: 0">{{ message() }}</p>
|
|
2812
|
-
}
|
|
2813
|
-
</div>
|
|
2814
|
-
</div>
|
|
2815
|
-
`
|
|
2972
|
+
template: CALLBACK_TEMPLATE
|
|
2816
2973
|
}]
|
|
2817
|
-
}]
|
|
2818
|
-
|
|
2974
|
+
}]
|
|
2975
|
+
});
|
|
2976
|
+
var KcThaidCallbackComponent = class KcThaidCallbackComponent extends KcExternalCallbackBase {
|
|
2977
|
+
label = this.config.thaid?.label ?? "ThaiD";
|
|
2978
|
+
endpoint = "thaid";
|
|
2979
|
+
stateKey = KC_THAID_STATE_KEY;
|
|
2980
|
+
exchange(code) {
|
|
2981
|
+
this.http.post(`${this.config.apiBase}/user/thaid/callback`, { code }).subscribe({
|
|
2982
|
+
next: (res) => {
|
|
2983
|
+
if (res && "token" in res && res.token && res.user) {
|
|
2984
|
+
this.signIn(res);
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
if (res && "mode" in res && res.mode === "register" && res.ticket && this.config.register === "thaid") {
|
|
2988
|
+
this.toRegister(res.ticket);
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
this.fail("ไม่สามารถเข้าสู่ระบบได้ กรุณาลองใหม่อีกครั้ง");
|
|
2992
|
+
},
|
|
2993
|
+
error: (err) => this.fail(kcErrorMessage(err, `ไม่สามารถเข้าสู่ระบบผ่าน ${this.label} ได้`))
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
2997
|
+
minVersion: "12.0.0",
|
|
2998
|
+
version: "22.2.1",
|
|
2999
|
+
ngImport: i0,
|
|
3000
|
+
type: KcThaidCallbackComponent,
|
|
3001
|
+
deps: null,
|
|
3002
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
3003
|
+
});
|
|
3004
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
3005
|
+
minVersion: "17.0.0",
|
|
3006
|
+
version: "22.2.1",
|
|
3007
|
+
type: KcThaidCallbackComponent,
|
|
3008
|
+
isStandalone: true,
|
|
3009
|
+
selector: "kc-thaid-callback",
|
|
3010
|
+
usesInheritance: true,
|
|
3011
|
+
ngImport: i0,
|
|
3012
|
+
template: "\n <div class=\"kc-auth-screen\">\n <div class=\"kc-auth-card center\">\n <h1 class=\"kc-auth-title\">{{ label }}</h1>\n @if (failed()) {\n <div class=\"kc-auth-alert error\" role=\"alert\" style=\"margin-top: 16px\">{{ message() }}</div>\n <a class=\"kc-auth-btn\" [routerLink]=\"paths.login\">กลับไปหน้าเข้าสู่ระบบ</a>\n } @else {\n <p class=\"kc-auth-subtitle\" style=\"margin-bottom: 0\">{{ message() }}</p>\n }\n </div>\n </div>\n",
|
|
3013
|
+
isInline: true,
|
|
3014
|
+
dependencies: [{
|
|
3015
|
+
kind: "directive",
|
|
3016
|
+
type: RouterLink,
|
|
3017
|
+
selector: "[routerLink]",
|
|
3018
|
+
inputs: [
|
|
3019
|
+
"target",
|
|
3020
|
+
"queryParams",
|
|
3021
|
+
"fragment",
|
|
3022
|
+
"queryParamsHandling",
|
|
3023
|
+
"state",
|
|
3024
|
+
"info",
|
|
3025
|
+
"relativeTo",
|
|
3026
|
+
"preserveFragment",
|
|
3027
|
+
"skipLocationChange",
|
|
3028
|
+
"replaceUrl",
|
|
3029
|
+
"browserUrl",
|
|
3030
|
+
"routerLink"
|
|
3031
|
+
]
|
|
3032
|
+
}]
|
|
3033
|
+
});
|
|
3034
|
+
};
|
|
3035
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
3036
|
+
minVersion: "12.0.0",
|
|
3037
|
+
version: "22.2.1",
|
|
3038
|
+
ngImport: i0,
|
|
3039
|
+
type: KcThaidCallbackComponent,
|
|
3040
|
+
decorators: [{
|
|
3041
|
+
type: Component,
|
|
3042
|
+
args: [{
|
|
3043
|
+
selector: "kc-thaid-callback",
|
|
3044
|
+
standalone: true,
|
|
3045
|
+
imports: [RouterLink],
|
|
3046
|
+
template: CALLBACK_TEMPLATE
|
|
3047
|
+
}]
|
|
3048
|
+
}]
|
|
2819
3049
|
});
|
|
2820
3050
|
function kcAuthRoutes() {
|
|
2821
3051
|
return [
|
|
@@ -2833,6 +3063,11 @@ function kcAuthRoutes() {
|
|
|
2833
3063
|
path: "sso-callback",
|
|
2834
3064
|
component: KcSsoCallbackComponent,
|
|
2835
3065
|
title: "เข้าสู่ระบบ"
|
|
3066
|
+
},
|
|
3067
|
+
{
|
|
3068
|
+
path: "thaid-callback",
|
|
3069
|
+
component: KcThaidCallbackComponent,
|
|
3070
|
+
title: "เข้าสู่ระบบ"
|
|
2836
3071
|
}
|
|
2837
3072
|
];
|
|
2838
3073
|
}
|
|
@@ -2847,10 +3082,12 @@ var KcProfileComponent = class KcProfileComponent {
|
|
|
2847
3082
|
success = signal("", ...ngDevMode ? [{ debugName: "success" }] : /* istanbul ignore next */ []);
|
|
2848
3083
|
fullName = signal("", ...ngDevMode ? [{ debugName: "fullName" }] : /* istanbul ignore next */ []);
|
|
2849
3084
|
orgName = signal("", ...ngDevMode ? [{ debugName: "orgName" }] : /* istanbul ignore next */ []);
|
|
3085
|
+
orgs = signal([], ...ngDevMode ? [{ debugName: "orgs" }] : /* istanbul ignore next */ []);
|
|
2850
3086
|
form = inject(FormBuilder).nonNullable.group({
|
|
2851
3087
|
email: ["", [Validators.required, Validators.email]],
|
|
2852
3088
|
mobile: ["", [Validators.required, Validators.pattern(/^0\d{9}$/)]],
|
|
2853
3089
|
username: ["", [Validators.required, Validators.pattern(/^\S+$/)]],
|
|
3090
|
+
org: [""],
|
|
2854
3091
|
password: [""],
|
|
2855
3092
|
confirmPassword: [""]
|
|
2856
3093
|
});
|
|
@@ -2862,10 +3099,12 @@ var KcProfileComponent = class KcProfileComponent {
|
|
|
2862
3099
|
next: (me) => {
|
|
2863
3100
|
this.fullName.set(me.fullName);
|
|
2864
3101
|
this.orgName.set(me.orgName || me.org);
|
|
3102
|
+
if (this.config.profileOrgChange && me.orgs?.length) this.orgs.set(me.orgs);
|
|
2865
3103
|
this.form.patchValue({
|
|
2866
3104
|
email: me.email ?? "",
|
|
2867
3105
|
mobile: me.mobile ?? "",
|
|
2868
|
-
username: me.username ?? ""
|
|
3106
|
+
username: me.username ?? "",
|
|
3107
|
+
org: me.org ?? ""
|
|
2869
3108
|
});
|
|
2870
3109
|
this.loading.set(false);
|
|
2871
3110
|
},
|
|
@@ -2885,7 +3124,7 @@ var KcProfileComponent = class KcProfileComponent {
|
|
|
2885
3124
|
this.success.set("");
|
|
2886
3125
|
this.form.markAllAsTouched();
|
|
2887
3126
|
const v = this.form.getRawValue();
|
|
2888
|
-
const problem = kcContactProblem(v.email, v.mobile) || kcUsernameProblem(v.username) || kcPasswordProblem(v.password, v.confirmPassword, this.config.passwordMinLength, false);
|
|
3127
|
+
const problem = kcContactProblem(v.email, v.mobile) || kcUsernameProblem(v.username) || kcPasswordProblem(v.password, v.confirmPassword, this.config.passwordMinLength, false) || (this.orgs().length && !v.org ? `กรุณาเลือก${this.config.orgLabel}ที่สังกัด` : "");
|
|
2889
3128
|
this.error.set(problem);
|
|
2890
3129
|
if (problem) return;
|
|
2891
3130
|
const body = {
|
|
@@ -2894,6 +3133,7 @@ var KcProfileComponent = class KcProfileComponent {
|
|
|
2894
3133
|
mobile: v.mobile.trim(),
|
|
2895
3134
|
password: v.password
|
|
2896
3135
|
};
|
|
3136
|
+
if (this.orgs().length) body.org = v.org;
|
|
2897
3137
|
this.saving.set(true);
|
|
2898
3138
|
this.http.put(`${this.config.apiBase}/user/me`, body).subscribe({
|
|
2899
3139
|
next: (res) => {
|
|
@@ -2942,11 +3182,19 @@ var KcProfileComponent = class KcProfileComponent {
|
|
|
2942
3182
|
<input class="kc-auth-input" type="text" [value]="fullName()" disabled />
|
|
2943
3183
|
</label>
|
|
2944
3184
|
|
|
3185
|
+
@if (orgs().length) {
|
|
3186
|
+
<div class="kc-auth-field">
|
|
3187
|
+
<label class="kc-auth-label" for="kc-profile-org">{{ config.orgLabel }}ที่สังกัด <span class="req">*</span></label>
|
|
3188
|
+
<app-search-select inputId="kc-profile-org" [options]="orgs()" formControlName="org"
|
|
3189
|
+
[placeholder]="'-- เลือก' + config.orgLabel + 'ที่สังกัด --'" />
|
|
3190
|
+
</div>
|
|
3191
|
+
} @else {
|
|
2945
3192
|
<label class="kc-auth-field">
|
|
2946
3193
|
<span>{{ config.orgLabel }}ที่สังกัด</span>
|
|
2947
3194
|
<input class="kc-auth-input" type="text" [value]="orgName()" disabled />
|
|
2948
3195
|
<small class="kc-auth-hint">หากต้องการย้าย{{ config.orgLabel }} กรุณาติดต่อผู้ดูแลระบบ</small>
|
|
2949
3196
|
</label>
|
|
3197
|
+
}
|
|
2950
3198
|
|
|
2951
3199
|
<label class="kc-auth-field">
|
|
2952
3200
|
<span>อีเมล <span class="req">*</span></span>
|
|
@@ -3051,6 +3299,17 @@ var KcProfileComponent = class KcProfileComponent {
|
|
|
3051
3299
|
],
|
|
3052
3300
|
outputs: ["ngModelChange"]
|
|
3053
3301
|
},
|
|
3302
|
+
{
|
|
3303
|
+
kind: "component",
|
|
3304
|
+
type: SearchSelectComponent,
|
|
3305
|
+
selector: "app-search-select",
|
|
3306
|
+
inputs: [
|
|
3307
|
+
"options",
|
|
3308
|
+
"placeholder",
|
|
3309
|
+
"searchHint",
|
|
3310
|
+
"inputId"
|
|
3311
|
+
]
|
|
3312
|
+
},
|
|
3054
3313
|
{
|
|
3055
3314
|
kind: "component",
|
|
3056
3315
|
type: KcPasswordToggleComponent,
|
|
@@ -3069,7 +3328,11 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
3069
3328
|
args: [{
|
|
3070
3329
|
selector: "kc-profile",
|
|
3071
3330
|
standalone: true,
|
|
3072
|
-
imports: [
|
|
3331
|
+
imports: [
|
|
3332
|
+
ReactiveFormsModule,
|
|
3333
|
+
SearchSelectComponent,
|
|
3334
|
+
KcPasswordToggleComponent
|
|
3335
|
+
],
|
|
3073
3336
|
template: `
|
|
3074
3337
|
<div class="kc-auth-page single">
|
|
3075
3338
|
<div class="kc-auth-panel">
|
|
@@ -3086,11 +3349,19 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
3086
3349
|
<input class="kc-auth-input" type="text" [value]="fullName()" disabled />
|
|
3087
3350
|
</label>
|
|
3088
3351
|
|
|
3352
|
+
@if (orgs().length) {
|
|
3353
|
+
<div class="kc-auth-field">
|
|
3354
|
+
<label class="kc-auth-label" for="kc-profile-org">{{ config.orgLabel }}ที่สังกัด <span class="req">*</span></label>
|
|
3355
|
+
<app-search-select inputId="kc-profile-org" [options]="orgs()" formControlName="org"
|
|
3356
|
+
[placeholder]="'-- เลือก' + config.orgLabel + 'ที่สังกัด --'" />
|
|
3357
|
+
</div>
|
|
3358
|
+
} @else {
|
|
3089
3359
|
<label class="kc-auth-field">
|
|
3090
3360
|
<span>{{ config.orgLabel }}ที่สังกัด</span>
|
|
3091
3361
|
<input class="kc-auth-input" type="text" [value]="orgName()" disabled />
|
|
3092
3362
|
<small class="kc-auth-hint">หากต้องการย้าย{{ config.orgLabel }} กรุณาติดต่อผู้ดูแลระบบ</small>
|
|
3093
3363
|
</label>
|
|
3364
|
+
}
|
|
3094
3365
|
|
|
3095
3366
|
<label class="kc-auth-field">
|
|
3096
3367
|
<span>อีเมล <span class="req">*</span></span>
|
|
@@ -3163,6 +3434,33 @@ var KcConfigUsersComponent = class KcConfigUsersComponent {
|
|
|
3163
3434
|
search = signal("", ...ngDevMode ? [{ debugName: "search" }] : /* istanbul ignore next */ []);
|
|
3164
3435
|
editing = signal(null, ...ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []);
|
|
3165
3436
|
myId = computed(() => this.auth.user()?.id ?? "", ...ngDevMode ? [{ debugName: "myId" }] : /* istanbul ignore next */ []);
|
|
3437
|
+
methodsSaved = signal(null, ...ngDevMode ? [{ debugName: "methodsSaved" }] : /* istanbul ignore next */ []);
|
|
3438
|
+
methodsDraft = signal(null, ...ngDevMode ? [{ debugName: "methodsDraft" }] : /* istanbul ignore next */ []);
|
|
3439
|
+
savingMethods = signal(false, ...ngDevMode ? [{ debugName: "savingMethods" }] : /* istanbul ignore next */ []);
|
|
3440
|
+
methodsError = signal("", ...ngDevMode ? [{ debugName: "methodsError" }] : /* istanbul ignore next */ []);
|
|
3441
|
+
methodOptions = computed(() => [
|
|
3442
|
+
...this.config.passwordLogin ? [{
|
|
3443
|
+
key: "password",
|
|
3444
|
+
label: "ชื่อผู้ใช้ / รหัสผ่าน"
|
|
3445
|
+
}] : [],
|
|
3446
|
+
...this.config.sso ? [{
|
|
3447
|
+
key: "sso",
|
|
3448
|
+
label: this.config.sso.label
|
|
3449
|
+
}] : [],
|
|
3450
|
+
...this.config.thaid ? [{
|
|
3451
|
+
key: "thaid",
|
|
3452
|
+
label: this.config.thaid.label
|
|
3453
|
+
}] : []
|
|
3454
|
+
], ...ngDevMode ? [{ debugName: "methodOptions" }] : /* istanbul ignore next */ []);
|
|
3455
|
+
enabledCount = computed(() => {
|
|
3456
|
+
const m = this.methodsDraft();
|
|
3457
|
+
return m ? this.methodOptions().filter((o) => m[o.key]).length : 0;
|
|
3458
|
+
}, ...ngDevMode ? [{ debugName: "enabledCount" }] : /* istanbul ignore next */ []);
|
|
3459
|
+
methodsDirty = computed(() => {
|
|
3460
|
+
const a = this.methodsDraft();
|
|
3461
|
+
const b = this.methodsSaved();
|
|
3462
|
+
return !!a && !!b && (a.password !== b.password || a.sso !== b.sso || a.thaid !== b.thaid);
|
|
3463
|
+
}, ...ngDevMode ? [{ debugName: "methodsDirty" }] : /* istanbul ignore next */ []);
|
|
3166
3464
|
orgOptions = computed(() => [{
|
|
3167
3465
|
value: "",
|
|
3168
3466
|
label: "-- ไม่ระบุ --"
|
|
@@ -3188,6 +3486,51 @@ var KcConfigUsersComponent = class KcConfigUsersComponent {
|
|
|
3188
3486
|
}
|
|
3189
3487
|
ngOnInit() {
|
|
3190
3488
|
this.load();
|
|
3489
|
+
this.http.get(`${this.config.apiBase}/user/login-methods`).subscribe({
|
|
3490
|
+
next: (m) => {
|
|
3491
|
+
const v = {
|
|
3492
|
+
password: m?.password !== false,
|
|
3493
|
+
sso: m?.sso !== false,
|
|
3494
|
+
thaid: m?.thaid !== false
|
|
3495
|
+
};
|
|
3496
|
+
this.methodsSaved.set(v);
|
|
3497
|
+
this.methodsDraft.set({ ...v });
|
|
3498
|
+
},
|
|
3499
|
+
error: () => {}
|
|
3500
|
+
});
|
|
3501
|
+
}
|
|
3502
|
+
toggleMethod(key, on) {
|
|
3503
|
+
const m = this.methodsDraft();
|
|
3504
|
+
if (!m) return;
|
|
3505
|
+
const next = {
|
|
3506
|
+
...m,
|
|
3507
|
+
[key]: on
|
|
3508
|
+
};
|
|
3509
|
+
if (!this.methodOptions().some((o) => next[o.key])) return;
|
|
3510
|
+
this.methodsError.set("");
|
|
3511
|
+
this.methodsDraft.set(next);
|
|
3512
|
+
}
|
|
3513
|
+
saveMethods() {
|
|
3514
|
+
const m = this.methodsDraft();
|
|
3515
|
+
if (!m || this.savingMethods()) return;
|
|
3516
|
+
if (!this.methodOptions().some((o) => m[o.key])) {
|
|
3517
|
+
this.methodsError.set("ต้องเปิดการเข้าสู่ระบบอย่างน้อย 1 วิธี");
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
this.savingMethods.set(true);
|
|
3521
|
+
this.http.put(`${this.config.apiBase}/config/login-methods`, m).subscribe({
|
|
3522
|
+
next: (res) => {
|
|
3523
|
+
this.savingMethods.set(false);
|
|
3524
|
+
this.methodsSaved.set({ ...m });
|
|
3525
|
+
this.methodsError.set("");
|
|
3526
|
+
this.error.set("");
|
|
3527
|
+
this.success.set(res?.message || "บันทึกวิธีเข้าสู่ระบบเรียบร้อยแล้ว");
|
|
3528
|
+
},
|
|
3529
|
+
error: (err) => {
|
|
3530
|
+
this.savingMethods.set(false);
|
|
3531
|
+
this.methodsError.set(kcErrorMessage(err, "บันทึกวิธีเข้าสู่ระบบไม่สำเร็จ"));
|
|
3532
|
+
}
|
|
3533
|
+
});
|
|
3191
3534
|
}
|
|
3192
3535
|
load() {
|
|
3193
3536
|
this.loading.set(true);
|
|
@@ -3286,6 +3629,30 @@ var KcConfigUsersComponent = class KcConfigUsersComponent {
|
|
|
3286
3629
|
<div class="kc-auth-alert success" role="status">{{ success() }}</div>
|
|
3287
3630
|
}
|
|
3288
3631
|
|
|
3632
|
+
@if (methodOptions().length > 1 && methodsDraft(); as m) {
|
|
3633
|
+
<div class="kc-auth-panel">
|
|
3634
|
+
<h2 class="kc-auth-panel-title">วิธีเข้าสู่ระบบ</h2>
|
|
3635
|
+
<div class="kc-auth-methods">
|
|
3636
|
+
@for (o of methodOptions(); track o.key) {
|
|
3637
|
+
<label class="kc-auth-check">
|
|
3638
|
+
<input type="checkbox" [checked]="m[o.key]" [disabled]="savingMethods() || (m[o.key] && enabledCount() === 1)"
|
|
3639
|
+
(change)="toggleMethod(o.key, $any($event.target).checked)" />
|
|
3640
|
+
{{ o.label }}
|
|
3641
|
+
</label>
|
|
3642
|
+
}
|
|
3643
|
+
<button type="button" class="kc-auth-btn primary" [disabled]="savingMethods() || !methodsDirty()" (click)="saveMethods()">
|
|
3644
|
+
{{ savingMethods() ? 'กำลังบันทึก...' : 'บันทึก' }}
|
|
3645
|
+
</button>
|
|
3646
|
+
</div>
|
|
3647
|
+
@if (methodsError()) {
|
|
3648
|
+
<div class="kc-auth-alert error" role="alert" style="margin: 10px 0 0">{{ methodsError() }}</div>
|
|
3649
|
+
}
|
|
3650
|
+
<p class="kc-auth-hint" style="margin: 10px 0 0">
|
|
3651
|
+
ต้องเปิดอย่างน้อย 1 วิธี — ก่อนปิดวิธีอื่น ให้ลองเข้าสู่ระบบด้วยวิธีที่จะเหลือให้ได้ก่อน มีผลกับการเข้าสู่ระบบครั้งถัดไป (ผู้ที่เข้าอยู่แล้วใช้งานต่อได้)
|
|
3652
|
+
</p>
|
|
3653
|
+
</div>
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3289
3656
|
<div class="kc-auth-panel">
|
|
3290
3657
|
<input class="kc-auth-input" type="search" [placeholder]="'ค้นหาชื่อผู้ใช้ ชื่อ-สกุล ' + config.orgLabel + ' ระดับผู้ใช้ หรืออีเมล'"
|
|
3291
3658
|
[ngModel]="search()" (ngModelChange)="search.set($event)" aria-label="ค้นหาผู้ใช้" />
|
|
@@ -3499,6 +3866,30 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
3499
3866
|
<div class="kc-auth-alert success" role="status">{{ success() }}</div>
|
|
3500
3867
|
}
|
|
3501
3868
|
|
|
3869
|
+
@if (methodOptions().length > 1 && methodsDraft(); as m) {
|
|
3870
|
+
<div class="kc-auth-panel">
|
|
3871
|
+
<h2 class="kc-auth-panel-title">วิธีเข้าสู่ระบบ</h2>
|
|
3872
|
+
<div class="kc-auth-methods">
|
|
3873
|
+
@for (o of methodOptions(); track o.key) {
|
|
3874
|
+
<label class="kc-auth-check">
|
|
3875
|
+
<input type="checkbox" [checked]="m[o.key]" [disabled]="savingMethods() || (m[o.key] && enabledCount() === 1)"
|
|
3876
|
+
(change)="toggleMethod(o.key, $any($event.target).checked)" />
|
|
3877
|
+
{{ o.label }}
|
|
3878
|
+
</label>
|
|
3879
|
+
}
|
|
3880
|
+
<button type="button" class="kc-auth-btn primary" [disabled]="savingMethods() || !methodsDirty()" (click)="saveMethods()">
|
|
3881
|
+
{{ savingMethods() ? 'กำลังบันทึก...' : 'บันทึก' }}
|
|
3882
|
+
</button>
|
|
3883
|
+
</div>
|
|
3884
|
+
@if (methodsError()) {
|
|
3885
|
+
<div class="kc-auth-alert error" role="alert" style="margin: 10px 0 0">{{ methodsError() }}</div>
|
|
3886
|
+
}
|
|
3887
|
+
<p class="kc-auth-hint" style="margin: 10px 0 0">
|
|
3888
|
+
ต้องเปิดอย่างน้อย 1 วิธี — ก่อนปิดวิธีอื่น ให้ลองเข้าสู่ระบบด้วยวิธีที่จะเหลือให้ได้ก่อน มีผลกับการเข้าสู่ระบบครั้งถัดไป (ผู้ที่เข้าอยู่แล้วใช้งานต่อได้)
|
|
3889
|
+
</p>
|
|
3890
|
+
</div>
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3502
3893
|
<div class="kc-auth-panel">
|
|
3503
3894
|
<input class="kc-auth-input" type="search" [placeholder]="'ค้นหาชื่อผู้ใช้ ชื่อ-สกุล ' + config.orgLabel + ' ระดับผู้ใช้ หรืออีเมล'"
|
|
3504
3895
|
[ngModel]="search()" (ngModelChange)="search.set($event)" aria-label="ค้นหาผู้ใช้" />
|
|
@@ -4115,6 +4506,6 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
4115
4506
|
}],
|
|
4116
4507
|
ctorParameters: () => []
|
|
4117
4508
|
});
|
|
4118
|
-
export { AnchoredPopup, ClockPickerDirective, ClockPickerPopupComponent, DatePickerDirective, DatePickerPopupComponent, KC_AUTH_CONFIG, KC_AUTH_PATHS, KC_RETURN_URL_KEY, KC_SSO_STATE_KEY, KC_SSO_TICKET_KEY, KcAuthService, KcConfigKclluComponent, KcConfigUsersComponent, KcLoginComponent, KcPasswordToggleComponent, KcProfileComponent, KcRegisterComponent, KcSsoCallbackComponent, PICKER_CSS, SearchSelectComponent, ThaiDatePickerComponent, ensurePickerStyles, fixThaiText, formatDate, kcAdminGuard, kcAuthGuard, kcAuthInterceptor, kcAuthRoutes, kcErrorMessage, kcTokenExpired, parseDate, provideKcAuth, provideThaiPasteFix, resolveKcAuthConfig, toBuddhistText, toChristianText };
|
|
4509
|
+
export { AnchoredPopup, ClockPickerDirective, ClockPickerPopupComponent, DatePickerDirective, DatePickerPopupComponent, KC_AUTH_CONFIG, KC_AUTH_PATHS, KC_LOGIN_LOCK_KEY, KC_LOGIN_LOCK_SECONDS, KC_RETURN_URL_KEY, KC_SSO_STATE_KEY, KC_SSO_TICKET_KEY, KC_THAID_STATE_KEY, KcAuthService, KcConfigKclluComponent, KcConfigUsersComponent, KcLoginComponent, KcPasswordToggleComponent, KcProfileComponent, KcRegisterComponent, KcSsoCallbackComponent, KcThaidCallbackComponent, PICKER_CSS, SearchSelectComponent, ThaiDatePickerComponent, ensurePickerStyles, fixThaiText, formatDate, kcAdminGuard, kcAuthGuard, kcAuthInterceptor, kcAuthRoutes, kcErrorMessage, kcLoginRetryAfter, kcTokenExpired, parseDate, provideKcAuth, provideThaiPasteFix, resolveKcAuthConfig, toBuddhistText, toChristianText };
|
|
4119
4510
|
|
|
4120
4511
|
//# sourceMappingURL=kengcom202-kcore-ui.mjs.map
|