Skip to content
Merged
253 changes: 253 additions & 0 deletions ui/src/components/SnmpDataCollection/SourcesTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
<template>
<TableCard class="snmp-data-collection-source-table">
<div class="header">
<div class="title-container">
<h2 class="title">Data Collection Sources</h2>
</div>
<div class="action-container">
<div class="search-container">
<FeatherInput
label="Search"
type="search"
data-test="search-input"
v-model.trim="store.sourcesSearchTerm"
:hint="'Search by Source, Vendor, UEI or Label'"
@update:modelValue.self="((e: string) => onChangeSearchTerm(e))"
>
<template #pre>
<FeatherIcon :icon="Search" />
</template>
</FeatherInput>
</div>
<div class="refresh">
<FeatherButton
primary
icon="Refresh"
data-test="refresh-button"
@click="store.refreshSourcesfilters()"
>
<FeatherIcon :icon="Refresh"> </FeatherIcon>
</FeatherButton>
</div>
</div>
</div>
<div class="container">
<table
class="data-table"
aria-label="Events Table"
v-if="store.sources.length"
>
<thead>
<tr>
<FeatherSortHeader
v-for="col of columns"
:key="col.label"
scope="col"
:property="col.id"
:sort="(sort as any)[col.id]"
v-on:sort-changed="sortChanged"
>
{{ col.label }}
</FeatherSortHeader>
<th>Actions</th>
</tr>
</thead>
<TransitionGroup
name="data-table"
tag="tbody"
>
<tr
v-for="source in store.sources"
:key="source.id"
>
<td>{{ source.name }}</td>
<td>{{ source.vendor }}</td>
<td>{{ source.uploadedBy }}</td>
<td>{{ source.enabled ? 'Enabled' : 'Disabled' }}</td>
<td>
<FeatherButton
icon="View Details"
data-test="view-button"
@click="onSourceClick(source)"
>
<FeatherIcon :icon="ViewDetails"> </FeatherIcon>
</FeatherButton>
<FeatherButton
icon="Download XML"
data-test="download-button"
>
<FeatherIcon :icon="Download"> </FeatherIcon>
</FeatherButton>
<FeatherDropdown>
<template v-slot:trigger="{ attrs, on }">
<FeatherButton
link
href="#"
v-bind="attrs"
v-on="on"
:icon="`More actions for ${source.name}`"
>
<FeatherIcon :icon="MenuIcon" />
</FeatherButton>
</template>
<FeatherDropdownItem data-test="edit-source-button"> Edit Source </FeatherDropdownItem>
<FeatherDropdownItem data-test="delete-source-button"> Delete Source </FeatherDropdownItem>
</FeatherDropdown>
</td>
</tr>
</TransitionGroup>
</table>
<div v-if="!store.sources.length">
<EmptyList
:content="emptyListContent"
data-test="empty-list"
/>
</div>
</div>
</TableCard>
</template>

<script lang="ts" setup>
import { useSnmpDataCollectionStore } from '@/stores/snmpDataCollectionStore'
import { FeatherButton } from '@featherds/button'
import { FeatherDropdown, FeatherDropdownItem } from '@featherds/dropdown'
import { FeatherIcon } from '@featherds/icon'
import Download from '@featherds/icon/action/DownloadFile'
import Search from '@featherds/icon/action/Search'
import ViewDetails from '@featherds/icon/action/ViewDetails'
import MenuIcon from '@featherds/icon/navigation/MoreHoriz'
import Refresh from '@featherds/icon/navigation/Refresh'
import { FeatherInput } from '@featherds/input'
import { FeatherSortHeader, SORT } from '@featherds/table'
import { debounce } from 'lodash'
import EmptyList from '../Common/EmptyList.vue'
import TableCard from '../Common/TableCard.vue'

const store = useSnmpDataCollectionStore()
const emptyListContent = {
msg: 'No results found.'
}

const columns = computed(() => [
{ id: 'name', label: 'Source' },
{ id: 'vendor', label: 'Vendor' },
{ id: 'uploadedBy', label: 'Uploaded By' },
{ id: 'enabled', label: 'Status' }
])

const sort = reactive({
name: SORT.NONE,
vendor: SORT.NONE,
uploadedBy: SORT.NONE,
enabled: SORT.NONE
}) as any

const onSourceClick = (source: any) => {
// Navigate to source detail page
console.log('Source clicked:', source)
}

const sortChanged = (sortObj: { property: string; value: SORT }) => {
if (sortObj.value === 'asc' || sortObj.value === 'desc') {
store.onSourcesSortChange(sortObj.property, sortObj.value)
} else {
store.onSourcesSortChange('createdTime', 'desc')
}

for (const prop in sort) {
sort[prop] = SORT.NONE
}
sort[sortObj.property] = sortObj.value
}

const onChangeSearchTerm = debounce(async (value: string) => {
await store.onChangeSourcesSearchTerm(value)
}, 500)

onMounted(async () => {
await store.fetchSnmpCollectionSources()
})
</script>

<style lang="scss" scoped>
@use '@featherds/styles/themes/variables';
@use '@featherds/styles/mixins/typography';
@use '@featherds/table/scss/table';
@use '@/styles/_transitionDataTable';

.snmp-data-collection-source-table {
margin-top: 10px;
padding: 25px;

.header {
display: flex;
justify-content: space-between;
margin-bottom: 20px;

.title-container {
display: flex;
align-items: center;

.title {
@include typography.headline3;
}
}

.action-container {
display: flex;
align-items: flex-start;
justify-content: flex-end;
gap: 5px;
width: 30%;

.search-container {
width: 80%;
}
}
}

.container {
table {
width: 100%;
@include table.table;

thead {
background: var(variables.$background);
text-transform: uppercase;
}

td {
white-space: nowrap;
box-shadow: none;
border-bottom: 1px solid var(variables.$border-on-surface);

div {
border-radius: 5px;
padding: 0px 5px 0px 5px;
}

.action-container {
display: flex;
align-items: center;
gap: 5px;

button {
margin: 0px;
}

:deep(.feather-menu-dropdown) {
.feather-dropdown {
li {
a {
padding: 8px 16px !important;
}
}
}
}
}
}
}
}
}
</style>

34 changes: 34 additions & 0 deletions ui/src/components/SnmpDataCollection/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { SnmpCollectionSource } from '@/types/snmpDataCollection'

export const data: SnmpCollectionSource[] = [
{
id: 1,
name: 'Cisco Devices',
vendor: 'Cisco',
description: 'Data collection source for Cisco devices',
enabled: true,
createdTime: new Date('2023-01-15T10:00:00Z'),
lastModified: new Date('2023-06-10T12:30:00Z'),
uploadedBy: 'admin'
},
{
id: 2,
name: 'Juniper Routers',
vendor: 'Juniper',
description: 'Data collection source for Juniper routers',
enabled: false,
createdTime: new Date('2023-02-20T14:15:00Z'),
lastModified: new Date('2023-05-05T09:45:00Z'),
uploadedBy: 'network_ops'
},
{
id: 3,
name: 'HP Switches',
vendor: 'HP',
description: 'Data collection source for HP switches',
enabled: true,
createdTime: new Date('2023-03-10T08:30:00Z'),
lastModified: new Date('2023-04-22T11:20:00Z'),
uploadedBy: 'tech_team'
}
]
51 changes: 51 additions & 0 deletions ui/src/containers/SnmpDataCollection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<div class="snmp-data-collection-container">
<div class="feather-row">
<div class="feather-col-12">
<BreadCrumbs :items="breadcrumbs" />
</div>
</div>
<div class="header">
<div class="heading">
<h1>Manage SNMP Data Collection Sources</h1>
</div>
<div class="action">
<FeatherButton primary> Create New Data Collection Source </FeatherButton>
<FeatherButton secondary> Import Data Collection Source </FeatherButton>
</div>
</div>
<div class="container">
<SourcesTable />
</div>
</div>
</template>

<script lang="ts" setup>
import BreadCrumbs from '@/components/Layout/BreadCrumbs.vue'
import SourcesTable from '@/components/SnmpDataCollection/SourcesTable.vue'
import { useMenuStore } from '@/stores/menuStore'
import { BreadCrumb } from '@/types'
import { FeatherButton } from '@featherds/button'

const menuStore = useMenuStore()
const homeUrl = computed<string>(() => menuStore.mainMenu?.homeUrl)

const breadcrumbs = computed<BreadCrumb[]>(() => ([
{ label: 'Home', to: homeUrl.value, isAbsoluteLink: true },
{ label: 'SNMP Data Collection', to: '#', position: 'last' }
]))
</script>

<style lang="scss" scoped>
.snmp-data-collection-container {
padding: 20px;

.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
}
</style>

5 changes: 5 additions & 0 deletions ui/src/main/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@ const router = createRouter({
name: 'Event Configuration Create',
component: () => import('@/containers/EventConfigEventCreate.vue')
},
{
path: '/snmp-data-collection',
name: 'SNMP Data Collection',
component: () => import('@/containers/SnmpDataCollection.vue')
},
{
path: '/:pathMatch(.*)*', // catch other paths and redirect
redirect: '/'
Expand Down
Loading