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,55 @@
import type { KnowledgeBaseDto } from "../../dtos/KnowledgeBaseDto";
import BreadcrumbSimple from "~/components/ui/breadcrumbs/BreadcrumbSimple";
import type { KbArticleDto } from "~/modules/knowledgeBase/dtos/KbArticleDto";
import type { KbCategoryDto } from "~/modules/knowledgeBase/dtos/KbCategoryDto";
import KbArticleContent from "./KbArticleContent";
import { useParams } from "react-router";
import KnowledgeBaseUtils from "../../utils/KnowledgeBaseUtils";
export default function KbArticle({
kb,
category,
item,
userState,
actions,
withMenu = true,
}: {
kb: KnowledgeBaseDto;
category: KbCategoryDto;
item: KbArticleDto;
userState: {
hasThumbsUp: boolean;
hasThumbsDown: boolean;
};
actions: {
onThumbsUp: () => void;
onThumbsDown: () => void;
};
withMenu?: boolean;
}) {
const params = useParams();
return (
<div className="space-y-6">
{withMenu && (
<BreadcrumbSimple
menu={[
// {
// title: kb.title,
// routePath: KnowledgeBaseUtils.getKbUrl({ kb, params }),
// },
{
title: category.title,
routePath: KnowledgeBaseUtils.getCategoryUrl({ kb, category, params }),
},
{
title: item.title,
routePath: item.href,
},
]}
/>
)}
<KbArticleContent item={item} content={item.contentPublished} userState={userState} actions={actions} />
</div>
);
}
@@ -0,0 +1,98 @@
import { Link } from "react-router";
import { useTranslation } from "react-i18next";
import ThumbsDownIcon from "~/components/ui/icons/ThumbsDownIcon";
import ThumbsDownIconFilled from "~/components/ui/icons/ThumbsDownIconFilled";
import ThumbsUpIcon from "~/components/ui/icons/ThumbsUpIcon";
import ThumbsUpIconFilled from "~/components/ui/icons/ThumbsUpIconFilled";
import { KbArticleDto } from "~/modules/knowledgeBase/dtos/KbArticleDto";
import { renderSanitizedMarkdown } from "~/utils/html-sanitizer";
import { useLocaleFormatters } from "~/locale/format";
export default function KbArticleContent({
item,
content,
actions,
userState,
}: {
item: KbArticleDto;
content: string;
userState?: {
hasThumbsUp: boolean;
hasThumbsDown: boolean;
};
actions?: {
onThumbsUp: () => void;
onThumbsDown: () => void;
};
}) {
const { t } = useTranslation();
const formatters = useLocaleFormatters();
return (
<div>
<div className="flex flex-col space-y-6">
<div className="flex items-center space-x-8">
<div className="flex w-full flex-col">
<div className="flex items-center space-x-3">
<h1 className="text-xl font-bold md:text-3xl">{item.title}</h1>
</div>
<p className="text-muted-foreground mt-2 font-light">{item.description}</p>
<div className="flex justify-between space-x-2">
<li className="mt-5 flex items-center space-x-2">
{item.createdByUser?.avatar && (
<img
src={item.createdByUser.avatar}
alt={`${item.createdByUser.firstName} ${item.createdByUser.lastName}`}
className="h-8 w-8 rounded-full"
/>
)}
<div>
{item.createdByUser && (
<dl className="whitespace-no-wrap text-sm font-medium leading-5">
<dt className="sr-only">{t("knowledgeBase.article.author")}</dt>
<dd className="">
<span className="text-muted-foreground font-normal">{t("shared.by")}</span> {item.createdByUser.firstName} {item.createdByUser.lastName}
</dd>
</dl>
)}
<dl className="whitespace-no-wrap text-xs leading-5">{formatters.relativeDate(item.createdAt)}</dl>
</div>
</li>
{actions && (
<div className="flex items-center space-x-2">
<button type="button" onClick={actions.onThumbsUp} className="text-muted-foreground hover:text-foreground flex items-center space-x-2">
{userState?.hasThumbsUp ? <ThumbsUpIconFilled className="h-4 w-4" /> : <ThumbsUpIcon className="h-4 w-4" />}
</button>
<button type="button" onClick={actions.onThumbsDown} className="text-muted-foreground hover:text-foreground flex items-center space-x-2">
{userState?.hasThumbsDown ? <ThumbsDownIconFilled className="h-4 w-4" /> : <ThumbsDownIcon className="h-4 w-4" />}
</button>
</div>
)}
</div>
</div>
</div>
</div>
<div className="border-border border-b pt-6"></div>
<div className="grid gap-8 sm:grid-cols-12">
<div className="overflow-auto sm:col-span-9">
<div className="prose dark:prose-dark">
<div dangerouslySetInnerHTML={{ __html: renderSanitizedMarkdown(content) }} />
</div>
</div>
{item.relatedArticles.length > 0 && (
<div className="space-y-3 py-5 sm:col-span-3">
<div className="text-muted-foreground text-sm font-medium">{t("knowledgeBase.article.relatedArticles")}</div>
<div className="space-y-2">
{item.relatedArticles.map((x) => (
<Link to={x.href} key={x.href} className="flex items-center space-x-2 hover:underline">
<div className="text-sm">{x.title}</div>
</Link>
))}
</div>
</div>
)}
</div>
</div>
);
}
@@ -0,0 +1,38 @@
import { Link } from "react-router";
import RightIcon from "~/components/ui/icons/RightIcon";
import clsx from "clsx";
import ColorHoverUtils from "~/utils/shared/colors/ColorHoverUtils";
import type { KnowledgeBaseDto } from "../../dtos/KnowledgeBaseDto";
export default function KbArticles({
kb,
items,
}: {
kb: KnowledgeBaseDto;
items: {
order: number;
title: string;
description: string;
href: string;
sectionId: string | null;
}[];
}) {
return (
<div className="rounded-md border border-border bg-background py-3">
{items.map((item) => {
return (
<div key={item.title} className={clsx("group", ColorHoverUtils.getBorder500(kb.color))}>
<Link to={item.href}>
<div className="flex items-center justify-between space-x-2 px-6 py-3 hover:bg-secondary">
<div className="">
<div className={clsx(" text-muted-foreground group-hover:text-foreground")}>{item.title}</div>
</div>
<RightIcon className={clsx("h-5 w-5 shrink-0 text-muted-foreground group-hover:text-foreground")} />
</div>
</Link>
</div>
);
})}
</div>
);
}
@@ -0,0 +1,32 @@
import EmptyState from "~/components/ui/emptyState/EmptyState";
import type { KbCategoryDto } from "../../dtos/KbCategoryDto";
import type { KnowledgeBaseDto } from "../../dtos/KnowledgeBaseDto";
import KbArticles from "./KbArticles";
import KnowledgeBaseUtils from "../../utils/KnowledgeBaseUtils";
import { useTranslation } from "react-i18next";
export default function KbArticlesBySection({ kb, item }: { kb: KnowledgeBaseDto; item: KbCategoryDto }) {
const { t } = useTranslation();
return (
<div>
{item.articles.length === 0 ? (
<EmptyState className="bg-background" captions={{ thereAreNo: t("knowledgeBase.article.empty") }} />
) : (
<div className="space-y-6">
{KnowledgeBaseUtils.getCategoryArticlesBySections({ kb, category: item }).map((item, idx) => {
return (
<div key={idx} className="space-y-3">
{item.section && (
<div className="flex items-center space-x-2">
<div className="text-xl font-bold">{item.section.title}</div>
</div>
)}
<KbArticles kb={kb} items={item.articles} />
</div>
);
})}
</div>
)}
</div>
);
}
@@ -0,0 +1,35 @@
import OrderListButtons from "~/components/ui/sort/OrderListButtons";
import TableSimple from "~/components/ui/tables/TableSimple";
import { useTranslation } from "react-i18next";
export default function KbSortArticles({
items,
}: {
items: {
id: string;
order: number;
title: string;
}[];
}) {
const { t } = useTranslation();
return (
<div className="space-y-1">
<label className="text-muted-foreground block text-xs font-medium">{t("knowledgeBase.authoring.articles.title")}</label>
<TableSimple
items={items}
headers={[
{
name: "order",
title: "",
value: (i, idx) => <OrderListButtons index={idx} items={items} editable={true} />,
},
{
name: "title",
title: t("shared.title"),
value: (item) => item.title,
},
]}
/>
</div>
);
}