diff --git a/.github/workflows/merge-conflict-check.yml b/.github/workflows/merge-conflict-check.yml new file mode 100644 index 000000000..3966991be --- /dev/null +++ b/.github/workflows/merge-conflict-check.yml @@ -0,0 +1,132 @@ +name: Check for Merge Conflicts + +on: + pull_request: + types: [opened, synchronize, reopened] + # Also run when the base branch is updated + push: + branches: + - main + - master + +jobs: + check-conflicts: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fetch base branch + run: | + git fetch origin ${{ github.base_ref || github.event.repository.default_branch }} + + - name: Check for merge conflicts + id: conflict-check + run: | + # Determine the base branch + BASE_BRANCH="${{ github.base_ref }}" + if [ -z "$BASE_BRANCH" ]; then + BASE_BRANCH="${{ github.event.repository.default_branch }}" + fi + + echo "Checking for conflicts with origin/$BASE_BRANCH" + + # Try to merge the base branch to see if there are conflicts + if git merge-tree $(git merge-base HEAD origin/$BASE_BRANCH) origin/$BASE_BRANCH HEAD | grep -q "^<<<<<"; then + echo "has_conflicts=true" >> $GITHUB_OUTPUT + echo "✗ Merge conflicts detected!" + else + echo "has_conflicts=false" >> $GITHUB_OUTPUT + echo "✓ No merge conflicts detected" + fi + + - name: Comment on PR if conflicts exist + if: steps.conflict-check.outputs.has_conflicts == 'true' && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const comment = `## ⚠️ Merge Conflicts Detected + + @copilot This pull request has merge conflicts that need to be resolved. + + **Please resolve the conflicts by:** + 1. Merging the latest changes from the base branch + 2. Resolving any conflicting files + 3. Pushing the updated changes + + --- + *This is an automated message from the merge conflict checker.*`; + + // Check if we already commented + 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('Merge Conflicts Detected') + ); + + if (botComment) { + // Update existing comment + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: comment + }); + } else { + // Create new comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: comment + }); + } + + - name: Add label if conflicts exist + if: steps.conflict-check.outputs.has_conflicts == 'true' && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['merge-conflict'] + }); + } catch (error) { + console.log('Label might not exist yet, skipping...'); + } + + - name: Remove label if no conflicts + if: steps.conflict-check.outputs.has_conflicts == 'false' && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + name: 'merge-conflict' + }); + } catch (error) { + console.log('Label does not exist or is not applied, skipping...'); + } + + - name: Fail if conflicts exist + if: steps.conflict-check.outputs.has_conflicts == 'true' + run: | + echo "❌ This PR has merge conflicts and cannot be merged." + exit 1 diff --git a/.gitignore b/.gitignore index 6cfe203cc..5eb20d6c2 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,6 @@ dist-ssr .env **/agent-eval-report* -packages pids .file-manifest .devcontainer/ diff --git a/README.md b/README.md index 6dc46e959..75f04bef7 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,24 @@ A declarative admin panel generator that creates full-featured CRUD interfaces f ## Quick Start -1. Launch the app -2. Use the sidebar to navigate between models -3. Click "Create New" to add records -4. Edit or delete records using the action buttons -5. Click "Edit Schema" to customize your data models +1. Clone the repository +2. Install dependencies: `npm install` +3. Launch the app: `npm run dev` +4. Use the sidebar to navigate between models +5. Click "Create New" to add records +6. Edit or delete records using the action buttons +7. Click "Edit Schema" to customize your data models + +## Packages + +This project uses a modular package system. The `packages/` folder contains component packages that are committed to the repository. + +If you need to add a new package, use: +```bash +npm run setup-packages +``` + +This will create the required package structure with placeholder files. ## Schema Structure diff --git a/package-lock.json b/package-lock.json index cbf5b485a..f5039954f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "spark-template", "version": "0.0.0", + "hasInstallScript": true, "dependencies": { "@github/spark": ">=0.43.1 <1", "@heroicons/react": "^2.2.0", @@ -7106,6 +7107,8 @@ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "dompurify": "3.2.7", "marked": "14.0.0" @@ -7851,16 +7854,8 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, "node_modules/router/node_modules/ms": { diff --git a/package.json b/package.json index 2626a36ac..e0f75eff2 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "build": "tsc -b --noCheck && vite build", "lint": "eslint .", "optimize": "vite optimize", - "preview": "vite preview" + "preview": "vite preview", + "setup-packages": "node scripts/setup-packages.cjs", + "postinstall": "node scripts/setup-packages.cjs" }, "dependencies": { "@github/spark": ">=0.43.1 <1", diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 000000000..7becc7635 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,61 @@ +# Packages Folder + +This folder contains modular packages for the MetaBuilder application. Each package is self-contained with its own components, metadata, and examples. + +## Structure + +Each package follows this structure: + +``` +packages/ + ├── package_name/ + │ ├── seed/ + │ │ ├── components.json # Component definitions + │ │ ├── metadata.json # Package metadata + │ │ └── scripts/ # Optional Lua scripts + │ └── static_content/ + │ └── examples.json # Optional usage examples +``` + +## Available Packages + +- **admin_dialog**: Admin dialog components for management interfaces +- **data_table**: Data table components for displaying tabular data +- **form_builder**: Form builder components for creating dynamic forms +- **nav_menu**: Navigation menu components +- **dashboard**: Dashboard layout components +- **notification_center**: Notification center components + +## Package Metadata Format + +Each `metadata.json` file should contain: + +```json +{ + "packageId": "package_name", + "name": "Display Name", + "version": "1.0.0", + "description": "Package description", + "author": "Author name", + "category": "ui", + "dependencies": [], + "exports": { + "components": [] + } +} +``` + +## Components Format + +Each `components.json` file should contain an array of component definitions. + +## Development + +The main application imports from these packages via relative paths in `src/lib/package-glue.ts`. + +To add a new package: + +1. Run `npm run setup-packages ` to create the package structure +2. Add optional `static_content/examples.json` if needed +3. Update `src/lib/package-glue.ts` to import the new package +4. Commit the new package files to the repository diff --git a/packages/admin_dialog/seed/components.json b/packages/admin_dialog/seed/components.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/packages/admin_dialog/seed/components.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/admin_dialog/seed/metadata.json b/packages/admin_dialog/seed/metadata.json new file mode 100644 index 000000000..ceaa883d2 --- /dev/null +++ b/packages/admin_dialog/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "admin_dialog", + "name": "Admin Dialog", + "version": "1.0.0", + "description": "Admin dialog components", + "author": "MetaBuilder", + "category": "ui", + "dependencies": [], + "exports": { + "components": [] + } +} \ No newline at end of file diff --git a/packages/admin_dialog/static_content/examples.json b/packages/admin_dialog/static_content/examples.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/admin_dialog/static_content/examples.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/dashboard/seed/components.json b/packages/dashboard/seed/components.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/packages/dashboard/seed/components.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/dashboard/seed/metadata.json b/packages/dashboard/seed/metadata.json new file mode 100644 index 000000000..0e1121b38 --- /dev/null +++ b/packages/dashboard/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "dashboard", + "name": "Dashboard", + "version": "1.0.0", + "description": "Dashboard components", + "author": "MetaBuilder", + "category": "ui", + "dependencies": [], + "exports": { + "components": [] + } +} \ No newline at end of file diff --git a/packages/data_table/seed/components.json b/packages/data_table/seed/components.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/packages/data_table/seed/components.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/data_table/seed/metadata.json b/packages/data_table/seed/metadata.json new file mode 100644 index 000000000..aba4d5bb9 --- /dev/null +++ b/packages/data_table/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "data_table", + "name": "Data Table", + "version": "1.0.0", + "description": "Data table components", + "author": "MetaBuilder", + "category": "ui", + "dependencies": [], + "exports": { + "components": [] + } +} \ No newline at end of file diff --git a/packages/data_table/static_content/examples.json b/packages/data_table/static_content/examples.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/data_table/static_content/examples.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/form_builder/seed/components.json b/packages/form_builder/seed/components.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/packages/form_builder/seed/components.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/form_builder/seed/metadata.json b/packages/form_builder/seed/metadata.json new file mode 100644 index 000000000..ee860a4c5 --- /dev/null +++ b/packages/form_builder/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "form_builder", + "name": "Form Builder", + "version": "1.0.0", + "description": "Form builder components", + "author": "MetaBuilder", + "category": "ui", + "dependencies": [], + "exports": { + "components": [] + } +} \ No newline at end of file diff --git a/packages/form_builder/static_content/examples.json b/packages/form_builder/static_content/examples.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/form_builder/static_content/examples.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/nav_menu/seed/components.json b/packages/nav_menu/seed/components.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/packages/nav_menu/seed/components.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/nav_menu/seed/metadata.json b/packages/nav_menu/seed/metadata.json new file mode 100644 index 000000000..a99600e4f --- /dev/null +++ b/packages/nav_menu/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "nav_menu", + "name": "Navigation Menu", + "version": "1.0.0", + "description": "Navigation menu components", + "author": "MetaBuilder", + "category": "ui", + "dependencies": [], + "exports": { + "components": [] + } +} \ No newline at end of file diff --git a/packages/notification_center/seed/components.json b/packages/notification_center/seed/components.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/packages/notification_center/seed/components.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/packages/notification_center/seed/metadata.json b/packages/notification_center/seed/metadata.json new file mode 100644 index 000000000..956ab5fd8 --- /dev/null +++ b/packages/notification_center/seed/metadata.json @@ -0,0 +1,12 @@ +{ + "packageId": "notification_center", + "name": "Notification Center", + "version": "1.0.0", + "description": "Notification center components", + "author": "MetaBuilder", + "category": "ui", + "dependencies": [], + "exports": { + "components": [] + } +} \ No newline at end of file diff --git a/scripts/setup-packages.cjs b/scripts/setup-packages.cjs new file mode 100755 index 000000000..2fa105b9d --- /dev/null +++ b/scripts/setup-packages.cjs @@ -0,0 +1,168 @@ +#!/usr/bin/env node + +'use strict'; + +/** + * Setup script for creating package folder structure + * Usage: + * node scripts/setup-packages.cjs - Create a specific package + * node scripts/setup-packages.cjs - Verify all required packages exist + */ + +const fs = require('fs'); +const path = require('path'); + +const packagesDir = path.join(__dirname, '..', 'packages'); + +// Get package name from command line argument +const packageName = process.argv[2]; + +// Package definitions +const packageTemplates = { + 'admin_dialog': { + id: 'admin_dialog', + name: 'Admin Dialog', + description: 'Admin dialog components', + hasExamples: true + }, + 'data_table': { + id: 'data_table', + name: 'Data Table', + description: 'Data table components', + hasExamples: true + }, + 'form_builder': { + id: 'form_builder', + name: 'Form Builder', + description: 'Form builder components', + hasExamples: true + }, + 'nav_menu': { + id: 'nav_menu', + name: 'Navigation Menu', + description: 'Navigation menu components', + hasExamples: false + }, + 'dashboard': { + id: 'dashboard', + name: 'Dashboard', + description: 'Dashboard components', + hasExamples: false + }, + 'notification_center': { + id: 'notification_center', + name: 'Notification Center', + description: 'Notification center components', + hasExamples: false + } +}; + +function createPackage(pkg) { + const pkgDir = path.join(packagesDir, pkg.id); + const seedDir = path.join(pkgDir, 'seed'); + + // Create directories + if (!fs.existsSync(packagesDir)) { + fs.mkdirSync(packagesDir, { recursive: true }); + } + + if (!fs.existsSync(seedDir)) { + fs.mkdirSync(seedDir, { recursive: true }); + } + + // Create components.json + const componentsPath = path.join(seedDir, 'components.json'); + if (!fs.existsSync(componentsPath)) { + fs.writeFileSync(componentsPath, '[]', 'utf8'); + } + + // Create metadata.json + const metadataPath = path.join(seedDir, 'metadata.json'); + if (!fs.existsSync(metadataPath)) { + const metadata = { + packageId: pkg.id, + name: pkg.name, + version: '1.0.0', + description: pkg.description, + author: 'MetaBuilder', + category: 'ui', + dependencies: [], + exports: { + components: [] + } + }; + fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), 'utf8'); + } + + // Create examples.json if needed + if (pkg.hasExamples) { + const staticDir = path.join(pkgDir, 'static_content'); + if (!fs.existsSync(staticDir)) { + fs.mkdirSync(staticDir, { recursive: true }); + } + + const examplesPath = path.join(staticDir, 'examples.json'); + if (!fs.existsSync(examplesPath)) { + fs.writeFileSync(examplesPath, '{}', 'utf8'); + } + } + + console.log(`✓ Created ${pkg.name} package`); +} + +// If a specific package name is provided +if (packageName) { + const pkg = packageTemplates[packageName]; + + if (!pkg) { + console.error(`Error: Unknown package '${packageName}'`); + console.log('\nAvailable packages:'); + Object.keys(packageTemplates).forEach(key => { + console.log(` - ${key}`); + }); + process.exit(1); + } + + // Check if package already exists + const pkgDir = path.join(packagesDir, pkg.id); + if (fs.existsSync(pkgDir)) { + console.log(`✓ Package '${pkg.name}' already exists`); + process.exit(0); + } + + console.log(`Creating package: ${pkg.name}...\n`); + createPackage(pkg); + console.log('\n✅ Package created successfully!'); +} else { + // No package name provided - verification mode (postinstall or manual check) + // Verify that all required packages exist + const requiredPackages = Object.keys(packageTemplates); + const missingPackages = []; + + if (!fs.existsSync(packagesDir)) { + console.error('Error: packages folder does not exist!'); + console.log('Run this script with a package name to create packages.'); + process.exit(1); + } + + for (const pkgId of requiredPackages) { + const componentsPath = path.join(packagesDir, pkgId, 'seed', 'components.json'); + const metadataPath = path.join(packagesDir, pkgId, 'seed', 'metadata.json'); + + if (!fs.existsSync(componentsPath) || !fs.existsSync(metadataPath)) { + missingPackages.push(pkgId); + } + } + + if (missingPackages.length > 0) { + console.error('Error: Missing required packages:', missingPackages.join(', ')); + console.log('\nCreate missing packages with:'); + missingPackages.forEach(pkg => { + console.log(` npm run setup-packages ${pkg}`); + }); + process.exit(1); + } + + console.log('✓ All required packages exist and are committed to the repository.'); +} + diff --git a/tsconfig.json b/tsconfig.json index 4baa496ca..22e0d23e2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,7 @@ }, }, "include": [ - "src" + "src", + "packages" ] }