feat: services dropdown
All checks were successful
ci / docker (push) Successful in 4m10s

This commit is contained in:
Tine Jozelj 2023-09-13 13:01:52 +02:00
parent 49c564c3d9
commit b659b7ac48
Signed by: mentos1386
SSH key fingerprint: SHA256:MNtTsLbihYaWF8j1fkOHfkKNlnN1JQfxEU/rBU8nCGw
9 changed files with 286 additions and 164 deletions

View file

@ -114,7 +114,7 @@ const config = {
}, },
{ to: "/blog", label: "Blog", position: "left" }, { to: "/blog", label: "Blog", position: "left" },
{ type: "custom-status-navbar-item", position: "left" }, { type: "custom-status-navbar-item", position: "left" },
{ type: "custom-services-navbar-item", position: "left" },
{ {
type: "search", type: "search",
position: "right", position: "right",

View file

@ -331,5 +331,17 @@
}, },
"home.tagline": { "home.tagline": {
"message": "Your personal space in the cloud." "message": "Your personal space in the cloud."
},
"Services": {
"message": "Services"
},
"Public Services": {
"message": "Public Services"
},
"Private Services": {
"message": "Private Services"
},
"Upcoming Services": {
"message": "Upcoming Services"
} }
} }

View file

@ -259,7 +259,7 @@
"description": "The ARIA label and title attribute for expand button of doc sidebar" "description": "The ARIA label and title attribute for expand button of doc sidebar"
}, },
"theme.SearchPage.existingResultsTitle": { "theme.SearchPage.existingResultsTitle": {
"message": "Search results for \"{query}\"", "message": "Rezultati za iskanje \"{query}\"",
"description": "The search page title for non-empty query" "description": "The search page title for non-empty query"
}, },
"theme.SearchPage.emptyResultsTitle": { "theme.SearchPage.emptyResultsTitle": {
@ -331,5 +331,17 @@
}, },
"home.tagline": { "home.tagline": {
"message": "Tvoj osebni prostor v oblaku." "message": "Tvoj osebni prostor v oblaku."
},
"Services": {
"message": "Storitve"
},
"Public Services": {
"message": "Javne Storitve"
},
"Private Services": {
"message": "Zasebne Storitve"
},
"Upcoming Services": {
"message": "Prihajajoče Storitve"
} }
} }

View file

@ -1,162 +1,9 @@
import React from "react"; import React from "react";
import clsx from "clsx"; import clsx from "clsx";
import styles from "./styles.module.css"; import styles from "./styles.module.css";
import { ServiceDescription, sections, services } from "@site/src/services";
type ServicesSection = { function Service({ title, Svg, description }: ServiceDescription) {
title: string;
description: string | JSX.Element;
services: ServiceItem[];
};
type ServiceItem = {
title: string;
Svg: React.ComponentType<React.ComponentProps<"svg">>;
description: JSX.Element;
};
const PrivateServices: ServicesSection = {
title: "Private Services",
description: (
<>
Services that require <b>id.tjo.space</b> account.
</>
),
services: [
{
title: "Chat",
Svg: require("@site/static/img/undraw_chat_tjo_space.svg").default,
description: (
<>
Use <a href="https://chat.tjo.space">chat.tjo.space</a> Metrix server
which you can use to chat with other users of tjo.space or world wide
with anyone else on the Matrix network.
</>
),
},
{
title: "Cloud",
Svg: require("@site/static/img/undraw_cloud_tjo_space.svg").default,
description: (
<>
Use <a href="https://cloud.tjo.space">cloud.tjo.space</a> Nextcloud
server as a personal cloud to store your <b>Photos</b>, <b>Videos</b>,
<b>Files</b>, <b>Calendar</b>, <b>Contacts</b> as well as share all
that with other users of tjo.space or anyone else.
</>
),
},
{
title: "Code",
Svg: require("@site/static/img/undraw_code_tjo_space.svg").default,
description: (
<>
Use <a href="https://code.tjo.space">code.tjo.space</a> Gitea server
as a Git repository and CI/CD system.
</>
),
},
{
title: "Paperless",
Svg: require("@site/static/img/undraw_paperless_tjo_space.svg").default,
description: (
<>
Use <a href="https://paperless.tjo.space">paperless.tjo.space</a>{" "}
Paperless server to archive your documents and make them searchable.
Can be connected to your email provider so that all attachments are
autmatically archived.
</>
),
},
{
title: "RSS",
Svg: require("@site/static/img/undraw_rss_tjo_space.svg").default,
description: (
<>
Use <a href="https://rss.tjo.space">rss.tjo.space</a> server as a RSS
reader for all of your news, blogs and podcasts.
</>
),
},
],
};
const PublicServices: ServicesSection = {
title: "Public Services",
description: "Services that are available to everyone.",
services: [
{
title: "Search",
Svg: require("@site/static/img/undraw_search_tjo_space.svg").default,
description: (
<>
Use <a href="https://search.tjo.space">search.tjo.space</a> private
meta search engine that doesn't track you.
</>
),
},
{
title: "Send",
Svg: require("@site/static/img/undraw_send_tjo_space.svg").default,
description: (
<>
Use <a href="https://send.tjo.space">send.tjo.space</a> to create
private temporary link to share files up to 20GB in size.
</>
),
},
{
title: "Watch",
Svg: require("@site/static/img/undraw_yt_tjo_space.svg").default,
description: (
<>
Use <a href="https://yt.tjo.space">yt.tjo.space</a> an Invidious
server to privately watch YouTube videos.
</>
),
},
],
};
const UpcomingServices: ServicesSection = {
title: "Upcoming Services",
description:
"Services that we plan to provide in the future, but are not quite ready yet.",
services: [
{
title: "Mail",
Svg: require("@site/static/img/undraw_mail_tjo_space.svg").default,
description: (
<>
Use <a href="https://mail.tjo.space">mail.tjo.space</a> to get your
personal <b>@tjo.space</b> email address.
</>
),
},
{
title: "Vault",
Svg: require("@site/static/img/undraw_vault_tjo_space.svg").default,
description: (
<>
Use <a href="https://vault.tjo.space">vault.tjo.space</a> Bitwarden
server to securily store your passwords.
</>
),
},
{
title: "VPN",
Svg: require("@site/static/img/undraw_vpn_tjo_space.svg").default,
description: (
<>
Use <a href="https://vpn.tjo.space">vpn.tjo.space</a> Tailscale server
to securily connect with other tjo.space users or use one of available
exit locations to have secure internet connection.
</>
),
},
],
};
function Service({ title, Svg, description }: ServiceItem) {
return ( return (
<div className={clsx("col col--4 service")}> <div className={clsx("col col--4 service")}>
<div className="text--center"> <div className="text--center">
@ -175,16 +22,18 @@ export default function HomepageFeatures(): JSX.Element {
<section className={styles.features}> <section className={styles.features}>
<div className="container"> <div className="container">
<h1 className={styles.featureHeader}>Services that we provide</h1> <h1 className={styles.featureHeader}>Services that we provide</h1>
{[PublicServices, PrivateServices, UpcomingServices].map( {Object.entries(sections).map(
({ title, description, services }) => { ([sectionKind, { title, description }]) => {
return ( return (
<> <>
<h2 className={styles.featureHeader}>{title}</h2> <h2 className={styles.featureHeader}>{title}</h2>
<h3 className={styles.featureSubHeader}>{description}</h3> <h3 className={styles.featureSubHeader}>{description}</h3>
<div className={clsx("row", styles.row)}> <div className={clsx("row", styles.row)}>
{services.map((props, idx) => ( {services
<Service key={idx} {...props} /> .filter((service) => service.sectionKind === sectionKind)
))} .map((props, idx) => (
<Service key={idx} {...props} />
))}
</div> </div>
</> </>
); );

View file

@ -27,8 +27,7 @@ function Mobile({ healthy, status }) {
href="https://status.tjo.space" href="https://status.tjo.space"
style={{ display: "inline-block", width: "100%" }} style={{ display: "inline-block", width: "100%" }}
> >
{status} {status} <Healthy healthy={healthy}></Healthy>
<Healthy healthy={healthy}></Healthy>
</a> </a>
</li> </li>
); );

View file

@ -0,0 +1,54 @@
import Translate from "@docusaurus/Translate";
import { ServiceSectionKind, services } from "@site/src/services";
import React from "react";
function Mobile() {
return (
<li className="menu__list-item">
<span className="menu__link menu__link--sublist">
<Translate>Services</Translate>
</span>
<ul className="menu__list">
{services
.filter(
(service) => service.sectionKind !== ServiceSectionKind.UPCOMING
)
.map((service) => (
<li className="menu__list-item">
<a className="menu__link" href={service.url}>
{service.title}
</a>
</li>
))}
</ul>
</li>
);
}
export default function ServicesNavbarItem(props: {
content: string;
mobile?: boolean;
}): JSX.Element | null {
if (props.mobile) return <Mobile></Mobile>;
return (
<div className="navbar__item dropdown dropdown--hoverable">
<span className="navbar__link">
<Translate>Services</Translate>
</span>
<ul className="dropdown__menu">
{services
.filter(
(service) => service.sectionKind !== ServiceSectionKind.UPCOMING
)
.map((service) => (
<li>
<a className="dropdown__link" href={service.url}>
{service.title}
</a>
</li>
))}
</ul>
</div>
);
}

View file

@ -28,3 +28,8 @@
--ifm-color-primary-lightest: #e64dff; --ifm-color-primary-lightest: #e64dff;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
} }
.navbar__search-input {
border-radius: var(--ifm-button-border-radius);
width: auto;
}

189
src/services.tsx Normal file
View file

@ -0,0 +1,189 @@
import { translate } from "@docusaurus/Translate";
import React from "react";
export enum ServiceSectionKind {
PUBLIC = "PUBLIC",
PRIVATE = "PRIVATE",
UPCOMING = "UPCOMING",
}
export interface ServicesSection {
title: string;
description: string | JSX.Element;
}
export interface ServiceDescription {
sectionKind: ServiceSectionKind;
title: string;
url: string;
Svg: React.ComponentType<React.ComponentProps<"svg">>;
description: JSX.Element;
}
export const sections: { [key in ServiceSectionKind]: ServicesSection } = {
[ServiceSectionKind.PUBLIC]: {
title: translate({ message: "Public Services" }),
description: "Services that are available to everyone.",
},
[ServiceSectionKind.PRIVATE]: {
title: translate({ message: "Private Services" }),
description: (
<>
Services that require <b>id.tjo.space</b> account.
</>
),
},
[ServiceSectionKind.UPCOMING]: {
title: translate({ message: "Upcoming Services" }),
description:
"Services that we plan to provide in the future, but are not quite ready yet.",
},
};
export const services: ServiceDescription[] = [
{
sectionKind: ServiceSectionKind.PRIVATE,
title: "Chat",
url: "https://chat.tjo.space",
Svg: require("@site/static/img/undraw_chat_tjo_space.svg").default,
description: (
<>
Use <a href="https://chat.tjo.space">chat.tjo.space</a> Metrix server
which you can use to chat with other users of tjo.space or world wide
with anyone else on the Matrix network.
</>
),
},
{
sectionKind: ServiceSectionKind.PRIVATE,
title: "Cloud",
url: "https://code.tjo.space",
Svg: require("@site/static/img/undraw_cloud_tjo_space.svg").default,
description: (
<>
Use <a href="https://cloud.tjo.space">cloud.tjo.space</a> Nextcloud
server as a personal cloud to store your <b>Photos</b>, <b>Videos</b>,
<b>Files</b>, <b>Calendar</b>, <b>Contacts</b> as well as share all that
with other users of tjo.space or anyone else.
</>
),
},
{
sectionKind: ServiceSectionKind.PRIVATE,
title: "Code",
url: "https://code.tjo.space",
Svg: require("@site/static/img/undraw_code_tjo_space.svg").default,
description: (
<>
Use <a href="https://code.tjo.space">code.tjo.space</a> Gitea server as
a Git repository and CI/CD system.
</>
),
},
{
sectionKind: ServiceSectionKind.PRIVATE,
title: "Paperless",
url: "https://paperless.tjo.space",
Svg: require("@site/static/img/undraw_paperless_tjo_space.svg").default,
description: (
<>
Use <a href="https://paperless.tjo.space">paperless.tjo.space</a>{" "}
Paperless server to archive your documents and make them searchable. Can
be connected to your email provider so that all attachments are
autmatically archived.
</>
),
},
{
sectionKind: ServiceSectionKind.PRIVATE,
title: "RSS",
url: "https://rss.tjo.space",
Svg: require("@site/static/img/undraw_rss_tjo_space.svg").default,
description: (
<>
Use <a href="https://rss.tjo.space">rss.tjo.space</a> server as a RSS
reader for all of your news, blogs and podcasts.
</>
),
},
{
sectionKind: ServiceSectionKind.PUBLIC,
title: "Search",
url: "https://search.tjo.space",
Svg: require("@site/static/img/undraw_search_tjo_space.svg").default,
description: (
<>
Use <a href="https://search.tjo.space">search.tjo.space</a> private meta
search engine that doesn't track you.
</>
),
},
{
sectionKind: ServiceSectionKind.PUBLIC,
title: "Send",
url: "https://send.tjo.space",
Svg: require("@site/static/img/undraw_send_tjo_space.svg").default,
description: (
<>
Use <a href="https://send.tjo.space">send.tjo.space</a> to create
private temporary link to share files up to 20GB in size.
</>
),
},
{
sectionKind: ServiceSectionKind.PUBLIC,
title: "Watch",
url: "https://yt.tjo.space",
Svg: require("@site/static/img/undraw_yt_tjo_space.svg").default,
description: (
<>
Use <a href="https://yt.tjo.space">yt.tjo.space</a> an Invidious server
to privately watch YouTube videos.
</>
),
},
{
sectionKind: ServiceSectionKind.UPCOMING,
title: "Mail",
url: "https://mail.tjo.space",
Svg: require("@site/static/img/undraw_mail_tjo_space.svg").default,
description: (
<>
Use <a href="https://mail.tjo.space">mail.tjo.space</a> to get your
personal <b>@tjo.space</b> email address.
</>
),
},
{
sectionKind: ServiceSectionKind.UPCOMING,
title: "Vault",
url: "https://vault.tjo.space",
Svg: require("@site/static/img/undraw_vault_tjo_space.svg").default,
description: (
<>
Use <a href="https://vault.tjo.space">vault.tjo.space</a> Bitwarden
server to securily store your passwords.
</>
),
},
{
sectionKind: ServiceSectionKind.UPCOMING,
title: "VPN",
url: "https://vpn.tjo.space",
Svg: require("@site/static/img/undraw_vpn_tjo_space.svg").default,
description: (
<>
Use <a href="https://vpn.tjo.space">vpn.tjo.space</a> Tailscale server
to securily connect with other tjo.space users or use one of available
exit locations to have secure internet connection.
</>
),
},
].sort((a, b) => {
const titleA = a.title.toUpperCase();
const titleB = b.title.toUpperCase();
if (titleA < titleB) return -1;
if (titleA > titleB) return 1;
return 0;
});

View file

@ -8,6 +8,7 @@ import DocSidebarNavbarItem from "@theme/NavbarItem/DocSidebarNavbarItem";
import DocsVersionNavbarItem from "@theme/NavbarItem/DocsVersionNavbarItem"; import DocsVersionNavbarItem from "@theme/NavbarItem/DocsVersionNavbarItem";
import DocsVersionDropdownNavbarItem from "@theme/NavbarItem/DocsVersionDropdownNavbarItem"; import DocsVersionDropdownNavbarItem from "@theme/NavbarItem/DocsVersionDropdownNavbarItem";
import CustomStatusNavbarItem from "@site/src/components/NavbarItems/CustomStatusNavbarItem"; import CustomStatusNavbarItem from "@site/src/components/NavbarItems/CustomStatusNavbarItem";
import ServicesNavbarItem from "@site/src/components/NavbarItems/ServicesNavbarItem";
const ComponentTypes = { const ComponentTypes = {
default: DefaultNavbarItem, default: DefaultNavbarItem,
localeDropdown: LocaleDropdownNavbarItem, localeDropdown: LocaleDropdownNavbarItem,
@ -22,4 +23,5 @@ const ComponentTypes = {
export default { export default {
...ComponentTypes, ...ComponentTypes,
"custom-status-navbar-item": CustomStatusNavbarItem, "custom-status-navbar-item": CustomStatusNavbarItem,
"custom-services-navbar-item": ServicesNavbarItem,
}; };