Add searchbox in settings page (#338)

* Add searchbox in setting page

Signed-off-by: Andy Lee <andy.lee@suse.com>

* update based on comment

Signed-off-by: Andy Lee <andy.lee@suse.com>

* PR feedback

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
Andy Lee 2025-06-17 14:50:52 +08:00 committed by GitHub
parent fb59b396d1
commit d9c97de0fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 131 additions and 10 deletions

View File

@ -30,15 +30,23 @@ export default {
category: { category: {
type: String, type: String,
required: true, required: true,
},
searchQuery: {
type: String,
default: ''
} }
}, },
data() { data() {
const categorySettings = this.filterCategorySettings(); const categorySettings = this.filterCategorySettings();
const filteredSettings = this.filterSearchSettings(categorySettings, this.searchQuery);
return { return {
HCI_SETTING, HCI_SETTING,
categorySettings, categorySettings,
filteredSettings,
originalHideMap: this.createHideMap(categorySettings)
}; };
}, },
@ -48,12 +56,81 @@ export default {
settings: { settings: {
deep: true, deep: true,
handler() { handler() {
this['categorySettings'] = this.filterCategorySettings(); this.categorySettings = this.filterCategorySettings();
this.filteredSettings = this.filterSearchSettings(this.categorySettings, this.searchQuery);
}
},
searchQuery: {
immediate: true,
handler(newQuery) {
const filtered = this.filterSearchSettings(this.categorySettings, newQuery);
this.filteredSettings = newQuery ? this.openJsonSettings(filtered) : filtered.map((s) => ({ ...s, hide: this.originalHideMap[s.id] ?? false }));
} }
} }
}, },
methods: { methods: {
createHideMap(settings = []) {
const map = settings.reduce((acc, s) => {
acc[s.id] = s.hide ?? false;
return acc;
}, {} );
return map;
},
filterSearchSettings(settings, searchKey) {
if (!searchKey) {
return this.filterCategorySettings();
}
const searchQuery = searchKey.toLowerCase();
return settings.filter((setting) => {
const id = setting.id?.toLowerCase() || '';
// filter by id
if (id.includes(searchQuery) ) {
return true;
}
const description = this.t(setting.description, {}, true)?.toLowerCase() || '';
// filter by description
if (description.includes(searchQuery)) {
return true;
}
// filter by customized value
if (setting.customized === true && setting.data?.value) {
const value = setting.data.value?.toLowerCase() || '';
return value.includes(searchQuery);
}
// filter by json value
if (setting.kind === 'json' && setting.json) {
try {
const json = JSON.parse(setting.json);
const jsonString = JSON.stringify(json).toLowerCase();
return jsonString.includes(searchQuery);
} catch (e) {
console.error(`${ setting.id }: wrong format`, e); // eslint-disable-line no-console
return false;
}
}
// filter by default value
if (setting.data?.default) {
return setting.data?.default.includes(searchQuery);
}
return false;
});
},
filterCategorySettings() { filterCategorySettings() {
return this.settings.filter((s) => { return this.settings.filter((s) => {
if (!this.getFeatureEnabled(s.featureFlag)) { if (!this.getFeatureEnabled(s.featureFlag)) {
@ -87,12 +164,17 @@ export default {
return HCI_ALLOWED_SETTINGS.find((setting) => setting.id === id); return HCI_ALLOWED_SETTINGS.find((setting) => setting.id === id);
}, },
openJsonSettings(settings) {
return settings.map((s) => s.hide ? { ...s, hide: false } : s);
},
toggleHide(s) { toggleHide(s) {
this.categorySettings.find((setting) => { const setting = this.filteredSettings.find((setting) => setting.id === s.id);
if (setting.id === s.id) {
setting.hide = !setting.hide; if (setting) {
} setting.hide = !setting.hide;
}); this.originalHideMap[setting.id] = setting.hide;
}
}, },
async testConnect(buttonDone, value) { async testConnect(buttonDone, value) {
@ -126,7 +208,7 @@ export default {
<template> <template>
<div> <div>
<div <div
v-for="(setting, i) in categorySettings" v-for="(setting, i) in filteredSettings"
:key="i" :key="i"
class="advanced-setting mb-20" class="advanced-setting mb-20"
> >
@ -221,6 +303,12 @@ export default {
{{ setting.data.errMessage }} {{ setting.data.errMessage }}
</Banner> </Banner>
</div> </div>
<div
v-if="filteredSettings.length === 0"
class="advanced-setting mb-20 no-search-match"
>
<p> {{ t('harvester.setting.noSearchMatch') }} </p>
</div>
</div> </div>
</template> </template>
@ -271,4 +359,8 @@ export default {
padding: 2px 10px; padding: 2px 10px;
font-size: 12px; font-size: 12px;
} }
.no-search-match {
text-align: center;
}
</style> </style>

View File

@ -1041,6 +1041,7 @@ harvester:
accessKeyId: Specify your access key ID accessKeyId: Specify your access key ID
secretAccessKey: Specify your secret access key secretAccessKey: Specify your secret access key
cert: Upload a self-signed SSL certificate cert: Upload a self-signed SSL certificate
noSearchMatch: No settings match your search.
vlanChangeTip: The newly modified default network interface only applies to newly added nodes, not existing ones. vlanChangeTip: The newly modified default network interface only applies to newly added nodes, not existing ones.
defaultPhysicalNIC: Default Network Interface defaultPhysicalNIC: Default Network Interface
modifiedMessage: Settings that have been customized from default settings are tagged with 'Modified'. modifiedMessage: Settings that have been customized from default settings are tagged with 'Modified'.

View File

@ -85,7 +85,7 @@ export default {
}, },
data() { data() {
return { initSettings: [] }; return { initSettings: [], searchQuery: '' };
}, },
computed: { computed: {
@ -135,9 +135,19 @@ export default {
{{ t('harvester.setting.modifiedMessage') }} {{ t('harvester.setting.modifiedMessage') }}
</div> </div>
</Banner> </Banner>
<div class="fixed-header-actions harvester-settings-search">
<div class="search row">
<input
v-model="searchQuery"
type="search"
class="input-sm search-box"
:aria-label="t('sortableTable.searchLabel')"
:placeholder="t('sortableTable.search')"
>
</div>
</div>
<Tabbed <Tabbed
class="mt-30" class="mt-15"
> >
<Tab <Tab
name="advanced" name="advanced"
@ -146,6 +156,7 @@ export default {
> >
<Settings <Settings
:settings="settings" :settings="settings"
:search-query="searchQuery"
category="advanced" category="advanced"
/> />
</Tab> </Tab>
@ -156,6 +167,7 @@ export default {
> >
<Settings <Settings
:settings="settings" :settings="settings"
:search-query="searchQuery"
category="ui" category="ui"
/> />
</Tab> </Tab>
@ -167,4 +179,20 @@ export default {
.settings-banner { .settings-banner {
margin-top: 0; margin-top: 0;
} }
.harvester-settings-search {
padding: 0;
}
.search {
display: flex;
justify-content: flex-end;
}
.search-box {
height: 40px;
margin-left: 10px;
min-width: 180px;
}
</style> </style>