@rglabs/butterfly 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +201 -0
- package/README.md +371 -0
- package/dist/commands/add.d.ts +23 -0
- package/dist/commands/add.js +303 -0
- package/dist/commands/code.d.ts +11 -0
- package/dist/commands/code.js +72 -0
- package/dist/commands/create-object.d.ts +6 -0
- package/dist/commands/create-object.js +293 -0
- package/dist/commands/create-report.d.ts +6 -0
- package/dist/commands/create-report.js +154 -0
- package/dist/commands/diff.d.ts +4 -0
- package/dist/commands/diff.js +238 -0
- package/dist/commands/download.d.ts +4 -0
- package/dist/commands/download.js +374 -0
- package/dist/commands/layout.d.ts +12 -0
- package/dist/commands/layout.js +83 -0
- package/dist/commands/record.d.ts +21 -0
- package/dist/commands/record.js +483 -0
- package/dist/commands/run-poc.d.ts +3 -0
- package/dist/commands/run-poc.js +18 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.js +66 -0
- package/dist/commands/start-poc.d.ts +3 -0
- package/dist/commands/start-poc.js +55 -0
- package/dist/commands/sync-docs.d.ts +3 -0
- package/dist/commands/sync-docs.js +27 -0
- package/dist/commands/translate.d.ts +13 -0
- package/dist/commands/translate.js +401 -0
- package/dist/commands/upload.d.ts +3 -0
- package/dist/commands/upload.js +150 -0
- package/dist/commands/workflow-info.d.ts +13 -0
- package/dist/commands/workflow-info.js +161 -0
- package/dist/components/ConflictResolver.d.ts +12 -0
- package/dist/components/ConflictResolver.js +77 -0
- package/dist/components/DiffView.d.ts +11 -0
- package/dist/components/DiffView.js +101 -0
- package/dist/components/DownloadProgress.d.ts +11 -0
- package/dist/components/DownloadProgress.js +29 -0
- package/dist/components/RecordPreview.d.ts +11 -0
- package/dist/components/RecordPreview.js +91 -0
- package/dist/components/SetupForm.d.ts +8 -0
- package/dist/components/SetupForm.js +56 -0
- package/dist/components/UploadProgress.d.ts +13 -0
- package/dist/components/UploadProgress.js +42 -0
- package/dist/diff/adapters/index.d.ts +8 -0
- package/dist/diff/adapters/index.js +18 -0
- package/dist/diff/adapters/objectsAdapter.d.ts +13 -0
- package/dist/diff/adapters/objectsAdapter.js +177 -0
- package/dist/diff/adapters/reportsAdapter.d.ts +14 -0
- package/dist/diff/adapters/reportsAdapter.js +212 -0
- package/dist/diff/adapters/types.d.ts +19 -0
- package/dist/diff/adapters/types.js +2 -0
- package/dist/diff/engine.d.ts +19 -0
- package/dist/diff/engine.js +57 -0
- package/dist/diff/types.d.ts +34 -0
- package/dist/diff/types.js +110 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +117 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/api.d.ts +85 -0
- package/dist/utils/api.js +1031 -0
- package/dist/utils/auth.d.ts +4 -0
- package/dist/utils/auth.js +22 -0
- package/dist/utils/bfySplitter.d.ts +12 -0
- package/dist/utils/bfySplitter.js +151 -0
- package/dist/utils/docs.d.ts +16 -0
- package/dist/utils/docs.js +186 -0
- package/dist/utils/errorLogger.d.ts +6 -0
- package/dist/utils/errorLogger.js +29 -0
- package/dist/utils/files.d.ts +14 -0
- package/dist/utils/files.js +772 -0
- package/dist/utils/lockManager.d.ts +15 -0
- package/dist/utils/lockManager.js +126 -0
- package/dist/utils/resourceHandlers.d.ts +50 -0
- package/dist/utils/resourceHandlers.js +684 -0
- package/dist/utils/resourceMapping.d.ts +32 -0
- package/dist/utils/resourceMapping.js +210 -0
- package/dist/utils/singleResourceDownload.d.ts +14 -0
- package/dist/utils/singleResourceDownload.js +261 -0
- package/dist/utils/summaryGenerator.d.ts +2 -0
- package/dist/utils/summaryGenerator.js +183 -0
- package/dist/utils/uploadHandler.d.ts +31 -0
- package/dist/utils/uploadHandler.js +263 -0
- package/docs/AI_API.md +93 -0
- package/docs/CLAUDE.md +216 -0
- package/docs/PROJECT_SPECIFIC.md +1 -0
- package/docs/RECORD_COMMAND.md +262 -0
- package/docs/WORKFLOW_API.md +480 -0
- package/docs/bfy-splitting.md +126 -0
- package/docs/cli-commands.md +333 -0
- package/docs/examples/README.md +95 -0
- package/docs/examples/order-system.md +147 -0
- package/docs/examples/product-catalog.md +195 -0
- package/docs/examples/reports.md +187 -0
- package/docs/excel-export.md +216 -0
- package/docs/field-types/README.md +29 -0
- package/docs/field-types/calculated.md +147 -0
- package/docs/field-types/code-mappings.md +84 -0
- package/docs/field-types/custom.md +340 -0
- package/docs/object-specs/README.md +136 -0
- package/docs/object-specs/code-parameters.md +151 -0
- package/docs/object-specs/creating.md +203 -0
- package/docs/object-specs/js-code-examples.md +208 -0
- package/docs/object-specs/js-field-updates.md +168 -0
- package/docs/objects/README.md +89 -0
- package/docs/objects/creating.md +127 -0
- package/docs/page-layout.md +361 -0
- package/docs/permissions.md +260 -0
- package/docs/reports.md +197 -0
- package/docs/state-machines.md +544 -0
- package/docs/tasks/create-object.md +81 -0
- package/docs/translations.md +346 -0
- package/docs/twig-helpers.md +283 -0
- package/docs/webservices.md +159 -0
- package/docs/workspaces.md +176 -0
- package/package.json +59 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, Box, Text, useInput } from 'ink';
|
|
3
|
+
import Spinner from 'ink-spinner';
|
|
4
|
+
import { existsSync, readFileSync } from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { ButterflyAPI } from '../utils/api.js';
|
|
7
|
+
import { loadAuthConfig } from '../utils/auth.js';
|
|
8
|
+
const CreateObjectCommand = ({ options }) => {
|
|
9
|
+
const [status, setStatus] = React.useState('loading');
|
|
10
|
+
const [errorMessage, setErrorMessage] = React.useState('');
|
|
11
|
+
const [quotes, setQuotes] = React.useState([]);
|
|
12
|
+
const [currentQuoteIndex, setCurrentQuoteIndex] = React.useState(0);
|
|
13
|
+
const [title, setTitle] = React.useState('');
|
|
14
|
+
const [resultMessage, setResultMessage] = React.useState('');
|
|
15
|
+
const [logs, setLogs] = React.useState([]);
|
|
16
|
+
const [pollCount, setPollCount] = React.useState(0);
|
|
17
|
+
const [pendingExecute, setPendingExecute] = React.useState(null);
|
|
18
|
+
const [previewData, setPreviewData] = React.useState(null);
|
|
19
|
+
const addLog = (message, type = 'info') => {
|
|
20
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
21
|
+
setLogs(prev => [...prev.slice(-10), { timestamp, message, type }]);
|
|
22
|
+
};
|
|
23
|
+
useInput((input, key) => {
|
|
24
|
+
if (status !== 'confirming')
|
|
25
|
+
return;
|
|
26
|
+
if (input.toLowerCase() === 'y' || key.return) {
|
|
27
|
+
executeAction();
|
|
28
|
+
}
|
|
29
|
+
else if (input.toLowerCase() === 'n' || key.escape) {
|
|
30
|
+
addLog('Cancelled by user', 'warning');
|
|
31
|
+
setStatus('cancelled');
|
|
32
|
+
setTimeout(() => process.exit(0), 1000);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const executeAction = async () => {
|
|
36
|
+
if (!pendingExecute)
|
|
37
|
+
return;
|
|
38
|
+
try {
|
|
39
|
+
setStatus('executing');
|
|
40
|
+
addLog('POST /admin/bfy_ai_task/executeAction', 'info');
|
|
41
|
+
const executeResponse = await pendingExecute.api.aiTaskExecuteAction('create-objects', {
|
|
42
|
+
objects: pendingExecute.message.objects,
|
|
43
|
+
specs: pendingExecute.message.specs,
|
|
44
|
+
response_message: pendingExecute.message.response_message || pendingExecute.message.specs
|
|
45
|
+
});
|
|
46
|
+
if (executeResponse.success) {
|
|
47
|
+
addLog('Action executed successfully!', 'success');
|
|
48
|
+
setResultMessage('Objects created successfully!');
|
|
49
|
+
setStatus('success');
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw new Error(executeResponse.message || 'Failed to execute action');
|
|
53
|
+
}
|
|
54
|
+
setTimeout(() => process.exit(0), 2000);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
addLog(`Error: ${error.message}`, 'error');
|
|
58
|
+
setErrorMessage(error.message || 'Unknown error');
|
|
59
|
+
setStatus('error');
|
|
60
|
+
setTimeout(() => process.exit(1), 3000);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
if (quotes.length === 0)
|
|
65
|
+
return;
|
|
66
|
+
const interval = setInterval(() => {
|
|
67
|
+
setCurrentQuoteIndex(prev => (prev + 1) % quotes.length);
|
|
68
|
+
}, 3000);
|
|
69
|
+
return () => clearInterval(interval);
|
|
70
|
+
}, [quotes]);
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
const run = async () => {
|
|
73
|
+
try {
|
|
74
|
+
const filePath = path.isAbsolute(options.file)
|
|
75
|
+
? options.file
|
|
76
|
+
: path.join(process.cwd(), options.file);
|
|
77
|
+
if (!existsSync(filePath)) {
|
|
78
|
+
throw new Error(`File not found: ${filePath}`);
|
|
79
|
+
}
|
|
80
|
+
addLog(`Reading file: ${filePath}`, 'info');
|
|
81
|
+
const promptContent = readFileSync(filePath, 'utf-8');
|
|
82
|
+
addLog(`File loaded (${promptContent.length} bytes)`, 'success');
|
|
83
|
+
setStatus('authenticating');
|
|
84
|
+
addLog('Loading authentication config...', 'info');
|
|
85
|
+
const authConfig = await loadAuthConfig();
|
|
86
|
+
if (!authConfig) {
|
|
87
|
+
throw new Error('Not authenticated. Run "butterfly-cli setup" first.');
|
|
88
|
+
}
|
|
89
|
+
const api = new ButterflyAPI(authConfig);
|
|
90
|
+
addLog(`Connecting to: ${authConfig.endpoint}`, 'info');
|
|
91
|
+
await api.authenticate();
|
|
92
|
+
addLog('Authentication successful', 'success');
|
|
93
|
+
setStatus('generating-quotes');
|
|
94
|
+
addLog('POST /admin/bfy_ai_task/response (generate-waiting-quotes)', 'info');
|
|
95
|
+
const quotesResponse = await api.aiTaskResponse('generate-waiting-quotes', {
|
|
96
|
+
title: 'Create Objects',
|
|
97
|
+
prompt: promptContent
|
|
98
|
+
});
|
|
99
|
+
if (!quotesResponse.success) {
|
|
100
|
+
throw new Error(quotesResponse.message || 'Failed to generate quotes');
|
|
101
|
+
}
|
|
102
|
+
addLog(`Quotes generated: ${quotesResponse.message?.quotes?.length || 0} messages`, 'success');
|
|
103
|
+
if (quotesResponse.message?.quotes) {
|
|
104
|
+
setQuotes(quotesResponse.message.quotes);
|
|
105
|
+
setTitle(quotesResponse.message.title || 'Processing');
|
|
106
|
+
}
|
|
107
|
+
setStatus('waiting-quotes');
|
|
108
|
+
let aiLogId;
|
|
109
|
+
let pollAttempt = 0;
|
|
110
|
+
addLog('POST /admin/bfy_ai_task/status (waiting for task ID)', 'info');
|
|
111
|
+
while (true) {
|
|
112
|
+
pollAttempt++;
|
|
113
|
+
setPollCount(pollAttempt);
|
|
114
|
+
const statusResponse = await api.aiTaskStatus();
|
|
115
|
+
addLog(`Status response: ${JSON.stringify(statusResponse)}`, 'info');
|
|
116
|
+
if (statusResponse.success && statusResponse.id) {
|
|
117
|
+
aiLogId = statusResponse.id;
|
|
118
|
+
addLog(`Task ID received: ${aiLogId}`, 'success');
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
addLog(`Status check #${pollAttempt}: Not ready yet, waiting...`, 'warning');
|
|
122
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
123
|
+
}
|
|
124
|
+
setStatus('creating-objects');
|
|
125
|
+
addLog(`POST /admin/bfy_ai_task/response (create-objects, request_ai_log_id: ${aiLogId})`, 'info');
|
|
126
|
+
await api.aiTaskResponse('create-objects', {
|
|
127
|
+
prompt: promptContent,
|
|
128
|
+
response: '',
|
|
129
|
+
ai_model_id: '1'
|
|
130
|
+
}, { requestAiLogId: aiLogId });
|
|
131
|
+
addLog('Create objects request sent', 'success');
|
|
132
|
+
setStatus('waiting-create');
|
|
133
|
+
pollAttempt = 0;
|
|
134
|
+
addLog(`POST /admin/bfy_ai_task/status (ai_log_id: ${aiLogId})`, 'info');
|
|
135
|
+
while (true) {
|
|
136
|
+
pollAttempt++;
|
|
137
|
+
setPollCount(pollAttempt);
|
|
138
|
+
const statusResponse = await api.aiTaskStatus(aiLogId);
|
|
139
|
+
addLog(`Status response: ${JSON.stringify(statusResponse)}`, 'info');
|
|
140
|
+
if (statusResponse.success) {
|
|
141
|
+
addLog('AI task completed!', 'success');
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
addLog(`Status check #${pollAttempt}: AI is still working...`, 'warning');
|
|
145
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
146
|
+
}
|
|
147
|
+
setStatus('fetching-result');
|
|
148
|
+
addLog(`POST /admin/bfy_ai_task/response (fetching result, ai_log_id: ${aiLogId})`, 'info');
|
|
149
|
+
const resultResponse = await api.aiTaskResponse('create-objects', {
|
|
150
|
+
response: '',
|
|
151
|
+
ai_model_id: '1'
|
|
152
|
+
}, { aiLogId });
|
|
153
|
+
if (!resultResponse.success) {
|
|
154
|
+
throw new Error(resultResponse.message || 'Failed to get AI response');
|
|
155
|
+
}
|
|
156
|
+
addLog('AI response received', 'success');
|
|
157
|
+
const message = resultResponse.message;
|
|
158
|
+
addLog(`Objects: ${message.objects?.length || 0}, Specs: ${message.specs?.length || 0}`, 'info');
|
|
159
|
+
setPreviewData({
|
|
160
|
+
objects: message.objects || [],
|
|
161
|
+
specs: message.specs || []
|
|
162
|
+
});
|
|
163
|
+
setPendingExecute({ api, message });
|
|
164
|
+
setStatus('confirming');
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
addLog(`Error: ${error.message}`, 'error');
|
|
168
|
+
setErrorMessage(error.message || 'Unknown error');
|
|
169
|
+
setStatus('error');
|
|
170
|
+
setTimeout(() => process.exit(1), 3000);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
run();
|
|
174
|
+
}, [options.file]);
|
|
175
|
+
const getStatusText = () => {
|
|
176
|
+
switch (status) {
|
|
177
|
+
case 'loading': return 'Loading...';
|
|
178
|
+
case 'authenticating': return 'Authenticating...';
|
|
179
|
+
case 'generating-quotes': return 'Generating loading messages...';
|
|
180
|
+
case 'waiting-quotes': return `${title || 'Processing...'} (poll #${pollCount})`;
|
|
181
|
+
case 'creating-objects': return title || 'Creating objects...';
|
|
182
|
+
case 'waiting-create': return `${title || 'AI is working...'} (poll #${pollCount})`;
|
|
183
|
+
case 'fetching-result': return 'Fetching results...';
|
|
184
|
+
case 'confirming': return 'Waiting for confirmation...';
|
|
185
|
+
case 'executing': return 'Executing action...';
|
|
186
|
+
case 'success': return resultMessage;
|
|
187
|
+
case 'cancelled': return 'Operation cancelled.';
|
|
188
|
+
case 'error': return `Error: ${errorMessage}`;
|
|
189
|
+
default: return 'Processing...';
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const isProcessing = ['waiting-quotes', 'creating-objects', 'waiting-create'].includes(status);
|
|
193
|
+
const currentQuote = quotes[currentQuoteIndex];
|
|
194
|
+
const getLogColor = (type) => {
|
|
195
|
+
switch (type) {
|
|
196
|
+
case 'success': return 'green';
|
|
197
|
+
case 'warning': return 'yellow';
|
|
198
|
+
case 'error': return 'red';
|
|
199
|
+
default: return 'gray';
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
203
|
+
React.createElement(Box, null,
|
|
204
|
+
status !== 'success' && status !== 'error' && status !== 'cancelled' && status !== 'confirming' && (React.createElement(Text, { color: "cyan" },
|
|
205
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
206
|
+
' ')),
|
|
207
|
+
status === 'success' && React.createElement(Text, { color: "green" }, "\u2713 "),
|
|
208
|
+
status === 'error' && React.createElement(Text, { color: "red" }, "\u2717 "),
|
|
209
|
+
status === 'cancelled' && React.createElement(Text, { color: "yellow" }, "\u2298 "),
|
|
210
|
+
status === 'confirming' && React.createElement(Text, { color: "magenta" }, "? "),
|
|
211
|
+
React.createElement(Text, { color: status === 'error' ? 'red' : status === 'success' ? 'green' : status === 'cancelled' ? 'yellow' : 'yellow' }, getStatusText())),
|
|
212
|
+
isProcessing && currentQuote && (React.createElement(Box, { marginTop: 1, marginLeft: 2 },
|
|
213
|
+
React.createElement(Text, { color: "cyan", italic: true }, currentQuote))),
|
|
214
|
+
status === 'confirming' && previewData && (React.createElement(Box, { flexDirection: "column", marginTop: 1, borderStyle: "double", borderColor: "magenta", paddingX: 1 },
|
|
215
|
+
React.createElement(Text, { color: "magenta", bold: true }, "Onizleme - Olusturulacak Tablolar:"),
|
|
216
|
+
(() => {
|
|
217
|
+
const parseBacktickData = (data) => {
|
|
218
|
+
if (Array.isArray(data))
|
|
219
|
+
return data;
|
|
220
|
+
if (typeof data !== 'string')
|
|
221
|
+
return [];
|
|
222
|
+
const lines = data.split('\n').filter(l => l.trim());
|
|
223
|
+
if (lines.length < 2)
|
|
224
|
+
return [];
|
|
225
|
+
const headers = lines[0].split('`');
|
|
226
|
+
return lines.slice(1).map(line => {
|
|
227
|
+
const values = line.split('`');
|
|
228
|
+
const obj = {};
|
|
229
|
+
headers.forEach((h, i) => {
|
|
230
|
+
obj[h] = values[i] || '';
|
|
231
|
+
});
|
|
232
|
+
return obj;
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
const objects = parseBacktickData(previewData.objects);
|
|
236
|
+
const specs = parseBacktickData(previewData.specs);
|
|
237
|
+
const specsByTable = {};
|
|
238
|
+
specs.forEach(spec => {
|
|
239
|
+
const tableName = spec.table_name || spec.object_id || 'unknown';
|
|
240
|
+
if (!specsByTable[tableName]) {
|
|
241
|
+
specsByTable[tableName] = [];
|
|
242
|
+
}
|
|
243
|
+
specsByTable[tableName].push(spec);
|
|
244
|
+
});
|
|
245
|
+
return objects.map((obj, index) => {
|
|
246
|
+
const tableName = obj.table_name || '';
|
|
247
|
+
const tableSpecs = specsByTable[tableName] || [];
|
|
248
|
+
return (React.createElement(Box, { key: index, flexDirection: "column", marginTop: 1 },
|
|
249
|
+
React.createElement(Box, null,
|
|
250
|
+
React.createElement(Text, { color: "green", bold: true }, "Tablo: "),
|
|
251
|
+
React.createElement(Text, { color: "white", bold: true }, obj.name || tableName),
|
|
252
|
+
React.createElement(Text, { color: "gray" },
|
|
253
|
+
" (",
|
|
254
|
+
tableName,
|
|
255
|
+
")")),
|
|
256
|
+
obj.menu_path && (React.createElement(Box, { marginLeft: 2 },
|
|
257
|
+
React.createElement(Text, { color: "gray" },
|
|
258
|
+
"Menu: ",
|
|
259
|
+
obj.menu_path))),
|
|
260
|
+
tableSpecs.length > 0 && (React.createElement(Box, { flexDirection: "column", marginLeft: 2, marginTop: 0 },
|
|
261
|
+
React.createElement(Text, { color: "yellow" }, "Alanlar:"),
|
|
262
|
+
tableSpecs.slice(0, 8).map((spec, specIndex) => (React.createElement(Box, { key: specIndex, marginLeft: 2 },
|
|
263
|
+
React.createElement(Text, { color: "cyan" },
|
|
264
|
+
"- ",
|
|
265
|
+
spec.name || spec.column_name || '-'),
|
|
266
|
+
React.createElement(Text, { color: "gray" },
|
|
267
|
+
" (",
|
|
268
|
+
spec.type || '-',
|
|
269
|
+
")"),
|
|
270
|
+
spec.required === '1' && React.createElement(Text, { color: "red" }, " *")))),
|
|
271
|
+
tableSpecs.length > 8 && (React.createElement(Box, { marginLeft: 2 },
|
|
272
|
+
React.createElement(Text, { color: "gray" },
|
|
273
|
+
"... ve ",
|
|
274
|
+
tableSpecs.length - 8,
|
|
275
|
+
" alan daha")))))));
|
|
276
|
+
});
|
|
277
|
+
})(),
|
|
278
|
+
React.createElement(Box, { marginTop: 1 },
|
|
279
|
+
React.createElement(Text, { color: "yellow", bold: true }, "Onaylamak icin Y, iptal icin N tusuna basin: ")))),
|
|
280
|
+
React.createElement(Box, { flexDirection: "column", marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 },
|
|
281
|
+
React.createElement(Text, { color: "white", bold: true }, "Logs:"),
|
|
282
|
+
logs.map((log, index) => (React.createElement(Box, { key: index },
|
|
283
|
+
React.createElement(Text, { color: "gray" },
|
|
284
|
+
"[",
|
|
285
|
+
log.timestamp,
|
|
286
|
+
"] "),
|
|
287
|
+
React.createElement(Text, { color: getLogColor(log.type) }, log.message)))),
|
|
288
|
+
logs.length === 0 && React.createElement(Text, { color: "gray" }, "No logs yet..."))));
|
|
289
|
+
};
|
|
290
|
+
export default (options) => {
|
|
291
|
+
render(React.createElement(CreateObjectCommand, { options: options }));
|
|
292
|
+
};
|
|
293
|
+
//# sourceMappingURL=create-object.js.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, Box, Text } from 'ink';
|
|
3
|
+
import Spinner from 'ink-spinner';
|
|
4
|
+
import { existsSync, readFileSync } from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { ButterflyAPI } from '../utils/api.js';
|
|
7
|
+
import { loadAuthConfig } from '../utils/auth.js';
|
|
8
|
+
const CreateReportCommand = ({ options }) => {
|
|
9
|
+
const [status, setStatus] = React.useState('loading');
|
|
10
|
+
const [errorMessage, setErrorMessage] = React.useState('');
|
|
11
|
+
const [logs, setLogs] = React.useState([]);
|
|
12
|
+
const [reportUrl, setReportUrl] = React.useState('');
|
|
13
|
+
const addLog = (message, type = 'info') => {
|
|
14
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
15
|
+
setLogs(prev => [...prev.slice(-15), { timestamp, message, type }]);
|
|
16
|
+
};
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
const run = async () => {
|
|
19
|
+
try {
|
|
20
|
+
const folderPath = path.isAbsolute(options.file)
|
|
21
|
+
? options.file
|
|
22
|
+
: path.join(process.cwd(), options.file);
|
|
23
|
+
addLog(`Checking folder: ${folderPath}`, 'info');
|
|
24
|
+
if (!existsSync(folderPath)) {
|
|
25
|
+
throw new Error(`Folder not found: ${folderPath}`);
|
|
26
|
+
}
|
|
27
|
+
setStatus('checking-files');
|
|
28
|
+
const queryCodePath = path.join(folderPath, 'query_code.twig');
|
|
29
|
+
const scriptJsPath = path.join(folderPath, 'script.js');
|
|
30
|
+
if (!existsSync(queryCodePath)) {
|
|
31
|
+
throw new Error(`Required file not found: query_code.twig`);
|
|
32
|
+
}
|
|
33
|
+
addLog('Found: query_code.twig', 'success');
|
|
34
|
+
if (!existsSync(scriptJsPath)) {
|
|
35
|
+
throw new Error(`Required file not found: script.js`);
|
|
36
|
+
}
|
|
37
|
+
addLog('Found: script.js', 'success');
|
|
38
|
+
const queryCode = readFileSync(queryCodePath, 'utf-8');
|
|
39
|
+
const jsCode = readFileSync(scriptJsPath, 'utf-8');
|
|
40
|
+
addLog(`Files loaded (query: ${queryCode.length} bytes, js: ${jsCode.length} bytes)`, 'success');
|
|
41
|
+
const folderName = path.basename(folderPath);
|
|
42
|
+
const alias = folderName;
|
|
43
|
+
const friendlyName = folderName
|
|
44
|
+
.split('-')
|
|
45
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
46
|
+
.join(' ');
|
|
47
|
+
addLog(`Report name: ${friendlyName}, alias: ${alias}`, 'info');
|
|
48
|
+
setStatus('authenticating');
|
|
49
|
+
addLog('Loading authentication config...', 'info');
|
|
50
|
+
const authConfig = await loadAuthConfig();
|
|
51
|
+
if (!authConfig) {
|
|
52
|
+
throw new Error('Not authenticated. Run "butterfly-cli setup" first.');
|
|
53
|
+
}
|
|
54
|
+
const api = new ButterflyAPI(authConfig);
|
|
55
|
+
addLog(`Connecting to: ${authConfig.endpoint}`, 'info');
|
|
56
|
+
await api.authenticate();
|
|
57
|
+
addLog('Authentication successful', 'success');
|
|
58
|
+
setStatus('creating-report');
|
|
59
|
+
addLog('POST /admin/ajax/cms_object/operation?do=cms_reports__add', 'info');
|
|
60
|
+
const reportResponse = await api.createReport({
|
|
61
|
+
name: friendlyName,
|
|
62
|
+
alias: alias,
|
|
63
|
+
type: '0',
|
|
64
|
+
cache_duration: '',
|
|
65
|
+
cms_report_category_id: '0',
|
|
66
|
+
detail_query: '',
|
|
67
|
+
others: '',
|
|
68
|
+
bfy_workspace_id: '0',
|
|
69
|
+
object_id: '0',
|
|
70
|
+
permission_level: '',
|
|
71
|
+
hide_in_list: '0'
|
|
72
|
+
});
|
|
73
|
+
addLog(`Response: ${JSON.stringify(reportResponse)}`, 'info');
|
|
74
|
+
if (!reportResponse.success) {
|
|
75
|
+
throw new Error(reportResponse.message || 'Failed to create report');
|
|
76
|
+
}
|
|
77
|
+
const reportId = reportResponse.data?.id || reportResponse.id;
|
|
78
|
+
addLog(`Report created with ID: ${reportId}`, 'success');
|
|
79
|
+
setStatus('creating-query');
|
|
80
|
+
addLog('POST /admin/ajax/cms_object/operation?do=cms_report_queries__add', 'info');
|
|
81
|
+
const queryResponse = await api.createReportQuery({
|
|
82
|
+
cms_report_id: reportId,
|
|
83
|
+
title: friendlyName,
|
|
84
|
+
query: queryCode,
|
|
85
|
+
column_size: '1',
|
|
86
|
+
system_name: alias,
|
|
87
|
+
js_code: jsCode,
|
|
88
|
+
show_title: '1'
|
|
89
|
+
});
|
|
90
|
+
addLog(`Response: ${JSON.stringify(queryResponse)}`, 'info');
|
|
91
|
+
if (!queryResponse.success) {
|
|
92
|
+
throw new Error(queryResponse.message || 'Failed to create report query');
|
|
93
|
+
}
|
|
94
|
+
addLog('Report query created successfully', 'success');
|
|
95
|
+
const fullUrl = `${authConfig.endpoint}admin/cms_report/view/${reportId}`;
|
|
96
|
+
setReportUrl(fullUrl);
|
|
97
|
+
addLog(`Report ready: ${fullUrl}`, 'success');
|
|
98
|
+
setStatus('success');
|
|
99
|
+
setTimeout(() => process.exit(0), 3000);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
addLog(`Error: ${error.message}`, 'error');
|
|
103
|
+
setErrorMessage(error.message || 'Unknown error');
|
|
104
|
+
setStatus('error');
|
|
105
|
+
setTimeout(() => process.exit(1), 3000);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
run();
|
|
109
|
+
}, [options.file]);
|
|
110
|
+
const getStatusText = () => {
|
|
111
|
+
switch (status) {
|
|
112
|
+
case 'loading': return 'Loading...';
|
|
113
|
+
case 'authenticating': return 'Authenticating...';
|
|
114
|
+
case 'checking-files': return 'Checking required files...';
|
|
115
|
+
case 'creating-report': return 'Creating report...';
|
|
116
|
+
case 'creating-query': return 'Creating report query...';
|
|
117
|
+
case 'success': return 'Report created successfully!';
|
|
118
|
+
case 'error': return `Error: ${errorMessage}`;
|
|
119
|
+
default: return 'Processing...';
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const getLogColor = (type) => {
|
|
123
|
+
switch (type) {
|
|
124
|
+
case 'success': return 'green';
|
|
125
|
+
case 'warning': return 'yellow';
|
|
126
|
+
case 'error': return 'red';
|
|
127
|
+
default: return 'gray';
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
131
|
+
React.createElement(Box, null,
|
|
132
|
+
status !== 'success' && status !== 'error' && (React.createElement(Text, { color: "cyan" },
|
|
133
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
134
|
+
' ')),
|
|
135
|
+
status === 'success' && React.createElement(Text, { color: "green" }, "\u2713 "),
|
|
136
|
+
status === 'error' && React.createElement(Text, { color: "red" }, "\u2717 "),
|
|
137
|
+
React.createElement(Text, { color: status === 'error' ? 'red' : status === 'success' ? 'green' : 'yellow' }, getStatusText())),
|
|
138
|
+
status === 'success' && reportUrl && (React.createElement(Box, { marginTop: 1 },
|
|
139
|
+
React.createElement(Text, { color: "cyan", bold: true }, "Report URL: "),
|
|
140
|
+
React.createElement(Text, { color: "white" }, reportUrl))),
|
|
141
|
+
React.createElement(Box, { flexDirection: "column", marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 },
|
|
142
|
+
React.createElement(Text, { color: "white", bold: true }, "Logs:"),
|
|
143
|
+
logs.map((log, index) => (React.createElement(Box, { key: index },
|
|
144
|
+
React.createElement(Text, { color: "gray" },
|
|
145
|
+
"[",
|
|
146
|
+
log.timestamp,
|
|
147
|
+
"] "),
|
|
148
|
+
React.createElement(Text, { color: getLogColor(log.type) }, log.message)))),
|
|
149
|
+
logs.length === 0 && React.createElement(Text, { color: "gray" }, "No logs yet..."))));
|
|
150
|
+
};
|
|
151
|
+
export default (options) => {
|
|
152
|
+
render(React.createElement(CreateReportCommand, { options: options }));
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=create-report.js.map
|