/ opencodehub / src / pages / index.astro
swadhinbiswas feat: implement air-gapped mode, UI fixes, and project management integrations · 7/9/2026
e3bd4a1
150 lines · 5.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
---
import { getDatabase, schema } from "@/db";
import BaseLayout from "@/layouts/BaseLayout.astro";
import { eq } from "drizzle-orm";
import { BookOpen, Star } from "lucide-react";
import HeroSection from "@/components/home/HeroSection";
import FeaturesSection from "@/components/home/FeaturesSection";
import FeatureDemosSection from "@/components/home/FeatureDemosSection";
import BackgroundGrid from "@/components/ui/BackgroundGrid.astro";

// Fetch public repositories for the landing page
const db = getDatabase();
let recentRepos: any[] = [];
let stats = {
  repos: 0,
  users: 0,
  commits: 0,
};

try {
  // Get recent public repos
  recentRepos = await db.query.repositories.findMany({
    where: eq(schema.repositories.visibility, "public"),
    orderBy: (repositories, { desc }) => [desc(repositories.updatedAt)],
    limit: 6,
    with: {
      owner: true,
    },
  });

  // Get basic stats
  const repositories = await db.query.repositories.findMany();
  const repoCount = repositories.length;
  const users = await db.query.users.findMany();
  const userCount = users.length;

  stats.repos = repoCount;
  stats.users = userCount;
} catch (e) {
  console.error("Failed to fetch data:", e);
}
---

<BaseLayout title="OpenCodeHub - Your Self-Hosted Git Platform">
  <BackgroundGrid />
  <!-- Hero Section with Aceternity UI Components -->
  <HeroSection client:load />

  <!-- Trending Repositories Section -->
  {
    recentRepos.length > 0 && (
      <section class="container py-20 border-t border-border">
        <div class="flex items-center justify-between mb-10">
          <h2 class="text-2xl font-semibold tracking-tight text-foreground">
            Trending Repositories
          </h2>
          <a
            href="/explore"
            class="text-sm text-primary hover:text-primary/80 transition-colors"
          >
            View all →
          </a>
        </div>
        <div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
          {recentRepos.map((repo: any) => (
            <div class="group relative rounded-xl border border-border bg-card p-6 transition-all duration-300 hover:border-primary/30 hover:shadow-lg hover:-translate-y-1">
              <div class="flex items-center justify-between mb-4">
                <div class="flex items-center gap-2">
                  <BookOpen className="h-4 w-4 text-muted-foreground" />
                  <a
                    href={`/${repo.owner.username}/${repo.name}`}
                    class="font-medium text-foreground hover:text-primary transition-colors"
                  >
                    {repo.owner.username}/{repo.name}
                  </a>
                </div>
                <div class="flex items-center gap-1 text-muted-foreground">
                  <Star className="h-3.5 w-3.5" />
                  <span class="text-xs font-medium">{repo.starCount}</span>
                </div>
              </div>
              <p class="text-sm text-muted-foreground line-clamp-2 mb-4">
                {repo.description || "No description provided."}
              </p>
              <div class="flex items-center gap-4 text-xs text-muted-foreground">
                {repo.language && (
                  <div class="flex items-center gap-1.5">
                    <span class="h-2.5 w-2.5 rounded-full bg-primary" />
                    {repo.language}
                  </div>
                )}
                <div>
                  Updated {new Date(repo.updatedAt).toLocaleDateString()}
                </div>
              </div>
            </div>
          ))}
        </div>
      </section>
    )
  }

  <!-- Interactive Feature Demonstrations -->
  <FeatureDemosSection client:load />

  <!-- Features Section with Aceternity UI Components -->
  <FeaturesSection client:load />

  <!-- Footer CTA -->
  <section class="container py-20 border-t border-border">
    <div
      class="relative rounded-2xl border border-border bg-gradient-to-br from-primary/10 via-transparent to-purple-500/10 p-12 overflow-hidden"
    >
      <!-- Background decorations -->
      <div
        class="absolute top-0 right-0 w-64 h-64 bg-primary/20 rounded-full blur-[100px] pointer-events-none"
      >
      </div>
      <div
        class="absolute bottom-0 left-0 w-48 h-48 bg-purple-500/20 rounded-full blur-[80px] pointer-events-none"
      >
      </div>

      <div class="relative z-10 text-center max-w-2xl mx-auto">
        <h2 class="text-3xl md:text-4xl font-bold mb-4 text-foreground">
          Ready to ship faster?
        </h2>
        <p class="text-muted-foreground mb-8">
          Join thousands of developers using OpenCodeHub to streamline their
          workflow with stacked PRs, AI reviews, and smart merge queues.
        </p>
        <div class="flex flex-col sm:flex-row gap-4 justify-center">
          <a
            href="/register"
            class="inline-flex items-center justify-center rounded-lg bg-primary px-8 py-3.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 transition-all hover:scale-105 shadow-md shadow-primary/20"
          >
            Get Started Free
          </a>
          <a
            href="https://github.com/swadhinbiswas/OpencodeHub"
            class="inline-flex items-center justify-center rounded-lg border border-border bg-secondary/50 px-8 py-3.5 text-sm font-medium text-foreground hover:bg-secondary transition-all"
          >
            Star on GitHub ⭐
          </a>
        </div>
      </div>
    </div>
  </section>
</BaseLayout>