From 719a1e81d5af5c872f79f11f462e4b30211f80b7 Mon Sep 17 00:00:00 2001
From: IwanIDev <iwan@iwani.dev>
Date: Fri, 20 Mar 2026 13:39:53 +0000
Subject: Add local development support with Docker for Drupal and configure
 API proxy

---
 src/App.tsx | 107 ++++++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 82 insertions(+), 25 deletions(-)

(limited to 'src/App.tsx')

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>
   )
 }
 
-- 
cgit 

