Skip to content

Commit de71e8e

Browse files
committed
Add caching and auth token retrieval to DFDA components
Implemented caching for DFDA variable search results to improve performance. Added a function to retrieve and use DFDA access tokens for secure data fetching. Enhanced user interface with loading indicators and improved layout for various components. Took 23 minutes
1 parent bbd9c07 commit de71e8e

File tree

6 files changed

+160
-25
lines changed

6 files changed

+160
-25
lines changed

app/dfda/components/CitizenScienceSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default function CitizenScienceSection() {
3939
<motion.div
4040
initial={{ opacity: 0, y: 20 }}
4141
animate={{ opacity: 1, y: 0 }}
42-
className="space-y-8"
42+
className="grid grid-cols-1 md:grid-cols-3 gap-8"
4343
>
4444
{features.map((feature, index) => (
4545
<FeatureBox

app/dfda/components/CostSavingsTable.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,19 @@ export default function CostSavingsTable() {
5959

6060
return (
6161
<div className="relative overflow-hidden rounded-xl border-4 border-black bg-white p-6 shadow-[8px_8px_0px_0px_rgba(0,0,0,1)]">
62+
<motion.h1
63+
className="mb-6 text-4xl md:text-4xl font-black uppercase"
64+
initial={{ opacity: 0, y: -20 }}
65+
animate={{ opacity: 1, y: 0 }}
66+
>
67+
👀Look at those savings! 🤑
68+
</motion.h1>
6269
<motion.h2
63-
className="mb-6 text-2xl md:text-4xl font-black uppercase"
70+
className="mb-6 md:text-2xl font-black uppercase"
6471
initial={{ opacity: 0, y: -20 }}
6572
animate={{ opacity: 1, y: 0 }}
6673
>
67-
Savings from a Decentralized Autonomous FDA 💰
74+
Savings from a Decentralized Autonomous FDA
6875
</motion.h2>
6976

7077
<div className="-mx-6 sm:mx-0">

app/dfda/components/SolutionSection.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,50 @@
33
import { motion } from 'framer-motion'
44
import { Database, Brain, Bell } from 'lucide-react'
55
import { FeatureBox } from './FeatureBox'
6+
import { getSafeUrlWithToken } from '../dfdaActions'
7+
import { useState } from 'react'
68

79
export default function SolutionSection() {
10+
const [isLoading, setIsLoading] = useState(false)
11+
12+
const handleDigitalTwinSafeClick = async (path: string) => {
13+
setIsLoading(true)
14+
const url = await getSafeUrlWithToken('')
15+
window.open(url, '_blank')
16+
}
817
const features = [
918
{
1019
title: "Automated Data Collection",
1120
desc: "Import data from wearables and apps while leveraging AI-powered collection methods for comprehensive health insights.",
1221
color: "bg-green-400",
1322
icon: Database,
1423
media: "https://fdai.earth/wp-content/uploads/2024/03/import.gif",
15-
onClick: () => console.log("Data Collection clicked")
24+
onClick: () => {
25+
// go to https://safe.dfda.earth/import in new tab
26+
handleDigitalTwinSafeClick('/import')
27+
}
1628
},
1729
{
1830
title: "Automated Analysis",
1931
desc: "Advanced AI algorithms analyze your health data to identify patterns and potential root causes.",
2032
color: "bg-teal-400",
2133
icon: Brain,
2234
media: "https://fdai.earth/wp-content/uploads/2024/03/root-cause-analysis-4x-small.gif",
23-
onClick: () => console.log("Analysis clicked")
35+
onClick: () => {
36+
// go to https://safe.dfda.earth/app/public/#/app/studies in new tab
37+
handleDigitalTwinSafeClick('/studies')
38+
}
2439
},
2540
{
2641
title: "Real-Time Decision Support",
2742
desc: "Get personalized insights and recommendations based on your real-time health data analysis.",
2843
color: "bg-emerald-400",
2944
icon: Bell,
3045
media: "https://fdai.earth/wp-content/uploads/2024/03/real-time-decision-support-notifications-personalized-app-image.png",
31-
onClick: () => console.log("Decision Support clicked")
46+
onClick: () => {
47+
// go to https://safe.dfda.earth/app/public/#/app/studies
48+
window.open('https://safe.dfda.earth/app/public/#/app/studies', '_blank')
49+
}
3250
}
3351
]
3452

@@ -39,7 +57,7 @@ export default function SolutionSection() {
3957
<motion.div
4058
initial={{ opacity: 0, y: 20 }}
4159
animate={{ opacity: 1, y: 0 }}
42-
className="space-y-8"
60+
className="grid grid-cols-1 md:grid-cols-3 gap-8"
4361
>
4462
{features.map((feature, index) => (
4563
<FeatureBox

app/dfda/components/VariableSearchAutocomplete.tsx

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useState, useEffect } from 'react'
3+
import { useState, useEffect, useRef } from 'react'
44
import { Input } from '@/components/ui/input'
55
import { GlobalVariable } from '@/types/models/GlobalVariable'
66
import { searchDfdaVariables } from '@/lib/clinicaltables'
@@ -11,6 +11,45 @@ interface VariableSearchAutocompleteProps {
1111
placeholder: string
1212
}
1313

14+
// Add cache interface
15+
interface SearchCache {
16+
timestamp: number
17+
results: GlobalVariable[]
18+
}
19+
20+
// Add cache duration constant (e.g., 1 hour)
21+
const CACHE_DURATION = 60 * 60 * 1000
22+
23+
function getCachedResults(key: string): GlobalVariable[] | null {
24+
try {
25+
const cached = localStorage.getItem(key)
26+
if (!cached) return null
27+
28+
const { timestamp, results } = JSON.parse(cached) as SearchCache
29+
if (Date.now() - timestamp > CACHE_DURATION) {
30+
localStorage.removeItem(key)
31+
return null
32+
}
33+
34+
return results
35+
} catch (error) {
36+
console.error('Error reading from cache:', error)
37+
return null
38+
}
39+
}
40+
41+
function setCachedResults(key: string, results: GlobalVariable[]) {
42+
try {
43+
const cacheData: SearchCache = {
44+
timestamp: Date.now(),
45+
results
46+
}
47+
localStorage.setItem(key, JSON.stringify(cacheData))
48+
} catch (error) {
49+
console.error('Error writing to cache:', error)
50+
}
51+
}
52+
1453
export default function VariableSearchAutocomplete({
1554
onVariableSelect,
1655
searchParams = {},
@@ -20,14 +59,44 @@ export default function VariableSearchAutocomplete({
2059
const [variables, setVariables] = useState<GlobalVariable[]>([])
2160
const [isLoading, setIsLoading] = useState(false)
2261
const [showDropdown, setShowDropdown] = useState(false)
62+
const componentRef = useRef<HTMLDivElement>(null)
63+
64+
useEffect(() => {
65+
function handleClickOutside(event: MouseEvent) {
66+
if (componentRef.current && !componentRef.current.contains(event.target as Node)) {
67+
setShowDropdown(false)
68+
}
69+
}
70+
71+
document.addEventListener('mousedown', handleClickOutside)
72+
return () => {
73+
document.removeEventListener('mousedown', handleClickOutside)
74+
}
75+
}, [])
2376

2477
useEffect(() => {
2578
const search = async () => {
2679
console.log('Searching for:', searchTerm ? `"${searchTerm}"` : '(empty string)')
2780
setIsLoading(true)
2881
try {
82+
// Create a cache key based on search term and params
83+
const cacheKey = `dfda-variable-search:${searchTerm}:${JSON.stringify(searchParams)}`
84+
85+
// Check cache first
86+
const cachedResults = getCachedResults(cacheKey)
87+
if (cachedResults) {
88+
console.log(`Using cached results for ${cacheKey}`)
89+
setVariables(cachedResults)
90+
setIsLoading(false)
91+
return
92+
}
93+
94+
// If not in cache, perform the search
2995
const results = await searchDfdaVariables(searchTerm, searchParams)
30-
console.log('Search results:', results.length, 'items found')
96+
console.log(`Search results for ${cacheKey}:`, results.length, 'items found')
97+
98+
// Cache the results
99+
setCachedResults(cacheKey, results)
31100
setVariables(results)
32101
} catch (error) {
33102
console.error('Error searching:', error)
@@ -41,7 +110,7 @@ export default function VariableSearchAutocomplete({
41110
}, [searchTerm, searchParams])
42111

43112
return (
44-
<div className="relative flex-grow">
113+
<div ref={componentRef} className="relative flex-grow">
45114
<Input
46115
type="search"
47116
value={searchTerm}

app/dfda/components/home-page.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import OutcomeSearchAutocomplete from './OutcomeSearchAutocomplete'
1111
import AdvancedTrialSearch from './AdvancedTrialSearch'
1212
import { Robot } from '@phosphor-icons/react'
1313
import { FeatureBox } from './FeatureBox'
14-
import ProblemSection from './ProblemSection'
1514
import SolutionSection from './SolutionSection'
16-
import GoodNewsSection from './GoodNewsSection'
1715
import CitizenScienceSection from './CitizenScienceSection'
1816
import { useRouter } from 'next/navigation'
17+
import { useState } from 'react'
18+
import { getSafeUrlWithToken } from '../dfdaActions'
1919

2020
const SquigglyPattern = () => (
2121
<svg className="absolute inset-0 h-full w-full" xmlns="http://www.w3.org/2000/svg">
@@ -28,41 +28,46 @@ const SquigglyPattern = () => (
2828

2929
export default function HomePage() {
3030
const router = useRouter()
31+
const [isLoading, setIsLoading] = useState(false)
32+
33+
const handleDigitalTwinSafeClick = async (path: string) => {
34+
//setIsLoading(true)
35+
const url = await getSafeUrlWithToken('')
36+
window.open(url, '_blank')
37+
}
3138

3239
const features = [
3340
{
34-
title: "Right to Trial Act",
35-
desc: "Help us create this groundbreaking legislation",
41+
title: "The Cure Acceleration Act",
42+
desc: "Help us give people suffering access to the most promising treatments",
3643
color: "bg-blue-400",
3744
icon: Scroll,
38-
media: "https://example.com/right-to-trial.jpg",
39-
onClick: () => {
45+
media: "https://wiki.dfda.earth/right_to_trial_act_image.jpg",
46+
onClick: async () => {
4047
console.log("Right to Trial Act clicked")
48+
setIsLoading(true)
4149
router.push("/dfda/right-to-trial-act")
4250
}
4351
},
4452
{
45-
title: "FDAi Agent",
53+
title: "Your Personal FDAi Agent",
4654
desc: "Help us give everyone a free superintelligent doctor",
4755
color: "bg-green-400",
4856
icon: Robot,
4957
media: "https://player.vimeo.com/video/930843979?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479",
5058
onClick: () => {
5159
console.log("FDAi Agent clicked")
5260
// Add specific behavior here, e.g., open a modal with AI tool demo
61+
window.open("https://fdai.earth", "_blank")
5362
}
5463
},
5564
{
56-
title: "Digital Twin Safe",
65+
title: "Your Digital Twin Safe",
5766
desc: "Securely store and control your health data",
5867
color: "bg-purple-400",
5968
icon: Pill,
6069
media: "https://user-images.githubusercontent.com/2808553/180306571-ac9cc741-6f34-4059-a814-6f8a72ed8322.png",
61-
onClick: () => {
62-
console.log("Digital Twin Safe clicked")
63-
// get a dfda access token and append it to safe.dfda.earth and open it in a new tab
64-
window.open("https://safe/dfda.earth", "_blank")
65-
}
70+
onClick: handleDigitalTwinSafeClick
6671
},
6772
{
6873
title: "Clinipedia",
@@ -93,6 +98,7 @@ export default function HomePage() {
9398
desc: "Learn about the historical context and need for decentralization",
9499
color: "bg-yellow-400",
95100
icon: Info,
101+
media: "https://thinkbynumbers.org/wp-content/uploads/2021/03/news-story-headline-1-1024x563.png",
96102
href: "/dfda/why",
97103
onClick: () => {
98104
// open https://dfda.earth in a new tab
@@ -133,6 +139,11 @@ export default function HomePage() {
133139

134140
return (
135141
<div className="">
142+
{isLoading && (
143+
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center">
144+
<div className="animate-spin rounded-full h-16 w-16 border-t-4 border-white"></div>
145+
</div>
146+
)}
136147
<SquigglyPattern />
137148
<header className="relative mb-12 overflow-hidden rounded-xl border-4 border-black bg-white p-6 text-center shadow-[8px_8px_0px_0px_rgba(0,0,0,1)]">
138149
<motion.h1

app/dfda/dfdaActions.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
11
"use server"
22

3-
import { Effectiveness } from "@prisma/client"
3+
import {Effectiveness} from "@prisma/client"
4+
import {authOptions} from "@/lib/auth"
45

5-
import { prisma } from "@/lib/db"
6+
import {prisma} from "@/lib/db"
67
import {findArticleByTopic, writeArticle} from "@/lib/agents/researcher/researcher";
8+
import {dfdaGET, getOrCreateDfdaAccessToken} from '@/lib/dfda'
9+
import {getServerSession} from 'next-auth/next'
10+
11+
export async function getSafeUrlWithToken(url: string) {
12+
if(!url.includes('https')) {
13+
url = 'https://safe.dfda.earth/app/public/#/app/' + url;
14+
}
15+
const token = await getDfdaAccessToken();
16+
return `${url}?access_token=${token}`;
17+
}
18+
19+
export async function getDfdaAccessToken() {
20+
const session = await getServerSession(authOptions)
21+
if (!session?.user?.id) {
22+
console.log('No user ID found in session:', session)
23+
return null
24+
}
25+
26+
try {
27+
return await getOrCreateDfdaAccessToken(session.user.id)
28+
} catch (error) {
29+
console.error('Error getting DFDA access token:', error)
30+
return null
31+
}
32+
}
733

834
export async function fetchConditions() {
935
return prisma.dfdaCondition.findMany()
@@ -159,4 +185,8 @@ export async function getMetaAnalysis(treatmentName: string, conditionName: stri
159185
}
160186

161187
return writeArticle(topic, "test-user");
188+
}
189+
190+
export const getDataSources = async (): Promise<any> => {
191+
return dfdaGET('connectors/list', { final_callback_url: window.location.href })
162192
}

0 commit comments

Comments
 (0)