diff --git a/src/App.new.tsx b/src/App.new.tsx
index 7719cdc..7b53bf7 100644
--- a/src/App.new.tsx
+++ b/src/App.new.tsx
@@ -176,6 +176,7 @@ function App() {
storybookStories={storybookStories}
unitTests={unitTests}
flaskConfig={flaskConfig}
+ nextjsConfig={nextjsConfig}
/>
diff --git a/src/App.refactored.tsx b/src/App.refactored.tsx
index d6178e5..0735ddf 100644
--- a/src/App.refactored.tsx
+++ b/src/App.refactored.tsx
@@ -116,7 +116,7 @@ function App() {
-
+
{featureToggles.codeEditor && (
diff --git a/src/components/ProjectDashboard.tsx b/src/components/ProjectDashboard.tsx
index 96163ed..36ac56e 100644
--- a/src/components/ProjectDashboard.tsx
+++ b/src/components/ProjectDashboard.tsx
@@ -13,8 +13,9 @@ import {
CheckCircle,
Warning
} from '@phosphor-icons/react'
-import { ProjectFile, PrismaModel, ComponentNode, ThemeConfig, PlaywrightTest, StorybookStory, UnitTest, FlaskConfig } from '@/types/project'
+import { ProjectFile, PrismaModel, ComponentNode, ThemeConfig, PlaywrightTest, StorybookStory, UnitTest, FlaskConfig, NextJsConfig } from '@/types/project'
import { SeedDataStatus } from '@/components/atoms'
+import { GitHubBuildStatus } from '@/components/molecules/GitHubBuildStatus'
interface ProjectDashboardProps {
files: ProjectFile[]
@@ -25,6 +26,7 @@ interface ProjectDashboardProps {
storybookStories: StorybookStory[]
unitTests: UnitTest[]
flaskConfig: FlaskConfig
+ nextjsConfig: NextJsConfig
}
export function ProjectDashboard({
@@ -36,6 +38,7 @@ export function ProjectDashboard({
storybookStories,
unitTests,
flaskConfig,
+ nextjsConfig,
}: ProjectDashboardProps) {
const totalFiles = files.length
const totalModels = models.length
@@ -134,6 +137,13 @@ export function ProjectDashboard({
+ {nextjsConfig?.githubRepo && (
+
+ )}
+
Project Details
diff --git a/src/components/molecules/GitHubBuildStatus.tsx b/src/components/molecules/GitHubBuildStatus.tsx
new file mode 100644
index 0000000..583f749
--- /dev/null
+++ b/src/components/molecules/GitHubBuildStatus.tsx
@@ -0,0 +1,271 @@
+import { useState, useEffect } from 'react'
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
+import { Button } from '@/components/ui/button'
+import {
+ GitBranch,
+ CheckCircle,
+ XCircle,
+ Clock,
+ ArrowSquareOut,
+ Warning
+} from '@phosphor-icons/react'
+import { Skeleton } from '@/components/ui/skeleton'
+
+interface WorkflowRun {
+ id: number
+ name: string
+ status: string
+ conclusion: string | null
+ created_at: string
+ updated_at: string
+ html_url: string
+ head_branch: string
+ head_sha: string
+ event: string
+}
+
+interface GitHubBuildStatusProps {
+ owner: string
+ repo: string
+}
+
+export function GitHubBuildStatus({ owner, repo }: GitHubBuildStatusProps) {
+ const [workflows, setWorkflows] = useState([])
+ const [loading, setLoading] = useState(true)
+ const [error, setError] = useState(null)
+
+ useEffect(() => {
+ fetchWorkflowRuns()
+ }, [owner, repo])
+
+ const fetchWorkflowRuns = async () => {
+ try {
+ setLoading(true)
+ setError(null)
+
+ const response = await fetch(
+ `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=5`,
+ {
+ headers: {
+ 'Accept': 'application/vnd.github.v3+json',
+ },
+ }
+ )
+
+ if (!response.ok) {
+ throw new Error(`GitHub API error: ${response.status}`)
+ }
+
+ const data = await response.json()
+ setWorkflows(data.workflow_runs || [])
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to fetch workflows')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const getStatusIcon = (status: string, conclusion: string | null) => {
+ if (status === 'completed') {
+ if (conclusion === 'success') {
+ return
+ }
+ if (conclusion === 'failure') {
+ return
+ }
+ if (conclusion === 'cancelled') {
+ return
+ }
+ }
+ return
+ }
+
+ const getStatusBadge = (status: string, conclusion: string | null) => {
+ if (status === 'completed') {
+ if (conclusion === 'success') {
+ return Success
+ }
+ if (conclusion === 'failure') {
+ return Failed
+ }
+ if (conclusion === 'cancelled') {
+ return Cancelled
+ }
+ }
+ return Running
+ }
+
+ const formatTime = (dateString: string) => {
+ const date = new Date(dateString)
+ const now = new Date()
+ const diff = now.getTime() - date.getTime()
+ const minutes = Math.floor(diff / 60000)
+ const hours = Math.floor(minutes / 60)
+ const days = Math.floor(hours / 24)
+
+ if (days > 0) return `${days}d ago`
+ if (hours > 0) return `${hours}h ago`
+ if (minutes > 0) return `${minutes}m ago`
+ return 'just now'
+ }
+
+ if (loading) {
+ return (
+
+
+
+
+ GitHub Actions
+
+ Recent workflow runs
+
+
+ {[...Array(3)].map((_, i) => (
+
+ ))}
+
+
+ )
+ }
+
+ if (error) {
+ return (
+
+
+
+
+ GitHub Actions
+
+ Unable to fetch workflow status
+
+
+
+
+
+
{error}
+
+
+
+
+
+ )
+ }
+
+ if (workflows.length === 0) {
+ return (
+
+
+
+
+ GitHub Actions
+
+ No workflow runs found
+
+
+
+ No GitHub Actions workflows have been run yet.
+
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+ GitHub Actions
+
+ Recent workflow runs from {owner}/{repo}
+
+
+
+
+
+ {workflows.map((workflow) => (
+
+
+ {getStatusIcon(workflow.status, workflow.conclusion)}
+
+
+
+ {workflow.name}
+
+ {getStatusBadge(workflow.status, workflow.conclusion)}
+
+
+ {workflow.head_branch}
+ •
+ {formatTime(workflow.updated_at)}
+ •
+ {workflow.event}
+
+
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/src/components/molecules/index.ts b/src/components/molecules/index.ts
index db910ae..90d77dd 100644
--- a/src/components/molecules/index.ts
+++ b/src/components/molecules/index.ts
@@ -5,6 +5,7 @@ export { EditorToolbar } from './EditorToolbar'
export { EmptyEditorState } from './EmptyEditorState'
export { EmptyState } from './EmptyState'
export { FileTabs } from './FileTabs'
+export { GitHubBuildStatus } from './GitHubBuildStatus'
export { LabelWithBadge } from './LabelWithBadge'
export { LazyInlineMonacoEditor } from './LazyInlineMonacoEditor'
export { LazyMonacoEditor, preloadMonacoEditor } from './LazyMonacoEditor'
diff --git a/src/config/pages.json b/src/config/pages.json
index 7e9e5c2..b719244 100644
--- a/src/config/pages.json
+++ b/src/config/pages.json
@@ -9,7 +9,7 @@
"shortcut": "ctrl+1",
"order": 1,
"props": {
- "state": ["files", "models", "components", "theme", "playwrightTests", "storybookStories", "unitTests", "flaskConfig"]
+ "state": ["files", "models", "components", "theme", "playwrightTests", "storybookStories", "unitTests", "flaskConfig", "nextjsConfig"]
}
},
{
diff --git a/src/hooks/use-project-state.ts b/src/hooks/use-project-state.ts
index b4e35b6..2341f00 100644
--- a/src/hooks/use-project-state.ts
+++ b/src/hooks/use-project-state.ts
@@ -33,6 +33,10 @@ const DEFAULT_NEXTJS_CONFIG: NextJsConfig = {
appRouter: true,
importAlias: '@/*',
turbopack: false,
+ githubRepo: {
+ owner: 'johndoe6345789',
+ repo: 'low-code-react-app-b',
+ },
}
const DEFAULT_NPM_SETTINGS: NpmSettings = {
diff --git a/src/types/project.ts b/src/types/project.ts
index cd41e77..c9db538 100644
--- a/src/types/project.ts
+++ b/src/types/project.ts
@@ -169,6 +169,10 @@ export interface NextJsConfig {
appRouter: boolean
importAlias: string
turbopack?: boolean
+ githubRepo?: {
+ owner: string
+ repo: string
+ }
}
export interface NpmPackage {