mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
refactor: use useSearch in views
This commit is contained in:
parent
501bce484a
commit
8bb2451e9c
7 changed files with 122 additions and 155 deletions
|
@ -7,8 +7,10 @@ import CardDeckFeed from '@/components/CardDeckFeed.vue'
|
||||||
import { useForm } from '@/composables/form'
|
import { useForm } from '@/composables/form'
|
||||||
import { useAutoModal } from '@/composables/useAutoModal'
|
import { useAutoModal } from '@/composables/useAutoModal'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
|
import { useSearch } from '@/composables/useSearch'
|
||||||
import { randint } from '@/helpers/commons'
|
import { randint } from '@/helpers/commons'
|
||||||
import { appRepoUrl, required } from '@/helpers/validators'
|
import { appRepoUrl, required } from '@/helpers/validators'
|
||||||
|
import type { Obj } from '@/types/commons'
|
||||||
import type { FieldProps, FormFieldDict } from '@/types/form'
|
import type { FieldProps, FormFieldDict } from '@/types/form'
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
|
@ -35,10 +37,42 @@ const { loading } = useInitialQueries(
|
||||||
{ onQueriesResponse },
|
{ onQueriesResponse },
|
||||||
)
|
)
|
||||||
|
|
||||||
const apps = ref()
|
|
||||||
const selectedApp = ref()
|
const selectedApp = ref()
|
||||||
const antifeatures = ref()
|
const antifeatures = ref()
|
||||||
|
|
||||||
|
const apps = ref<Obj[] | undefined>()
|
||||||
|
const [search, filteredApps] = useSearch(
|
||||||
|
apps,
|
||||||
|
(s, app) => {
|
||||||
|
// app doesn't match quality filter
|
||||||
|
if (props.quality !== 'all' && !app[props.quality]) return false
|
||||||
|
// app doesn't match category filter
|
||||||
|
if (props.category !== 'all' && app.category !== props.category)
|
||||||
|
return false
|
||||||
|
if (props.subtag !== 'all') {
|
||||||
|
const appMatchSubtag =
|
||||||
|
props.subtag === 'others'
|
||||||
|
? app.subtags.length === 0
|
||||||
|
: app.subtags.includes(props.subtag)
|
||||||
|
// app doesn't match subtag filter
|
||||||
|
if (!appMatchSubtag) return false
|
||||||
|
}
|
||||||
|
if (s === '') return true
|
||||||
|
if (app.searchValues.includes(search)) return true
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
externalSearch: () => props.search,
|
||||||
|
filterIfNoSearch: true,
|
||||||
|
filterAllFn(s) {
|
||||||
|
if (props.category === null) return false
|
||||||
|
if (props.quality === 'all' && props.category === 'all' && s === '') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
const form = ref({ url: '' })
|
const form = ref({ url: '' })
|
||||||
const fields = {
|
const fields = {
|
||||||
url: {
|
url: {
|
||||||
|
@ -68,34 +102,6 @@ const categories = reactive([
|
||||||
// The rest is filled from api data
|
// The rest is filled from api data
|
||||||
])
|
])
|
||||||
|
|
||||||
const filteredApps = computed(() => {
|
|
||||||
if (!apps.value || props.category === null) return
|
|
||||||
const search = props.search.toLowerCase()
|
|
||||||
|
|
||||||
if (props.quality === 'all' && props.category === 'all' && search === '') {
|
|
||||||
return apps.value
|
|
||||||
}
|
|
||||||
const filtered = apps.value.filter((app) => {
|
|
||||||
// app doesn't match quality filter
|
|
||||||
if (props.quality !== 'all' && !app[props.quality]) return false
|
|
||||||
// app doesn't match category filter
|
|
||||||
if (props.category !== 'all' && app.category !== props.category)
|
|
||||||
return false
|
|
||||||
if (props.subtag !== 'all') {
|
|
||||||
const appMatchSubtag =
|
|
||||||
props.subtag === 'others'
|
|
||||||
? app.subtags.length === 0
|
|
||||||
: app.subtags.includes(props.subtag)
|
|
||||||
// app doesn't match subtag filter
|
|
||||||
if (!appMatchSubtag) return false
|
|
||||||
}
|
|
||||||
if (search === '') return true
|
|
||||||
if (app.searchValues.includes(search)) return true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
return filtered.length ? filtered : null
|
|
||||||
})
|
|
||||||
|
|
||||||
const subtags = computed(() => {
|
const subtags = computed(() => {
|
||||||
// build an options array for subtags v-model/options
|
// build an options array for subtags v-model/options
|
||||||
if (props.category && categories.length > 2) {
|
if (props.category && categories.length > 2) {
|
||||||
|
@ -196,8 +202,8 @@ const onCustomInstallClick = onSubmit(async () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ViewSearch
|
<ViewSearch
|
||||||
:filtered-items="filteredApps"
|
v-model="search"
|
||||||
:items="apps"
|
:items="filteredApps"
|
||||||
items-name="apps"
|
items-name="apps"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
|
@ -211,15 +217,15 @@ const onCustomInstallClick = onSubmit(async () => {
|
||||||
|
|
||||||
<BFormInput
|
<BFormInput
|
||||||
id="search-input"
|
id="search-input"
|
||||||
|
:model-value="search"
|
||||||
:placeholder="$t('search.for', { items: $t('items.apps', 2) })"
|
:placeholder="$t('search.for', { items: $t('items.apps', 2) })"
|
||||||
:modelValue="search"
|
@update:model-value="updateQuery('search', $event)"
|
||||||
@update:modelValue="updateQuery('search', $event)"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BFormSelect
|
<BFormSelect
|
||||||
:modelValue="quality"
|
:model-value="quality"
|
||||||
:options="qualityOptions"
|
:options="qualityOptions"
|
||||||
@update:modelValue="updateQuery('quality', $event)"
|
@update:model-value="updateQuery('quality', $event)"
|
||||||
/>
|
/>
|
||||||
</BInputGroup>
|
</BInputGroup>
|
||||||
|
|
||||||
|
@ -230,9 +236,9 @@ const onCustomInstallClick = onSubmit(async () => {
|
||||||
</BInputGroupText>
|
</BInputGroupText>
|
||||||
|
|
||||||
<BFormSelect
|
<BFormSelect
|
||||||
:modelValue="category"
|
:model-value="category"
|
||||||
:options="categories"
|
:options="categories"
|
||||||
@update:modelValue="updateQuery('category', $event)"
|
@update:model-value="updateQuery('category', $event)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BButton
|
<BButton
|
||||||
|
@ -260,9 +266,9 @@ const onCustomInstallClick = onSubmit(async () => {
|
||||||
|
|
||||||
<BFormSelect
|
<BFormSelect
|
||||||
id="subtags-select"
|
id="subtags-select"
|
||||||
:modelValue="subtag"
|
:model-value="subtag"
|
||||||
:options="subtags"
|
:options="subtags"
|
||||||
@update:modelValue="updateQuery('subtag', $event)"
|
@update:model-value="updateQuery('subtag', $event)"
|
||||||
/>
|
/>
|
||||||
</BInputGroup>
|
</BInputGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,29 +1,24 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
|
import { useSearch } from '@/composables/useSearch'
|
||||||
|
import type { Obj } from '@/types/commons'
|
||||||
|
|
||||||
const { loading } = useInitialQueries([['GET', 'apps?full']], {
|
const { loading } = useInitialQueries([['GET', 'apps?full']], {
|
||||||
onQueriesResponse,
|
onQueriesResponse,
|
||||||
})
|
})
|
||||||
const search = ref('')
|
|
||||||
const apps = ref()
|
|
||||||
|
|
||||||
const filteredApps = computed(() => {
|
const apps = ref<Obj[] | undefined>()
|
||||||
if (!apps.value) return
|
const [search, filteredApps] = useSearch(apps, (s, app) => {
|
||||||
const search_ = search.value.toLowerCase()
|
return Object.values(app).some(
|
||||||
// Check if any value in apps (label, id, name, description) match the search query.
|
(value) => value && value.toLowerCase().includes(s),
|
||||||
const filtered = apps.value.filter((app) =>
|
|
||||||
Object.values(app).some(
|
|
||||||
(item) => item && item.toLowerCase().includes(search_),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
return filtered.length ? filtered : null
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function onQueriesResponse(data: any) {
|
function onQueriesResponse(data: any) {
|
||||||
if (data.apps.length === 0) {
|
if (data.apps.length === 0) {
|
||||||
apps.value = null
|
apps.value = undefined
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +34,9 @@ function onQueriesResponse(data: any) {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ViewSearch
|
<ViewSearch
|
||||||
v-model:search="search"
|
v-model="search"
|
||||||
:filtered-items="filteredApps"
|
|
||||||
items-name="installed_apps"
|
items-name="installed_apps"
|
||||||
:items="apps"
|
:items="filteredApps"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template #top-bar-buttons>
|
<template #top-bar-buttons>
|
||||||
|
|
|
@ -1,41 +1,29 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useStoreGetters } from '@/store/utils'
|
import { useStoreGetters } from '@/store/utils'
|
||||||
import { computed, ref } from 'vue'
|
|
||||||
|
|
||||||
import RecursiveListGroup from '@/components/RecursiveListGroup.vue'
|
import RecursiveListGroup from '@/components/RecursiveListGroup.vue'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
|
import { useSearch } from '@/composables/useSearch'
|
||||||
import type { TreeRootNode } from '@/helpers/data/tree'
|
import type { TreeRootNode } from '@/helpers/data/tree'
|
||||||
|
import type { ComputedRef } from 'vue'
|
||||||
|
|
||||||
const { domains, mainDomain, domainsTree } = useStoreGetters()
|
const { mainDomain, domainsTree } = useStoreGetters()
|
||||||
const { loading } = useInitialQueries([
|
const { loading } = useInitialQueries([
|
||||||
['GET', { uri: 'domains', storeKey: 'domains' }],
|
['GET', { uri: 'domains', storeKey: 'domains' }],
|
||||||
])
|
])
|
||||||
|
|
||||||
const search = ref('')
|
const [search, filteredTree] = useSearch(
|
||||||
|
|
||||||
const tree = computed(() => {
|
|
||||||
// FIXME rm ts type when moved to pinia or else
|
// FIXME rm ts type when moved to pinia or else
|
||||||
const tree = domainsTree.value as TreeRootNode | undefined
|
domainsTree as ComputedRef<TreeRootNode | undefined>,
|
||||||
if (!tree) return
|
(s, node) => node.id.includes(s),
|
||||||
const search_ = search.value.toLowerCase()
|
)
|
||||||
if (search_) {
|
|
||||||
return tree.filter((node) => node.id.includes(search_))
|
|
||||||
}
|
|
||||||
return tree
|
|
||||||
})
|
|
||||||
|
|
||||||
const hasFilteredItems = computed(() => {
|
|
||||||
if (!tree.value) return null
|
|
||||||
return tree.value.children.length ? tree.value.children : null
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ViewSearch
|
<ViewSearch
|
||||||
id="domain-list"
|
id="domain-list"
|
||||||
v-model:search="search"
|
v-model="search"
|
||||||
:filtered-items="hasFilteredItems"
|
:items="filteredTree ? filteredTree.children : filteredTree"
|
||||||
:items="domains"
|
|
||||||
items-name="domains"
|
items-name="domains"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
|
@ -47,8 +35,8 @@ const hasFilteredItems = computed(() => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<RecursiveListGroup
|
<RecursiveListGroup
|
||||||
v-if="tree"
|
v-if="filteredTree"
|
||||||
:tree="tree"
|
:tree="filteredTree"
|
||||||
:toggle-text="$t('domain.toggle_subdomains')"
|
:toggle-text="$t('domain.toggle_subdomains')"
|
||||||
class="mb-5"
|
class="mb-5"
|
||||||
>
|
>
|
||||||
|
@ -67,9 +55,9 @@ const hasFilteredItems = computed(() => {
|
||||||
|
|
||||||
<small
|
<small
|
||||||
v-if="data.name === mainDomain"
|
v-if="data.name === mainDomain"
|
||||||
|
v-b-tooltip.hover
|
||||||
:title="$t('domain.types.main_domain')"
|
:title="$t('domain.types.main_domain')"
|
||||||
class="ms-1"
|
class="ms-1"
|
||||||
v-b-tooltip.hover
|
|
||||||
>
|
>
|
||||||
<YIcon iname="star" />
|
<YIcon iname="star" />
|
||||||
</small>
|
</small>
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import TagsSelectizeItem from '@/components/globals/formItems/TagsSelectizeItem.vue'
|
import TagsSelectizeItem from '@/components/globals/formItems/TagsSelectizeItem.vue'
|
||||||
import { useAutoModal } from '@/composables/useAutoModal'
|
import { useAutoModal } from '@/composables/useAutoModal'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
|
import { useSearch } from '@/composables/useSearch'
|
||||||
import { isEmptyValue } from '@/helpers/commons'
|
import { isEmptyValue } from '@/helpers/commons'
|
||||||
|
import type { Obj } from '@/types/commons'
|
||||||
|
|
||||||
// TODO add global search with type (search by: group, user, permission)
|
// TODO add global search with type (search by: group, user, permission)
|
||||||
// TODO add vuex store update on inputs ?
|
// TODO add vuex store update on inputs ?
|
||||||
|
@ -28,25 +30,15 @@ const { loading } = useInitialQueries(
|
||||||
{ onQueriesResponse },
|
{ onQueriesResponse },
|
||||||
)
|
)
|
||||||
|
|
||||||
const search = ref('')
|
|
||||||
const permissions = ref()
|
const permissions = ref()
|
||||||
const permissionsOptions = ref()
|
const permissionsOptions = ref()
|
||||||
const primaryGroups = ref()
|
const primaryGroups = ref<Obj[] | undefined>()
|
||||||
const userGroups = ref()
|
const userGroups = ref()
|
||||||
const usersOptions = ref()
|
const usersOptions = ref()
|
||||||
const activeUserGroups = ref()
|
const activeUserGroups = ref()
|
||||||
|
|
||||||
const filteredGroups = computed(() => {
|
const [search, filteredGroups] = useSearch(primaryGroups, (s, group) => {
|
||||||
const groups = primaryGroups.value
|
return group.name.toLowerCase().includes(s)
|
||||||
if (!groups) return
|
|
||||||
const search_ = search.value.toLowerCase()
|
|
||||||
const filtered = {}
|
|
||||||
for (const groupName in groups) {
|
|
||||||
if (groupName.toLowerCase().includes(search_)) {
|
|
||||||
filtered[groupName] = groups[groupName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isEmptyValue(filtered) ? null : filtered
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function onQueriesResponse(users: any, allGroups: any, permsDict: any) {
|
function onQueriesResponse(users: any, allGroups: any, permsDict: any) {
|
||||||
|
@ -57,12 +49,12 @@ function onQueriesResponse(users: any, allGroups: any, permsDict: any) {
|
||||||
...value,
|
...value,
|
||||||
}))
|
}))
|
||||||
const userNames = users ? Object.keys(users) : []
|
const userNames = users ? Object.keys(users) : []
|
||||||
const primaryGroups_ = {}
|
const primaryGroups_ = []
|
||||||
const userGroups_ = {}
|
const userGroups_ = {}
|
||||||
|
|
||||||
for (const groupName in allGroups) {
|
for (const groupName in allGroups) {
|
||||||
// copy the group to unlink it from the store
|
// copy the group to unlink it from the store
|
||||||
const group_ = { ...allGroups[groupName] }
|
const group_ = { ...allGroups[groupName], name: groupName }
|
||||||
group_.permissions = group_.permissions.map((perm) => {
|
group_.permissions = group_.permissions.map((perm) => {
|
||||||
return permsDict[perm].label
|
return permsDict[perm].label
|
||||||
})
|
})
|
||||||
|
@ -103,7 +95,7 @@ function onQueriesResponse(users: any, allGroups: any, permsDict: any) {
|
||||||
.map(({ id }) => permsDict[id].label)
|
.map(({ id }) => permsDict[id].label)
|
||||||
}
|
}
|
||||||
|
|
||||||
primaryGroups_[groupName] = group_
|
primaryGroups_.push(group_)
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeUserGroups_ = Object.entries(userGroups_)
|
const activeUserGroups_ = Object.entries(userGroups_)
|
||||||
|
@ -179,16 +171,17 @@ async function deleteGroup(groupName) {
|
||||||
{ key: 'groups.delete', name: groupName },
|
{ key: 'groups.delete', name: groupName },
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
delete primaryGroups.value[groupName]
|
primaryGroups.value = primaryGroups.value?.filter(
|
||||||
|
(group) => group.name !== groupName,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ViewSearch
|
<ViewSearch
|
||||||
v-model:search="search"
|
v-model="search"
|
||||||
:filtered-items="filteredGroups"
|
:items="filteredGroups"
|
||||||
:items="primaryGroups"
|
|
||||||
items-name="groups"
|
items-name="groups"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
skeleton="CardFormSkeleton"
|
skeleton="CardFormSkeleton"
|
||||||
|
@ -201,13 +194,13 @@ async function deleteGroup(groupName) {
|
||||||
|
|
||||||
<!-- PRIMARY GROUPS CARDS -->
|
<!-- PRIMARY GROUPS CARDS -->
|
||||||
<YCard
|
<YCard
|
||||||
v-for="(group, groupName) in filteredGroups"
|
v-for="group in filteredGroups"
|
||||||
:key="groupName"
|
:key="group.name"
|
||||||
collapsable
|
collapsable
|
||||||
:title="
|
:title="
|
||||||
group.isSpecial
|
group.isSpecial
|
||||||
? $t('group_' + groupName)
|
? $t('group_' + group.name)
|
||||||
: `${$t('group')} '${groupName}'`
|
: `${$t('group')} '${group.name}'`
|
||||||
"
|
"
|
||||||
icon="group"
|
icon="group"
|
||||||
>
|
>
|
||||||
|
@ -215,7 +208,7 @@ async function deleteGroup(groupName) {
|
||||||
<!-- DELETE GROUP -->
|
<!-- DELETE GROUP -->
|
||||||
<BButton
|
<BButton
|
||||||
v-if="!group.isSpecial"
|
v-if="!group.isSpecial"
|
||||||
@click="deleteGroup(groupName)"
|
@click="deleteGroup(group.name)"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
>
|
>
|
||||||
|
@ -231,23 +224,23 @@ async function deleteGroup(groupName) {
|
||||||
<template v-if="group.isSpecial">
|
<template v-if="group.isSpecial">
|
||||||
<p class="text-primary-emphasis">
|
<p class="text-primary-emphasis">
|
||||||
<YIcon iname="info-circle" />
|
<YIcon iname="info-circle" />
|
||||||
{{ $t('group_explain_' + groupName) }}
|
{{ $t('group_explain_' + group.name) }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-primary-emphasis" v-if="groupName === 'visitors'">
|
<p class="text-primary-emphasis" v-if="group.name === 'visitors'">
|
||||||
<em>{{
|
<em>{{
|
||||||
$t('group_explain_visitors_needed_for_external_client')
|
$t('group_explain_visitors_needed_for_external_client')
|
||||||
}}</em>
|
}}</em>
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="groupName == 'admins' || !group.isSpecial">
|
<template v-if="group.name == 'admins' || !group.isSpecial">
|
||||||
<TagsSelectizeItem
|
<TagsSelectizeItem
|
||||||
v-model="group.members"
|
v-model="group.members"
|
||||||
:options="usersOptions"
|
:options="usersOptions"
|
||||||
:id="groupName + '-users'"
|
:id="group.name + '-users'"
|
||||||
:label="$t('group_add_member')"
|
:label="$t('group_add_member')"
|
||||||
tag-icon="user"
|
tag-icon="user"
|
||||||
items-name="users"
|
items-name="users"
|
||||||
@tag-update="onUserChanged({ ...$event, groupName })"
|
@tag-update="onUserChanged({ ...$event, groupName: group.name })"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</BCol>
|
</BCol>
|
||||||
|
@ -262,11 +255,13 @@ async function deleteGroup(groupName) {
|
||||||
<TagsSelectizeItem
|
<TagsSelectizeItem
|
||||||
v-model="group.permissions"
|
v-model="group.permissions"
|
||||||
:options="permissionsOptions"
|
:options="permissionsOptions"
|
||||||
:id="groupName + '-perms'"
|
:id="group.name + '-perms'"
|
||||||
:label="$t('group_add_permission')"
|
:label="$t('group_add_permission')"
|
||||||
tag-icon="key-modern"
|
tag-icon="key-modern"
|
||||||
items-name="permissions"
|
items-name="permissions"
|
||||||
@tag-update="onPermissionChanged({ ...$event, groupName })"
|
@tag-update="
|
||||||
|
onPermissionChanged({ ...$event, groupName: group.name })
|
||||||
|
"
|
||||||
:disabled-items="group.disabledItems"
|
:disabled-items="group.disabledItems"
|
||||||
/>
|
/>
|
||||||
</BCol>
|
</BCol>
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
|
import { useSearch } from '@/composables/useSearch'
|
||||||
import { distanceToNow } from '@/helpers/filters/date'
|
import { distanceToNow } from '@/helpers/filters/date'
|
||||||
|
import type { Obj } from '@/types/commons'
|
||||||
|
|
||||||
const { loading } = useInitialQueries([['GET', 'services']], {
|
const { loading } = useInitialQueries([['GET', 'services']], {
|
||||||
onQueriesResponse,
|
onQueriesResponse,
|
||||||
})
|
})
|
||||||
const search = ref('')
|
|
||||||
const services = ref()
|
|
||||||
|
|
||||||
const filteredServices = computed(() => {
|
const services = ref<Obj[] | undefined>()
|
||||||
if (!services.value) return
|
const [search, filteredServices] = useSearch(services, (s, service) => {
|
||||||
const services_ = services.value.filter(({ name }) => {
|
return service.name.toLowerCase().includes(s)
|
||||||
return name.toLowerCase().includes(search.value.toLowerCase())
|
|
||||||
})
|
|
||||||
return services_.length ? services_ : null
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function onQueriesResponse(services_: any) {
|
function onQueriesResponse(services_: any) {
|
||||||
|
@ -34,9 +31,8 @@ function onQueriesResponse(services_: any) {
|
||||||
<template>
|
<template>
|
||||||
<ViewSearch
|
<ViewSearch
|
||||||
id="service-list"
|
id="service-list"
|
||||||
v-model:search="search"
|
v-model="search"
|
||||||
:filtered-items="filteredServices"
|
:items="filteredServices"
|
||||||
:items="services"
|
|
||||||
items-name="services"
|
items-name="services"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
|
import { useSearch } from '@/composables/useSearch'
|
||||||
import { distanceToNow, readableDate } from '@/helpers/filters/date'
|
import { distanceToNow, readableDate } from '@/helpers/filters/date'
|
||||||
|
import type { Obj } from '@/types/commons'
|
||||||
|
|
||||||
const { loading } = useInitialQueries(
|
const { loading } = useInitialQueries(
|
||||||
[['GET', `logs?limit=${25}&with_details`]],
|
[['GET', `logs?limit=${25}&with_details`]],
|
||||||
{ onQueriesResponse },
|
{ onQueriesResponse },
|
||||||
)
|
)
|
||||||
|
|
||||||
const search = ref('')
|
const operations = ref<Obj[] | undefined>()
|
||||||
const operations = ref()
|
const [search, filteredOperations] = useSearch(operations, (s, op) => {
|
||||||
|
return op.description.toLowerCase().includes(s)
|
||||||
const filteredOperations = computed(() => {
|
|
||||||
if (!operations.value) return
|
|
||||||
const search_ = search.value.toLowerCase()
|
|
||||||
const operations_ = operations.value.filter(({ description }) => {
|
|
||||||
return description.toLowerCase().includes(search_)
|
|
||||||
})
|
|
||||||
return operations_.length ? operations_ : null
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function onQueriesResponse({ operation }: any) {
|
function onQueriesResponse({ operation }: any) {
|
||||||
|
@ -40,9 +35,8 @@ function onQueriesResponse({ operation }: any) {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ViewSearch
|
<ViewSearch
|
||||||
v-model:search="search"
|
v-model="search"
|
||||||
:filtered-items="filteredOperations"
|
:items="filteredOperations"
|
||||||
:items="operations"
|
|
||||||
items-name="logs"
|
items-name="logs"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
skeleton="CardListSkeleton"
|
skeleton="CardListSkeleton"
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { type ComputedRef } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
|
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
|
import { useSearch } from '@/composables/useSearch'
|
||||||
import { useStoreGetters } from '@/store/utils'
|
import { useStoreGetters } from '@/store/utils'
|
||||||
|
import type { Obj } from '@/types/commons'
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const { loading } = useInitialQueries([
|
const { loading } = useInitialQueries([
|
||||||
|
@ -15,20 +17,13 @@ const { loading } = useInitialQueries([
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
const { users } = useStoreGetters()
|
|
||||||
|
|
||||||
const search = ref('')
|
const { users } = useStoreGetters()
|
||||||
const filteredUsers = computed(() => {
|
const [search, filteredUsers] = useSearch(
|
||||||
if (!users.value) return
|
users as ComputedRef<Obj[] | undefined>,
|
||||||
const search_ = search.value.toLowerCase()
|
(s, user) =>
|
||||||
const filtered = users.value.filter((user) => {
|
user.username.toLowerCase().includes(s) || user.groups.includes(s),
|
||||||
return (
|
)
|
||||||
user.username.toLowerCase().includes(search_) ||
|
|
||||||
user.groups.includes(search_)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return filtered.length === 0 ? null : filtered
|
|
||||||
})
|
|
||||||
|
|
||||||
function downloadExport() {
|
function downloadExport() {
|
||||||
const host = store.getters.host
|
const host = store.getters.host
|
||||||
|
@ -38,9 +33,8 @@ function downloadExport() {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ViewSearch
|
<ViewSearch
|
||||||
v-model:search="search"
|
v-model="search"
|
||||||
:filtered-items="filteredUsers"
|
:items="filteredUsers"
|
||||||
:items="users"
|
|
||||||
items-name="users"
|
items-name="users"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in a new issue