Spaces:
Running
Running
Commit ·
4fb0c68
0
Parent(s):
Initial commit with clean Git repository
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .eslintrc.cjs +21 -0
- .gitattributes +1 -0
- .gitignore +3 -0
- README.md +8 -0
- index.html +12 -0
- package-lock.json +0 -0
- package.json +38 -0
- postcss.config.js +6 -0
- public/HiDigi.jpg +3 -0
- public/HiDigiH.jpg +3 -0
- public/bot.png +0 -0
- public/vite.svg +1 -0
- public/you.png +0 -0
- src/App.jsx +42 -0
- src/assets/react.svg +1 -0
- src/chartSetup.js +25 -0
- src/components/ChatMessage.jsx +47 -0
- src/components/ConfigForm.jsx +131 -0
- src/components/ERPCredentials.jsx +112 -0
- src/components/ForgotPassword.jsx +48 -0
- src/components/HomeDashboard.jsx +16 -0
- src/components/InvoiceForm.jsx +132 -0
- src/components/Login.jsx +84 -0
- src/components/Logout.jsx +20 -0
- src/components/OCRDashboard.jsx +147 -0
- src/components/OCRHeader.jsx +20 -0
- src/components/OCRQuota.jsx +16 -0
- src/components/OCRSidebar.jsx +67 -0
- src/components/OCRTemplate.jsx +212 -0
- src/components/PrivacyPolicy.jsx +16 -0
- src/components/ProtectedRoute.jsx +39 -0
- src/components/Register.jsx +171 -0
- src/components/SideBarGPT.jsx +18 -0
- src/components/Terms.jsx +16 -0
- src/components/TextToSpeech.jsx +153 -0
- src/data/languageVoices.jsx +25 -0
- src/index.css +95 -0
- src/layouts/MainLayout.jsx +15 -0
- src/main.jsx +12 -0
- src/pages/AnalyticsPage.jsx +80 -0
- src/pages/ChatPage.jsx +66 -0
- src/pages/DocumentManagement.jsx +117 -0
- src/pages/ERPCredentialPage.jsx +7 -0
- src/pages/Home.jsx +7 -0
- src/pages/LoginPage.jsx +7 -0
- src/pages/OCRDashboardPage.jsx +12 -0
- src/pages/OCRTemplatePage.jsx +7 -0
- src/pages/TextToSpeechPage.jsx +7 -0
- src/services/api.js +185 -0
- src/services/axiosInstance.js +44 -0
.eslintrc.cjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module.exports = {
|
| 2 |
+
root: true,
|
| 3 |
+
env: { browser: true, es2020: true },
|
| 4 |
+
extends: [
|
| 5 |
+
'eslint:recommended',
|
| 6 |
+
'plugin:react/recommended',
|
| 7 |
+
'plugin:react/jsx-runtime',
|
| 8 |
+
'plugin:react-hooks/recommended',
|
| 9 |
+
],
|
| 10 |
+
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
| 11 |
+
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
| 12 |
+
settings: { react: { version: '18.2' } },
|
| 13 |
+
plugins: ['react-refresh'],
|
| 14 |
+
rules: {
|
| 15 |
+
'react/jsx-no-target-blank': 'off',
|
| 16 |
+
'react-refresh/only-export-components': [
|
| 17 |
+
'warn',
|
| 18 |
+
{ allowConstantExport: true },
|
| 19 |
+
],
|
| 20 |
+
},
|
| 21 |
+
}
|
.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules/
|
| 2 |
+
dist/
|
| 3 |
+
.env
|
README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# React + Vite
|
| 2 |
+
|
| 3 |
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
| 4 |
+
|
| 5 |
+
Currently, two official plugins are available:
|
| 6 |
+
|
| 7 |
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
| 8 |
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
index.html
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<title>Vite + React</title>
|
| 8 |
+
<body>
|
| 9 |
+
<div id="root"></div>
|
| 10 |
+
<script type="module" src="/src/main.jsx"></script>
|
| 11 |
+
</body>
|
| 12 |
+
</html>
|
package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "frontendspeech",
|
| 3 |
+
"private": true,
|
| 4 |
+
"version": "0.0.0",
|
| 5 |
+
"type": "module",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "vite",
|
| 8 |
+
"build": "vite build",
|
| 9 |
+
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
| 10 |
+
"preview": "vite preview"
|
| 11 |
+
},
|
| 12 |
+
"dependencies": {
|
| 13 |
+
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
| 14 |
+
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
| 15 |
+
"@fortawesome/react-fontawesome": "^0.2.2",
|
| 16 |
+
"@heroicons/react": "^1.0.6",
|
| 17 |
+
"axios": "^1.7.2",
|
| 18 |
+
"chart.js": "^4.4.3",
|
| 19 |
+
"jwt-decode": "^4.0.0",
|
| 20 |
+
"react": "^18.3.1",
|
| 21 |
+
"react-chartjs-2": "^5.2.0",
|
| 22 |
+
"react-dom": "^18.3.1",
|
| 23 |
+
"react-router-dom": "^6.24.0"
|
| 24 |
+
},
|
| 25 |
+
"devDependencies": {
|
| 26 |
+
"@types/react": "^18.3.3",
|
| 27 |
+
"@types/react-dom": "^18.3.0",
|
| 28 |
+
"@vitejs/plugin-react": "^4.3.1",
|
| 29 |
+
"autoprefixer": "^10.4.19",
|
| 30 |
+
"eslint": "^8.57.0",
|
| 31 |
+
"eslint-plugin-react": "^7.34.2",
|
| 32 |
+
"eslint-plugin-react-hooks": "^4.6.2",
|
| 33 |
+
"eslint-plugin-react-refresh": "^0.4.7",
|
| 34 |
+
"postcss": "^8.4.39",
|
| 35 |
+
"tailwindcss": "^3.4.4",
|
| 36 |
+
"vite": "^5.3.1"
|
| 37 |
+
}
|
| 38 |
+
}
|
postcss.config.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export default {
|
| 2 |
+
plugins: {
|
| 3 |
+
tailwindcss: {},
|
| 4 |
+
autoprefixer: {},
|
| 5 |
+
},
|
| 6 |
+
}
|
public/HiDigi.jpg
ADDED
|
Git LFS Details
|
public/HiDigiH.jpg
ADDED
|
Git LFS Details
|
public/bot.png
ADDED
|
public/vite.svg
ADDED
|
|
public/you.png
ADDED
|
src/App.jsx
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
|
| 2 |
+
import { useState } from 'react';
|
| 3 |
+
import Register from './components/Register';
|
| 4 |
+
import ConfigForm from './components/ConfigForm';
|
| 5 |
+
import Home from './pages/Home';
|
| 6 |
+
import TextToSpeechPage from './pages/TextToSpeechPage';
|
| 7 |
+
import LoginPage from './pages/LoginPage';
|
| 8 |
+
import ProtectedRoute from './components/ProtectedRoute';
|
| 9 |
+
import OCRDashboard from './pages/OCRDashboardPage';
|
| 10 |
+
import ERPCredential from './pages/ERPCredentialPage';
|
| 11 |
+
import OCRTemplate from './pages/OCRTemplatePage';
|
| 12 |
+
import Logout from './components/Logout';
|
| 13 |
+
import Chat from './pages/ChatPage';
|
| 14 |
+
import Analytics from './pages/AnalyticsPage';
|
| 15 |
+
import DocumentManagement from './pages/DocumentManagement';
|
| 16 |
+
|
| 17 |
+
function App() {
|
| 18 |
+
const [token, setToken] = useState(localStorage.getItem('token'));
|
| 19 |
+
|
| 20 |
+
return (
|
| 21 |
+
<Router>
|
| 22 |
+
<div className="min-h-screen flex flex-col">
|
| 23 |
+
<Routes>
|
| 24 |
+
<Route path="/" element={<Navigate to="/ocrdashboard" replace />} />
|
| 25 |
+
<Route path="/text-to-speech" element={<ProtectedRoute token={token}><TextToSpeechPage /></ProtectedRoute>} />
|
| 26 |
+
<Route path="/login" element={<LoginPage setToken={setToken} />} />
|
| 27 |
+
<Route path="/logout" element={<ProtectedRoute token={token}><Logout setToken={setToken} /></ProtectedRoute>} />
|
| 28 |
+
<Route path="/register" element={<Register />} />
|
| 29 |
+
<Route path="/ocrdashboard" element={<ProtectedRoute token={token}><OCRDashboard /></ProtectedRoute>} />
|
| 30 |
+
<Route path="/ocrtemplate" element={<ProtectedRoute token={token}><OCRTemplate /></ProtectedRoute>} />
|
| 31 |
+
<Route path="/erpcredential" element={<ProtectedRoute token={token}><ERPCredential /></ProtectedRoute>} />
|
| 32 |
+
<Route path="/chat" element={<ProtectedRoute token={token}><Chat /></ProtectedRoute>} />
|
| 33 |
+
<Route path="/analytics" element={<ProtectedRoute token={token}><Analytics /></ProtectedRoute>} />
|
| 34 |
+
<Route path="/models" element={<ProtectedRoute token={token}><DocumentManagement /></ProtectedRoute>} />
|
| 35 |
+
<Route path="/config" element={<ProtectedRoute token={token}><ConfigForm /></ProtectedRoute>} />
|
| 36 |
+
</Routes>
|
| 37 |
+
</div>
|
| 38 |
+
</Router>
|
| 39 |
+
);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
export default App;
|
src/assets/react.svg
ADDED
|
|
src/chartSetup.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
Chart as ChartJS,
|
| 3 |
+
CategoryScale,
|
| 4 |
+
LinearScale,
|
| 5 |
+
PointElement,
|
| 6 |
+
LineElement,
|
| 7 |
+
BarElement,
|
| 8 |
+
ArcElement,
|
| 9 |
+
Title,
|
| 10 |
+
Tooltip,
|
| 11 |
+
Legend,
|
| 12 |
+
} from 'chart.js';
|
| 13 |
+
|
| 14 |
+
ChartJS.register(
|
| 15 |
+
CategoryScale,
|
| 16 |
+
LinearScale,
|
| 17 |
+
PointElement,
|
| 18 |
+
LineElement,
|
| 19 |
+
BarElement,
|
| 20 |
+
ArcElement,
|
| 21 |
+
Title,
|
| 22 |
+
Tooltip,
|
| 23 |
+
Legend
|
| 24 |
+
);
|
| 25 |
+
|
src/components/ChatMessage.jsx
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
| 3 |
+
import { faVolumeUp, faCopy, faSync, faStar, faCogs } from '@fortawesome/free-solid-svg-icons';
|
| 4 |
+
|
| 5 |
+
const ChatMessage = ({ message, isUser }) => {
|
| 6 |
+
return (
|
| 7 |
+
<div className={`flex ${isUser ? 'justify-end' : 'justify-start'} mb-2`}>
|
| 8 |
+
<div className={`flex items-end ${isUser ? 'flex-row-reverse' : 'flex-row'}`}>
|
| 9 |
+
<div className="flex flex-col items-center">
|
| 10 |
+
<div className="w-10 h-10 rounded-full overflow-hidden">
|
| 11 |
+
<img
|
| 12 |
+
src={isUser ? '/public/you.png' : '/public/bot.png'}
|
| 13 |
+
alt={isUser ? 'You' : 'AI Bot'}
|
| 14 |
+
className="w-full h-full object-cover"
|
| 15 |
+
/>
|
| 16 |
+
</div>
|
| 17 |
+
<p className="text-xs mt-1">{isUser ? 'You' : 'AI Bot'}</p>
|
| 18 |
+
</div>
|
| 19 |
+
<div className={`p-3 rounded-lg ml-2 max-w-lg ${isUser ? 'bg-blue-600 text-white' : 'bg-gray-300 text-black'}`}>
|
| 20 |
+
<p className="text-sm whitespace-pre-wrap" dangerouslySetInnerHTML={{ __html: message.text }}></p>
|
| 21 |
+
<p className="text-xs mt-1">{message.timestamp}</p>
|
| 22 |
+
{!isUser && (
|
| 23 |
+
<div className="flex mt-2 space-x-2">
|
| 24 |
+
<button className="p-1 text-sm bg-white rounded-full hover:bg-gray-200">
|
| 25 |
+
<FontAwesomeIcon icon={faVolumeUp} />
|
| 26 |
+
</button>
|
| 27 |
+
<button className="p-1 text-sm bg-white rounded-full hover:bg-gray-200">
|
| 28 |
+
<FontAwesomeIcon icon={faCopy} />
|
| 29 |
+
</button>
|
| 30 |
+
<button className="p-1 text-sm bg-white rounded-full hover:bg-gray-200">
|
| 31 |
+
<FontAwesomeIcon icon={faSync} />
|
| 32 |
+
</button>
|
| 33 |
+
<button className="p-1 text-sm bg-white rounded-full hover:bg-gray-200">
|
| 34 |
+
<FontAwesomeIcon icon={faStar} />
|
| 35 |
+
</button>
|
| 36 |
+
<button className="p-1 text-sm bg-white rounded-full hover:bg-gray-200">
|
| 37 |
+
<FontAwesomeIcon icon={faCogs} />
|
| 38 |
+
</button>
|
| 39 |
+
</div>
|
| 40 |
+
)}
|
| 41 |
+
</div>
|
| 42 |
+
</div>
|
| 43 |
+
</div>
|
| 44 |
+
);
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
export default ChatMessage;
|
src/components/ConfigForm.jsx
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from 'react';
|
| 2 |
+
import axios from 'axios';
|
| 3 |
+
import Sidebar from './OCRSidebar';
|
| 4 |
+
import Header from './OCRHeader';
|
| 5 |
+
|
| 6 |
+
const ConfigForm = () => {
|
| 7 |
+
const [config, setConfig] = useState({
|
| 8 |
+
MONGO_DETAILS: '',
|
| 9 |
+
MongoDB_NAME: '',
|
| 10 |
+
COLLECTION_NAMES: '',
|
| 11 |
+
SECRET_KEY: '',
|
| 12 |
+
ALGORITHM: '',
|
| 13 |
+
ACCESS_TOKEN_EXPIRE_MINUTES: 0
|
| 14 |
+
});
|
| 15 |
+
|
| 16 |
+
useEffect(() => {
|
| 17 |
+
axios.get('http://localhost:8000/config')
|
| 18 |
+
.then(response => setConfig(response.data))
|
| 19 |
+
.catch(error => console.error('Error fetching config:', error));
|
| 20 |
+
}, []);
|
| 21 |
+
|
| 22 |
+
const handleChange = (e) => {
|
| 23 |
+
const { name, value } = e.target;
|
| 24 |
+
setConfig({
|
| 25 |
+
...config,
|
| 26 |
+
[name]: value
|
| 27 |
+
});
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
+
const handleSubmit = (e) => {
|
| 31 |
+
e.preventDefault();
|
| 32 |
+
axios.patch('http://localhost:8000/config', config)
|
| 33 |
+
.then(response => {
|
| 34 |
+
setConfig(response.data);
|
| 35 |
+
alert('Config updated successfully!');
|
| 36 |
+
})
|
| 37 |
+
.catch(error => console.error('Error updating config:', error));
|
| 38 |
+
};
|
| 39 |
+
|
| 40 |
+
return (
|
| 41 |
+
<div className="min-h-screen bg-gray-100 flex">
|
| 42 |
+
<Sidebar />
|
| 43 |
+
<div className="flex-1 p-6">
|
| 44 |
+
<Header />
|
| 45 |
+
<div className="bg-white p-6 rounded-lg shadow-lg w-full max-w-4xl mx-auto">
|
| 46 |
+
<h2 className="text-2xl font-bold mb-6">Manage Credentials</h2>
|
| 47 |
+
<form className="space-y-4" onSubmit={handleSubmit}>
|
| 48 |
+
<div>
|
| 49 |
+
<label className="block text-gray-700">ERP details</label>
|
| 50 |
+
<input
|
| 51 |
+
type="text"
|
| 52 |
+
name="MONGO_DETAILS"
|
| 53 |
+
value={config.MONGO_DETAILS}
|
| 54 |
+
onChange={handleChange}
|
| 55 |
+
className="w-full px-4 py-2 border rounded-md"
|
| 56 |
+
/>
|
| 57 |
+
</div>
|
| 58 |
+
<div>
|
| 59 |
+
<label className="block text-gray-700">Client ID</label>
|
| 60 |
+
<input
|
| 61 |
+
type="text"
|
| 62 |
+
name="MongoDB_NAME"
|
| 63 |
+
value={config.MongoDB_NAME}
|
| 64 |
+
onChange={handleChange}
|
| 65 |
+
className="w-full px-4 py-2 border rounded-md"
|
| 66 |
+
/>
|
| 67 |
+
</div>
|
| 68 |
+
<div>
|
| 69 |
+
<label className="block text-gray-700">Client Secret</label>
|
| 70 |
+
<input
|
| 71 |
+
type="text"
|
| 72 |
+
name="COLLECTION_NAMES"
|
| 73 |
+
value={config.COLLECTION_NAMES}
|
| 74 |
+
onChange={handleChange}
|
| 75 |
+
className="w-full px-4 py-2 border rounded-md"
|
| 76 |
+
/>
|
| 77 |
+
</div>
|
| 78 |
+
<div>
|
| 79 |
+
<label className="block text-gray-700">Username</label>
|
| 80 |
+
<input
|
| 81 |
+
type="text"
|
| 82 |
+
name="SECRET_KEY"
|
| 83 |
+
value={config.SECRET_KEY}
|
| 84 |
+
onChange={handleChange}
|
| 85 |
+
className="w-full px-4 py-2 border rounded-md"
|
| 86 |
+
/>
|
| 87 |
+
</div>
|
| 88 |
+
<div>
|
| 89 |
+
<label className="block text-gray-700">Password</label>
|
| 90 |
+
<input
|
| 91 |
+
type="password"
|
| 92 |
+
name="ALGORITHM"
|
| 93 |
+
value={config.ALGORITHM}
|
| 94 |
+
onChange={handleChange}
|
| 95 |
+
className="w-full px-4 py-2 border rounded-md"
|
| 96 |
+
/>
|
| 97 |
+
</div>
|
| 98 |
+
<div>
|
| 99 |
+
<label className="block text-gray-700">Company Code</label>
|
| 100 |
+
<input
|
| 101 |
+
type="text"
|
| 102 |
+
name="ACCESS_TOKEN_EXPIRE_MINUTES"
|
| 103 |
+
value={config.ACCESS_TOKEN_EXPIRE_MINUTES}
|
| 104 |
+
onChange={handleChange}
|
| 105 |
+
className="w-full px-4 py-2 border rounded-md"
|
| 106 |
+
/>
|
| 107 |
+
</div>
|
| 108 |
+
<div>
|
| 109 |
+
<label className="block text-gray-700">API Key (Optional)</label>
|
| 110 |
+
<input
|
| 111 |
+
type="text"
|
| 112 |
+
name="API_KEY"
|
| 113 |
+
value={config.API_KEY}
|
| 114 |
+
onChange={handleChange}
|
| 115 |
+
className="w-full px-4 py-2 border rounded-md"
|
| 116 |
+
/>
|
| 117 |
+
</div>
|
| 118 |
+
<button
|
| 119 |
+
type="submit"
|
| 120 |
+
className="w-full bg-blue-500 text-white px-4 py-2 rounded-md"
|
| 121 |
+
>
|
| 122 |
+
Save Credentials
|
| 123 |
+
</button>
|
| 124 |
+
</form>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
</div>
|
| 128 |
+
);
|
| 129 |
+
};
|
| 130 |
+
|
| 131 |
+
export default ConfigForm;
|
src/components/ERPCredentials.jsx
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState } from 'react';
|
| 2 |
+
import Sidebar from './OCRSidebar';
|
| 3 |
+
|
| 4 |
+
function Credentials({ onSave }) {
|
| 5 |
+
const [credentials, setCredentials] = useState({
|
| 6 |
+
sapUrl: '',
|
| 7 |
+
clientId: '',
|
| 8 |
+
clientSecret: '',
|
| 9 |
+
username: '',
|
| 10 |
+
password: '',
|
| 11 |
+
companyCode: '',
|
| 12 |
+
apiKey: ''
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
const handleChange = (e) => {
|
| 16 |
+
const { name, value } = e.target;
|
| 17 |
+
setCredentials({ ...credentials, [name]: value });
|
| 18 |
+
};
|
| 19 |
+
|
| 20 |
+
const handleSubmit = (e) => {
|
| 21 |
+
e.preventDefault();
|
| 22 |
+
onSave(credentials);
|
| 23 |
+
};
|
| 24 |
+
|
| 25 |
+
return (
|
| 26 |
+
<div className="min-h-screen bg-gray-100 flex">
|
| 27 |
+
<Sidebar />
|
| 28 |
+
<div className="flex-1 p-6">
|
| 29 |
+
<div className="bg-white p-6 rounded-lg shadow-lg w-full max-w-4xl mx-auto">
|
| 30 |
+
<h2 className="text-xl font-semibold text-gray-700 mb-4">Manage Credentials</h2>
|
| 31 |
+
<form onSubmit={handleSubmit}>
|
| 32 |
+
<div className="mb-4">
|
| 33 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">SAP System URL</label>
|
| 34 |
+
<input
|
| 35 |
+
type="text"
|
| 36 |
+
name="sapUrl"
|
| 37 |
+
value={credentials.sapUrl}
|
| 38 |
+
onChange={handleChange}
|
| 39 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 40 |
+
/>
|
| 41 |
+
</div>
|
| 42 |
+
<div className="mb-4">
|
| 43 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Client ID</label>
|
| 44 |
+
<input
|
| 45 |
+
type="text"
|
| 46 |
+
name="clientId"
|
| 47 |
+
value={credentials.clientId}
|
| 48 |
+
onChange={handleChange}
|
| 49 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 50 |
+
/>
|
| 51 |
+
</div>
|
| 52 |
+
<div className="mb-4">
|
| 53 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Client Secret</label>
|
| 54 |
+
<input
|
| 55 |
+
type="text"
|
| 56 |
+
name="clientSecret"
|
| 57 |
+
value={credentials.clientSecret}
|
| 58 |
+
onChange={handleChange}
|
| 59 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 60 |
+
/>
|
| 61 |
+
</div>
|
| 62 |
+
<div className="mb-4">
|
| 63 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Username</label>
|
| 64 |
+
<input
|
| 65 |
+
type="text"
|
| 66 |
+
name="username"
|
| 67 |
+
value={credentials.username}
|
| 68 |
+
onChange={handleChange}
|
| 69 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 70 |
+
/>
|
| 71 |
+
</div>
|
| 72 |
+
<div className="mb-4">
|
| 73 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Password</label>
|
| 74 |
+
<input
|
| 75 |
+
type="password"
|
| 76 |
+
name="password"
|
| 77 |
+
value={credentials.password}
|
| 78 |
+
onChange={handleChange}
|
| 79 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 80 |
+
/>
|
| 81 |
+
</div>
|
| 82 |
+
<div className="mb-4">
|
| 83 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Company Code</label>
|
| 84 |
+
<input
|
| 85 |
+
type="text"
|
| 86 |
+
name="companyCode"
|
| 87 |
+
value={credentials.companyCode}
|
| 88 |
+
onChange={handleChange}
|
| 89 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 90 |
+
/>
|
| 91 |
+
</div>
|
| 92 |
+
<div className="mb-4">
|
| 93 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">API Key (Optional)</label>
|
| 94 |
+
<input
|
| 95 |
+
type="text"
|
| 96 |
+
name="apiKey"
|
| 97 |
+
value={credentials.apiKey}
|
| 98 |
+
onChange={handleChange}
|
| 99 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 100 |
+
/>
|
| 101 |
+
</div>
|
| 102 |
+
<button type="submit" className="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition duration-200">
|
| 103 |
+
Save Credentials
|
| 104 |
+
</button>
|
| 105 |
+
</form>
|
| 106 |
+
</div>
|
| 107 |
+
</div>
|
| 108 |
+
</div>
|
| 109 |
+
);
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
export default Credentials;
|
src/components/ForgotPassword.jsx
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState } from 'react';
|
| 2 |
+
import axios from 'axios';
|
| 3 |
+
import '../styles/ForgotPassword.css';
|
| 4 |
+
|
| 5 |
+
const ForgotPassword = () => {
|
| 6 |
+
const [email, setEmail] = useState('');
|
| 7 |
+
|
| 8 |
+
const handleSubmit = async (e) => {
|
| 9 |
+
e.preventDefault();
|
| 10 |
+
try {
|
| 11 |
+
const response = await axios.post('/api/forgot-password', { email });
|
| 12 |
+
console.log('Response:', response.data);
|
| 13 |
+
alert('Password reset link sent to your email');
|
| 14 |
+
} catch (error) {
|
| 15 |
+
console.error('Failed to send password reset link', error);
|
| 16 |
+
}
|
| 17 |
+
};
|
| 18 |
+
|
| 19 |
+
return (
|
| 20 |
+
<div className="min-h-screen flex items-center justify-center bg-gradient-to-r from-purple-400 via-pink-500 to-blue-500">
|
| 21 |
+
<div className="flex flex-col md:flex-row bg-white rounded-lg shadow-md overflow-hidden w-full max-w-4xl">
|
| 22 |
+
<div className="md:w-1/2 flex justify-center items-center bg-gray-100">
|
| 23 |
+
<img src="/public/HiDigiH.jpg" alt="HiDigi Logo" style={{ height: '250px', width: '250px' }} />
|
| 24 |
+
</div>
|
| 25 |
+
<div className="md:w-1/2 p-8">
|
| 26 |
+
<h2 className="text-2xl font-bold mb-6 text-center">Forgot Password</h2>
|
| 27 |
+
<form onSubmit={handleSubmit}>
|
| 28 |
+
<div className="mb-4">
|
| 29 |
+
<label className="block text-gray-700 mb-2">Email</label>
|
| 30 |
+
<input
|
| 31 |
+
type="email"
|
| 32 |
+
name="email"
|
| 33 |
+
value={email}
|
| 34 |
+
onChange={(e) => setEmail(e.target.value)}
|
| 35 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 36 |
+
placeholder="Type your email"
|
| 37 |
+
required
|
| 38 |
+
/>
|
| 39 |
+
</div>
|
| 40 |
+
<button type="submit" className="w-full bg-blue-500 text-white py-3 px-4 rounded mt-4 hover:bg-blue-600">Send Reset Link</button>
|
| 41 |
+
</form>
|
| 42 |
+
</div>
|
| 43 |
+
</div>
|
| 44 |
+
</div>
|
| 45 |
+
);
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
export default ForgotPassword;
|
src/components/HomeDashboard.jsx
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Link } from 'react-router-dom';
|
| 2 |
+
|
| 3 |
+
function HomeDashboard() {
|
| 4 |
+
|
| 5 |
+
return (
|
| 6 |
+
<div className="min-h-screen bg-gray-100 flex flex-col justify-center items-center">
|
| 7 |
+
<h1 className="text-4xl font-bold mb-8">Welcome to the Speech Services Dashboard</h1>
|
| 8 |
+
<div className="flex space-x-4">
|
| 9 |
+
<Link to="/text-to-speech" className="px-6 py-3 bg-blue-500 text-white rounded-lg">Text to Speech</Link>
|
| 10 |
+
<Link to="/speech-to-text" className="px-6 py-3 bg-green-500 text-white rounded-lg">Speech to Text</Link>
|
| 11 |
+
</div>
|
| 12 |
+
</div>
|
| 13 |
+
);
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
export default HomeDashboard;
|
src/components/InvoiceForm.jsx
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// src/components/InvoiceForm.jsx
|
| 2 |
+
import React, { useState } from 'react';
|
| 3 |
+
|
| 4 |
+
function InvoiceForm ({ onSubmit }) {
|
| 5 |
+
const [invoiceData, setInvoiceData] = useState({
|
| 6 |
+
invoiceNumber: '',
|
| 7 |
+
invoiceDate: '',
|
| 8 |
+
vendorNumber: '',
|
| 9 |
+
vendorName: '',
|
| 10 |
+
poNumber: '',
|
| 11 |
+
poDate: '',
|
| 12 |
+
grDate: '',
|
| 13 |
+
amount: '',
|
| 14 |
+
currency: 'USD',
|
| 15 |
+
paymentTerms: '',
|
| 16 |
+
taxAmount: '',
|
| 17 |
+
taxCode: '',
|
| 18 |
+
description: '',
|
| 19 |
+
costCenter: '',
|
| 20 |
+
glAccount: '',
|
| 21 |
+
lineItems: [
|
| 22 |
+
{
|
| 23 |
+
itemNumber: '',
|
| 24 |
+
description: '',
|
| 25 |
+
quantity: '',
|
| 26 |
+
unitPrice: '',
|
| 27 |
+
totalPrice: ''
|
| 28 |
+
}
|
| 29 |
+
]
|
| 30 |
+
});
|
| 31 |
+
|
| 32 |
+
const handleChange = (e) => {
|
| 33 |
+
const { name, value } = e.target;
|
| 34 |
+
setInvoiceData({
|
| 35 |
+
...invoiceData,
|
| 36 |
+
[name]: value
|
| 37 |
+
});
|
| 38 |
+
};
|
| 39 |
+
|
| 40 |
+
const handleLineItemChange = (index, e) => {
|
| 41 |
+
const { name, value } = e.target;
|
| 42 |
+
const newLineItems = [...invoiceData.lineItems];
|
| 43 |
+
newLineItems[index][name] = value;
|
| 44 |
+
setInvoiceData({
|
| 45 |
+
...invoiceData,
|
| 46 |
+
lineItems: newLineItems
|
| 47 |
+
});
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
const addLineItem = () => {
|
| 51 |
+
setInvoiceData({
|
| 52 |
+
...invoiceData,
|
| 53 |
+
lineItems: [
|
| 54 |
+
...invoiceData.lineItems,
|
| 55 |
+
{ itemNumber: '', description: '', quantity: '', unitPrice: '', totalPrice: '' }
|
| 56 |
+
]
|
| 57 |
+
});
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
const handleSubmit = (e) => {
|
| 61 |
+
e.preventDefault();
|
| 62 |
+
onSubmit(invoiceData);
|
| 63 |
+
};
|
| 64 |
+
|
| 65 |
+
return (
|
| 66 |
+
<form onSubmit={handleSubmit}>
|
| 67 |
+
<h2 className="text-xl font-semibold text-gray-700 mb-4">Enter Invoice Data</h2>
|
| 68 |
+
<div className="mb-4">
|
| 69 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Invoice Number</label>
|
| 70 |
+
<input
|
| 71 |
+
type="text"
|
| 72 |
+
name="invoiceNumber"
|
| 73 |
+
value={invoiceData.invoiceNumber}
|
| 74 |
+
onChange={handleChange}
|
| 75 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 76 |
+
/>
|
| 77 |
+
</div>
|
| 78 |
+
{/* Add more fields here following the same pattern */}
|
| 79 |
+
{/* Vendor Information */}
|
| 80 |
+
{/* Purchase Order Information */}
|
| 81 |
+
{/* Goods Receipt Date, Amount, etc. */}
|
| 82 |
+
<div className="mb-4">
|
| 83 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Line Items</label>
|
| 84 |
+
{invoiceData.lineItems.map((item, index) => (
|
| 85 |
+
<div key={index} className="mb-2">
|
| 86 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Item {index + 1}</label>
|
| 87 |
+
<input
|
| 88 |
+
type="text"
|
| 89 |
+
name="description"
|
| 90 |
+
value={item.description}
|
| 91 |
+
onChange={(e) => handleLineItemChange(index, e)}
|
| 92 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600 mb-2"
|
| 93 |
+
placeholder="Description"
|
| 94 |
+
/>
|
| 95 |
+
<input
|
| 96 |
+
type="text"
|
| 97 |
+
name="quantity"
|
| 98 |
+
value={item.quantity}
|
| 99 |
+
onChange={(e) => handleLineItemChange(index, e)}
|
| 100 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600 mb-2"
|
| 101 |
+
placeholder="Quantity"
|
| 102 |
+
/>
|
| 103 |
+
<input
|
| 104 |
+
type="text"
|
| 105 |
+
name="unitPrice"
|
| 106 |
+
value={item.unitPrice}
|
| 107 |
+
onChange={(e) => handleLineItemChange(index, e)}
|
| 108 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600 mb-2"
|
| 109 |
+
placeholder="Unit Price"
|
| 110 |
+
/>
|
| 111 |
+
<input
|
| 112 |
+
type="text"
|
| 113 |
+
name="totalPrice"
|
| 114 |
+
value={item.totalPrice}
|
| 115 |
+
onChange={(e) => handleLineItemChange(index, e)}
|
| 116 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 117 |
+
placeholder="Total Price"
|
| 118 |
+
/>
|
| 119 |
+
</div>
|
| 120 |
+
))}
|
| 121 |
+
<button type="button" onClick={addLineItem} className="bg-blue-500 text-white py-2 px-4 rounded-lg hover:bg-blue-600 transition duration-200">
|
| 122 |
+
Add Line Item
|
| 123 |
+
</button>
|
| 124 |
+
</div>
|
| 125 |
+
<button type="submit" className="w-full bg-green-500 text-white py-2 rounded-lg hover:bg-green-600 transition duration-200">
|
| 126 |
+
Submit Invoice
|
| 127 |
+
</button>
|
| 128 |
+
</form>
|
| 129 |
+
);
|
| 130 |
+
};
|
| 131 |
+
|
| 132 |
+
export default InvoiceForm;
|
src/components/Login.jsx
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import axios from 'axios';
|
| 2 |
+
import { useState } from 'react';
|
| 3 |
+
import { useNavigate } from 'react-router-dom';
|
| 4 |
+
import { login } from '../services/api';
|
| 5 |
+
import '../styles/Login.css';
|
| 6 |
+
|
| 7 |
+
axios.defaults.withCredentials = true;
|
| 8 |
+
|
| 9 |
+
function Login() {
|
| 10 |
+
const [formData, setFormData] = useState({
|
| 11 |
+
usernameOrEmail: '',
|
| 12 |
+
password: '',
|
| 13 |
+
});
|
| 14 |
+
const navigate = useNavigate();
|
| 15 |
+
|
| 16 |
+
const handleChange = (e) => {
|
| 17 |
+
setFormData({ ...formData, [e.target.name]: e.target.value });
|
| 18 |
+
};
|
| 19 |
+
|
| 20 |
+
const handleSubmit = async (e) => {
|
| 21 |
+
e.preventDefault();
|
| 22 |
+
const { usernameOrEmail, password } = formData;
|
| 23 |
+
try {
|
| 24 |
+
const response = await login(usernameOrEmail, password);
|
| 25 |
+
console.log(response.data);
|
| 26 |
+
localStorage.setItem('token', response.data["access_token"]);
|
| 27 |
+
console.log('Token stored in localStorage:', localStorage.getItem('token'));
|
| 28 |
+
navigate('/ocrdashboard');
|
| 29 |
+
} catch (error) {
|
| 30 |
+
console.error('Login failed', error);
|
| 31 |
+
}
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
return (
|
| 35 |
+
<div className="min-h-screen flex items-center justify-center bg-gradient-to-r from-purple-400 via-pink-500 to-blue-500">
|
| 36 |
+
<div className="flex flex-col md:flex-row bg-white rounded-lg shadow-md overflow-hidden w-full max-w-4xl">
|
| 37 |
+
<div className="md:w-1/2 flex justify-center items-center bg-gray-100">
|
| 38 |
+
<img src="/public/HiDigiH.jpg" alt="HiDigi Logo" style={{ height: '250px', width: '250px' }} />
|
| 39 |
+
</div>
|
| 40 |
+
<div className="md:w-1/2 p-8">
|
| 41 |
+
<h2 className="text-2xl font-bold mb-6 text-center">Sign in</h2>
|
| 42 |
+
<form onSubmit={handleSubmit}>
|
| 43 |
+
<div className="mb-4">
|
| 44 |
+
<label className="block text-gray-700 mb-2">Username or Email</label>
|
| 45 |
+
<input
|
| 46 |
+
type="text"
|
| 47 |
+
name="usernameOrEmail"
|
| 48 |
+
value={formData.usernameOrEmail}
|
| 49 |
+
onChange={handleChange}
|
| 50 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 51 |
+
placeholder="Type your username or email"
|
| 52 |
+
required
|
| 53 |
+
/>
|
| 54 |
+
</div>
|
| 55 |
+
<div className="mb-4">
|
| 56 |
+
<label className="block text-gray-700 mb-2">Password</label>
|
| 57 |
+
<input
|
| 58 |
+
type="password"
|
| 59 |
+
name="password"
|
| 60 |
+
value={formData.password}
|
| 61 |
+
onChange={handleChange}
|
| 62 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 63 |
+
placeholder="Type your password"
|
| 64 |
+
required
|
| 65 |
+
/>
|
| 66 |
+
<div className="text-right mt-2">
|
| 67 |
+
<a href="/forgot-password" className="text-blue-500 hover:underline">Forgot Password?</a>
|
| 68 |
+
</div>
|
| 69 |
+
</div>
|
| 70 |
+
<button type="submit" className="w-full bg-blue-500 text-white py-3 px-4 rounded mt-4 hover:bg-blue-600">Sign in</button>
|
| 71 |
+
</form>
|
| 72 |
+
<div className="text-center text-gray-500 text-sm mt-6">
|
| 73 |
+
By clicking "Sign in", you agree to our <a href="/terms" className="text-blue-500 hover:underline">Terms and Conditions</a> and <a href="/privacy" className="text-blue-500 hover:underline">Privacy Policy</a>.
|
| 74 |
+
</div>
|
| 75 |
+
<div className="text-center text-gray-500 text-sm mt-4">
|
| 76 |
+
Don't have an account? <a href="/register" className="text-blue-500 hover:underline">Register</a>
|
| 77 |
+
</div>
|
| 78 |
+
</div>
|
| 79 |
+
</div>
|
| 80 |
+
</div>
|
| 81 |
+
);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
export default Login;
|
src/components/Logout.jsx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useEffect } from 'react';
|
| 2 |
+
import { useNavigate } from 'react-router-dom';
|
| 3 |
+
|
| 4 |
+
function Logout() {
|
| 5 |
+
const navigate = useNavigate();
|
| 6 |
+
|
| 7 |
+
useEffect(() => {
|
| 8 |
+
const handleLogout = () => {
|
| 9 |
+
// Clear any stored user data (e.g., tokens) here
|
| 10 |
+
localStorage.removeItem('token'); // Example, adjust based on your implementation
|
| 11 |
+
navigate('/login');
|
| 12 |
+
};
|
| 13 |
+
|
| 14 |
+
handleLogout();
|
| 15 |
+
}, [navigate]);
|
| 16 |
+
|
| 17 |
+
return null; // Or a loading spinner/message if desired
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
export default Logout;
|
src/components/OCRDashboard.jsx
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from 'react';
|
| 2 |
+
import Sidebar from './OCRSidebar';
|
| 3 |
+
import Header from './OCRHeader';
|
| 4 |
+
import { getTemplates, ocrProcess } from '../services/api';
|
| 5 |
+
import Quota from './OCRQuota';
|
| 6 |
+
import OCRTemplate from './OCRTemplate';
|
| 7 |
+
import Credentials from './ERPCredentials';
|
| 8 |
+
import InvoiceForm from './InvoiceForm';
|
| 9 |
+
|
| 10 |
+
const Dashboard = () => {
|
| 11 |
+
const [selectedFiles, setSelectedFiles] = useState([]);
|
| 12 |
+
const [templates, setTemplates] = useState([]); // Ensure templates is initialized as an array
|
| 13 |
+
const [selectedTemplate, setSelectedTemplate] = useState('');
|
| 14 |
+
const [credentials, setCredentials] = useState({
|
| 15 |
+
sapUrl: '',
|
| 16 |
+
clientId: '',
|
| 17 |
+
clientSecret: '',
|
| 18 |
+
username: '',
|
| 19 |
+
password: '',
|
| 20 |
+
companyCode: '',
|
| 21 |
+
apiKey: ''
|
| 22 |
+
});
|
| 23 |
+
|
| 24 |
+
const fetchTemplates = async (templateName = false) => {
|
| 25 |
+
try {
|
| 26 |
+
const response = await getTemplates(templateName);
|
| 27 |
+
setTemplates(response); // Ensure response is directly set to templates
|
| 28 |
+
} catch (error) {
|
| 29 |
+
console.error('Error fetching templates:', error);
|
| 30 |
+
}
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
const handleFolderSelect = (event) => {
|
| 34 |
+
const files = Array.from(event.target.files);
|
| 35 |
+
const validFileTypes = ['application/pdf', 'image/png', 'image/jpeg', 'image/jpg', 'image/PNG', 'image/JPEG'];
|
| 36 |
+
const filteredFiles = files.filter(file => validFileTypes.includes(file.type));
|
| 37 |
+
setSelectedFiles(filteredFiles);
|
| 38 |
+
};
|
| 39 |
+
|
| 40 |
+
const handleTemplateChange = (event) => {
|
| 41 |
+
const newValue = event.target.value;
|
| 42 |
+
setSelectedTemplate(newValue);
|
| 43 |
+
console.log("selectedTemplate:", newValue);
|
| 44 |
+
// Fetch templates based on the selected template
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
const handleCredentialsSave = (newCredentials) => {
|
| 48 |
+
setCredentials(newCredentials);
|
| 49 |
+
};
|
| 50 |
+
|
| 51 |
+
const handleSubmit = async (event) => {
|
| 52 |
+
event.preventDefault(); // Prevent default form submission behavior
|
| 53 |
+
|
| 54 |
+
if (selectedFiles.length === 0) {
|
| 55 |
+
alert("Please select files containing PDF, PNG, or JPG files.");
|
| 56 |
+
return;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
if (!selectedTemplate) {
|
| 60 |
+
alert("Please select a template.");
|
| 61 |
+
return;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
const formData = new FormData();
|
| 65 |
+
selectedFiles.forEach(file => formData.append("files", file));
|
| 66 |
+
formData.append("template_name", selectedTemplate);
|
| 67 |
+
|
| 68 |
+
// Debugging formData
|
| 69 |
+
for (let [key, value] of formData.entries()) {
|
| 70 |
+
console.log(`${key}: ${value.name || value}`);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
try {
|
| 74 |
+
const response = await ocrProcess(formData);
|
| 75 |
+
console.log("Response:", response);
|
| 76 |
+
alert("Files processed successfully!");
|
| 77 |
+
} catch (error) {
|
| 78 |
+
console.error("Error:", error);
|
| 79 |
+
alert("There was an error processing the files.");
|
| 80 |
+
}
|
| 81 |
+
};
|
| 82 |
+
|
| 83 |
+
// Fetch templates on component mount
|
| 84 |
+
useEffect(() => {
|
| 85 |
+
fetchTemplates();
|
| 86 |
+
}, []);
|
| 87 |
+
|
| 88 |
+
return (
|
| 89 |
+
<div className="min-h-screen bg-gray-100 flex">
|
| 90 |
+
<Sidebar />
|
| 91 |
+
<div className="flex-1 p-6">
|
| 92 |
+
<Header />
|
| 93 |
+
<div className="bg-white p-6 rounded-lg shadow-lg w-full max-w-4xl mx-auto">
|
| 94 |
+
<h1 className="text-2xl font-semibold text-gray-700 mb-4">Automate Data Input & OCR</h1>
|
| 95 |
+
<p className="text-gray-600 mb-4">
|
| 96 |
+
AI services that allow you to extract information from any documents and automate data input to external applications.
|
| 97 |
+
</p>
|
| 98 |
+
<div className="border-t border-gray-200 pt-4">
|
| 99 |
+
<h2 className="text-xl font-semibold text-gray-700 mb-4">Upload PDF Folder</h2>
|
| 100 |
+
<div className="mb-4">
|
| 101 |
+
<input
|
| 102 |
+
type="file"
|
| 103 |
+
// webkitdirectory="true"
|
| 104 |
+
// mozdirectory="true"
|
| 105 |
+
// directory=""
|
| 106 |
+
onChange={handleFolderSelect}
|
| 107 |
+
multiple
|
| 108 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 109 |
+
/>
|
| 110 |
+
</div>
|
| 111 |
+
{selectedFiles.length > 0 && (
|
| 112 |
+
<div className="mb-4">
|
| 113 |
+
<h3 className="text-lg font-semibold text-gray-700">Detected PDF Files:</h3>
|
| 114 |
+
<ul className="list-disc pl-5">
|
| 115 |
+
{selectedFiles.map((file, index) => (
|
| 116 |
+
<li key={index} className="text-gray-600">{file.name}</li>
|
| 117 |
+
))}
|
| 118 |
+
</ul>
|
| 119 |
+
</div>
|
| 120 |
+
)}
|
| 121 |
+
<div className="mb-4">
|
| 122 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Select Template</label>
|
| 123 |
+
<select
|
| 124 |
+
value={selectedTemplate}
|
| 125 |
+
onChange={handleTemplateChange}
|
| 126 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600"
|
| 127 |
+
>
|
| 128 |
+
<option value="">Select a template</option>
|
| 129 |
+
{templates.map((template, index) => (
|
| 130 |
+
<option key={index} value={template.template_name}>{template.template_name}</option>
|
| 131 |
+
))}
|
| 132 |
+
</select>
|
| 133 |
+
</div>
|
| 134 |
+
<button
|
| 135 |
+
onClick={handleSubmit}
|
| 136 |
+
className="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition duration-200 mb-4"
|
| 137 |
+
>
|
| 138 |
+
Process Files
|
| 139 |
+
</button>
|
| 140 |
+
</div>
|
| 141 |
+
</div>
|
| 142 |
+
</div>
|
| 143 |
+
</div>
|
| 144 |
+
);
|
| 145 |
+
};
|
| 146 |
+
|
| 147 |
+
export default Dashboard;
|
src/components/OCRHeader.jsx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// src/components/Header.jsx
|
| 2 |
+
import React from 'react';
|
| 3 |
+
|
| 4 |
+
function Header() {
|
| 5 |
+
return (
|
| 6 |
+
<div className="flex justify-between items-center mb-4">
|
| 7 |
+
<h1 className="text-2xl font-semibold text-gray-700">OCR Dashboard</h1>
|
| 8 |
+
<div className="flex space-x-4">
|
| 9 |
+
<button className="bg-blue-500 text-white py-2 px-4 rounded-lg hover:bg-blue-600 transition duration-200">
|
| 10 |
+
Contact us
|
| 11 |
+
</button>
|
| 12 |
+
<button className="bg-green-500 text-white py-2 px-4 rounded-lg hover:bg-green-600 transition duration-200">
|
| 13 |
+
Get Your Free Trial
|
| 14 |
+
</button>
|
| 15 |
+
</div>
|
| 16 |
+
</div>
|
| 17 |
+
);
|
| 18 |
+
};
|
| 19 |
+
|
| 20 |
+
export default Header;
|
src/components/OCRQuota.jsx
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// src/components/Quota.jsx
|
| 2 |
+
import React from 'react';
|
| 3 |
+
|
| 4 |
+
function Quota() {
|
| 5 |
+
return (
|
| 6 |
+
<div className="border-t border-gray-200 pt-4">
|
| 7 |
+
<h2 className="text-xl font-semibold text-gray-700">Your Quota Remaining</h2>
|
| 8 |
+
<div className="flex items-center space-x-2 mb-4">
|
| 9 |
+
<span className="text-4xl font-bold text-gray-800">0</span>
|
| 10 |
+
<span className="text-gray-600">times</span>
|
| 11 |
+
</div>
|
| 12 |
+
</div>
|
| 13 |
+
);
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
export default Quota;
|
src/components/OCRSidebar.jsx
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import { Link, useNavigate } from 'react-router-dom';
|
| 3 |
+
import { DocumentTextIcon, KeyIcon, ChartBarIcon, ClipboardListIcon, CogIcon, LogoutIcon, TemplateIcon } from '@heroicons/react/outline';
|
| 4 |
+
|
| 5 |
+
function Sidebar() {
|
| 6 |
+
const navigate = useNavigate();
|
| 7 |
+
|
| 8 |
+
const handleLogout = () => {
|
| 9 |
+
// Clear any stored user data (e.g., tokens) here
|
| 10 |
+
localStorage.removeItem('token'); // Example, adjust based on your implementation
|
| 11 |
+
navigate('/login');
|
| 12 |
+
};
|
| 13 |
+
|
| 14 |
+
return (
|
| 15 |
+
<div className="bg-gray-800 text-white w-64 p-4 space-y-6">
|
| 16 |
+
<h2 className="text-2xl font-bold">HiDigi</h2>
|
| 17 |
+
<nav>
|
| 18 |
+
<ul className="space-y-4">
|
| 19 |
+
<li className="hover:bg-gray-700 p-2 rounded-lg">
|
| 20 |
+
<Link to="/ocrdashboard" className="flex items-center space-x-2">
|
| 21 |
+
<DocumentTextIcon className="h-6 w-6" />
|
| 22 |
+
<span>Home</span>
|
| 23 |
+
</Link>
|
| 24 |
+
</li>
|
| 25 |
+
<li className="hover:bg-gray-700 p-2 rounded-lg">
|
| 26 |
+
<Link to="/ocrtemplate" className="flex items-center space-x-2">
|
| 27 |
+
<TemplateIcon className="h-6 w-6" />
|
| 28 |
+
<span>Application Template</span>
|
| 29 |
+
</Link>
|
| 30 |
+
</li>
|
| 31 |
+
<li className="hover:bg-gray-700 p-2 rounded-lg">
|
| 32 |
+
<Link to="/erpcredential" className="flex items-center space-x-2">
|
| 33 |
+
<KeyIcon className="h-6 w-6" />
|
| 34 |
+
<span>Manage Credentials</span>
|
| 35 |
+
</Link>
|
| 36 |
+
</li>
|
| 37 |
+
<li className="hover:bg-gray-700 p-2 rounded-lg">
|
| 38 |
+
<Link to="/summary" className="flex items-center space-x-2">
|
| 39 |
+
<ChartBarIcon className="h-6 w-6" />
|
| 40 |
+
<span>Summary</span>
|
| 41 |
+
</Link>
|
| 42 |
+
</li>
|
| 43 |
+
<li className="hover:bg-gray-700 p-2 rounded-lg">
|
| 44 |
+
<Link to="/transaction-detail" className="flex items-center space-x-2">
|
| 45 |
+
<ClipboardListIcon className="h-6 w-6" />
|
| 46 |
+
<span>Transaction Detail</span>
|
| 47 |
+
</Link>
|
| 48 |
+
</li>
|
| 49 |
+
<li className="hover:bg-gray-700 p-2 rounded-lg">
|
| 50 |
+
<Link to="/settings" className="flex items-center space-x-2">
|
| 51 |
+
<CogIcon className="h-6 w-6" />
|
| 52 |
+
<span>Settings</span>
|
| 53 |
+
</Link>
|
| 54 |
+
</li>
|
| 55 |
+
<li className="hover:bg-gray-700 p-2 rounded-lg">
|
| 56 |
+
<Link to="/logout" className="flex items-center space-x-2">
|
| 57 |
+
<LogoutIcon className="h-6 w-6" />
|
| 58 |
+
<span>Logout</span>
|
| 59 |
+
</Link>
|
| 60 |
+
</li>
|
| 61 |
+
</ul>
|
| 62 |
+
</nav>
|
| 63 |
+
</div>
|
| 64 |
+
);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
export default Sidebar;
|
src/components/OCRTemplate.jsx
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from 'react';
|
| 2 |
+
import Sidebar from './OCRSidebar';
|
| 3 |
+
import { createTemplate, getTemplates, deleteTemplate, createOrUpdateTemplate } from '../services/api';
|
| 4 |
+
|
| 5 |
+
function OCRTemplate() {
|
| 6 |
+
const [templates, setTemplates] = useState([]);
|
| 7 |
+
const [selectedTemplate, setSelectedTemplate] = useState(null);
|
| 8 |
+
const [newTemplateName, setNewTemplateName] = useState('');
|
| 9 |
+
const [isEditing, setIsEditing] = useState(false);
|
| 10 |
+
const [message, setMessage] = useState('');
|
| 11 |
+
|
| 12 |
+
const fetchTemplates = async () => {
|
| 13 |
+
try {
|
| 14 |
+
const response = await getTemplates();
|
| 15 |
+
console.log('fetchTemplates Response:', response);
|
| 16 |
+
const mappedTemplates = response.map(template => ({
|
| 17 |
+
template_name: template.template_name,
|
| 18 |
+
variables: Object.values(template.fields || {}), // Map fields to variables
|
| 19 |
+
user_id: template.user_id,
|
| 20 |
+
}));
|
| 21 |
+
setTemplates(mappedTemplates);
|
| 22 |
+
} catch (error) {
|
| 23 |
+
console.error('Error fetching templates:', error);
|
| 24 |
+
}
|
| 25 |
+
};
|
| 26 |
+
const handleTemplateChange = (e) => {
|
| 27 |
+
const templateName = e.target.value;
|
| 28 |
+
if (templateName === 'new') {
|
| 29 |
+
setSelectedTemplate(null);
|
| 30 |
+
setNewTemplateName('');
|
| 31 |
+
setIsEditing(false);
|
| 32 |
+
} else {
|
| 33 |
+
|
| 34 |
+
const existingTemplate = templates.find(template => template.template_name === templateName);
|
| 35 |
+
setSelectedTemplate(existingTemplate || null);
|
| 36 |
+
setIsEditing(!!existingTemplate);
|
| 37 |
+
|
| 38 |
+
}
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
const addVariable = () => {
|
| 44 |
+
const newTemplate = { ...selectedTemplate, variables: [...(selectedTemplate.variables || []), ''] };
|
| 45 |
+
setSelectedTemplate(newTemplate);
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
const handleTemplateNameChange = (value) => {
|
| 49 |
+
const newTemplate = { ...selectedTemplate, template_name: value };
|
| 50 |
+
setSelectedTemplate(newTemplate);
|
| 51 |
+
};
|
| 52 |
+
|
| 53 |
+
const handleVariableChange = (variableIndex, value) => {
|
| 54 |
+
const newTemplate = { ...selectedTemplate };
|
| 55 |
+
newTemplate.variables[variableIndex] = value;
|
| 56 |
+
setSelectedTemplate(newTemplate);
|
| 57 |
+
};
|
| 58 |
+
|
| 59 |
+
const addTemplate = () => {
|
| 60 |
+
const newTemplate = { template_name: newTemplateName, variables: [''] };
|
| 61 |
+
// setTemplates([...templates, newTemplate]);
|
| 62 |
+
setSelectedTemplate(newTemplate);
|
| 63 |
+
setIsEditing(true);
|
| 64 |
+
};
|
| 65 |
+
|
| 66 |
+
const handleSubmit = async () => {
|
| 67 |
+
const template = selectedTemplate;
|
| 68 |
+
const fields = {};
|
| 69 |
+
template.variables.forEach((variable, i) => {
|
| 70 |
+
fields[`additionalProp${i + 1}`] = variable;
|
| 71 |
+
});
|
| 72 |
+
|
| 73 |
+
try {
|
| 74 |
+
console.log(template);
|
| 75 |
+
// Check if the template name already exists
|
| 76 |
+
const existingTemplate = templates.find(t => t.template_name === template.template_name);
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
if (existingTemplate) {
|
| 81 |
+
// Update existing template
|
| 82 |
+
console.log('existingTemplate', existingTemplate.template_name);
|
| 83 |
+
await createOrUpdateTemplate(template.template_name, fields, template.user_id, true);
|
| 84 |
+
setMessage('Template updated successfully!');
|
| 85 |
+
} else {
|
| 86 |
+
console.log('Creating new template');
|
| 87 |
+
// Create new template
|
| 88 |
+
const newTemplate = await createOrUpdateTemplate(template.template_name, fields, template.user_id, false);
|
| 89 |
+
|
| 90 |
+
setMessage('Template created successfully!');
|
| 91 |
+
}
|
| 92 |
+
fetchTemplates(); // Fetch templates again to update the list
|
| 93 |
+
} catch (error) {
|
| 94 |
+
setMessage('Handle submit Error creating/updating template.');
|
| 95 |
+
}
|
| 96 |
+
};
|
| 97 |
+
|
| 98 |
+
const handleDeleteTemplate = async () => {
|
| 99 |
+
const templateName = selectedTemplate.template_name;
|
| 100 |
+
try {
|
| 101 |
+
await deleteTemplate(templateName);
|
| 102 |
+
setTemplates(templates.filter(template => template.template_name !== templateName));
|
| 103 |
+
setSelectedTemplate(null);
|
| 104 |
+
setMessage('Template deleted successfully!');
|
| 105 |
+
} catch (error) {
|
| 106 |
+
setMessage('Error deleting template.');
|
| 107 |
+
}
|
| 108 |
+
};
|
| 109 |
+
|
| 110 |
+
const handleDeleteVariable = (variableIndex) => {
|
| 111 |
+
const newTemplate = { ...selectedTemplate };
|
| 112 |
+
newTemplate.variables.splice(variableIndex, 1);
|
| 113 |
+
setSelectedTemplate(newTemplate);
|
| 114 |
+
};
|
| 115 |
+
|
| 116 |
+
// Fetch templates on component mount
|
| 117 |
+
useEffect(() => {
|
| 118 |
+
fetchTemplates();
|
| 119 |
+
}, []);
|
| 120 |
+
|
| 121 |
+
return (
|
| 122 |
+
<div className="min-h-screen bg-gray-100 flex">
|
| 123 |
+
<Sidebar />
|
| 124 |
+
<div className="flex-1 p-6">
|
| 125 |
+
<div className="bg-white p-6 rounded-lg shadow-lg w-full max-w-4xl mx-auto">
|
| 126 |
+
<h2 className="text-xl font-semibold text-gray-700">Application Template</h2>
|
| 127 |
+
<div className="mb-4">
|
| 128 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Select Template</label>
|
| 129 |
+
<select
|
| 130 |
+
value={selectedTemplate ? selectedTemplate.template_name : 'new'}
|
| 131 |
+
onChange={handleTemplateChange}
|
| 132 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600 mb-2"
|
| 133 |
+
>
|
| 134 |
+
<option value="new">Create New Template</option>
|
| 135 |
+
{templates.map((template, index) => (
|
| 136 |
+
<option key={index} value={template.template_name}>{template.template_name}</option>
|
| 137 |
+
))}
|
| 138 |
+
</select>
|
| 139 |
+
</div>
|
| 140 |
+
{!selectedTemplate && (
|
| 141 |
+
<div className="mb-4">
|
| 142 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Template Name</label>
|
| 143 |
+
<input
|
| 144 |
+
type="text"
|
| 145 |
+
value={newTemplateName}
|
| 146 |
+
onChange={(e) => setNewTemplateName(e.target.value)}
|
| 147 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600 mb-2"
|
| 148 |
+
/>
|
| 149 |
+
<button
|
| 150 |
+
onClick={addTemplate}
|
| 151 |
+
className="bg-green-500 text-white py-2 px-4 rounded-lg hover:bg-green-600 transition duration-200"
|
| 152 |
+
>
|
| 153 |
+
Add Template
|
| 154 |
+
</button>
|
| 155 |
+
</div>
|
| 156 |
+
)}
|
| 157 |
+
{selectedTemplate && (
|
| 158 |
+
<div className="mb-4">
|
| 159 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Template Name</label>
|
| 160 |
+
<input
|
| 161 |
+
type="text"
|
| 162 |
+
value={selectedTemplate.template_name}
|
| 163 |
+
onChange={(e) => handleTemplateNameChange(e.target.value)}
|
| 164 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600 mb-2"
|
| 165 |
+
/>
|
| 166 |
+
{selectedTemplate.variables.map((variable, varIndex) => (
|
| 167 |
+
<div key={varIndex} className="mb-2 flex items-center">
|
| 168 |
+
<label className="block text-gray-700 text-sm font-bold mb-2">Variable {varIndex + 1}</label>
|
| 169 |
+
<input
|
| 170 |
+
type="text"
|
| 171 |
+
value={variable}
|
| 172 |
+
onChange={(e) => handleVariableChange(varIndex, e.target.value)}
|
| 173 |
+
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600 mx-2"
|
| 174 |
+
/>
|
| 175 |
+
<button
|
| 176 |
+
onClick={() => handleDeleteVariable(varIndex)}
|
| 177 |
+
className="bg-red-500 text-white py-2 px-4 rounded-lg hover:bg-red-600 transition duration-200"
|
| 178 |
+
>
|
| 179 |
+
Delete Variable
|
| 180 |
+
</button>
|
| 181 |
+
</div>
|
| 182 |
+
))}
|
| 183 |
+
<div className="flex items-center">
|
| 184 |
+
<button
|
| 185 |
+
onClick={addVariable}
|
| 186 |
+
className="bg-blue-500 text-white py-2 px-4 rounded-lg hover:bg-blue-600 transition duration-200 mr-2"
|
| 187 |
+
>
|
| 188 |
+
Add Variable
|
| 189 |
+
</button>
|
| 190 |
+
<button
|
| 191 |
+
onClick={handleSubmit}
|
| 192 |
+
className="bg-green-500 text-white py-2 px-4 rounded-lg hover:bg-green-600 transition duration-200 mr-2"
|
| 193 |
+
>
|
| 194 |
+
Submit Template
|
| 195 |
+
</button>
|
| 196 |
+
<button
|
| 197 |
+
onClick={handleDeleteTemplate}
|
| 198 |
+
className="bg-red-500 text-white py-2 px-4 rounded-lg hover:bg-red-600 transition duration-200"
|
| 199 |
+
>
|
| 200 |
+
Delete Template
|
| 201 |
+
</button>
|
| 202 |
+
</div>
|
| 203 |
+
</div>
|
| 204 |
+
)}
|
| 205 |
+
{message && <p className="mt-4 text-gray-700">{message}</p>}
|
| 206 |
+
</div>
|
| 207 |
+
</div>
|
| 208 |
+
</div>
|
| 209 |
+
);
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
export default OCRTemplate;
|
src/components/PrivacyPolicy.jsx
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import '../styles/PrivacyPolicy.css';
|
| 2 |
+
|
| 3 |
+
const PrivacyPolicy = () => {
|
| 4 |
+
return (
|
| 5 |
+
<div className="min-h-screen flex items-center justify-center bg-gradient-to-r from-purple-400 via-pink-500 to-blue-500">
|
| 6 |
+
<div className="bg-white p-8 rounded-lg shadow-md w-full max-w-4xl">
|
| 7 |
+
<h2 className="text-2xl font-bold mb-6 text-center">Privacy Policy</h2>
|
| 8 |
+
<p className="text-gray-700">
|
| 9 |
+
// Insert your privacy policy content here.
|
| 10 |
+
</p>
|
| 11 |
+
</div>
|
| 12 |
+
</div>
|
| 13 |
+
);
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
export default PrivacyPolicy;
|
src/components/ProtectedRoute.jsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Navigate } from 'react-router-dom';
|
| 2 |
+
import {jwtDecode} from 'jwt-decode';
|
| 3 |
+
|
| 4 |
+
const getCookie = (name) => {
|
| 5 |
+
const cookies = document.cookie.split(';');
|
| 6 |
+
console.log(cookies);
|
| 7 |
+
for (let cookie of cookies) {
|
| 8 |
+
const [key, value] = cookie.trim().split('=');
|
| 9 |
+
if (key === name) {
|
| 10 |
+
return value;
|
| 11 |
+
}
|
| 12 |
+
}
|
| 13 |
+
return null;
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
const isTokenValid = (token) => {
|
| 17 |
+
try {
|
| 18 |
+
const decodedToken = jwtDecode(token);
|
| 19 |
+
const currentTime = Date.now() / 1000;
|
| 20 |
+
if (decodedToken.exp < currentTime) {
|
| 21 |
+
return false;
|
| 22 |
+
}
|
| 23 |
+
return true;
|
| 24 |
+
} catch (error) {
|
| 25 |
+
return false;
|
| 26 |
+
}
|
| 27 |
+
};
|
| 28 |
+
function ProtectedRoute({ children }) {
|
| 29 |
+
// const token = getCookie('access-token');
|
| 30 |
+
const token = localStorage.getItem('token');
|
| 31 |
+
|
| 32 |
+
if (!token || !isTokenValid(token)) {
|
| 33 |
+
return <Navigate to="/login" />;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
return children;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
export default ProtectedRoute;
|
src/components/Register.jsx
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState } from 'react';
|
| 2 |
+
import { useNavigate } from 'react-router-dom';
|
| 3 |
+
import axios from 'axios';
|
| 4 |
+
import '../styles/Register.css';
|
| 5 |
+
import { register } from '../services/api';
|
| 6 |
+
|
| 7 |
+
const API_URL = import.meta.env.VITE_API_URL;
|
| 8 |
+
|
| 9 |
+
const Register = () => {
|
| 10 |
+
const [formData, setFormData] = useState({
|
| 11 |
+
first_name: '',
|
| 12 |
+
surname: '',
|
| 13 |
+
email: '',
|
| 14 |
+
phone: '',
|
| 15 |
+
country: '',
|
| 16 |
+
address: '',
|
| 17 |
+
password: '',
|
| 18 |
+
confirm_password: ''
|
| 19 |
+
});
|
| 20 |
+
const [error, setError] = useState(null);
|
| 21 |
+
|
| 22 |
+
const navigate = useNavigate();
|
| 23 |
+
|
| 24 |
+
const handleChange = (e) => {
|
| 25 |
+
setFormData({ ...formData, [e.target.name]: e.target.value });
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
const handleSubmit = async (e) => {
|
| 29 |
+
e.preventDefault();
|
| 30 |
+
const { first_name, surname, email, phone, country, address, password, confirm_password } = formData;
|
| 31 |
+
|
| 32 |
+
if (password !== confirm_password) {
|
| 33 |
+
setError('Passwords do not match');
|
| 34 |
+
return;
|
| 35 |
+
}
|
| 36 |
+
try {
|
| 37 |
+
console.log('Form Data:', formData);
|
| 38 |
+
const response = await register(first_name, surname, email, phone, country, address, password);
|
| 39 |
+
// const response = await axios.post(`${API_URL}/api/v1/user/`, formData);
|
| 40 |
+
console.log('Response:', response.data);
|
| 41 |
+
navigate('/login');
|
| 42 |
+
} catch (error) {
|
| 43 |
+
console.error('Registration failed', error);
|
| 44 |
+
if (error.response && error.response.status === 400) {
|
| 45 |
+
setError(error.response.data.detail || 'Registration failed');
|
| 46 |
+
} else {
|
| 47 |
+
setError('An unexpected error occurred');
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
};
|
| 51 |
+
|
| 52 |
+
return (
|
| 53 |
+
<div className="min-h-screen flex items-center justify-center bg-gradient-to-r from-purple-400 via-pink-500 to-blue-500">
|
| 54 |
+
<div className="flex flex-col md:flex-row bg-white rounded-lg shadow-md overflow-hidden w-full max-w-7xl">
|
| 55 |
+
<div className="md:w-1/2 flex justify-center items-center bg-gray-100">
|
| 56 |
+
<img src="/public/HiDigiH.jpg" alt="HiDigi Logo" style={{ height: '250px', width: '250px' }} />
|
| 57 |
+
</div>
|
| 58 |
+
<div className="md:w-1/2 p-8">
|
| 59 |
+
<h2 className="text-2xl font-bold mb-6 text-center">Sign Up</h2>
|
| 60 |
+
<form onSubmit={handleSubmit}>
|
| 61 |
+
{error && <div className="mb-4 text-center text-red-500">{error}</div>}
|
| 62 |
+
<div className="grid grid-cols-2 gap-4">
|
| 63 |
+
<div className="mb-4">
|
| 64 |
+
<label className="block text-gray-700 mb-2">First Name</label>
|
| 65 |
+
<input
|
| 66 |
+
type="text"
|
| 67 |
+
name="first_name"
|
| 68 |
+
value={formData.first_name}
|
| 69 |
+
onChange={handleChange}
|
| 70 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 71 |
+
placeholder="First Name"
|
| 72 |
+
required
|
| 73 |
+
/>
|
| 74 |
+
</div>
|
| 75 |
+
<div className="mb-4">
|
| 76 |
+
<label className="block text-gray-700 mb-2">Surname</label>
|
| 77 |
+
<input
|
| 78 |
+
type="text"
|
| 79 |
+
name="surname"
|
| 80 |
+
value={formData.surname}
|
| 81 |
+
onChange={handleChange}
|
| 82 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 83 |
+
placeholder="Surname"
|
| 84 |
+
required
|
| 85 |
+
/>
|
| 86 |
+
</div>
|
| 87 |
+
<div className="mb-4">
|
| 88 |
+
<label className="block text-gray-700 mb-2">Phone Number</label>
|
| 89 |
+
<input
|
| 90 |
+
type="text"
|
| 91 |
+
name="phone"
|
| 92 |
+
value={formData.phone}
|
| 93 |
+
onChange={handleChange}
|
| 94 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 95 |
+
placeholder="Phone Number"
|
| 96 |
+
required
|
| 97 |
+
/>
|
| 98 |
+
</div>
|
| 99 |
+
<div className="mb-4">
|
| 100 |
+
<label className="block text-gray-700 mb-2">Country</label>
|
| 101 |
+
<input
|
| 102 |
+
type="text"
|
| 103 |
+
name="country"
|
| 104 |
+
value={formData.country}
|
| 105 |
+
onChange={handleChange}
|
| 106 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 107 |
+
placeholder="Country"
|
| 108 |
+
required
|
| 109 |
+
/>
|
| 110 |
+
</div>
|
| 111 |
+
<div className="mb-4">
|
| 112 |
+
<label className="block text-gray-700 mb-2">Address</label>
|
| 113 |
+
<input
|
| 114 |
+
type="text"
|
| 115 |
+
name="address"
|
| 116 |
+
value={formData.address}
|
| 117 |
+
onChange={handleChange}
|
| 118 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 119 |
+
placeholder="Address"
|
| 120 |
+
required
|
| 121 |
+
/>
|
| 122 |
+
</div>
|
| 123 |
+
<div className="mb-4">
|
| 124 |
+
<label className="block text-gray-700 mb-2">E-Mail</label>
|
| 125 |
+
<input
|
| 126 |
+
type="email"
|
| 127 |
+
name="email"
|
| 128 |
+
value={formData.email}
|
| 129 |
+
onChange={handleChange}
|
| 130 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 131 |
+
placeholder="E-Mail"
|
| 132 |
+
required
|
| 133 |
+
/>
|
| 134 |
+
</div>
|
| 135 |
+
<div className="mb-4">
|
| 136 |
+
<label className="block text-gray-700 mb-2">Password</label>
|
| 137 |
+
<input
|
| 138 |
+
type="password"
|
| 139 |
+
name="password"
|
| 140 |
+
value={formData.password}
|
| 141 |
+
onChange={handleChange}
|
| 142 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 143 |
+
placeholder="Password"
|
| 144 |
+
required
|
| 145 |
+
/>
|
| 146 |
+
</div>
|
| 147 |
+
<div className="mb-4">
|
| 148 |
+
<label className="block text-gray-700 mb-2">Confirm Password</label>
|
| 149 |
+
<input
|
| 150 |
+
type="password"
|
| 151 |
+
name="confirm_password"
|
| 152 |
+
value={formData.confirm_password}
|
| 153 |
+
onChange={handleChange}
|
| 154 |
+
className="w-full p-3 border border-gray-300 rounded"
|
| 155 |
+
placeholder="Confirm Password"
|
| 156 |
+
required
|
| 157 |
+
/>
|
| 158 |
+
</div>
|
| 159 |
+
</div>
|
| 160 |
+
<button type="submit" className="w-full bg-blue-500 text-white py-3 px-4 rounded mt-4 hover:bg-blue-600">Register</button>
|
| 161 |
+
</form>
|
| 162 |
+
<div className="text-center text-gray-500 text-sm mt-6">
|
| 163 |
+
Already have an account? <a href="/login" className="text-blue-500 hover:underline">Sign in</a>
|
| 164 |
+
</div>
|
| 165 |
+
</div>
|
| 166 |
+
</div>
|
| 167 |
+
</div>
|
| 168 |
+
);
|
| 169 |
+
};
|
| 170 |
+
|
| 171 |
+
export default Register;
|
src/components/SideBarGPT.jsx
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import { Link } from 'react-router-dom';
|
| 3 |
+
|
| 4 |
+
const Sidebar = () => {
|
| 5 |
+
return (
|
| 6 |
+
<div className="h-screen w-64 bg-gray-800 text-white flex flex-col fixed">
|
| 7 |
+
<div className="p-4 text-2xl font-bold">Dashboard</div>
|
| 8 |
+
<nav className="mt-4 flex flex-col space-y-2">
|
| 9 |
+
<Link to="/" className="px-4 py-2 hover:bg-gray-700">Chat</Link>
|
| 10 |
+
<Link to="/analytics" className="px-4 py-2 hover:bg-gray-700">Analytics</Link>
|
| 11 |
+
<Link to="/models" className="px-4 py-2 hover:bg-gray-700">Models</Link>
|
| 12 |
+
<Link to="/settings" className="px-4 py-2 hover:bg-gray-700">Settings</Link>
|
| 13 |
+
</nav>
|
| 14 |
+
</div>
|
| 15 |
+
);
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
+
export default Sidebar;
|
src/components/Terms.jsx
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import '../styles/Terms.css';
|
| 2 |
+
|
| 3 |
+
const Terms = () => {
|
| 4 |
+
return (
|
| 5 |
+
<div className="min-h-screen flex items-center justify-center bg-gradient-to-r from-purple-400 via-pink-500 to-blue-500">
|
| 6 |
+
<div className="bg-white p-8 rounded-lg shadow-md w-full max-w-4xl">
|
| 7 |
+
<h2 className="text-2xl font-bold mb-6 text-center">Terms and Conditions</h2>
|
| 8 |
+
<p className="text-gray-700">
|
| 9 |
+
// Insert your terms and conditions content here.
|
| 10 |
+
</p>
|
| 11 |
+
</div>
|
| 12 |
+
</div>
|
| 13 |
+
);
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
export default Terms;
|
src/components/TextToSpeech.jsx
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState } from 'react';
|
| 2 |
+
import { textToSpeech } from '../services/api';
|
| 3 |
+
import languageVoices from '../data/languageVoices'; // Adjust the path as needed
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
function TextToSpeech() {
|
| 7 |
+
const [text, setText] = useState('');
|
| 8 |
+
const [languageCode, setLanguageCode] = useState('en-US');
|
| 9 |
+
const [ssmlGender, setSsmlGender] = useState('NEUTRAL');
|
| 10 |
+
const [name, setName] = useState('en-US-Standard-C');
|
| 11 |
+
const [pitch, setPitch] = useState(0.0);
|
| 12 |
+
const [speakingRate, setSpeakingRate] = useState(1.0);
|
| 13 |
+
const [volumeGainDb, setVolumeGainDb] = useState(0.0);
|
| 14 |
+
const [audioUrl, setAudioUrl] = useState(null);
|
| 15 |
+
// Ensure you have VITE_API_URL set in your .env file
|
| 16 |
+
const API_URL = import.meta.env.VITE_API_URL;
|
| 17 |
+
const handleTextToSpeech = async () => {
|
| 18 |
+
// Call text-to-speech service
|
| 19 |
+
try {
|
| 20 |
+
const response = await textToSpeech(text, languageCode, ssmlGender, name, pitch, speakingRate, volumeGainDb);
|
| 21 |
+
|
| 22 |
+
const baseUrl = new URL(API_URL);
|
| 23 |
+
const audioUrl = `${baseUrl.origin}/static/storage/audio/${response}`;
|
| 24 |
+
console.log(audioUrl)
|
| 25 |
+
setAudioUrl(audioUrl);
|
| 26 |
+
} catch (error) {
|
| 27 |
+
console.error('Text-to-Speech failed:', error);
|
| 28 |
+
}
|
| 29 |
+
};
|
| 30 |
+
|
| 31 |
+
const handleDeleteAudio = () => {
|
| 32 |
+
setAudioUrl(null);
|
| 33 |
+
};
|
| 34 |
+
return (
|
| 35 |
+
<div className="flex justify-center items-center min-h-screen bg-gray-100 p-4">
|
| 36 |
+
<div className="w-full max-w-3xl p-6 bg-white rounded-lg shadow-md">
|
| 37 |
+
<h2 className="text-3xl font-bold mb-6 text-center text-blue-500">Text-to-Speech AI</h2>
|
| 38 |
+
|
| 39 |
+
<div className="mb-6">
|
| 40 |
+
<textarea
|
| 41 |
+
value={text}
|
| 42 |
+
onChange={(e) => setText(e.target.value)}
|
| 43 |
+
className="w-full h-32 p-3 border rounded"
|
| 44 |
+
placeholder="Enter some text here..."
|
| 45 |
+
/>
|
| 46 |
+
</div>
|
| 47 |
+
|
| 48 |
+
<div className="mb-4">
|
| 49 |
+
<label className="block text-gray-700 font-bold mb-2">Volume</label>
|
| 50 |
+
<input
|
| 51 |
+
type="range"
|
| 52 |
+
min="-96.0"
|
| 53 |
+
max="16.0"
|
| 54 |
+
step="0.1"
|
| 55 |
+
value={volumeGainDb}
|
| 56 |
+
onChange={(e) => setVolumeGainDb(parseFloat(e.target.value))}
|
| 57 |
+
className="w-full"
|
| 58 |
+
/>
|
| 59 |
+
<span className="block text-right text-gray-600">{volumeGainDb.toFixed(1)}</span>
|
| 60 |
+
</div>
|
| 61 |
+
|
| 62 |
+
<div className="mb-4">
|
| 63 |
+
<label className="block text-gray-700 font-bold mb-2">Rate</label>
|
| 64 |
+
<input
|
| 65 |
+
type="range"
|
| 66 |
+
min="0.25"
|
| 67 |
+
max="4.0"
|
| 68 |
+
step="0.01"
|
| 69 |
+
value={speakingRate}
|
| 70 |
+
onChange={(e) => setSpeakingRate(parseFloat(e.target.value))}
|
| 71 |
+
className="w-full"
|
| 72 |
+
/>
|
| 73 |
+
<span className="block text-right text-gray-600">{speakingRate.toFixed(2)}</span>
|
| 74 |
+
</div>
|
| 75 |
+
|
| 76 |
+
<div className="mb-4">
|
| 77 |
+
<label className="block text-gray-700 font-bold mb-2">Pitch</label>
|
| 78 |
+
<input
|
| 79 |
+
type="range"
|
| 80 |
+
min="-20.0"
|
| 81 |
+
max="20.0"
|
| 82 |
+
step="0.1"
|
| 83 |
+
value={pitch}
|
| 84 |
+
onChange={(e) => setPitch(parseFloat(e.target.value))}
|
| 85 |
+
className="w-full"
|
| 86 |
+
/>
|
| 87 |
+
<span className="block text-right text-gray-600">{pitch.toFixed(1)}</span>
|
| 88 |
+
</div>
|
| 89 |
+
|
| 90 |
+
<div className="mb-4">
|
| 91 |
+
<label className="block text-gray-700 font-bold mb-2">Language</label>
|
| 92 |
+
<select
|
| 93 |
+
value={languageCode}
|
| 94 |
+
onChange={(e) => {
|
| 95 |
+
setLanguageCode(e.target.value);
|
| 96 |
+
setName(languageVoices[e.target.value][0].name); // Set default voice for selected language
|
| 97 |
+
}}
|
| 98 |
+
className="w-full p-2 border rounded"
|
| 99 |
+
>
|
| 100 |
+
{Object.keys(languageVoices).map((lang) => (
|
| 101 |
+
<option key={lang} value={lang}>
|
| 102 |
+
{lang}
|
| 103 |
+
</option>
|
| 104 |
+
))}
|
| 105 |
+
</select>
|
| 106 |
+
</div>
|
| 107 |
+
|
| 108 |
+
<div className="mb-4">
|
| 109 |
+
<label className="block text-gray-700 font-bold mb-2">Voice</label>
|
| 110 |
+
<select
|
| 111 |
+
value={name}
|
| 112 |
+
onChange={(e) => setName(e.target.value)}
|
| 113 |
+
className="w-full p-2 border rounded"
|
| 114 |
+
>
|
| 115 |
+
{languageVoices[languageCode].map((voice) => (
|
| 116 |
+
<option key={voice.name} value={voice.name}>
|
| 117 |
+
{voice.label}
|
| 118 |
+
</option>
|
| 119 |
+
))}
|
| 120 |
+
</select>
|
| 121 |
+
</div>
|
| 122 |
+
|
| 123 |
+
<div className="flex justify-between mt-6">
|
| 124 |
+
<button
|
| 125 |
+
onClick={handleTextToSpeech} // Stop button handler
|
| 126 |
+
className="w-fit px-4 py-2 bg-blue-500 text-white rounded hover:shadow-lg hover:bg-blue-700 transition duration-200"
|
| 127 |
+
>
|
| 128 |
+
Generate
|
| 129 |
+
</button>
|
| 130 |
+
|
| 131 |
+
</div>
|
| 132 |
+
|
| 133 |
+
{audioUrl && (
|
| 134 |
+
<div className="mt-6">
|
| 135 |
+
<div className="flex items-center">
|
| 136 |
+
<audio controls src={audioUrl} className="w-full">
|
| 137 |
+
Your browser does not support the audio element.
|
| 138 |
+
</audio>
|
| 139 |
+
<button
|
| 140 |
+
onClick={handleDeleteAudio}
|
| 141 |
+
className="ml-4 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-700 transition duration-200"
|
| 142 |
+
>
|
| 143 |
+
Delete
|
| 144 |
+
</button>
|
| 145 |
+
</div>
|
| 146 |
+
</div>
|
| 147 |
+
)}
|
| 148 |
+
</div>
|
| 149 |
+
</div>
|
| 150 |
+
);
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
export default TextToSpeech;
|
src/data/languageVoices.jsx
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const languageVoices = {
|
| 2 |
+
"en-US": [
|
| 3 |
+
{ name: "en-US-Standard-A", label: "Alex" },
|
| 4 |
+
{ name: "en-US-Standard-B", label: "Bob" },
|
| 5 |
+
{ name: "en-US-Standard-C", label: "Charlie" },
|
| 6 |
+
{ name: "en-US-Standard-D", label: "Dave" },
|
| 7 |
+
],
|
| 8 |
+
"en-GB": [
|
| 9 |
+
{ name: "en-GB-Standard-A", label: "David" },
|
| 10 |
+
{ name: "en-GB-Standard-B", label: "Emma" },
|
| 11 |
+
{ name: "en-GB-Standard-C", label: "Geraint" },
|
| 12 |
+
{ name: "en-GB-Standard-D", label: "Brian" },
|
| 13 |
+
],
|
| 14 |
+
"id-ID": [
|
| 15 |
+
{ name: "id-ID-Standard-A", label: "Indra" },
|
| 16 |
+
{ name: "id-ID-Standard-B", label: "Kartika" },
|
| 17 |
+
{ name: "id-ID-Standard-C", label: "Kusuma" },
|
| 18 |
+
{ name: "id-ID-Standard-D", label: "Nadia" },
|
| 19 |
+
|
| 20 |
+
],
|
| 21 |
+
// Add more languages and voices as needed
|
| 22 |
+
};
|
| 23 |
+
|
| 24 |
+
export default languageVoices;
|
| 25 |
+
|
src/index.css
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import 'tailwindcss/base';
|
| 2 |
+
@import 'tailwindcss/components';
|
| 3 |
+
@import 'tailwindcss/utilities';
|
| 4 |
+
|
| 5 |
+
:root {
|
| 6 |
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
| 7 |
+
line-height: 1.5;
|
| 8 |
+
font-weight: 400;
|
| 9 |
+
|
| 10 |
+
color-scheme: light dark;
|
| 11 |
+
color: rgba(255, 255, 255, 0.87);
|
| 12 |
+
background-color: #292727;
|
| 13 |
+
|
| 14 |
+
font-synthesis: none;
|
| 15 |
+
text-rendering: optimizeLegibility;
|
| 16 |
+
-webkit-font-smoothing: antialiased;
|
| 17 |
+
-moz-osx-font-smoothing: grayscale;
|
| 18 |
+
}
|
| 19 |
+
body, html, #root, .App {
|
| 20 |
+
margin: 0;
|
| 21 |
+
padding: 0;
|
| 22 |
+
width: 100%;
|
| 23 |
+
height: 100%;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.App {
|
| 27 |
+
display: flex;
|
| 28 |
+
justify-content: center;
|
| 29 |
+
align-items: center;
|
| 30 |
+
min-height: 100vh;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
a {
|
| 34 |
+
font-weight: 500;
|
| 35 |
+
color: #646cff;
|
| 36 |
+
text-decoration: inherit;
|
| 37 |
+
}
|
| 38 |
+
a:hover {
|
| 39 |
+
color: #535bf2;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
h1 {
|
| 44 |
+
font-size: 3.2em;
|
| 45 |
+
line-height: 1.1;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
button {
|
| 49 |
+
border-radius: 8px;
|
| 50 |
+
border: 1px solid transparent;
|
| 51 |
+
padding: 0.6em 1.2em;
|
| 52 |
+
font-size: 1em;
|
| 53 |
+
font-weight: 500;
|
| 54 |
+
font-family: inherit;
|
| 55 |
+
background-color: #1a1a1a;
|
| 56 |
+
cursor: pointer;
|
| 57 |
+
transition: border-color 0.25s;
|
| 58 |
+
}
|
| 59 |
+
button:hover {
|
| 60 |
+
border-color: #646cff;
|
| 61 |
+
}
|
| 62 |
+
button:focus,
|
| 63 |
+
button:focus-visible {
|
| 64 |
+
outline: 4px auto -webkit-focus-ring-color;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
@media (prefers-color-scheme: light) {
|
| 68 |
+
:root {
|
| 69 |
+
color: #213547;
|
| 70 |
+
background-color: #ffffff;
|
| 71 |
+
}
|
| 72 |
+
a:hover {
|
| 73 |
+
color: #747bff;
|
| 74 |
+
}
|
| 75 |
+
button {
|
| 76 |
+
background-color: #f9f9f9;
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.w-10 {
|
| 81 |
+
width: 2.5rem;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.h-10 {
|
| 85 |
+
height: 2.5rem;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.rounded-full {
|
| 89 |
+
border-radius: 9999px;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
.object-cover {
|
| 93 |
+
object-fit: cover;
|
| 94 |
+
}
|
| 95 |
+
|
src/layouts/MainLayout.jsx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import Sidebar from '../components/SideBarGPT';
|
| 3 |
+
|
| 4 |
+
const MainLayout = ({ children }) => {
|
| 5 |
+
return (
|
| 6 |
+
<div className="flex min-h-screen">
|
| 7 |
+
<Sidebar />
|
| 8 |
+
<div className="flex-1 p-6 bg-gray-100">
|
| 9 |
+
{children}
|
| 10 |
+
</div>
|
| 11 |
+
</div>
|
| 12 |
+
);
|
| 13 |
+
};
|
| 14 |
+
|
| 15 |
+
export default MainLayout;
|
src/main.jsx
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// src/main.jsx
|
| 2 |
+
import React from 'react';
|
| 3 |
+
import ReactDOM from 'react-dom/client';
|
| 4 |
+
import App from './App';
|
| 5 |
+
import './index.css';
|
| 6 |
+
import './chartSetup';
|
| 7 |
+
|
| 8 |
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
| 9 |
+
<React.StrictMode>
|
| 10 |
+
<App />
|
| 11 |
+
</React.StrictMode>
|
| 12 |
+
);
|
src/pages/AnalyticsPage.jsx
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import { Line, Bar, Doughnut } from 'react-chartjs-2';
|
| 3 |
+
import Sidebar from '../components/SideBarGPT';
|
| 4 |
+
|
| 5 |
+
const Analytics = () => {
|
| 6 |
+
// Sample data for charts
|
| 7 |
+
const messagesOverTimeData = {
|
| 8 |
+
labels: ['January', 'February', 'March', 'April', 'May', 'June'],
|
| 9 |
+
datasets: [
|
| 10 |
+
{
|
| 11 |
+
label: 'Messages',
|
| 12 |
+
data: [65, 59, 80, 81, 56, 55],
|
| 13 |
+
fill: false,
|
| 14 |
+
backgroundColor: 'rgba(75,192,192,0.2)',
|
| 15 |
+
borderColor: 'rgba(75,192,192,1)',
|
| 16 |
+
},
|
| 17 |
+
],
|
| 18 |
+
};
|
| 19 |
+
|
| 20 |
+
const userBotMessagesData = {
|
| 21 |
+
labels: ['User', 'Bot'],
|
| 22 |
+
datasets: [
|
| 23 |
+
{
|
| 24 |
+
label: 'Messages',
|
| 25 |
+
data: [300, 450],
|
| 26 |
+
backgroundColor: ['#36A2EB', '#FF6384'],
|
| 27 |
+
},
|
| 28 |
+
],
|
| 29 |
+
};
|
| 30 |
+
|
| 31 |
+
const responseTimeData = {
|
| 32 |
+
labels: ['<1s', '1-2s', '2-3s', '>3s'],
|
| 33 |
+
datasets: [
|
| 34 |
+
{
|
| 35 |
+
label: 'Response Time',
|
| 36 |
+
data: [50, 100, 75, 25],
|
| 37 |
+
backgroundColor: [
|
| 38 |
+
'rgba(255, 99, 132, 0.2)',
|
| 39 |
+
'rgba(54, 162, 235, 0.2)',
|
| 40 |
+
'rgba(255, 206, 86, 0.2)',
|
| 41 |
+
'rgba(75, 192, 192, 0.2)',
|
| 42 |
+
],
|
| 43 |
+
borderColor: [
|
| 44 |
+
'rgba(255, 99, 132, 1)',
|
| 45 |
+
'rgba(54, 162, 235, 1)',
|
| 46 |
+
'rgba(255, 206, 86, 1)',
|
| 47 |
+
'rgba(75, 192, 192, 1)',
|
| 48 |
+
],
|
| 49 |
+
borderWidth: 1,
|
| 50 |
+
},
|
| 51 |
+
],
|
| 52 |
+
};
|
| 53 |
+
|
| 54 |
+
return (
|
| 55 |
+
<div className="flex">
|
| 56 |
+
<Sidebar />
|
| 57 |
+
<div className="ml-64 flex-1 p-6 bg-gray-100">
|
| 58 |
+
<div className="p-4 bg-white rounded-lg shadow">
|
| 59 |
+
<h2 className="text-2xl font-bold mb-4">Analytics</h2>
|
| 60 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
| 61 |
+
<div className="p-4 bg-gray-100 rounded-lg">
|
| 62 |
+
<h3 className="text-xl font-semibold mb-2">Messages Over Time</h3>
|
| 63 |
+
<Line data={messagesOverTimeData} />
|
| 64 |
+
</div>
|
| 65 |
+
<div className="p-4 bg-gray-100 rounded-lg">
|
| 66 |
+
<h3 className="text-xl font-semibold mb-2">User vs Bot Messages</h3>
|
| 67 |
+
<Doughnut data={userBotMessagesData} />
|
| 68 |
+
</div>
|
| 69 |
+
<div className="p-4 bg-gray-100 rounded-lg">
|
| 70 |
+
<h3 className="text-xl font-semibold mb-2">Response Time Distribution</h3>
|
| 71 |
+
<Bar data={responseTimeData} />
|
| 72 |
+
</div>
|
| 73 |
+
</div>
|
| 74 |
+
</div>
|
| 75 |
+
</div>
|
| 76 |
+
</div>
|
| 77 |
+
);
|
| 78 |
+
};
|
| 79 |
+
|
| 80 |
+
export default Analytics;
|
src/pages/ChatPage.jsx
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState } from 'react';
|
| 2 |
+
import ChatMessage from '../components/ChatMessage';
|
| 3 |
+
import Sidebar from '../components/SideBarGPT';
|
| 4 |
+
|
| 5 |
+
const Chat = () => {
|
| 6 |
+
const [messages, setMessages] = useState([]);
|
| 7 |
+
const [input, setInput] = useState('');
|
| 8 |
+
|
| 9 |
+
const handleSend = () => {
|
| 10 |
+
if (input.trim()) {
|
| 11 |
+
const newMessage = {
|
| 12 |
+
text: input,
|
| 13 |
+
timestamp: new Date().toLocaleTimeString(),
|
| 14 |
+
isUser: true,
|
| 15 |
+
};
|
| 16 |
+
setMessages([...messages, newMessage]);
|
| 17 |
+
|
| 18 |
+
// Simulate bot response
|
| 19 |
+
setTimeout(() => {
|
| 20 |
+
const botMessage = {
|
| 21 |
+
text: 'Onboarding clients is a critical process that sets the tone for your relationship and ensures that the client understands and is comfortable with your services. Here\'s a detailed procedure for onboarding clients, tailored to a technology or software development company:',
|
| 22 |
+
timestamp: new Date().toLocaleTimeString(),
|
| 23 |
+
isUser: false,
|
| 24 |
+
};
|
| 25 |
+
setMessages((prevMessages) => [...prevMessages, botMessage]);
|
| 26 |
+
}, 1000);
|
| 27 |
+
|
| 28 |
+
setInput('');
|
| 29 |
+
}
|
| 30 |
+
};
|
| 31 |
+
|
| 32 |
+
return (
|
| 33 |
+
<div className="flex">
|
| 34 |
+
<Sidebar />
|
| 35 |
+
<div className="ml-64 flex-1 p-6 bg-gray-100">
|
| 36 |
+
<div className="p-4 bg-white rounded-lg shadow">
|
| 37 |
+
<h2 className="text-2xl font-bold mb-4">Chat</h2>
|
| 38 |
+
<div className="flex flex-col h-96 bg-gray-100 rounded-lg overflow-hidden">
|
| 39 |
+
<div className="flex-1 p-4 overflow-y-auto">
|
| 40 |
+
{messages.map((message, index) => (
|
| 41 |
+
<ChatMessage key={index} message={message} isUser={message.isUser} />
|
| 42 |
+
))}
|
| 43 |
+
</div>
|
| 44 |
+
<div className="flex p-4 border-t">
|
| 45 |
+
<input
|
| 46 |
+
type="text"
|
| 47 |
+
className="flex-1 p-2 border rounded-lg"
|
| 48 |
+
value={input}
|
| 49 |
+
onChange={(e) => setInput(e.target.value)}
|
| 50 |
+
placeholder="Type a message..."
|
| 51 |
+
/>
|
| 52 |
+
<button
|
| 53 |
+
onClick={handleSend}
|
| 54 |
+
className="ml-4 px-4 py-2 bg-blue-600 text-white rounded-lg"
|
| 55 |
+
>
|
| 56 |
+
Send
|
| 57 |
+
</button>
|
| 58 |
+
</div>
|
| 59 |
+
</div>
|
| 60 |
+
</div>
|
| 61 |
+
</div>
|
| 62 |
+
</div>
|
| 63 |
+
);
|
| 64 |
+
};
|
| 65 |
+
|
| 66 |
+
export default Chat;
|
src/pages/DocumentManagement.jsx
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import Sidebar from '../components/SideBarGPT';
|
| 3 |
+
|
| 4 |
+
const DocumentManagement = () => {
|
| 5 |
+
return (
|
| 6 |
+
<div className="flex">
|
| 7 |
+
<Sidebar />
|
| 8 |
+
<div className="ml-64 flex-1 p-6 bg-gray-100">
|
| 9 |
+
<div className="p-4 bg-white rounded-lg shadow">
|
| 10 |
+
<h2 className="text-2xl font-bold mb-4">Selected MDL List</h2>
|
| 11 |
+
<div className="space-y-4">
|
| 12 |
+
<div className="p-4 bg-blue-50 rounded-lg">
|
| 13 |
+
<h3 className="font-semibold">Project Details</h3>
|
| 14 |
+
</div>
|
| 15 |
+
<div className="p-4 bg-blue-50 rounded-lg">
|
| 16 |
+
<h3 className="font-semibold">SO Details</h3>
|
| 17 |
+
</div>
|
| 18 |
+
<div className="p-4 bg-blue-50 rounded-lg">
|
| 19 |
+
<h3 className="font-semibold">Master Document List</h3>
|
| 20 |
+
<table className="min-w-full bg-white rounded-lg">
|
| 21 |
+
<thead className="bg-gray-200">
|
| 22 |
+
<tr>
|
| 23 |
+
<th className="py-2 px-4">Document Title</th>
|
| 24 |
+
<th className="py-2 px-4">Planned Date</th>
|
| 25 |
+
<th className="py-2 px-4">Doc. No</th>
|
| 26 |
+
<th className="py-2 px-4">Latest Rev No</th>
|
| 27 |
+
<th className="py-2 px-4">Submission Dt</th>
|
| 28 |
+
<th className="py-2 px-4">Commented Dt</th>
|
| 29 |
+
<th className="py-2 px-4">Approved Dt</th>
|
| 30 |
+
<th className="py-2 px-4">Approval Status</th>
|
| 31 |
+
<th className="py-2 px-4">Options For Enquiry</th>
|
| 32 |
+
</tr>
|
| 33 |
+
</thead>
|
| 34 |
+
<tbody>
|
| 35 |
+
<tr>
|
| 36 |
+
<td className="border px-4 py-2">Strainer Drawing</td>
|
| 37 |
+
<td className="border px-4 py-2">06/17/2017</td>
|
| 38 |
+
<td className="border px-4 py-2">15201</td>
|
| 39 |
+
<td className="border px-4 py-2">R1</td>
|
| 40 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 41 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 42 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 43 |
+
<td className="border px-4 py-2">APPROVED</td>
|
| 44 |
+
<td className="border px-4 py-2">
|
| 45 |
+
<button className="bg-blue-500 text-white px-2 py-1 rounded">Options</button>
|
| 46 |
+
</td>
|
| 47 |
+
</tr>
|
| 48 |
+
<tr>
|
| 49 |
+
<td className="border px-4 py-2">Strainer QCP</td>
|
| 50 |
+
<td className="border px-4 py-2">08/20/2017</td>
|
| 51 |
+
<td className="border px-4 py-2">EC5024</td>
|
| 52 |
+
<td className="border px-4 py-2">R4</td>
|
| 53 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 54 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 55 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 56 |
+
<td className="border px-4 py-2">APPROVED</td>
|
| 57 |
+
<td className="border px-4 py-2">
|
| 58 |
+
<button className="bg-blue-500 text-white px-2 py-1 rounded">Options</button>
|
| 59 |
+
</td>
|
| 60 |
+
</tr>
|
| 61 |
+
<tr>
|
| 62 |
+
<td className="border px-4 py-2">Motor GA Drawing</td>
|
| 63 |
+
<td className="border px-4 py-2">08/30/2017</td>
|
| 64 |
+
<td className="border px-4 py-2">EC4121</td>
|
| 65 |
+
<td className="border px-4 py-2">R2</td>
|
| 66 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 67 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 68 |
+
<td className="border px-4 py-2">05/07/2017</td>
|
| 69 |
+
<td className="border px-4 py-2">APPROVED</td>
|
| 70 |
+
<td className="border px-4 py-2">
|
| 71 |
+
<button className="bg-blue-500 text-white px-2 py-1 rounded">Options</button>
|
| 72 |
+
</td>
|
| 73 |
+
</tr>
|
| 74 |
+
</tbody>
|
| 75 |
+
</table>
|
| 76 |
+
</div>
|
| 77 |
+
<div className="p-4 bg-blue-50 rounded-lg">
|
| 78 |
+
<h3 className="font-semibold">Revision History: Strainer Drawing</h3>
|
| 79 |
+
<table className="min-w-full bg-white rounded-lg">
|
| 80 |
+
<thead className="bg-gray-200">
|
| 81 |
+
<tr>
|
| 82 |
+
<th className="py-2 px-4">SR. No</th>
|
| 83 |
+
<th className="py-2 px-4">Document Title</th>
|
| 84 |
+
<th className="py-2 px-4">Doc. No</th>
|
| 85 |
+
<th className="py-2 px-4">Latest Rev No</th>
|
| 86 |
+
<th className="py-2 px-4">Submission Dt</th>
|
| 87 |
+
<th className="py-2 px-4">Commented Dt</th>
|
| 88 |
+
<th className="py-2 px-4">Approved Dt</th>
|
| 89 |
+
<th className="py-2 px-4">Approval Status</th>
|
| 90 |
+
<th className="py-2 px-4">Options For Enquiry</th>
|
| 91 |
+
</tr>
|
| 92 |
+
</thead>
|
| 93 |
+
<tbody>
|
| 94 |
+
<tr>
|
| 95 |
+
<td className="border px-4 py-2">1</td>
|
| 96 |
+
<td className="border px-4 py-2">Strainer Drawing</td>
|
| 97 |
+
<td className="border px-4 py-2">15201</td>
|
| 98 |
+
<td className="border px-4 py-2">R0</td>
|
| 99 |
+
<td className="border px-4 py-2">02/07/2017</td>
|
| 100 |
+
<td className="border px-4 py-2">02/07/2017</td>
|
| 101 |
+
<td className="border px-4 py-2">02/07/2017</td>
|
| 102 |
+
<td className="border px-4 py-2">Enquiry</td>
|
| 103 |
+
<td className="border px-4 py-2">
|
| 104 |
+
<button className="bg-blue-500 text-white px-2 py-1 rounded">Options</button>
|
| 105 |
+
</td>
|
| 106 |
+
</tr>
|
| 107 |
+
</tbody>
|
| 108 |
+
</table>
|
| 109 |
+
</div>
|
| 110 |
+
</div>
|
| 111 |
+
</div>
|
| 112 |
+
</div>
|
| 113 |
+
</div>
|
| 114 |
+
);
|
| 115 |
+
};
|
| 116 |
+
|
| 117 |
+
export default DocumentManagement;
|
src/pages/ERPCredentialPage.jsx
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ERPCredential from '../components/ERPCredentials';
|
| 2 |
+
|
| 3 |
+
function ERPCredentialPage() {
|
| 4 |
+
return <ERPCredential />;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export default ERPCredentialPage;
|
src/pages/Home.jsx
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import HomeDashboard from '../components/HomeDashboard';
|
| 2 |
+
|
| 3 |
+
function Home() {
|
| 4 |
+
return <HomeDashboard />;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export default Home;
|
src/pages/LoginPage.jsx
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Login from '../components/Login';
|
| 2 |
+
|
| 3 |
+
function LoginPage() {
|
| 4 |
+
return <Login />;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export default LoginPage;
|
src/pages/OCRDashboardPage.jsx
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import Dashboard from '../components/OCRDashboard';
|
| 3 |
+
|
| 4 |
+
function DashboardPage () {
|
| 5 |
+
return (
|
| 6 |
+
|
| 7 |
+
<Dashboard />
|
| 8 |
+
|
| 9 |
+
);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
export default DashboardPage;
|
src/pages/OCRTemplatePage.jsx
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import OCRTemplate from "../components/OCRTemplate";
|
| 2 |
+
|
| 3 |
+
function OCRTemplatePage() {
|
| 4 |
+
return <OCRTemplate />;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export default OCRTemplatePage;
|
src/pages/TextToSpeechPage.jsx
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import TextToSpeech from '../components/TextToSpeech';
|
| 2 |
+
|
| 3 |
+
function TextToSpeechPage() {
|
| 4 |
+
return <TextToSpeech />;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
export default TextToSpeechPage;
|
src/services/api.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import axios from 'axios';
|
| 2 |
+
import axiosInstance from './axiosInstance';
|
| 3 |
+
|
| 4 |
+
// Ensure you have VITE_API_URL set in your .env file
|
| 5 |
+
const API_URL = import.meta.env.VITE_API_URL;
|
| 6 |
+
|
| 7 |
+
export const register = async (first_name, surname, email, phone, country, address, password) => {
|
| 8 |
+
const data = new URLSearchParams();
|
| 9 |
+
data.append('first_name', first_name);
|
| 10 |
+
data.append('surname', surname);
|
| 11 |
+
data.append('email', email);
|
| 12 |
+
data.append('phone', phone);
|
| 13 |
+
data.append('country', country);
|
| 14 |
+
data.append('address', address);
|
| 15 |
+
data.append('password', password);
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
try {
|
| 20 |
+
console.log('data', data);
|
| 21 |
+
const response = await axios.post(`${API_URL}/api/v1/user/`, data,{
|
| 22 |
+
headers: {
|
| 23 |
+
'Content-Type': 'application/x-www-form-urlencoded',
|
| 24 |
+
'accept': 'application/json',
|
| 25 |
+
},
|
| 26 |
+
});
|
| 27 |
+
return response.data;
|
| 28 |
+
} catch (error) {
|
| 29 |
+
console.error('Registration failed:', error);
|
| 30 |
+
throw error;
|
| 31 |
+
}
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
export const login = async (email, password) => {
|
| 35 |
+
const data = new URLSearchParams();
|
| 36 |
+
data.append('username', email);
|
| 37 |
+
data.append('password', password);
|
| 38 |
+
|
| 39 |
+
try {
|
| 40 |
+
const response = await axios.post(`${API_URL}/token`, data,{
|
| 41 |
+
headers: {
|
| 42 |
+
'Content-Type': 'application/x-www-form-urlencoded',
|
| 43 |
+
'accept': 'application/json',
|
| 44 |
+
},
|
| 45 |
+
});
|
| 46 |
+
return response;
|
| 47 |
+
} catch (error) {
|
| 48 |
+
console.error('Login failed:', error);
|
| 49 |
+
throw error;
|
| 50 |
+
}
|
| 51 |
+
};
|
| 52 |
+
|
| 53 |
+
export const logoutUser = () => {
|
| 54 |
+
localStorage.removeItem('token');
|
| 55 |
+
};
|
| 56 |
+
|
| 57 |
+
export const getTemplates = async (templateName) => {
|
| 58 |
+
try {
|
| 59 |
+
const response = await axiosInstance.get(`${API_URL}/api/v1/ocrtemplate/`, { params: { template_name: templateName } });
|
| 60 |
+
return response.data;
|
| 61 |
+
} catch (error) {
|
| 62 |
+
console.error('Error fetching templates:', error);
|
| 63 |
+
throw error;
|
| 64 |
+
}
|
| 65 |
+
};
|
| 66 |
+
export const createTemplate = async (templateName, fields, userId) => {
|
| 67 |
+
const data = {
|
| 68 |
+
template_name: templateName,
|
| 69 |
+
fields: fields,
|
| 70 |
+
user_id: userId,
|
| 71 |
+
};
|
| 72 |
+
try {
|
| 73 |
+
const response = await axiosInstance.post(`${API_URL}/api/v1/ocrtemplate/`, data);
|
| 74 |
+
return response.data;
|
| 75 |
+
} catch (error) {
|
| 76 |
+
console.error('Error creating template:', error);
|
| 77 |
+
throw error;
|
| 78 |
+
}
|
| 79 |
+
};
|
| 80 |
+
export const createOrUpdateTemplate = async (templateName, fields, userId, updated) => {
|
| 81 |
+
const data = {
|
| 82 |
+
template_name: templateName,
|
| 83 |
+
fields: fields,
|
| 84 |
+
user_id: userId,
|
| 85 |
+
};
|
| 86 |
+
|
| 87 |
+
try {
|
| 88 |
+
let response;
|
| 89 |
+
console.log(updated)
|
| 90 |
+
if (updated) {
|
| 91 |
+
// Update existing template
|
| 92 |
+
response = await axiosInstance.put(`${API_URL}/api/v1/ocrtemplate/templates/`, data);
|
| 93 |
+
} else {
|
| 94 |
+
// Create new template
|
| 95 |
+
response = await axiosInstance.post(`${API_URL}/api/v1/ocrtemplate/`, data);
|
| 96 |
+
}
|
| 97 |
+
return response.data;
|
| 98 |
+
} catch (error) {
|
| 99 |
+
console.error('Error creating/updating template:', error);
|
| 100 |
+
throw error;
|
| 101 |
+
}
|
| 102 |
+
};
|
| 103 |
+
|
| 104 |
+
export const deleteTemplate = async (templateName) => {
|
| 105 |
+
try {
|
| 106 |
+
const response = await axiosInstance.delete(`${API_URL}/api/v1/ocrtemplate/${templateName}`);
|
| 107 |
+
return response.data;
|
| 108 |
+
} catch (error) {
|
| 109 |
+
console.error('Error deleting template:', error);
|
| 110 |
+
throw error;
|
| 111 |
+
}
|
| 112 |
+
};
|
| 113 |
+
export const ocrProcess = async (formData) => {
|
| 114 |
+
try {
|
| 115 |
+
for (let [key, value] of formData.entries()) {
|
| 116 |
+
console.log(`${key}: ${value.name || value}`);
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
const response = await axiosInstance.post(`${API_URL}/api/v1/ocr/process`, formData);
|
| 120 |
+
|
| 121 |
+
// Check for HTTP errors
|
| 122 |
+
if (response.status !== 200) {
|
| 123 |
+
throw new Error(`Error: ${response.statusText}`);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
const data = response.data; // axios automatically parses JSON
|
| 127 |
+
console.log("Success:", data);
|
| 128 |
+
return data; // Return the parsed data
|
| 129 |
+
} catch (error) {
|
| 130 |
+
console.error("Error:", error);
|
| 131 |
+
alert("There was an error processing the files.");
|
| 132 |
+
throw error; // Rethrow the error for further handling if needed
|
| 133 |
+
}
|
| 134 |
+
};
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
export const textToSpeech = async (text, languageCode, ssmlGender, name, pitch, speakingRate, volumeGainDb) => {
|
| 138 |
+
const data = {
|
| 139 |
+
text,
|
| 140 |
+
language_code: languageCode,
|
| 141 |
+
ssml_gender: ssmlGender,
|
| 142 |
+
name,
|
| 143 |
+
pitch,
|
| 144 |
+
speaking_rate: speakingRate,
|
| 145 |
+
volume_gain_db: volumeGainDb
|
| 146 |
+
};
|
| 147 |
+
// Get the token from your storage solution (e.g., localStorage)
|
| 148 |
+
const token = localStorage.getItem('token');
|
| 149 |
+
const headers = {
|
| 150 |
+
'xi-api-key': 'u2', // Replace with the actual API key for the user
|
| 151 |
+
'Content-Type': 'application/json',
|
| 152 |
+
'Authorization': `Bearer ${token}`,
|
| 153 |
+
};
|
| 154 |
+
|
| 155 |
+
try {
|
| 156 |
+
console.log(data)
|
| 157 |
+
// const response = await axios.post(`${API_URL}/api/v1/text-to-speech/`, data, {headers, responseType: 'blob'});
|
| 158 |
+
const response = await axios.post(`${API_URL}/api/v1/text-to-speech/generate`, data, {headers});
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
// Create a URL for the blob and return it
|
| 162 |
+
// const audioUrl = window.URL.createObjectURL(new Blob([response.data]));
|
| 163 |
+
|
| 164 |
+
const audioUrl = response.data.data.key;
|
| 165 |
+
console.log(audioUrl )
|
| 166 |
+
|
| 167 |
+
return audioUrl
|
| 168 |
+
} catch (error) {
|
| 169 |
+
if (error.response) {
|
| 170 |
+
// The request was made and the server responded with a status code
|
| 171 |
+
// that falls out of the range of 2xx
|
| 172 |
+
console.error('Error response data:', error.response.data);
|
| 173 |
+
console.error('Error response status:', error.response.status);
|
| 174 |
+
console.error('Error response headers:', error.response.headers);
|
| 175 |
+
} else if (error.request) {
|
| 176 |
+
// The request was made but no response was received
|
| 177 |
+
console.error('Error request data:', error.request);
|
| 178 |
+
} else {
|
| 179 |
+
// Something happened in setting up the request that triggered an Error
|
| 180 |
+
console.error('Error message:', error.message);
|
| 181 |
+
}
|
| 182 |
+
console.error('Error config:', error.config);
|
| 183 |
+
throw error;
|
| 184 |
+
}
|
| 185 |
+
};
|
src/services/axiosInstance.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import axios from 'axios';
|
| 2 |
+
|
| 3 |
+
const API_URL = import.meta.env.VITE_API_URL;;
|
| 4 |
+
|
| 5 |
+
// Create an axios instance
|
| 6 |
+
const axiosInstance = axios.create({
|
| 7 |
+
baseURL: API_URL,
|
| 8 |
+
});
|
| 9 |
+
|
| 10 |
+
// Add a request interceptor
|
| 11 |
+
axiosInstance.interceptors.request.use(
|
| 12 |
+
(config) => {
|
| 13 |
+
// Get the token from your storage solution (e.g., localStorage)
|
| 14 |
+
const token = localStorage.getItem('token');
|
| 15 |
+
if (token) {
|
| 16 |
+
config.headers['Authorization'] = `Bearer ${token}`;
|
| 17 |
+
}
|
| 18 |
+
// Add any other custom headers here
|
| 19 |
+
config.headers['xi-api-key'] = 'u2';
|
| 20 |
+
|
| 21 |
+
// Set the appropriate Content-Type based on the request data type
|
| 22 |
+
if (config.data instanceof FormData) {
|
| 23 |
+
config.headers['Content-Type'] = 'multipart/form-data';
|
| 24 |
+
} else if (typeof config.data === 'object') {
|
| 25 |
+
config.headers['Content-Type'] = 'application/json';
|
| 26 |
+
}
|
| 27 |
+
return config;
|
| 28 |
+
},
|
| 29 |
+
(error) => {
|
| 30 |
+
// Handle the error
|
| 31 |
+
return Promise.reject(error);
|
| 32 |
+
}
|
| 33 |
+
);
|
| 34 |
+
|
| 35 |
+
// You can also add a response interceptor if you need to handle responses globally
|
| 36 |
+
axiosInstance.interceptors.response.use(
|
| 37 |
+
(response) => response,
|
| 38 |
+
(error) => {
|
| 39 |
+
// Handle the error
|
| 40 |
+
return Promise.reject(error);
|
| 41 |
+
}
|
| 42 |
+
);
|
| 43 |
+
|
| 44 |
+
export default axiosInstance;
|