fix(frontend): tackle some vue-tsc errors

only 159 to go!
This commit is contained in:
MiniDigger | Martin 2022-12-28 21:38:19 +01:00
parent 88e0a41469
commit 2acafbc382
32 changed files with 123 additions and 104 deletions

View File

@ -5,16 +5,10 @@ import { hasSlotContent } from "~/lib/composables/useSlot";
import Table from "~/lib/components/design/Table.vue";
import PaginationButtons from "~/lib/components/design/PaginationButtons.vue";
import PaginationComponent from "~/lib/components/design/Pagination.vue";
import { Header } from "~/types/components/SortableTable";
type T = Record<string, any>; // remove when https://github.com/vuejs/rfcs/discussions/436 lands or when using volar
export interface Header {
name: string;
title: string;
sortable?: boolean;
width?: string;
}
const props = defineProps<{
headers: Header[];
items: T[];

View File

@ -37,7 +37,7 @@ async function submit(close: () => void) {
useNotificationStore().success(i18n.t("project.flag.flagSend"));
await router.go(0);
} catch (e) {
handleRequestError(e as AxiosError);
handleRequestError(e);
}
}
</script>

View File

@ -30,7 +30,7 @@ async function confirm(close: () => void) {
router.go(0);
useNotificationStore().success(i18n.t(`author.lock.success${props.user.locked ? "Unlock" : "Lock"}`, [props.user.name]));
} catch (e) {
handleRequestError(e as AxiosError);
handleRequestError(e);
}
}
</script>

View File

@ -8,9 +8,10 @@ import Button from "~/lib/components/design/Button.vue";
import Modal from "~/lib/components/modals/Modal.vue";
import { useBackendData } from "~/store/backendData";
import InputText from "~/lib/components/ui/InputText.vue";
import InputSelect, { Option } from "~/lib/components/ui/InputSelect.vue";
import InputSelect from "~/lib/components/ui/InputSelect.vue";
import { useInternalApi } from "~/composables/useApi";
import { handleRequestError } from "~/composables/useErrorHandling";
import { Option } from "~/lib/types/components/ui/InputSelect";
const props = defineProps<{
projectId: number;
@ -41,10 +42,10 @@ watch(name, async () => {
parentId: parent.value,
})
.catch((err: AxiosError) => {
if (!err.response?.data.isHangarApiException) {
if (!err.response?.data?.isHangarApiException) {
return handleRequestError(err);
}
nameErrorMessages.value.push(i18n.t(err.response.data.message));
nameErrorMessages.value.push(i18n.t(err.response.data?.message));
})
.finally(() => {
validateLoading.value = false;

View File

@ -9,8 +9,9 @@ import InputText from "~/lib/components/ui/InputText.vue";
import { required } from "~/lib/composables/useValidationHelpers";
import InputAutocomplete from "~/lib/components/ui/InputAutocomplete.vue";
import { useApi } from "~/composables/useApi";
import Tabs, { Tab } from "~/lib/components/design/Tabs.vue";
import { watch, ref, useRoute } from "#imports";
import Tabs from "~/lib/components/design/Tabs.vue";
import { ref, useRoute } from "#imports";
import { Tab } from "~/lib/types/components/design/Tabs";
const route = useRoute();
const i18n = useI18n();

View File

@ -23,7 +23,7 @@ class Auth {
const result = await useAxios()
.get(`/logout?returnUrl=${useConfig().publicHost}?loggedOut`)
.catch((e) => handleRequestError(e));
if ("status" in result && result?.status === 200 && result?.data) {
if (result?.status === 200 && result?.data) {
location.replace(result?.data);
} else {
await useNotificationStore().error("Error while logging out?!");

View File

@ -1,12 +1,12 @@
import axios, { AxiosError } from "axios";
import { HangarApiException, HangarValidationException, MultiHangarApiException } from "hangar-api";
import { Composer } from "vue-i18n";
import { ref } from "vue";
import { useNotificationStore } from "~/lib/store/notification";
import { I18n } from "~/lib/i18n";
import { createError } from "#imports";
export function handleRequestError(err: AxiosError | unknown, i18n: Composer = I18n.value, msg: string | undefined = undefined) {
export function handleRequestError(err: AxiosError | unknown, msg: string | undefined = undefined) {
const i18n: Composer = I18n.value;
if (import.meta.env.SSR) {
_handleRequestError(err, i18n);
}
@ -40,43 +40,37 @@ export function handleRequestError(err: AxiosError | unknown, i18n: Composer = I
}
function _handleRequestError(err: AxiosError | unknown, i18n: Composer) {
function writeResponse(object: unknown) {
console.log("writeResponse", object);
// throw new Error("TODO: Implement me"); // TODO
createError({ statusCode: object.status, statusMessage: object.statusText });
}
const transformed = transformAxiosError(err);
if (!axios.isAxiosError(err)) {
// everything should be an AxiosError
writeResponse({
status: 500,
createError({
statusCode: 500,
});
console.log("handle not axios error", transformed);
} else if (err.response && typeof err.response.data === "object" && err.response.data) {
if ("isHangarApiException" in err.response.data) {
const data =
"isMultiException" in err.response.data ? (err.response.data as MultiHangarApiException).exceptions[0] : (err.response.data as HangarApiException);
writeResponse({
status: data.httpError.statusCode,
statusText: i18n.te(data.message) ? i18n.t(data.message) : data.message,
createError({
statusCode: data.httpError.statusCode,
statusMessage: i18n.te(data.message) ? i18n.t(data.message) : data.message,
});
} else if ("isHangarValidationException" in err.response.data) {
const data = err.response.data as HangarValidationException;
writeResponse({
status: data.httpError.statusCode,
statusText: data.fieldErrors.map((f) => f.errorMsg).join(", "),
createError({
statusCode: data.httpError.statusCode,
statusMessage: data.fieldErrors.map((f) => f.errorMsg).join(", "),
});
} else {
writeResponse({
status: err.response.status,
statusText: err.response.statusText,
createError({
statusCode: err.response.status,
statusMessage: err.response.statusText,
});
}
} else {
writeResponse({
status: 500,
statusText: "Internal Error: " + transformed.code,
createError({
statusCode: 500,
statusMessage: "Internal Error: " + transformed.code,
});
}
}

View File

@ -2,7 +2,7 @@ import { helpers } from "@vuelidate/validators";
import { useInternalApi } from "~/composables/useApi";
import { withOverrideMessage } from "~/lib/composables/useValidationHelpers";
export const validProjectName = withOverrideMessage((ownerId: () => string) =>
export const validProjectName = withOverrideMessage((ownerId: () => number) =>
helpers.withParams(
{ ownerId, type: "validProjectName" },
helpers.withAsync(async (value: string) => {

View File

@ -24,7 +24,7 @@ export async function useProjectPage(route: RouteLocationNormalizedLoaded, route
if (!page) return;
await useInternalApi(`pages/save/${project.id}/${page.value?.id}`, "post", {
content,
}).catch((e) => handleRequestError(e, i18n, "page.new.error.save"));
}).catch((e) => handleRequestError(e, "page.new.error.save"));
if (page.value) {
page.value.contents = content;
}
@ -33,7 +33,7 @@ export async function useProjectPage(route: RouteLocationNormalizedLoaded, route
async function deletePage() {
if (!page) return;
await useInternalApi(`pages/delete/${project.id}/${page.value?.id}`, "post").catch((e) => handleRequestError(e, i18n, "page.new.error.save"));
await useInternalApi(`pages/delete/${project.id}/${page.value?.id}`, "post").catch((e) => handleRequestError(e, "page.new.error.save"));
await router.replace(`/${route.params.user}/${route.params.project}`);
}

@ -1 +1 @@
Subproject commit a6c3c7eb08fd543684116ac314ac841be375def1
Subproject commit 7fe3405bc026b8110503fa87d2f8ac7a64ee2660

View File

@ -5,7 +5,6 @@ import { HangarProject, ProjectChannel } from "hangar-internal";
import { useHead } from "@vueuse/head";
import { useRoute } from "vue-router";
import Card from "~/lib/components/design/Card.vue";
import { Header } from "~/components/SortableTable.vue";
import { ChannelFlag } from "~/types/enums";
import { useProjectChannels } from "~/composables/useApiHelper";
import { handleRequestError } from "~/composables/useErrorHandling";

View File

@ -6,12 +6,13 @@ import { useHead } from "@vueuse/head";
import { useRoute } from "vue-router";
import Card from "~/lib/components/design/Card.vue";
import Link from "~/lib/components/design/Link.vue";
import SortableTable, { Header } from "~/components/SortableTable.vue";
import SortableTable from "~/components/SortableTable.vue";
import Alert from "~/lib/components/design/Alert.vue";
import { useProjectFlags } from "~/composables/useApiHelper";
import { useSeo } from "~/composables/useSeo";
import { projectIconUrl } from "~/composables/useUrlHelper";
import { definePageMeta } from "#imports";
import { Header } from "~/types/components/SortableTable";
definePageMeta({
projectPermsRequired: ["MOD_NOTES_AND_FLAGS"],
@ -25,13 +26,13 @@ const i18n = useI18n();
const route = useRoute();
const flags = await useProjectFlags(props.project.id);
const headers = [
const headers: Header[] = [
{ title: "Submitter", name: "user" },
{ title: "Reason", name: "reason" },
{ title: "Comment", name: "comment" },
{ title: "When", name: "createdAt" },
{ title: "Resolved", name: "resolved" },
] as Header[];
];
useHead(useSeo("Flags | " + props.project.name, props.project.description, route, projectIconUrl(props.project.namespace.owner, props.project.namespace.slug)));
</script>

View File

@ -7,7 +7,7 @@ import { useHead } from "@vueuse/head";
import { useRoute } from "vue-router";
import Card from "~/lib/components/design/Card.vue";
import Link from "~/lib/components/design/Link.vue";
import SortableTable, { Header } from "~/components/SortableTable.vue";
import SortableTable from "~/components/SortableTable.vue";
import Alert from "~/lib/components/design/Alert.vue";
import { useProjectNotes } from "~/composables/useApiHelper";
import { handleRequestError } from "~/composables/useErrorHandling";
@ -17,6 +17,7 @@ import Button from "~/lib/components/design/Button.vue";
import { useSeo } from "~/composables/useSeo";
import { projectIconUrl } from "~/composables/useUrlHelper";
import { definePageMeta } from "#imports";
import { Header } from "~/types/components/SortableTable";
definePageMeta({
projectPermsRequired: ["MOD_NOTES_AND_FLAGS"],
@ -32,11 +33,11 @@ const notes = await useProjectNotes(props.project.id);
const text = ref("");
const loading = ref(false);
const headers = [
const headers: Header[] = [
{ title: "Date", name: "createdAt", width: "10%" },
{ title: "User", name: "userName", width: "10%" },
{ title: "Message", name: "message", width: "80%" },
] as Header[];
];
useHead(useSeo("Notes | " + props.project.name, props.project.description, route, projectIconUrl(props.project.namespace.owner, props.project.namespace.slug)));

View File

@ -34,6 +34,7 @@ import "vue-advanced-cropper/dist/style.css";
import InputAutocomplete from "~/lib/components/ui/InputAutocomplete.vue";
import { definePageMeta } from "#imports";
import Alert from "~/lib/components/design/Alert.vue";
import { Tab } from "~/lib/types/components/design/Tabs";
definePageMeta({
projectPermsRequired: ["EDIT_SUBJECT_SETTINGS"],
@ -49,7 +50,7 @@ const props = defineProps<{
}>();
const selectedTab = ref(route.hash.substring(1) || "general");
const tabs = [
const tabs: Tab[] = [
{ value: "general", header: i18n.t("project.settings.tabs.general") },
{ value: "links", header: i18n.t("project.settings.tabs.links") },
{ value: "management", header: i18n.t("project.settings.tabs.management") },
@ -94,7 +95,7 @@ function changeImage({ canvas }: CropperResult) {
});
}
const newName = ref<string | null>("");
const newName = ref<string | null | undefined>("");
const newNameField = ref<InstanceType<typeof InputText> | null>(null);
const loading = reactive({
save: false,
@ -112,7 +113,7 @@ watch(selectedTab, (val) => history.replaceState({}, "", route.path + "#" + val)
const search = ref<string>("");
const result = ref<string[]>([]);
async function doSearch(val: string) {
async function doSearch(val: unknown) {
result.value = [];
const users = await useApi<PaginatedResult<User>>("users", "get", {
query: val,
@ -272,8 +273,8 @@ useHead(
<InputText
v-model="form.description"
counter
:maxlength="useBackendData.validations?.project?.desc?.max"
:rules="[required(), maxLength()(useBackendData.validations?.project?.desc?.max)]"
:maxlength="useBackendData.validations?.project?.desc?.max || 120"
:rules="[required(), maxLength()(useBackendData.validations?.project?.desc?.max || 120)]"
/>
</ProjectSettingsSection>
<!-- todo: forums integration -->
@ -284,9 +285,9 @@ useHead(
<InputTag
v-model="form.settings.keywords"
counter
:maxlength="useBackendData.validations?.project.keywords.max"
:maxlength="useBackendData.validations?.project?.keywords?.max || 5"
:label="i18n.t('project.new.step3.keywords')"
:rules="[maxLength()(useBackendData.validations?.project.keywords.max)]"
:rules="[maxLength()(useBackendData.validations?.project?.keywords?.max || 5)]"
/>
</ProjectSettingsSection>
<ProjectSettingsSection>

View File

@ -100,7 +100,7 @@ async function savePage(content: string) {
}
editingPage.value = false;
} catch (err) {
handleRequestError(err as AxiosError, i18n, "page.new.error.save");
handleRequestError(err, "page.new.error.save");
}
}
@ -155,7 +155,7 @@ async function restoreVersion() {
</script>
<template>
<div v-if="projectVersion" class="flex <sm:flex-col flex-wrap md:flex-nowrap gap-4">
<div v-if="projectVersion && platform" class="flex <sm:flex-col flex-wrap md:flex-nowrap gap-4">
<section class="basis-full md:basis-9/12 flex-grow overflow-auto">
<div class="flex flex-wrap gap-2 justify-between">
<div>
@ -340,7 +340,7 @@ async function restoreVersion() {
</div>
</Card>
<Card v-if="projectVersion.pluginDependencies[platform?.name.toUpperCase()] || hasPerms(NamedPermission.EDIT_VERSION)">
<Card v-if="(platform?.name && projectVersion?.pluginDependencies[platform?.name.toUpperCase()]) || hasPerms(NamedPermission.EDIT_VERSION)">
<template #header>
<div class="inline-flex w-full">
<h3 class="flex-grow">{{ i18n.t("version.page.dependencies") }}</h3>

View File

@ -378,7 +378,7 @@ useHead(
<div v-if="isCurrentReviewOpen && currentUserReview === review" class="space-x-1">
<TextAreaModal :title="t('reviews.stopReview')" :label="t('general.message')" :submit="stopReview">
<template #activator="slotProps">
<Button size="small" color="error" v-bind="slotProps.attrs" v-on="slotProps.on">
<Button size="small" color="error" v-on="slotProps.on">
<IconMdiStop />
{{ t("reviews.stopReview") }}
</Button>

View File

@ -9,7 +9,7 @@ import { remove } from "lodash-es";
import { type ValidationRule } from "@vuelidate/core";
import { useSeo } from "~/composables/useSeo";
import { projectIconUrl } from "~/composables/useUrlHelper";
import Steps, { Step } from "~/lib/components/design/Steps.vue";
import Steps from "~/lib/components/design/Steps.vue";
import InputFile from "~/lib/components/ui/InputFile.vue";
import InputText from "~/lib/components/ui/InputText.vue";
import InputSelect from "~/lib/components/ui/InputSelect.vue";
@ -25,10 +25,12 @@ import ChannelModal from "~/components/modals/ChannelModal.vue";
import { useBackendData } from "~/store/backendData";
import DependencyTable from "~/components/projects/DependencyTable.vue";
import InputTag from "~/lib/components/ui/InputTag.vue";
import Tabs, { Tab } from "~/lib/components/design/Tabs.vue";
import Tabs from "~/lib/components/design/Tabs.vue";
import PlatformLogo from "~/components/logos/platforms/PlatformLogo.vue";
import { useProjectChannels } from "~/composables/useApiHelper";
import { definePageMeta } from "#imports";
import { Step } from "~/lib/types/components/design/Steps";
import { Tab } from "~/lib/types/components/design/Tabs";
definePageMeta({
projectPermsRequired: ["CREATE_VERSION"],
@ -302,8 +304,8 @@ useHead(
</div>
<div class="basis-full md:(basis-4/12 -ml-2)">
<ChannelModal :project-id="project.id" @create="addChannel">
<template #activator="{ on, attrs }">
<Button class="basis-4/12" v-bind="attrs" size="medium" v-on="on">
<template #activator="{ on }">
<Button class="basis-4/12" size="medium" v-on="on">
<IconMdiPlus />
{{ t("version.new.form.addChannel") }}
</Button>
@ -345,7 +347,7 @@ useHead(
<p class="mb-4">{{ i18n.t("version.new.form.versionDescription") }}</p>
<div class="flex flex-wrap mt-2 md:-space-x-2 <md:space-y-2">
<!-- TODO validate version string against existing versions - now super easy! -->
<div class="basis-full md:basis-4/12 items-center">
<div v-if="pendingVersion" class="basis-full md:basis-4/12 items-center">
<InputText
v-model="pendingVersion.versionString"
:label="t('version.new.form.versionString')"
@ -357,7 +359,7 @@ useHead(
</div>
<p class="mt-8 text-xl">{{ t("version.new.form.addedArtifacts") }}</p>
<div v-for="(pendingFile, idx) in pendingVersion.files" :key="idx" class="mb-2">
<div v-for="(pendingFile, idx) in pendingVersion?.files" :key="idx" class="mb-2">
<div class="flex flex-wrap items-center mt-4">
<div v-if="pendingFile.fileInfo" class="basis-full <md:mt-4 md:basis-4/12">
<InputText :model-value="pendingFile.fileInfo.name" :label="t('version.new.form.fileName')" disabled />
@ -383,7 +385,12 @@ useHead(
<div v-for="platform in selectedPlatformsData" :key="platform.enumName" class="basis-full">
<span class="text-lg inline-flex items-center"><PlatformLogo :platform="platform.enumName" :size="25" class="mr-1" /> {{ platform.name }}</span>
<div class="mt-2">
<InputTag v-model="pendingVersion.platformDependencies[platform.enumName]" :options="platform.possibleVersions" :rules="platformVersionRules" />
<InputTag
v-if="pendingVersion"
v-model="pendingVersion.platformDependencies[platform.enumName]"
:options="platform.possibleVersions"
:rules="platformVersionRules"
/>
</div>
</div>
</div>
@ -392,7 +399,13 @@ useHead(
<div class="flex flex-wrap space-y-7">
<div v-for="platform in selectedPlatformsData" :key="platform.enumName" class="basis-full">
<span class="text-lg inline-flex items-center"><PlatformLogo :platform="platform.enumName" :size="25" class="mr-1" /> {{ platform.name }}</span>
<DependencyTable ref="dependencyTables" :key="`${platform.name}-deps-table`" :platform="platform.enumName" :version="pendingVersion" />
<DependencyTable
v-if="pendingVersion"
ref="dependencyTables"
:key="`${platform.name}-deps-table`"
:platform="platform.enumName"
:version="pendingVersion"
/>
</div>
</div>
</template>

View File

@ -3,7 +3,7 @@ import { useI18n } from "vue-i18n";
import { Review, ReviewQueueEntry } from "hangar-internal";
import { useHead } from "@vueuse/head";
import { useRoute } from "vue-router";
import SortableTable, { Header } from "~/components/SortableTable.vue";
import SortableTable from "~/components/SortableTable.vue";
import { ReviewAction } from "~/types/enums";
import { useVersionApprovals } from "~/composables/useApiHelper";
import Card from "~/lib/components/design/Card.vue";
@ -12,6 +12,7 @@ import Tag from "~/components/Tag.vue";
import { useSeo } from "~/composables/useSeo";
import Button from "~/lib/components/design/Button.vue";
import { definePageMeta } from "#imports";
import { Header } from "~/types/components/SortableTable";
definePageMeta({
globalPermsRequired: ["REVIEWER"],

View File

@ -6,8 +6,9 @@ import { useHead } from "@vueuse/head";
import PageTitle from "~/lib/components/design/PageTitle.vue";
import { useSeo } from "~/composables/useSeo";
import Flags from "~/components/Flags.vue";
import Tabs, { Tab } from "~/lib/components/design/Tabs.vue";
import Tabs from "~/lib/components/design/Tabs.vue";
import { definePageMeta } from "#imports";
import { Tab } from "~/lib/types/components/design/Tabs";
definePageMeta({
globalPermsRequired: ["MOD_NOTES_AND_FLAGS"],

View File

@ -9,7 +9,7 @@ import { debounce } from "lodash-es";
import PageTitle from "~/lib/components/design/PageTitle.vue";
import { useActionLogs } from "~/composables/useApiHelper";
import Card from "~/lib/components/design/Card.vue";
import SortableTable, { Header } from "~/components/SortableTable.vue";
import SortableTable from "~/components/SortableTable.vue";
import Link from "~/lib/components/design/Link.vue";
import MarkdownModal from "~/components/modals/MarkdownModal.vue";
import DiffModal from "~/components/modals/DiffModal.vue";
@ -19,6 +19,7 @@ import { definePageMeta, useInternalApi, watch } from "#imports";
import InputText from "~/lib/components/ui/InputText.vue";
import InputSelect from "~/lib/components/ui/InputSelect.vue";
import { useBackendData } from "~/store/backendData";
import { Header } from "~/types/components/SortableTable";
definePageMeta({
globalPermsRequired: ["VIEW_LOGS"],
@ -29,7 +30,7 @@ const route = useRoute();
const loggedActions = await useActionLogs();
// TODO add support for sorting
const headers = [
const headers: Header[] = [
{ title: i18n.t("userActionLog.user"), name: "user", sortable: false },
{ title: i18n.t("userActionLog.address"), name: "address", sortable: false },
{ title: i18n.t("userActionLog.time"), name: "time", sortable: false },
@ -37,7 +38,7 @@ const headers = [
{ title: i18n.t("userActionLog.context"), name: "context", sortable: false },
{ title: i18n.t("userActionLog.oldState"), name: "oldState", sortable: false },
{ title: i18n.t("userActionLog.newState"), name: "newState", sortable: false },
] as Header[];
];
const page = ref(0);
const sort = ref<string[]>([]);

View File

@ -1,6 +1,6 @@
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
import { PaginatedResult, Project, User } from "hangar-api";
import { User } from "hangar-api";
import { useRoute, useRouter } from "vue-router";
import { OrganizationRoleTable } from "hangar-internal";
import { computed, ref } from "vue";
@ -11,7 +11,7 @@ import Link from "~/lib/components/design/Link.vue";
import Card from "~/lib/components/design/Card.vue";
import { useApi, useInternalApi } from "~/composables/useApi";
import { handleRequestError } from "~/composables/useErrorHandling";
import SortableTable, { Header } from "~/components/SortableTable.vue";
import SortableTable from "~/components/SortableTable.vue";
import InputCheckbox from "~/lib/components/ui/InputCheckbox.vue";
import { useSeo } from "~/composables/useSeo";
import { authUrl, forumUserUrl } from "~/composables/useUrlHelper";
@ -21,6 +21,7 @@ import InputSelect from "~/lib/components/ui/InputSelect.vue";
import { useBackendData } from "~/store/backendData";
import Button from "~/lib/components/design/Button.vue";
import { definePageMeta } from "#imports";
import { Header } from "~/types/components/SortableTable";
definePageMeta({
globalPermsRequired: ["EDIT_ALL_USER_SETTINGS"],
@ -36,19 +37,19 @@ const orgs = await useInternalApi<{ [key: string]: OrganizationRoleTable }>(`org
);
const user = await useUser(route.params.user as string);
const projectsConfig = [
const projectsConfig: Header[] = [
{ title: i18n.t("userAdmin.project"), name: "name" },
{ title: i18n.t("userAdmin.owner"), name: "owner" },
{ title: i18n.t("userAdmin.role"), name: "role" },
{ title: i18n.t("userAdmin.accepted"), name: "accepted" },
] as Header[];
];
const orgConfig = [
const orgConfig: Header[] = [
{ title: i18n.t("userAdmin.organization"), name: "name" },
{ title: i18n.t("userAdmin.owner"), name: "owner" },
{ title: i18n.t("userAdmin.role"), name: "role" },
{ title: i18n.t("userAdmin.accepted"), name: "accepted" },
] as Header[];
];
const orgList = computed(() => {
return orgs

View File

@ -8,12 +8,13 @@ import PageTitle from "~/lib/components/design/PageTitle.vue";
import Link from "~/lib/components/design/Link.vue";
import Tag from "~/components/Tag.vue";
import { useApi } from "~/composables/useApi";
import { Header } from "~/components/SortableTable.vue";
import { useSeo } from "~/composables/useSeo";
import { definePageMeta, watch } from "#imports";
import { useUsers } from "~/composables/useApiHelper";
import InputCheckbox from "~/lib/components/ui/InputCheckbox.vue";
import InputText from "~/lib/components/ui/InputText.vue";
import { Header } from "~/types/components/SortableTable";
import SortableTable from "~/components/SortableTable.vue";
definePageMeta({
globalPermsRequired: ["EDIT_ALL_USER_SETTINGS"],
@ -23,7 +24,7 @@ const i18n = useI18n();
const route = useRoute();
const router = useRouter();
const headers = [
const headers: Header[] = [
{ name: "pic", title: "", sortable: false },
{ name: "name", title: i18n.t("pages.headers.username"), sortable: true },
{ name: "roles", title: i18n.t("pages.headers.roles"), sortable: true },
@ -31,7 +32,7 @@ const headers = [
{ name: "projectCount", title: i18n.t("pages.headers.projects"), sortable: true },
{ name: "locked", title: i18n.t("pages.headers.locked"), sortable: true },
{ name: "org", title: i18n.t("pages.headers.organization"), sortable: true },
] as Header[];
];
const users = await useUsers();
const page = ref(0);

View File

@ -5,22 +5,22 @@ import { useHead } from "@vueuse/head";
import { PaginatedResult, User } from "hangar-api";
import { computed, ref } from "vue";
import { useAuthors } from "~/composables/useApiHelper";
import { handleRequestError } from "~/composables/useErrorHandling";
import SortableTable, { Header } from "~/components/SortableTable.vue";
import SortableTable from "~/components/SortableTable.vue";
import PageTitle from "~/lib/components/design/PageTitle.vue";
import UserAvatar from "~/components/UserAvatar.vue";
import { useSeo } from "~/composables/useSeo";
import Link from "~/lib/components/design/Link.vue";
import { useApi } from "~/composables/useApi";
import { Header } from "~/types/components/SortableTable";
const i18n = useI18n();
const route = useRoute();
const headers = [
const headers: Header[] = [
{ name: "pic", title: "", sortable: false },
{ name: "name", title: i18n.t("pages.headers.username"), sortable: true },
{ name: "joinDate", title: i18n.t("pages.headers.joined"), sortable: true },
{ name: "projectCount", title: i18n.t("pages.headers.projects"), sortable: true },
] as Header[];
];
const page = ref(0);
const sort = ref<string[]>(["-projectCount"]);

View File

@ -37,7 +37,7 @@ const toArray = (input: unknown) => (Array.isArray(input) ? input : input ? [inp
const filters = ref({
versions: toArray(route.query.version),
categories: toArray(route.query.category),
platform: route.query.platform || null,
platform: (route.query.platform || null) as string | null,
licenses: toArray(route.query.license),
});

View File

@ -10,12 +10,12 @@ import { handleRequestError } from "~/composables/useErrorHandling";
import { useInternalApi } from "~/composables/useApi";
import { useBackendData, useCategoryOptions, useLicenseOptions } from "~/store/backendData";
import { useSeo } from "~/composables/useSeo";
import Steps, { Step } from "~/lib/components/design/Steps.vue";
import Steps from "~/lib/components/design/Steps.vue";
import { useSettingsStore } from "~/store/useSettingsStore";
import InputSelect from "~/lib/components/ui/InputSelect.vue";
import InputText from "~/lib/components/ui/InputText.vue";
import InputTag from "~/lib/components/ui/InputTag.vue";
import Tabs, { Tab } from "~/lib/components/design/Tabs.vue";
import Tabs from "~/lib/components/design/Tabs.vue";
import Button from "~/lib/components/design/Button.vue";
import Markdown from "~/components/Markdown.vue";
import InputTextarea from "~/lib/components/ui/InputTextarea.vue";
@ -25,6 +25,8 @@ import Spinner from "~/lib/components/design/Spinner.vue";
import Link from "~/lib/components/design/Link.vue";
import { usePossibleOwners } from "~/composables/useApiHelper";
import { definePageMeta } from "#imports";
import { Step } from "~/lib/types/components/design/Steps";
import { Tab } from "~/lib/types/components/design/Tabs";
interface NewProjectForm extends ProjectSettingsForm {
ownerId: ProjectOwner["userId"];
@ -80,7 +82,7 @@ const steps: Step[] = [
{
value: "tos",
header: i18n.t("project.new.step1.title"),
showBack: () => false,
showBack: false,
},
{
value: "basic",
@ -98,7 +100,7 @@ const steps: Step[] = [
return true;
},
},
{ value: "finishing", header: i18n.t("project.new.step5.title"), showNext: () => false, showBack: () => false },
{ value: "finishing", header: i18n.t("project.new.step5.title"), showNext: false, showBack: false },
];
const selectBBCodeTab = ref("convert");
@ -249,8 +251,8 @@ function createProject() {
<InputTag
v-model="form.settings.keywords"
:label="i18n.t('project.new.step3.keywords')"
:rules="[maxLength()(useBackendData.validations.project.keywords.max)]"
:maxlength="useBackendData.validations.project.keywords.max"
:rules="[maxLength()(useBackendData?.validations?.project?.keywords?.max || 5)]"
:maxlength="useBackendData?.validations?.project?.keywords?.max || 5"
/>
</div>
</div>

View File

@ -18,8 +18,9 @@ import IconMdiInformationOutline from "~icons/mdi/information-outline";
import IconMdiMessageOutline from "~icons/mdi/message-outline";
import IconMdiCheck from "~icons/mdi/check";
import Pagination from "~/lib/components/design/Pagination.vue";
import Tabs, { Tab } from "~/lib/components/design/Tabs.vue";
import Tabs from "~/lib/components/design/Tabs.vue";
import { definePageMeta } from "#imports";
import { Tab } from "~/lib/types/components/design/Tabs";
definePageMeta({
loginRequired: true,

View File

@ -5,24 +5,24 @@ import { useHead } from "@vueuse/head";
import { computed, ref } from "vue";
import { PaginatedResult, User } from "hangar-api";
import { useStaff } from "~/composables/useApiHelper";
import { handleRequestError } from "~/composables/useErrorHandling";
import SortableTable, { Header } from "~/components/SortableTable.vue";
import SortableTable from "~/components/SortableTable.vue";
import PageTitle from "~/lib/components/design/PageTitle.vue";
import { useSeo } from "~/composables/useSeo";
import UserAvatar from "~/components/UserAvatar.vue";
import Link from "~/lib/components/design/Link.vue";
import Tag from "~/components/Tag.vue";
import { useApi } from "~/composables/useApi";
import { Header } from "~/types/components/SortableTable";
const i18n = useI18n();
const route = useRoute();
const headers = [
const headers: Header[] = [
{ name: "pic", title: "", sortable: false },
{ name: "name", title: i18n.t("pages.headers.username"), sortable: true },
{ name: "roles", title: i18n.t("pages.headers.roles"), sortable: true },
{ name: "joinDate", title: i18n.t("pages.headers.joined"), sortable: true },
] as Header[];
];
const page = ref(0);
const sort = ref<string[]>(["roles"]);

View File

@ -4,7 +4,7 @@ import { IPlatform, IProjectCategory, IPrompt } from "hangar-internal";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import backendData from "~/generated/backendData.json";
import { Option } from "~/lib/components/ui/InputAutocomplete.vue";
import { Option } from "~/lib/types/components/ui/InputAutocomplete";
const typedBackendData = backendData as unknown as BackendData;

View File

@ -17,7 +17,7 @@ export const usePrismStore = defineStore("prism", () => {
async function loadLanguages() {
const langs = new Set<string>();
for (const codeBlock of document.querySelectorAll('code[class*="language-"]')) {
for (const codeBlock of document.querySelectorAll('code[class*="language-"]') as any as Element[]) {
langs.add(codeBlock.className.replace("language-", ""));
}
prismLog("Load languages", langs);

View File

@ -23,9 +23,9 @@ declare module "hangar-api" {
}
interface License {
name: string | null;
url: string | null;
type: string | null;
name?: string | null;
url?: string | null;
type?: string | null;
}
interface ProjectSettings {

View File

@ -0,0 +1,6 @@
export interface Header {
name: string;
title: string;
sortable?: boolean;
width?: string;
}

View File

@ -4,8 +4,8 @@
"compilerOptions": {
"baseUrl": ".",
"module": "ESNext",
"target": "es2017",
"lib": ["DOM", "ESNext"],
"target": "ES2020",
"lib": ["DOM", "ES2020"],
"strict": true,
"esModuleInterop": true,
"incremental": true,