decoupled-cli 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +416 -0
  2. package/bin/decoupled-cli +3 -0
  3. package/dist/commands/auth.d.ts +3 -0
  4. package/dist/commands/auth.d.ts.map +1 -0
  5. package/dist/commands/auth.js +386 -0
  6. package/dist/commands/auth.js.map +1 -0
  7. package/dist/commands/config.d.ts +3 -0
  8. package/dist/commands/config.d.ts.map +1 -0
  9. package/dist/commands/config.js +84 -0
  10. package/dist/commands/config.js.map +1 -0
  11. package/dist/commands/content.d.ts +3 -0
  12. package/dist/commands/content.d.ts.map +1 -0
  13. package/dist/commands/content.js +199 -0
  14. package/dist/commands/content.js.map +1 -0
  15. package/dist/commands/download.d.ts +4 -0
  16. package/dist/commands/download.d.ts.map +1 -0
  17. package/dist/commands/download.js +127 -0
  18. package/dist/commands/download.js.map +1 -0
  19. package/dist/commands/health.d.ts +3 -0
  20. package/dist/commands/health.d.ts.map +1 -0
  21. package/dist/commands/health.js +28 -0
  22. package/dist/commands/health.js.map +1 -0
  23. package/dist/commands/org.d.ts +3 -0
  24. package/dist/commands/org.d.ts.map +1 -0
  25. package/dist/commands/org.js +73 -0
  26. package/dist/commands/org.js.map +1 -0
  27. package/dist/commands/spaces.d.ts +3 -0
  28. package/dist/commands/spaces.d.ts.map +1 -0
  29. package/dist/commands/spaces.js +613 -0
  30. package/dist/commands/spaces.js.map +1 -0
  31. package/dist/commands/tokens.d.ts +3 -0
  32. package/dist/commands/tokens.d.ts.map +1 -0
  33. package/dist/commands/tokens.js +90 -0
  34. package/dist/commands/tokens.js.map +1 -0
  35. package/dist/commands/usage.d.ts +3 -0
  36. package/dist/commands/usage.d.ts.map +1 -0
  37. package/dist/commands/usage.js +104 -0
  38. package/dist/commands/usage.js.map +1 -0
  39. package/dist/index.d.ts +3 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +49 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/lib/api.d.ts +40 -0
  44. package/dist/lib/api.d.ts.map +1 -0
  45. package/dist/lib/api.js +162 -0
  46. package/dist/lib/api.js.map +1 -0
  47. package/dist/lib/config.d.ts +63 -0
  48. package/dist/lib/config.d.ts.map +1 -0
  49. package/dist/lib/config.js +317 -0
  50. package/dist/lib/config.js.map +1 -0
  51. package/examples/.cursorrules +189 -0
  52. package/examples/CLAUDE.md +1080 -0
  53. package/examples/GEMINI.md +1056 -0
  54. package/examples/content-import-sample.json +114 -0
  55. package/package.json +55 -0
@@ -0,0 +1,1080 @@
1
+ # CLAUDE.md - End-to-End Content Type Development Guide
2
+
3
+ This document provides Claude Code with comprehensive instructions for building complete content type implementations from frontend to backend in this Drupal-Next.js project.
4
+
5
+ ## Project Overview
6
+
7
+ **Architecture**: Headless Drupal backend with Next.js frontend
8
+ **Backend**: Drupal 11 with GraphQL Compose and DCloud Import API
9
+ **Frontend**: Next.js 15 with TypeScript, Tailwind CSS, and Apollo GraphQL
10
+ **Environment**: DDEV local development
11
+
12
+ ## Environment Configuration
13
+
14
+ **Environment Variables (`.env.local`):**
15
+ - `NEXT_PUBLIC_DRUPAL_BASE_URL` - Drupal backend URL
16
+ - `DRUPAL_CLIENT_ID` - OAuth client ID for API authentication
17
+ - `DRUPAL_CLIENT_SECRET` - OAuth client secret for API authentication
18
+ - `DRUPAL_REVALIDATE_SECRET` - Secret for on-demand revalidation
19
+ - `NODE_TLS_REJECT_UNAUTHORIZED=0` - Allow self-signed certificates (development)
20
+
21
+ ## DCloud CLI Setup
22
+
23
+ **First-time CLI setup:**
24
+ ```bash
25
+ # 1. Install dcloud-cli locally (if not already present in package.json)
26
+ npm install --save-dev dcloud-cli
27
+
28
+ # 2. Configure authentication - you have two options:
29
+
30
+ # Option A: Use OAuth with your existing Drupal credentials (recommended for local development)
31
+ npx dcloud-cli auth oauth
32
+
33
+ # Option B: Use personal access token (for production/remote deployments)
34
+ npx dcloud-cli auth login
35
+
36
+ # 3. Set your default space for content imports (optional but recommended)
37
+ npx dcloud-cli spaces use <space_id>
38
+
39
+ # 4. Verify authentication and space setup
40
+ npx dcloud-cli spaces current
41
+ ```
42
+
43
+ **OAuth Prerequisites (for `dcloud auth oauth`):**
44
+ Your `.env.local` must contain:
45
+ - `NEXT_PUBLIC_DRUPAL_BASE_URL=https://your-drupal-site.ddev.site`
46
+ - `DRUPAL_CLIENT_ID=your_oauth_client_id`
47
+ - `DRUPAL_CLIENT_SECRET=your_oauth_client_secret`
48
+
49
+ **Authentication Method Differences:**
50
+ - **OAuth** → Works with your Drupal site API (content import, Drupal operations)
51
+ - **Personal Access Token** → Works with DrupalCloud platform API (spaces, users, organizations)
52
+
53
+ **If CLI is not available locally:**
54
+ - For projects with dcloud-cli in package.json: Run `npm install` then use `npx dcloud-cli`
55
+ - For development: `cd cli && npm install && npm run build && npm link` then use `dcloud`
56
+ - Always prefer using `npx dcloud-cli` for consistency and local package management
57
+
58
+ ## End-to-End Development Workflow
59
+
60
+ When asked to create a new content type (e.g., "create a product page"), follow these steps:
61
+
62
+ **Recommended Workflow (OAuth + Drupal API):**
63
+ Use OAuth authentication for direct Drupal content import, then frontend development.
64
+
65
+ ### 1. Content Analysis & Planning
66
+
67
+ **Analyze the request and determine:**
68
+ - Content type name and machine name
69
+ - Required fields and their types
70
+ - Frontend components needed
71
+ - URL structure and routing
72
+ - Display requirements (listing, detail pages, etc.)
73
+
74
+ **Create a todo list for tracking progress:**
75
+ ```markdown
76
+ 1. Verify DCloud CLI authentication (npx dcloud-cli auth status)
77
+ 2. Create DCloud Import JSON for [content_type]
78
+ 3. Import content type and sample content to Drupal (npx dcloud-cli content import)
79
+ 4. **CRITICAL**: Run schema generation (`npm run generate-schema`) to update GraphQL schema
80
+ 5. Create TypeScript types and GraphQL queries
81
+ 6. Build frontend components ([ContentCard], [ContentRenderer])
82
+ 7. Create listing and detail pages
83
+ 8. Test build process and fix errors
84
+ 9. Validate end-to-end functionality
85
+ ```
86
+
87
+ **Legacy Platform Workflow (PAT + DrupalCloud Platform):**
88
+ ```markdown
89
+ 1. Verify DCloud CLI authentication (npx dcloud-cli auth status)
90
+ 2. Set default space if needed (npx dcloud-cli spaces use <id>)
91
+ 3. Create DCloud Import JSON for [content_type]
92
+ 4. Import content type and sample content via platform (npx dcloud-cli spaces content-import)
93
+ 5. **CRITICAL**: Run schema generation (`npm run generate-schema`) to update GraphQL schema
94
+ 6. Create TypeScript types and GraphQL queries
95
+ 7. Build frontend components ([ContentCard], [ContentRenderer])
96
+ 8. Create listing and detail pages
97
+ 9. Test build process and fix errors
98
+ 10. Validate end-to-end functionality
99
+ ```
100
+
101
+ ### 0. CLI Authentication Check
102
+
103
+ **ALWAYS start by verifying CLI setup:**
104
+ ```bash
105
+ # Check if CLI is authenticated
106
+ npx dcloud-cli auth status
107
+
108
+ # If not authenticated, choose authentication method:
109
+ # For local development with Drupal OAuth:
110
+ npx dcloud-cli auth oauth
111
+
112
+ # For production with personal access token:
113
+ npx dcloud-cli auth login
114
+
115
+ # List available spaces and set default
116
+ npx dcloud-cli spaces list
117
+ npx dcloud-cli spaces use <space_id>
118
+ ```
119
+
120
+ **If CLI authentication fails:**
121
+ - For OAuth: Verify `.env.local` contains `NEXT_PUBLIC_DRUPAL_BASE_URL`, `DRUPAL_CLIENT_ID`, `DRUPAL_CLIENT_SECRET`
122
+ - For personal tokens: Check that DCloud platform is accessible
123
+ - Ensure user has proper permissions for the operations you're trying to perform
124
+
125
+ **Important: Command Availability by Authentication Method**
126
+ - **OAuth commands**: `content import`, `content status`, `auth status`
127
+ - **PAT-only commands**: `spaces list`, `spaces use`, `usage`, `org info`
128
+ - **Universal commands**: `auth login`, `auth oauth`, `auth test`
129
+
130
+ ### 2. DCloud Import JSON Creation
131
+
132
+ **Get example format with CLI:**
133
+ ```bash
134
+ npx dcloud-cli spaces content-import --example > my-content-type.json
135
+ ```
136
+
137
+ **Use this JSON structure:**
138
+
139
+ ```json
140
+ {
141
+ "model": [
142
+ {
143
+ "bundle": "content_type_name",
144
+ "description": "Description of the content type",
145
+ "label": "Content Type Label",
146
+ "body": true,
147
+ "fields": [
148
+ {
149
+ "id": "field_name",
150
+ "label": "Field Label",
151
+ "type": "text|string|image|datetime|bool|text[]"
152
+ }
153
+ ]
154
+ }
155
+ ],
156
+ "content": [
157
+ {
158
+ "id": "item1",
159
+ "type": "node.content_type_name",
160
+ "path": "/content-type/item-slug",
161
+ "values": {
162
+ "title": "Item Title",
163
+ "body": "<p>Body content</p>",
164
+ "field_name": "field_value"
165
+ }
166
+ }
167
+ ]
168
+ }
169
+ ```
170
+
171
+ **Field Type Reference:**
172
+ - `text` - Long text with formatting
173
+ - `string` - Short plain text (max 255 chars)
174
+ - `image` - Image upload
175
+ - `datetime` - Date and time
176
+ - `bool` - Boolean true/false
177
+ - `text[]` - Multiple text values
178
+ - `string[]` - Multiple string values
179
+
180
+ **Important Notes:**
181
+ - Field IDs become `field_[id]` in Drupal (e.g., `"id": "price"` → `field_price`)
182
+ - **CRITICAL**: In content values, use the field ID directly without "field_" prefix (e.g., `"price": "$299.99"` NOT `"field_price": "$299.99"`)
183
+ - Use `"body": true` to include standard body field
184
+ - Always include sample content for testing
185
+ - Path aliases should follow `/content-type/slug` pattern
186
+ - **For image fields**: Use full URLs with the Drupal domain from `.env.local`, not relative paths:
187
+ ```json
188
+ "featured_image": {
189
+ "uri": "${DRUPAL_BASE_URL}/modules/custom/dcloud_import/resources/placeholder.png",
190
+ "alt": "Description of the image",
191
+ "title": "Image title"
192
+ }
193
+ ```
194
+ Always read the `NEXT_PUBLIC_DRUPAL_BASE_URL` from `.env.local` and use that as the base for image URIs to ensure images load correctly from the Drupal backend.
195
+
196
+ ### 3. Import via DCloud CLI
197
+
198
+ **Import Content Type:**
199
+ ```bash
200
+ # NEW: Import content directly to your Drupal site (OAuth authentication)
201
+ npx dcloud-cli content import --file content-type-import.json
202
+
203
+ # Or preview first to see what will be imported
204
+ npx dcloud-cli content import --file content-type-import.json --preview
205
+
206
+ # LEGACY: Import via DrupalCloud platform (requires PAT authentication and space setup)
207
+ npx dcloud-cli spaces content-import --file content-type-import.json
208
+ npx dcloud-cli spaces content-import <space_id> --file content-type-import.json
209
+ ```
210
+
211
+ **Generate example import file:**
212
+ ```bash
213
+ # Get example import JSON structure (works with both methods)
214
+ npx dcloud-cli content import --example > content-type-import.json
215
+ # Then edit the generated file with your content type definition
216
+ ```
217
+
218
+ **Always check the response for success and note:**
219
+ - Content type machine name (may differ from input)
220
+ - Field machine names (auto-generated)
221
+ - Node IDs created
222
+ - GraphQL schema updates
223
+ - **Important**: GraphQL field names may differ from DCloud field IDs (check actual schema)
224
+
225
+ **CRITICAL: Immediately Update GraphQL Schema After Import:**
226
+ ```bash
227
+ # Generate updated GraphQL schema to include new content type
228
+ npm run generate-schema
229
+ ```
230
+
231
+ **Verify the schema includes your content type:**
232
+ ```bash
233
+ # Check that your content type appears in the generated schema
234
+ grep -i [your_content_type] schema/schema.graphql
235
+
236
+ # Example for products:
237
+ grep -i product schema/schema.graphql
238
+ ```
239
+
240
+ **Test that content was imported successfully:**
241
+ ```bash
242
+ # Check that new content type appears in generated schema
243
+ npm run generate-schema
244
+ grep -i [your_content_type] schema/schema.graphql
245
+ ```
246
+
247
+ ### 4. Frontend Implementation
248
+
249
+ **File Structure:**
250
+ ```
251
+ app/
252
+ ├── components/
253
+ │ ├── [ContentType]Card.tsx # List view component
254
+ │ ├── [ContentType]Renderer.tsx # Detail view component
255
+ │ └── DynamicIcon.tsx # Icon component (if needed)
256
+ ├── [content-type]/
257
+ │ └── page.tsx # Listing page
258
+ ├── [...slug]/
259
+ │ └── page.tsx # Dynamic routing (update)
260
+ lib/
261
+ ├── queries.ts # GraphQL queries (update)
262
+ └── types.ts # TypeScript types (update)
263
+ ```
264
+
265
+ #### GraphQL Queries
266
+
267
+ **Add to `lib/queries.ts`:**
268
+ ```typescript
269
+ export const GET_CONTENT_TYPES = gql`
270
+ query GetContentTypes($first: Int = 10) {
271
+ node[ContentType]s(first: $first, sortKey: CREATED_AT) {
272
+ nodes {
273
+ id
274
+ title
275
+ path
276
+ created { timestamp }
277
+ changed { timestamp }
278
+ ... on Node[ContentType] {
279
+ body { processed }
280
+ field[FieldName] { processed }
281
+ // Add all fields
282
+ }
283
+ }
284
+ }
285
+ }
286
+ `
287
+ ```
288
+
289
+ **Update `GET_NODE_BY_PATH` to include new content type:**
290
+ ```typescript
291
+ ... on Node[ContentType] {
292
+ id
293
+ title
294
+ body { processed }
295
+ field[FieldName] { processed }
296
+ // Add all fields
297
+ }
298
+ ```
299
+
300
+ #### TypeScript Types
301
+
302
+ **Add to `lib/types.ts`:**
303
+ ```typescript
304
+ export interface Drupal[ContentType] extends DrupalNode {
305
+ body?: { processed: string }
306
+ field[FieldName]?: { processed: string }
307
+ // Add all fields with proper types
308
+ }
309
+
310
+ export interface [ContentType]Data {
311
+ node[ContentType]s: {
312
+ nodes: Drupal[ContentType][]
313
+ }
314
+ }
315
+ ```
316
+
317
+ #### Component Templates
318
+
319
+ **Card Component (`[ContentType]Card.tsx`):**
320
+ ```typescript
321
+ import Link from 'next/link'
322
+ import { Drupal[ContentType] } from '@/lib/types'
323
+
324
+ interface [ContentType]CardProps {
325
+ [contentType]: Drupal[ContentType]
326
+ }
327
+
328
+ export default function [ContentType]Card({ [contentType] }: [ContentType]CardProps) {
329
+ return (
330
+ <div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden hover:shadow-md transition-shadow duration-200">
331
+ <div className="p-6">
332
+ <h3 className="text-xl font-semibold text-gray-900 mb-2">
333
+ {[contentType].title}
334
+ </h3>
335
+
336
+ {/* Add field displays */}
337
+ {/* For text[] fields with HTML content, use dangerouslySetInnerHTML */}
338
+ {/* Example: <span dangerouslySetInnerHTML={{ __html: field.processed }} /> */}
339
+
340
+ <Link
341
+ href={[contentType].path || `/[content-type]/${[contentType].id}`}
342
+ className="inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 transition-colors duration-200"
343
+ >
344
+ Learn More
345
+ </Link>
346
+ </div>
347
+ </div>
348
+ )
349
+ }
350
+ ```
351
+
352
+ **Renderer Component (`[ContentType]Renderer.tsx`):**
353
+ ```typescript
354
+ import Header from './Header'
355
+ import { Drupal[ContentType] } from '@/lib/types'
356
+
357
+ interface [ContentType]RendererProps {
358
+ [contentType]: Drupal[ContentType]
359
+ }
360
+
361
+ export default function [ContentType]Renderer({ [contentType] }: [ContentType]RendererProps) {
362
+ return (
363
+ <div className="min-h-screen bg-gray-50">
364
+ <Header />
365
+ <main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
366
+ <article className="bg-white rounded-lg shadow-sm overflow-hidden">
367
+ <div className="p-8">
368
+ <h1 className="text-3xl md:text-4xl font-bold text-gray-900 mb-6">
369
+ {[contentType].title}
370
+ </h1>
371
+
372
+ {/* Add field displays */}
373
+ {/* For text[] fields with HTML content, use dangerouslySetInnerHTML */}
374
+ {/* Example: <span dangerouslySetInnerHTML={{ __html: field.processed }} /> */}
375
+
376
+ {[contentType].body?.processed && (
377
+ <div
378
+ className="prose prose-lg max-w-none mt-8"
379
+ dangerouslySetInnerHTML={{ __html: [contentType].body.processed }}
380
+ />
381
+ )}
382
+ </div>
383
+ </article>
384
+ </main>
385
+ </div>
386
+ )
387
+ }
388
+ ```
389
+
390
+ #### Listing Page
391
+
392
+ **Create `app/[content-type]/page.tsx`:**
393
+ ```typescript
394
+ import Header from '../components/Header'
395
+ import [ContentType]Card from '../components/[ContentType]Card'
396
+ import { GET_CONTENT_TYPES } from '@/lib/queries'
397
+ import { getServerApolloClient } from '@/lib/apollo-client'
398
+ import { [ContentType]Data } from '@/lib/types'
399
+
400
+ export const revalidate = 300
401
+
402
+ export default async function [ContentType]sPage() {
403
+ const apollo = getServerApolloClient(await headers())
404
+
405
+ try {
406
+ const { data } = await apollo.query<[ContentType]Data>({
407
+ query: GET_CONTENT_TYPES,
408
+ variables: { first: 20 },
409
+ fetchPolicy: 'no-cache'
410
+ })
411
+
412
+ const [contentType]s = data?.node[ContentType]s?.nodes || []
413
+
414
+ return (
415
+ <div className="min-h-screen bg-gray-50">
416
+ <Header />
417
+ <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
418
+ <div className="text-center mb-12">
419
+ <h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
420
+ [Content Types]
421
+ </h1>
422
+ <p className="text-xl text-gray-600 max-w-3xl mx-auto">
423
+ Description of content type listing
424
+ </p>
425
+ </div>
426
+
427
+ {[contentType]s.length > 0 ? (
428
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
429
+ {[contentType]s.map(([contentType]) => (
430
+ <[ContentType]Card key={[contentType].id} [contentType]={[contentType]} />
431
+ ))}
432
+ </div>
433
+ ) : (
434
+ <div className="text-center py-16">
435
+ <p className="text-gray-600">No [content types] available.</p>
436
+ </div>
437
+ )}
438
+ </main>
439
+ </div>
440
+ )
441
+ } catch (error) {
442
+ console.error('Error loading [content types]:', error)
443
+ // Return error state
444
+ }
445
+ }
446
+ ```
447
+
448
+ #### Update Dynamic Routing
449
+
450
+ **Add to `app/[...slug]/page.tsx`:**
451
+ ```typescript
452
+ // In generateMetadata and page functions, add:
453
+ ... on Node[ContentType] {
454
+ id
455
+ title
456
+ // Add fields needed for display
457
+ }
458
+
459
+ // In the page component return:
460
+ if (node.__typename === 'Node[ContentType]') {
461
+ return <[ContentType]Renderer [contentType]={node as Drupal[ContentType]} />
462
+ }
463
+ ```
464
+
465
+ #### Update Navigation
466
+
467
+ **Add to `app/components/Header.tsx`:**
468
+ ```typescript
469
+ // Add navigation link
470
+ <Link
471
+ href="/[content-type]"
472
+ className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
473
+ pathname === '/[content-type]' ? 'bg-blue-100 text-blue-700' : 'text-gray-700 hover:text-blue-600'
474
+ }`}
475
+ >
476
+ [Content Types]
477
+ </Link>
478
+ ```
479
+
480
+ ### 5. Build and Test Process
481
+
482
+ **Always run these commands in sequence:**
483
+
484
+ 1. **Build the application:**
485
+ ```bash
486
+ npm run build
487
+ ```
488
+
489
+ 2. **Fix any TypeScript/build errors** that appear
490
+
491
+ 3. **Start development server:**
492
+ ```bash
493
+ npm run dev
494
+ ```
495
+
496
+ 4. **Test endpoints:**
497
+ - Listing page: `http://localhost:3001/[content-type]`
498
+ - Detail pages: `http://localhost:3001/[content-type]/[slug]`
499
+
500
+ ### 6. Testing Checklist
501
+
502
+ **Verify each step:**
503
+ - [ ] DCloud import successful with no errors
504
+ - [ ] GraphQL schema includes new content type
505
+ - [ ] TypeScript types are properly defined
506
+ - [ ] Build process completes without errors
507
+ - [ ] Listing page displays content
508
+ - [ ] Detail pages render correctly
509
+ - [ ] Navigation links work
510
+ - [ ] Responsive design functions
511
+ - [ ] Error states handled gracefully
512
+
513
+ ## Common Content Type Patterns
514
+
515
+ ### E-commerce Product
516
+ ```json
517
+ {
518
+ "id": "price",
519
+ "label": "Price",
520
+ "type": "string"
521
+ },
522
+ {
523
+ "id": "product_images",
524
+ "label": "Product Images",
525
+ "type": "image[]"
526
+ },
527
+ {
528
+ "id": "in_stock",
529
+ "label": "In Stock",
530
+ "type": "bool"
531
+ },
532
+ {
533
+ "id": "features",
534
+ "label": "Key Features",
535
+ "type": "string[]"
536
+ }
537
+ ```
538
+
539
+ **Sample content with image URI:**
540
+ ```json
541
+ "product_images": [
542
+ {
543
+ "uri": "${DRUPAL_BASE_URL}/modules/custom/dcloud_import/resources/placeholder.png",
544
+ "alt": "Product showcase image",
545
+ "title": "Product Image"
546
+ }
547
+ ]
548
+ ```
549
+
550
+ ### Event/Conference
551
+ ```json
552
+ {
553
+ "id": "event_date",
554
+ "label": "Event Date",
555
+ "type": "datetime"
556
+ },
557
+ {
558
+ "id": "location",
559
+ "label": "Location",
560
+ "type": "string"
561
+ },
562
+ {
563
+ "id": "speakers",
564
+ "label": "Speakers",
565
+ "type": "string[]"
566
+ }
567
+ ```
568
+
569
+ ### Team Member
570
+ ```json
571
+ {
572
+ "id": "position",
573
+ "label": "Position",
574
+ "type": "string"
575
+ },
576
+ {
577
+ "id": "profile_image",
578
+ "label": "Profile Image",
579
+ "type": "image"
580
+ },
581
+ {
582
+ "id": "bio",
583
+ "label": "Biography",
584
+ "type": "text"
585
+ }
586
+ ```
587
+
588
+ **Sample content with image URI:**
589
+ ```json
590
+ "profile_image": {
591
+ "uri": "${DRUPAL_BASE_URL}/modules/custom/dcloud_import/resources/placeholder.png",
592
+ "alt": "Team member headshot",
593
+ "title": "Profile Photo"
594
+ }
595
+ ```
596
+
597
+ ### Portfolio/Case Study
598
+ ```json
599
+ {
600
+ "id": "project_url",
601
+ "label": "Project URL",
602
+ "type": "string"
603
+ },
604
+ {
605
+ "id": "technologies",
606
+ "label": "Technologies Used",
607
+ "type": "string[]"
608
+ },
609
+ {
610
+ "id": "project_images",
611
+ "label": "Project Images",
612
+ "type": "image[]"
613
+ }
614
+ ```
615
+
616
+ **Sample content with image URIs:**
617
+ ```json
618
+ "project_images": [
619
+ {
620
+ "uri": "${DRUPAL_BASE_URL}/modules/custom/dcloud_import/resources/placeholder.png",
621
+ "alt": "Project screenshot showing main interface",
622
+ "title": "Project Interface"
623
+ }
624
+ ]
625
+ ```
626
+
627
+ ## Troubleshooting
628
+
629
+ ### Common Issues
630
+
631
+ **1. DCloud Import Fails**
632
+ - Check OAuth token expiration
633
+ - Verify JSON structure matches `schema/sample.json` format
634
+ - Ensure field IDs don't start with `field_`
635
+ - **Critical**: In content values, use field ID without "field_" prefix (e.g., `"price": "$299.99"` not `"field_price": "$299.99"`)
636
+
637
+ **2. GraphQL Errors**
638
+ - Check if content type was created successfully in Drupal
639
+ - Verify GraphQL Compose configuration
640
+ - Clear GraphQL cache in Drupal admin
641
+
642
+ **3. Build Errors**
643
+ - Check TypeScript type definitions
644
+ - Ensure all imports are correct
645
+ - Verify GraphQL query syntax
646
+
647
+ **4. Content Not Displaying**
648
+ - Check GraphQL query field names match Drupal fields (may not match DCloud field IDs)
649
+ - Verify content was created and published
650
+ - Check query variables and pagination
651
+ - For HTML content showing raw tags, use `dangerouslySetInnerHTML={{ __html: field.processed }}`
652
+
653
+ **5. GraphQL Schema Not Updated After DCloud Import**
654
+ - **Critical Issue**: DCloud imports create content types successfully but GraphQL schema may not update immediately
655
+ - Content exists in Drupal but `nodeProducts` query returns "field not found" errors
656
+ - This is the most common issue with DCloud imports
657
+
658
+ **Solution Process**:
659
+ 1. Clear Drupal caches (if you have admin access)
660
+ 2. **Always run schema generation script**: `npm run generate-schema`
661
+ 3. This regenerates the GraphQL schema files and validates the schema is updated
662
+ 4. Test queries again after schema regeneration
663
+
664
+ ### Essential Schema Generation Workflow
665
+
666
+ **CRITICAL**: Always run schema generation after DCloud imports to ensure GraphQL integration works properly.
667
+
668
+ ```bash
669
+ # Generate updated GraphQL schema after content type imports
670
+ npm run generate-schema
671
+ ```
672
+
673
+ This command:
674
+ - Authenticates with Drupal using OAuth
675
+ - Performs GraphQL introspection to get the current schema
676
+ - Generates updated schema files in `/schema/` directory
677
+ - Validates that new content types are available in GraphQL
678
+
679
+ **Add this to your workflow**:
680
+ 1. Import content type via DCloud API
681
+ 2. **Immediately run**: `npm run generate-schema`
682
+ 3. Check generated schema includes your new content type
683
+ 4. Test GraphQL queries
684
+ 5. Proceed with frontend implementation
685
+
686
+ ### Debug Commands
687
+
688
+ ```bash
689
+ # Check CLI authentication status
690
+ npx dcloud-cli auth status
691
+
692
+ # OAUTH/DRUPAL COMMANDS:
693
+ # Check content import API status
694
+ npx dcloud-cli content status
695
+
696
+ # Preview content import without applying changes
697
+ npx dcloud-cli content import --file your-import.json --preview
698
+
699
+ # Import content to Drupal
700
+ npx dcloud-cli content import --file your-import.json
701
+
702
+ # Generate fresh schema (most important after any import)
703
+ npm run generate-schema
704
+
705
+ # PAT/PLATFORM COMMANDS (require npx dcloud-cli auth login):
706
+ # List available spaces
707
+ npx dcloud-cli spaces list
708
+
709
+ # Check current default space
710
+ npx dcloud-cli spaces current
711
+
712
+ # Get health status of DCloud platform
713
+ npx dcloud-cli health check
714
+
715
+ # Check organization info
716
+ npx dcloud-cli org info
717
+
718
+ # Legacy platform content import
719
+ npx dcloud-cli spaces content-import --file your-import.json --preview
720
+ npx dcloud-cli spaces content-import --file your-import.json --json
721
+ ```
722
+
723
+ ## Content Import Command Reference
724
+
725
+ ### OAuth + Drupal API Commands
726
+
727
+ **Check content import API status:**
728
+ ```bash
729
+ npx dcloud-cli content status
730
+ ```
731
+
732
+ **Generate example import JSON:**
733
+ ```bash
734
+ npx dcloud-cli content import --example > my-content-type.json
735
+ ```
736
+
737
+ **Preview import (dry run):**
738
+ ```bash
739
+ npx dcloud-cli content import --file my-content-type.json --preview
740
+ ```
741
+
742
+ **Import content to Drupal:**
743
+ ```bash
744
+ npx dcloud-cli content import --file my-content-type.json
745
+ ```
746
+
747
+ ### Authentication Setup for Content Import
748
+
749
+ **Quick OAuth Setup (recommended for local development):**
750
+ ```bash
751
+ # 1. Ensure .env.local has OAuth credentials
752
+ # 2. Set up OAuth profile
753
+ npx dcloud-cli auth oauth
754
+
755
+ # 3. Verify it's working
756
+ npx dcloud-cli content status
757
+
758
+ # 4. Import content
759
+ npx dcloud-cli content import --file your-content.json
760
+ ```
761
+
762
+ **When to use OAuth vs PAT:**
763
+ - **OAuth**: Local development, direct Drupal operations, content import
764
+ - **PAT**: Production deployments, platform management, space operations
765
+
766
+ ## Best Practices
767
+
768
+ 1. **Always create sample content** during import for immediate testing
769
+ 2. **Use descriptive field names** that are clear and consistent
770
+ 3. **Follow existing naming conventions** in the codebase
771
+ 4. **Test responsive design** on different screen sizes
772
+ 5. **Handle empty/missing data gracefully** in components
773
+ 6. **Use semantic HTML** for accessibility
774
+ 7. **Include proper TypeScript types** for all data structures
775
+ 8. **Test the full user journey** from listing to detail pages
776
+ 9. **Use `dangerouslySetInnerHTML`** for processed HTML content from Drupal to avoid raw tag display
777
+ 10. **Verify GraphQL field names** match actual schema, not DCloud field IDs
778
+
779
+ ## Success Criteria
780
+
781
+ A successful end-to-end implementation should:
782
+ - [ ] Build without errors
783
+ - [ ] Display content correctly on listing and detail pages
784
+ - [ ] Handle navigation between pages
785
+ - [ ] Render responsively on all device sizes
786
+ - [ ] Show appropriate fallbacks for missing data
787
+ - [ ] Follow the established design patterns
788
+ - [ ] Include proper metadata and SEO elements
789
+ - [ ] Work with the existing authentication and routing system
790
+
791
+ ## Key Learnings and Common Mistakes
792
+
793
+ Based on successful product catalog implementation, here are critical learnings to avoid common pitfalls:
794
+
795
+ ### DCloud Import Format Issues
796
+
797
+ **Problem**: Field values incorrectly formatted with "field_" prefix
798
+ ```json
799
+ // WRONG - This causes import failures
800
+ "values": {
801
+ "field_price": "$299.99",
802
+ "field_in_stock": true
803
+ }
804
+
805
+ // CORRECT - Use field ID directly
806
+ "values": {
807
+ "price": "$299.99",
808
+ "in_stock": true
809
+ }
810
+ ```
811
+
812
+ **Solution**: Always use the field `id` value directly in content values, never add "field_" prefix.
813
+
814
+ ### GraphQL Field Name Mapping
815
+
816
+ **Problem**: Assuming GraphQL field names match DCloud field IDs
817
+ ```typescript
818
+ // WRONG - Field names may be transformed
819
+ price: string // DCloud field ID
820
+ fieldPrice: string // What you might expect in GraphQL
821
+
822
+ // CORRECT - Check actual schema
823
+ price: string // Actual GraphQL field name (can match DCloud ID)
824
+ inStock: boolean // camelCase transformation
825
+ productImages: object // snake_case to camelCase
826
+ ```
827
+
828
+ **Solution**: Always verify GraphQL field names by checking the actual schema or API response before writing queries.
829
+
830
+ ### GraphQL Field Value Structure Discovery
831
+
832
+ **Critical Learning**: GraphQL field values from DCloud imports return as simple types, not objects:
833
+
834
+ ```typescript
835
+ // WRONG - Assuming field values are objects with .processed
836
+ fieldPrice?: {
837
+ processed: string
838
+ }
839
+ fieldFeatures?: Array<{
840
+ processed: string
841
+ }>
842
+
843
+ // CORRECT - DCloud imports create simple value fields
844
+ price?: string
845
+ features?: string[]
846
+ ```
847
+
848
+ **Discovery Process**: Check GraphQL schema to understand actual field structure:
849
+ ```bash
850
+ # Generate fresh schema and check field structure
851
+ npm run generate-schema
852
+ grep -i nodeProducts schema/schema.graphql
853
+ # Check the actual schema files for field names and types
854
+ ```
855
+
856
+ **Solution**: Always check the generated GraphQL schema first to understand the actual field structure before writing TypeScript types and components.
857
+
858
+ ### HTML Content Rendering
859
+
860
+ **Problem**: Drupal processed HTML showing as raw tags in frontend
861
+ ```jsx
862
+ // WRONG - Shows: "Key Features <p>Fast Wireless Charging</p> <p>Qi-Compatible</p>"
863
+ <span>{feature.processed}</span>
864
+
865
+ // CORRECT - Renders HTML properly
866
+ <span dangerouslySetInnerHTML={{ __html: feature.processed }} />
867
+ ```
868
+
869
+ **Better Solution**: Use `string[]` instead of `text[]` for simple lists to avoid HTML altogether:
870
+ ```jsx
871
+ // BEST - Clean, simple, no HTML rendering needed
872
+ <span>{feature}</span> // Just plain text: "Fast Wireless Charging"
873
+ ```
874
+
875
+ **Solution**: Use `dangerouslySetInnerHTML` only when necessary for rich `text[]` fields. Prefer `string[]` for simple bullet lists.
876
+
877
+ ### Field Type Selection
878
+
879
+ **Best Practice**: Choose field types based on content structure:
880
+ - `string[]` for bullet points, features, specifications, tags, categories (clean plain text, no HTML rendering issues)
881
+ - `text[]` only when you need rich formatting within each item (rare - avoid unless necessary)
882
+ - `text` for rich content descriptions that need HTML formatting
883
+ - `string` for simple values like price, SKU, names (plain text, max 255 chars)
884
+
885
+ **Recommendation**: For repeated items like product features, use `string[]` instead of `text[]` to avoid HTML rendering complexity and security concerns.
886
+
887
+ ### Navigation Menu Integration Pattern
888
+
889
+ **Essential Step**: Always update the main navigation when creating new content types:
890
+
891
+ ```typescript
892
+ // Update navigationItems array in app/components/Header.tsx
893
+ const navigationItems = [
894
+ { name: 'Home', href: '/' },
895
+ { name: 'Products', href: '/products' }, // Add new content type
896
+ { name: 'Articles', href: '/articles' },
897
+ { name: 'About', href: '/about' }
898
+ ]
899
+
900
+ // Update active tab detection to include content type routes
901
+ const getActiveTab = () => {
902
+ if (pathname === '/') return 'Home'
903
+ if (pathname === '/products' || pathname.startsWith('/products/')) return 'Products'
904
+ if (pathname === '/articles' || pathname.startsWith('/articles/')) return 'Articles'
905
+ if (pathname === '/about') return 'About'
906
+ return null
907
+ }
908
+ ```
909
+
910
+ **Pattern**: Use `.startsWith()` for active state detection to highlight navigation for both listing and detail pages.
911
+
912
+ ### Component Architecture Best Practices
913
+
914
+ **Proven Pattern**: Create two components per content type:
915
+ 1. **`[ContentType]Card.tsx`** - For listing views with preview information
916
+ 2. **`[ContentType]Renderer.tsx`** - For detail pages with complete information
917
+
918
+ **Component Features**:
919
+ - **Cards**: Preview data, stock/status indicators, truncated feature lists, call-to-action links
920
+ - **Renderers**: Full data display, sticky sidebars, responsive grids, structured information hierarchy
921
+
922
+ **File Organization**:
923
+ ```
924
+ app/
925
+ ├── components/
926
+ │ ├── ProductCard.tsx # Reusable card for listings
927
+ │ ├── ProductRenderer.tsx # Full page renderer for details
928
+ │ └── Header.tsx # Updated with new navigation
929
+ ├── products/
930
+ │ └── page.tsx # Listing page using ProductCard
931
+ └── [...slug]/
932
+ └── page.tsx # Updated to handle new content type routing
933
+ ```
934
+
935
+ ### Build Process Validation
936
+
937
+ **Critical**: Always run build process after major changes to catch TypeScript and import errors:
938
+
939
+ ```bash
940
+ npm run build # Must complete without errors
941
+ npm run dev # Test in development mode
942
+ ```
943
+
944
+ **Testing Checklist**:
945
+ - [ ] Build completes successfully
946
+ - [ ] Listing page loads and displays content
947
+ - [ ] Detail pages render via dynamic routing
948
+ - [ ] Navigation highlights correctly
949
+ - [ ] Mobile responsive design works
950
+ - [ ] Error states display appropriately
951
+
952
+ ### GraphQL Schema Generation Workflow (CRITICAL LEARNING)
953
+
954
+ The most important learning from the product catalog implementation is the **mandatory schema generation step**:
955
+
956
+ **Problem**: DCloud imports successfully create content types and content, but GraphQL schema doesn't update automatically.
957
+ **Symptoms**:
958
+ - `nodeProducts` query returns "field not found" errors
959
+ - Content exists in Drupal but not accessible via GraphQL
960
+ - Route queries work but content type queries fail
961
+
962
+ **Solution**: **ALWAYS run schema generation immediately after DCloud imports**:
963
+
964
+ ```bash
965
+ # After any DCloud import, immediately run:
966
+ npm run generate-schema
967
+
968
+ # This step is MANDATORY for GraphQL integration to work
969
+ ```
970
+
971
+ **Why this is critical**:
972
+ - DCloud API creates Drupal content types but doesn't trigger GraphQL schema rebuilds
973
+ - The `generate-schema` script performs fresh introspection and updates local schema files
974
+ - Without this step, frontend development will fail with "type not found" errors
975
+ - This step bridges the gap between Drupal content type creation and Next.js GraphQL integration
976
+
977
+ **Workflow Integration**:
978
+ 1. Import via DCloud API ✅
979
+ 2. **Run `npm run generate-schema`** ✅ ← CRITICAL STEP
980
+ 3. Verify schema includes new content type ✅
981
+ 4. Proceed with frontend development ✅
982
+
983
+ This learning transforms the development workflow from "sometimes works" to "reliably works every time."
984
+
985
+ ### GraphQL Schema Field Name Discovery
986
+
987
+ **Critical Process**: Always verify actual GraphQL field names after DCloud import - they may differ from field IDs used in import JSON.
988
+
989
+ **Discovery Method**:
990
+ ```bash
991
+ # Check generated schema to understand actual field structure
992
+ npm run generate-schema
993
+ grep -A 10 -B 5 nodeProducts schema/schema.graphql
994
+ # This shows the actual field names and types available
995
+ ```
996
+
997
+ **Key Learning**: DCloud field IDs may transform in GraphQL schema:
998
+ - `in_stock` becomes `inStock` (camelCase)
999
+ - `product_images` becomes `productImages` (camelCase)
1000
+ - Simple field types work reliably (string, boolean, string[])
1001
+
1002
+ ### Component Architecture Patterns
1003
+
1004
+ **Proven Two-Component Pattern**:
1005
+ 1. **`[ContentType]Card.tsx`** - Listing view component
1006
+ - Preview information only
1007
+ - Truncated content (features.slice(0, 3))
1008
+ - Call-to-action buttons
1009
+ - Stock/status indicators
1010
+
1011
+ 2. **`[ContentType]Renderer.tsx`** - Detail page component
1012
+ - Complete data display
1013
+ - Full feature/specification lists
1014
+ - Image galleries
1015
+ - Action buttons and forms
1016
+
1017
+ **Navigation Integration Pattern**:
1018
+ ```typescript
1019
+ // Always update navigationItems array
1020
+ const navigationItems = [
1021
+ { name: 'Home', href: '/' },
1022
+ { name: 'Products', href: '/products' }, // Add new content type
1023
+ // ...
1024
+ ]
1025
+
1026
+ // Use .startsWith() for active state detection
1027
+ const getActiveTab = () => {
1028
+ if (pathname === '/products' || pathname.startsWith('/products/')) return 'Products'
1029
+ // ...
1030
+ }
1031
+ ```
1032
+
1033
+ ### Field Type Best Practices (Revised)
1034
+
1035
+ Based on successful product catalog implementation:
1036
+
1037
+ **Reliable Field Types**:
1038
+ - `string` - Simple text values (price, SKU, category)
1039
+ - `string[]` - Multiple simple values (features, specifications)
1040
+ - `bool` - Boolean flags (in_stock, featured)
1041
+ - `text` - Rich HTML content (body, descriptions)
1042
+ - `image[]` - Multiple image uploads
1043
+ - `image` - Single image upload
1044
+
1045
+ **Recommendation**: Use `string[]` for lists (features, specifications) instead of `text[]` to avoid HTML rendering complexity.
1046
+
1047
+ ### Build Process Integration
1048
+
1049
+ **Essential Commands Sequence**:
1050
+ ```bash
1051
+ # After DCloud import, always run:
1052
+ npm run generate-schema # Updates GraphQL schema
1053
+ npm run build # Validates TypeScript and builds
1054
+ npm run dev # Test in development
1055
+ ```
1056
+
1057
+ **Success Criteria**:
1058
+ - ✅ Build completes without TypeScript errors
1059
+ - ✅ Listing page loads with HTTP 200
1060
+ - ✅ Detail pages load with proper titles
1061
+ - ✅ Navigation highlighting works correctly
1062
+
1063
+ ### Deployment Field Mapping Reference
1064
+
1065
+ **DCloud Import → GraphQL Schema Mapping**:
1066
+ ```json
1067
+ // DCloud import format:
1068
+ {
1069
+ "in_stock": true, // → GraphQL: inStock
1070
+ "product_images": [...], // → GraphQL: productImages
1071
+ "features": ["A", "B"], // → GraphQL: features
1072
+ "specifications": ["X"] // → GraphQL: specifications
1073
+ }
1074
+ ```
1075
+
1076
+ **Always verify field names in generated schema before writing TypeScript types.**
1077
+
1078
+ ## Summary
1079
+
1080
+ This comprehensive guide enables "one-shot" prompts like "create a product catalog" to result in complete, working implementations from backend to frontend, with known limitations documented for efficient troubleshooting.