summaryrefslogtreecommitdiff
path: root/src/App.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/App.tsx')
-rw-r--r--src/App.tsx107
1 files changed, 82 insertions, 25 deletions
diff --git a/src/App.tsx b/src/App.tsx
index aaeba0a..3472fd0 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,34 +1,91 @@
-import { useState } from 'react'
-import reactLogo from './assets/react.svg'
-import viteLogo from '/vite.svg'
+import { useEffect, useState } from 'react'
+import { fetchDrupalResource } from './lib/drupalClient'
import './App.css'
+type DrupalArticle = {
+ id: string
+ attributes?: {
+ title?: string
+ status?: boolean
+ created?: string
+ }
+}
+
+type DrupalCollectionResponse<T> = {
+ data: T[]
+}
+
+type DrupalJsonApiEntryPoint = {
+ links: Record<string, { href: string }>
+}
+
function App() {
- const [count, setCount] = useState(0)
+ const [articles, setArticles] = useState<DrupalArticle[]>([])
+ const [resourcePath, setResourcePath] = useState<string | null>(null)
+ const [isLoading, setIsLoading] = useState(true)
+ const [error, setError] = useState<string | null>(null)
+
+ useEffect(() => {
+ const loadArticles = async () => {
+ try {
+ setIsLoading(true)
+ setError(null)
+
+ const entryPoint = await fetchDrupalResource<DrupalJsonApiEntryPoint>('')
+ const nodeResources = Object.keys(entryPoint.links).filter((key) => key.startsWith('node--'))
+
+ if (nodeResources.length === 0) {
+ throw new Error('No node resources found in JSON:API. Create a content type (for example Article) and ensure JSON:API is enabled.')
+ }
+
+ const selectedNodeResource = nodeResources.includes('node--article') ? 'node--article' : nodeResources[0]
+ const selectedPath = `/${selectedNodeResource.replace('--', '/')}`
+
+ setResourcePath(selectedPath)
+
+ const response = await fetchDrupalResource<DrupalCollectionResponse<DrupalArticle>>(selectedPath)
+ setArticles(response.data)
+ } catch (loadError) {
+ const message = loadError instanceof Error ? loadError.message : 'Failed to load articles from Drupal.'
+ setError(message)
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ void loadArticles()
+ }, [])
return (
- <>
- <div className="flex gap-8 justify-center mb-8">
- <a href="https://vite.dev" target="_blank" rel="noreferrer" className="hover:drop-shadow-lg transition-all">
- <img src={viteLogo} className="h-24 w-24" alt="Vite logo" />
- </a>
- <a href="https://react.dev" target="_blank" rel="noreferrer" className="hover:drop-shadow-lg transition-all">
- <img src={reactLogo} className="h-24 w-24" alt="React logo" />
- </a>
- </div>
- <h1>Vite + React</h1>
- <div className="card">
- <button onClick={() => setCount((count) => count + 1)}>
- count is {count}
- </button>
- <p>
- Edit <code>src/App.tsx</code> and save to test HMR
+ <main>
+ <h1 className="mb-4 text-2xl font-bold">Articles</h1>
+ {!isLoading && !error && resourcePath && <p>Using resource: {resourcePath}</p>}
+
+ {isLoading && <p>Loading articles…</p>}
+
+ {error && (
+ <p role="alert" className="text-red-600">
+ Could not fetch articles: {error}
</p>
- </div>
- <p className="read-the-docs">
- Click on the Vite and React logos to learn more
- </p>
- </>
+ )}
+
+ {!isLoading && !error && articles.length === 0 && (
+ <p>Connected to Drupal, but no articles were returned.</p>
+ )}
+
+ {!isLoading && !error && articles.length > 0 && (
+ <ul style={{ textAlign: 'left' }}>
+ {articles.map((article) => (
+ <li key={article.id}>
+ <strong>{article.attributes?.title ?? '(Untitled)'}</strong>
+ <div>ID: {article.id}</div>
+ <div>Status: {article.attributes?.status ? 'Published' : 'Unpublished'}</div>
+ {article.attributes?.created && <div>Created: {new Date(article.attributes.created).toLocaleString()}</div>}
+ </li>
+ ))}
+ </ul>
+ )}
+ </main>
)
}