Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix infinite loop effect and consolidate panic pages using route params #39

Merged
merged 4 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 70 additions & 52 deletions src/components/Resources/ResourceTable.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
import { useEffect, useState } from 'react'
import { QueryData } from '@supabase/supabase-js'
import client from '@/database/client.tsx'
import TagSection from '@/components/TagSection.tsx'
import { FaHeart } from 'react-icons/fa'
import client from '@/database/client'
import { Tooltip } from 'flowbite-react'
import { useEffect, useState } from 'react'
import { FaHeart } from 'react-icons/fa'
import { useLoaderData } from 'react-router-dom'

export interface Resource {
id: number
name: string
description: string
link: string
num_helped: number
}

export default function ResourceTable() {
const resourcesQuery = client.from('resources').select()
type ResourcesType = QueryData<typeof resourcesQuery>
const { data = [] } = useLoaderData() as { data: Resource[] }
const [tableData, setTableData] = useState<Resource[]>([])

const [data, setData] = useState<ResourcesType>([])
// TODO: Pass this responsibility to a context provider so we can reduce calls on panic pages where table is rendered
useEffect(() => {
const fetchData = async () => {
const { data, error } = await resourcesQuery
if (error) throw error
const resources: ResourcesType = data
setData(resources)
const fetchResources = async () => {
try {
const resourcesQuery = client.from('resources').select()
const { data, error } = await resourcesQuery
if (error) throw error
const resources: Resource[] = data
setTableData(resources)
} catch (error) {
console.error('unable to fetch resources', error)
}
}

if (data.length === 0 || tableData.length === 0) {
fetchResources()
}
fetchData()
}, [resourcesQuery])
}, [tableData.length, data.length])

return (
<table className="w-full rounded-lg bg-white">
<thead className="border-0 border-b-8 border-solid border-orange-50 ">
Expand All @@ -30,45 +47,46 @@ export default function ResourceTable() {
</tr>
</thead>
<tbody>
{data.map((d) => {
return (
<tr
className="border-orange-50 hover:bg-gray-100"
key={d.name}
>
<td className="w-96 p-4">
<a
className="text-orange-900 hover:underline"
href={d.link}
rel="noreferrer noopener"
target="_blank"
>
{d.name}
</a>
</td>
<td className="p-4">{d.description}</td>
<td className="flex w-48 flex-wrap gap-1 p-4">
<TagSection resourceId={d.id} />
</td>
<td className="p-4 text-xs">
<Tooltip
content={`This resource has helped ${d.num_helped} people`}
animation="duration-1000"
className="bg-gray-900 text-white dark:bg-gray-700"
arrow={false}
>
<span
data-tooltip-target="tooltip-default"
className="flex justify-end text-right text-xs font-bold"
{tableData.length > 0 &&
tableData.map((d) => {
return (
<tr
className="border-orange-50 hover:bg-gray-100"
key={d.name}
>
<td className="w-96 p-4">
<a
className="text-orange-900 hover:underline"
href={d.link}
rel="noreferrer noopener"
target="_blank"
>
{d.name}
</a>
</td>
<td className="p-4">{d.description}</td>
<td className="flex w-48 flex-wrap gap-1 p-4">
<TagSection resourceId={d.id} />
</td>
<td className="p-4 text-xs">
<Tooltip
content={`This resource has helped ${d.num_helped} people`}
animation="duration-1000"
className="bg-gray-900 text-white dark:bg-gray-700"
arrow={false}
>
{d.num_helped}
<FaHeart className="ml-1 text-orange-500 " />
</span>
</Tooltip>
</td>
</tr>
)
})}
<span
data-tooltip-target="tooltip-default"
className="flex justify-end text-right text-xs font-bold"
>
{d.num_helped}
<FaHeart className="ml-1 text-orange-500 " />
</span>
</Tooltip>
</td>
</tr>
)
})}
</tbody>
</table>
)
Expand Down
24 changes: 13 additions & 11 deletions src/components/Resources/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import client from '@/database/client'
import { useEffect, useState } from 'react'
import { ReactSearchAutocomplete } from 'react-search-autocomplete'
import '../../index.css'
import client from '@/database/client.tsx'
import { useEffect, useState } from 'react'
import { QueryData } from '@supabase/supabase-js'

export const SearchBar = () => {
const resourcesQuery = client.from('resources').select()
type ResourcesType = QueryData<typeof resourcesQuery>
const [searchQuery] = useState<string>('')

// TODO: Define type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [items, setItems] = useState<any>()
const [items] = useState<any>([])
Dismissed Show dismissed Hide dismissed
const resourcesQuery = client.from('resources').select()

useEffect(() => {
const fetchData = async () => {
const { data, error } = await resourcesQuery
if (error) throw error
const resources: ResourcesType = data
setItems(resources)
// TODO: Fetch new resources that match searchQuery from user input
// const { data, error } = await resourcesQuery
// if (error) throw error
// setItems(data?.resources || [])
}
fetchData()
}, [resourcesQuery])
}, [searchQuery, resourcesQuery])

return <ReactSearchAutocomplete items={items} />
}
14 changes: 9 additions & 5 deletions src/components/TagSection.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import MiniTag from '@/components/MiniTag'
import { useState, useEffect } from 'react'
import client from '@/database/client'
import { QueryData } from '@supabase/supabase-js'
import { useEffect, useMemo, useState } from 'react'

type TagSectionProps = {
resourceId: number
}
export default function TagSection({ resourceId }: TagSectionProps) {
const tagsQuery = client
.from('tag_resource')
.select('tags(name)')
.eq('resource_id', resourceId)
const tagsQuery = useMemo(
() =>
client
.from('tag_resource')
.select('tags(name)')
.eq('resource_id', resourceId),
[resourceId]
)
type TagsType = QueryData<typeof tagsQuery>
const [tags, setTags] = useState<TagsType>([])

Expand Down
23 changes: 14 additions & 9 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import LoginPage from '@/views/AuthPages/LoginPage.tsx'
import ChangePasswordPage from '@/views/AuthPages/ChangePasswordPage.tsx'
import { ResourcesPage } from '@/views/ResourcesPage/ResourcesPage.tsx'
import { PanicPage } from '@/views/PanicPage/PanicPage.tsx'
import client from './database/client.tsx'

const router = createBrowserRouter([
{
Expand All @@ -33,22 +34,26 @@ const router = createBrowserRouter([
{
path: '/resources',
element: <ResourcesPage />,
loader: async () => {
return await client.from('resources').select()
},
},
{
path: '/panic',
element: <PanicPage level="main" />,
},
{
path: '/panic/prevent',
element: <PanicPage level="prevent" />,
element: <PanicPage />,
},

{
path: '/panic/prepare',
element: <PanicPage level="prepare" />,
// level: prevent | prepare | predicament
path: '/panic/:level',
element: <PanicPage />,
loader: async () => {
return await client.from('resources').select()
},
},
{
path: '/panic/predicament',
element: <PanicPage level="predicament" />,
path: '*',
element: <NotFoundPage />,
},
])

Expand Down
29 changes: 11 additions & 18 deletions src/views/PanicPage/PanicPage.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
import { Footer } from '@/components/Footer/Footer.tsx'
import { Navigation } from '@/components/Navigation/Navigation.tsx'
import { PanicMain } from '@/components/Panic/PanicMain.tsx'
import { useEffect, useState } from 'react'
import { PanicPredicament } from '@/components/Panic/PanicPredicament.tsx'
import { PanicPrepare } from '@/components/Panic/PanicPrepare.tsx'
import { PanicPrevent } from '@/components/Panic/PanicPrevent.tsx'
import { useLocation } from 'react-router-dom'
import { useMemo } from 'react'
import { useParams } from 'react-router-dom'

export const PanicPage = (props: { level: string }) => {
const { level } = props
const [activePage, setActivePage] = useState(<PanicMain />)
const location = useLocation()
useEffect(() => {
export const PanicPage = () => {
const { level = 'main' } = useParams<{ level: string }>()

const activePage = useMemo(() => {
switch (level) {
case 'main':
setActivePage(<PanicMain />)
break
case 'prevent':
setActivePage(<PanicPrevent />)
break
return <PanicPrevent />
case 'prepare':
setActivePage(<PanicPrepare />)
break
return <PanicPrepare />
case 'predicament':
setActivePage(<PanicPredicament />)
break
return <PanicPredicament />
default:
setActivePage(<PanicMain />)
return <PanicMain />
}
}, [location, level])
}, [level])
return (
<div className="relative min-h-screen w-screen bg-orange-50">
<Navigation />
Expand Down