Skip to content

Commit

Permalink
Merge branch 'ui-consolidated-history-community' into 'main'
Browse files Browse the repository at this point in the history
Consolidated history UI for community

See merge request reportcreator/reportcreator!385
  • Loading branch information
MWedl committed Dec 22, 2023
2 parents a499237 + fe22372 commit 68bad8e
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 18 deletions.
9 changes: 4 additions & 5 deletions api/src/reportcreator_api/pentests/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,13 @@ def get_history_timeline_queryset(self):
timeline_querysets = self.get_history_timeline_queryset_parts()
queryset = None
for qs in timeline_querysets:
qs = qs \
.annotate(history_model=Value(qs.model.instance_type.__name__)) \
.select_related('history_user')
if 'history_model_order' not in qs.query.annotations:
qs = qs.annotate(history_model_order=F('history_model'))
if 'model_id' not in qs.query.annotations:
qs = qs.annotate(model_id=Cast(F('id'), output_field=CharField()))
if 'history_model_order' not in qs.query.annotations:
qs = qs.annotate(history_model_order=Value(qs.model.instance_type.__name__))
qs = qs \
.annotate(history_model=Value(qs.model.instance_type.__name__)) \
.select_related('history_user') \
.only('history_date', 'history_type', 'history_user', 'history_title', 'history_change_reason', 'id')
if queryset is None:
queryset = qs
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/ListView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</slot>
<page-loader :items="items" class="mt-4" />
<v-list-item
v-if="items.data.value.length === 0 && !items.hasNextPage.value"
v-if="items.data.value.length === 0 && !items.hasNextPage.value && items.hasBaseURL.value"
title="No data found"
/>
</v-list>
Expand All @@ -45,7 +45,7 @@
import { useSearchableCursorPaginationFetcher } from "~/composables/api";
const props = defineProps<{
url: string
url: string|null;
}>();
const router = useRouter();
Expand Down Expand Up @@ -82,7 +82,7 @@ defineExpose({
.list-header {
position: sticky;
top: 0;
z-index: 1;
z-index: 10;
background-color: vuetify.$list-background;
}
.list-header-actions {
Expand Down
19 changes: 12 additions & 7 deletions frontend/src/composables/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export async function useAsyncDataE<T>(handler: (ctx?: NuxtApp) => Promise<T>, o
return res.data as Ref<T>;
}

export function useCursorPaginationFetcher<T>({ baseURL, query }: { baseURL: string, query?: Object }) {
const searchParams = new URLSearchParams(baseURL.split('?')[1]);
export function useCursorPaginationFetcher<T>({ baseURL, query }: { baseURL: string|null, query?: Object }) {
const searchParams = new URLSearchParams((baseURL || '').split('?')[1]);
for (const [k, v] of Object.entries(query || {})) {
if (v) {
if (Array.isArray(v)) {
Expand All @@ -39,8 +39,11 @@ export function useCursorPaginationFetcher<T>({ baseURL, query }: { baseURL: str
searchParams.delete(k);
}
}
baseURL = baseURL.split('?')[0] + '?' + searchParams.toString();
if (baseURL) {
baseURL = baseURL.split('?')[0] + '?' + searchParams.toString();
}

const hasBaseURL = computed(() => !!baseURL);
const nextPageURL = ref<string|null>(baseURL);
const hasNextPage = computed(() => !!nextPageURL.value);
const pending = ref(false);
Expand Down Expand Up @@ -75,18 +78,19 @@ export function useCursorPaginationFetcher<T>({ baseURL, query }: { baseURL: str
pending,
hasError,
hasNextPage,
hasBaseURL,
fetchNextPage,
};
}

export function useSearchableCursorPaginationFetcher<T>(options: { baseURL: string, query?: Object }) {
export function useSearchableCursorPaginationFetcher<T>(options: { baseURL: string|null, query?: Object }) {
const initializingFetcher = ref(false);
const fetcher = ref<ReturnType<typeof useCursorPaginationFetcher<T>>>(null as any);
const fetchNextPageDebounced = debounce(async () => {
await fetcher.value.fetchNextPage();
initializingFetcher.value = true;
}, 750);
function createFetcher(options: { baseURL: string, query?: Object, fetchInitialPage?: boolean, debounce?: boolean }) {
function createFetcher(options: { baseURL: string|null, query?: Object, fetchInitialPage?: boolean, debounce?: boolean }) {
const newFetcher = useCursorPaginationFetcher<T>(options) as any;
if (options.fetchInitialPage) {
initializingFetcher.value = true;
Expand All @@ -107,7 +111,7 @@ export function useSearchableCursorPaginationFetcher<T>(options: { baseURL: stri
}

const currentQuery = computed(() => {
return Object.fromEntries(new URLSearchParams(fetcher.value.baseURL.split('?')[1] || ''));
return Object.fromEntries(new URLSearchParams((fetcher.value.baseURL || '').split('?')[1] || ''));
});

function applyFilters(query: Object, { fetchInitialPage = true, debounce = false } = {}) {
Expand All @@ -123,7 +127,7 @@ export function useSearchableCursorPaginationFetcher<T>(options: { baseURL: stri
set: (val: string) => applyFilters({ search: val }, { debounce: true }),
});

function reset(options: { baseURL: string, query?: Object }) {
function reset(options: { baseURL: string|null, query?: Object }) {
createFetcher(options);
}

Expand All @@ -136,6 +140,7 @@ export function useSearchableCursorPaginationFetcher<T>(options: { baseURL: stri
pending: computed(() => initializingFetcher.value || fetcher.value.pending),
hasError: computed(() => fetcher.value.hasError),
hasNextPage: computed(() => fetcher.value.hasNextPage),
hasBaseURL: computed(() => fetcher.value.hasBaseURL),
currentQuery,
search,
applyFilters,
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/pages/projects/[projectId].vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
<v-list-item :to="`/projects/${project.id}/publish/`" prepend-icon="mdi-earth" title="Publish">
<s-tooltip activator="parent" text="Publish" />
</v-list-item>
<v-list-item :to="`/projects/${project.id}/history/`" prepend-icon="mdi-history">
<v-list-item-title><pro-info>History</pro-info></v-list-item-title>
<v-list-item :to="`/projects/${project.id}/history/`" prepend-icon="mdi-history" title="History">
<s-tooltip activator="parent" text="History" />
</v-list-item>
</template>
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/pages/projects/[projectId]/history.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<template>
<list-view :url="`/api/v1/pentestprojects/${project.id}/history-timeline/?mode=medium`">
<list-view :url="apiSettings.isProfessionalLicense ? `/api/v1/pentestprojects/${project.id}/history-timeline/?mode=medium` : null">
<template #title>
<pro-info>Version History</pro-info>
</template>
<template #searchbar>
<!-- hide searchbar -->
<span />
</template>
<template #items="{ items }">
<v-timeline
v-if="apiSettings.isProfessionalLicense"
direction="vertical"
side="end"
align="start"
Expand All @@ -27,12 +31,17 @@
:details="true"
/>
</v-timeline>
<v-list-item v-else>
Version history is available in SysReptor Professional.<br><br>
See <a href="https://docs.sysreptor.com/features-and-pricing/" target="_blank" class="text-primary">https://docs.sysreptor.com/features-and-pricing/</a>
</v-list-item>
</template>
</list-view>
</template>

<script setup lang="ts">
const route = useRoute();
const apiSettings = useApiSettings();
const projectStore = useProjectStore();
const project = await useAsyncDataE(() => projectStore.getById(route.params.projectId as string));
Expand Down

0 comments on commit 68bad8e

Please sign in to comment.