File size: 3,855 Bytes
cd201c7
7a85021
85181c4
7a85021
22932a5
cd201c7
7a85021
cd201c7
6c9b36e
7a85021
 
 
7fc47b1
 
7a85021
fdb7c58
 
7a85021
7fc47b1
cd201c7
c608763
 
7fc47b1
 
 
 
c608763
cd201c7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ab1abdd
c608763
22932a5
 
 
 
 
 
 
198dbcf
22932a5
 
 
 
929d75e
22932a5
 
 
cd201c7
22932a5
 
 
cd201c7
 
 
 
 
 
 
 
 
22932a5
 
 
cd201c7
85181c4
22932a5
 
 
 
 
 
 
929d75e
ab1abdd
22932a5
 
cd201c7
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 orgs contributing to AI4Science 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;