Track HiveGrid Cloud planning reference

This commit is contained in:
david committed 2026-06-26 20:48:01 +02:00
1 parent b3d5ea6764
commit e341c5938a
5784 files changed
+1104929 -3

No files matched your search

@@ -0,0 +1,97 @@
import { ActionFunctionArgs, LoaderFunctionArgs, redirect } from "react-router";
import { MetaTagsDto } from "~/application/dtos/seo/MetaTagsDto";
import { getUserInfo } from "~/utils/session.server";
import { createKnowledgeBaseArticleView, getArticleStateByUserAnalyticsId, voteArticle } from "../../db/kbAnalytics.db.server";
import { KbArticleDto } from "../../dtos/KbArticleDto";
import { KbCategoryDto } from "../../dtos/KbCategoryDto";
import { KnowledgeBaseDto } from "../../dtos/KnowledgeBaseDto";
import KnowledgeBaseService from "../../service/KnowledgeBaseService.server";
import KnowledgeBaseUtils from "../../utils/KnowledgeBaseUtils";
import { KbSearchResultDto } from "../../dtos/KbSearchResultDto";
import { getUser } from "~/utils/db/users.db.server";
import { getAnalyticsInfo } from "~/utils/analyticsCookie.server";
import { getTranslations } from "~/locale/i18next.server";
export namespace KbRoutesArticleApi {
export type LoaderData = {
metatags?: MetaTagsDto;
kb: KnowledgeBaseDto;
search: KbSearchResultDto | undefined;
item: {
article: KbArticleDto;
category: KbCategoryDto;
} | null;
userState: {
hasThumbsUp: boolean;
hasThumbsDown: boolean;
};
isAdmin: boolean;
categories: KbCategoryDto[];
};
export const loader = async ({ request, params }: LoaderFunctionArgs, { kbSlug }: { kbSlug?: string } = {}) => {
const analyticsInfo = await getAnalyticsInfo(request);
const userInfo = await getUserInfo(request);
const kb = await KnowledgeBaseService.get({ slug: kbSlug ?? params.slug!, enabled: true, request });
const item = await KnowledgeBaseService.getArticle({
kb,
slug: params.article ?? "",
params,
request,
});
if (!item) {
throw redirect(KnowledgeBaseUtils.getKbUrl({ kb, params }));
}
if (item?.article) {
createKnowledgeBaseArticleView({ userAnalyticsId: analyticsInfo.userAnalyticsId, articleId: item.article.id });
}
let userState = await getArticleStateByUserAnalyticsId({
userAnalyticsId: analyticsInfo.userAnalyticsId,
articleId: item?.article.id ?? "",
});
const searchParams = new URL(request.url).searchParams;
const query = searchParams.get("q")?.toString();
const currentUser = await getUser(userInfo.userId);
KnowledgeBaseUtils.fixDarkMode({ article: item.article });
const data: LoaderData = {
metatags: item?.article.metatags,
kb,
search: await KnowledgeBaseService.search({ query, kb, params, request }),
item,
userState,
isAdmin: !!currentUser?.admin,
categories: await KnowledgeBaseService.getCategories({
kb,
params,
request,
}),
};
return data;
};
export const action = async ({ request, params }: ActionFunctionArgs, { kbSlug }: { kbSlug?: string } = {}) => {
const { t } = await getTranslations(request);
const { userAnalyticsId } = await getAnalyticsInfo(request);
const form = await request.formData();
const action = form.get("action") as string;
const kb = await KnowledgeBaseService.get({ slug: kbSlug ?? params.slug!, enabled: true, request });
const item = await KnowledgeBaseService.getArticle({
kb,
slug: params.article ?? "",
params,
request,
});
if (!item) {
return Response.json({ error: t("knowledgeBase.public.errors.articleNotFound") }, { status: 404 });
}
if (action === "thumbsUp") {
await voteArticle({ userAnalyticsId, articleId: item.article.id, type: "up" });
return Response.json({ success: true });
} else if (action === "thumbsDown") {
await voteArticle({ userAnalyticsId, articleId: item.article.id, type: "down" });
return Response.json({ success: true });
}
return Response.json({ error: t("knowledgeBase.public.errors.invalidAction") }, { status: 400 });
};
}
@@ -0,0 +1,48 @@
import { LoaderFunctionArgs, redirect } from "react-router";
import { MetaTagsDto } from "~/application/dtos/seo/MetaTagsDto";
import { KbCategoryDto } from "../../dtos/KbCategoryDto";
import { KnowledgeBaseDto } from "../../dtos/KnowledgeBaseDto";
import KnowledgeBaseService from "../../service/KnowledgeBaseService.server";
import KnowledgeBaseUtils from "../../utils/KnowledgeBaseUtils";
import { KbSearchResultDto } from "../../dtos/KbSearchResultDto";
export namespace KbRoutesCategoryApi {
export type LoaderData = {
metatags?: MetaTagsDto;
kb: KnowledgeBaseDto;
search: KbSearchResultDto | undefined;
item: KbCategoryDto | null;
language: string;
allCategories: KbCategoryDto[];
};
export const loader = async ({ request, params }: LoaderFunctionArgs, { kbSlug }: { kbSlug?: string } = {}) => {
const kb = await KnowledgeBaseService.get({ slug: kbSlug ?? params.slug!, enabled: true, request });
const language = params.lang ?? kb.defaultLanguage;
const item = await KnowledgeBaseService.getCategory({
kb,
category: params.category ?? "",
language,
params,
request,
});
if (!item) {
throw redirect(KnowledgeBaseUtils.getKbUrl({ kb, params }));
}
const searchParams = new URL(request.url).searchParams;
const query = searchParams.get("q")?.toString();
const data: LoaderData = {
metatags: item?.metatags,
kb,
search: await KnowledgeBaseService.search({ query, kb, params, request }),
item,
allCategories: await KnowledgeBaseService.getCategories({
kb,
params,
request,
}),
language,
};
return data;
};
}
@@ -0,0 +1,55 @@
import { LoaderFunctionArgs } from "react-router";
import { MetaTagsDto } from "~/application/dtos/seo/MetaTagsDto";
import { createKnowledgeBaseView } from "../../db/kbAnalytics.db.server";
import { KbArticleDto } from "../../dtos/KbArticleDto";
import { KbCategoryDto } from "../../dtos/KbCategoryDto";
import { KnowledgeBaseDto } from "../../dtos/KnowledgeBaseDto";
import KnowledgeBaseService from "../../service/KnowledgeBaseService.server";
import { KbSearchResultDto } from "../../dtos/KbSearchResultDto";
import KnowledgeBaseUtils from "../../utils/KnowledgeBaseUtils";
import { getAnalyticsInfo } from "~/utils/analyticsCookie.server";
import { getUserInfo } from "~/utils/session.server";
import { getUser } from "~/utils/db/users.db.server";
export namespace KbRoutesIndexApi {
export type LoaderData = {
metatags: MetaTagsDto;
kb: KnowledgeBaseDto;
search: KbSearchResultDto | undefined;
categories: KbCategoryDto[];
featured: KbArticleDto[];
isAdmin: boolean;
};
export const loader = async ({ request, params }: LoaderFunctionArgs, { kbSlug }: { kbSlug?: string } = {}) => {
if (params.lang && !KnowledgeBaseUtils.supportedLanguages.find((f) => f.value === params.lang)) {
throw Response.json({}, { status: 404 });
}
const kb = await KnowledgeBaseService.get({ slug: kbSlug ?? params.slug!, enabled: true, request });
const { userAnalyticsId } = await getAnalyticsInfo(request);
const userInfo = await getUserInfo(request);
await createKnowledgeBaseView({ userAnalyticsId, knowledgeBaseId: kb.id });
const searchParams = new URL(request.url).searchParams;
const query = searchParams.get("q")?.toString();
const currentUser = await getUser(userInfo.userId);
const data: LoaderData = {
metatags: kb.metatags,
kb,
search: await KnowledgeBaseService.search({ query, kb, params, request }),
categories: await KnowledgeBaseService.getCategories({
kb,
params,
request,
}),
featured: await KnowledgeBaseService.getFeaturedArticles({
kb,
params,
request,
}),
isAdmin: !!currentUser?.admin,
// searchResult,
};
return data;
};
}
@@ -0,0 +1,57 @@
import { useLoaderData, useSubmit } from "react-router";
import { useTranslation } from "react-i18next";
import KbHeader from "../../components/KbHeader";
import KbArticle from "../../components/articles/KbArticle";
import { KbRoutesArticleApi } from "../api/KbRoutes.Article.Api";
import KbDocsLayout from "../../components/layouts/KbDocsLayout";
export default function KbRoutesArticleView() {
const data = useLoaderData<KbRoutesArticleApi.LoaderData>();
const submit = useSubmit();
const { t } = useTranslation();
if (data.kb.layout === "docs") {
return (
<KbDocsLayout
kb={data.kb}
categories={data.categories}
article={!data.item ? null : { ...data.item, userState: data.userState }}
/>
);
}
function onAction(name: string) {
const form = new FormData();
form.set("action", name);
submit(form, {
method: "post",
});
}
return (
<div className="bg-background text-foreground min-h-screen">
<KbHeader kb={data.kb} withTitleAndDescription={false} withSearch={false} />
<div className="mx-auto max-w-5xl px-8 py-8">
<div className="space-y-3">
{!data.item ? (
<div>{t("knowledgeBase.public.errors.articleNotFound")}</div>
) : (
<KbArticle
kb={data.kb}
category={data.item.category}
item={data.item.article}
userState={{
hasThumbsUp: data.userState.hasThumbsUp,
hasThumbsDown: data.userState.hasThumbsDown,
}}
actions={{
onThumbsUp: () => onAction("thumbsUp"),
onThumbsDown: () => onAction("thumbsDown"),
}}
/>
)}
</div>
</div>
</div>
);
}
@@ -0,0 +1,18 @@
import { useLoaderData } from "react-router";
import { useTranslation } from "react-i18next";
import KbHeader from "../../components/KbHeader";
import KbCategory from "../../components/categories/KbCategory";
import { KbRoutesCategoryApi } from "../api/KbRoutes.Category.Api";
export default function KbRoutesCategoryView() {
const data = useLoaderData<KbRoutesCategoryApi.LoaderData>();
const { t } = useTranslation();
return (
<div className="bg-background text-foreground min-h-screen">
<KbHeader kb={data.kb} withTitleAndDescription={false} />
<div className="mx-auto min-h-screen max-w-5xl px-8 py-8">
<div className="space-y-5">{!data.item ? <div>{t("knowledgeBase.public.errors.categoryNotFound")}</div> : <KbCategory kb={data.kb} item={data.item} allCategories={data.allCategories} />}</div>
</div>
</div>
);
}
@@ -0,0 +1,26 @@
import { KbRoutesIndexApi } from "../api/KbRoutes.Index.Api";
import KbHeader from "../../components/KbHeader";
import KbFeaturedArticles from "../../components/KbFeaturedArticles";
import KbCategories from "../../components/categories/KbCategories";
import KbDocsLayout from "../../components/layouts/KbDocsLayout";
import { useLoaderData } from "react-router";
export default function KbRoutesIndex() {
const data = useLoaderData<KbRoutesIndexApi.LoaderData>();
if (data.kb.layout === "docs") {
return <KbDocsLayout kb={data.kb} categories={data.categories} featured={data.featured} />;
}
return (
<div className="bg-background text-foreground min-h-screen">
<KbHeader kb={data.kb} withTitleAndDescription={true} />
<div className="mx-auto max-w-5xl px-2 py-6 sm:px-8">
<div className="space-y-8">
{data.featured.length > 0 && <KbFeaturedArticles kb={data.kb} items={data.featured} />}
<KbCategories kb={data.kb} items={data.categories} />
</div>
</div>
</div>
);
}