Skip to content

Commit

Permalink
Add new components: SearchAutocomplete, TreatmentSearch, UrlDisplay, …
Browse files Browse the repository at this point in the history
…and LoginPrompt

Introduces SearchAutocomplete for dynamic search suggestions, TreatmentSearchAutocomplete for specific treatment queries, UrlDisplay for displaying URLs with favicons, and LoginPromptButton for user authentication prompts. Enhances the UI with reusable, functionality-focused components.
  • Loading branch information
mikepsinn committed Sep 14, 2024
1 parent 73c52ef commit 3bba7e4
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 0 deletions.
57 changes: 57 additions & 0 deletions app/dfda/components/SearchAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useState, useEffect } from 'react'
import { Input } from "@/components/ui/input"

interface SearchAutocompleteProps {
onSelect: (item: string) => void
placeholder?: string
searchFunction: (query: string) => Promise<string[]>
}

export default function SearchAutocomplete({ onSelect, placeholder, searchFunction }: SearchAutocompleteProps) {
const [query, setQuery] = useState('')
const [suggestions, setSuggestions] = useState<string[]>([])
const [showDropdown, setShowDropdown] = useState(false)

useEffect(() => {
if (query.length > 2) {
searchFunction(query).then(extractedSuggestions => {
setSuggestions(extractedSuggestions)
setShowDropdown(true)
})
} else {
setSuggestions([])
setShowDropdown(false)
}
}, [query, searchFunction])

const handleSuggestionClick = (suggestion: string) => {
setQuery(suggestion)
setShowDropdown(false)
onSelect(suggestion)
}

return (
<div className="relative">
<Input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder={placeholder}
className="w-full"
/>
{showDropdown && (
<ul className="absolute z-10 w-full bg-background border border-input mt-1 max-h-60 overflow-auto rounded-md shadow-md">
{suggestions.map((suggestion, index) => (
<li
key={index}
onClick={() => handleSuggestionClick(suggestion)}
className="px-4 py-2 hover:bg-accent hover:text-accent-foreground cursor-pointer"
>
{suggestion}
</li>
))}
</ul>
)}
</div>
)
}
17 changes: 17 additions & 0 deletions app/dfda/components/TreatmentSearchAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import SearchAutocomplete from './SearchAutocomplete'
import { searchDfdaTreatments } from "@/lib/clinicaltables"

interface TreatmentSearchAutocompleteProps {
onTreatmentSelect: (treatment: string) => void
placeholder?: string
}

export default function TreatmentSearchAutocomplete({ onTreatmentSelect, placeholder = "Enter treatment" }: TreatmentSearchAutocompleteProps) {
return (
<SearchAutocomplete
onSelect={onTreatmentSelect}
placeholder={placeholder}
searchFunction={searchDfdaTreatments}
/>
)
}
45 changes: 45 additions & 0 deletions components/LoginPromptButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useState, useEffect } from "react"
import { Dialog, DialogContent, DialogTrigger, DialogTitle } from "@/components/ui/dialog"
import { UserAuthForm } from "@/components/user/user-auth-form"
import { VisuallyHidden } from "@radix-ui/themes"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"

interface LoginPromptProps {
buttonText?: string
buttonVariant?: "outline" | "default"
buttonSize?: "default" | "sm" | "lg" | "icon"
}

export function LoginPromptButton({
buttonText = "Sign in",
buttonVariant = "outline",
buttonSize = "default",
}: LoginPromptProps) {
const [open, setOpen] = useState(false)
const [callbackUrl, setCallbackUrl] = useState<string | undefined>(undefined)

useEffect(() => {
if (typeof window !== "undefined") {
setCallbackUrl(window.location.href)
} else {
console.error("window is not defined in LoginPrompt")
}
}, [])

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<button className={cn(buttonVariants({ variant: buttonVariant, size: buttonSize }))}>
{buttonText}
</button>
</DialogTrigger>
<DialogContent>
<VisuallyHidden>
<DialogTitle>Sign In</DialogTitle>
</VisuallyHidden>
<UserAuthForm callbackUrl={callbackUrl} />
</DialogContent>
</Dialog>
)
}
29 changes: 29 additions & 0 deletions components/article/UrlDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Image from "next/image"

interface UrlDisplayProps {
url: string;
}

export function UrlDisplay({ url }: UrlDisplayProps) {
const hostname = new URL(url).hostname;

return (
<div className="flex items-center space-x-2">
<Image
src={`https://www.google.com/s2/favicons?sz=32&domain=${hostname}`}
alt={`${hostname} favicon`}
width={16}
height={16}
className="rounded-sm flex-shrink-0"
/>
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-gray-600 no-underline"
>
{hostname}
</a>
</div>
);
}

0 comments on commit 3bba7e4

Please sign in to comment.