Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
+103
@@ -0,0 +1,103 @@
|
||||
import { Link, useNavigate, useParams, useSearchParams } from "react-router";
|
||||
import { Fragment } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import clsx from "clsx";
|
||||
import ColorBackgroundUtils from "~/utils/shared/colors/ColorBackgroundUtils";
|
||||
import ColorTextUtils from "~/utils/shared/colors/ColorTextUtils";
|
||||
import ColorFocusUtils from "~/utils/shared/colors/ColorFocusUtils";
|
||||
import ColorRingUtils from "~/utils/shared/colors/ColorRingUtils";
|
||||
import ColorGradientUtils from "~/utils/shared/colors/ColorGradientUtils";
|
||||
import { logoLight, logoDark } from "~/assets/brand/default";
|
||||
import KnowledgeBaseUtils from "../../utils/KnowledgeBaseUtils";
|
||||
import { KnowledgeBaseDto } from "../../dtos/KnowledgeBaseDto";
|
||||
|
||||
interface Props {
|
||||
kb: KnowledgeBaseDto;
|
||||
}
|
||||
const withTitleAndDescription = true;
|
||||
export default function KbDocsHeader({ kb }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const params = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const logoAlt = t("knowledgeBase.header.logoAlt", { title: kb.title });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={clsx("bg-linear-to-r text-white", ColorGradientUtils.getFrom800To900(kb.color))}>
|
||||
<div className="mx-auto max-w-(--breakpoint-2xl) space-y-8 px-8 py-4">
|
||||
<div className="flex items-center justify-between space-x-2">
|
||||
<Link to={KnowledgeBaseUtils.getKbUrl({ kb, params })} className="flex select-none items-center space-x-2">
|
||||
{kb.logo === "light" ? (
|
||||
<img className="h-8 w-auto" src={logoLight} alt={logoAlt} />
|
||||
) : kb.logo === "dark" ? (
|
||||
<img className="h-8 w-auto" src={logoDark} alt={logoAlt} />
|
||||
) : (
|
||||
<img className="h-8 w-auto" src={kb.logo} alt={logoAlt} />
|
||||
)}
|
||||
{!withTitleAndDescription && (
|
||||
<Fragment>
|
||||
<span className={clsx("hidden px-2 sm:block", ColorTextUtils.getText300(kb.color))}>|</span>
|
||||
<span className="hidden font-semibold sm:block">{kb.title}</span>
|
||||
</Fragment>
|
||||
)}
|
||||
</Link>
|
||||
<div className="flex items-center space-x-4">
|
||||
{kb.links.map((link) => {
|
||||
return (
|
||||
<a key={link.href} href={link.href} className={clsx("transition-colors duration-150 hover:text-white", ColorTextUtils.getText300(kb.color))}>
|
||||
{link.name}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
<div className="flex items-center space-x-2">
|
||||
{kb.languages.length > 1 && (
|
||||
<select
|
||||
id="lang"
|
||||
name="lang"
|
||||
defaultValue={params.lang || kb.defaultLanguage}
|
||||
className={clsx(
|
||||
"block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-white ring-1 ring-inset focus:ring-2 sm:text-sm sm:leading-6",
|
||||
ColorBackgroundUtils.getBg700(kb.color),
|
||||
ColorRingUtils.getRing800(kb.color),
|
||||
ColorFocusUtils.getRing600(kb.color)
|
||||
)}
|
||||
onChange={(e) => {
|
||||
navigate(
|
||||
`${KnowledgeBaseUtils.getKbUrl({
|
||||
kb,
|
||||
params: {
|
||||
lang: e.target.value,
|
||||
},
|
||||
})}?${searchParams.toString()}`
|
||||
);
|
||||
}}
|
||||
>
|
||||
{kb.languages.map((lang) => {
|
||||
const language = KnowledgeBaseUtils.supportedLanguages.find((f) => f.value === lang);
|
||||
if (!language) return null;
|
||||
return (
|
||||
<Fragment key={lang}>
|
||||
<option key={lang} value={lang}>
|
||||
{KnowledgeBaseUtils.getLanguageName(language.value, t)}
|
||||
</option>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* {withTitleAndDescription && (
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-2xl font-semibold tracking-tight sm:text-4xl">{kb.title}</h1>
|
||||
<p className={clsx("text-base", ColorTextUtils.getText300(kb.color))}>{kb.description}</p>
|
||||
</div>
|
||||
)} */}
|
||||
{/* {withSearch && <KbSearch kb={kb} autoFocus={withTitleAndDescription} />} */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
import { useLocation, useSubmit } from "react-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { KnowledgeBaseDto } from "../../dtos/KnowledgeBaseDto";
|
||||
import { KbCategoryDto } from "../../dtos/KbCategoryDto";
|
||||
import clsx from "clsx";
|
||||
import { MobileNav } from "~/components/ui/docs/DocsMobileNav";
|
||||
import { Fragment } from "react";
|
||||
import RightIcon from "~/components/ui/icons/RightIcon";
|
||||
import { ScrollArea } from "~/components/ui/scroll-area";
|
||||
import { SideBarItem } from "~/application/sidebar/SidebarItem";
|
||||
import { KbArticleDto } from "../../dtos/KbArticleDto";
|
||||
import KbArticle from "../articles/KbArticle";
|
||||
import KnowledgeBaseUtils from "../../utils/KnowledgeBaseUtils";
|
||||
import KbDocsHeader from "./KbDocsHeader";
|
||||
import KbFeaturedArticles from "../KbFeaturedArticles";
|
||||
import KbCategories from "../categories/KbCategories";
|
||||
import { DocsSidebarNav } from "~/components/ui/docs/DocsSidebarNav";
|
||||
|
||||
export default function KbDocsLayout({
|
||||
kb,
|
||||
categories,
|
||||
featured,
|
||||
article,
|
||||
}: {
|
||||
kb: KnowledgeBaseDto;
|
||||
categories: KbCategoryDto[];
|
||||
featured?: KbArticleDto[];
|
||||
article?: {
|
||||
article: KbArticleDto;
|
||||
category: KbCategoryDto;
|
||||
userState: {
|
||||
hasThumbsUp: boolean;
|
||||
hasThumbsDown: boolean;
|
||||
};
|
||||
} | null;
|
||||
}) {
|
||||
// const outlet = useOutlet();
|
||||
const location = useLocation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const items: SideBarItem[] = [];
|
||||
categories.forEach((category) => {
|
||||
const sections = KnowledgeBaseUtils.getCategoryArticlesBySections({ kb, category });
|
||||
items.push({
|
||||
title: category.title,
|
||||
path: category.href,
|
||||
items: [{ title: t("shared.overview"), path: category.href }],
|
||||
});
|
||||
let categoryItem = items[items.length - 1];
|
||||
sections.forEach((section) => {
|
||||
if (section.articles.length === 0) return;
|
||||
if (section.section) {
|
||||
items.push({
|
||||
title: section.section.title,
|
||||
path: "",
|
||||
items: [],
|
||||
});
|
||||
categoryItem = items[items.length - 1];
|
||||
}
|
||||
section.articles.forEach((article) => {
|
||||
categoryItem.items?.push({
|
||||
title: article.title,
|
||||
path: article.href,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const currentItem = () => {
|
||||
const findRecursive = (items: SideBarItem[]): SideBarItem | undefined => {
|
||||
for (const item of items) {
|
||||
if (item.path && item.title && item.path === location.pathname) return item;
|
||||
if (item.items) {
|
||||
const found = findRecursive(item.items);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
};
|
||||
return findRecursive(items);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<KbDocsHeader kb={kb} />
|
||||
<div className="mx-auto max-w-(--breakpoint-2xl) flex-1 items-start px-4 pt-3 sm:px-6 md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
|
||||
<nav className={clsx("not-prose border-border mb-2 flex items-center truncate border-b pb-2 md:hidden")}>
|
||||
<ol className="flex items-center space-x-2">
|
||||
<MobileNav items={items} />
|
||||
{currentItem() && (
|
||||
<Fragment>
|
||||
<RightIcon className="text-muted-foreground h-4 w-4 shrink-0" />
|
||||
<div>
|
||||
<h3 className="text-muted-foreground text-base">{currentItem()?.title}</h3>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</ol>
|
||||
</nav>
|
||||
<aside className="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-90px)] w-full shrink-0 md:sticky md:block">
|
||||
<ScrollArea className="h-full pb-6 pr-6 lg:pb-8">
|
||||
<DocsSidebarNav items={items} />
|
||||
</ScrollArea>
|
||||
</aside>
|
||||
{article ? (
|
||||
<Article kb={kb} article={article} />
|
||||
) : (
|
||||
<div>
|
||||
<div className="mx-auto max-w-5xl px-2 py-6 sm:px-8">
|
||||
<div className="space-y-8">
|
||||
{featured && featured.length > 0 && <KbFeaturedArticles kb={kb} items={featured} />}
|
||||
<KbCategories kb={kb} items={categories} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Article({
|
||||
kb,
|
||||
article,
|
||||
}: {
|
||||
kb: KnowledgeBaseDto;
|
||||
article: {
|
||||
article: KbArticleDto;
|
||||
category: KbCategoryDto;
|
||||
userState: {
|
||||
hasThumbsUp: boolean;
|
||||
hasThumbsDown: boolean;
|
||||
};
|
||||
};
|
||||
}) {
|
||||
const submit = useSubmit();
|
||||
function onAction(name: string) {
|
||||
const form = new FormData();
|
||||
form.set("action", name);
|
||||
submit(form, {
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="px-2 py-2 sm:px-8 sm:py-8">
|
||||
<div className="space-y-3">
|
||||
<KbArticle
|
||||
kb={kb}
|
||||
category={article.category}
|
||||
item={article.article}
|
||||
userState={{
|
||||
hasThumbsUp: article.userState.hasThumbsUp,
|
||||
hasThumbsDown: article.userState.hasThumbsDown,
|
||||
}}
|
||||
actions={{
|
||||
onThumbsUp: () => onAction("thumbsUp"),
|
||||
onThumbsDown: () => onAction("thumbsDown"),
|
||||
}}
|
||||
withMenu={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// function getArticles({ kb, category, sectionId }: { kb: KnowledgeBaseDto; category: KbCategoryDto; sectionId: string | null }) {
|
||||
// return category.articles
|
||||
// .filter((f) => f.publishedAt !== null && (sectionId ? f.sectionId === sectionId : true))
|
||||
// .sort((a, b) => a.order - b.order)
|
||||
// .map((article) => ({
|
||||
// title: article.title,
|
||||
// path: KnowledgeBaseUtils.getArticleUrl({ kb, article, params: {} }),
|
||||
// }));
|
||||
// }
|
||||
Reference in new issue
Block a user