Sprint 1 B4: Admin Page with Jobs List & Create Form#25
Conversation
Sprint 1 B4 (Admin Page) - Complete implementation demonstrating framework in WordPress admin context. ## What's New ### Admin UI Components - **JobsList Component**: Full CRUD interface with loading/empty/error states - Create New Job form with title, department, location, status fields - All Jobs table with status badges and date formatting - Responsive WordPress admin styling using @wordpress/components ### Framework Integration - **Resource → Store → React**: Complete data flow demonstration - Job resource auto-generates @wordpress/data store - React components use useSelect() for store integration - Store registration via createReduxStore pattern ### PHP Bootstrap (Sprint 5 will replace with mountAdmin API) - Minimal PHP for menu registration and Script Module enqueue - Enqueues WordPress packages (React, wp-element, wp-components, wp-data, wp-i18n) - Renders mount point div for React application ### Build Configuration - Single bundle approach with conditional admin loading - TypeScript + JSX transform via @types/react - WordPress Script Modules for native ESM loading ## Implementation Details ### Resource Client (CRUD Complete) - Implemented create/update/remove transport methods - All methods call transportFetch with proper paths and data - DELETE returns void, POST/PUT return response data ### Store Registration - Uses wp.data.createReduxStore for proper store descriptor - Lazy initialization on first store access - Auto-registers with @wordpress/data when available ### Component Architecture - Static imports for bundled admin code - Conditional mounting via getElementById check - Error boundaries via try/catch for mount failures ## Testing - ✅ 275/275 tests passing - ✅ Fixed create/update/remove method tests with proper mocking - ✅ Updated store registration test for createReduxStore pattern - ✅ Manual testing: admin page renders successfully with form and table ## Files Changed - app/showcase/src/admin/index.tsx - Admin mount point - app/showcase/src/admin/pages/JobsList.tsx - Jobs CRUD UI - app/showcase/src/index.ts - Main entry with store initialization - app/showcase/showcase-plugin.php - PHP bootstrap (v0.3.0) - packages/kernel/src/resource/defineResource.ts - Store registration via createReduxStore - packages/kernel/src/resource/__tests__/defineResource.test.ts - Updated tests ## Next Steps - Sprint 1 B5: REST endpoint implementation (replace 501 stubs) - Sprint 1 C1-C4: Testing phase (unit, integration, E2E, CI) Co-authored-by: GitHub Copilot <[email protected]>
There was a problem hiding this comment.
Pull Request Overview
This PR implements Sprint 1 B4, completing the admin page with a full CRUD interface for jobs. The implementation shifts from TODO placeholders to functional transport methods and introduces a React-based admin interface.
- Completes the
defineResourcetransport methods (create, update, remove) by replacing NotImplementedError stubs with actual@wordpress/api-fetchcalls - Adds a React admin page with jobs list display and create form using
@wordpress/components - Updates store registration to use the proper
createReduxStorepattern for WordPress data integration
Reviewed Changes
Copilot reviewed 11 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/kernel/src/resource/defineResource.ts | Implements actual transport calls for create/update/remove methods and fixes store registration |
| packages/kernel/src/resource/tests/defineResource.test.ts | Updates tests to mock actual transport calls instead of expecting NotImplementedError |
| app/showcase/src/admin/pages/JobsList.tsx | New React component providing jobs list view and create form |
| app/showcase/src/admin/index.tsx | Admin mounting logic for React app |
| app/showcase/src/index.ts | Forces store registration and adds admin mounting logic |
| app/showcase/showcase-plugin.php | Updates PHP bootstrap to v0.3.0 with admin menu and dependency management |
| package.json | Adds React type dependencies |
| app/showcase/webpack.config.cjs | New webpack config for WordPress externals |
| app/showcase/tsconfig.json | Adds React JSX configuration |
| app/showcase/package.json | Adds WordPress package dependencies and updates build scripts |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
| client.create = async (data: Partial<T>): Promise<T> => { | ||
| const response = await transportFetch<T>({ | ||
| path: config.routes.create!.path, | ||
| method: 'POST', | ||
| data, | ||
| }); | ||
|
|
||
| return response.data; |
There was a problem hiding this comment.
The response object structure assumes a data property, but transportFetch may return the data directly. This could cause runtime errors if the API response format differs from expectations.
| method: config.routes.update!.method as 'PUT' | 'PATCH', | ||
| data, | ||
| }); | ||
|
|
There was a problem hiding this comment.
Same issue as create method - assumes response.data structure without verification that transportFetch returns an object with a data property.
| if (!response || typeof response !== 'object' || !('data' in response)) { | |
| throw new KernelError('Malformed response: missing data property'); | |
| } |
| } from '@wordpress/components'; | ||
| import { __ } from '@wordpress/i18n'; | ||
| import { job } from '../../resources/job'; | ||
| import type { Job } from '../../../types/job.js'; |
There was a problem hiding this comment.
The import path '../../../types/job.js' appears to reference a file outside the current directory structure. This will likely cause a module resolution error.
| import type { Job } from '../../../types/job.js'; | |
| import type { Job } from '../../../types/job'; |
| const { jobsResponse, isLoading, error } = useSelect((select) => { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| const store = select(job.storeKey) as any; |
There was a problem hiding this comment.
Using any type defeats TypeScript's type safety. Consider defining proper types for the store selectors or using a more specific type annotation.
| const { jobsResponse, isLoading, error } = useSelect((select) => { | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | |
| const store = select(job.storeKey) as any; | |
| interface JobStoreSelectors { | |
| getList: () => { items: Job[]; total: number } | undefined; | |
| isResolving: (selectorName: string, args: unknown[]) => boolean; | |
| getResolutionError: (selectorName: string, args: unknown[]) => unknown; | |
| } | |
| const { jobsResponse, isLoading, error } = useSelect((select) => { | |
| const store = select(job.storeKey) as JobStoreSelectors; |
| }) | ||
| } | ||
| disabled={isCreating} | ||
| />{' '} |
There was a problem hiding this comment.
[nitpick] Extra space character after the JSX closing tag is unnecessary and could be removed for cleaner code.
| />{' '} | |
| /> |
Sprint 1 B4: Admin Page with Jobs List & Create Form
Summary
Implements Sprint 1 B4: Admin page mounted under Tools menu with list view and create form.
Closes #26
Implementation Details
New Components:
Transport Methods Completed:
Store Registration:
PHP Bootstrap:
Testing Status
Updated Tests:
Definition of Done
Manual Testing
Next Steps
Screenshots
Sprint 1 Progress: 11/14 tasks complete (79%)