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 Managed_tablesDBApi = require('../db/api/managed_tables');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
const config = require('../config');
|
|
||||||
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@ -160,27 +158,27 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* id:
|
* id:
|
||||||
* description: ID of the updated item
|
* description: ID of the updated item
|
||||||
* type: string
|
* type: string
|
||||||
* data:
|
data:
|
||||||
* description: Data of the updated item
|
description: Data of the updated item
|
||||||
* type: object
|
type: object
|
||||||
* $ref: "#/components/schemas/Managed_tables"
|
$ref: "#/components/schemas/Managed_tables"
|
||||||
* required:
|
required:
|
||||||
* - id
|
- id
|
||||||
* responses:
|
responses:
|
||||||
* 200:
|
200:
|
||||||
* description: The item data was successfully updated
|
description: The item data was successfully updated
|
||||||
* content:
|
content:
|
||||||
* application/json:
|
application/json:
|
||||||
* schema:
|
schema:
|
||||||
* $ref: "#/components/schemas/Managed_tables"
|
$ref: "#/components/schemas/Managed_tables"
|
||||||
* 400:
|
400:
|
||||||
* description: Invalid ID supplied
|
description: Invalid ID supplied
|
||||||
* 401:
|
401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
$ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
404:
|
||||||
* description: Item not found
|
description: Item not found
|
||||||
* 500:
|
500:
|
||||||
* description: Some server error
|
description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
router.put('/:id', wrapAsync(async (req, res) => {
|
||||||
await Managed_tablesService.update(req.body.data, req.body.id, req.currentUser);
|
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
|
* required: true
|
||||||
* schema:
|
* schema:
|
||||||
* type: string
|
* type: string
|
||||||
* responses:
|
responses:
|
||||||
* 200:
|
200:
|
||||||
* description: The item was successfully deleted
|
description: The item was successfully deleted
|
||||||
* content:
|
content:
|
||||||
* application/json:
|
application/json:
|
||||||
* schema:
|
schema:
|
||||||
* $ref: "#/components/schemas/Managed_tables"
|
$ref: "#/components/schemas/Managed_tables"
|
||||||
* 400:
|
400:
|
||||||
* description: Invalid ID supplied
|
description: Invalid ID supplied
|
||||||
* 401:
|
401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
$ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
404:
|
||||||
* description: Item not found
|
description: Item not found
|
||||||
* 500:
|
500:
|
||||||
* description: Some server error
|
description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
router.delete('/:id', wrapAsync(async (req, res) => {
|
||||||
await Managed_tablesService.remove(req.params.id, req.currentUser);
|
await Managed_tablesService.remove(req.params.id, req.currentUser);
|
||||||
@ -244,19 +242,19 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* ids:
|
* ids:
|
||||||
* description: IDs of the updated items
|
* description: IDs of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* responses:
|
responses:
|
||||||
* 200:
|
200:
|
||||||
* description: The items was successfully deleted
|
description: The items was successfully deleted
|
||||||
* content:
|
content:
|
||||||
* application/json:
|
application/json:
|
||||||
* schema:
|
schema:
|
||||||
* $ref: "#/components/schemas/Managed_tables"
|
$ref: "#/components/schemas/Managed_tables"
|
||||||
* 401:
|
401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
$ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
404:
|
||||||
* description: Items not found
|
description: Items not found
|
||||||
* 500:
|
500:
|
||||||
* description: Some server error
|
description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
||||||
await Managed_tablesService.deleteByIds(req.body.data, req.currentUser);
|
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
|
* summary: Get all managed_tables
|
||||||
* description: Get all managed_tables
|
* description: Get all managed_tables
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
200:
|
||||||
* description: Managed_tables list successfully received
|
description: Managed_tables list successfully received
|
||||||
* content:
|
content:
|
||||||
* application/json:
|
application/json:
|
||||||
* schema:
|
schema:
|
||||||
* type: array
|
type: array
|
||||||
* items:
|
items:
|
||||||
* $ref: "#/components/schemas/Managed_tables"
|
$ref: "#/components/schemas/Managed_tables"
|
||||||
* 401:
|
401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
$ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
404:
|
||||||
* description: Data not found
|
description: Data not found
|
||||||
* 500:
|
500:
|
||||||
* description: Some server error
|
description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/', wrapAsync(async (req, res) => {
|
router.get('/', wrapAsync(async (req, res) => {
|
||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
@ -416,21 +414,21 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* required: true
|
* required: true
|
||||||
* schema:
|
* schema:
|
||||||
* type: string
|
* type: string
|
||||||
* responses:
|
responses:
|
||||||
* 200:
|
200:
|
||||||
* description: Selected item successfully received
|
description: Selected item successfully received
|
||||||
* content:
|
content:
|
||||||
* application/json:
|
application/json:
|
||||||
* schema:
|
schema:
|
||||||
* $ref: "#/components/schemas/Managed_tables"
|
$ref: "#/components/schemas/Managed_tables"
|
||||||
* 400:
|
400:
|
||||||
* description: Invalid ID supplied
|
description: Invalid ID supplied
|
||||||
* 401:
|
401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
$ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
404:
|
||||||
* description: Item not found
|
description: Item not found
|
||||||
* 500:
|
500:
|
||||||
* description: Some server error
|
description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await Managed_tablesDBApi.findBy(
|
const payload = await Managed_tablesDBApi.findBy(
|
||||||
@ -442,6 +440,37 @@ router.get('/:id', wrapAsync(async (req, res) => {
|
|||||||
res.status(200).send(payload);
|
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);
|
router.use('/', require('../helpers').commonErrorHandler);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@ -3,13 +3,8 @@ const Managed_tablesDBApi = require('../db/api/managed_tables');
|
|||||||
const processFile = require("../middlewares/upload");
|
const processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
const csv = require('csv-parser');
|
||||||
const axios = require('axios');
|
|
||||||
const config = require('../config');
|
|
||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
|
const OpenAiService = require('./openai');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = class Managed_tablesService {
|
module.exports = class Managed_tablesService {
|
||||||
static async create(data, currentUser) {
|
static async create(data, currentUser) {
|
||||||
@ -28,9 +23,9 @@ module.exports = class Managed_tablesService {
|
|||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
static async bulkImport(req, res) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -95,7 +90,7 @@ module.exports = class Managed_tablesService {
|
|||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, currentUser) {
|
static async deleteByIds(ids, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
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 Head from 'next/head'
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import DatePicker from "react-datepicker";
|
import DatePicker from "react-datepicker";
|
||||||
@ -31,6 +31,7 @@ import { useRouter } from 'next/router'
|
|||||||
import {saveFile} from "../../helpers/fileSaver";
|
import {saveFile} from "../../helpers/fileSaver";
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
import dataFormatter from '../../helpers/dataFormatter';
|
||||||
import ImageField from "../../components/ImageField";
|
import ImageField from "../../components/ImageField";
|
||||||
|
import PluginGenerator from '../../components/Managed_tables/PluginGenerator';
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
import {hasPermission} from "../../helpers/userPermissions";
|
||||||
|
|
||||||
@ -361,8 +362,10 @@ const EditManaged_tables = () => {
|
|||||||
const { managed_tablesId } = router.query
|
const { managed_tablesId } = router.query
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (managed_tablesId) {
|
||||||
dispatch(fetch({ id: managed_tablesId }))
|
dispatch(fetch({ id: managed_tablesId }))
|
||||||
}, [managed_tablesId])
|
}
|
||||||
|
}, [managed_tablesId, dispatch])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof managed_tables === 'object') {
|
if (typeof managed_tables === 'object') {
|
||||||
@ -918,6 +921,11 @@ const EditManaged_tables = () => {
|
|||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
|
||||||
|
<PluginGenerator
|
||||||
|
managedTableId={managed_tablesId as string}
|
||||||
|
tableName={initialValues.table_name}
|
||||||
|
/>
|
||||||
</SectionMain>
|
</SectionMain>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user