main
vue 235 lines 6.5 KB
Raw
1 <template>
2 <div class="@container flex flex-col gap-8">
3 <div class="flex w-full flex-col gap-4">
4 <div class="flex flex-col justify-between gap-4 @2xl:flex-row @2xl:items-center">
5 <h2 class="text-xl font-semibold">GitHub Audit Configurations</h2>
6 <div class="flex flex-wrap items-center gap-x-6 gap-y-2">
7 <n-button text @click="showInfo = true">
8 <template #icon>
9 <Icon :name="InfoIcon" />
10 </template>
11 Reference Guide
12 </n-button>
13 <n-button type="primary" @click="openCreateForm">
14 <template #icon>
15 <Icon :name="AddIcon" />
16 </template>
17 New Configuration
18 </n-button>
19 </div>
20 </div>
21
22 <div class="flex flex-wrap items-center gap-2">
23 <n-select
24 v-model:value="filterCustomerCode"
25 placeholder="Filter by Customer"
26 clearable
27 :options="customerOptions"
28 :loading="loadingCustomers"
29 class="min-w-50 flex-1"
30 @update:value="loadConfigs"
31 />
32 <n-select
33 v-model:value="filterStatus"
34 placeholder="Filter by Status"
35 clearable
36 :options="statusOptions"
37 class="min-w-50 flex-1"
38 @update:value="loadConfigs"
39 />
40 <n-input
41 v-model:value="filterOrganization"
42 placeholder="Search organization..."
43 clearable
44 class="min-w-80 flex-1"
45 @keyup.enter="loadConfigs"
46 @clear="loadConfigs"
47 >
48 <template #prefix>
49 <Icon :name="SearchIcon" />
50 </template>
51 </n-input>
52 </div>
53 </div>
54
55 <n-spin :show="loading">
56 <div v-if="configs.length === 0 && !loading" class="py-8 text-center">
57 <n-empty description="No configurations found">
58 <template #extra>
59 <n-button type="primary" @click="openCreateForm">Create your first configuration</n-button>
60 </template>
61 </n-empty>
62 </div>
63
64 <div v-else class="grid grid-cols-1 gap-4 @4xl:grid-cols-2">
65 <GitHubAuditCard
66 v-for="config in configs"
67 :key="config.id"
68 :config
69 @click="openDetail(config)"
70 @edit="openEditForm"
71 @audit-complete="loadConfigs"
72 />
73 </div>
74 </n-spin>
75
76 <n-drawer v-model:show="showForm" :width="600" placement="right" class="max-w-[98vw]">
77 <n-drawer-content
78 :title="selectedConfig ? 'Edit Configuration' : 'New GitHub Audit Configuration'"
79 closable
80 :native-scrollbar="false"
81 >
82 <GitHubAuditConfigForm
83 v-if="showForm"
84 :config="selectedConfig"
85 @saved="onConfigSaved"
86 @cancel="showForm = false"
87 />
88 </n-drawer-content>
89 </n-drawer>
90
91 <n-drawer v-model:show="showDetail" :width="800" placement="right" class="max-w-[98vw]">
92 <n-drawer-content v-if="selectedConfig" closable :native-scrollbar="false">
93 <template #header>
94 <div class="flex items-center gap-3">
95 <Icon name="codicon:organization" :size="24" />
96 <span>{{ selectedConfig.organization }}</span>
97 <n-tag v-if="!selectedConfig.enabled" type="warning" size="small">Disabled</n-tag>
98 </div>
99 </template>
100
101 <GitHubAuditDetail
102 :config="selectedConfig"
103 @updated="loadConfigs"
104 @edit="openEditForm"
105 @close="showDetail = false"
106 />
107 </n-drawer-content>
108 </n-drawer>
109
110 <n-drawer v-model:show="showInfo" :width="700" placement="right" class="max-w-[98vw]">
111 <n-drawer-content closable :native-scrollbar="false">
112 <template #header>
113 <div class="flex items-center gap-3">
114 <Icon :name="InfoIcon" :size="24" />
115 <span>GitHub Audit Reference Guide</span>
116 </div>
117 </template>
118
119 <GitHubAuditInfo />
120 </n-drawer-content>
121 </n-drawer>
122 </div>
123 </template>
124
125 <script setup lang="ts">
126 import type { GitHubAuditConfig } from "@/types/githubAudit.d"
127 import { NButton, NDrawer, NDrawerContent, NEmpty, NInput, NSelect, NSpin, NTag, useMessage } from "naive-ui"
128 import { onBeforeMount, ref } from "vue"
129 import Api from "@/api"
130 import Icon from "@/components/common/Icon.vue"
131 import GitHubAuditCard from "./GitHubAuditCard.vue"
132 import GitHubAuditConfigForm from "./GitHubAuditConfigForm.vue"
133 import GitHubAuditDetail from "./GitHubAuditDetail.vue"
134 import GitHubAuditInfo from "./GitHubAuditInfo.vue"
135
136 const AddIcon = "ion:add"
137 const SearchIcon = "ion:search-outline"
138 const InfoIcon = "ion:information-circle-outline"
139
140 const message = useMessage()
141 const loading = ref(false)
142 const configs = ref<GitHubAuditConfig[]>([])
143 const showForm = ref(false)
144 const showDetail = ref(false)
145 const showInfo = ref(false)
146 const selectedConfig = ref<GitHubAuditConfig | null>(null)
147
148 // Filters
149 const filterCustomerCode = ref<string | null>(null)
150 const filterStatus = ref<string | null>(null)
151 const filterOrganization = ref<string | null>(null)
152 const customerOptions = ref<{ label: string; value: string }[]>([])
153 const loadingCustomers = ref(false)
154
155 const statusOptions = [
156 { label: "Enabled", value: "enabled" },
157 { label: "Disabled", value: "disabled" }
158 ]
159
160 async function loadCustomers() {
161 if (loadingCustomers.value) return
162
163 loadingCustomers.value = true
164 try {
165 const response = await Api.customers.getCustomers()
166 if (response.data.customers) {
167 customerOptions.value = response.data.customers.map((c: any) => ({
168 label: `${c.customer_name} (${c.customer_code})`,
169 value: c.customer_code
170 }))
171 }
172 } catch (error) {
173 console.error("Failed to load customers:", error)
174 } finally {
175 loadingCustomers.value = false
176 }
177 }
178
179 async function loadConfigs() {
180 if (loading.value) return
181
182 loading.value = true
183 try {
184 const response = await Api.githubAudit.getConfigs(filterCustomerCode.value || undefined)
185 if (response.data.configs) {
186 let filteredConfigs = response.data.configs
187
188 if (filterStatus.value) {
189 const isEnabled = filterStatus.value === "enabled"
190 filteredConfigs = filteredConfigs.filter((c: GitHubAuditConfig) => c.enabled === isEnabled)
191 }
192
193 if (filterOrganization.value) {
194 const searchTerm = filterOrganization.value.toLowerCase()
195 filteredConfigs = filteredConfigs.filter((c: GitHubAuditConfig) =>
196 c.organization.toLowerCase().includes(searchTerm)
197 )
198 }
199
200 configs.value = filteredConfigs
201 }
202 } catch (error: any) {
203 message.error(error.response?.data?.detail || "Failed to load configurations")
204 configs.value = []
205 } finally {
206 loading.value = false
207 }
208 }
209
210 function openCreateForm() {
211 selectedConfig.value = null
212 showForm.value = true
213 }
214
215 function openEditForm(config: GitHubAuditConfig) {
216 selectedConfig.value = config
217 showDetail.value = false
218 showForm.value = true
219 }
220
221 function openDetail(config: GitHubAuditConfig) {
222 selectedConfig.value = config
223 showDetail.value = true
224 }
225
226 function onConfigSaved() {
227 showForm.value = false
228 loadConfigs()
229 }
230
231 onBeforeMount(() => {
232 loadCustomers()
233 loadConfigs()
234 })
235 </script>