@kengcom202/kcore-ui 0.4.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kengcom202/kcore-ui",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Angular UI กลาง: ช่องเลือกวันที่แบบ พ.ศ., ช่องเลือกเวลาแบบหน้าปัด, dropdown ค้นหาได้, ตัวแก้ข้อความไทยที่คัดลอกจาก PDF และชุดหน้าเข้าสู่ระบบ/ลงทะเบียน/ข้อมูลส่วนตัว/config (ไม่พึ่งไลบรารีภายนอก)",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -1,8 +1,8 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { AfterContentInit, ApplicationRef, ElementRef, EnvironmentInjector, EnvironmentProviders, InjectionToken, NgZone, OnDestroy, OnInit, Type } from "@angular/core";
3
3
  import { ControlValueAccessor } from "@angular/forms";
4
- import { HttpInterceptorFn } from "@angular/common/http";
5
- import { CanActivateFn, Routes } from "@angular/router";
4
+ import { HttpClient, HttpInterceptorFn } from "@angular/common/http";
5
+ import { CanActivateFn, Router, Routes } from "@angular/router";
6
6
  /**
7
7
  * ช่องเลือกวันที่แบบไทย ใช้กับ <input type="text" appDatePicker formControlName="...">
8
8
  * - ค่าใน form control เป็น ค.ศ. yyyy-MM-dd เสมอ (ตรงกับที่ API ใช้) ส่วนในช่องแสดงเป็น พ.ศ.
@@ -339,8 +339,15 @@ export interface KcAuthOptions {
339
339
  label?: string;
340
340
  };
341
341
  /**
342
- * เข้าสู่ระบบด้วยชื่อผู้ใช้/รหัสผ่าน ค่าเริ่มต้น true — false = หน้าเข้าสู่ระบบมีแต่ปุ่ม SSO ตรงกลาง
343
- * (มีผลเฉพาะเมื่อเปิด sso ไว้ ถ้าปิดทั้งคู่จะยังแสดงช่องรหัสผ่าน กันไม่ให้ไม่มีทางเข้าระบบ)
342
+ * เปิดปุ่ม ThaiD (แอปยืนยันตัวตนของกรมการปกครอง) — เข้าสู่ระบบอย่างเดียว ไม่ใช้ลงทะเบียน
343
+ * API จับคู่เลขบัตรประชาชนกับบัญชีที่มีอยู่แล้ว ไม่พบ = แจ้งให้ติดต่อผู้ดูแล; label ค่าเริ่มต้น 'ThaiD'; false/ไม่ใส่ = ปิด
344
+ */
345
+ thaid?: false | {
346
+ label?: string;
347
+ };
348
+ /**
349
+ * เข้าสู่ระบบด้วยชื่อผู้ใช้/รหัสผ่าน ค่าเริ่มต้น true — false = หน้าเข้าสู่ระบบมีแต่ปุ่ม SSO / ThaiD ตรงกลาง
350
+ * (มีผลเฉพาะเมื่อเปิด sso หรือ thaid ไว้ ถ้าปิดหมดจะยังแสดงช่องรหัสผ่าน กันไม่ให้ไม่มีทางเข้าระบบ)
344
351
  */
345
352
  passwordLogin?: boolean;
346
353
  /**
@@ -368,6 +375,9 @@ export interface KcAuthConfig {
368
375
  sso: {
369
376
  label: string;
370
377
  } | null;
378
+ thaid: {
379
+ label: string;
380
+ } | null;
371
381
  passwordLogin: boolean;
372
382
  register: 'none' | 'form' | 'sso';
373
383
  orgLabel: string;
@@ -381,13 +391,14 @@ export declare const KC_AUTH_PATHS: {
381
391
  readonly login: "/login";
382
392
  readonly register: "/register";
383
393
  readonly ssoCallback: "/sso-callback";
394
+ readonly thaidCallback: "/thaid-callback";
384
395
  };
385
396
  export declare function resolveKcAuthConfig(o: KcAuthOptions): KcAuthConfig;
386
397
  /**
387
398
  * ตั้งค่าชุดหน้าบัญชีผู้ใช้ — ใส่ใน app.config.ts ครั้งเดียว คู่กับ kcAuthInterceptor
388
399
  * providers: [
389
400
  * provideHttpClient(withInterceptors([kcAuthInterceptor])),
390
- * provideKcAuth({ appName: 'ระบบ...', sso: { label: 'DMS SSO' }, register: 'sso' }),
401
+ * provideKcAuth({ appName: 'ระบบ...', sso: { label: 'DMS SSO' }, thaid: {}, register: 'sso' }),
391
402
  * ]
392
403
  */
393
404
  export declare function provideKcAuth(options: KcAuthOptions): EnvironmentProviders;
@@ -405,11 +416,32 @@ export interface KcSession {
405
416
  token: string;
406
417
  user: KcUser;
407
418
  }
408
- /** GET {api}/user/sso/authorize-url */
419
+ /**
420
+ * POST {api}/user/login ตอบ 429 เมื่อรหัสผ่านผิดครบ 3 ครั้ง (นับต่อชื่อผู้ใช้) — ต้องรอ retryAfter วินาที (5 นาทีนับจากครั้งที่ 3)
421
+ * ระหว่างล็อก API ไม่ตรวจรหัสผ่าน (ถูกก็ตอบ 429) และเข้าสู่ระบบสำเร็จ = ล้างตัวนับ
422
+ */
423
+ export interface KcLoginLocked {
424
+ message: string;
425
+ retryAfter: number;
426
+ }
427
+ /** ไม่มี retryAfter ใน 429 = ถือว่ารอ 5 นาที */
428
+ export declare const KC_LOGIN_LOCK_SECONDS = 300;
429
+ /** วินาทีที่ต้องรอจาก 429 ของ user/login — คำตอบอื่น = 0 */
430
+ export declare function kcLoginRetryAfter(err: unknown): number;
431
+ /** GET {api}/user/sso/authorize-url และ GET {api}/user/thaid/authorize-url */
409
432
  export interface KcSsoAuthorize {
410
433
  url: string;
411
434
  state: string;
412
435
  }
436
+ /**
437
+ * GET {api}/user/login-methods (ไม่ต้องเข้าสู่ระบบ) และ PUT {api}/config/login-methods (ผู้ดูแล)
438
+ * วิธีเข้าสู่ระบบที่ผู้ดูแลเปิดไว้ — ต้องเปิดอย่างน้อย 1 วิธี (API ต้องปฏิเสธเมื่อปิดหมด)
439
+ */
440
+ export interface KcLoginMethods {
441
+ password: boolean;
442
+ sso: boolean;
443
+ thaid: boolean;
444
+ }
413
445
  /** POST {api}/user/sso/callback { code } */
414
446
  export type KcSsoCallbackResult = ({
415
447
  mode: 'signed-in';
@@ -518,6 +550,7 @@ export interface KcMessage {
518
550
  export declare function kcErrorMessage(err: unknown, fallback: string): string;
519
551
  export declare const KC_SSO_STATE_KEY = "kc_sso_state";
520
552
  export declare const KC_SSO_TICKET_KEY = "kc_sso_ticket";
553
+ export declare const KC_THAID_STATE_KEY = "kc_thaid_state";
521
554
  export declare const KC_RETURN_URL_KEY = "kc_return_url";
522
555
  /** token เป็น JWT ที่มี exp และเลยเวลาแล้ว — token รูปแบบอื่นถือว่ายังไม่หมดอายุ (ให้ API ตัดสินด้วย 401) */
523
556
  export declare function kcTokenExpired(token: string, now?: number): boolean;
@@ -578,7 +611,7 @@ export declare const kcAuthInterceptor: HttpInterceptorFn;
578
611
  * { path: 'config/kcllu', component: KcConfigKclluComponent, canActivate: [kcAdminGuard] },
579
612
  * ] },
580
613
  * ];
581
- * path ตรงกับ KC_AUTH_PATHS (login, register, sso-callback)
614
+ * path ตรงกับ KC_AUTH_PATHS (login, register, sso-callback, thaid-callback)
582
615
  */
583
616
  export declare function kcAuthRoutes(): Routes;
584
617
  /**
@@ -596,10 +629,14 @@ export declare class KcPasswordToggleComponent implements AfterContentInit {
596
629
  static ɵfac: i0.ɵɵFactoryDeclaration<KcPasswordToggleComponent, never>;
597
630
  static ɵcmp: i0.ɵɵComponentDeclaration<KcPasswordToggleComponent, "kc-password", never, {}, {}, never, ["*"], true, never>;
598
631
  }
632
+ /** เวลาปลดล็อกของแต่ละชื่อผู้ใช้หลังได้ 429 จาก user/login */
633
+ export declare const KC_LOGIN_LOCK_KEY = "kc_login_lock";
599
634
  /**
600
635
  * หน้าเข้าสู่ระบบ (route /login จาก kcAuthRoutes())
601
- * ชื่อผู้ใช้/รหัสผ่าน → POST {api}/user/login → KcSession
636
+ * ชื่อผู้ใช้/รหัสผ่าน → POST {api}/user/login → KcSession (ผิดครบ 3 ครั้ง API ตอบ 429 → นับถอยหลังตาม retryAfter และปิดปุ่ม)
602
637
  * ปุ่ม SSO (ถ้าเปิด) → GET {api}/user/sso/authorize-url → ไปหน้า SSO แล้วกลับมาที่ /sso-callback
638
+ * ปุ่ม ThaiD (ถ้าเปิด) → GET {api}/user/thaid/authorize-url → ไปหน้า ThaiD แล้วกลับมาที่ /thaid-callback (ไม่มีลงทะเบียน)
639
+ * วิธีที่แสดงตาม GET {api}/user/login-methods (ผู้ดูแลเปิด/ปิดที่หน้า config ผู้ใช้) — ไม่มี endpoint = ตาม provideKcAuth
603
640
  * query: returnUrl = หน้าที่จะกลับไปหลังเข้าสู่ระบบ, expired=1 = แจ้งหมดเวลาการใช้งาน
604
641
  * เนื้อหาที่ใส่ไว้ใน <kc-login>...</kc-login> แสดงท้ายการ์ด (เช่นเบอร์ติดต่อผู้ดูแล)
605
642
  */
@@ -609,12 +646,31 @@ export declare class KcLoginComponent implements OnInit {
609
646
  readonly login: "/login";
610
647
  readonly register: "/register";
611
648
  readonly ssoCallback: "/sso-callback";
649
+ readonly thaidCallback: "/thaid-callback";
612
650
  };
613
651
  private readonly auth;
614
652
  private readonly http;
615
653
  private readonly router;
616
654
  private readonly route;
617
- protected readonly busy: import("@angular/core").WritableSignal<"" | "sso" | "password">;
655
+ private readonly doc;
656
+ protected readonly busy: import("@angular/core").WritableSignal<"" | "sso" | "password" | "thaid">;
657
+ /**
658
+ * วิธีที่ผู้ดูแลเปิดไว้ (GET {api}/user/login-methods) — ก่อนได้คำตอบ หรือ API ยังไม่มี endpoint นี้ ใช้ค่าจาก provideKcAuth
659
+ * แต่ละวิธีแสดงเมื่อทั้ง provideKcAuth เปิดไว้ (passwordLogin / sso / thaid) และผู้ดูแลเปิด
660
+ */
661
+ protected readonly methods: import("@angular/core").WritableSignal<KcLoginMethods>;
662
+ /** ปุ่มเข้าสู่ระบบภายนอกที่เปิดไว้ เรียง SSO ก่อน ThaiD */
663
+ protected readonly external: import("@angular/core").Signal<({
664
+ key: "sso";
665
+ label: string;
666
+ stateKey: string;
667
+ } | {
668
+ key: "thaid";
669
+ label: string;
670
+ stateKey: string;
671
+ })[]>;
672
+ /** ไม่เหลือปุ่มภายนอกเลย = แสดงช่องรหัสผ่านเสมอ กันไม่ให้ไม่มีทางเข้าระบบ */
673
+ protected readonly showPassword: import("@angular/core").Signal<boolean>;
618
674
  protected readonly message: import("@angular/core").WritableSignal<{
619
675
  text: string;
620
676
  error: boolean;
@@ -623,11 +679,27 @@ export declare class KcLoginComponent implements OnInit {
623
679
  username: import("@angular/forms").FormControl<string>;
624
680
  password: import("@angular/forms").FormControl<string>;
625
681
  }>;
682
+ /**
683
+ * ชื่อผู้ใช้ที่ถูกล็อก → เวลาปลดล็อก (ms) — เก็บใน localStorage ให้รีเฟรชแล้วยังนับต่อ
684
+ * API เป็นผู้ล็อกจริง ส่วนนี้แค่แสดงเวลานับถอยหลังและกันกดซ้ำ — พิมพ์ชื่อผู้ใช้อื่นกดได้ตามปกติ
685
+ */
686
+ private readonly locks;
687
+ private readonly now;
688
+ private readonly username;
689
+ /** วินาทีที่ยังต้องรอของชื่อผู้ใช้ในช่อง (0 = ไม่ล็อก) */
690
+ protected readonly lockLeft: import("@angular/core").Signal<number>;
691
+ protected readonly lockText: import("@angular/core").Signal<string>;
692
+ private timer;
626
693
  constructor();
627
694
  private get returnUrl();
628
695
  ngOnInit(): void;
629
696
  protected submit(): void;
630
- protected startSso(): void;
697
+ /** until = 0 คือปลดล็อก */
698
+ private setLock;
699
+ private startTimer;
700
+ private stopTimer;
701
+ private storage;
702
+ protected startExternal(key: 'sso' | 'thaid'): void;
631
703
  static ɵfac: i0.ɵɵFactoryDeclaration<KcLoginComponent, never>;
632
704
  static ɵcmp: i0.ɵɵComponentDeclaration<KcLoginComponent, "kc-login", never, {}, {}, never, ["*"], true, never>;
633
705
  }
@@ -646,6 +718,7 @@ export declare class KcRegisterComponent implements OnInit {
646
718
  readonly login: "/login";
647
719
  readonly register: "/register";
648
720
  readonly ssoCallback: "/sso-callback";
721
+ readonly thaidCallback: "/thaid-callback";
649
722
  };
650
723
  protected readonly ssoLabel: string;
651
724
  private readonly auth;
@@ -679,34 +752,63 @@ export declare class KcRegisterComponent implements OnInit {
679
752
  static ɵfac: i0.ɵɵFactoryDeclaration<KcRegisterComponent, never>;
680
753
  static ɵcmp: i0.ɵɵComponentDeclaration<KcRegisterComponent, "kc-register", never, {}, {}, never, never, true, never>;
681
754
  }
682
- /**
683
- * หน้ารับ redirect กลับจาก SSO (route /sso-callback) — path นี้ต้องตรงกับ redirect_uri ที่ API ส่งให้ SSO
684
- * และที่ลงทะเบียนไว้กับ SSO server
685
- * ?code=&state= → ตรวจ state → POST {api}/user/sso/callback { code }
686
- * signed-in → เริ่ม session แล้วกลับหน้าเดิม / homeUrl
687
- * register → เก็บตั๋วไว้ใน sessionStorage แล้วไป /register
688
- * ไม่มี code (เช่นเปิดจากเมนูของ SSO portal) → เริ่ม flow ใหม่เอง
689
- */
690
- export declare class KcSsoCallbackComponent implements OnInit {
691
- private readonly config;
692
- private readonly auth;
693
- private readonly http;
755
+ /** ส่วนที่หน้า callback ของ SSO และ ThaiD ใช้ร่วมกัน — ต่างกันที่ endpoint, คีย์ state และผลของ callback */
756
+ declare abstract class KcExternalCallbackBase implements OnInit {
757
+ protected readonly config: import("@kengcom202/kcore-ui").KcAuthConfig;
758
+ protected readonly auth: KcAuthService;
759
+ protected readonly http: HttpClient;
760
+ protected readonly router: Router;
694
761
  private readonly route;
695
- private readonly router;
696
762
  protected readonly paths: {
697
763
  readonly login: "/login";
698
764
  readonly register: "/register";
699
765
  readonly ssoCallback: "/sso-callback";
766
+ readonly thaidCallback: "/thaid-callback";
700
767
  };
701
- protected readonly label: string;
768
+ protected abstract readonly label: string;
769
+ /** path ใต้ {api}/user/ เช่น 'sso' → {api}/user/sso/authorize-url, {api}/user/sso/callback */
770
+ protected abstract readonly endpoint: string;
771
+ protected abstract readonly stateKey: string;
702
772
  protected readonly message: import("@angular/core").WritableSignal<string>;
703
773
  protected readonly failed: import("@angular/core").WritableSignal<boolean>;
704
774
  constructor();
775
+ /** แลก code แล้วจัดการผล */
776
+ protected abstract exchange(code: string): void;
705
777
  ngOnInit(): void;
706
- private fail;
778
+ protected signIn(session: KcSession): void;
779
+ protected fail(message: string): void;
780
+ static ɵfac: i0.ɵɵFactoryDeclaration<KcExternalCallbackBase, never>;
781
+ static ɵdir: i0.ɵɵDirectiveDeclaration<KcExternalCallbackBase, never, never, {}, {}, never, never, true, never>;
782
+ }
783
+ /**
784
+ * หน้ารับ redirect กลับจาก SSO (route /sso-callback) — path นี้ต้องตรงกับ redirect_uri ที่ API ส่งให้ SSO
785
+ * และที่ลงทะเบียนไว้กับ SSO server
786
+ * ?code=&state= → ตรวจ state → POST {api}/user/sso/callback { code }
787
+ * signed-in → เริ่ม session แล้วกลับหน้าเดิม / homeUrl
788
+ * register → เก็บตั๋วไว้ใน sessionStorage แล้วไป /register
789
+ * ไม่มี code (เช่นเปิดจากเมนูของ SSO portal) → เริ่ม flow ใหม่เอง
790
+ */
791
+ export declare class KcSsoCallbackComponent extends KcExternalCallbackBase {
792
+ protected readonly label: string;
793
+ protected readonly endpoint = "sso";
794
+ protected readonly stateKey = "kc_sso_state";
795
+ protected exchange(code: string): void;
707
796
  static ɵfac: i0.ɵɵFactoryDeclaration<KcSsoCallbackComponent, never>;
708
797
  static ɵcmp: i0.ɵɵComponentDeclaration<KcSsoCallbackComponent, "kc-sso-callback", never, {}, {}, never, never, true, never>;
709
798
  }
799
+ /**
800
+ * หน้ารับ redirect กลับจาก ThaiD (route /thaid-callback) — เข้าสู่ระบบอย่างเดียว ไม่มีการลงทะเบียน
801
+ * ?code=&state= → ตรวจ state → POST {api}/user/thaid/callback { code } → KcSession
802
+ * API ไม่พบบัญชีที่ผูกกับเลขบัตรประชาชน = ตอบ 403 { message } แสดงข้อความนั้น (ให้ติดต่อผู้ดูแล)
803
+ */
804
+ export declare class KcThaidCallbackComponent extends KcExternalCallbackBase {
805
+ protected readonly label: string;
806
+ protected readonly endpoint = "thaid";
807
+ protected readonly stateKey = "kc_thaid_state";
808
+ protected exchange(code: string): void;
809
+ static ɵfac: i0.ɵɵFactoryDeclaration<KcThaidCallbackComponent, never>;
810
+ static ɵcmp: i0.ɵɵComponentDeclaration<KcThaidCallbackComponent, "kc-thaid-callback", never, {}, {}, never, never, true, never>;
811
+ }
710
812
  /**
711
813
  * หน้า "บัญชีของฉัน" (user/detail) — รูปแบบตาม ictprocure: กล่องเดียวกลางหน้า ช่องเรียงแถวเดียว
712
814
  * วางใน layout ของโปรเจกต์พร้อม canActivate: [kcAuthGuard]
@@ -751,6 +853,8 @@ interface UserEdit extends KcConfigUserUpdate {
751
853
  * ค้นหา แล้วคลิกแถวเพื่อแก้ ชื่อผู้ใช้ / ระดับผู้ใช้ / สถานะ / หน่วยงาน / ตั้งรหัสผ่านใหม่
752
854
  * ไม่มีการลบ — ใช้ "ระงับการใช้งาน" แทน และแก้ระดับ/สถานะของตัวเองไม่ได้ (กันผู้ดูแลล็อกตัวเองออก)
753
855
  * GET {api}/config/users → { users, orgs, roles }, PUT {api}/config/users/{id} ← KcConfigUserUpdate
856
+ * ส่วนบน: เปิด/ปิดวิธีเข้าสู่ระบบ (รหัสผ่าน / SSO / ThaiD ที่ provideKcAuth เปิดไว้) ห้ามปิดหมด
857
+ * GET {api}/user/login-methods → KcLoginMethods, PUT {api}/config/login-methods ← KcLoginMethods (ไม่มี endpoint = ซ่อนส่วนนี้)
754
858
  */
755
859
  export declare class KcConfigUsersComponent implements OnInit {
756
860
  protected readonly config: import("@kengcom202/kcore-ui").KcAuthConfig;
@@ -767,11 +871,31 @@ export declare class KcConfigUsersComponent implements OnInit {
767
871
  protected readonly search: import("@angular/core").WritableSignal<string>;
768
872
  protected readonly editing: import("@angular/core").WritableSignal<UserEdit | null>;
769
873
  protected readonly myId: import("@angular/core").Signal<string>;
874
+ /** วิธีเข้าสู่ระบบ: ค่าที่บันทึกแล้ว / ค่าที่กำลังแก้ (null = API ไม่มี endpoint หรือโหลดไม่ได้ → ซ่อนส่วนนี้) */
875
+ private readonly methodsSaved;
876
+ protected readonly methodsDraft: import("@angular/core").WritableSignal<KcLoginMethods | null>;
877
+ protected readonly savingMethods: import("@angular/core").WritableSignal<boolean>;
878
+ protected readonly methodsError: import("@angular/core").WritableSignal<string>;
879
+ /** เฉพาะวิธีที่ provideKcAuth เปิดไว้ — วิธีที่หน้าเว็บไม่มีปุ่มให้ไม่นับ */
880
+ protected readonly methodOptions: import("@angular/core").Signal<({
881
+ key: "password";
882
+ label: string;
883
+ } | {
884
+ key: "sso";
885
+ label: string;
886
+ } | {
887
+ key: "thaid";
888
+ label: string;
889
+ })[]>;
890
+ protected readonly enabledCount: import("@angular/core").Signal<number>;
891
+ protected readonly methodsDirty: import("@angular/core").Signal<boolean>;
770
892
  /** ตัวเลือกหน่วยงานในป็อปอัป — มี "ไม่ระบุ" ไว้ล้างค่า */
771
893
  protected readonly orgOptions: import("@angular/core").Signal<SearchOption[]>;
772
894
  protected readonly filtered: import("@angular/core").Signal<KcConfigUser[]>;
773
895
  constructor();
774
896
  ngOnInit(): void;
897
+ protected toggleMethod(key: keyof KcLoginMethods, on: boolean): void;
898
+ protected saveMethods(): void;
775
899
  protected load(): void;
776
900
  protected roleName(role: string): string;
777
901
  protected hasRole(role: string): boolean;