diff --git a/.github/workflows/deploy-branch-selector.yml b/.github/workflows/deploy-branch-selector.yml new file mode 100644 index 000000000..79817bfb5 --- /dev/null +++ b/.github/workflows/deploy-branch-selector.yml @@ -0,0 +1,262 @@ +name: Deploy Branch Selector + +on: + pull_request: + branches: + - deploy + types: [opened, synchronize, reopened] + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to deploy (optional - will auto-detect if running from PR)' + required: false + type: string + +permissions: + contents: write + pages: write + id-token: write + pull-requests: write + +concurrency: + group: "deploy-branch-selector-${{ github.event.pull_request.number || github.event.inputs.pr_number || 'manual' }}" + cancel-in-progress: true + +jobs: + deploy-branch-selector: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Get PR information + id: pr_info + shell: bash + run: | + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + PR_NUMBER="${{ github.event.pull_request.number }}" + HEAD_SHA="${{ github.event.pull_request.head.sha }}" + HEAD_REF="${{ github.event.pull_request.head.ref }}" + elif [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ github.event.inputs.pr_number }}" ]]; then + PR_NUMBER="${{ github.event.inputs.pr_number }}" + # We'll need to fetch PR details via API + HEAD_SHA="${{ github.sha }}" + HEAD_REF="${{ github.ref_name }}" + else + echo "ERROR: This workflow should only run on PRs or with PR number input" + exit 1 + fi + + echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "HEAD_SHA=$HEAD_SHA" >> $GITHUB_OUTPUT + echo "HEAD_REF=$HEAD_REF" >> $GITHUB_OUTPUT + + echo "Deploying PR #$PR_NUMBER (SHA: $HEAD_SHA, Ref: $HEAD_REF)" + + - name: Checkout PR head + uses: actions/checkout@v4 + with: + ref: ${{ steps.pr_info.outputs.HEAD_SHA }} + fetch-depth: 0 + + - name: Configure git user + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build branch deployment selector + shell: bash + run: | + set -e + + echo "Building branch deployment selector React app..." + + # Build the React app + npm run build + + # Verify build output exists + if [[ ! -f "build/index.html" ]]; then + echo "ERROR: React build failed - index.html not found in build output" + exit 1 + fi + + echo "✅ Branch deployment selector build completed successfully" + echo "Build contents:" + ls -la build/ + + - name: Deploy to gh-pages root (preserving existing branches) + shell: bash + run: | + set -e + + # Save the React app build + echo "Preserving React app build..." + mkdir -p /tmp/selector-build + cp -a build/. /tmp/selector-build/ + + # Checkout gh-pages branch + if git show-ref --verify --quiet refs/remotes/origin/gh-pages; then + echo "Checking out existing gh-pages branch" + git stash -u -m "Stash before gh-pages checkout" || echo "Nothing to stash" + git clean -fd -e node_modules + git checkout gh-pages + else + echo "Creating new gh-pages branch" + git checkout --orphan gh-pages + git rm -rf . + echo "# GitHub Pages" > README.md + git add README.md + git commit -m "Initial gh-pages branch" + git push origin gh-pages + fi + + # CRITICAL: Only remove files, NEVER remove directories (to preserve branch builds) + echo "Removing only root-level files (preserving all branch directories)..." + + # List what directories exist for safety verification + echo "Existing directories before cleanup:" + ls -la */ 2>/dev/null || echo "No directories found" + + # Remove only specific files that could conflict with deployment selector + rm -f index.html + rm -f manifest.json + rm -f asset-manifest.json + rm -f service-worker.js + rm -f robots.txt + rm -f favicon.ico + rm -f logo*.png + rm -f README.md + rm -f package.json + rm -f package-lock.json + + # Remove static directory only if it exists at root (not in branch subdirs) + if [[ -d "static" ]]; then + echo "Removing root static directory..." + rm -rf static + fi + + # Deploy deployment selector to root + echo "Deploying branch deployment selector to root..." + cp -a /tmp/selector-build/. . + + # Verify existing directories are still there + echo "Directories after deployment:" + ls -la */ 2>/dev/null || echo "No directories found" + + # Clean up temporary build + rm -rf /tmp/selector-build + + echo "✅ Branch deployment selector deployed to gh-pages root while preserving all branch directories" + + - name: Commit and push changes + shell: bash + run: | + set -e + + # Verify we're on gh-pages branch + current_branch=$(git branch --show-current) + if [[ "$current_branch" != "gh-pages" ]]; then + echo "ERROR: Not on gh-pages branch! Current branch: $current_branch" + exit 1 + fi + + # Stage all changes + git add -A + + # Check if there are changes to commit + if git diff --cached --quiet; then + echo "No changes to commit" + else + echo "Committing branch deployment selector..." + + git commit -m "🌐 Deploy branch deployment selector from PR #${{ steps.pr_info.outputs.PR_NUMBER }} + + - Updated deployment selector from deploy branch PR + - Built from commit ${{ steps.pr_info.outputs.HEAD_SHA }} + - Deployed at $(date -u '+%Y-%m-%d %H:%M:%S UTC') + - Preserves all existing feature branch deployments" + + echo "Pushing to gh-pages..." + git pull origin gh-pages --rebase || echo "Pull failed, attempting to push anyway..." + git push origin gh-pages + + echo "✅ Branch deployment selector deployment completed successfully" + fi + + - name: Output deployment info + id: deployment + shell: bash + run: | + deployment_url="https://litlfred.github.io/sgex/" + + echo "🌐 Branch Deployment Selector Summary:" + echo "- Deployment URL: $deployment_url" + echo "- PR Number: #${{ steps.pr_info.outputs.PR_NUMBER }}" + echo "- Source Branch: ${{ steps.pr_info.outputs.HEAD_REF }}" + echo "- Commit: ${{ steps.pr_info.outputs.HEAD_SHA }}" + echo "- Deployed at: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" + + echo "page_url=$deployment_url" >> $GITHUB_OUTPUT + + - name: Comment on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('🌐 Branch Deployment Selector') + ); + + const deploymentUrl = "https://litlfred.github.io/sgex/"; + const commitSha = "${{ steps.pr_info.outputs.HEAD_SHA }}"; + const shortSha = commitSha.substring(0, 7); + + const commentBody = `🌐 **Branch Deployment Selector Deployed** + + The branch deployment selector has been successfully deployed from this PR. + + 🔗 **View Deployment**: ${deploymentUrl} + + 📋 **Deployment Details**: + - **Source**: \`${{ steps.pr_info.outputs.HEAD_REF }}\` branch + - **Commit**: \`${shortSha}\` + - **Deployed**: ${new Date().toISOString().replace('T', ' ').substring(0, 19)} UTC + + â„šī¸ **Note**: This deployment replaces the root page of the site but preserves all existing feature branch deployments in their subdirectories. + + --- + *Deployment will update automatically when new commits are pushed to this PR.*`; + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: commentBody + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: commentBody + }); + } \ No newline at end of file diff --git a/.github/workflows/landing-page-deploy.yml b/.github/workflows/landing-page-deploy.yml index 2b0498bca..f625cbb62 100644 --- a/.github/workflows/landing-page-deploy.yml +++ b/.github/workflows/landing-page-deploy.yml @@ -49,77 +49,33 @@ jobs: - name: Install dependencies run: npm ci - - name: Build self-contained landing page + - name: Build React landing page application shell: bash run: | set -e - echo "Building self-contained landing page..." + echo "Building React landing page application..." - # Check if we have landing page assets from deploy branch - if [[ -f "public/branch-listing.html" ]]; then - echo "Using landing page assets from deploy branch..." - npm run build:landing - else - echo "Deploy branch assets not available, creating basic landing page..." - - # Create a minimal landing page build - mkdir -p build - - cat > build/index.html << 'EOF' - - - - - - SGEX Workbench - - - -
-

🚀 SGEX Workbench

-

WHO SMART Guidelines Exchange

- -
-

🌟 Main Application

-

Launch SGEX Workbench (Main Branch)

-
- -
-

📝 Branch & PR Previews

-

Branch and pull request previews are automatically deployed to subdirectories.

-

Each branch is accessible at /branch-name/

-
- -
-

â„šī¸ About

-

SGEX is an experimental collaborative project developing a workbench of tools to make it easier and faster to develop high fidelity SMART Guidelines Digital Adaptation Kits (DAKs).

-
-
- - -EOF - fi + # Build the React app + npm run build - # Verify index.html exists + # Verify build output exists if [[ ! -f "build/index.html" ]]; then - echo "ERROR: index.html not found in build output" + echo "ERROR: React build failed - index.html not found in build output" exit 1 fi - echo "✅ Landing page build completed" + echo "✅ React landing page build completed successfully" + echo "Build contents:" + ls -la build/ - name: Deploy to gh-pages root (preserving existing branches) shell: bash run: | set -e - # Save the landing page build - echo "Preserving landing page build..." + # Save the React app build + echo "Preserving React app build..." mkdir -p /tmp/landing-build cp -a build/. /tmp/landing-build/ @@ -165,7 +121,7 @@ EOF fi # Deploy landing page to root (this will not affect any subdirectories) - echo "Deploying landing page to root..." + echo "Deploying React app to root..." cp -a /tmp/landing-build/. . # Verify existing directories are still there @@ -175,7 +131,7 @@ EOF # Clean up temporary build rm -rf /tmp/landing-build - echo "✅ Landing page deployed to gh-pages root while preserving all branch directories" + echo "✅ React app deployed to gh-pages root while preserving all branch directories" - name: Commit and push changes shell: bash @@ -207,9 +163,9 @@ EOF trigger_info="Auto-triggered by push to main" fi - git commit -m "🏠 Deploy landing page (${deployment_type}) + git commit -m "🏠 Deploy React landing page (${deployment_type}) - - Updated landing page with self-contained assets from deploy branch + - Updated with React app build from main branch - ${trigger_info} - Deployed at $(date -u '+%Y-%m-%d %H:%M:%S UTC') - Commit: ${{ github.sha }}" @@ -218,7 +174,7 @@ EOF git pull origin gh-pages --rebase || echo "Pull failed, attempting to push anyway..." git push origin gh-pages - echo "✅ Landing page deployment completed successfully" + echo "✅ React landing page deployment completed successfully" fi - name: Output deployment info @@ -235,7 +191,7 @@ EOF trigger_info="Auto-triggered by push to main" fi - echo "🏠 Landing Page Deployment Summary:" + echo "🏠 React Landing Page Deployment Summary:" echo "- Landing Page URL: $landing_url" echo "- Deployment Type: $deployment_type" echo "- $trigger_info" diff --git a/package.json b/package.json index 3ffe2f430..4de05f6ac 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "SGEX Workbench - WHO SMART Guidelines Exchange collaborative editor", "private": true, - "homepage": "/sgex/", + "homepage": "/", "dependencies": { "@octokit/rest": "^22.0.0", "@testing-library/jest-dom": "^6.6.4", diff --git a/src/App.js b/src/App.js index bbc21000e..3a70635b6 100644 --- a/src/App.js +++ b/src/App.js @@ -1,44 +1,20 @@ import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import './i18n'; // Initialize i18n -import WelcomePage from './components/WelcomePage'; -import SelectProfilePage from './components/SelectProfilePage'; -import DAKActionSelection from './components/DAKActionSelection'; -import DAKSelection from './components/DAKSelection'; -import OrganizationSelection from './components/OrganizationSelection'; -import DAKConfiguration from './components/DAKConfiguration'; - -import ComponentEditor from './components/ComponentEditor'; -import CoreDataDictionaryViewer from './components/CoreDataDictionaryViewer'; -import ActorEditor from './components/ActorEditor'; -import BPMNEditor from './components/BPMNEditor'; -import BusinessProcessSelection from './components/BusinessProcessSelection'; -import BPMNViewer from './components/BPMNViewer'; -import BPMNSource from './components/BPMNSource'; -import BPMNViewerTestComponent from './components/BPMNViewerTestComponent'; -import DocumentationViewer from './components/DocumentationViewer'; -import DecisionSupportLogicView from './components/DecisionSupportLogicView'; -import TestDashboard from './components/TestDashboard'; -import TestingViewer from './components/TestingViewer'; -import PagesManager from './components/PagesManager'; +import BranchDeploymentSelector from './components/BranchDeploymentSelector'; import NotFound from './components/NotFound'; -import LandingPageWithFramework from './components/LandingPageWithFramework'; -import DAKDashboardWithFramework from './components/DAKDashboardWithFramework'; -import DashboardRedirect from './components/DashboardRedirect'; -import TestDocumentationPage from './components/TestDocumentationPage'; -import AssetEditorTest from './components/AssetEditorTest'; import logger from './utils/logger'; import './App.css'; function App() { const appLogger = logger.getLogger('App'); - // Get basename from PUBLIC_URL or default to /sgex, but use empty in development if no PUBLIC_URL - const basename = process.env.PUBLIC_URL || (process.env.NODE_ENV === 'development' ? '' : '/sgex'); + // Get basename from PUBLIC_URL or default to empty for deployment selector + const basename = process.env.PUBLIC_URL || ''; React.useEffect(() => { appLogger.componentMount(); - appLogger.info('SGEX Workbench application started', { + appLogger.info('SGEX Branch Deployment Selector started', { environment: process.env.NODE_ENV, basename: basename }); @@ -52,66 +28,7 @@ function App() {
- } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - {/* Framework test routes */} - } /> - } /> - } /> - } /> - } /> - + } /> } />
diff --git a/src/components/BranchDeploymentSelector.css b/src/components/BranchDeploymentSelector.css new file mode 100644 index 000000000..d491b2717 --- /dev/null +++ b/src/components/BranchDeploymentSelector.css @@ -0,0 +1,303 @@ +.deployment-selector-content { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +.deployment-hero { + margin-bottom: 3rem; +} + +.deployment-intro { + display: flex; + align-items: center; + gap: 2rem; + flex-wrap: wrap; +} + +.deployment-mascot img { + width: 120px; + height: 120px; + object-fit: contain; +} + +.deployment-text h1 { + color: var(--primary-color); + margin-bottom: 0.5rem; + font-size: 2.5rem; +} + +.deployment-text h2 { + color: var(--secondary-color); + margin-bottom: 1rem; + font-size: 1.5rem; + font-weight: 400; +} + +.deployment-description { + font-size: 1.1rem; + line-height: 1.6; + color: var(--text-color); + max-width: 600px; +} + +.deployments-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; + margin-bottom: 3rem; +} + +.deployment-card { + background: var(--card-background); + border: 2px solid var(--border-color); + border-radius: 12px; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.deployment-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 4px 20px rgba(0, 123, 255, 0.15); +} + +.deployment-card.main { + border-color: var(--success-color); +} + +.deployment-card.main:hover { + border-color: var(--success-color); + box-shadow: 0 4px 20px rgba(40, 167, 69, 0.15); +} + +.deployment-card.feature { + border-color: var(--warning-color); +} + +.deployment-card.feature:hover { + border-color: var(--warning-color); + box-shadow: 0 4px 20px rgba(255, 193, 7, 0.15); +} + +.deployment-card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.deployment-status { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.status-indicator { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: var(--success-color); +} + +.status-indicator.active { + background-color: var(--success-color); +} + +.status-indicator.inactive { + background-color: var(--secondary-color); +} + +.status-text { + font-size: 0.9rem; + color: var(--text-muted); + text-transform: capitalize; +} + +.deployment-type-badge { + background-color: var(--accent-color); + color: white; + padding: 0.25rem 0.75rem; + border-radius: 16px; + font-size: 0.8rem; + font-weight: 500; +} + +.deployment-card.main .deployment-type-badge { + background-color: var(--success-color); +} + +.deployment-card.feature .deployment-type-badge { + background-color: var(--warning-color); +} + +.deployment-card-content h3 { + color: var(--heading-color); + margin-bottom: 0.5rem; + font-size: 1.3rem; +} + +.deployment-branch { + font-family: 'Courier New', monospace; + background-color: var(--code-background); + color: var(--code-color); + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.9rem; + margin-bottom: 0.75rem; + display: inline-block; +} + +.deployment-description { + color: var(--text-color); + line-height: 1.5; + margin-bottom: 1rem; +} + +.deployment-meta { + margin-bottom: 1.5rem; +} + +.deployment-last-updated { + font-size: 0.9rem; + color: var(--text-muted); + margin-bottom: 0.25rem; +} + +.deployment-url { + font-family: 'Courier New', monospace; + font-size: 0.8rem; + color: var(--text-muted); + background-color: var(--code-background); + padding: 0.25rem 0.5rem; + border-radius: 4px; + display: inline-block; +} + +.deployment-card-footer { + border-top: 1px solid var(--border-color); + padding-top: 1rem; + margin-top: 1rem; +} + +.deployment-launch-btn { + background-color: var(--primary-color); + color: white; + border: none; + padding: 0.75rem 1.5rem; + border-radius: 8px; + cursor: pointer; + font-weight: 500; + transition: background-color 0.3s ease; + width: 100%; +} + +.deployment-launch-btn:hover { + background-color: var(--primary-color-dark); +} + +.deployment-card.main .deployment-launch-btn { + background-color: var(--success-color); +} + +.deployment-card.main .deployment-launch-btn:hover { + background-color: var(--success-color-dark); +} + +.deployment-card.feature .deployment-launch-btn { + background-color: var(--warning-color); + color: var(--text-dark); +} + +.deployment-card.feature .deployment-launch-btn:hover { + background-color: var(--warning-color-dark); +} + +.deployment-info { + background-color: var(--info-background); + border: 1px solid var(--info-border); + border-radius: 8px; + padding: 2rem; +} + +.info-card h3 { + color: var(--heading-color); + margin-bottom: 1rem; +} + +.info-card p { + color: var(--text-color); + line-height: 1.6; + margin-bottom: 1rem; +} + +.info-card p:last-child { + margin-bottom: 0; +} + +.loading-section, .error-section { + text-align: center; + padding: 3rem; +} + +.spinner { + border: 4px solid var(--border-color); + border-top: 4px solid var(--primary-color); + border-radius: 50%; + width: 40px; + height: 40px; + animation: spin 1s linear infinite; + margin: 0 auto 1rem; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.error-section h2 { + color: var(--error-color); + margin-bottom: 1rem; +} + +.error-section button { + background-color: var(--primary-color); + color: white; + border: none; + padding: 0.75rem 1.5rem; + border-radius: 8px; + cursor: pointer; + margin-top: 1rem; +} + +.error-section button:hover { + background-color: var(--primary-color-dark); +} + +/* Responsive design */ +@media (max-width: 768px) { + .deployment-selector-content { + padding: 1rem; + } + + .deployment-intro { + flex-direction: column; + text-align: center; + } + + .deployment-text h1 { + font-size: 2rem; + } + + .deployments-grid { + grid-template-columns: 1fr; + gap: 1rem; + } + + .deployment-card { + padding: 1rem; + } +} \ No newline at end of file diff --git a/src/components/BranchDeploymentSelector.js b/src/components/BranchDeploymentSelector.js new file mode 100644 index 000000000..d42eff7f6 --- /dev/null +++ b/src/components/BranchDeploymentSelector.js @@ -0,0 +1,194 @@ +import React, { useState, useEffect } from 'react'; +import { PageLayout } from './framework'; +import useThemeImage from '../hooks/useThemeImage'; +import './BranchDeploymentSelector.css'; + +const BranchDeploymentSelector = () => { + const [deployments, setDeployments] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // Theme-aware image paths + const mascotImage = useThemeImage('sgex-mascot.png'); + + useEffect(() => { + const fetchDeployments = async () => { + try { + setLoading(true); + + // For now, we'll use a mock list of deployments + // In the future, this could be fetched from GitHub Pages API or a deployment manifest + const mockDeployments = [ + { + id: 'main', + name: 'Main Application', + branch: 'main', + url: '/sgex/', + description: 'Primary SGEX Workbench application with all features', + status: 'active', + lastUpdated: new Date().toISOString(), + type: 'main' + }, + { + id: 'feature-branch-1', + name: 'Feature: Enhanced Editor', + branch: 'feature/enhanced-editor', + url: '/sgex/feature-enhanced-editor/', + description: 'Testing new enhanced component editor features', + status: 'active', + lastUpdated: new Date(Date.now() - 86400000).toISOString(), // 1 day ago + type: 'feature' + }, + { + id: 'feature-branch-2', + name: 'Feature: Improved UI', + branch: 'feature/improved-ui', + url: '/sgex/feature-improved-ui/', + description: 'Updated user interface with improved accessibility', + status: 'active', + lastUpdated: new Date(Date.now() - 172800000).toISOString(), // 2 days ago + type: 'feature' + } + ]; + + setDeployments(mockDeployments); + } catch (err) { + console.error('Error fetching deployments:', err); + setError('Failed to load deployment information'); + } finally { + setLoading(false); + } + }; + + fetchDeployments(); + }, []); + + const handleDeploymentSelect = (deployment) => { + // Navigate to the deployment URL + window.location.href = deployment.url; + }; + + const formatLastUpdated = (dateString) => { + const date = new Date(dateString); + const now = new Date(); + const diffInHours = Math.floor((now - date) / (1000 * 60 * 60)); + + if (diffInHours < 1) { + return 'Updated less than an hour ago'; + } else if (diffInHours < 24) { + return `Updated ${diffInHours} hour${diffInHours > 1 ? 's' : ''} ago`; + } else { + const diffInDays = Math.floor(diffInHours / 24); + return `Updated ${diffInDays} day${diffInDays > 1 ? 's' : ''} ago`; + } + }; + + if (loading) { + return ( + +
+
+
+

Loading deployments...

+
+
+
+ ); + } + + if (error) { + return ( + +
+
+

Error Loading Deployments

+

{error}

+ +
+
+
+ ); + } + + return ( + +
+
+
+
+ SGEX Workbench Helper +
+
+

SGEX Deployments

+

Branch Deployment Selector

+

+ Select a deployment to explore different versions and features of SGEX Workbench. + Each deployment represents a different branch or feature in development. +

+
+
+
+ +
+ {deployments.map((deployment) => ( +
handleDeploymentSelect(deployment)} + > +
+
+ + {deployment.status} +
+
+ {deployment.type === 'main' ? '🏠 Main' : 'đŸ”Ŧ Feature'} +
+
+ +
+

{deployment.name}

+

Branch: {deployment.branch}

+

{deployment.description}

+ +
+

+ {formatLastUpdated(deployment.lastUpdated)} +

+

+ {deployment.url} +

+
+
+ +
+ +
+
+ ))} +
+ +
+
+

About SGEX Deployments

+

+ Each deployment represents a different version of SGEX Workbench. The main deployment + contains the stable, production-ready version. Feature deployments contain experimental + features and improvements that are being tested before they are merged into the main application. +

+

+ Feature deployments may contain incomplete features or bugs. Use them to preview upcoming + functionality and provide feedback to the development team. +

+
+
+
+
+ ); +}; + +export default BranchDeploymentSelector; \ No newline at end of file