File size: 3,840 Bytes
e603004
7a85021
85181c4
7a85021
22932a5
e603004
7a85021
e603004
6c9b36e
7a85021
 
 
7fc47b1
 
7a85021
fdb7c58
 
7a85021
7fc47b1
e603004
c608763
 
7fc47b1
 
 
 
c608763
e603004
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ab1abdd
c608763
22932a5
 
 
 
 
 
 
198dbcf
22932a5
 
 
 
929d75e
22932a5
 
 
198dbcf
22932a5
 
 
e603004
 
 
 
 
 
 
 
 
22932a5
 
 
e603004
85181c4
22932a5
 
 
 
 
 
 
929d75e
ab1abdd
22932a5
 
e603004
22932a5
 
 
ab1abdd
03b5e31
c07926b
7a85021
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7fc47b1
7a85021
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
import React, { useState, useEffect, useMemo } from "react";
import { ProviderInfo, ModelData, CalendarData } from "../types/heatmap";
import OrganizationButton from "../components/OrganizationButton";
import HeatmapGrid from "../components/HeatmapGrid";
import Navbar from "../components/Navbar";
import TagSelector from "../components/TagSelector";
import { getProviders } from "../utils/ranking";
import { ORGANIZATIONS, SCIENTIFIC_TAGS } from "../constants/organizations";

interface PageProps {
  calendarData: CalendarData;
  providers: ProviderInfo[];
}

function Page({
  calendarData,
  providers,
}: PageProps) {
  const [isLoading, setIsLoading] = useState(true);
  const [selectedTags, setSelectedTags] = useState<string[]>([]);

  useEffect(() => {
    if (calendarData && Object.keys(calendarData).length > 0) {
      setIsLoading(false);
    }
  }, [calendarData]);

  // Filter providers based on selected tags
  const filteredProviders = useMemo(() => {
    if (selectedTags.length === 0) {
      return providers;
    }
    
    return providers.filter(provider => {
      if (!provider.tags) return false;
      return selectedTags.some(tag => provider.tags!.includes(tag));
    });
  }, [providers, selectedTags]);

  const handleTagToggle = (tagId: string) => {
    setSelectedTags(prev => {
      if (prev.includes(tagId)) {
        return prev.filter(t => t !== tagId);
      } else {
        return [...prev, tagId];
      }
    });
  };

  return (
    <div className="w-full">
      <Navbar />
      
      <div className="w-full p-4 py-16">
        <div className="text-center mb-16 max-w-4xl mx-auto">
          <h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-6xl font-bold text-foreground mb-6 bg-gradient-to-r from-foreground to-foreground/80 bg-clip-text">
            <span className="inline-flex items-center gap-1 sm:gap-2">
              Hugging Science Heatmap
              <img
                src="/hf-icon.svg"
                alt="Hugging Face icon"
                className="size-6 sm:size-8 md:size-10"
              />
            </span>
          </h1>
          <p className="text-base sm:text-lg lg:text-xl text-muted-foreground max-w-2xl mx-auto leading-relaxed px-4">
            Open models, datasets, and apps from influential labs in the last year.
          </p>
        </div>

        {/* Tag Selector */}
        <div className="mb-16">
          <TagSelector
            tags={SCIENTIFIC_TAGS}
            selectedTags={selectedTags}
            onTagToggle={handleTagToggle}
          />
        </div>

        <div className="mb-16 mx-auto">
          <div className="overflow-x-auto scrollbar-hide">
            <div className="flex gap-6 px-4 py-2 min-w-max justify-center">
              {filteredProviders.map((provider, index) => (
                <OrganizationButton
                  key={provider.fullName || provider.authors[0]}
                  provider={provider}
                  calendarData={calendarData}
                  rank={index + 1}
                />
              ))}
            </div>
          </div>
        </div>
             
        <HeatmapGrid 
          sortedProviders={filteredProviders}
          calendarData={calendarData}
          isLoading={isLoading}
        />
      </div>
    </div>
  );
}


export async function getStaticProps() {
  try {
    const { calendarData, providers } = await getProviders(ORGANIZATIONS);

    return {
      props: {
        calendarData,
        providers,
      },
      revalidate: 3600, // regenerate every hour
    };
  } catch (error) {
    console.error("Error fetching data:", error);
    return {
      props: {
        calendarData: {},
        providers: ORGANIZATIONS,
      },
      revalidate: 60, // retry after 1 minute if there was an error
    };
  }
}

export default Page;