import React from "react";
import type { ReactView } from "@ethicdevs/react-monolith";
import type { CommonProps } from "../types";
import { AppRoute } from "../routes.defs";
import { Button } from "../components/Button.styled";
import { Card } from "../components/Card.styled";
import { Grid } from "../components/Grid";
import { Layout } from "../components/Layout";
import { PageWrapper } from "../components/PageWrapper";
import {} from "../components";
import { buildRouteLink } from "../utils/shared";
export interface HomeViewProps extends CommonProps {
foo?: boolean;
}
const tagline =
"GitFOSS is a small and simple, free and open-source Git Forge built w/ Node.JS, Fastify, Prisma and React!";
type FeatureCard = {
title: string;
description: string;
icon?: string;
image?: string;
};
const features: FeatureCard[] = [
{
title: "Free hosted or self-hosted",
description:
"Free hosting for everyone, or self-hosted deployments, both free.",
icon: "π ",
},
{
title: "MIT Licensed Open Source",
description: "Fully Open Source Software licensed under the MIT License.",
icon: "βοΈ",
},
{
title: "Modern tech stack",
description:
"Written with React, TypeScript, Prisma, and MVC architecture.",
icon: "π§°",
},
{
title: "Git repo browser",
description: "Files, branches, and tags are browsable in the UI.",
icon: "ποΈ",
},
{
title: "PRs & merges",
description: "Comment, review, merge, and delete source branches.",
icon: "π",
image: "https://i.ibb.co/xp1bfGG/image.png",
},
{
title: "Fork repositories",
description: "Fork repository workflow support for collaboration.",
icon: "π΄",
image: "https://i.ibb.co/1Ghq4j73/image.png",
},
{
title: "Tests & Coverage UI",
description: "Browser-based tests and coverage details.",
icon: "β
",
image: "https://i.ibb.co/bgvkWwFh/image.png",
},
{
title: "Builds & CI",
description: "Pipelines, actions, and a GitFOSS runner for CI.",
icon: "ποΈ",
image: "https://i.ibb.co/bgvkWwFh/image.png",
},
{
title: "Deploy",
description: "Pipelines and continuous deployment capabilities.",
icon: "π",
image: "https://i.ibb.co/bgvkWwFh/image.png",
},
{
title: "API Reference",
description: "Auto-generated API reference from repos and docs.",
icon: "π",
image: "https://i.ibb.co/bgvkWwFh/image.png",
},
{
title: "AI assistant",
description:
"Small no-context model leveraging repo docs, API reference, and docs to assist.",
icon: "π€",
image: "https://i.ibb.co/bgvkWwFh/image.png",
},
{
title: "SSH Keys management",
description: "SSH keys management for secure access to repos.",
icon: "π",
image: "https://i.ibb.co/bgvkWwFh/image.png",
},
{
title: "User profiles",
description:
"Profiles with starred/followed repos, followers, and user readmes/links.",
icon: "π§βπ»",
image: "https://i.ibb.co/bgvkWwFh/image.png",
},
];
function generateDescription(base: string, idx: number): string {
const extras = [
"This feature is integrated with a modern, scalable workflow.",
"Discover how this can accelerate your teamβs productivity.",
"Includes settings and CLI for automation.",
];
const extra = extras[idx % extras.length];
return `${base} ${extra}`;
}
const HomeView: ReactView<HomeViewProps> = (props) => {
const { commonProps } = props;
const primary = features.slice(0, 4);
const rest = features.slice(4);
return (
<Layout {...commonProps}>
<PageWrapper style={{ maxWidth: 960, margin: "0 auto" }}>
{}
<Grid.Row fluid style={{ minHeight: "72vh", alignItems: "center" }}>
<Grid.Col
fluid
nowrap
justifyContent={"center"}
style={{
marginTop: 80,
paddingRight: 20,
minWidth: 420,
}}
>
<h1 style={{ fontSize: "3rem", margin: 0, lineHeight: 1.05 }}>
GitFOSS
</h1>
<p style={{ fontSize: "1.15rem", color: "#555", maxWidth: 600 }}>
{tagline}
</p>
<Grid.Row fluid gap={16} style={{ marginTop: 20 }}>
<Button as="a" href={buildRouteLink(AppRoute.AUTH_REGISTER, {})}>
Register
</Button>
<Button as="a" href={buildRouteLink(AppRoute.AUTH_LOGIN, {})}>
Login
</Button>
<Button
as="a"
href={buildRouteLink(AppRoute.REPOSITORY_EXPLORE, {})}
>
Explore
</Button>
</Grid.Row>
</Grid.Col>
<img
src="https://i.ibb.co/6cCs4VgG/image.png"
alt="GitFOSS hero illustration"
style={{
width: "100%",
maxWidth: 500,
minWidth: 400,
height: "auto",
marginTop: 24,
objectFit: "contain",
borderRadius: 24,
}}
/>
</Grid.Row>
{}
<Grid.Row fluid gap={16} style={{ padding: "10vh 0 20vh 0" }}>
{primary.map((f, idx) => (
<Grid.Col key={idx} fluid nowrap>
<Card
themeScheme={commonProps.themeScheme}
style={{ minHeight: 238, minWidth: 210 }}
>
<div
style={{
display: "flex",
flexDirection: "column",
gap: 12,
}}
>
<div
className="icon"
aria-label={f.title}
style={{ fontSize: 48 }}
>
{f.icon ?? "β¨"}
</div>
<div
className="feature-title"
style={{ fontWeight: 700, fontSize: 20 }}
>
{f.title}
</div>
<div className="feature-desc" style={{ color: "#777777" }}>
{f.description}
</div>
</div>
</Card>
</Grid.Col>
))}
</Grid.Row>
{}
{rest.map((f, idx) => {
const genDesc = generateDescription(f.description, idx);
const isEven = idx % 2 === 0;
const imageSrc = f.image ?? `/assets/feature-${idx + 4}.png`;
return (
<section
key={idx}
aria-label={`Feature: ${f.title}`}
style={{ padding: "40px 0", minHeight: "80vh" }}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: 48,
flexWrap: "wrap",
}}
>
{isEven && (
<>
<div style={{ flex: 1, minWidth: 260 }}>
<img
src={imageSrc}
alt={`${f.title} illustration`}
style={{
width: "100%",
height: "auto",
borderRadius: 24,
}}
/>
</div>
<div style={{ flex: 1, minWidth: 260 }}>
<h1 style={{ margin: 0, fontSize: 48 }}>{f.title}</h1>
<h3
className="feature-desc"
style={{
color: "#777777",
lineHeight: 1.4,
fontWeight: "normal",
}}
>
{genDesc}
</h3>
<div
className="cta"
style={{ display: "flex", gap: 16, marginTop: 16 }}
>
<Button
as="a"
href={buildRouteLink(AppRoute.AUTH_REGISTER, {})}
>
Register
</Button>
<Button
as="a"
href={buildRouteLink(AppRoute.AUTH_LOGIN, {})}
>
Login
</Button>
</div>
</div>
</>
)}
{!isEven && (
<>
<div style={{ flex: 1, minWidth: 260 }}>
<h1 style={{ margin: 0, fontSize: 48 }}>{f.title}</h1>
<h3
className="feature-desc"
style={{
color: "#777777",
lineHeight: 1.4,
fontWeight: "normal",
}}
>
{genDesc}
</h3>
<div
className="cta"
style={{ display: "flex", gap: 16, marginTop: 16 }}
>
<Button
as="a"
href={buildRouteLink(AppRoute.AUTH_REGISTER, {})}
>
Register
</Button>
<Button
as="a"
href={buildRouteLink(AppRoute.AUTH_LOGIN, {})}
>
Login
</Button>
</div>
</div>
<div style={{ flex: 1, minWidth: 260 }}>
<img
src={imageSrc}
alt={`${f.title} illustration`}
style={{
width: "100%",
height: "auto",
borderRadius: 24,
}}
/>
</div>
</>
)}
</div>
</section>
);
})}
{}
<section
aria-label="Contribute to GitFOSS"
style={{
padding: "48px 0",
borderTop: "2px dashed #4e7b50",
borderBottom: "2px dashed #4e7b50",
width: "100%",
}}
>
<Grid.Col
fluid
nowrap
justifyContent={"center"}
alignItems={"center"}
>
<h1
style={{
margin: "0 0 8px",
maxWidth: 400,
fontSize: 48,
textAlign: "center",
}}
>
Contribute to GitFOSS
</h1>
<h3
style={{
color: "#555",
margin: "16px 0",
maxWidth: 400,
textAlign: "center",
fontWeight: "normal",
}}
>
Help grow GitFOSS by contributing code, docs, and governance. Your
involvement keeps the project open, fast, and secure.
</h3>
<Button
as="a"
href="/ethicdevs/gitfoss"
style={{ width: 300, height: 60, fontSize: 24, marginTop: 24 }}
>
Contribute Now
</Button>
</Grid.Col>
</section>
</PageWrapper>
</Layout>
);
};
HomeView.displayName = "HomeView";
export default HomeView;