main
js 132 lines 3.3 KB
Raw
1 import { createStore } from "/js/AlpineStore.js";
2 import { store as fileBrowserStore } from "/components/modals/file-browser/file-browser-store.js";
3
4 const fetchApi = globalThis.fetchApi;
5
6 function matchesSearchQuery(query, values) {
7 const normalized = String(query || "").trim().toLowerCase();
8 if (!normalized) return true;
9 return values.some((value) => String(value ?? "").toLowerCase().includes(normalized));
10 }
11
12 const model = {
13 loading: false,
14 error: "",
15 skills: [],
16 projects: [],
17 projectName: "",
18 skillSearch: "",
19
20 async init() {
21 this.resetState();
22 await this.loadProjects();
23 await this.loadSkills();
24 },
25
26 resetState() {
27 this.loading = false;
28 this.error = "";
29 this.skills = [];
30 this.projects = [];
31 this.projectName = "";
32 this.skillSearch = "";
33 },
34
35 onClose() {
36 this.resetState();
37 },
38
39 async loadProjects() {
40 try {
41 const response = await fetchApi("/projects", {
42 method: "POST",
43 headers: { "Content-Type": "application/json" },
44 body: JSON.stringify({ action: "list_options" }),
45 });
46 const data = await response.json().catch(() => ({}));
47 this.projects = data.ok ? (data.data || []) : [];
48 } catch (e) {
49 console.error("Failed to load projects:", e);
50 this.projects = [];
51 }
52 },
53
54 async loadSkills() {
55 try {
56 this.loading = true;
57 this.error = "";
58 const response = await fetchApi("/skills", {
59 method: "POST",
60 headers: { "Content-Type": "application/json" },
61 body: JSON.stringify({
62 action: "list",
63 project_name: this.projectName || null,
64 }),
65 });
66 const result = await response.json().catch(() => ({}));
67 if (!result.ok) {
68 this.error = result.error || "Failed to load skills";
69 this.skills = [];
70 return;
71 }
72 this.skills = Array.isArray(result.data) ? result.data : [];
73 } catch (e) {
74 this.error = e?.message || "Failed to load skills";
75 this.skills = [];
76 } finally {
77 this.loading = false;
78 }
79 },
80
81 get filteredSkills() {
82 return this.skills.filter((skill) => matchesSearchQuery(this.skillSearch, [
83 skill.name,
84 skill.description,
85 skill.path,
86 skill.scope,
87 skill.project_name,
88 ]));
89 },
90
91 get skillSearchActive() {
92 return !!String(this.skillSearch || "").trim();
93 },
94
95 clearSkillSearch() {
96 this.skillSearch = "";
97 },
98
99 async deleteSkill(skill) {
100 if (!skill) return;
101 try {
102 const response = await fetchApi("/skills", {
103 method: "POST",
104 headers: { "Content-Type": "application/json" },
105 body: JSON.stringify({
106 action: "delete",
107 skill_path: skill.path,
108 }),
109 });
110 const result = await response.json().catch(() => ({}));
111 if (!result.ok) {
112 throw new Error(result.error || "Delete failed");
113 }
114 if (window.toastFrontendSuccess) {
115 window.toastFrontendSuccess("Skill deleted", "Skills");
116 }
117 await this.loadSkills();
118 } catch (e) {
119 const msg = e?.message || "Delete failed";
120 if (window.toastFrontendError) {
121 window.toastFrontendError(msg, "Skills");
122 }
123 }
124 },
125
126 async openSkill(skill) {
127 await fileBrowserStore.open(skill.path);
128 },
129 };
130
131 const store = createStore("skillsListStore", model);
132 export { store };