← back to inzamulhaque1__threadN

Function bodies 143 total

All specs Real LLM only Function bodies
getCached function · typescript · L28-L35 (8 LOC)
app/api/trends/route.ts
function getCached<T>(key: string): T | null {
  const entry = cache.get(key);
  if (entry && entry.expiresAt > Date.now()) {
    return entry.data as T;
  }
  cache.delete(key);
  return null;
}
setCache function · typescript · L37-L39 (3 LOC)
app/api/trends/route.ts
function setCache(key: string, data: unknown): void {
  cache.set(key, { data, expiresAt: Date.now() + CACHE_TTL });
}
fetchWithTimeout function · typescript · L42-L55 (14 LOC)
app/api/trends/route.ts
async function fetchWithTimeout(url: string, options: RequestInit = {}): Promise<Response> {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT);

  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal,
    });
    return response;
  } finally {
    clearTimeout(timeoutId);
  }
}
GET function · typescript · L68-L163 (96 LOC)
app/api/trends/route.ts
export async function GET(request: NextRequest) {
  try {
    // Rate limiting
    const clientIp = getClientIp(request.headers);
    const rateLimit = withRateLimit(clientIp, "trends-get", RATE_LIMIT_CONFIGS.api);

    if (!rateLimit.allowed) {
      return NextResponse.json(
        { success: false, error: "Too many requests", code: "RATE_LIMITED" },
        { status: 429, headers: rateLimit.headers }
      );
    }

    // Authentication check
    const session = await auth();
    if (!session?.user?.id) {
      return NextResponse.json(
        { success: false, error: "Please login to access trends" },
        { status: 401 }
      );
    }

    // Validate input
    const { searchParams } = new URL(request.url);
    const validation = GetTrendsSchema.safeParse({
      country: searchParams.get("country") || "US",
      category: searchParams.get("category") || "all",
    });

    if (!validation.success) {
      return NextResponse.json(
        { success: false, error: validati
filterByCategory function · typescript · L166-L183 (18 LOC)
app/api/trends/route.ts
function filterByCategory(trends: TrendResult[], category: string): TrendResult[] {
  const categoryKeywords: Record<string, string[]> = {
    entertainment: ["movie", "film", "show", "music", "actor", "singer", "celebrity", "netflix", "disney", "hulu", "tv", "series", "album", "concert"],
    sports: ["game", "nfl", "nba", "mlb", "soccer", "football", "basketball", "baseball", "team", "player", "championship", "super bowl", "match", "score"],
    business: ["stock", "market", "company", "ceo", "business", "economy", "trade", "investment", "earnings", "ipo", "merger"],
    technology: ["ai", "tech", "apple", "google", "microsoft", "software", "app", "phone", "computer", "robot", "crypto", "bitcoin"],
    health: ["health", "covid", "vaccine", "doctor", "hospital", "disease", "medical", "treatment", "drug", "fda"],
    science: ["science", "space", "nasa", "research", "study", "discovery", "climate", "planet", "species"],
  };

  const keywords = categoryKeywords[category] || [];
  if (
fetchGoogleTrendsRSS function · typescript · L186-L232 (47 LOC)
app/api/trends/route.ts
async function fetchGoogleTrendsRSS(country: string): Promise<TrendResult[]> {
  try {
    const response = await fetchWithTimeout(`https://trends.google.com/trending/rss?geo=${country}`, {
      headers: {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
      },
      cache: "no-store",
    });

    if (!response.ok) {
      console.error("Google RSS status:", response.status);
      return [];
    }

    const xml = await response.text();
    const trends: TrendResult[] = [];
    const items = xml.match(/<item>([\s\S]*?)<\/item>/g) || [];

    for (const item of items.slice(0, 25)) {
      const title = extractCDATA(item, "title") || extractTag(item, "title");
      const traffic = extractTag(item, "ht:approx_traffic");
      const picture = extractTag(item, "ht:picture");

      const newsItems = item.match(/<ht:news_item>([\s\S]*?)<\/ht:news_item>/g) || [];
      const articles = newsItems.slic
fetchYouTubeTrending function · typescript · L235-L289 (55 LOC)
app/api/trends/route.ts
async function fetchYouTubeTrending(country: string): Promise<TrendResult[]> {
  try {
    const response = await fetchWithTimeout(`https://www.youtube.com/feed/trending?gl=${country}`, {
      headers: {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Accept-Language": "en-US,en;q=0.9",
      },
      cache: "no-store",
    });

    if (!response.ok) {
      console.error("YouTube trending status:", response.status);
      return [];
    }

    const html = await response.text();
    const trends: TrendResult[] = [];

    // Extract video data from ytInitialData
    const dataMatch = html.match(/ytInitialData\s*=\s*({[\s\S]*?});\s*<\/script>/);
    if (dataMatch) {
      try {
        const data = JSON.parse(dataMatch[1]);
        const tabs = data.contents?.twoColumnBrowseResultsRenderer?.tabs || [];

        for (const tab of tabs) {
          const contents = tab.tabRenderer?.content?.
Repobility · code-quality intelligence platform · https://repobility.com
parseViews function · typescript · L291-L295 (5 LOC)
app/api/trends/route.ts
function parseViews(text: string): number {
  const match = text.match(/([\d,.]+)/);
  if (!match) return 0;
  return parseInt(match[1].replace(/[,.]/g, ""), 10) || 0;
}
fetchRedditTrends function · typescript · L298-L354 (57 LOC)
app/api/trends/route.ts
async function fetchRedditTrends(category: string): Promise<TrendResult[]> {
  try {
    const subreddits: Record<string, string> = {
      all: "popular",
      entertainment: "entertainment+movies+television+Music",
      sports: "sports+nfl+nba+soccer",
      business: "business+stocks+wallstreetbets+finance",
      technology: "technology+programming+gadgets",
      health: "health+fitness+nutrition",
      science: "science+space+environment",
    };

    const sub = subreddits[category] || "popular";

    const response = await fetchWithTimeout(`https://www.reddit.com/r/${sub}/.rss?limit=20`, {
      headers: {
        "User-Agent": "ThreadSmith/1.0",
      },
      cache: "no-store",
    });

    if (!response.ok) {
      console.error("Reddit RSS status:", response.status);
      return [];
    }

    const xml = await response.text();
    const trends: TrendResult[] = [];

    // Parse Atom feed entries
    const entries = xml.match(/<entry>[\s\S]*?<\/entry>/g) || [];

    for
fetchHackerNewsTrends function · typescript · L357-L394 (38 LOC)
app/api/trends/route.ts
async function fetchHackerNewsTrends(): Promise<TrendResult[]> {
  try {
    const idsResponse = await fetchWithTimeout("https://hacker-news.firebaseio.com/v0/topstories.json", {
      cache: "no-store",
    });

    if (!idsResponse.ok) return [];

    const ids = await idsResponse.json();
    const topIds = ids.slice(0, 15); // Reduced from 20 to limit parallel requests

    const stories = await Promise.all(
      topIds.map(async (id: number) => {
        try {
          const res = await fetchWithTimeout(`https://hacker-news.firebaseio.com/v0/item/${id}.json`);
          return res.json();
        } catch {
          return null;
        }
      })
    );

    return stories
      .filter((s): s is NonNullable<typeof s> => s !== null && s.title)
      .map((story) => ({
        title: story.title,
        url: story.url || `https://news.ycombinator.com/item?id=${story.id}`,
        points: story.score || 0,
        author: story.by || "unknown",
        comments: story.descendants
extractTag function · typescript · L396-L400 (5 LOC)
app/api/trends/route.ts
function extractTag(xml: string, tag: string): string {
  const regex = new RegExp(`<${tag}[^>]*>([^<]*)</${tag}>`);
  const match = xml.match(regex);
  return match ? match[1].trim() : "";
}
extractCDATA function · typescript · L402-L406 (5 LOC)
app/api/trends/route.ts
function extractCDATA(xml: string, tag: string): string {
  const regex = new RegExp(`<${tag}[^>]*><!\\[CDATA\\[([\\s\\S]*?)\\]\\]></${tag}>`);
  const match = xml.match(regex);
  return match ? match[1].trim() : "";
}
POST function · typescript · L424-L505 (82 LOC)
app/api/trends/route.ts
export async function POST(request: NextRequest) {
  try {
    // Rate limiting (stricter for keyword search)
    const clientIp = getClientIp(request.headers);
    const rateLimit = withRateLimit(clientIp, "trends-search", {
      windowMs: 60 * 1000,
      maxRequests: 10, // 10 searches per minute
    });

    if (!rateLimit.allowed) {
      return NextResponse.json(
        { success: false, error: "Too many requests", code: "RATE_LIMITED" },
        { status: 429, headers: rateLimit.headers }
      );
    }

    // Authentication check
    const session = await auth();
    if (!session?.user?.id) {
      return NextResponse.json(
        { success: false, error: "Please login to search trends" },
        { status: 401 }
      );
    }

    // Validate and sanitize input
    const body = await request.json();
    const validation = PostTrendsSchema.safeParse(body);

    if (!validation.success) {
      return NextResponse.json(
        { success: false, error: validation.error.erro
fetchRelatedQueries function · typescript · L507-L532 (26 LOC)
app/api/trends/route.ts
async function fetchRelatedQueries(keyword: string, country: string) {
  try {
    const googleTrends = await import("google-trends-api");
    const results = await googleTrends.default.relatedQueries({
      keyword,
      geo: country,
    });

    const parsed = JSON.parse(results);
    const defaultData = parsed.default;

    return {
      top: (defaultData?.rankedList?.[0]?.rankedKeyword || []).slice(0, 10).map((item: { query: string; value: number }) => ({
        query: item.query,
        value: item.value,
      })),
      rising: (defaultData?.rankedList?.[1]?.rankedKeyword || []).slice(0, 10).map((item: { query: string; formattedValue: string }) => ({
        query: item.query,
        growth: item.formattedValue,
      })),
    };
  } catch (error) {
    console.error("Related queries error:", error);
    return { top: [], rising: [] };
  }
}
fetchInterestOverTime function · typescript · L534-L553 (20 LOC)
app/api/trends/route.ts
async function fetchInterestOverTime(keyword: string, country: string) {
  try {
    const googleTrends = await import("google-trends-api");
    const results = await googleTrends.default.interestOverTime({
      keyword,
      geo: country,
    });

    const parsed = JSON.parse(results);
    const timeline = parsed.default?.timelineData || [];

    return timeline.slice(-30).map((point: { formattedTime: string; value: number[] }) => ({
      time: point.formattedTime,
      value: point.value?.[0] || 0,
    }));
  } catch (error) {
    console.error("Interest over time error:", error);
    return [];
  }
}
All rows scored by the Repobility analyzer (https://repobility.com)
GET function · typescript · L6-L61 (56 LOC)
app/api/users/history/route.ts
export async function GET(request: NextRequest) {
  try {
    const session = await auth();

    if (!session?.user?.id) {
      return NextResponse.json(
        { success: false, error: "Please login to continue" },
        { status: 401 }
      );
    }

    await dbConnect();

    const { searchParams } = new URL(request.url);
    const page = parseInt(searchParams.get("page") || "1");
    const limit = parseInt(searchParams.get("limit") || "20");
    const type = searchParams.get("type"); // "hooks" | "thread" | null

    const skip = (page - 1) * limit;

    // Build query
    const query: Record<string, unknown> = { userId: session.user.id };
    if (type) {
      query.type = type;
    }

    // Get generations
    const [generations, total] = await Promise.all([
      Generation.find(query)
        .sort({ createdAt: -1 })
        .skip(skip)
        .limit(limit)
        .lean(),
      Generation.countDocuments(query),
    ]);

    return NextResponse.json({
      success: tr
LoginPage function · typescript · L11-L197 (187 LOC)
app/(auth)/login/page.tsx
export default function LoginPage() {
  const router = useRouter();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [isGoogleLoading, setIsGoogleLoading] = useState(false);
  const [showPassword, setShowPassword] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError("");
    setIsLoading(true);

    const result = await signIn("credentials", {
      email,
      password,
      redirect: false,
    });

    if (result?.error) {
      setError("Invalid email or password");
      setIsLoading(false);
    } else {
      router.push("/dashboard");
      router.refresh();
    }
  };

  const handleGoogleLogin = async () => {
    setIsGoogleLoading(true);
    await signIn("google", { callbackUrl: "/dashboard" });
  };

  return (
    <div className="min-h-screen flex items-center justify-cent
RegisterPage function · typescript · L12-L231 (220 LOC)
app/(auth)/register/page.tsx
export default function RegisterPage() {
  const router = useRouter();
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [isGoogleLoading, setIsGoogleLoading] = useState(false);
  const [showPassword, setShowPassword] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError("");
    setIsLoading(true);

    if (password.length < 6) {
      setError("Password must be at least 6 characters");
      setIsLoading(false);
      return;
    }

    // First register the user
    const result = await authApi.register({ email, password, name });

    if (result.success) {
      // Then sign in with credentials
      const signInResult = await signIn("credentials", {
        email,
        password,
        redirect: false,
      });

      if (signInResult?.err
AchievementsPage function · typescript · L61-L290 (230 LOC)
app/(dashboard)/dashboard/achievements/page.tsx
export default function AchievementsPage() {
  const [achievements, setAchievements] = useState<AchievementData[]>([]);
  const [streak, setStreak] = useState<StreakData | null>(null);
  const [totalXP, setTotalXP] = useState(0);
  const [unlockedCount, setUnlockedCount] = useState(0);
  const [totalCount, setTotalCount] = useState(0);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [selectedCategory, setSelectedCategory] = useState<string>("all");

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    setIsLoading(true);
    const [achievementsResult, streakResult] = await Promise.all([
      achievementsApi.list(),
      achievementsApi.getStreak(),
    ]);

    if (achievementsResult.success && achievementsResult.data) {
      const data = achievementsResult.data as {
        achievements: AchievementData[];
        totalXP: number;
        unlockedCount: number;
        totalCount
HistoryPage function · typescript · L10-L199 (190 LOC)
app/(dashboard)/dashboard/history/page.tsx
export default function HistoryPage() {
  const [generations, setGenerations] = useState<IGeneration[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [page, setPage] = useState(1);
  const [totalPages, setTotalPages] = useState(1);
  const [filter, setFilter] = useState<"all" | "hooks" | "thread">("all");
  const [copiedId, setCopiedId] = useState<string | null>(null);

  useEffect(() => {
    fetchHistory();
  }, [page, filter]);

  const fetchHistory = async () => {
    setIsLoading(true);
    const type = filter === "all" ? undefined : filter;
    const result = await userApi.history(page, 10, type);

    if (result.success && result.data) {
      const data = result.data as {
        generations: IGeneration[];
        pagination: { totalPages: number };
      };
      setGenerations(data.generations);
      setTotalPages(data.pagination.totalPages);
    }
    setIsLoading(false);
  };

  const handleCopy = async (text: string, id: string) => {
    await navigato
SchedulerPage function · typescript · L53-L414 (362 LOC)
app/(dashboard)/dashboard/scheduler/page.tsx
export default function SchedulerPage() {
  const [posts, setPosts] = useState<ScheduledPostData[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [showCreateModal, setShowCreateModal] = useState(false);
  const [editingPost, setEditingPost] = useState<ScheduledPostData | null>(null);
  const [statusFilter, setStatusFilter] = useState<string>("");

  // Form state
  const [formHook, setFormHook] = useState("");
  const [formThread, setFormThread] = useState("");
  const [formPlatforms, setFormPlatforms] = useState<Platform[]>(["facebook"]);
  const [formScheduledAt, setFormScheduledAt] = useState("");
  const [formRecurrence, setFormRecurrence] = useState<"none" | "daily" | "weekly">("none");
  const [isSubmitting, setIsSubmitting] = useState(false);

  useEffect(() => {
    fetchPosts();
  }, [statusFilter]);

  const fetchPosts = async () => {
    setIsLoading(true);
    const result = await schedulerApi.lis
SettingsPage function · typescript · L9-L138 (130 LOC)
app/(dashboard)/dashboard/settings/page.tsx
export default function SettingsPage() {
  const { user } = useAuth();

  if (!user) return null;

  const limits = PLAN_LIMITS[user.plan];

  return (
    <div className="max-w-2xl mx-auto font-body">
      <div className="mb-8">
        <h1 className="text-2xl sm:text-3xl font-heading font-bold text-white flex items-center gap-3">
          <Settings className="w-7 h-7 sm:w-8 sm:h-8 text-purple-400" />
          Settings
        </h1>
        <p className="text-gray-400 text-sm sm:text-base font-accent mt-1">Manage your account</p>
      </div>

      <div className="space-y-6">
        {/* Profile */}
        <Card>
          <h2 className="text-lg sm:text-xl font-heading font-semibold mb-4 flex items-center gap-2 text-white">
            <User className="w-5 h-5 text-purple-400" />
            Profile
          </h2>
          <div className="space-y-4">
            <div>
              <label className="text-sm text-gray-400">Name</label>
              <p className="font-medium">{u
TrendsPage function · typescript · L98-L354 (257 LOC)
app/(dashboard)/dashboard/trends/page.tsx
export default function TrendsPage() {
  const [country, setCountry] = useState("US");
  const [category, setCategory] = useState("all");
  const [selectedNiche, setSelectedNiche] = useState<string | null>(null);
  const [trends, setTrends] = useState<TrendsData | null>(null);
  const [keywordData, setKeywordData] = useState<KeywordData | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [searchKeyword, setSearchKeyword] = useState("");
  const [isSearching, setIsSearching] = useState(false);
  const [activeTab, setActiveTab] = useState<"google" | "youtube" | "reddit" | "hackernews" | "niche">("google");

  useEffect(() => {
    fetchTrends();
  }, [country, category]);

  const fetchTrends = async () => {
    setIsLoading(true);
    try {
      const res = await fetch(`/api/trends?country=${country}&category=${category}`);
      const data = await res.json();
      if (data.success) {
        setTrends(d
Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
LoadingState function · typescript · L392-L404 (13 LOC)
app/(dashboard)/dashboard/trends/page.tsx
function LoadingState({ country }: { country?: { flag: string; name: string } }) {
  return (
    <div className="flex flex-col items-center justify-center py-20">
      <div className="relative w-16 h-16">
        <div className="absolute inset-0 rounded-full border-4 border-green-500/20"></div>
        <div className="absolute inset-0 rounded-full border-4 border-transparent border-t-green-500 animate-spin"></div>
        <TrendingUp className="w-6 h-6 text-green-400 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2" />
      </div>
      <p className="text-gray-400 mt-4">Fetching live trends...</p>
      {country && <p className="text-gray-500 text-sm">{country.flag} {country.name}</p>}
    </div>
  );
}
GoogleTrends function · typescript · L407-L457 (51 LOC)
app/(dashboard)/dashboard/trends/page.tsx
function GoogleTrends({ trends, country }: { trends: TrendItem[]; country?: string }) {
  if (trends.length === 0) {
    return (
      <Card className="p-8 text-center">
        <TrendingUp className="w-12 h-12 text-gray-600 mx-auto mb-3" />
        <p className="text-gray-400">No Google trends found{country ? ` for ${country}` : ""}</p>
      </Card>
    );
  }

  return (
    <div className="space-y-3">
      {trends.map((trend, i) => (
        <Card key={i} className="p-4 hover:border-blue-500/30 transition-all">
          <div className="flex items-start gap-4">
            <div className="w-10 h-10 rounded-xl bg-blue-500/20 flex items-center justify-center shrink-0">
              <span className="text-lg font-bold text-blue-400">#{i + 1}</span>
            </div>
            <div className="flex-1 min-w-0">
              <div className="flex items-start justify-between gap-3">
                <div>
                  <h3 className="font-semibold text-white text-lg">{trend.title}<
YouTubeTrends function · typescript · L460-L500 (41 LOC)
app/(dashboard)/dashboard/trends/page.tsx
function YouTubeTrends({ trends }: { trends: TrendItem[] }) {
  if (trends.length === 0) {
    return (
      <Card className="p-8 text-center">
        <Play className="w-12 h-12 text-gray-600 mx-auto mb-3" />
        <p className="text-gray-400">No YouTube trends available</p>
      </Card>
    );
  }

  return (
    <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
      {trends.map((trend, i) => (
        <a key={i} href={trend.url} target="_blank" rel="noopener noreferrer" className="block">
          <Card className="p-0 overflow-hidden hover:border-red-500/30 transition-all group">
            <div className="relative">
              {trend.image && (
                <img src={trend.image} alt="" className="w-full h-36 object-cover" />
              )}
              <div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-all flex items-center justify-center">
                <Play className="w-12 h-12 text-white" />
              </div>
formatViews function · typescript · L502-L506 (5 LOC)
app/(dashboard)/dashboard/trends/page.tsx
function formatViews(views: number): string {
  if (views >= 1000000) return `${(views / 1000000).toFixed(1)}M`;
  if (views >= 1000) return `${(views / 1000).toFixed(0)}K`;
  return views.toString();
}
RedditTrends function · typescript · L509-L550 (42 LOC)
app/(dashboard)/dashboard/trends/page.tsx
function RedditTrends({ trends }: { trends: TrendItem[] }) {
  if (trends.length === 0) {
    return (
      <Card className="p-8 text-center">
        <Users className="w-12 h-12 text-gray-600 mx-auto mb-3" />
        <p className="text-gray-400">No Reddit trends available</p>
      </Card>
    );
  }

  return (
    <div className="space-y-3">
      {trends.map((trend, i) => (
        <Card key={i} className="p-4 hover:border-orange-500/30 transition-all">
          <div className="flex items-start gap-4">
            <div className="flex flex-col items-center gap-1 shrink-0 w-12">
              <ArrowUp className="w-5 h-5 text-orange-500" />
              <span className="text-sm font-bold text-orange-500">
                {trend.score ? (trend.score >= 1000 ? `${(trend.score / 1000).toFixed(1)}k` : trend.score) : 0}
              </span>
            </div>
            <div className="flex-1 min-w-0">
              <a href={trend.url} target="_blank" rel="noopener noreferrer" classN
HackerNewsTrends function · typescript · L553-L589 (37 LOC)
app/(dashboard)/dashboard/trends/page.tsx
function HackerNewsTrends({ trends }: { trends: TrendItem[] }) {
  if (trends.length === 0) {
    return (
      <Card className="p-8 text-center">
        <Code className="w-12 h-12 text-gray-600 mx-auto mb-3" />
        <p className="text-gray-400">No Hacker News trends available</p>
      </Card>
    );
  }

  return (
    <div className="space-y-3">
      {trends.map((trend, i) => (
        <Card key={i} className="p-4 hover:border-amber-500/30 transition-all">
          <div className="flex items-start gap-4">
            <div className="flex flex-col items-center gap-1 shrink-0 w-12">
              <Zap className="w-5 h-5 text-amber-500" />
              <span className="text-sm font-bold text-amber-500">{trend.points}</span>
            </div>
            <div className="flex-1 min-w-0">
              <a href={trend.url} target="_blank" rel="noopener noreferrer" className="font-medium text-white hover:text-amber-400 line-clamp-2">
                {trend.title}
              </a>
DashboardLayout function · typescript · L41-L221 (181 LOC)
app/(dashboard)/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const router = useRouter();
  const pathname = usePathname();
  const { user, isLoading, isAuthenticated, logout } = useAuth();
  const [streak, setStreak] = useState<{ currentStreak: number; isActive: boolean } | null>(null);
  const [sidebarOpen, setSidebarOpen] = useState(false);

  useEffect(() => {
    if (!isLoading && !isAuthenticated) {
      router.push("/login");
    }
  }, [isLoading, isAuthenticated, router]);

  // Close sidebar on route change
  useEffect(() => {
    setSidebarOpen(false);
  }, [pathname]);

  // Fetch streak data
  useEffect(() => {
    if (isAuthenticated) {
      achievementsApi.getStreak().then((result) => {
        if (result.success && result.data) {
          const data = result.data as { currentStreak: number; isActive: boolean };
          setStreak(data);
        }
      });
    }
  }, [isAuthenticated]);

  if (isLoading) {
    return (
      <div cla
RootLayout function · typescript · L56-L71 (16 LOC)
app/layout.tsx
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" className={`${spaceGrotesk.variable} ${inter.variable} ${dmSans.variable} ${jetbrainsMono.variable}`}>
      <body className="antialiased font-body">
        <Providers>
          <div className="animated-bg" />
          {children}
        </Providers>
      </body>
    </html>
  );
}
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
NotFound function · typescript · L5-L31 (27 LOC)
app/not-found.tsx
export default function NotFound() {
  return (
    <div className="min-h-screen flex items-center justify-center px-4">
      <div className="text-center">
        <div className="text-8xl font-bold text-neon-gradient mb-4">404</div>
        <h2 className="text-2xl font-bold mb-2">Page Not Found</h2>
        <p className="text-gray-400 mb-8">
          The page you&apos;re looking for doesn&apos;t exist or has been moved.
        </p>
        <div className="flex flex-col sm:flex-row gap-4 justify-center">
          <Link href="/">
            <Button>
              <Home className="w-4 h-4 mr-2" />
              Go Home
            </Button>
          </Link>
          <Link href="/dashboard">
            <Button variant="glass">
              <Search className="w-4 h-4 mr-2" />
              Dashboard
            </Button>
          </Link>
        </div>
      </div>
    </div>
  );
}
generateMetadata function · typescript · L11-L61 (51 LOC)
app/share/[id]/page.tsx
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { id } = await params;

  try {
    await dbConnect();
    const sharedCard = await SharedCard.findOne({ uniqueId: id }).select("title description uniqueId");

    if (!sharedCard) {
      return {
        title: "Card Not Found | ThreadN",
        description: "This shared card could not be found.",
      };
    }

    const appUrl = process.env.NEXT_PUBLIC_APP_URL || "https://threadn.launchory.org";
    const imageUrl = `${appUrl}/api/share/${id}/image`;
    const shareUrl = `${appUrl}/share/${id}`;

    return {
      title: `${sharedCard.title} | ThreadN`,
      description: sharedCard.description || "Created with ThreadN - AI-powered content creation",
      openGraph: {
        title: sharedCard.title,
        description: sharedCard.description || "Created with ThreadN - AI-powered content creation",
        url: shareUrl,
        siteName: "ThreadN",
        images: [
          {
            
SharePage function · typescript · L63-L66 (4 LOC)
app/share/[id]/page.tsx
export default async function SharePage({ params }: Props) {
  const { id } = await params;
  return <SharePageClient shareId={id} />;
}
SharePageClient function · typescript · L22-L361 (340 LOC)
app/share/[id]/SharePageClient.tsx
export default function SharePageClient({ shareId }: SharePageClientProps) {
  const [card, setCard] = useState<SharedCardData | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [copied, setCopied] = useState(false);

  useEffect(() => {
    fetchCard();
  }, [shareId]);

  const fetchCard = async () => {
    try {
      const res = await fetch(`/api/share/${shareId}`);
      const data = await res.json();

      if (data.success) {
        setCard(data.data.card);
      } else {
        setError(data.error || "Card not found");
      }
    } catch {
      setError("Failed to load card");
    } finally {
      setIsLoading(false);
    }
  };

  const handleDownload = async () => {
    if (!card) return;

    const link = document.createElement("a");
    link.download = `${card.title.replace(/[^a-zA-Z0-9]/g, "_")}.png`;
    link.href = card.imageData;
    link.click();
  };

  const handleCopyLink = async
TermsPage function · typescript · L17-L313 (297 LOC)
app/terms/page.tsx
export default function TermsPage() {
  return (
    <div className="min-h-screen bg-gradient-to-b from-gray-950 via-gray-900 to-gray-950 font-body">
      <Navbar showLinks={false} />

      {/* Hero */}
      <section className="py-12 sm:py-16 px-4 relative">
        <div className="absolute top-10 left-1/4 w-64 h-64 bg-purple-500/10 rounded-full blur-3xl" />
        <div className="max-w-4xl mx-auto text-center relative">
          <div className="inline-flex items-center justify-center w-14 h-14 sm:w-16 sm:h-16 rounded-2xl bg-gradient-to-br from-purple-500/20 to-cyan-500/20 mb-5 sm:mb-6">
            <FileText className="w-7 h-7 sm:w-8 sm:h-8 text-purple-400" />
          </div>
          <h1 className="text-3xl sm:text-4xl font-heading font-bold mb-3 sm:mb-4 text-white">Terms of Service</h1>
          <p className="text-gray-400 text-sm sm:text-base font-accent mb-2">
            Please read these terms carefully before using ThreadN
          </p>
          <p className="text-xs 
Footer function · typescript · L18-L277 (260 LOC)
components/Footer.tsx
export function Footer({ showCTA = true }: FooterProps) {
  return (
    <footer className="relative pt-12 sm:pt-20 pb-8 sm:pb-10 px-4 overflow-hidden">
      {/* Background Elements */}
      <div className="absolute inset-0 bg-gradient-to-t from-black via-gray-950 to-transparent" />
      <div className="absolute bottom-0 left-1/4 w-48 sm:w-96 h-48 sm:h-96 bg-purple-500/5 rounded-full blur-3xl" />
      <div className="absolute bottom-0 right-1/4 w-48 sm:w-96 h-48 sm:h-96 bg-cyan-500/5 rounded-full blur-3xl" />

      <div className="max-w-6xl mx-auto relative">
        {/* Top Section - Newsletter/CTA */}
        {showCTA && (
          <div className="glass-card p-5 sm:p-8 mb-10 sm:mb-16 bg-gradient-to-r from-purple-500/10 via-transparent to-cyan-500/10">
            <div className="flex flex-col md:flex-row items-center justify-between gap-4 sm:gap-6 text-center md:text-left">
              <div>
                <h3 className="text-lg sm:text-xl font-heading font-bold mb-1 sm:mb-2
Navbar function · typescript · L14-L154 (141 LOC)
components/Navbar.tsx
export function Navbar({ showLinks = true }: NavbarProps) {
  const { data: session, status } = useSession();
  const isLoggedIn = status === "authenticated" && session?.user;
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);

  return (
    <nav className="navbar-glass sticky top-0 z-50">
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div className="flex items-center justify-between h-16">
          {/* Logo */}
          <Link href="/" className="flex items-center">
            <Image
              src="/logo.png"
              alt="ThreadN"
              width={140}
              height={40}
              className="h-8 sm:h-10 w-auto"
              priority
            />
          </Link>

          {/* Desktop Nav Links */}
          {showLinks && (
            <div className="hidden md:flex items-center gap-8">
              <Link
                href="/#features"
                className="text-gray-400 hover:text-white transition-colors te
Providers function · typescript · L6-L8 (3 LOC)
components/Providers.tsx
export function Providers({ children }: { children: ReactNode }) {
  return <SessionProvider>{children}</SessionProvider>;
}
Repobility · code-quality intelligence platform · https://repobility.com
Card function · typescript · L12-L24 (13 LOC)
components/ui/Card.tsx
export function Card({ children, className, hover = false }: CardProps) {
  return (
    <div
      className={cn(
        "glass-card p-6",
        hover && "hover-lift cursor-pointer",
        className
      )}
    >
      {children}
    </div>
  );
}
CardHeader function · typescript · L26-L34 (9 LOC)
components/ui/Card.tsx
export function CardHeader({
  children,
  className,
}: {
  children: ReactNode;
  className?: string;
}) {
  return <div className={cn("mb-4", className)}>{children}</div>;
}
CardTitle function · typescript · L36-L48 (13 LOC)
components/ui/Card.tsx
export function CardTitle({
  children,
  className,
}: {
  children: ReactNode;
  className?: string;
}) {
  return (
    <h3 className={cn("text-lg font-semibold text-white", className)}>
      {children}
    </h3>
  );
}
CardContent function · typescript · L50-L58 (9 LOC)
components/ui/Card.tsx
export function CardContent({
  children,
  className,
}: {
  children: ReactNode;
  className?: string;
}) {
  return <div className={cn("text-gray-300", className)}>{children}</div>;
}
Modal function · typescript · L15-L65 (51 LOC)
components/ui/Modal.tsx
export function Modal({ isOpen, onClose, title, children, className }: ModalProps) {
  useEffect(() => {
    if (isOpen) {
      document.body.style.overflow = "hidden";
    } else {
      document.body.style.overflow = "unset";
    }

    return () => {
      document.body.style.overflow = "unset";
    };
  }, [isOpen]);

  useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === "Escape") onClose();
    };

    if (isOpen) {
      window.addEventListener("keydown", handleEscape);
    }

    return () => {
      window.removeEventListener("keydown", handleEscape);
    };
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div
        className={cn("modal-content", className)}
        onClick={(e) => e.stopPropagation()}
      >
        {title && (
          <div className="flex items-center justify-between mb-4">
            <h2 className="text-xl font-semibold text-white">{title}<
useAuth function · typescript · L9-L71 (63 LOC)
hooks/useAuth.ts
export function useAuth() {
  const { data: session, status, update } = useSession();
  const router = useRouter();
  const [user, setUser] = useState<IUserPublic | null>(null);
  const [isLoadingUser, setIsLoadingUser] = useState(false);

  const isLoading = status === "loading";
  const isAuthenticated = status === "authenticated";
  // Use session role directly for faster admin check
  const isAdmin = session?.user?.role === "admin";

  // Fetch full user data from database
  const fetchUser = useCallback(async () => {
    if (!session?.user?.id) return;

    setIsLoadingUser(true);
    try {
      const response = await api("/auth/me");
      if (response.success && response.data) {
        setUser((response.data as { user: IUserPublic }).user);
      }
    } catch (error) {
      console.error("Failed to fetch user:", error);
    } finally {
      setIsLoadingUser(false);
    }
  }, [session?.user?.id]);

  // Fetch user data when authenticated
  useEffect(() => {
    if (status =
checkAndUnlockAchievement function · typescript · L14-L44 (31 LOC)
lib/achievements.ts
export async function checkAndUnlockAchievement(
  userId: string,
  achievementId: string
): Promise<AchievementUnlockResult> {
  await dbConnect();

  const achievement = ACHIEVEMENTS[achievementId];
  if (!achievement) {
    return { unlocked: false };
  }

  // Check if already unlocked
  const existing = await UserAchievement.findOne({
    userId,
    achievementId,
  });

  if (existing) {
    return { unlocked: true, achievement, isNew: false };
  }

  // Create new achievement
  await UserAchievement.create({
    userId,
    achievementId,
    unlockedAt: new Date(),
    progress: achievement.requirement,
  });

  return { unlocked: true, achievement, isNew: true };
}
checkAllAchievements function · typescript · L49-L109 (61 LOC)
lib/achievements.ts
export async function checkAllAchievements(userId: string): Promise<AchievementUnlockResult[]> {
  await dbConnect();

  const results: AchievementUnlockResult[] = [];

  // Get user stats
  const user = await User.findById(userId);
  if (!user) return results;

  const streak = await UserStreak.findOne({ userId });

  // Get counts
  const [templateCount, collectionCount, scheduledCount] = await Promise.all([
    Template.countDocuments({ userId }),
    Collection.countDocuments({ userId }),
    ScheduledPost.countDocuments({ userId }),
  ]);

  // Check generation achievements
  if (user.usage.totalHooks >= 1) {
    results.push(await checkAndUnlockAchievement(userId, "first_hook"));
  }
  if (user.usage.totalHooks >= 10) {
    results.push(await checkAndUnlockAchievement(userId, "hooks_10"));
  }
  if (user.usage.totalHooks >= 100) {
    results.push(await checkAndUnlockAchievement(userId, "hooks_100"));
  }
  if (user.usage.totalThreads >= 1) {
    results.push(await checkAndUnlock
All rows scored by the Repobility analyzer (https://repobility.com)
recordActivityAndUpdateStreak function · typescript · L114-L119 (6 LOC)
lib/achievements.ts
export async function recordActivityAndUpdateStreak(userId: string): Promise<{
  streak: { currentStreak: number; longestStreak: number };
  isNewDay: boolean;
  streakBroken: boolean;
  newAchievements: AchievementUnlockResult[];
}> {
getUserXP function · typescript · L169-L178 (10 LOC)
lib/achievements.ts
export async function getUserXP(userId: string): Promise<number> {
  await dbConnect();

  const unlockedAchievements = await UserAchievement.find({ userId });

  return unlockedAchievements.reduce((sum, ua) => {
    const achievement = ACHIEVEMENTS[ua.achievementId];
    return sum + (achievement?.xp || 0);
  }, 0);
}
getYouTubeVideoId function · typescript · L17-L28 (12 LOC)
lib/ai.ts
function getYouTubeVideoId(url: string): string | null {
  const patterns = [
    /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/v\/)([^&\n?#]+)/,
    /youtube\.com\/shorts\/([^&\n?#]+)/,
  ];

  for (const pattern of patterns) {
    const match = url.match(pattern);
    if (match) return match[1];
  }
  return null;
}
‹ prevpage 2 / 3next ›