Fix vue template type errors

This commit is contained in:
Michael Wedl 2024-04-10 13:29:19 +02:00
parent d80dc620d6
commit 4dc8559991
38 changed files with 90 additions and 77 deletions

View File

@ -10,10 +10,12 @@
</template>
<script setup lang="ts">
import type { BtnConfirmVariant } from './Confirm.vue';
const props = withDefaults(defineProps<{
copy: () => Promise<void>;
confirmText?: string;
buttonVariant?: string;
buttonVariant?: BtnConfirmVariant;
}>(), {
confirmText: undefined,
buttonVariant: 'list-item',

View File

@ -1,6 +1,13 @@
<template>
<chip-date
:value="props.value"
icon="mdi-file-document-plus"
tooltip-prefix-text="Created: "
/>
</template>
<script setup lang="ts">
const props = defineProps<{
value: string;
}>();
</script>

View File

@ -114,7 +114,7 @@ watch(dialogVisible, () => {
});
const templateLanguage = ref<string|null>(null);
const templateLanguageChoices = computed(() => currentTemplate.value?.translations?.map(tr => apiSettings.settings!.languages.find(l => l.code === tr.language)) || []);
const templateLanguageChoices = computed(() => currentTemplate.value?.translations?.map(tr => apiSettings.settings!.languages.find(l => l.code === tr.language)!) || []);
const displayLanguage = computed(() => templateLanguage.value || props.project.language);
watch(currentTemplate, () => {
if (!currentTemplate.value) {

View File

@ -101,7 +101,7 @@ async function uploadSingleFile(file: File) {
requestErrorToast({ error, message: 'Failed to upload ' + file.name });
}
}
async function performFileUpload(files?: FileList|null) {
async function performFileUpload(files?: File[]|FileList|null) {
if (uploadInProgress.value || props.disabled || !files) {
return;
}

View File

@ -61,7 +61,7 @@ onMounted(() => {
codeLens: false,
folding: true,
lightbulb: {
enabled: false,
enabled: monaco.editor.ShowLightbulbIconMode.Off,
},
automaticLayout: true,
wordWrap: 'on',

View File

@ -262,7 +262,7 @@ const fieldAttrs = computed(() => ({
showFieldIds: true,
uploadFile: props.uploadFile,
rewriteFileUrl: props.rewriteFileUrl,
selectableUsers: [auth.user.value],
selectableUsers: [auth.user.value!],
lang: props.projectType.language,
readonly: props.readonly,
spellcheckEnabled: localSettings.designSpellcheckEnabled,

View File

@ -91,7 +91,7 @@
</div>
</template>
<script setup lang="ts" generic="T extends { id: string, lock_info?: LockInfo }">
<script setup lang="ts" generic="T extends { id: string, lock_info?: LockInfo|null }">
import debounce from 'lodash/debounce';
import cloneDeep from 'lodash/cloneDeep';
import isEqual from 'lodash/isEqual';
@ -101,7 +101,7 @@ import type { LockInfo } from '@/utils/types';
import { EditMode } from '@/utils/types';
const props = withDefaults(defineProps<{
data?: T,
data?: T|null,
form?: VForm,
canSave?: boolean,
canAutoSave?: boolean,

View File

@ -40,7 +40,7 @@
>
<history-timeline-item
v-if="props.currentUrl"
:value="{history_type: '~', history_change_reason: 'Current Version'}"
:value="{history_type: '~', history_change_reason: 'Current Version'} as unknown as HistoryTimelineRecord"
:to="currentUrl"
>
<template #info><span /></template>
@ -58,7 +58,7 @@
<script setup lang="ts">
const props = defineProps<{
modelValue: boolean;
modelValue?: boolean;
url: string;
currentUrl?: string|null;
}>();

View File

@ -45,7 +45,7 @@
</full-height-page>
</template>
<script setup lang="ts" generic="T">
<script setup lang="ts" generic="T = any">
import { useSearchableCursorPaginationFetcher } from "~/composables/api";
const props = defineProps<{

View File

@ -34,16 +34,14 @@
</template>
<script setup lang="ts">
const props = defineProps(makeMarkdownProps({
files: true,
spellcheckSupportedDefault: true,
}));
const props = defineProps(makeMarkdownProps());
const emit = defineEmits(makeMarkdownEmits());
const { editorView, markdownToolbarAttrs, markdownStatusbarAttrs, markdownPreviewAttrs, onIntersect, focus, blur } = useMarkdownEditor({
props: computed(() => ({ ...props, spellcheckSupported: true } as any)),
emit,
extensions: markdownEditorDefaultExtensions(),
fileUploadSupported: true,
});
defineExpose({

View File

@ -39,16 +39,14 @@
</template>
<script setup lang="ts">
const props = defineProps(makeMarkdownProps({
files: true,
spellcheckSupportedDefault: true,
}));
const props = defineProps(makeMarkdownProps());
const emit = defineEmits(makeMarkdownEmits());
const { editorView, markdownToolbarAttrs, markdownPreviewAttrs, onIntersect, focus, blur } = useMarkdownEditor({
props: computed(() => ({ ...props, spellcheckSupported: true } as any)),
emit,
extensions: markdownEditorPageExtensions(),
fileUploadSupported: true,
});
function initialScrollTop() {

View File

@ -16,7 +16,6 @@ import {
} from "~/composables/markdown";
const props = defineProps(makeMarkdownProps({
files: false,
spellcheckSupportedDefault: false
}));
const emit = defineEmits(makeMarkdownEmits());
@ -25,6 +24,7 @@ const { onIntersect, focus, blur } = useMarkdownEditor({
props: computed(() => props),
emit,
extensions: markdownEditorTextFieldExtensions(),
fileUploadSupported: false,
});
defineExpose({
focus,

View File

@ -91,12 +91,12 @@ import type { VToolbar } from 'vuetify/lib/components/index.mjs';
import { MarkdownEditorMode } from '@/utils/types';
const props = defineProps<{
editorView?: EditorView;
editorState?: EditorState;
editorView?: EditorView|null;
editorState?: EditorState|null;
spellcheckEnabled?: boolean;
markdownEditorMode?: MarkdownEditorMode;
disabled?: boolean;
lang?: string;
lang?: string|null;
uploadFiles?: (files: FileList) => Promise<void>;
fileUploadInProgress?: boolean;
}>();

View File

@ -22,9 +22,10 @@
<script setup lang="ts">
const props = withDefaults(defineProps<{
modelValue: string|null;
modelValue?: string|null;
items?: Language[]|null;
}>(), {
modelValue: null,
items: null
});
const emit = defineEmits<{

View File

@ -1,7 +1,7 @@
<template>
<s-user-selection
:model-value="modelValue"
@update:model-value="updateMembers"
@update:model-value="members => updateMembers(members as unknown as ProjectMember[])"
label="Members"
:multiple="true"
:disabled="props.disabled"

View File

@ -9,9 +9,9 @@
import { ReviewStatus } from "~/utils/types";
const props = defineProps<{
value: ReviewStatus;
value?: ReviewStatus|null;
}>();
const statusInfo = computed(() => ReviewStatusItems.find(i => i.value === props.value)!);
const statusInfo = computed(() => ReviewStatusItems.find(i => i.value === props.value));
</script>
<style lang="scss" scoped>

View File

@ -26,11 +26,12 @@ import { VSelect } from "vuetify/lib/components/index.mjs";
import { ReviewStatus, ProjectTypeStatus } from "~/utils/types";
const props = withDefaults(defineProps<{
modelValue: ReviewStatus|ProjectTypeStatus|null;
modelValue?: ReviewStatus|ProjectTypeStatus|null;
items?: typeof ReviewStatusItems|typeof ProjectTypeStatusItems;
variant?: VSelect['variant'];
density?: VSelect['density'];
}>(), {
modelValue: null,
items: () => ReviewStatusItems,
variant: 'underlined',
density: 'compact'

View File

@ -154,7 +154,7 @@ export function useLockEdit<T>(options: LockEditOptions<T>) {
} : {}),
...((options.performDelete) ? {
delete: options.performDelete,
deleteConfirmInput: options.deleteConfirmInput?.value,
deleteConfirmInput: options.deleteConfirmInput?.value || undefined,
...(options.canDelete ? {
canDelete: options.canDelete.value,
} : {}),

View File

@ -24,7 +24,7 @@ export type MarkdownProps = {
rewriteReferenceLink?: (src: string) => {href: string, title: string}|null;
}
export function makeMarkdownProps(options: { files: boolean, spellcheckSupportedDefault: boolean }) {
export function makeMarkdownProps(options: { spellcheckSupportedDefault: boolean } = { spellcheckSupportedDefault: true }) {
return {
modelValue: {
type: String,
@ -58,27 +58,25 @@ export function makeMarkdownProps(options: { files: boolean, spellcheckSupported
type: Object as PropType<CollabPropType>,
default: undefined,
},
...(options.files ? {
uploadFile: {
type: Function as PropType<MarkdownProps['uploadFile']>,
default: undefined,
},
rewriteFileUrl: {
type: Function as PropType<MarkdownProps['rewriteFileUrl']>,
default: undefined,
},
rewriteReferenceLink: {
type: Function as PropType<MarkdownProps['rewriteReferenceLink']>,
default: undefined,
},
} : {}),
uploadFile: {
type: Function as PropType<MarkdownProps['uploadFile']>,
default: undefined,
},
rewriteFileUrl: {
type: Function as PropType<MarkdownProps['rewriteFileUrl']>,
default: undefined,
},
rewriteReferenceLink: {
type: Function as PropType<MarkdownProps['rewriteReferenceLink']>,
default: undefined,
},
}
}
export function makeMarkdownEmits() {
return ['update:modelValue', 'update:spellcheckEnabled', 'update:markdownEditorMode', 'collab', 'focus', 'blur'];
}
export function useMarkdownEditor({ props, emit, extensions }: {
export function useMarkdownEditor({ props, emit, extensions, fileUploadSupported }: {
extensions: any[];
props: ComputedRef<MarkdownProps & {
modelValue: string|null;
@ -87,6 +85,7 @@ export function useMarkdownEditor({ props, emit, extensions }: {
spellcheckSupported?: boolean;
}>;
emit: any;
fileUploadSupported: boolean;
}) {
const apiSettings = useApiSettings();
const theme = useTheme();
@ -135,7 +134,7 @@ export function useMarkdownEditor({ props, emit, extensions }: {
const fileUploadInProgress = ref(false);
async function uploadFiles(files?: FileList, pos?: number|null) {
if (!editorView.value || !props.value.uploadFile || !files || files.length === 0 || fileUploadInProgress.value) {
if (!editorView.value || !props.value.uploadFile || !fileUploadSupported || !files || files.length === 0 || fileUploadInProgress.value) {
return;
}
@ -273,7 +272,7 @@ export function useMarkdownEditor({ props, emit, extensions }: {
editorActions.value.disabled(Boolean(props.value.disabled || props.value.readonly));
editorActions.value.spellcheckLanguageTool(spellcheckLanguageToolEnabled.value);
editorActions.value.spellcheckBrowser(spellcheckBrowserEnabled.value);
editorActions.value.uploadFile(Boolean(props.value.uploadFile));
editorActions.value.uploadFile(Boolean(props.value.uploadFile) && fileUploadSupported);
editorActions.value.darkTheme(theme.current.value.dark);
}
onMounted(() => initializeEditorView());
@ -350,7 +349,7 @@ export function useMarkdownEditor({ props, emit, extensions }: {
editorView: editorView.value,
editorState: editorState.value,
disabled: props.value.disabled || props.value.readonly,
uploadFiles: props.value.uploadFile ? uploadFiles : undefined,
uploadFiles: (props.value.uploadFile && fileUploadSupported) ? uploadFiles : undefined,
fileUploadInProgress: fileUploadInProgress.value,
lang: props.value.lang,
spellcheckEnabled: props.value.spellcheckEnabled,
@ -359,8 +358,8 @@ export function useMarkdownEditor({ props, emit, extensions }: {
'onUpdate:markdownEditorMode': (val: MarkdownEditorMode) => emit('update:markdownEditorMode', val),
}));
const markdownStatusbarAttrs = computed(() => ({
editorState: editorState.value,
fileUploadEnabled: Boolean(props.value.uploadFile),
editorState: editorState.value!,
fileUploadEnabled: Boolean(props.value.uploadFile) && fileUploadSupported,
fileUploadInProgress: fileUploadInProgress.value,
}));
const markdownPreviewAttrs = computed(() => ({

View File

@ -90,7 +90,7 @@
<template #default>
<div class="h-100">
<edit-toolbar v-bind="toolbarAttrs" :form="$refs.form" />
<edit-toolbar v-bind="toolbarAttrs" :form="$refs.form as VForm" />
<template v-if="currentField === null">
<design-finding-ordering-definition
v-model="projectType.finding_ordering"
@ -123,6 +123,7 @@
import omit from 'lodash/omit';
import sortBy from "lodash/sortBy";
import Draggable from "vuedraggable";
import type { VForm } from 'vuetify/components';
import { uniqueName } from '@/utils/urls';
import { useProjectTypeLockEditOptions } from "@/composables/lockedit";
import { FieldDataType, FieldOrigin } from "@/utils/types";

View File

@ -1,7 +1,7 @@
<template>
<v-container class="pt-0">
<v-form ref="form">
<edit-toolbar v-bind="toolbarAttrs" :form="$refs.form">
<edit-toolbar v-bind="toolbarAttrs" :form="$refs.form as VForm">
<template #context-menu>
<btn-copy
:disabled="!auth.permissions.value.designer"
@ -55,6 +55,7 @@
</template>
<script setup lang="ts">
import { type VForm } from 'vuetify/components';
import { useProjectTypeLockEditOptions } from "~/composables/lockedit";
const auth = useAuth();

View File

@ -19,7 +19,7 @@
<template #default>
<full-height-page>
<template #header>
<edit-toolbar v-bind="toolbarAttrs" :form="$refs.form">
<edit-toolbar v-bind="toolbarAttrs" :form="$refs.form as VForm">
<template #title v-if="currentNote">
<div class="note-title-container">
<div>
@ -84,6 +84,7 @@
<script setup lang="ts">
import { v4 as uuid4 } from 'uuid';
import { VForm } from 'vuetify/components';
const localSettings = useLocalSettings();
@ -154,7 +155,7 @@ function updateNoteChecked(note: NoteBase) {
dn.checked = note.checked;
}
}
async function selectNote(note: NoteBase) {
async function selectNote(note: NoteBase|null) {
currentNote.value = note;
await nextTick();
textRef.value?.focus();

View File

@ -105,7 +105,7 @@
</template>
<template #default>
<edit-toolbar v-bind="toolbarAttrs" :form="$refs.form" />
<edit-toolbar v-bind="toolbarAttrs" :form="$refs.form as VForm" />
<template v-if="currentItemIsSection">
<s-card>
@ -171,6 +171,7 @@
import Draggable from "vuedraggable";
import omit from 'lodash/omit';
import isEqual from 'lodash/isEqual';
import { VForm } from "vuetify/components";
import { uniqueName } from '@/utils/urls';
import { FieldOrigin } from "~/utils/types";

View File

@ -10,7 +10,7 @@
<v-tab :to="{ path: '/designs/', query: route.query }" exact prepend-icon="mdi-earth" text="Global" />
<v-tab :to="{ path: '/designs/private/', query: route.query }" prepend-icon="mdi-account" text="Private" />
</template>
<template #item="{item}">
<template #item="{item}: {item: ProjectType}">
<design-list-item :item="item" />
</template>
</list-view>

View File

@ -10,7 +10,7 @@
<v-tab :to="{ path: '/designs/', query: route.query }" exact prepend-icon="mdi-earth" text="Global" />
<v-tab :to="{ path: '/designs/private/', query: route.query }" prepend-icon="mdi-account" text="Private" />
</template>
<template #item="{item}">
<template #item="{item}: {item: ProjectType}">
<design-list-item :item="item" />
</template>
</list-view>

View File

@ -59,7 +59,7 @@
<script setup lang="ts">
import urlJoin from "url-join";
import { MarkdownEditorMode } from "~/utils/types";
import { MarkdownEditorMode, type UserNote } from "~/utils/types";
import { uploadFileHelper } from "~/utils/upload";
const route = useRoute();
@ -71,7 +71,7 @@ const note = computed(() => notesCollab.data.value.notes[route.params.noteId as
const toolbarAttrs = computed(() => ({
data: note.value,
delete: async (note: ProjectNote) => {
delete: async (note: UserNote) => {
await userNotesStore.deleteNote(note);
await navigateTo('/notes/personal/');
},

View File

@ -86,15 +86,15 @@ const archiverUsers = computed(() => allArchiverUsers.value.filter(u => u.can_re
const threshold = computed(() => apiSettings.settings!.archiving_threshold);
const canArchive = computed(() => apiSettings.settings!.features.archiving && threshold.value > 0 && threshold.value <= archiverUsers.value.length);
const warnings = computed(() => {
const out = [];
const out = [] as ErrorMessage[];
if (archiverUsers.value.length < threshold.value) {
out.push({
level: 'error',
level: MessageLevel.ERROR,
message: `Too few archivers. At least ${threshold.value} users are required to restore the archive.`,
});
} else if (threshold.value === archiverUsers.value.length) {
out.push({
level: 'warning',
level: MessageLevel.WARNING,
message: 'All archivers are required to restore the archive. If one user loses their key, the archive is lost forever. Consider adding more users before archiving.',
});
}

View File

@ -18,13 +18,13 @@
class="history-timeline-content"
>
<history-timeline-item
:value="{history_type: '~', history_change_reason: 'Current Version'}"
:value="{history_type: '~', history_change_reason: 'Current Version'} as unknown as HistoryTimelineRecord"
:to="`/projects/${project.id}/reporting/`"
>
<template #info><span /></template>
</history-timeline-item>
<history-timeline-item-project
v-for="item in items.data.value"
v-for="item in (items.data.value as HistoryTimelineRecord[])"
:key="item.id"
:item="item"
:project="project"

View File

@ -86,12 +86,12 @@
<error-list v-if="!pendingCheckMessages" :value="allMessages" :group="true" :show-no-message-info="true">
<template #location="{msg}">
<NuxtLink v-if="messageLocationUrl(msg)" :to="messageLocationUrl(msg)" target="_blank" class="text-primary">
<NuxtLink v-if="messageLocationUrl(msg) && msg.location" :to="messageLocationUrl(msg)" target="_blank" class="text-primary">
in {{ msg.location.type }}
<span v-if="msg.location.name"> "{{ msg.location.name }}"</span>
<span v-if="msg.location.path"> field "{{ msg.location.path }}"</span>
</NuxtLink>
<span v-else-if="msg.location.name">
<span v-else-if="msg.location?.name">
in {{ msg.location.type }}
<span v-if="msg.location.name"> "{{ msg.location.name }}"</span>
<span v-if="msg.location.path"> field "{{ msg.location.path }}"</span>

View File

@ -8,7 +8,7 @@
<pro-info>Archived</pro-info>
</v-tab>
</template>
<template #item="{item}">
<template #item="{item}: {item: PentestProject}">
<project-list-item :item="item" />
</template>
</list-view>

View File

@ -13,7 +13,7 @@
<pro-info>Archived</pro-info>
</v-tab>
</template>
<template #item="{item}">
<template #item="{item}: {item: PentestProject}">
<project-list-item :item="item" />
</template>
</list-view>

View File

@ -72,11 +72,11 @@ const fetchLoaderAttrs = computed(() => ({
}));
const editorDiffAttrs = computed(() => ({
historic: {
value: fetchState.data.value?.templateHistoric,
value: fetchState.data.value?.templateHistoric as FindingTemplate,
rewriteFileUrl: rewriteFileUrlHistoric,
},
current: {
value: fetchState.data.value?.templateCurrent,
value: fetchState.data.value?.templateCurrent as FindingTemplate,
rewriteFileUrl: rewriteFileUrlCurrent,
},
initialLanguage: route.query?.language as string,

View File

@ -37,7 +37,7 @@
:disabled="!auth.permissions.value.template_editor"
/>
</template>
<template #item="{item}">
<template #item="{item}: {item: FindingTemplate}">
<template-list-item
:template="item"
:language="currentLanguage"
@ -65,7 +65,7 @@ const auth = useAuth();
const listViewRef = ref();
const languageChoices = computed(() => [{ code: null as string|null, name: 'All' }].concat(apiSettings.settings!.languages.filter(l => l.enabled || l.code === route.query.language)));
const languageChoices = computed(() => [{ code: null as string|null, name: 'All' } as Language].concat(apiSettings.settings!.languages.filter(l => l.enabled || l.code === route.query.language)));
const currentLanguage = computed({
get: () => (Array.isArray(route.query.language) ? route.query.language[0] : route.query.language) || null,
set: (val) => {

View File

@ -2,7 +2,7 @@
<v-form ref="form">
<edit-toolbar
:data="user"
:form="$refs.form"
:form="$refs.form as VForm"
:edit-mode="canEdit ? EditMode.EDIT : EditMode.READONLY"
:save="performSave"
:delete="performDelete"
@ -55,6 +55,7 @@
</template>
<script setup lang="ts">
import { VForm } from "vuetify/components";
import { EditMode } from "~/utils/types";
const route = useRoute();

View File

@ -1,12 +1,14 @@
<template>
<v-form ref="form">
<edit-toolbar :data="user" :form="$refs.form" :save="performSave" />
<edit-toolbar :data="user" :form="$refs.form as VForm" :save="performSave" />
<user-info-form v-model="user" :errors="serverErrors" />
</v-form>
</template>
<script setup lang="ts">
import { VForm } from 'vuetify/components';
const auth = useAuth();
const user = await useFetchE<User>('/api/v1/pentestusers/self/', { method: 'GET' });

View File

@ -105,7 +105,7 @@ export const useUserNotesStore = defineStore('usernotes', {
},
useNotesCollab(noteId?: string) {
const collabState = this.notesCollabState;
const collab = useCollab(collabState as unknown as CollabStoreState<{ notes: {[key: string]: UserNote}}>);
const collab = useCollab(collabState as CollabStoreState<{ notes: {[key: string]: UserNote}}>);
const hasEditPermissions = computed(() => true);
return {

View File

@ -498,8 +498,8 @@ export enum MessageLocationType {
export type ErrorMessage = {
level: MessageLevel;
message: string;
details: string|null;
location: {
details?: string|null;
location?: {
type:MessageLocationType;
id: string|null;
name: string|null;

View File

@ -25,7 +25,7 @@ export function spellcheck({ performSpellcheckRequest, performSpellcheckAddWordR
}) {
return linter(async (view) => {
const initial = view.state.doc.toString();
const annotatedText = markdownToAnnotatedText(initial) as unknown as AnnotatedText[];
const annotatedText = markdownToAnnotatedText(initial);
if (annotatedText.length === 0) {
return [];
}