@audashai/cli 1.0.0
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/README.md +57 -0
- package/bin/audashai.js +3 -0
- package/dist/commands/add.d.ts +1 -0
- package/dist/commands/add.js +73 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +62 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +16 -0
- package/dist/templates/components/AudashAIChatBot.d.ts +8 -0
- package/dist/templates/components/AudashAIChatBot.js +14 -0
- package/dist/templates/components/AudashAIInput.d.ts +1 -0
- package/dist/templates/components/AudashAIInput.js +114 -0
- package/dist/templates/components/AudashAIOutput.d.ts +1 -0
- package/dist/templates/components/AudashAIOutput.js +66 -0
- package/dist/templates/components/BarChart.d.ts +2 -0
- package/dist/templates/components/BarChart.js +41 -0
- package/dist/templates/components/ErrorDisplay.d.ts +4 -0
- package/dist/templates/components/ErrorDisplay.js +61 -0
- package/dist/templates/components/LineChart.d.ts +2 -0
- package/dist/templates/components/LineChart.js +41 -0
- package/dist/templates/context/AudashAIContext.d.ts +1 -0
- package/dist/templates/context/AudashAIContext.js +5 -0
- package/dist/templates/context/AudashAIProvider.d.ts +7 -0
- package/dist/templates/context/AudashAIProvider.js +50 -0
- package/dist/templates/hooks/useAudashAI.d.ts +1 -0
- package/dist/templates/hooks/useAudashAI.js +13 -0
- package/dist/templates/index.d.ts +9 -0
- package/dist/templates/index.js +38 -0
- package/dist/templates/types/index.d.ts +39 -0
- package/dist/templates/types/index.js +2 -0
- package/dist/templates/utils/api.d.ts +3 -0
- package/dist/templates/utils/api.js +52 -0
- package/dist/utils/config.d.ts +6 -0
- package/dist/utils/config.js +15 -0
- package/dist/utils/copy-files.d.ts +1 -0
- package/dist/utils/copy-files.js +14 -0
- package/package.json +42 -0
- package/templates/components/AudashAIChatBot.tsx +34 -0
- package/templates/components/AudashAIInput.tsx +181 -0
- package/templates/components/AudashAIOutput.tsx +90 -0
- package/templates/components/BarChart.tsx +113 -0
- package/templates/components/ErrorDisplay.tsx +87 -0
- package/templates/components/LineChart.tsx +105 -0
- package/templates/context/AudashAIContext.tsx +18 -0
- package/templates/context/AudashAIProvider.tsx +60 -0
- package/templates/hooks/useAudashAI.ts +10 -0
- package/templates/index.tsx +12 -0
- package/templates/types/index.ts +44 -0
- package/templates/utils/api.ts +57 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AudashAIProvider = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const api_1 = require("../utils/api");
|
|
6
|
+
const AudashAIContext_1 = require("./AudashAIContext");
|
|
7
|
+
const AudashAIProvider = ({ config, children }) => {
|
|
8
|
+
const [chartData, setChartData] = (0, react_1.useState)(null);
|
|
9
|
+
const [descData, setDescData] = (0, react_1.useState)(null);
|
|
10
|
+
const [loading, setLoading] = (0, react_1.useState)(false);
|
|
11
|
+
const [errorInfo, setErrorInfo] = (0, react_1.useState)(null);
|
|
12
|
+
const [isRefreshing, setIsRefreshing] = (0, react_1.useState)(false);
|
|
13
|
+
const refreshData = async () => {
|
|
14
|
+
if (!config.dataApiUrl || !config.endpoints || config.endpoints.length === 0) {
|
|
15
|
+
console.warn('Data API URL or endpoints not configured');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
setIsRefreshing(true);
|
|
19
|
+
try {
|
|
20
|
+
const results = await Promise.allSettled(config.endpoints.map((endpoint) => (0, api_1.ingestSingleEndpoint)(endpoint, config)));
|
|
21
|
+
const successful = results.filter((r) => r.status === 'fulfilled').length;
|
|
22
|
+
const failed = results.filter((r) => r.status === 'rejected').length;
|
|
23
|
+
console.log(`✅ Data refresh completed: ${successful} success, ${failed} failed`);
|
|
24
|
+
if (failed > 0) {
|
|
25
|
+
console.warn('Some endpoints failed to ingest');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error('Error during data refresh:', error);
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
setIsRefreshing(false);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const value = {
|
|
36
|
+
config,
|
|
37
|
+
chartData,
|
|
38
|
+
setChartData,
|
|
39
|
+
descData,
|
|
40
|
+
setDescData,
|
|
41
|
+
loading,
|
|
42
|
+
setLoading,
|
|
43
|
+
errorInfo,
|
|
44
|
+
setErrorInfo,
|
|
45
|
+
refreshData,
|
|
46
|
+
isRefreshing,
|
|
47
|
+
};
|
|
48
|
+
return <AudashAIContext_1.AudashAIContext.Provider value={value}>{children}</AudashAIContext_1.AudashAIContext.Provider>;
|
|
49
|
+
};
|
|
50
|
+
exports.AudashAIProvider = AudashAIProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useAudashAI: () => any;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAudashAI = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const AudashAIContext_1 = require("../context/AudashAIContext");
|
|
6
|
+
const useAudashAI = () => {
|
|
7
|
+
const context = (0, react_1.useContext)(AudashAIContext_1.AudashAIContext);
|
|
8
|
+
if (context === undefined) {
|
|
9
|
+
throw new Error('useAudashAI must be used within AudashAIProvider');
|
|
10
|
+
}
|
|
11
|
+
return context;
|
|
12
|
+
};
|
|
13
|
+
exports.useAudashAI = useAudashAI;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as AudashAIChatbot } from './components/AudashAIChatBot';
|
|
2
|
+
export { AudashAIInput } from './components/AudashAIInput';
|
|
3
|
+
export { AudashAIOutput } from './components/AudashAIOutput';
|
|
4
|
+
export { BarChart } from './components/BarChart';
|
|
5
|
+
export { LineChart } from './components/LineChart';
|
|
6
|
+
export { ErrorDisplay } from './components/ErrorDisplay';
|
|
7
|
+
export { AudashAIContext } from './context/AudashAIContext';
|
|
8
|
+
export { useAudashAI } from './hooks/useAudashAI';
|
|
9
|
+
export * from './types';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.useAudashAI = exports.AudashAIContext = exports.ErrorDisplay = exports.LineChart = exports.BarChart = exports.AudashAIOutput = exports.AudashAIInput = exports.AudashAIChatbot = void 0;
|
|
21
|
+
// src/index.tsx
|
|
22
|
+
var AudashAIChatBot_1 = require("./components/AudashAIChatBot");
|
|
23
|
+
Object.defineProperty(exports, "AudashAIChatbot", { enumerable: true, get: function () { return __importDefault(AudashAIChatBot_1).default; } });
|
|
24
|
+
var AudashAIInput_1 = require("./components/AudashAIInput");
|
|
25
|
+
Object.defineProperty(exports, "AudashAIInput", { enumerable: true, get: function () { return AudashAIInput_1.AudashAIInput; } });
|
|
26
|
+
var AudashAIOutput_1 = require("./components/AudashAIOutput");
|
|
27
|
+
Object.defineProperty(exports, "AudashAIOutput", { enumerable: true, get: function () { return AudashAIOutput_1.AudashAIOutput; } });
|
|
28
|
+
var BarChart_1 = require("./components/BarChart");
|
|
29
|
+
Object.defineProperty(exports, "BarChart", { enumerable: true, get: function () { return BarChart_1.BarChart; } });
|
|
30
|
+
var LineChart_1 = require("./components/LineChart");
|
|
31
|
+
Object.defineProperty(exports, "LineChart", { enumerable: true, get: function () { return LineChart_1.LineChart; } });
|
|
32
|
+
var ErrorDisplay_1 = require("./components/ErrorDisplay");
|
|
33
|
+
Object.defineProperty(exports, "ErrorDisplay", { enumerable: true, get: function () { return ErrorDisplay_1.ErrorDisplay; } });
|
|
34
|
+
var AudashAIContext_1 = require("./context/AudashAIContext");
|
|
35
|
+
Object.defineProperty(exports, "AudashAIContext", { enumerable: true, get: function () { return AudashAIContext_1.AudashAIContext; } });
|
|
36
|
+
var useAudashAI_1 = require("./hooks/useAudashAI");
|
|
37
|
+
Object.defineProperty(exports, "useAudashAI", { enumerable: true, get: function () { return useAudashAI_1.useAudashAI; } });
|
|
38
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface ChartDataset {
|
|
2
|
+
label: string;
|
|
3
|
+
data: number[];
|
|
4
|
+
}
|
|
5
|
+
export interface ChartData {
|
|
6
|
+
visualization_type: 'bar' | 'line';
|
|
7
|
+
labels: string[];
|
|
8
|
+
datasets: ChartDataset[];
|
|
9
|
+
}
|
|
10
|
+
export interface ErrorInfo {
|
|
11
|
+
error_type: string;
|
|
12
|
+
message: string;
|
|
13
|
+
reason?: string;
|
|
14
|
+
suggestion?: string;
|
|
15
|
+
available_data_summary?: string;
|
|
16
|
+
documents_found: number;
|
|
17
|
+
}
|
|
18
|
+
export interface QueryResponse {
|
|
19
|
+
response_type: 'visualization' | 'description' | 'error';
|
|
20
|
+
chart_config?: ChartData;
|
|
21
|
+
text_response?: string;
|
|
22
|
+
processing_info?: {
|
|
23
|
+
error_type?: string;
|
|
24
|
+
message?: string;
|
|
25
|
+
suggestion?: string;
|
|
26
|
+
available_data_summary?: string;
|
|
27
|
+
documents_found?: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface AudashAIConfig {
|
|
31
|
+
apiKey: string;
|
|
32
|
+
apiUrl: string;
|
|
33
|
+
dataApiUrl?: string;
|
|
34
|
+
endpoints?: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface ChartProps {
|
|
37
|
+
labels: string[];
|
|
38
|
+
datasets: ChartDataset[];
|
|
39
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchDataFromAPI = fetchDataFromAPI;
|
|
4
|
+
exports.ingestSingleEndpoint = ingestSingleEndpoint;
|
|
5
|
+
async function fetchDataFromAPI(endpoint, baseUrl) {
|
|
6
|
+
try {
|
|
7
|
+
const response = await fetch(`${baseUrl}/${endpoint}`, {
|
|
8
|
+
method: 'GET',
|
|
9
|
+
headers: {
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`);
|
|
15
|
+
}
|
|
16
|
+
return await response.json();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.error('Error fetching data from API:', error);
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function ingestSingleEndpoint(endpoint, config) {
|
|
24
|
+
if (!config.dataApiUrl) {
|
|
25
|
+
throw new Error('Data API URL not configured');
|
|
26
|
+
}
|
|
27
|
+
let data;
|
|
28
|
+
try {
|
|
29
|
+
data = await fetchDataFromAPI(endpoint, config.dataApiUrl);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(`❌ Failed to fetch data from ${endpoint}:`, error);
|
|
33
|
+
throw new Error(`Failed to fetch data from ${endpoint}`);
|
|
34
|
+
}
|
|
35
|
+
const response = await fetch(`${config.apiUrl}/ingest`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
data: data.data || data,
|
|
42
|
+
source_name: endpoint,
|
|
43
|
+
api_key: config.apiKey,
|
|
44
|
+
metadata: { description: `${endpoint} data from API` },
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
const errorBody = await response.text();
|
|
49
|
+
throw new Error(`HTTP error! Status: ${response.status}, Endpoint: ${endpoint}, Body: ${errorBody}`);
|
|
50
|
+
}
|
|
51
|
+
return await response.json();
|
|
52
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getConfig = getConfig;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
async function getConfig() {
|
|
10
|
+
const configPath = path_1.default.join(process.cwd(), 'audashai.config.json');
|
|
11
|
+
if (!(await fs_extra_1.default.pathExists(configPath))) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return await fs_extra_1.default.readJSON(configPath);
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function copyTemplate(templatePath: string, targetPath: string): Promise<void>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.copyTemplate = copyTemplate;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
async function copyTemplate(templatePath, targetPath) {
|
|
10
|
+
// Path ke templates di root folder (bukan di src)
|
|
11
|
+
const sourcePath = path_1.default.join(__dirname, '../../templates', templatePath);
|
|
12
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(targetPath));
|
|
13
|
+
await fs_extra_1.default.copy(sourcePath, targetPath);
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@audashai/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool to add AudashAI components to your React project",
|
|
5
|
+
"bin": {
|
|
6
|
+
"audashai": "./bin/audashai.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"bin",
|
|
11
|
+
"templates",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"cli",
|
|
21
|
+
"audashai",
|
|
22
|
+
"react",
|
|
23
|
+
"components"
|
|
24
|
+
],
|
|
25
|
+
"author": "AudashAI Team",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"type": "commonjs",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"chalk": "^5.6.2",
|
|
30
|
+
"commander": "^14.0.2",
|
|
31
|
+
"execa": "^9.6.0",
|
|
32
|
+
"fs-extra": "^11.3.2",
|
|
33
|
+
"inquirer": "^13.0.1",
|
|
34
|
+
"ora": "^9.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/fs-extra": "^11.0.4",
|
|
38
|
+
"@types/inquirer": "^9.0.9",
|
|
39
|
+
"@types/node": "^24.10.1",
|
|
40
|
+
"typescript": "^5.9.3"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AudashAIProvider } from '../context/AudashAIProvider';
|
|
2
|
+
import { AudashAIInput } from './AudashAIInput';
|
|
3
|
+
import { AudashAIOutput } from './AudashAIOutput';
|
|
4
|
+
|
|
5
|
+
interface AudashAIChatbotProps {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
apiUrl: string;
|
|
8
|
+
dataApiUrl?: string;
|
|
9
|
+
endpoints?: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const AudashAIChatbot: React.FC<AudashAIChatbotProps> = ({
|
|
13
|
+
apiKey,
|
|
14
|
+
apiUrl,
|
|
15
|
+
dataApiUrl,
|
|
16
|
+
endpoints,
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<AudashAIProvider config={{ apiKey, apiUrl, dataApiUrl, endpoints }}>
|
|
20
|
+
<div className="w-full max-w-7xl mx-auto p-4">
|
|
21
|
+
<div className="flex flex-col lg:flex-row-reverse gap-4 lg:h-[300px] items-stretch">
|
|
22
|
+
<div className="w-full lg:w-[350px] flex-shrink-0">
|
|
23
|
+
<AudashAIInput />
|
|
24
|
+
</div>
|
|
25
|
+
<div className="w-full flex-1 min-w-0 overflow-hidden">
|
|
26
|
+
<AudashAIOutput />
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</AudashAIProvider>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default AudashAIChatbot;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import type { QueryResponse } from '../types';
|
|
3
|
+
import { useAudashAI } from '../hooks/useAudashAI';
|
|
4
|
+
|
|
5
|
+
export const AudashAIInput: React.FC = () => {
|
|
6
|
+
const {
|
|
7
|
+
config,
|
|
8
|
+
setChartData,
|
|
9
|
+
setDescData,
|
|
10
|
+
setLoading,
|
|
11
|
+
setErrorInfo,
|
|
12
|
+
loading,
|
|
13
|
+
refreshData,
|
|
14
|
+
isRefreshing,
|
|
15
|
+
} = useAudashAI();
|
|
16
|
+
const [inputValue, setInputValue] = useState('');
|
|
17
|
+
const [error, setError] = useState('');
|
|
18
|
+
|
|
19
|
+
const handleSubmit = async () => {
|
|
20
|
+
const wordCount = inputValue.trim().split(/\s+/).length;
|
|
21
|
+
if (inputValue.trim() === '' || wordCount < 3) {
|
|
22
|
+
setError('Minimal 3 kata.');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setLoading(true);
|
|
27
|
+
setChartData(null);
|
|
28
|
+
setDescData(null);
|
|
29
|
+
setErrorInfo(null);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const response = await fetch(`${config.apiUrl}/query`, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: {
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify({
|
|
38
|
+
query: inputValue,
|
|
39
|
+
api_key: config.apiKey,
|
|
40
|
+
limit: 10,
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const result: QueryResponse = await response.json();
|
|
45
|
+
|
|
46
|
+
if (result.response_type === 'visualization') {
|
|
47
|
+
setChartData(result.chart_config || null);
|
|
48
|
+
setDescData(null);
|
|
49
|
+
setErrorInfo(null);
|
|
50
|
+
} else if (result.response_type === 'description') {
|
|
51
|
+
setDescData(result.text_response || null);
|
|
52
|
+
setChartData(null);
|
|
53
|
+
setErrorInfo(null);
|
|
54
|
+
} else if (result.response_type === 'error') {
|
|
55
|
+
setChartData(null);
|
|
56
|
+
setDescData(null);
|
|
57
|
+
setErrorInfo({
|
|
58
|
+
error_type: result.processing_info?.error_type || 'unknown_error',
|
|
59
|
+
message: result.text_response || 'Terjadi kesalahan',
|
|
60
|
+
reason: result.processing_info?.message,
|
|
61
|
+
suggestion: result.processing_info?.suggestion,
|
|
62
|
+
available_data_summary: result.processing_info?.available_data_summary,
|
|
63
|
+
documents_found: result.processing_info?.documents_found || 0,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
} catch (error: unknown) {
|
|
67
|
+
console.error('Error:', error);
|
|
68
|
+
const reason = error instanceof Error ? error.message : 'Network error';
|
|
69
|
+
|
|
70
|
+
setChartData(null);
|
|
71
|
+
setDescData(null);
|
|
72
|
+
setErrorInfo({
|
|
73
|
+
error_type: 'network_error',
|
|
74
|
+
message: 'Koneksi gagal',
|
|
75
|
+
reason,
|
|
76
|
+
suggestion: 'Coba lagi nanti',
|
|
77
|
+
documents_found: 0,
|
|
78
|
+
});
|
|
79
|
+
} finally {
|
|
80
|
+
setLoading(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
85
|
+
setInputValue(event.target.value);
|
|
86
|
+
if (error) setError('');
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const handleKeyPress = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
90
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
handleSubmit();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="bg-white h-full w-full rounded-xl border border-slate-200 shadow-sm flex flex-col">
|
|
98
|
+
<div className="px-4 py-3 border-b border-slate-100 flex justify-between items-center bg-slate-50/50 rounded-t-xl">
|
|
99
|
+
<div className="flex items-center gap-2">
|
|
100
|
+
<div className="h-2 w-2 rounded-full bg-emerald-500"></div>
|
|
101
|
+
<h1 className="text-xs font-bold text-slate-600 uppercase tracking-wider">AI Control</h1>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
{config.dataApiUrl && (
|
|
105
|
+
<button
|
|
106
|
+
onClick={refreshData}
|
|
107
|
+
disabled={isRefreshing}
|
|
108
|
+
className="hover:cursor-pointer text-xs text-slate-400 hover:text-indigo-600 transition-colors disabled:opacity-50"
|
|
109
|
+
title="Refresh Data Source"
|
|
110
|
+
>
|
|
111
|
+
<svg
|
|
112
|
+
className={`h-4 w-4 ${isRefreshing ? 'animate-spin text-indigo-500' : ''}`}
|
|
113
|
+
fill="none"
|
|
114
|
+
viewBox="0 0 24 24"
|
|
115
|
+
stroke="currentColor"
|
|
116
|
+
strokeWidth="2"
|
|
117
|
+
>
|
|
118
|
+
<path
|
|
119
|
+
strokeLinecap="round"
|
|
120
|
+
strokeLinejoin="round"
|
|
121
|
+
d="M4 4v5h.058M20 20v-5h-.058M4.05 9a9 9 0 0115.66-4.5M19.95 15a9 9 0 01-15.66 4.5"
|
|
122
|
+
/>
|
|
123
|
+
</svg>
|
|
124
|
+
</button>
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div className="p-4 flex flex-col flex-1 gap-3">
|
|
129
|
+
<div
|
|
130
|
+
className={`relative flex-1 rounded-xl border transition-all duration-200 flex flex-col ${
|
|
131
|
+
error
|
|
132
|
+
? 'border-red-300 bg-red-50/30'
|
|
133
|
+
: 'border-slate-200 bg-slate-50/30 focus-within:bg-white focus-within:ring-2 focus-within:ring-indigo-100 focus-within:border-indigo-400'
|
|
134
|
+
}`}
|
|
135
|
+
>
|
|
136
|
+
<textarea
|
|
137
|
+
value={inputValue}
|
|
138
|
+
onChange={handleInputChange}
|
|
139
|
+
onKeyPress={handleKeyPress}
|
|
140
|
+
className="w-full h-full bg-transparent px-4 py-3 text-sm text-slate-700 placeholder:text-slate-400 focus:outline-none resize-none"
|
|
141
|
+
placeholder="Ketik perintah analisis..."
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
{error && <p className="text-xs text-red-500 font-medium px-1">{error}</p>}
|
|
146
|
+
|
|
147
|
+
<button
|
|
148
|
+
onClick={handleSubmit}
|
|
149
|
+
disabled={loading || !inputValue.trim()}
|
|
150
|
+
className="w-full flex justify-center items-center gap-2 bg-indigo-600 hover:bg-indigo-700 disabled:bg-slate-300 text-white px-4 py-2.5 rounded-lg text-sm font-medium transition-all shadow-sm"
|
|
151
|
+
>
|
|
152
|
+
{loading ? (
|
|
153
|
+
<span className="flex items-center gap-2">
|
|
154
|
+
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
|
|
155
|
+
<circle
|
|
156
|
+
className="opacity-25"
|
|
157
|
+
cx="12"
|
|
158
|
+
cy="12"
|
|
159
|
+
r="10"
|
|
160
|
+
stroke="currentColor"
|
|
161
|
+
strokeWidth="4"
|
|
162
|
+
fill="none"
|
|
163
|
+
></circle>
|
|
164
|
+
<path
|
|
165
|
+
className="opacity-75"
|
|
166
|
+
fill="currentColor"
|
|
167
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
168
|
+
></path>
|
|
169
|
+
</svg>
|
|
170
|
+
Processing
|
|
171
|
+
</span>
|
|
172
|
+
) : (
|
|
173
|
+
'Analisis Data'
|
|
174
|
+
)}
|
|
175
|
+
</button>
|
|
176
|
+
|
|
177
|
+
<p className="text-center text-[10px] text-slate-400">AudashAI Analyst v1.0</p>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { useAudashAI } from '../hooks/useAudashAI';
|
|
2
|
+
import { BarChart } from './BarChart';
|
|
3
|
+
import { ErrorDisplay } from './ErrorDisplay';
|
|
4
|
+
import { LineChart } from './LineChart';
|
|
5
|
+
|
|
6
|
+
export const AudashAIOutput: React.FC = () => {
|
|
7
|
+
const { chartData, descData, loading, errorInfo } = useAudashAI();
|
|
8
|
+
|
|
9
|
+
const renderContent = () => {
|
|
10
|
+
if (loading) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="w-full h-full flex flex-col justify-center items-center p-8 space-y-6">
|
|
13
|
+
<div className="w-full h-48 bg-slate-100 rounded-lg animate-pulse relative overflow-hidden">
|
|
14
|
+
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/40 to-transparent -translate-x-full animate-[shimmer_1.5s_infinite]"></div>
|
|
15
|
+
</div>
|
|
16
|
+
<div className="w-full space-y-2">
|
|
17
|
+
<div className="h-3 bg-slate-100 rounded w-1/2 animate-pulse"></div>
|
|
18
|
+
<div className="h-3 bg-slate-100 rounded w-1/3 animate-pulse"></div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (errorInfo) {
|
|
25
|
+
return (
|
|
26
|
+
<div className="p-4 h-full flex flex-col justify-center">
|
|
27
|
+
<ErrorDisplay errorInfo={errorInfo} />
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (descData) {
|
|
33
|
+
return (
|
|
34
|
+
<div className="w-full h-full overflow-y-auto p-6 scrollbar-thin scrollbar-thumb-slate-200">
|
|
35
|
+
<div className="prose prose-sm max-w-none">
|
|
36
|
+
<h3 className="text-slate-800 font-semibold mb-3 flex items-center gap-2">
|
|
37
|
+
<span className="bg-indigo-100 p-1 rounded">✨</span> Hasil Analisis
|
|
38
|
+
</h3>
|
|
39
|
+
<p className="text-slate-600 leading-relaxed whitespace-pre-wrap">{descData}</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (chartData) {
|
|
46
|
+
return (
|
|
47
|
+
<div className="w-full h-full flex flex-col p-4 animate-in zoom-in-95 duration-300">
|
|
48
|
+
<div className="flex-1 relative min-h-[300px]">
|
|
49
|
+
{chartData.visualization_type === 'bar' && (
|
|
50
|
+
<BarChart labels={chartData.labels} datasets={chartData.datasets} />
|
|
51
|
+
)}
|
|
52
|
+
{chartData.visualization_type === 'line' && (
|
|
53
|
+
<LineChart labels={chartData.labels} datasets={chartData.datasets} />
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="h-full flex flex-col items-center justify-center text-center p-8 bg-slate-50/30">
|
|
62
|
+
<div className="w-16 h-16 bg-white rounded-full shadow-sm border border-slate-100 flex items-center justify-center mb-4">
|
|
63
|
+
<svg
|
|
64
|
+
className="h-8 w-8 text-slate-300"
|
|
65
|
+
fill="none"
|
|
66
|
+
viewBox="0 0 24 24"
|
|
67
|
+
stroke="currentColor"
|
|
68
|
+
>
|
|
69
|
+
<path
|
|
70
|
+
strokeLinecap="round"
|
|
71
|
+
strokeLinejoin="round"
|
|
72
|
+
strokeWidth={1}
|
|
73
|
+
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
|
74
|
+
/>
|
|
75
|
+
</svg>
|
|
76
|
+
</div>
|
|
77
|
+
<h3 className="text-sm font-semibold text-slate-700">Area Visualisasi</h3>
|
|
78
|
+
<p className="text-xs text-slate-400 mt-1 max-w-[200px]">
|
|
79
|
+
Grafik akan muncul di sini setelah Anda mengirimkan perintah.
|
|
80
|
+
</p>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div className="bg-white h-full w-full rounded-xl border border-slate-200 shadow-sm overflow-hidden flex flex-col">
|
|
87
|
+
{renderContent()}
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
};
|