@leadertechie/personal-site-kit 0.1.0-alpha.5 → 0.1.0-alpha.6

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.
@@ -564,38 +564,151 @@ var __privateGet$3 = (obj, member, getter) => (__accessCheck$3(obj, member, "rea
564
564
  var __privateAdd$3 = (obj, member, value) => member.has(obj) ? __typeError$4("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
565
565
  var __privateSet$3 = (obj, member, value, setter) => (__accessCheck$3(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
566
566
  var __privateMethod$3 = (obj, member, method) => (__accessCheck$3(obj, member, "access private method"), method);
567
- var _staticDetails_dec, _activeSection_dec, _statusMessage_dec, _contentList_dec, _isAuthenticated_dec, _apiKey_dec, _a$4, _AdminPortal_decorators, _init$4, _apiKey, _isAuthenticated, _contentList, _statusMessage, _activeSection, _staticDetails;
568
- console.log("[AdminPortal] Module loading");
569
- console.log("[AdminPortal] About to define custom element");
567
+ var _loginError_dec, _staticDetails_dec, _activeSection_dec, _statusMessage_dec, _contentList_dec, _isLoading_dec, _isSetup_dec, _isAuthenticated_dec, _a$4, _AdminPortal_decorators, _init$4, _isAuthenticated, _isSetup, _isLoading, _contentList, _statusMessage, _activeSection, _staticDetails, _loginError;
570
568
  _AdminPortal_decorators = [customElement("admin-portal")];
571
- class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthenticated_dec = [state()], _contentList_dec = [state()], _statusMessage_dec = [state()], _activeSection_dec = [state()], _staticDetails_dec = [state()], _a$4) {
569
+ class AdminPortal extends (_a$4 = LitElement, _isAuthenticated_dec = [state()], _isSetup_dec = [state()], _isLoading_dec = [state()], _contentList_dec = [state()], _statusMessage_dec = [state()], _activeSection_dec = [state()], _staticDetails_dec = [state()], _loginError_dec = [state()], _a$4) {
572
570
  constructor() {
573
571
  super(...arguments);
574
- __privateAdd$3(this, _apiKey, __runInitializers$4(_init$4, 8, this, "")), __runInitializers$4(_init$4, 11, this);
575
- __privateAdd$3(this, _isAuthenticated, __runInitializers$4(_init$4, 12, this, false)), __runInitializers$4(_init$4, 15, this);
576
- __privateAdd$3(this, _contentList, __runInitializers$4(_init$4, 16, this, [])), __runInitializers$4(_init$4, 19, this);
577
- __privateAdd$3(this, _statusMessage, __runInitializers$4(_init$4, 20, this, "")), __runInitializers$4(_init$4, 23, this);
578
- __privateAdd$3(this, _activeSection, __runInitializers$4(_init$4, 24, this, "profile")), __runInitializers$4(_init$4, 27, this);
579
- __privateAdd$3(this, _staticDetails, __runInitializers$4(_init$4, 28, this, {})), __runInitializers$4(_init$4, 31, this);
572
+ __privateAdd$3(this, _isAuthenticated, __runInitializers$4(_init$4, 8, this, false)), __runInitializers$4(_init$4, 11, this);
573
+ __privateAdd$3(this, _isSetup, __runInitializers$4(_init$4, 12, this, false)), __runInitializers$4(_init$4, 15, this);
574
+ __privateAdd$3(this, _isLoading, __runInitializers$4(_init$4, 16, this, true)), __runInitializers$4(_init$4, 19, this);
575
+ __privateAdd$3(this, _contentList, __runInitializers$4(_init$4, 20, this, [])), __runInitializers$4(_init$4, 23, this);
576
+ __privateAdd$3(this, _statusMessage, __runInitializers$4(_init$4, 24, this, "")), __runInitializers$4(_init$4, 27, this);
577
+ __privateAdd$3(this, _activeSection, __runInitializers$4(_init$4, 28, this, "profile")), __runInitializers$4(_init$4, 31, this);
578
+ __privateAdd$3(this, _staticDetails, __runInitializers$4(_init$4, 32, this, {})), __runInitializers$4(_init$4, 35, this);
579
+ __privateAdd$3(this, _loginError, __runInitializers$4(_init$4, 36, this, "")), __runInitializers$4(_init$4, 39, this);
580
580
  }
581
581
  get apiUrl() {
582
582
  return window.__VITE_API_URL__ || void 0 || "http://localhost:8787";
583
583
  }
584
- handleLogin(e2) {
584
+ async connectedCallback() {
585
+ super.connectedCallback();
586
+ await this.checkAuthStatus();
587
+ }
588
+ async checkAuthStatus() {
589
+ try {
590
+ const res = await fetch(`${this.apiUrl}/auth/status`, {
591
+ credentials: "include"
592
+ });
593
+ if (res.ok) {
594
+ const status = await res.json();
595
+ this.isSetup = status.configured;
596
+ if (status.configured) {
597
+ await this.tryAutoLogin();
598
+ } else {
599
+ this.isLoading = false;
600
+ }
601
+ } else {
602
+ this.isSetup = false;
603
+ this.isLoading = false;
604
+ }
605
+ } catch (e2) {
606
+ this.isSetup = false;
607
+ this.isLoading = false;
608
+ }
609
+ }
610
+ async tryAutoLogin() {
611
+ try {
612
+ const res = await fetch(`${this.apiUrl}/content`, {
613
+ credentials: "include"
614
+ });
615
+ if (res.ok) {
616
+ this.contentList = await res.json();
617
+ this.isAuthenticated = true;
618
+ }
619
+ } catch (e2) {
620
+ }
621
+ this.isLoading = false;
622
+ }
623
+ async handleLogin(e2) {
585
624
  e2.preventDefault();
586
- const input = this.shadowRoot?.querySelector("#apiKey");
587
- if (input.value) {
588
- this.apiKey = input.value;
589
- this.isAuthenticated = true;
590
- this.fetchContent();
625
+ this.loginError = "";
626
+ const usernameInput = this.shadowRoot?.querySelector("#username");
627
+ const passwordInput = this.shadowRoot?.querySelector("#password");
628
+ if (!usernameInput?.value || !passwordInput?.value) {
629
+ this.loginError = "Username and password required";
630
+ return;
631
+ }
632
+ try {
633
+ const res = await fetch(`${this.apiUrl}/auth/login`, {
634
+ method: "POST",
635
+ credentials: "include",
636
+ headers: { "Content-Type": "application/json" },
637
+ body: JSON.stringify({
638
+ username: usernameInput.value,
639
+ password: passwordInput.value
640
+ })
641
+ });
642
+ if (res.ok) {
643
+ this.isAuthenticated = true;
644
+ await this.fetchContent();
645
+ } else {
646
+ const data = await res.json();
647
+ this.loginError = data.error || "Login failed";
648
+ }
649
+ } catch (e22) {
650
+ this.loginError = "Connection error";
651
+ }
652
+ }
653
+ async handleSetup(e2) {
654
+ e2.preventDefault();
655
+ this.loginError = "";
656
+ const usernameInput = this.shadowRoot?.querySelector("#username");
657
+ const passwordInput = this.shadowRoot?.querySelector("#password");
658
+ const confirmInput = this.shadowRoot?.querySelector("#confirmPassword");
659
+ if (!usernameInput?.value || !passwordInput?.value) {
660
+ this.loginError = "Username and password required";
661
+ return;
662
+ }
663
+ if (usernameInput.value.length < 3) {
664
+ this.loginError = "Username must be at least 3 characters";
665
+ return;
666
+ }
667
+ if (passwordInput.value.length < 8) {
668
+ this.loginError = "Password must be at least 8 characters";
669
+ return;
670
+ }
671
+ if (passwordInput.value !== confirmInput?.value) {
672
+ this.loginError = "Passwords do not match";
673
+ return;
674
+ }
675
+ try {
676
+ const res = await fetch(`${this.apiUrl}/auth/setup`, {
677
+ method: "POST",
678
+ credentials: "include",
679
+ headers: { "Content-Type": "application/json" },
680
+ body: JSON.stringify({
681
+ username: usernameInput.value,
682
+ password: passwordInput.value
683
+ })
684
+ });
685
+ if (res.ok) {
686
+ this.isAuthenticated = true;
687
+ this.isSetup = true;
688
+ await this.fetchContent();
689
+ } else {
690
+ const data = await res.json();
691
+ this.loginError = data.error || "Setup failed";
692
+ }
693
+ } catch (e22) {
694
+ this.loginError = "Connection error";
591
695
  }
592
696
  }
697
+ async handleLogout() {
698
+ try {
699
+ await fetch(`${this.apiUrl}/auth/logout`, {
700
+ method: "POST",
701
+ credentials: "include"
702
+ });
703
+ } catch (e2) {
704
+ }
705
+ this.isAuthenticated = false;
706
+ this.contentList = [];
707
+ }
593
708
  async fetchContent() {
594
709
  try {
595
710
  const res = await fetch(`${this.apiUrl}/content`, {
596
- headers: {
597
- "Authorization": `Bearer ${this.apiKey}`
598
- }
711
+ credentials: "include"
599
712
  });
600
713
  if (res.ok) {
601
714
  this.contentList = await res.json();
@@ -608,7 +721,9 @@ class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthen
608
721
  }
609
722
  async fetchStaticDetails() {
610
723
  try {
611
- const res = await fetch(`${this.apiUrl}/api/static`);
724
+ const res = await fetch(`${this.apiUrl}/static`, {
725
+ credentials: "include"
726
+ });
612
727
  if (res.ok) {
613
728
  this.staticDetails = await res.json();
614
729
  }
@@ -620,9 +735,7 @@ class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthen
620
735
  this.statusMessage = "Uploading...";
621
736
  const res = await fetch(`${this.apiUrl}/content/${key}`, {
622
737
  method: "PUT",
623
- headers: {
624
- "Authorization": `Bearer ${this.apiKey}`
625
- },
738
+ credentials: "include",
626
739
  body: file
627
740
  });
628
741
  if (res.ok) {
@@ -640,9 +753,7 @@ class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthen
640
753
  this.statusMessage = "Clearing cache...";
641
754
  const res = await fetch(`${this.apiUrl}/cache-clear`, {
642
755
  method: "POST",
643
- headers: {
644
- "Authorization": `Bearer ${this.apiKey}`
645
- }
756
+ credentials: "include"
646
757
  });
647
758
  if (res.ok) {
648
759
  this.statusMessage = "Cache cleared!";
@@ -658,9 +769,7 @@ class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthen
658
769
  try {
659
770
  const res = await fetch(`${this.apiUrl}/content/${key}`, {
660
771
  method: "DELETE",
661
- headers: {
662
- "Authorization": `Bearer ${this.apiKey}`
663
- }
772
+ credentials: "include"
664
773
  });
665
774
  if (res.ok) {
666
775
  this.fetchContent();
@@ -677,6 +786,39 @@ class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthen
677
786
  getSectionFiles(prefix) {
678
787
  return this.contentList.filter((c2) => c2.key.startsWith(prefix));
679
788
  }
789
+ renderLoginForm() {
790
+ return html`
791
+ <div class="container">
792
+ <div class="login-box">
793
+ <h2>Admin Setup</h2>
794
+ <p>Create your admin credentials</p>
795
+ <form @submit=${this.handleSetup}>
796
+ <input type="text" id="username" placeholder="Username (3+ chars)" />
797
+ <input type="password" id="password" placeholder="Password (8+ chars)" />
798
+ <input type="password" id="confirmPassword" placeholder="Confirm Password" />
799
+ ${this.loginError ? html`<div class="error-message">${this.loginError}</div>` : ""}
800
+ <button type="submit" class="btn-primary">Create Account</button>
801
+ </form>
802
+ </div>
803
+ </div>
804
+ `;
805
+ }
806
+ renderLogin() {
807
+ return html`
808
+ <div class="container">
809
+ <div class="login-box">
810
+ <h2>Admin Login</h2>
811
+ <p>Enter your credentials</p>
812
+ <form @submit=${this.handleLogin}>
813
+ <input type="text" id="username" placeholder="Username" autocomplete="username" />
814
+ <input type="password" id="password" placeholder="Password" autocomplete="current-password" />
815
+ ${this.loginError ? html`<div class="error-message">${this.loginError}</div>` : ""}
816
+ <button type="submit" class="btn-primary">Login</button>
817
+ </form>
818
+ </div>
819
+ </div>
820
+ `;
821
+ }
680
822
  renderHomeSection() {
681
823
  const home = this.getContent("home.md");
682
824
  return html`
@@ -875,67 +1017,6 @@ class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthen
875
1017
  </div>
876
1018
  `;
877
1019
  }
878
- render() {
879
- if (!this.isAuthenticated) {
880
- return html`
881
- <div class="container">
882
- <div class="login-box">
883
- <h2>Admin Login</h2>
884
- <p>Enter your API key to manage content</p>
885
- <form @submit=${this.handleLogin}>
886
- <input type="password" id="apiKey" placeholder="API Key" />
887
- <button type="submit" class="btn-primary">Login</button>
888
- </form>
889
- </div>
890
- </div>
891
- `;
892
- }
893
- return html`
894
- <div class="container">
895
- <div class="header">
896
- <h1>Content Manager</h1>
897
- <button class="btn-secondary" @click=${() => this.handleClearCache()}>Clear Cache</button>
898
- </div>
899
-
900
- <div class="nav-tabs">
901
- <button class="nav-tab ${this.activeSection === "home" ? "active" : ""}"
902
- @click=${() => this.activeSection = "home"}>Home</button>
903
- <button class="nav-tab ${this.activeSection === "profile" ? "active" : ""}"
904
- @click=${() => this.activeSection = "profile"}>Profile</button>
905
- <button class="nav-tab ${this.activeSection === "aboutme" ? "active" : ""}"
906
- @click=${() => this.activeSection = "aboutme"}>About Me</button>
907
- <button class="nav-tab ${this.activeSection === "blogs" ? "active" : ""}"
908
- @click=${() => this.activeSection = "blogs"}>Blogs</button>
909
- <button class="nav-tab ${this.activeSection === "stories" ? "active" : ""}"
910
- @click=${() => this.activeSection = "stories"}>Stories</button>
911
- <button class="nav-tab ${this.activeSection === "images" ? "active" : ""}"
912
- @click=${() => this.activeSection = "images"}>Images</button>
913
- <button class="nav-tab ${this.activeSection === "logo" ? "active" : ""}"
914
- @click=${() => this.activeSection = "logo"}>Logo</button>
915
- <button class="nav-tab ${this.activeSection === "static" ? "active" : ""}"
916
- @click=${() => {
917
- this.activeSection = "static";
918
- this.fetchStaticDetails();
919
- }}>Site Settings</button>
920
- </div>
921
-
922
- ${this.statusMessage ? html`
923
- <div class="status-message ${this.statusMessage.includes("successful") || this.statusMessage.includes("cleared") ? "success" : this.statusMessage.includes("failed") || this.statusMessage.includes("Error") ? "error" : ""}">
924
- ${this.statusMessage}
925
- </div>
926
- ` : ""}
927
-
928
- ${this.activeSection === "home" ? this.renderHomeSection() : ""}
929
- ${this.activeSection === "profile" ? this.renderProfileSection() : ""}
930
- ${this.activeSection === "aboutme" ? this.renderAboutMeSection() : ""}
931
- ${this.activeSection === "blogs" ? this.renderBlogsSection() : ""}
932
- ${this.activeSection === "stories" ? this.renderStoriesSection() : ""}
933
- ${this.activeSection === "images" ? this.renderImagesSection() : ""}
934
- ${this.activeSection === "logo" ? this.renderLogoSection() : ""}
935
- ${this.activeSection === "static" ? this.renderStaticSection() : ""}
936
- </div>
937
- `;
938
- }
939
1020
  renderStaticSection() {
940
1021
  return html`
941
1022
  <div class="section">
@@ -983,7 +1064,8 @@ class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthen
983
1064
  const url = `${this.apiUrl}/content/staticdetails.json`;
984
1065
  const res = await fetch(url, {
985
1066
  method: "PUT",
986
- headers: { "Authorization": `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
1067
+ credentials: "include",
1068
+ headers: { "Content-Type": "application/json" },
987
1069
  body: JSON.stringify(data)
988
1070
  });
989
1071
  if (res.ok) {
@@ -999,20 +1081,81 @@ class AdminPortal extends (_a$4 = LitElement, _apiKey_dec = [state()], _isAuthen
999
1081
  </div>
1000
1082
  `;
1001
1083
  }
1084
+ render() {
1085
+ if (this.isLoading) {
1086
+ return html`<div class="container"><div class="loading">Loading...</div></div>`;
1087
+ }
1088
+ if (!this.isSetup) {
1089
+ return this.renderLoginForm();
1090
+ }
1091
+ if (!this.isAuthenticated) {
1092
+ return this.renderLogin();
1093
+ }
1094
+ return html`
1095
+ <div class="container">
1096
+ <div class="header">
1097
+ <h1>Content Manager</h1>
1098
+ <button class="btn-secondary" @click=${() => this.handleLogout()}>Logout</button>
1099
+ <button class="btn-secondary" @click=${() => this.handleClearCache()}>Clear Cache</button>
1100
+ </div>
1101
+
1102
+ <div class="nav-tabs">
1103
+ <button class="nav-tab ${this.activeSection === "home" ? "active" : ""}"
1104
+ @click=${() => this.activeSection = "home"}>Home</button>
1105
+ <button class="nav-tab ${this.activeSection === "profile" ? "active" : ""}"
1106
+ @click=${() => this.activeSection = "profile"}>Profile</button>
1107
+ <button class="nav-tab ${this.activeSection === "aboutme" ? "active" : ""}"
1108
+ @click=${() => this.activeSection = "aboutme"}>About Me</button>
1109
+ <button class="nav-tab ${this.activeSection === "blogs" ? "active" : ""}"
1110
+ @click=${() => this.activeSection = "blogs"}>Blogs</button>
1111
+ <button class="nav-tab ${this.activeSection === "stories" ? "active" : ""}"
1112
+ @click=${() => this.activeSection = "stories"}>Stories</button>
1113
+ <button class="nav-tab ${this.activeSection === "images" ? "active" : ""}"
1114
+ @click=${() => this.activeSection = "images"}>Images</button>
1115
+ <button class="nav-tab ${this.activeSection === "logo" ? "active" : ""}"
1116
+ @click=${() => this.activeSection = "logo"}>Logo</button>
1117
+ <button class="nav-tab ${this.activeSection === "static" ? "active" : ""}"
1118
+ @click=${() => {
1119
+ this.activeSection = "static";
1120
+ this.fetchStaticDetails();
1121
+ }}>Site Settings</button>
1122
+ </div>
1123
+
1124
+ ${this.statusMessage ? html`
1125
+ <div class="status-message ${this.statusMessage.includes("successful") || this.statusMessage.includes("cleared") ? "success" : this.statusMessage.includes("failed") || this.statusMessage.includes("Error") ? "error" : ""}">
1126
+ ${this.statusMessage}
1127
+ </div>
1128
+ ` : ""}
1129
+
1130
+ ${this.activeSection === "home" ? this.renderHomeSection() : ""}
1131
+ ${this.activeSection === "profile" ? this.renderProfileSection() : ""}
1132
+ ${this.activeSection === "aboutme" ? this.renderAboutMeSection() : ""}
1133
+ ${this.activeSection === "blogs" ? this.renderBlogsSection() : ""}
1134
+ ${this.activeSection === "stories" ? this.renderStoriesSection() : ""}
1135
+ ${this.activeSection === "images" ? this.renderImagesSection() : ""}
1136
+ ${this.activeSection === "logo" ? this.renderLogoSection() : ""}
1137
+ ${this.activeSection === "static" ? this.renderStaticSection() : ""}
1138
+ </div>
1139
+ `;
1140
+ }
1002
1141
  }
1003
1142
  _init$4 = __decoratorStart$4(_a$4);
1004
- _apiKey = /* @__PURE__ */ new WeakMap();
1005
1143
  _isAuthenticated = /* @__PURE__ */ new WeakMap();
1144
+ _isSetup = /* @__PURE__ */ new WeakMap();
1145
+ _isLoading = /* @__PURE__ */ new WeakMap();
1006
1146
  _contentList = /* @__PURE__ */ new WeakMap();
1007
1147
  _statusMessage = /* @__PURE__ */ new WeakMap();
1008
1148
  _activeSection = /* @__PURE__ */ new WeakMap();
1009
1149
  _staticDetails = /* @__PURE__ */ new WeakMap();
1010
- __decorateElement$4(_init$4, 4, "apiKey", _apiKey_dec, AdminPortal, _apiKey);
1150
+ _loginError = /* @__PURE__ */ new WeakMap();
1011
1151
  __decorateElement$4(_init$4, 4, "isAuthenticated", _isAuthenticated_dec, AdminPortal, _isAuthenticated);
1152
+ __decorateElement$4(_init$4, 4, "isSetup", _isSetup_dec, AdminPortal, _isSetup);
1153
+ __decorateElement$4(_init$4, 4, "isLoading", _isLoading_dec, AdminPortal, _isLoading);
1012
1154
  __decorateElement$4(_init$4, 4, "contentList", _contentList_dec, AdminPortal, _contentList);
1013
1155
  __decorateElement$4(_init$4, 4, "statusMessage", _statusMessage_dec, AdminPortal, _statusMessage);
1014
1156
  __decorateElement$4(_init$4, 4, "activeSection", _activeSection_dec, AdminPortal, _activeSection);
1015
1157
  __decorateElement$4(_init$4, 4, "staticDetails", _staticDetails_dec, AdminPortal, _staticDetails);
1158
+ __decorateElement$4(_init$4, 4, "loginError", _loginError_dec, AdminPortal, _loginError);
1016
1159
  AdminPortal = __decorateElement$4(_init$4, 0, "AdminPortal", _AdminPortal_decorators, AdminPortal);
1017
1160
  AdminPortal.styles = adminStyles;
1018
1161
  __runInitializers$4(_init$4, 1, AdminPortal);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { A, B, M, R, W, c, a, g, b, d, h, e, r, s, v } from "./chunks/website-api-DI3muo2s.js";
2
2
  import { WebsitePrerender } from "./prerender.js";
3
- import { A as A2, B as B2, F, M as M2, a as a2, S } from "./chunks/index-VimKeB5W.js";
3
+ import { A as A2, B as B2, F, M as M2, a as a2, S } from "./chunks/index-C3wLSCKU.js";
4
4
  import { R as R2, S as S2, T, W as W2, b as b2, c as c2, g as g2, a as a3, i, r as r2 } from "./chunks/template-MawmknFQ.js";
5
5
  export {
6
6
  A as AUTH_KV,
@@ -13,14 +13,21 @@ interface StaticDetails {
13
13
  }
14
14
  export declare class AdminPortal extends LitElement {
15
15
  static styles: import('lit').CSSResult;
16
- accessor apiKey: string;
17
16
  accessor isAuthenticated: boolean;
17
+ accessor isSetup: boolean;
18
+ accessor isLoading: boolean;
18
19
  accessor contentList: ContentItem[];
19
20
  accessor statusMessage: string;
20
21
  accessor activeSection: string;
21
22
  accessor staticDetails: StaticDetails;
23
+ accessor loginError: string;
22
24
  get apiUrl(): any;
23
- handleLogin(e: Event): void;
25
+ connectedCallback(): Promise<void>;
26
+ checkAuthStatus(): Promise<void>;
27
+ tryAutoLogin(): Promise<void>;
28
+ handleLogin(e: Event): Promise<void>;
29
+ handleSetup(e: Event): Promise<void>;
30
+ handleLogout(): Promise<void>;
24
31
  fetchContent(): Promise<void>;
25
32
  fetchStaticDetails(): Promise<void>;
26
33
  handleUpload(key: string, file: File): Promise<void>;
@@ -28,6 +35,8 @@ export declare class AdminPortal extends LitElement {
28
35
  handleDelete(key: string): Promise<void>;
29
36
  getContent(key: string): ContentItem | undefined;
30
37
  getSectionFiles(prefix: string): ContentItem[];
38
+ renderLoginForm(): import('lit-html').TemplateResult<1>;
39
+ renderLogin(): import('lit-html').TemplateResult<1>;
31
40
  renderHomeSection(): import('lit-html').TemplateResult<1>;
32
41
  renderProfileSection(): import('lit-html').TemplateResult<1>;
33
42
  renderAboutMeSection(): import('lit-html').TemplateResult<1>;
@@ -35,8 +44,8 @@ export declare class AdminPortal extends LitElement {
35
44
  renderStoriesSection(): import('lit-html').TemplateResult<1>;
36
45
  renderImagesSection(): import('lit-html').TemplateResult<1>;
37
46
  renderLogoSection(): import('lit-html').TemplateResult<1>;
38
- render(): import('lit-html').TemplateResult<1>;
39
47
  renderStaticSection(): import('lit-html').TemplateResult<1>;
48
+ render(): import('lit-html').TemplateResult<1>;
40
49
  }
41
50
  export {};
42
51
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/admin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAO5C,UAAU,WAAW;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,aAAa;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,qBACa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BAAe;IAG5B,QAAQ,CAAC,MAAM,SAAM;IAGrB,QAAQ,CAAC,eAAe,UAAS;IAGjC,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,CAAM;IAGzC,QAAQ,CAAC,aAAa,SAAM;IAG5B,QAAQ,CAAC,aAAa,SAAa;IAGnC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAM;IAE3C,IAAI,MAAM,QAET;IAED,WAAW,CAAC,CAAC,EAAE,KAAK;IAUd,YAAY;IAiBZ,kBAAkB;IAWlB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI;IAsBpC,gBAAgB;IAoBhB,YAAY,CAAC,GAAG,EAAE,MAAM;IAqB9B,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIhD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE;IAI9C,iBAAiB;IAuBjB,oBAAoB;IAuBpB,oBAAoB;IAuBpB,kBAAkB;IAuClB,oBAAoB;IAuCpB,mBAAmB;IA+BnB,iBAAiB;IA2BjB,MAAM;IA+DN,mBAAmB;CAiEpB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/admin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C,UAAU,WAAW;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,aAAa;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD,qBACa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BAAe;IAG5B,QAAQ,CAAC,eAAe,UAAS;IAGjC,QAAQ,CAAC,OAAO,UAAS;IAGzB,QAAQ,CAAC,SAAS,UAAQ;IAG1B,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,CAAM;IAGzC,QAAQ,CAAC,aAAa,SAAM;IAG5B,QAAQ,CAAC,aAAa,SAAa;IAGnC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAM;IAG3C,QAAQ,CAAC,UAAU,SAAM;IAEzB,IAAI,MAAM,QAET;IAEK,iBAAiB;IAKjB,eAAe;IAwBf,YAAY;IAaZ,WAAW,CAAC,CAAC,EAAE,KAAK;IAmCpB,WAAW,CAAC,CAAC,EAAE,KAAK;IAoDpB,YAAY;IAWZ,YAAY;IAeZ,kBAAkB;IAWlB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI;IAoBpC,gBAAgB;IAkBhB,YAAY,CAAC,GAAG,EAAE,MAAM;IAmB9B,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIhD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE;IAI9C,eAAe;IAkBf,WAAW;IAiBX,iBAAiB;IAuBjB,oBAAoB;IAuBpB,oBAAoB;IAuBpB,kBAAkB;IAuClB,oBAAoB;IAuCpB,mBAAmB;IA+BnB,iBAAiB;IA2BjB,mBAAmB;IAmEnB,MAAM;CA4DP"}
package/dist/ui.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A, B, F, M, a, S } from "./chunks/index-VimKeB5W.js";
1
+ import { A, B, F, M, a, S } from "./chunks/index-C3wLSCKU.js";
2
2
  export {
3
3
  A as AdminPortal,
4
4
  B as BlogViewer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadertechie/personal-site-kit",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.1.0-alpha.6",
4
4
  "type": "module",
5
5
  "description": "A high-performance personal website engine for Cloudflare Workers and R2",
6
6
  "repository": {
@@ -3,8 +3,6 @@ import { customElement, state } from 'lit/decorators.js';
3
3
 
4
4
  import { adminStyles } from './styles';
5
5
 
6
- console.log('[AdminPortal] Module loading');
7
-
8
6
  interface ContentItem {
9
7
  key: string;
10
8
  size: number;
@@ -19,17 +17,23 @@ interface StaticDetails {
19
17
  email?: string;
20
18
  }
21
19
 
22
- console.log('[AdminPortal] About to define custom element');
20
+ interface AuthStatus {
21
+ configured: boolean;
22
+ username: string | null;
23
+ }
23
24
 
24
25
  @customElement('admin-portal')
25
26
  export class AdminPortal extends LitElement {
26
27
  static styles = adminStyles;
27
28
 
28
29
  @state()
29
- accessor apiKey = '';
30
+ accessor isAuthenticated = false;
30
31
 
31
32
  @state()
32
- accessor isAuthenticated = false;
33
+ accessor isSetup = false;
34
+
35
+ @state()
36
+ accessor isLoading = true;
33
37
 
34
38
  @state()
35
39
  accessor contentList: ContentItem[] = [];
@@ -43,26 +47,157 @@ export class AdminPortal extends LitElement {
43
47
  @state()
44
48
  accessor staticDetails: StaticDetails = {};
45
49
 
50
+ @state()
51
+ accessor loginError = '';
52
+
46
53
  get apiUrl() {
47
54
  return (window as any).__VITE_API_URL__ || import.meta.env.VITE_API_URL || 'http://localhost:8787';
48
55
  }
49
56
 
50
- handleLogin(e: Event) {
57
+ async connectedCallback() {
58
+ super.connectedCallback();
59
+ await this.checkAuthStatus();
60
+ }
61
+
62
+ async checkAuthStatus() {
63
+ try {
64
+ const res = await fetch(`${this.apiUrl}/auth/status`, {
65
+ credentials: 'include'
66
+ });
67
+ if (res.ok) {
68
+ const status: AuthStatus = await res.json();
69
+ this.isSetup = status.configured;
70
+
71
+ if (status.configured) {
72
+ await this.tryAutoLogin();
73
+ } else {
74
+ this.isLoading = false;
75
+ }
76
+ } else {
77
+ this.isSetup = false;
78
+ this.isLoading = false;
79
+ }
80
+ } catch (e) {
81
+ this.isSetup = false;
82
+ this.isLoading = false;
83
+ }
84
+ }
85
+
86
+ async tryAutoLogin() {
87
+ try {
88
+ const res = await fetch(`${this.apiUrl}/content`, {
89
+ credentials: 'include'
90
+ });
91
+ if (res.ok) {
92
+ this.contentList = await res.json();
93
+ this.isAuthenticated = true;
94
+ }
95
+ } catch (e) {}
96
+ this.isLoading = false;
97
+ }
98
+
99
+ async handleLogin(e: Event) {
100
+ e.preventDefault();
101
+ this.loginError = '';
102
+
103
+ const usernameInput = this.shadowRoot?.querySelector('#username') as HTMLInputElement;
104
+ const passwordInput = this.shadowRoot?.querySelector('#password') as HTMLInputElement;
105
+
106
+ if (!usernameInput?.value || !passwordInput?.value) {
107
+ this.loginError = 'Username and password required';
108
+ return;
109
+ }
110
+
111
+ try {
112
+ const res = await fetch(`${this.apiUrl}/auth/login`, {
113
+ method: 'POST',
114
+ credentials: 'include',
115
+ headers: { 'Content-Type': 'application/json' },
116
+ body: JSON.stringify({
117
+ username: usernameInput.value,
118
+ password: passwordInput.value
119
+ })
120
+ });
121
+
122
+ if (res.ok) {
123
+ this.isAuthenticated = true;
124
+ await this.fetchContent();
125
+ } else {
126
+ const data = await res.json();
127
+ this.loginError = data.error || 'Login failed';
128
+ }
129
+ } catch (e) {
130
+ this.loginError = 'Connection error';
131
+ }
132
+ }
133
+
134
+ async handleSetup(e: Event) {
51
135
  e.preventDefault();
52
- const input = this.shadowRoot?.querySelector('#apiKey') as HTMLInputElement;
53
- if (input.value) {
54
- this.apiKey = input.value;
55
- this.isAuthenticated = true;
56
- this.fetchContent();
136
+ this.loginError = '';
137
+
138
+ const usernameInput = this.shadowRoot?.querySelector('#username') as HTMLInputElement;
139
+ const passwordInput = this.shadowRoot?.querySelector('#password') as HTMLInputElement;
140
+ const confirmInput = this.shadowRoot?.querySelector('#confirmPassword') as HTMLInputElement;
141
+
142
+ if (!usernameInput?.value || !passwordInput?.value) {
143
+ this.loginError = 'Username and password required';
144
+ return;
145
+ }
146
+
147
+ if (usernameInput.value.length < 3) {
148
+ this.loginError = 'Username must be at least 3 characters';
149
+ return;
57
150
  }
151
+
152
+ if (passwordInput.value.length < 8) {
153
+ this.loginError = 'Password must be at least 8 characters';
154
+ return;
155
+ }
156
+
157
+ if (passwordInput.value !== confirmInput?.value) {
158
+ this.loginError = 'Passwords do not match';
159
+ return;
160
+ }
161
+
162
+ try {
163
+ const res = await fetch(`${this.apiUrl}/auth/setup`, {
164
+ method: 'POST',
165
+ credentials: 'include',
166
+ headers: { 'Content-Type': 'application/json' },
167
+ body: JSON.stringify({
168
+ username: usernameInput.value,
169
+ password: passwordInput.value
170
+ })
171
+ });
172
+
173
+ if (res.ok) {
174
+ this.isAuthenticated = true;
175
+ this.isSetup = true;
176
+ await this.fetchContent();
177
+ } else {
178
+ const data = await res.json();
179
+ this.loginError = data.error || 'Setup failed';
180
+ }
181
+ } catch (e) {
182
+ this.loginError = 'Connection error';
183
+ }
184
+ }
185
+
186
+ async handleLogout() {
187
+ try {
188
+ await fetch(`${this.apiUrl}/auth/logout`, {
189
+ method: 'POST',
190
+ credentials: 'include'
191
+ });
192
+ } catch (e) {}
193
+ this.isAuthenticated = false;
194
+ this.contentList = [];
58
195
  }
59
196
 
60
197
  async fetchContent() {
61
198
  try {
62
199
  const res = await fetch(`${this.apiUrl}/content`, {
63
- headers: {
64
- 'Authorization': `Bearer ${this.apiKey}`
65
- }
200
+ credentials: 'include'
66
201
  });
67
202
  if (res.ok) {
68
203
  this.contentList = await res.json();
@@ -76,13 +211,13 @@ export class AdminPortal extends LitElement {
76
211
 
77
212
  async fetchStaticDetails() {
78
213
  try {
79
- const res = await fetch(`${this.apiUrl}/api/static`);
214
+ const res = await fetch(`${this.apiUrl}/static`, {
215
+ credentials: 'include'
216
+ });
80
217
  if (res.ok) {
81
218
  this.staticDetails = await res.json();
82
219
  }
83
- } catch (e) {
84
- // Ignore errors
85
- }
220
+ } catch (e) {}
86
221
  }
87
222
 
88
223
  async handleUpload(key: string, file: File) {
@@ -90,9 +225,7 @@ export class AdminPortal extends LitElement {
90
225
  this.statusMessage = 'Uploading...';
91
226
  const res = await fetch(`${this.apiUrl}/content/${key}`, {
92
227
  method: 'PUT',
93
- headers: {
94
- 'Authorization': `Bearer ${this.apiKey}`
95
- },
228
+ credentials: 'include',
96
229
  body: file
97
230
  });
98
231
 
@@ -112,9 +245,7 @@ export class AdminPortal extends LitElement {
112
245
  this.statusMessage = 'Clearing cache...';
113
246
  const res = await fetch(`${this.apiUrl}/cache-clear`, {
114
247
  method: 'POST',
115
- headers: {
116
- 'Authorization': `Bearer ${this.apiKey}`
117
- }
248
+ credentials: 'include'
118
249
  });
119
250
 
120
251
  if (res.ok) {
@@ -133,9 +264,7 @@ export class AdminPortal extends LitElement {
133
264
  try {
134
265
  const res = await fetch(`${this.apiUrl}/content/${key}`, {
135
266
  method: 'DELETE',
136
- headers: {
137
- 'Authorization': `Bearer ${this.apiKey}`
138
- }
267
+ credentials: 'include'
139
268
  });
140
269
 
141
270
  if (res.ok) {
@@ -156,6 +285,41 @@ export class AdminPortal extends LitElement {
156
285
  return this.contentList.filter(c => c.key.startsWith(prefix));
157
286
  }
158
287
 
288
+ renderLoginForm() {
289
+ return html`
290
+ <div class="container">
291
+ <div class="login-box">
292
+ <h2>Admin Setup</h2>
293
+ <p>Create your admin credentials</p>
294
+ <form @submit=${this.handleSetup}>
295
+ <input type="text" id="username" placeholder="Username (3+ chars)" />
296
+ <input type="password" id="password" placeholder="Password (8+ chars)" />
297
+ <input type="password" id="confirmPassword" placeholder="Confirm Password" />
298
+ ${this.loginError ? html`<div class="error-message">${this.loginError}</div>` : ''}
299
+ <button type="submit" class="btn-primary">Create Account</button>
300
+ </form>
301
+ </div>
302
+ </div>
303
+ `;
304
+ }
305
+
306
+ renderLogin() {
307
+ return html`
308
+ <div class="container">
309
+ <div class="login-box">
310
+ <h2>Admin Login</h2>
311
+ <p>Enter your credentials</p>
312
+ <form @submit=${this.handleLogin}>
313
+ <input type="text" id="username" placeholder="Username" autocomplete="username" />
314
+ <input type="password" id="password" placeholder="Password" autocomplete="current-password" />
315
+ ${this.loginError ? html`<div class="error-message">${this.loginError}</div>` : ''}
316
+ <button type="submit" class="btn-primary">Login</button>
317
+ </form>
318
+ </div>
319
+ </div>
320
+ `;
321
+ }
322
+
159
323
  renderHomeSection() {
160
324
  const home = this.getContent('home.md');
161
325
  return html`
@@ -361,69 +525,6 @@ export class AdminPortal extends LitElement {
361
525
  `;
362
526
  }
363
527
 
364
- render() {
365
- if (!this.isAuthenticated) {
366
- return html`
367
- <div class="container">
368
- <div class="login-box">
369
- <h2>Admin Login</h2>
370
- <p>Enter your API key to manage content</p>
371
- <form @submit=${this.handleLogin}>
372
- <input type="password" id="apiKey" placeholder="API Key" />
373
- <button type="submit" class="btn-primary">Login</button>
374
- </form>
375
- </div>
376
- </div>
377
- `;
378
- }
379
-
380
- return html`
381
- <div class="container">
382
- <div class="header">
383
- <h1>Content Manager</h1>
384
- <button class="btn-secondary" @click=${() => this.handleClearCache()}>Clear Cache</button>
385
- </div>
386
-
387
- <div class="nav-tabs">
388
- <button class="nav-tab ${this.activeSection === 'home' ? 'active' : ''}"
389
- @click=${() => this.activeSection = 'home'}>Home</button>
390
- <button class="nav-tab ${this.activeSection === 'profile' ? 'active' : ''}"
391
- @click=${() => this.activeSection = 'profile'}>Profile</button>
392
- <button class="nav-tab ${this.activeSection === 'aboutme' ? 'active' : ''}"
393
- @click=${() => this.activeSection = 'aboutme'}>About Me</button>
394
- <button class="nav-tab ${this.activeSection === 'blogs' ? 'active' : ''}"
395
- @click=${() => this.activeSection = 'blogs'}>Blogs</button>
396
- <button class="nav-tab ${this.activeSection === 'stories' ? 'active' : ''}"
397
- @click=${() => this.activeSection = 'stories'}>Stories</button>
398
- <button class="nav-tab ${this.activeSection === 'images' ? 'active' : ''}"
399
- @click=${() => this.activeSection = 'images'}>Images</button>
400
- <button class="nav-tab ${this.activeSection === 'logo' ? 'active' : ''}"
401
- @click=${() => this.activeSection = 'logo'}>Logo</button>
402
- <button class="nav-tab ${this.activeSection === 'static' ? 'active' : ''}"
403
- @click=${() => {
404
- this.activeSection = 'static';
405
- this.fetchStaticDetails();
406
- }}>Site Settings</button>
407
- </div>
408
-
409
- ${this.statusMessage ? html`
410
- <div class="status-message ${this.statusMessage.includes('successful') || this.statusMessage.includes('cleared') ? 'success' : this.statusMessage.includes('failed') || this.statusMessage.includes('Error') ? 'error' : ''}">
411
- ${this.statusMessage}
412
- </div>
413
- ` : ''}
414
-
415
- ${this.activeSection === 'home' ? this.renderHomeSection() : ''}
416
- ${this.activeSection === 'profile' ? this.renderProfileSection() : ''}
417
- ${this.activeSection === 'aboutme' ? this.renderAboutMeSection() : ''}
418
- ${this.activeSection === 'blogs' ? this.renderBlogsSection() : ''}
419
- ${this.activeSection === 'stories' ? this.renderStoriesSection() : ''}
420
- ${this.activeSection === 'images' ? this.renderImagesSection() : ''}
421
- ${this.activeSection === 'logo' ? this.renderLogoSection() : ''}
422
- ${this.activeSection === 'static' ? this.renderStaticSection() : ''}
423
- </div>
424
- `;
425
- }
426
-
427
528
  renderStaticSection() {
428
529
  return html`
429
530
  <div class="section">
@@ -473,7 +574,8 @@ export class AdminPortal extends LitElement {
473
574
  const url = `${this.apiUrl}/content/staticdetails.json`;
474
575
  const res = await fetch(url, {
475
576
  method: 'PUT',
476
- headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' },
577
+ credentials: 'include',
578
+ headers: { 'Content-Type': 'application/json' },
477
579
  body: JSON.stringify(data)
478
580
  });
479
581
  if (res.ok) {
@@ -489,4 +591,65 @@ export class AdminPortal extends LitElement {
489
591
  </div>
490
592
  `;
491
593
  }
594
+
595
+ render() {
596
+ if (this.isLoading) {
597
+ return html`<div class="container"><div class="loading">Loading...</div></div>`;
598
+ }
599
+
600
+ if (!this.isSetup) {
601
+ return this.renderLoginForm();
602
+ }
603
+
604
+ if (!this.isAuthenticated) {
605
+ return this.renderLogin();
606
+ }
607
+
608
+ return html`
609
+ <div class="container">
610
+ <div class="header">
611
+ <h1>Content Manager</h1>
612
+ <button class="btn-secondary" @click=${() => this.handleLogout()}>Logout</button>
613
+ <button class="btn-secondary" @click=${() => this.handleClearCache()}>Clear Cache</button>
614
+ </div>
615
+
616
+ <div class="nav-tabs">
617
+ <button class="nav-tab ${this.activeSection === 'home' ? 'active' : ''}"
618
+ @click=${() => this.activeSection = 'home'}>Home</button>
619
+ <button class="nav-tab ${this.activeSection === 'profile' ? 'active' : ''}"
620
+ @click=${() => this.activeSection = 'profile'}>Profile</button>
621
+ <button class="nav-tab ${this.activeSection === 'aboutme' ? 'active' : ''}"
622
+ @click=${() => this.activeSection = 'aboutme'}>About Me</button>
623
+ <button class="nav-tab ${this.activeSection === 'blogs' ? 'active' : ''}"
624
+ @click=${() => this.activeSection = 'blogs'}>Blogs</button>
625
+ <button class="nav-tab ${this.activeSection === 'stories' ? 'active' : ''}"
626
+ @click=${() => this.activeSection = 'stories'}>Stories</button>
627
+ <button class="nav-tab ${this.activeSection === 'images' ? 'active' : ''}"
628
+ @click=${() => this.activeSection = 'images'}>Images</button>
629
+ <button class="nav-tab ${this.activeSection === 'logo' ? 'active' : ''}"
630
+ @click=${() => this.activeSection = 'logo'}>Logo</button>
631
+ <button class="nav-tab ${this.activeSection === 'static' ? 'active' : ''}"
632
+ @click=${() => {
633
+ this.activeSection = 'static';
634
+ this.fetchStaticDetails();
635
+ }}>Site Settings</button>
636
+ </div>
637
+
638
+ ${this.statusMessage ? html`
639
+ <div class="status-message ${this.statusMessage.includes('successful') || this.statusMessage.includes('cleared') ? 'success' : this.statusMessage.includes('failed') || this.statusMessage.includes('Error') ? 'error' : ''}">
640
+ ${this.statusMessage}
641
+ </div>
642
+ ` : ''}
643
+
644
+ ${this.activeSection === 'home' ? this.renderHomeSection() : ''}
645
+ ${this.activeSection === 'profile' ? this.renderProfileSection() : ''}
646
+ ${this.activeSection === 'aboutme' ? this.renderAboutMeSection() : ''}
647
+ ${this.activeSection === 'blogs' ? this.renderBlogsSection() : ''}
648
+ ${this.activeSection === 'stories' ? this.renderStoriesSection() : ''}
649
+ ${this.activeSection === 'images' ? this.renderImagesSection() : ''}
650
+ ${this.activeSection === 'logo' ? this.renderLogoSection() : ''}
651
+ ${this.activeSection === 'static' ? this.renderStaticSection() : ''}
652
+ </div>
653
+ `;
654
+ }
492
655
  }