Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions frontend/src/components/GitHubStarsLink.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react";

const GITHUB_REPO = "jaydeeprusia/kill-switch";
const GITHUB_REPO_URL = `https://github.com/${GITHUB_REPO}`;
const GITHUB_STARS_CACHE_KEY = "killswitch_github_stars";

function formatStarCount(count) {
if (count >= 1000000) {
return `${(count / 1000000).toFixed(1).replace(/\.0$/, "")}M`;
}
if (count >= 1000) {
return `${(count / 1000).toFixed(1).replace(/\.0$/, "")}K`;
}
return `${count}`;
}

export default function GitHubStarsLink({ className = "", testId = "github-link" }) {
const [starCount, setStarCount] = React.useState(null);

React.useEffect(() => {
let ignore = false;

const cachedStars = window.localStorage.getItem(GITHUB_STARS_CACHE_KEY);
if (cachedStars && !Number.isNaN(Number(cachedStars))) {
setStarCount(Number(cachedStars));
}
Comment on lines +20 to +26

const controller = new AbortController();

fetch(`https://api.github.com/repos/${GITHUB_REPO}`, {
signal: controller.signal,
})
.then((res) => {
Comment on lines +28 to +33
if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);
return res.json();
})
.then((data) => {
if (ignore || typeof data?.stargazers_count !== "number") return;
setStarCount(data.stargazers_count);
window.localStorage.setItem(GITHUB_STARS_CACHE_KEY, String(data.stargazers_count));
})
.catch(() => {
// Keep cached value if API call fails.
});

return () => {
ignore = true;
controller.abort();
};
}, []);

return (
<a
href={GITHUB_REPO_URL}
target="_blank"
rel="noopener noreferrer"
className={`group inline-flex items-center gap-2 rounded-full border border-zinc-200 bg-zinc-50/80 px-3 py-1.5 text-[14px] font-medium tracking-tight text-zinc-700 shadow-sm shadow-zinc-200/40 transition-all hover:border-zinc-300 hover:bg-white hover:text-zinc-950 hover:shadow-md hover:shadow-zinc-200/50 ${className}`}
data-testid={testId}
aria-label={`View ${GITHUB_REPO} on GitHub`}
>
<svg className="size-4 text-zinc-500 transition-colors group-hover:text-zinc-950" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
</svg>
<span className="tabular-nums">{typeof starCount === "number" ? formatStarCount(starCount) : "-"}</span>
</a>
);
}
30 changes: 23 additions & 7 deletions frontend/src/pages/AppShell.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { Link, useLocation } from "react-router-dom";
import { useAuth } from "../lib/auth";
import GitHubStarsLink from "../components/GitHubStarsLink";

export default function AppShell({ children }) {
const { user, logout } = useAuth();
const location = useLocation();
const navigate = useNavigate();

const nav = [
{to: "/", label: "Home" },
{ to: "/", label: "Home" },
{ to: "/dashboard", label: "Ideas" },
{ to: "/new", label: "New idea" },
{ to: "/profile", label: "Profile" },
Expand Down Expand Up @@ -57,6 +57,9 @@ export default function AppShell({ children }) {
</nav>
</div>
<div className="flex items-center gap-3">
<GitHubStarsLink
className="hidden lg:flex items-center gap-2 text-[15px] font-medium tracking-tight text-zinc-600 hover:text-zinc-950 transition-colors"
/>
{user && (
<div
className="hidden sm:flex items-center gap-2 pr-2 text-sm text-zinc-600"
Expand Down Expand Up @@ -84,18 +87,31 @@ export default function AppShell({ children }) {
</div>
</div>
</header>
<main className="max-w-7xl w-full mx-auto px-6 md:px-8 py-10 flex-1 flex flex-col">{children}</main>
<main className="max-w-7xl w-full mx-auto px-6 md:px-8 py-10 flex-1 flex flex-col">
{children}
</main>
<footer className="border-t border-zinc-200">
<div className="max-w-7xl mx-auto px-6 md:px-8 py-4 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between text-[10px] text-zinc-500 font-mono uppercase tracking-[0.18em]">
<span>Killswitch by Softwired</span>
<div className="flex items-center gap-4">
<a href="https://softwired.in" target="_blank" rel="noreferrer" className="hover:text-zinc-950 transition-colors">
<a
href="https://softwired.in"
target="_blank"
rel="noreferrer"
className="hover:text-zinc-950 transition-colors"
>
softwired.in
</a>
<a href="mailto:admin@softwired.in" className="hover:text-zinc-950 transition-colors">
<a
href="mailto:admin@softwired.in"
className="hover:text-zinc-950 transition-colors"
>
admin@softwired.in
</a>
<Link to="/contact" className="hover:text-zinc-950 transition-colors">
<Link
to="/contact"
className="hover:text-zinc-950 transition-colors"
>
Contact / Feedback
</Link>
</div>
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/pages/AuthCallback.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { api, createSession } from "../lib/api";
import { useAuth } from "../lib/auth";
import GitHubStarsLink from "../components/GitHubStarsLink";

export default function AuthCallback() {
const navigate = useNavigate();
Expand Down Expand Up @@ -48,6 +49,12 @@ export default function AuthCallback() {

return (
<div className="min-h-screen flex items-center justify-center bg-white">
<div className="fixed top-4 right-6 z-10">
<GitHubStarsLink
className="hidden lg:flex items-center gap-2 text-[15px] font-medium tracking-tight text-zinc-600 hover:text-zinc-950 transition-colors"
testId="github-link-auth-callback"
/>
</div>
<div className="text-center" data-testid="auth-callback-loading">
<div className="overline mb-4">authenticating</div>
<div className="font-display text-3xl">One moment.</div>
Expand Down
24 changes: 15 additions & 9 deletions frontend/src/pages/AuthForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { jwtLogin, jwtRegister } from "../lib/api";
import { useAuth } from "../lib/auth";
import GitHubStarsLink from "../components/GitHubStarsLink";

function formatErr(detail) {
if (detail == null) return "Something went wrong.";
Expand Down Expand Up @@ -61,15 +62,20 @@ export default function AuthForm({ mode }) {
Killswitch
</span>
</Link>
<Link
to={isRegister ? "/login" : "/register"}
className="text-sm text-zinc-600 hover:text-zinc-950 transition-colors"
data-testid="auth-toggle"
>
{isRegister
? "Already have an account? Sign in →"
: "New here? Create an account →"}
</Link>
<div className="flex items-center gap-4">
<GitHubStarsLink
className="hidden lg:flex items-center gap-2 text-[15px] font-medium tracking-tight text-zinc-600 hover:text-zinc-950 transition-colors"
/>
<Link
to={isRegister ? "/login" : "/register"}
className="text-sm text-zinc-600 hover:text-zinc-950 transition-colors"
data-testid="auth-toggle"
>
{isRegister
? "Already have an account? Sign in →"
: "New here? Create an account →"}
</Link>
</div>
</div>
</nav>

Expand Down
4 changes: 4 additions & 0 deletions frontend/src/pages/ContactFeedback.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import GitHubStarsLink from "../components/GitHubStarsLink";

const contactCards = [
{
Expand Down Expand Up @@ -43,6 +44,9 @@ export default function ContactFeedback() {
</div>
</Link>
<div className="flex items-center gap-2 text-sm">
<GitHubStarsLink
className="hidden lg:flex items-center gap-2 text-[15px] font-medium tracking-tight text-zinc-600 hover:text-zinc-950 transition-colors"
/>
<Link to="/" className="btn btn-ghost" data-testid="contact-home-link">Home</Link>
<a href="https://softwired.in" target="_blank" rel="noreferrer" className="btn btn-secondary" data-testid="contact-softwired-link">
Softwired
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/pages/Landing.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { api } from "../lib/api";
import StatCard from "@/components/StatCard";
import { track } from "@/lib/track";
import { Helmet } from "react-helmet-async";
import GitHubStarsLink from "../components/GitHubStarsLink";
import {
Accordion,
AccordionItem,
Expand Down Expand Up @@ -140,6 +141,9 @@ export default function Landing({ authProvider }) {
</div>
</div>
<div className="flex items-center gap-2">
<GitHubStarsLink
className="hidden lg:flex items-center gap-2 text-[15px] font-medium tracking-tight text-zinc-600 hover:text-zinc-950 transition-colors"
/>
Comment on lines 143 to +146
<Link
to="/contact"
className="btn btn-ghost text-sm"
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/pages/SharedDiagnosis.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getSharedDiagnosis } from "../lib/api";
import { useAuth } from "../lib/auth";
import { track } from "@/lib/track";
import { Helmet } from "react-helmet-async";
import GitHubStarsLink from "../components/GitHubStarsLink";

function verdictStyle(v) {
if (v === "pursue")
Expand Down Expand Up @@ -172,6 +173,9 @@ export default function SharedDiagnosis() {
</div>
</Link>
<div className="flex items-center gap-2">
<GitHubStarsLink
className="hidden lg:flex items-center gap-2 text-[15px] font-medium tracking-tight text-zinc-600 hover:text-zinc-950 transition-colors"
/>
<Link
to="/contact"
className="btn btn-ghost text-sm"
Expand Down
Loading