Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a85e3e1d9 |
@ -5,8 +5,6 @@ const Managed_tablesService = require('../services/managed_tables');
|
||||
const Managed_tablesDBApi = require('../db/api/managed_tables');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -160,27 +158,27 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||
* id:
|
||||
* description: ID of the updated item
|
||||
* type: string
|
||||
* data:
|
||||
* description: Data of the updated item
|
||||
* type: object
|
||||
* $ref: "#/components/schemas/Managed_tables"
|
||||
* required:
|
||||
* - id
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item data was successfully updated
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Managed_tables"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
data:
|
||||
description: Data of the updated item
|
||||
type: object
|
||||
$ref: "#/components/schemas/Managed_tables"
|
||||
required:
|
||||
- id
|
||||
responses:
|
||||
200:
|
||||
description: The item data was successfully updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Managed_tables"
|
||||
400:
|
||||
description: Invalid ID supplied
|
||||
401:
|
||||
$ref: "#/components/responses/UnauthorizedError"
|
||||
404:
|
||||
description: Item not found
|
||||
500:
|
||||
description: Some server error
|
||||
*/
|
||||
router.put('/:id', wrapAsync(async (req, res) => {
|
||||
await Managed_tablesService.update(req.body.data, req.body.id, req.currentUser);
|
||||
@ -204,21 +202,21 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The item was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Managed_tables"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
responses:
|
||||
200:
|
||||
description: The item was successfully deleted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Managed_tables"
|
||||
400:
|
||||
description: Invalid ID supplied
|
||||
401:
|
||||
$ref: "#/components/responses/UnauthorizedError"
|
||||
404:
|
||||
description: Item not found
|
||||
500:
|
||||
description: Some server error
|
||||
*/
|
||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||
await Managed_tablesService.remove(req.params.id, req.currentUser);
|
||||
@ -244,19 +242,19 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
||||
* ids:
|
||||
* description: IDs of the updated items
|
||||
* type: array
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The items was successfully deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Managed_tables"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Items not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
responses:
|
||||
200:
|
||||
description: The items was successfully deleted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Managed_tables"
|
||||
401:
|
||||
$ref: "#/components/responses/UnauthorizedError"
|
||||
404:
|
||||
description: Items not found
|
||||
500:
|
||||
description: Some server error
|
||||
*/
|
||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||
await Managed_tablesService.deleteByIds(req.body.data, req.currentUser);
|
||||
@ -274,20 +272,20 @@ router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||
* summary: Get all managed_tables
|
||||
* description: Get all managed_tables
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Managed_tables list successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Managed_tables"
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Data not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
200:
|
||||
description: Managed_tables list successfully received
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Managed_tables"
|
||||
401:
|
||||
$ref: "#/components/responses/UnauthorizedError"
|
||||
404:
|
||||
description: Data not found
|
||||
500:
|
||||
description: Some server error
|
||||
*/
|
||||
router.get('/', wrapAsync(async (req, res) => {
|
||||
const filetype = req.query.filetype
|
||||
@ -416,21 +414,21 @@ router.get('/autocomplete', async (req, res) => {
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Selected item successfully received
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/Managed_tables"
|
||||
* 400:
|
||||
* description: Invalid ID supplied
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Item not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
responses:
|
||||
200:
|
||||
description: Selected item successfully received
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Managed_tables"
|
||||
400:
|
||||
description: Invalid ID supplied
|
||||
401:
|
||||
$ref: "#/components/responses/UnauthorizedError"
|
||||
404:
|
||||
description: Item not found
|
||||
500:
|
||||
description: Some server error
|
||||
*/
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await Managed_tablesDBApi.findBy(
|
||||
@ -442,6 +440,37 @@ router.get('/:id', wrapAsync(async (req, res) => {
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/managed_tables/{id}/generate-plugin:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* tags: [Managed_tables]
|
||||
* summary: Generate WordPress plugin code for this table
|
||||
* description: Generate WordPress plugin code for this table using AI
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: ID of the managed table
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Plugin code successfully generated
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 404:
|
||||
* description: Managed table not found
|
||||
* 500:
|
||||
* description: Some server error
|
||||
*/
|
||||
router.post('/:id/generate-plugin', wrapAsync(async (req, res) => {
|
||||
const payload = await Managed_tablesService.generatePluginCode(req.params.id);
|
||||
res.status(200).send(payload);
|
||||
}));
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@ -3,13 +3,8 @@ const Managed_tablesDBApi = require('../db/api/managed_tables');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
|
||||
const OpenAiService = require('./openai');
|
||||
|
||||
module.exports = class Managed_tablesService {
|
||||
static async create(data, currentUser) {
|
||||
@ -28,9 +23,9 @@ module.exports = class Managed_tablesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
static async bulkImport(req, res) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
@ -95,7 +90,7 @@ module.exports = class Managed_tablesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
@ -132,7 +127,44 @@ module.exports = class Managed_tablesService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static async generatePluginCode(id) {
|
||||
const table = await Managed_tablesDBApi.findBy({ id });
|
||||
if (!table) {
|
||||
throw new ValidationError('managed_tablesNotFound');
|
||||
}
|
||||
|
||||
const columns = table.table_columns_managed_table || [];
|
||||
const columnDefinitions = columns.map(c => ({
|
||||
name: c.column_name,
|
||||
label: c.label,
|
||||
type: c.data_type,
|
||||
isPrimary: c.is_primary_key,
|
||||
isNullable: c.is_nullable
|
||||
}));
|
||||
|
||||
const prompt = `
|
||||
Generate a professional WordPress plugin (PHP) that provides a full CRUD admin interface for a custom MySQL table.
|
||||
|
||||
Table Name: ${table.table_name}
|
||||
Plugin Label: ${table.label}
|
||||
|
||||
Columns:
|
||||
${JSON.stringify(columnDefinitions, null, 2)}
|
||||
|
||||
Requirements:
|
||||
1. Use WordPress best practices and $wpdb for all database operations.
|
||||
2. Create a top-level admin menu item for this plugin.
|
||||
3. Implement a list table view with searching and sorting.
|
||||
4. Implement Add/Edit forms with proper validation and sanitization.
|
||||
5. Implement a Delete action with nonces for security.
|
||||
6. Include a activation hook to create the table if it doesn't exist (use dbDelta).
|
||||
7. Use clean, well-commented code.
|
||||
8. The plugin should be a single PHP file for simplicity.
|
||||
|
||||
Output only the PHP code block.
|
||||
`;
|
||||
|
||||
const result = await OpenAiService.askGpt(prompt);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
118
frontend/src/components/Managed_tables/PluginGenerator.tsx
Normal file
118
frontend/src/components/Managed_tables/PluginGenerator.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
import React, { useState } from 'react'
|
||||
import { mdiCodeBraces, mdiContentCopy, mdiLoading, mdiCheck } from '@mdi/js'
|
||||
import axios from 'axios'
|
||||
import BaseButton from '../BaseButton'
|
||||
import CardBox from '../CardBox'
|
||||
import CardBoxComponentBody from '../CardBoxComponentBody'
|
||||
import CardBoxComponentTitle from '../CardBoxComponentTitle'
|
||||
import CardBoxComponentFooter from '../CardBoxComponentFooter'
|
||||
import BaseIcon from '../BaseIcon'
|
||||
|
||||
interface PluginGeneratorProps {
|
||||
managedTableId: string
|
||||
tableName: string
|
||||
}
|
||||
|
||||
const PluginGenerator: React.FC<PluginGeneratorProps> = ({ managedTableId, tableName }) => {
|
||||
const [code, setCode] = useState<string | null>(null)
|
||||
const [isGenerating, setIsGenerating] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const handleGenerate = async () => {
|
||||
setIsGenerating(true)
|
||||
setError(null)
|
||||
try {
|
||||
const response = await axios.post(`/managed_tables/${managedTableId}/generate-plugin`)
|
||||
if (response.data && response.data.success) {
|
||||
setCode(response.data.data)
|
||||
} else {
|
||||
setError(response.data.error || 'Failed to generate plugin code.')
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
setError(err.response?.data?.error || 'An error occurred while generating the plugin.')
|
||||
} finally {
|
||||
setIsGenerating(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCopy = () => {
|
||||
if (code) {
|
||||
navigator.clipboard.writeText(code)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<CardBox className="mt-6 border-2 border-blue-500 shadow-lg">
|
||||
<CardBoxComponentTitle title="WordPress Plugin AI Generator" icon={mdiCodeBraces} />
|
||||
|
||||
<CardBoxComponentBody>
|
||||
<div className="mb-4">
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-2">
|
||||
Generate a custom WordPress plugin for the table <span className="font-bold text-blue-600">"{tableName}"</span>.
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-500">
|
||||
This will create a complete PHP plugin with admin CRUD operations using WordPress best practices.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
|
||||
<span className="block sm:inline">{error}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{code ? (
|
||||
<div className="relative group">
|
||||
<div className="absolute right-2 top-2 z-10">
|
||||
<BaseButton
|
||||
color={copied ? 'success' : 'info'}
|
||||
icon={copied ? mdiCheck : mdiContentCopy}
|
||||
label={copied ? 'Copied' : 'Copy Code'}
|
||||
small
|
||||
onClick={handleCopy}
|
||||
/>
|
||||
</div>
|
||||
<pre className="p-4 bg-gray-900 text-green-400 rounded-lg overflow-x-auto max-h-[500px] text-xs font-mono border-2 border-gray-700 aside-scrollbars">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-12 bg-gray-50 dark:bg-gray-800 rounded-lg border-2 border-dashed border-gray-300 dark:border-gray-700">
|
||||
<BaseIcon path={mdiCodeBraces} size={48} className="text-gray-300 dark:text-gray-600 mb-4" />
|
||||
<p className="text-gray-400 italic mb-6">No code generated yet.</p>
|
||||
<BaseButton
|
||||
color="info"
|
||||
label={isGenerating ? 'Generating Code...' : 'Generate WordPress Plugin'}
|
||||
icon={isGenerating ? mdiLoading : mdiCodeBraces}
|
||||
className={isGenerating ? 'animate-pulse' : ''}
|
||||
disabled={isGenerating}
|
||||
onClick={handleGenerate}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CardBoxComponentBody>
|
||||
|
||||
{code && (
|
||||
<CardBoxComponentFooter>
|
||||
<div className="flex justify-between items-center w-full">
|
||||
<span className="text-xs text-gray-500">Generated using AI based on your table schema.</span>
|
||||
<BaseButton
|
||||
color="info"
|
||||
outline
|
||||
label="Regenerate"
|
||||
icon={mdiCodeBraces}
|
||||
onClick={handleGenerate}
|
||||
disabled={isGenerating}
|
||||
/>
|
||||
</div>
|
||||
</CardBoxComponentFooter>
|
||||
)}
|
||||
</CardBox>
|
||||
)
|
||||
}
|
||||
|
||||
export default PluginGenerator
|
||||
@ -1,4 +1,4 @@
|
||||
import { mdiChartTimelineVariant, mdiUpload } from '@mdi/js'
|
||||
import { mdiChartTimelineVariant, mdiUpload, mdiCodeBraces } from '@mdi/js'
|
||||
import Head from 'next/head'
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import DatePicker from "react-datepicker";
|
||||
@ -31,6 +31,7 @@ import { useRouter } from 'next/router'
|
||||
import {saveFile} from "../../helpers/fileSaver";
|
||||
import dataFormatter from '../../helpers/dataFormatter';
|
||||
import ImageField from "../../components/ImageField";
|
||||
import PluginGenerator from '../../components/Managed_tables/PluginGenerator';
|
||||
|
||||
import {hasPermission} from "../../helpers/userPermissions";
|
||||
|
||||
@ -361,8 +362,10 @@ const EditManaged_tables = () => {
|
||||
const { managed_tablesId } = router.query
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id: managed_tablesId }))
|
||||
}, [managed_tablesId])
|
||||
if (managed_tablesId) {
|
||||
dispatch(fetch({ id: managed_tablesId }))
|
||||
}
|
||||
}, [managed_tablesId, dispatch])
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof managed_tables === 'object') {
|
||||
@ -918,6 +921,11 @@ const EditManaged_tables = () => {
|
||||
</Form>
|
||||
</Formik>
|
||||
</CardBox>
|
||||
|
||||
<PluginGenerator
|
||||
managedTableId={managed_tablesId as string}
|
||||
tableName={initialValues.table_name}
|
||||
/>
|
||||
</SectionMain>
|
||||
</>
|
||||
)
|
||||
@ -935,4 +943,4 @@ EditManaged_tables.getLayout = function getLayout(page: ReactElement) {
|
||||
)
|
||||
}
|
||||
|
||||
export default EditManaged_tables
|
||||
export default EditManaged_tables
|
||||
Loading…
x
Reference in New Issue
Block a user