mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-02-23 15:12:52 +08:00
tons of seo improvements (#798)
This commit is contained in:
parent
5e57fa0a0e
commit
66393fda73
@ -48,6 +48,7 @@ server.get("*", async (request: Request, response: Response) => {
|
||||
});
|
||||
|
||||
response.contentType("text/html");
|
||||
response.setHeader("X-Powered-By", "HangarSSR");
|
||||
response.writeHead(status || 200, statusText || headers, headers);
|
||||
response.end(html);
|
||||
});
|
||||
|
@ -344,7 +344,7 @@ function isRecent(date: string): boolean {
|
||||
|
||||
<!-- Login/register buttons -->
|
||||
<div v-else class="flex gap-2">
|
||||
<a class="flex items-center rounded-md p-2 hover:(text-primary-400 bg-primary-0)" :href="auth.loginUrl($route.fullPath)">
|
||||
<a class="flex items-center rounded-md p-2 hover:(text-primary-400 bg-primary-0)" :href="auth.loginUrl($route.fullPath)" rel="nofollow">
|
||||
<icon-mdi-key-outline class="mr-1 text-[1.2em]" />
|
||||
{{ t("nav.login") }}
|
||||
</a>
|
||||
|
@ -132,7 +132,7 @@ interface EditableMember {
|
||||
<Card :class="props.class">
|
||||
<template #header>
|
||||
<div class="inline-flex w-full flex-cols space-between">
|
||||
<span class="flex-grow">{{ i18n.t("project.members") }}</span>
|
||||
<h3 class="flex-grow">{{ i18n.t("project.members") }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -36,17 +36,17 @@ function getBorderClasses(): string {
|
||||
/>
|
||||
</div>
|
||||
<div class="overflow-clip overflow-hidden min-w-0">
|
||||
<span class="inline-flex items-center gap-x-1">
|
||||
<p>
|
||||
<h2 class="inline-flex items-center gap-x-1">
|
||||
<span>
|
||||
<Link :to="'/' + project.namespace.owner + '/' + project.namespace.slug">{{ project.name }}</Link>
|
||||
by
|
||||
<Link :to="'/' + project.namespace.owner">{{ project.namespace.owner }}</Link>
|
||||
</p>
|
||||
</span>
|
||||
<IconMdiCancel v-if="project.visibility === Visibility.SOFT_DELETE"></IconMdiCancel>
|
||||
<IconMdiEyeOff v-else-if="project.visibility !== Visibility.PUBLIC"></IconMdiEyeOff>
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<p v-if="project.description" class="mb-1">{{ project.description }}</p>
|
||||
<h3 v-if="project.description" class="mb-1">{{ project.description }}</h3>
|
||||
<span class="<sm:hidden text-gray-500 dark:text-gray-400 flex flex-row items-center">
|
||||
<CategoryLogo :category="project.category" :size="16" class="mr-1" />
|
||||
{{ i18n.t("project.category." + project.category) }}
|
||||
|
@ -123,7 +123,7 @@ function requiresConfirmation(): boolean {
|
||||
/>
|
||||
<router-link class="!sm:ml-0" :to="'/' + project.namespace.owner">{{ project.namespace.owner }}</router-link>
|
||||
<span class="text-gray-500 dark:text-gray-400"> / </span>
|
||||
<span class="font-semibold">{{ project.name }}</span>
|
||||
<h1 class="font-semibold">{{ project.name }}</h1>
|
||||
</div>
|
||||
<p>{{ project.description }}</p>
|
||||
</div>
|
||||
|
@ -11,7 +11,6 @@ import { NamedPermission } from "~/types/enums";
|
||||
import { HangarProject } from "hangar-internal";
|
||||
import DonationModal from "~/components/donation/DonationModal.vue";
|
||||
import VisibilityChangerModal from "~/components/modals/VisibilityChangerModal.vue";
|
||||
import { MenuItem } from "@headlessui/vue";
|
||||
|
||||
const props = defineProps<{
|
||||
project: HangarProject;
|
||||
@ -22,7 +21,9 @@ const slug = computed(() => props.project.namespace.owner + "/" + props.project.
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<template #header>{{ i18n.t("project.info.title") }}</template>
|
||||
<template #header>
|
||||
<h3>{{ i18n.t("project.info.title") }}</h3>
|
||||
</template>
|
||||
<template #default>
|
||||
<table class="w-full">
|
||||
<tbody>
|
||||
|
@ -22,7 +22,7 @@ const route = useRoute();
|
||||
<Card>
|
||||
<template #header>
|
||||
<div class="inline-flex w-full flex-cols space-between">
|
||||
<span class="flex-grow">{{ i18n.t("page.plural") }}</span>
|
||||
<h3 class="flex-grow">{{ i18n.t("page.plural") }}</h3>
|
||||
<NewPageModal v-if="hasPerms(NamedPermission.EDIT_PAGE)" :pages="project.pages" :project-id="project.id" activator-class="mr-2" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -9,6 +9,7 @@ import { useSeo } from "~/composables/useSeo";
|
||||
import { inject } from "vue";
|
||||
import { useInternalApi } from "~/composables/useApi";
|
||||
import { handleRequestError } from "~/composables/useErrorHandling";
|
||||
import { projectIconUrl } from "~/composables/useUrlHelper";
|
||||
|
||||
const props = defineProps<{
|
||||
project: HangarProject;
|
||||
@ -23,7 +24,8 @@ const updateProjectPages = inject<(pages: HangarProjectPage[]) => void>("updateP
|
||||
|
||||
const { editingPage, changeEditingPage, page, savePage, deletePage } = await useProjectPage(route, router, ctx, i18n, props.project);
|
||||
if (page) {
|
||||
useHead(useSeo(page.value?.name, props.project.description, route, null));
|
||||
const title = page.value?.name === "Home" ? props.project.name : page.value?.name + " | " + props.project.name;
|
||||
useHead(useSeo(title, props.project.description, route, projectIconUrl(props.project.namespace.owner, props.project.namespace.slug)));
|
||||
}
|
||||
|
||||
async function deletePageAndUpdateProject() {
|
||||
|
@ -9,7 +9,7 @@ export function useSeo(
|
||||
image: string | null
|
||||
): HeadObject {
|
||||
description = description || "Plugin repository for Paper plugins and more!";
|
||||
const canonical = baseUrl() + (route.fullPath.endsWith("/") ? route.fullPath : `${route.fullPath}/`);
|
||||
const canonical = baseUrl() + (route.fullPath.endsWith("/") ? route.fullPath.substring(0, route.fullPath.length - 1) : route.fullPath);
|
||||
image = image || "https://docs.papermc.io/img/paper.png";
|
||||
image = image.startsWith("http") ? image : baseUrl() + image;
|
||||
title = title ? title + " | Hangar" : "Hangar";
|
||||
@ -17,49 +17,40 @@ export function useSeo(
|
||||
title,
|
||||
link: [{ rel: "canonical", href: canonical }],
|
||||
meta: [
|
||||
{ hid: "description", name: "description", content: description },
|
||||
{ property: "description", name: "description", content: description },
|
||||
{
|
||||
property: "og:description",
|
||||
name: "og:description",
|
||||
vmid: "og:description",
|
||||
hid: "og:description",
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
property: "twitter:description",
|
||||
name: "twitter:description",
|
||||
vmid: "twitter:description",
|
||||
hid: "twitter:description",
|
||||
content: description,
|
||||
},
|
||||
{
|
||||
property: "og:title",
|
||||
name: "og:title",
|
||||
hid: "og:title",
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
property: "twitter:title",
|
||||
name: "twitter:title",
|
||||
hid: "twitter:title",
|
||||
content: title,
|
||||
},
|
||||
{
|
||||
property: "og:url",
|
||||
name: "og:url",
|
||||
hid: "og:url",
|
||||
content: canonical,
|
||||
},
|
||||
{
|
||||
property: "twitter:url",
|
||||
name: "twitter:url",
|
||||
hid: "twitter:url",
|
||||
content: canonical,
|
||||
},
|
||||
{
|
||||
property: "og:image",
|
||||
name: "og:image",
|
||||
hid: "og:image",
|
||||
content: image,
|
||||
},
|
||||
],
|
||||
|
@ -52,6 +52,7 @@
|
||||
"noProjects": "There are no projects. 😢",
|
||||
"noProjectsFound": "Found 0 projects. 😢",
|
||||
"title": "Find your favorite plugins",
|
||||
"subTitle": "Hangar allows you to find the best Velocity, Waterfall or Paper plugins for your Minecraft Servers.",
|
||||
"categories": "Categories",
|
||||
"licenses": "Licenses",
|
||||
"versions": "Minecraft versions",
|
||||
|
@ -9,10 +9,7 @@ import { handleRequestError } from "~/composables/useErrorHandling";
|
||||
import { useErrorRedirect } from "~/lib/composables/useErrorRedirect";
|
||||
import ProjectHeader from "~/components/projects/ProjectHeader.vue";
|
||||
import ProjectNav from "~/components/projects/ProjectNav.vue";
|
||||
import { useHead } from "@vueuse/head";
|
||||
import { useSeo } from "~/composables/useSeo";
|
||||
import { projectIconUrl } from "~/composables/useUrlHelper";
|
||||
import { HangarProject, HangarProjectPage } from "hangar-internal";
|
||||
import { HangarProjectPage } from "hangar-internal";
|
||||
|
||||
defineProps({
|
||||
user: {
|
||||
@ -27,8 +24,6 @@ const route = useRoute();
|
||||
const project = await useProject(route.params.user as string, route.params.project as string).catch((e) => handleRequestError(e, ctx, i18n));
|
||||
if (!project || !project.value) {
|
||||
await useRouter().replace(useErrorRedirect(route, 404, "Not found"));
|
||||
} else {
|
||||
useHead(useSeo(project.value.name, project.value.description, route, projectIconUrl(project.value.namespace.owner, project.value.namespace.slug)));
|
||||
}
|
||||
|
||||
provide("updateProjectPages", function (pages: HangarProjectPage[]) {
|
||||
|
@ -12,9 +12,6 @@ import { useRoute, useRouter } from "vue-router";
|
||||
import { useContext } from "vite-ssr/vue";
|
||||
import Markdown from "~/components/Markdown.vue";
|
||||
import ProjectPageList from "~/components/projects/ProjectPageList.vue";
|
||||
import { useHead } from "@vueuse/head";
|
||||
import { useSeo } from "~/composables/useSeo";
|
||||
import { projectIconUrl } from "~/composables/useUrlHelper";
|
||||
import { ref } from "vue";
|
||||
import { useInternalApi } from "~/composables/useApi";
|
||||
import { handleRequestError } from "~/composables/useErrorHandling";
|
||||
@ -56,7 +53,7 @@ function createPinnedVersionUrl(version: PinnedVersion): string {
|
||||
return `/${props.project.namespace.owner}/${props.project.namespace.slug}/versions/${version.name}`;
|
||||
}
|
||||
|
||||
useHead(useSeo(props.project.name, props.project.description, route, projectIconUrl(props.project.namespace.owner, props.project.namespace.slug)));
|
||||
// useSeo is in ProjectPageMarkdown
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -92,7 +89,7 @@ useHead(useSeo(props.project.name, props.project.description, route, projectIcon
|
||||
@save="saveSponsors"
|
||||
/>
|
||||
<template v-else>
|
||||
<h1 class="mt-3 ml-5 text-xl">{{ i18n.t("project.sponsors") }}</h1>
|
||||
<h2 class="mt-3 ml-5 text-xl">{{ i18n.t("project.sponsors") }}</h2>
|
||||
<Markdown :raw="sponsors" class="pt-0" />
|
||||
</template>
|
||||
</Card>
|
||||
@ -100,7 +97,9 @@ useHead(useSeo(props.project.name, props.project.description, route, projectIcon
|
||||
<section class="basis-full md:basis-3/12 space-y-4 min-w-280px">
|
||||
<ProjectInfo :project="project" />
|
||||
<Card>
|
||||
<template #header>{{ i18n.t("project.pinnedVersions") }}</template>
|
||||
<template #header>
|
||||
<h3>{{ i18n.t("project.pinnedVersions") }}</h3>
|
||||
</template>
|
||||
<ul class="divide-y divide-blue-500/50">
|
||||
<li v-for="(version, index) in project.pinnedVersions" :key="`${index}-${version.name}`" class="p-1 py-2">
|
||||
<div class="flex">
|
||||
|
@ -10,9 +10,6 @@ import MarkdownEditor from "~/components/MarkdownEditor.vue";
|
||||
import { hasPerms } from "~/composables/usePerm";
|
||||
import { NamedPermission } from "~/types/enums";
|
||||
import Card from "~/lib/components/design/Card.vue";
|
||||
import { useProjectPage } from "~/composables/useProjectPage";
|
||||
import { useHead } from "@vueuse/head";
|
||||
import { useSeo } from "~/composables/useSeo";
|
||||
import ProjectPageMarkdown from "~/components/projects/ProjectPageMarkdown.vue";
|
||||
import { useOpenProjectPages } from "~/composables/useOpenProjectPages";
|
||||
|
||||
@ -27,6 +24,7 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const open = await useOpenProjectPages(route, props.project);
|
||||
// useSeo is in ProjectPageMarkdown
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -46,15 +46,6 @@ if (!route.params.platform) {
|
||||
const entry = versionMap.keys().next();
|
||||
await (entry.value ? useRouter().replace({ path: `${path}/${entry.value.toLowerCase()}` }) : useRouter().replace(useErrorRedirect(route, 404, "Not found")));
|
||||
}
|
||||
|
||||
useHead(
|
||||
useSeo(
|
||||
(props.project.name + " " + route.params.version) as string,
|
||||
props.project.description,
|
||||
route,
|
||||
projectIconUrl(props.project.namespace.owner, props.project.namespace.slug)
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -152,11 +152,11 @@ async function restoreVersion() {
|
||||
<section class="basis-full md:basis-9/12 flex-grow overflow-auto">
|
||||
<div class="flex flex-wrap gap-2 justify-between">
|
||||
<div>
|
||||
<h1 class="text-3xl sm:inline-flex items-center gap-x-1">
|
||||
<h2 class="text-3xl sm:inline-flex items-center gap-x-1">
|
||||
<TagComponent class="mr-1" :name="projectVersion.channel.name" :color="{ background: projectVersion.channel.color }" :short-form="true" />
|
||||
{{ projectVersion.name }}
|
||||
</h1>
|
||||
<h2>
|
||||
</h2>
|
||||
<h3>
|
||||
<span class="inline-flex <sm:flex-wrap ml-1">
|
||||
{{ i18n.t("version.page.subheader", [projectVersion.author, lastUpdated(new Date(projectVersion.createdAt))]) }}
|
||||
<span v-if="projectVersion.fileInfo?.sizeBytes" class="inline-flex items-center sm:ml-3">
|
||||
@ -164,7 +164,7 @@ async function restoreVersion() {
|
||||
{{ filesize(projectVersion.fileInfo.sizeBytes) }}
|
||||
</span>
|
||||
</span>
|
||||
</h2>
|
||||
</h3>
|
||||
<em v-if="hasPerms(NamedPermission.REVIEWER) && projectVersion.approvedBy" class="text-lg ml-1">
|
||||
{{ i18n.t("version.page.adminMsg", [projectVersion.approvedBy, i18n.d(projectVersion.createdAt, "date")]) }}
|
||||
</em>
|
||||
@ -201,7 +201,9 @@ async function restoreVersion() {
|
||||
</section>
|
||||
<section class="basis-full md:basis-3/12 flex-grow space-y-4">
|
||||
<Card v-if="hasPerms(NamedPermission.DELETE_VERSION) || hasPerms(NamedPermission.VIEW_LOGS) || hasPerms(NamedPermission.REVIEWER)">
|
||||
<template #header>{{ i18n.t("version.page.manage") }}</template>
|
||||
<template #header>
|
||||
<h3>{{ i18n.t("version.page.manage") }}</h3>
|
||||
</template>
|
||||
|
||||
<span class="inline-flex items-center">
|
||||
<IconMdiInformation class="mr-1" />
|
||||
@ -274,7 +276,7 @@ async function restoreVersion() {
|
||||
<Card>
|
||||
<template #header>
|
||||
<div class="inline-flex w-full">
|
||||
<span class="flex-grow">{{ i18n.t("version.page.platform") }}</span>
|
||||
<h3 class="flex-grow">{{ i18n.t("version.page.platform") }}</h3>
|
||||
<PlatformVersionEditModal v-if="hasPerms(NamedPermission.EDIT_VERSION)" :project="project" :versions="versions" />
|
||||
</div>
|
||||
</template>
|
||||
@ -289,7 +291,7 @@ async function restoreVersion() {
|
||||
<Card v-if="projectVersion.pluginDependencies[platform?.name.toUpperCase()] || hasPerms(NamedPermission.EDIT_VERSION)">
|
||||
<template #header>
|
||||
<div class="inline-flex w-full">
|
||||
<span class="flex-grow">{{ i18n.t("version.page.dependencies") }}</span>
|
||||
<h3 class="flex-grow">{{ i18n.t("version.page.dependencies") }}</h3>
|
||||
<DependencyEditModal :project="project" :versions="versions" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -129,7 +129,7 @@ function getVisibilityTitle(visibility: Visibility) {
|
||||
<div class="flex flex-wrap">
|
||||
<div class="basis-full md:basis-5/12 truncate">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<span class="md:basis-full <md:mr-1 text-1.15rem leading-relaxed">{{ item.name }}</span>
|
||||
<h2 class="md:basis-full <md:mr-1 text-1.15rem leading-relaxed">{{ item.name }}</h2>
|
||||
<span class="md:hidden flex-grow"></span>
|
||||
<Tag :name="item.channel.name" :color="{ background: item.channel.color }" />
|
||||
<IconMdiCancel v-if="item.visibility === Visibility.SOFT_DELETE" class="ml-1"></IconMdiCancel>
|
||||
@ -183,10 +183,10 @@ function getVisibilityTitle(visibility: Visibility) {
|
||||
<template #header>
|
||||
<div class="inline-flex w-full flex-cols space-between">
|
||||
<InputCheckbox v-model="filter.allChecked.channels" @change="checkAllChannels" />
|
||||
<span class="flex-grow">{{ i18n.t("version.channels") }}</span>
|
||||
<h3 class="flex-grow">{{ i18n.t("version.channels") }}</h3>
|
||||
<Link v-if="hasPerms(NamedPermission.EDIT_TAGS)" :to="`/${project.owner.name}/${project.name}/channels`">
|
||||
<Button size="small" class="ml-2 text-sm"> <IconMdiPencil /> </Button
|
||||
></Link>
|
||||
<Button size="small" class="ml-2 text-sm"><IconMdiPencil /></Button>
|
||||
</Link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -203,7 +203,7 @@ function getVisibilityTitle(visibility: Visibility) {
|
||||
<template #header>
|
||||
<div class="inline-flex">
|
||||
<InputCheckbox v-model="filter.allChecked.platforms" class="flex-right" @change="checkAllPlatforms" />
|
||||
{{ i18n.t("version.platforms") }}
|
||||
<h3>{{ i18n.t("version.platforms") }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -74,7 +74,7 @@ const buttons = computed<UserButton[]>(() => {
|
||||
|
||||
const isCurrentUser = computed<boolean>(() => authStore.user != null && authStore.user.name === props.user.name);
|
||||
|
||||
useHead(useSeo(props.user.name, props.user.tagline, route, avatarUrl(props.user.name)));
|
||||
useHead(useSeo(props.user.name, props.user.name + " is an author on Hangar. " + props.user.tagline, route, avatarUrl(props.user.name)));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -47,12 +47,16 @@ onMounted(() => {
|
||||
bundle.onload = () => document.body.append(script);
|
||||
});
|
||||
|
||||
useHead(useSeo(i18n.t("apiDocs.title"), null, route, null));
|
||||
useHead(useSeo(i18n.t("apiDocs.title"), "API Docs for the Hangar REST API", route, null));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-100 dark:(bg-gray-200) rounded-md my-auto mx-2 py-1" lg="w-2/3 min-w-2/3 max-w-2/3">
|
||||
<div id="swagger-ui" />
|
||||
<div id="swagger-ui">
|
||||
<h1 class="text-3xl text-bold">Hangar API</h1>
|
||||
<h2 class="text-2xl">API Docs for the Hangar REST API</h2>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -23,7 +23,7 @@ const headers = [
|
||||
{ name: "projectCount", title: i18n.t("pages.headers.projects"), sortable: true },
|
||||
] as Header[];
|
||||
|
||||
useHead(useSeo(i18n.t("pages.authorsTitle"), null, route, null));
|
||||
useHead(useSeo(i18n.t("pages.authorsTitle"), "Hangar Project Authors", route, null));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -124,7 +124,8 @@ useHead(meta);
|
||||
<template>
|
||||
<Container class="flex flex-col items-center gap-4">
|
||||
<Alert v-if="loggedOut" type="success">{{ i18n.t("hangar.loggedOut") }}</Alert>
|
||||
<h2 class="text-3xl font-bold uppercase text-center my-4">{{ i18n.t("hangar.projectSearch.title") }}</h2>
|
||||
<h1 class="text-3xl font-bold uppercase text-center mt-4">{{ i18n.t("hangar.projectSearch.title") }}</h1>
|
||||
<h2 class="text-1xl text-center my-2">{{ i18n.t("hangar.projectSearch.subTitle") }}</h2>
|
||||
<!-- Search Bar -->
|
||||
<div class="relative rounded-md flex shadow-md w-full max-w-screen-md">
|
||||
<!-- Text Input -->
|
||||
@ -167,12 +168,12 @@ useHead(meta);
|
||||
<!-- Sidebar -->
|
||||
<Card accent class="min-w-300px flex flex-col gap-4">
|
||||
<div class="platforms">
|
||||
<h3 class="font-bold mb-1">
|
||||
<h4 class="font-bold mb-1">
|
||||
{{ i18n.t("hangar.projectSearch.platforms") }}
|
||||
<span v-if="filters.platform" class="font-normal text-sm hover:(underline) text-gray-600 dark:text-gray-400" @click="filters.platform = null">
|
||||
{{ i18n.t("hangar.projectSearch.clear") }}
|
||||
</span>
|
||||
</h3>
|
||||
</h4>
|
||||
<div class="flex flex-col gap-1">
|
||||
<ul>
|
||||
<li v-for="platform in backendData.visiblePlatforms" :key="platform.enumName" class="inline-flex w-full">
|
||||
@ -184,7 +185,7 @@ useHead(meta);
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="filters.platform" class="versions">
|
||||
<h3 class="font-bold mb-1">{{ i18n.t("hangar.projectSearch.versions") }}</h3>
|
||||
<h4 class="font-bold mb-1">{{ i18n.t("hangar.projectSearch.versions") }}</h4>
|
||||
<div class="flex flex-col gap-1 max-h-30 overflow-auto">
|
||||
<InputCheckbox
|
||||
v-for="version in versions(filters.platform)"
|
||||
@ -196,7 +197,7 @@ useHead(meta);
|
||||
</div>
|
||||
</div>
|
||||
<div class="categories">
|
||||
<h3 class="font-bold mb-1">{{ i18n.t("hangar.projectSearch.categories") }}</h3>
|
||||
<h4 class="font-bold mb-1">{{ i18n.t("hangar.projectSearch.categories") }}</h4>
|
||||
<div class="flex flex-col gap-1">
|
||||
<InputCheckbox
|
||||
v-for="category in backendData.visibleCategories"
|
||||
@ -210,7 +211,7 @@ useHead(meta);
|
||||
</div>
|
||||
</div>
|
||||
<div class="licenses">
|
||||
<h3 class="font-bold mb-1">{{ i18n.t("hangar.projectSearch.licenses") }}</h3>
|
||||
<h4 class="font-bold mb-1">{{ i18n.t("hangar.projectSearch.licenses") }}</h4>
|
||||
<div class="flex flex-col gap-1">
|
||||
<InputCheckbox v-for="license in backendData.licenses" :key="license" v-model="filters.licenses" :value="license" :label="license">
|
||||
<LicenseLogo :license="license" :size="22" class="mr-1" />
|
||||
|
Loading…
Reference in New Issue
Block a user