Compare commits
No commits in common. "feat/x_gpu/chat_gpt_new_version" and "master" have entirely different histories.
feat/x_gpu
...
master
|
|
@ -1,3 +0,0 @@
|
|||
node_modules/
|
||||
.cache/
|
||||
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
node_modules/
|
||||
.cache/
|
||||
public/
|
||||
public
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
FROM nginx:latest
|
||||
|
||||
WORKDIR /
|
||||
|
||||
RUN rm /usr/share/nginx/html/*
|
||||
|
||||
COPY ./public ./usr/share/nginx/html/
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
FROM node:18.19.1-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . ./
|
||||
|
||||
RUN npm install --silent --force
|
||||
|
||||
CMD ["npm", "run", "build"]
|
||||
13
default.conf
13
default.conf
|
|
@ -1,13 +0,0 @@
|
|||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
root /var/www/work_front/html;
|
||||
index index.html index.htm index.nginx-debian.html;
|
||||
|
||||
server_name work_front www.work_front;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from './src/redux/store';
|
||||
|
||||
export const wrapRootElement = ({ element }) => (
|
||||
<Provider store={store}>
|
||||
{element}
|
||||
</Provider>
|
||||
);
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from './src/redux/store';
|
||||
|
||||
export const wrapRootElement = ({ element }) => (
|
||||
<Provider store={store}>
|
||||
{element}
|
||||
</Provider>
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
|
|
@ -8,24 +8,17 @@
|
|||
"gatsby"
|
||||
],
|
||||
"scripts": {
|
||||
"develop": "gatsby develop --host=0.0.0.0",
|
||||
"start": "gatsby develop --host=0.0.0.0",
|
||||
"develop": "gatsby develop",
|
||||
"start": "gatsby develop",
|
||||
"build": "gatsby build",
|
||||
"serve": "gatsby serve",
|
||||
"clean": "gatsby clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
"@reduxjs/toolkit": "^2.6.0",
|
||||
"axios": "^1.8.1",
|
||||
"gatsby": "^5.13.3",
|
||||
"gatsby-plugin-sass": "^6.14.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^7.1.5",
|
||||
"react-tsparticles": "^2.12.2",
|
||||
"gatsby": "^2.32.3",
|
||||
"gatsby-plugin-sass": "^3.2.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"sass": "^1.32.7"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
src/App.js
59
src/App.js
|
|
@ -1,59 +0,0 @@
|
|||
// src/App.js
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
|
||||
|
||||
import LoginPage from './pages/auth/login';
|
||||
import RegisterPage from './pages/auth/register';
|
||||
import DashboardPage from './pages/dashboard';
|
||||
import LandingPage from './pages/landing';
|
||||
import NotFoundPage from './pages/404';
|
||||
|
||||
import FormLogin from './pages/FormLogin';
|
||||
import FormRegister from './pages/FormRegister';
|
||||
import FormModels from './pages/FormModels';
|
||||
import FormRenders from './pages/FormRenders';
|
||||
import FormAi from './pages/FormAi';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Router>
|
||||
<nav style={{ margin: '1rem' }}>
|
||||
<Link to="/login" style={{ marginRight: '10px' }}>
|
||||
Login
|
||||
</Link>
|
||||
<Link to="/register" style={{ marginRight: '10px' }}>
|
||||
Register
|
||||
</Link>
|
||||
<Link to="/landing" style={{ marginRight: '10px' }}>
|
||||
Offer
|
||||
</Link>
|
||||
<Link to="/dashboard" style={{ marginRight: '10px' }}>
|
||||
Dashboard
|
||||
</Link>
|
||||
<Link to="/models" style={{ marginRight: '10px' }}>
|
||||
Models
|
||||
</Link>
|
||||
<Link to="/renders" style={{ marginRight: '10px' }}>
|
||||
Renders
|
||||
</Link>
|
||||
<Link to="/ai-tasks" style={{ marginRight: '10px' }}>
|
||||
AI Tasks
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<Routes>
|
||||
<Route path="/" element={<LoginPage />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/register" element={<RegisterPage />} />
|
||||
<Route path="/landing" element={<LandingPage />} />
|
||||
<Route path="/dashboard/*" element={<DashboardPage />} />
|
||||
<Route path="/models" element={<FormModels />} />
|
||||
<Route path="/renders" element={<FormRenders />} />
|
||||
<Route path="/ai-tasks" element={<FormAi />} />
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import '../styles/general.scss';
|
||||
|
||||
import SmallCubeComponent from './smallCube.js';
|
||||
|
||||
|
||||
const FootComponent = () => {
|
||||
return (
|
||||
<footer>
|
||||
<div className="content-container">
|
||||
<div className="content">
|
||||
<div className="section">
|
||||
<SmallCubeComponent />
|
||||
</div>
|
||||
<div className="section">
|
||||
<h4>
|
||||
<a>
|
||||
BUSINESS
|
||||
</a>
|
||||
</h4>
|
||||
<h4>
|
||||
<a>
|
||||
SIGN IN
|
||||
</a>
|
||||
</h4>
|
||||
<h4>
|
||||
<a>
|
||||
SIGN UP
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="section">
|
||||
<h4>
|
||||
Company
|
||||
</h4>
|
||||
</div>
|
||||
<div className="section">
|
||||
<a>
|
||||
About Us
|
||||
</a>
|
||||
</div>
|
||||
<div className="section">
|
||||
<a>
|
||||
Contact Us
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="section">
|
||||
<h4>
|
||||
Resources
|
||||
</h4>
|
||||
</div>
|
||||
<div className="section">
|
||||
<a>
|
||||
Blog
|
||||
</a>
|
||||
</div>
|
||||
<div className="section">
|
||||
<a>
|
||||
Help Center
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="section">
|
||||
</div>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="section">
|
||||
</div>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="section">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bottom">
|
||||
<p>
|
||||
© {new Date().getFullYear()} FehuDev. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default FootComponent
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
import React, { useEffect } from 'react'
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { modelCrudSelector } from '../../../redux/slices/modelCrudSlice'
|
||||
import modelCrudAsyncThunk from '../../../redux/asyncThunks/modelCrudAsyncThunk'
|
||||
|
||||
import { GeneralAddress } from '../../../redux/asyncThunks/abstracts/abstractAddress'
|
||||
|
||||
import FormGenerator from '../formGenerator'
|
||||
|
||||
|
||||
const ModelShowAndDownloadForm = () => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const { models_list } = useSelector( modelCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
let blocker = false
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if ( models_list.length === 0 && user.id > 0 && token !== '' && !blocker ) {
|
||||
dispatch( modelCrudAsyncThunk.fetchGetAllModels(token) )
|
||||
if ( models_list.length === 0 ) {
|
||||
blocker = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Download',
|
||||
endpint: 'model/download',
|
||||
button_value: 'Refresh Models List'
|
||||
},
|
||||
{
|
||||
type: 'links-listing',
|
||||
name: 'Models',
|
||||
values: models_list,
|
||||
link: GeneralAddress + '/model/',
|
||||
}
|
||||
]
|
||||
|
||||
const handleRefresh = () => {
|
||||
dispatch( modelCrudAsyncThunk.fetchGetAllModels(token) )
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="float_form_model"
|
||||
style={ { marginTop: '10%' , overflowY: 'scroll', height: '500px' } }
|
||||
>
|
||||
<FormGenerator
|
||||
inputList={ inputList }
|
||||
refList={ [] }
|
||||
action={ handleRefresh }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default ModelShowAndDownloadForm
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { threeDModelCrudSelector } from '../../../redux/slices/threeDModelCrudSlice'
|
||||
import { uploadModel } from '../../../redux/asyncThunks/threeDModelCrudAsyncThunk'
|
||||
|
||||
import FormGenerator from '../formGenerator'
|
||||
|
||||
const ModelUploadForm = () => {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const [blend, setBlend] = useState('')
|
||||
const [blendInfo, setBlendInfo] = useState('Drop/Click\nfor upload "*.blend" file')
|
||||
|
||||
const { upload_blend_file_status } = useSelector( threeDModelCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Upload',
|
||||
endpoint: 'model/upload',
|
||||
button_value: 'Upload Model'
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: 'Model',
|
||||
fileType: 'blender',
|
||||
dropInfo: blendInfo,
|
||||
setDropInfo: setBlendInfo,
|
||||
file: blend,
|
||||
setFile: setBlend
|
||||
}
|
||||
]
|
||||
|
||||
const handleModelUpload = () => {
|
||||
if (!blend) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch( uploadModel({
|
||||
user_id: user.id,
|
||||
file: blend,
|
||||
token: token
|
||||
}));
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormGenerator
|
||||
inputList={ inputList }
|
||||
refList={ [] }
|
||||
action={ handleModelUpload }
|
||||
/>
|
||||
<p>
|
||||
{
|
||||
typeof upload_blend_file_status === 'string'
|
||||
? ''
|
||||
: 'info' in upload_blend_file_status
|
||||
? upload_blend_file_status.info
|
||||
: ''
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelUploadForm
|
||||
|
|
@ -1,265 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { renderWebsocketSelector } from '../../redux/slices/renderWebsocketSlice'
|
||||
import { connect, saveMessage, resetMessages, disconnect } from '../../redux/slices/renderWebsocketSlice'
|
||||
|
||||
import FormGenerator from './formGenerator'
|
||||
|
||||
|
||||
const ProgressBar = () => {
|
||||
|
||||
const {
|
||||
percents,
|
||||
connect
|
||||
} = useSelector( renderWebsocketSelector )
|
||||
|
||||
return (
|
||||
<div className="progress_bar_form">
|
||||
<div>
|
||||
{
|
||||
typeof percents === 'number'
|
||||
? 'Progress: ' + percents + '%'
|
||||
: percents
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
{
|
||||
!isNaN(percents)
|
||||
&& percents.toString().indexOf('.') != -1
|
||||
? <div
|
||||
className='progress_bar_progress'
|
||||
style={ { width: percents + '%', height: '100%', backgroundColor: 'green' } }
|
||||
>
|
||||
</div>
|
||||
: percents === 'render success'
|
||||
? <div
|
||||
className='progress_bar_progress'
|
||||
style={ { width: '100%', height: '100%', backgroundColor: 'green' } }
|
||||
>
|
||||
</div>
|
||||
: <></>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
const AbstractWebsocket = ({ addressWS, inputList, refList, bodyComparer }) => {
|
||||
|
||||
const [web_socket, setWebsocket] = useState(null)
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const {
|
||||
web_socket_address,
|
||||
address,
|
||||
room_uuid,
|
||||
messages,
|
||||
connected
|
||||
} = useSelector( renderWebsocketSelector )
|
||||
|
||||
|
||||
const handleConnect = ( event ) => {
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
if ( web_socket === null && address === '' && room_uuid === '') {
|
||||
dispatch(
|
||||
connect(
|
||||
{
|
||||
address: addressWS
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
const handleSendMessage = ( refs ) => {
|
||||
|
||||
let body = bodyComparer( refs )
|
||||
|
||||
console.log( body )
|
||||
try {
|
||||
|
||||
dispatch(
|
||||
saveMessage(
|
||||
{
|
||||
message: { info: 0 } // start
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
web_socket.send(
|
||||
JSON.stringify(
|
||||
body
|
||||
)
|
||||
)
|
||||
|
||||
} catch (error) {
|
||||
|
||||
web_socket.close()
|
||||
dispatch(
|
||||
disconnect()
|
||||
)
|
||||
setWebsocket(null)
|
||||
console.log(error)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const handleDisconnect = ( event ) => {
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
if ( web_socket !== null && address !== '' && room_uuid !== '' ) {
|
||||
try {
|
||||
web_socket.close()
|
||||
dispatch(
|
||||
disconnect()
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
} else if ( address !== '' && room_uuid !== '' ) {
|
||||
dispatch(
|
||||
disconnect()
|
||||
)
|
||||
}
|
||||
|
||||
setWebsocket(null)
|
||||
|
||||
console.log('disconnect')
|
||||
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
||||
if ( web_socket_address !== '' && web_socket === null ) {
|
||||
|
||||
console.log( 'connect' )
|
||||
|
||||
setWebsocket(new WebSocket( web_socket_address ))
|
||||
}
|
||||
|
||||
if ( web_socket !== null ) {
|
||||
|
||||
web_socket.onmessage = (event) => {
|
||||
console.log( event.data )
|
||||
if ( messages[ messages.length - 1 ] !== JSON.parse( event.data ) ) {
|
||||
dispatch(
|
||||
saveMessage(
|
||||
{
|
||||
message: JSON.parse( event.data )
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="float_form_render_async"
|
||||
style={
|
||||
connected
|
||||
? addressWS === '/vector/single/set/' || addressWS === '/vector/single/image/'
|
||||
? { marginTop: '10%', overflowY: 'scroll' }
|
||||
: { marginTop: '10%' }
|
||||
: { marginTop: '100%' }
|
||||
}
|
||||
>
|
||||
<FormGenerator
|
||||
inputList={ inputList }
|
||||
refList={ refList }
|
||||
action={ handleSendMessage }
|
||||
/>
|
||||
</div>
|
||||
<div className="float_form_connect">
|
||||
<p>
|
||||
{ addressWS }
|
||||
</p>
|
||||
<ProgressBar />
|
||||
{
|
||||
connected
|
||||
?
|
||||
<button onClick={ (event) => handleDisconnect(event) }>
|
||||
Disconnect
|
||||
</button>
|
||||
:
|
||||
<button onClick={ (event) => handleConnect(event) }>
|
||||
Connect
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
className="float_form_console"
|
||||
style={ connected ? { marginTop: '25%', height: '200px' } : { marginTop: '15%', height: '10px' } }
|
||||
>
|
||||
<p>
|
||||
console:
|
||||
</p>
|
||||
<div className="console_data">
|
||||
{
|
||||
messages.slice(0).reverse().map( (item) => {
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{
|
||||
Object.keys(item).map( (key) => {
|
||||
|
||||
if ( key == 'details' ) {
|
||||
return (
|
||||
<>
|
||||
{
|
||||
Object.keys(item[key]).map( (keyTwo) => {
|
||||
return (
|
||||
<div>
|
||||
{ ' ' + keyTwo + ': ' + item[key][keyTwo] + ' ' }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
} else if ( key === 'image' ) {
|
||||
return (
|
||||
<img
|
||||
src={ 'data:image/png;base64, ' + item[key] }
|
||||
style={ { width: '300px', height: '150px' } }
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{ key + ': ' + item[key] + ' ' }
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AbstractWebsocket
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { aiModelCrudSelector } from '../../../redux/slices/aiModelCrudSlice'
|
||||
import { uploadModel } from '../../../redux/asyncThunks/aiModelCrudAsyncThunk'
|
||||
|
||||
import FormGenerator from '../formGenerator'
|
||||
|
||||
const AIModelUploadForm = () => {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const [model, setModel] = useState('')
|
||||
const [modelInfo, setModelInfo] = useState('Drop/Click\nfor upload AI model file')
|
||||
|
||||
const { upload_model_status } = useSelector(aiModelCrudSelector)
|
||||
const { user, token } = useSelector(userAuthSelector)
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Upload',
|
||||
endpoint: 'ai-model/upload',
|
||||
button_value: 'Upload AI Model'
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: 'Model',
|
||||
fileType: 'ai',
|
||||
dropInfo: modelInfo,
|
||||
setDropInfo: setModelInfo,
|
||||
file: model,
|
||||
setFile: setModel
|
||||
}
|
||||
]
|
||||
|
||||
const handleModelUpload = () => {
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(uploadModel({
|
||||
user_id: user.id,
|
||||
file: model,
|
||||
token: token
|
||||
}));
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
refList={[]}
|
||||
action={handleModelUpload}
|
||||
/>
|
||||
<p>
|
||||
{
|
||||
!upload_model_status
|
||||
? ''
|
||||
: typeof upload_model_status === 'string'
|
||||
? ''
|
||||
: 'info' in upload_model_status
|
||||
? upload_model_status.info
|
||||
: ''
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AIModelUploadForm
|
||||
|
|
@ -1,674 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import passwordVisibleImg from '../../images/password-visible.png'
|
||||
import passwordHiddenImg from '../../images/password-hidden.png'
|
||||
|
||||
/**
|
||||
*
|
||||
* @param { [ {}, {}, ...{} ] } inputList - list of dicts with info about input
|
||||
* @param { } action - fetch method
|
||||
*/
|
||||
export const FormGenerator = ({
|
||||
inputList,
|
||||
action
|
||||
}) => {
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const formData = {};
|
||||
inputList.forEach((singleInput, index) => {
|
||||
if (typeof singleInput.type !== 'undefined' && singleInput.ref?.current) {
|
||||
formData[singleInput.name] = singleInput.ref.current.value;
|
||||
}
|
||||
});
|
||||
action(formData);
|
||||
};
|
||||
|
||||
let info
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="form">
|
||||
{
|
||||
inputList.map((input, key) => {
|
||||
|
||||
if (input.type === 'info') {
|
||||
info = input
|
||||
} else if (input.type === 'text') {
|
||||
return (
|
||||
<TextInputGenerator
|
||||
input={input}
|
||||
info={info}
|
||||
key={key}
|
||||
/>
|
||||
)
|
||||
} else if (input.type === 'password') {
|
||||
return (
|
||||
<PasswordInputGenerator
|
||||
input={input}
|
||||
info={info}
|
||||
key={key}
|
||||
/>
|
||||
)
|
||||
} else if (input.type === 'links-listing') {
|
||||
return (
|
||||
<DownloadFilesListInputGenerator
|
||||
input={input}
|
||||
info={info}
|
||||
key={key}
|
||||
/>
|
||||
)
|
||||
} else if (input.type === 'file') {
|
||||
return (
|
||||
<UploadInputGenerator
|
||||
input={input}
|
||||
info={info}
|
||||
key={key}
|
||||
/>
|
||||
)
|
||||
} else if (input.type === 'choice-listing') {
|
||||
return (
|
||||
<ChoiceListingGenerator
|
||||
input={input}
|
||||
info={info}
|
||||
key={key}
|
||||
/>
|
||||
)
|
||||
} else if (input.type === 'range') {
|
||||
return (
|
||||
<RangeInputGenerator
|
||||
input={input}
|
||||
info={info}
|
||||
key={key}
|
||||
/>
|
||||
)
|
||||
} else if (input.type === 'vector') {
|
||||
return (
|
||||
<VectorInputGenerator
|
||||
input={input}
|
||||
info={info}
|
||||
key={key}
|
||||
/>
|
||||
)
|
||||
} else if (input.type === 'label') {
|
||||
return (
|
||||
<div className="form-field">
|
||||
<label>{input.name}</label>
|
||||
<div className="label-value">{input.value}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
{
|
||||
info.button_value === ''
|
||||
? <></>
|
||||
: <button
|
||||
type='submit'
|
||||
disabled={!info.allowButtonAction}
|
||||
className={!info.allowButtonAction ? "button-disabled" : ""}
|
||||
>
|
||||
{info.button_value}
|
||||
</button>
|
||||
}
|
||||
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Text input generator, example:
|
||||
* @param {
|
||||
* {
|
||||
* type: 'text',
|
||||
* name: 'name',
|
||||
* ref: React.createRef(),
|
||||
* onChange: null OR validationFunc
|
||||
* validationInfo: null OR useState("")
|
||||
* } } input - basic text input
|
||||
* @param {
|
||||
* {
|
||||
* type: 'info',
|
||||
* action: 'Update',
|
||||
* endpoint: 'Album'
|
||||
* } } info - information about form
|
||||
*/
|
||||
const TextInputGenerator = ({
|
||||
input, info
|
||||
}) => {
|
||||
|
||||
const [textInputValidationInfo, setTextInputValidationInfo] = useState("Empty")
|
||||
|
||||
const inputRegex = /^[^'";<>=]+$/
|
||||
|
||||
const defaultValidation = (event) => {
|
||||
if (event.target.value === "") {
|
||||
setTextInputValidationInfo("Empty")
|
||||
} else if (!inputRegex.test(event.target.value)) {
|
||||
setTextInputValidationInfo("Please provide correct value")
|
||||
} else {
|
||||
setTextInputValidationInfo("Success")
|
||||
}
|
||||
input.validationInfo = textInputValidationInfo
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form-field">
|
||||
<label>
|
||||
{input.name}
|
||||
</label>
|
||||
<input
|
||||
id={input.name + info.action + info.endpoint + 'Input'}
|
||||
autoComplete='off'
|
||||
ref={input.ref}
|
||||
onChange={input.onChange === null ? defaultValidation : input.onChange}
|
||||
className={
|
||||
[ "Empty", "Success"].includes(
|
||||
input.validationInfo === null ? textInputValidationInfo : input.validationInfo
|
||||
) ? "" : "input-incorrect"
|
||||
}
|
||||
placeholder={input.placeholder === null ? "" : input.placeholder}
|
||||
/>
|
||||
<div
|
||||
className="popup"
|
||||
style={
|
||||
[ "Empty", "Success"].includes(
|
||||
input.validationInfo === null ? textInputValidationInfo : input.validationInfo
|
||||
) ? {"display": "none", "height": "0px"} : {"display": "block"}
|
||||
}
|
||||
>
|
||||
<div className="popup-content">
|
||||
{ input.validationInfo === null ? textInputValidationInfo : input.validationInfo }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Text input generator, example:
|
||||
* @param {
|
||||
* {
|
||||
* type: 'password',
|
||||
* name: 'name',
|
||||
* ref: React.createRef()
|
||||
* } } input - basic text input
|
||||
* @param {
|
||||
* {
|
||||
* type: 'info',
|
||||
* action: 'Update'
|
||||
* endpoint: 'Album'
|
||||
* } } info - information about form
|
||||
*/
|
||||
const PasswordInputGenerator = ({
|
||||
input, info
|
||||
}) => {
|
||||
|
||||
const [contentIsHidden, setContentIsHidden] = useState(true)
|
||||
|
||||
return (
|
||||
<div className="form-field">
|
||||
<label>
|
||||
{ input.name }
|
||||
</label>
|
||||
<div className="horizontal-container-input">
|
||||
<input
|
||||
id={input.name + info.action + info.endpoint + 'Input'}
|
||||
autoComplete='off'
|
||||
ref={input.ref}
|
||||
type={ contentIsHidden ? 'password' : 'text' }
|
||||
onChange={input.onChange}
|
||||
className={ [ "Empty", "Success"].includes(input.validationInfo) ? "" : "input-incorrect" }
|
||||
/>
|
||||
<img
|
||||
src={ contentIsHidden ? passwordHiddenImg : passwordVisibleImg }
|
||||
className={ [ "Empty", "Success"].includes(input.validationInfo) ? "" : "input-visible-incorrect" }
|
||||
onClick={ () => { setContentIsHidden(!contentIsHidden) } }
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="popup"
|
||||
style={ [ "Empty", "Success"].includes(input.validationInfo) ? {"display": "none", "height": "0px"} : {"display": "block"} }
|
||||
>
|
||||
<div className="popup-content">
|
||||
{ input.validationInfo }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ObjectIterator = ({
|
||||
object, divPlacer
|
||||
}) => {
|
||||
return(
|
||||
<>
|
||||
{
|
||||
typeof object == "object" ?
|
||||
|
||||
Object.keys( object ).map(
|
||||
( key ) => {
|
||||
return (
|
||||
<div key={ key }>
|
||||
{ divPlacer( object[key] ) }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
:
|
||||
|
||||
object
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Text input generator, example:
|
||||
* @param {
|
||||
* {
|
||||
* type: 'drop-box',
|
||||
* name: 'name',
|
||||
* values: list,
|
||||
* link: link to the file
|
||||
* } } input - basic text input
|
||||
* @param {
|
||||
* {
|
||||
* type: 'info',
|
||||
* action: 'Update'
|
||||
* endpoint: 'Album'
|
||||
* } } info - information about form
|
||||
*/
|
||||
const DownloadFilesListInputGenerator = ({
|
||||
input, info
|
||||
}) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
id={input.name + info.action + info.endpoint + 'DropBox'}
|
||||
>
|
||||
{input.name + ':'}
|
||||
{
|
||||
input.values.length == 0 ?
|
||||
|
||||
() => {
|
||||
return (
|
||||
<div>
|
||||
empty
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
: input.values.map( (item, index) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
key={ info.action + '_element_' + index }
|
||||
ls={ item }
|
||||
>
|
||||
{
|
||||
typeof item == 'string' ?
|
||||
|
||||
item
|
||||
|
||||
: Object.keys(item).map(
|
||||
( key, index ) => {
|
||||
|
||||
return(
|
||||
<div style={{ paddingLeft: '10px' }}>
|
||||
{ key + ': ' }
|
||||
{
|
||||
typeof item[key] === "object" ?
|
||||
|
||||
Object.keys( item[key] ).map(
|
||||
( key_two, index ) => {
|
||||
return (
|
||||
<div style={{ paddingLeft: '20px' }}>
|
||||
{ key_two + ': '}
|
||||
{
|
||||
typeof item[key][key_two] == 'object' ?
|
||||
|
||||
Object.keys( item[key][key_two] ).map(
|
||||
(key_three, index) => {
|
||||
return (
|
||||
<div style={{ paddingLeft: '30px' }}>
|
||||
{ key_three + ": " }
|
||||
{ "x: " + item[key][key_two][key_three].x + ", " }
|
||||
{ "y: " + item[key][key_two][key_three].y + ", " }
|
||||
{ "z: " + item[key][key_two][key_three].z + ", " }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
:
|
||||
|
||||
item[key][key_two]
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
:
|
||||
|
||||
item[key]
|
||||
}
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
<br />
|
||||
<br />
|
||||
<a
|
||||
href={ input.link + index + '/' }
|
||||
>
|
||||
download
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ width: '100%', height: '1px', backgroundColor: '#008000' }}>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Text input generator, example:
|
||||
* @param {
|
||||
* {
|
||||
* type: 'choice-listing',
|
||||
* name: 'name',
|
||||
* values: list,
|
||||
* ref: React.createRef()
|
||||
* } } input - basic text input
|
||||
* @param {
|
||||
* {
|
||||
* type: 'info',
|
||||
* action: 'Update'
|
||||
* endpoint: 'Album'
|
||||
* } } info - information about form
|
||||
*/
|
||||
const ChoiceListingGenerator = ({
|
||||
input, info
|
||||
}) => {
|
||||
|
||||
const __handleRef = ( event ) => {
|
||||
event.preventDefault()
|
||||
input.ref.current = {
|
||||
value: event.target.value
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id={input.name + info.action + info.endpoint + 'ChoiceListing'}
|
||||
>
|
||||
{input.name + ':'}
|
||||
<select
|
||||
onChange={ (event) => __handleRef( event ) }
|
||||
>
|
||||
<option
|
||||
value={ '--Select-Model--' }
|
||||
>
|
||||
--Select-Model--
|
||||
</option>
|
||||
{
|
||||
input.values.map( (item) => {
|
||||
return (
|
||||
<option
|
||||
key={ item.model }
|
||||
value={ item.model }
|
||||
>
|
||||
{ item.model.replace('.blend', '') }
|
||||
</option>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file input generator, example:
|
||||
* @param {
|
||||
* {
|
||||
* type: 'file',
|
||||
* name: 'name',
|
||||
* endpoint: 'Album',
|
||||
* fileType: 'image' or 'audio',
|
||||
* dropInfo: dropInfo, setDropInfo: setDropInfo(), #useState
|
||||
* file: file, setFile: setFile() #useState
|
||||
* } } input -
|
||||
*/
|
||||
const UploadInputGenerator = ({
|
||||
input, info
|
||||
}) => {
|
||||
|
||||
const onLoadFile = async (event) => {
|
||||
event.preventDefault()
|
||||
let data = event.target.files[0]
|
||||
input.setFile( data )
|
||||
setDropInfos(data.name, data.size)
|
||||
}
|
||||
|
||||
const onLoadFileDrop = async (event) => {
|
||||
event.preventDefault()
|
||||
event.persist()
|
||||
let data = event.dataTransfer.files[0]
|
||||
input.setFile( data )
|
||||
setDropInfos(data.name, data.size)
|
||||
}
|
||||
|
||||
const toBase64 = async (file) => new Promise((resolve, reject) => {
|
||||
let fileReader = new FileReader()
|
||||
fileReader.readAsDataURL(file)
|
||||
fileReader.onload = () => resolve(fileReader.result)
|
||||
fileReader.onerror = error => reject(error)
|
||||
})
|
||||
|
||||
const setDropInfos = (name, size) => {
|
||||
input.setDropInfo(
|
||||
{
|
||||
name: name,
|
||||
size: (Math.round(size / 100 + 'e-2') / 100) + ' MB'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onDrop={event => onLoadFileDrop(event)}
|
||||
className='upload_input_container'
|
||||
>
|
||||
<p>
|
||||
{
|
||||
typeof input.dropInfo === 'string' ?
|
||||
input.dropInfo
|
||||
:
|
||||
input.dropInfo.name
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
{
|
||||
typeof input.dropInfo === 'string' ?
|
||||
""
|
||||
:
|
||||
input.dropInfo.size
|
||||
}
|
||||
</p>
|
||||
<input
|
||||
id={input.name + info.action + info.endpoint + 'Input'}
|
||||
className='upload_input'
|
||||
type='file'
|
||||
accept={input.fileType + '/*'}
|
||||
autoComplete='off'
|
||||
onChange={event => onLoadFile(event)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Text input generator, example:
|
||||
* @param {
|
||||
* {
|
||||
* type: 'range',
|
||||
* name: 'name',
|
||||
* min: min range value,
|
||||
* max: max range value,
|
||||
* step: step of value,
|
||||
* unit: unit of range value,
|
||||
* ref: React.createRef()
|
||||
* } } input - basic text input
|
||||
* @param {
|
||||
* {
|
||||
* type: 'info',
|
||||
* action: 'Update'
|
||||
* endpoint: 'Album'
|
||||
* } } info - information about form
|
||||
*/
|
||||
const RangeInputGenerator = ({
|
||||
input, info
|
||||
}) => {
|
||||
|
||||
let name = input.name + info.action + info.endpoint + 'Input'
|
||||
|
||||
const [value, setValue] = useState(0)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{input.name + ': ' + value + ' ' + input.unit }
|
||||
</div>
|
||||
<input
|
||||
style={ { width: '380px' } }
|
||||
id={name}
|
||||
name={name}
|
||||
min={input.min}
|
||||
max={input.max}
|
||||
defaultValue={input.min}
|
||||
step={input.step}
|
||||
ref={input.ref}
|
||||
type='range'
|
||||
onChange={ event => setValue( event.target.value ) }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const RangeGenerator = ({
|
||||
key,
|
||||
label,
|
||||
labelStyle,
|
||||
valueStyle,
|
||||
style,
|
||||
name,
|
||||
unit,
|
||||
min,
|
||||
max,
|
||||
defaultValue,
|
||||
step,
|
||||
reference
|
||||
}) => {
|
||||
|
||||
const [value, setValue] = useState(0)
|
||||
|
||||
return (
|
||||
<div style={ { display: 'flex' } }>
|
||||
<div style={ labelStyle }>
|
||||
{ label + ': ' }
|
||||
</div>
|
||||
<input
|
||||
key={key}
|
||||
style={ style }
|
||||
id={ name }
|
||||
name={ name }
|
||||
min={min}
|
||||
max={max}
|
||||
defaultValue={ defaultValue }
|
||||
step={step}
|
||||
ref={reference}
|
||||
type='range'
|
||||
onChange={ event => setValue( event.target.value ) }
|
||||
/>
|
||||
<div style={ valueStyle }>
|
||||
{ value + ' ' + unit }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Text input generator, example:
|
||||
* @param {
|
||||
* {
|
||||
* type: 'vector',
|
||||
* name: 'name',
|
||||
* refDict:
|
||||
* {
|
||||
* x: React.createRef(),
|
||||
* y: React.createRef(),
|
||||
* z: React.createRef()
|
||||
* }
|
||||
* } } input - basic text input
|
||||
* @param {
|
||||
* {
|
||||
* type: 'info',
|
||||
* action: 'Update'
|
||||
* endpoint: 'Album'
|
||||
* } } info - information about form
|
||||
*/
|
||||
const VectorInputGenerator = ({
|
||||
input, info
|
||||
}) => {
|
||||
|
||||
return (
|
||||
<div style={ { width: '' } }>
|
||||
<div style={ { width: '100%' } }>
|
||||
{ input.name }
|
||||
</div>
|
||||
<div style={ { display: 'flex' } }>
|
||||
{
|
||||
Object.keys(input.refDict).map( (key) => {
|
||||
|
||||
let name = input.name + key + info.action + info.endpoint + 'Input'
|
||||
return (
|
||||
<div style={ { display: 'flex', width: '140px' } }>
|
||||
<RangeGenerator
|
||||
key={key}
|
||||
label={key}
|
||||
labelStyle={ { width: '5px', marginTop: '15px' } }
|
||||
valueStyle={ { width: '5px', marginTop: '15px', marginLeft: '5px' } }
|
||||
style={ { width: '80px' } }
|
||||
name={name}
|
||||
unit={ '' }
|
||||
min={input.min[key]}
|
||||
max={input.max[key]}
|
||||
defaultValue={ input.min[key] > 0 ? input.min[key] : 0 }
|
||||
step={0.1}
|
||||
reference={input.refDict[key]}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default FormGenerator
|
||||
|
|
@ -1,308 +0,0 @@
|
|||
import React, { useState, useMemo } from 'react';
|
||||
|
||||
/**
|
||||
* Generic List Generator Component
|
||||
*
|
||||
* @param {Array} data - Array of items to display
|
||||
* @param {string} title - Title of the list
|
||||
* @param {ReactElement|Function} onCreate - Component shown when user clicks "Create +"
|
||||
* @param {ReactElement|Function} onUpdate - Component shown when user clicks "Update"
|
||||
* @param {Function} onDelete - Function called when user clicks "Delete"
|
||||
* @param {Function} onRefresh - Optional function to refresh the list
|
||||
*/
|
||||
export const ListGenerator = ({
|
||||
data = [],
|
||||
title,
|
||||
onCreate = null,
|
||||
onUpdate = null,
|
||||
onDelete = null,
|
||||
onRefresh = null
|
||||
}) => {
|
||||
const columns = data.length > 0
|
||||
? Object.keys(data[0]).filter(key => !key.toLowerCase().includes('id'))
|
||||
: [];
|
||||
const [createVisible, setCreateVisible] = useState(false);
|
||||
const [itemBeingUpdated, setItemBeingUpdated] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedItem, setSelectedItem] = useState(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage, setItemsPerPage] = useState(10);
|
||||
|
||||
const pageOptions = [5, 10, 15, 25, 50];
|
||||
const totalPages = Math.ceil(data.length / itemsPerPage);
|
||||
|
||||
// Oblicz aktualnie wyświetlane elementy
|
||||
const currentItems = useMemo(() => {
|
||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||
return data.slice(startIndex, startIndex + itemsPerPage);
|
||||
}, [data, currentPage, itemsPerPage]);
|
||||
|
||||
// Obsługa zmiany strony
|
||||
const handlePageChange = (newPage) => {
|
||||
setCurrentPage(newPage);
|
||||
setSelectedItem(null);
|
||||
};
|
||||
|
||||
// Obsługa zmiany liczby elementów na stronie
|
||||
const handleItemsPerPageChange = (event) => {
|
||||
const newItemsPerPage = parseInt(event.target.value);
|
||||
setItemsPerPage(newItemsPerPage);
|
||||
setCurrentPage(1);
|
||||
setSelectedItem(null);
|
||||
};
|
||||
|
||||
// Toggle the "create" form
|
||||
const handleToggleCreate = () => {
|
||||
setCreateVisible((prev) => !prev);
|
||||
setItemBeingUpdated(null);
|
||||
setSelectedItem(null);
|
||||
};
|
||||
|
||||
// Toggle update form for a specific item
|
||||
const handleToggleUpdate = (itemId) => {
|
||||
setItemBeingUpdated((prev) => (prev === itemId ? null : itemId));
|
||||
setCreateVisible(false);
|
||||
};
|
||||
|
||||
// Handle delete with confirmation
|
||||
const handleDelete = async (item) => {
|
||||
if (window.confirm('Are you sure you want to delete this item?')) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (typeof onDelete === 'function') {
|
||||
await onDelete(item);
|
||||
if (onRefresh) {
|
||||
await onRefresh();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting item:', error);
|
||||
alert('Failed to delete item. Please try again.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Handle refresh
|
||||
const handleRefresh = async () => {
|
||||
if (onRefresh) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await onRefresh();
|
||||
} catch (error) {
|
||||
console.error('Error refreshing list:', error);
|
||||
alert('Failed to refresh list. Please try again.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Handle item selection
|
||||
const handleItemClick = (item) => {
|
||||
setSelectedItem(item);
|
||||
};
|
||||
|
||||
// Helper to render a component if it's a function or a React element
|
||||
const renderComponent = (component, props = {}) => {
|
||||
if (typeof component === 'function') {
|
||||
return component(props);
|
||||
}
|
||||
return React.cloneElement(component, props);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="list-generator-container">
|
||||
<div className="table-header">
|
||||
{title && <h2>{title}</h2>}
|
||||
<div className="header-actions">
|
||||
{onRefresh && (
|
||||
<button
|
||||
className="refresh-button"
|
||||
onClick={handleRefresh}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? 'Refreshing...' : 'Refresh'}
|
||||
</button>
|
||||
)}
|
||||
{onCreate && (
|
||||
<button
|
||||
className="create-button"
|
||||
onClick={handleToggleCreate}
|
||||
>
|
||||
{createVisible ? 'Close' : `+ ${title || 'Item'}`}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{createVisible && onCreate && (
|
||||
<div className="create-form">
|
||||
{renderComponent(onCreate)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="items-columns">
|
||||
{columns.map((column) => (
|
||||
<div key={column} className="item-column-row">
|
||||
{column.toUpperCase()}
|
||||
</div>
|
||||
))}
|
||||
{(onUpdate || onDelete) && (
|
||||
<div className="item-column-row">Actions</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="items-list">
|
||||
{data.length === 0 ? (
|
||||
<div className="no-data">
|
||||
No items found. {onCreate && `Click '+ ${title || 'Item'}' to add new items.`}
|
||||
</div>
|
||||
) : (
|
||||
currentItems.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className={`item-row ${selectedItem?.id === item.id ? 'selected' : ''}`}
|
||||
onClick={() => handleItemClick(item)}
|
||||
>
|
||||
{Object.entries(item)
|
||||
.filter(([key]) => !key.toLowerCase().includes('id'))
|
||||
.map(([key, value], index) => (
|
||||
<div key={index} className="item-info">
|
||||
{key.toLowerCase() === 'progress' ? (
|
||||
value === 100 ? (
|
||||
<span className="completed-text">Completed</span>
|
||||
) : (
|
||||
<div className="progress-bar">
|
||||
<div
|
||||
className="progress-fill"
|
||||
style={{ width: `${value}%` }}
|
||||
/>
|
||||
<span className="progress-text">{value}%</span>
|
||||
</div>
|
||||
)
|
||||
) : key.toLowerCase() === 'status' ? (
|
||||
<span className={`status-text ${value.toLowerCase().replace(/\s+/g, '-')}`}>
|
||||
{value}
|
||||
</span>
|
||||
) : (
|
||||
<span>{value}</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{(onUpdate || onDelete) && (
|
||||
<div className="action-buttons">
|
||||
{onUpdate && (
|
||||
<button
|
||||
className="update-button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleToggleUpdate(item.id);
|
||||
}}
|
||||
>
|
||||
{itemBeingUpdated === item.id ? 'Close' : 'Update'}
|
||||
</button>
|
||||
)}
|
||||
{onDelete && (
|
||||
<button
|
||||
className="delete-button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDelete(item);
|
||||
}}
|
||||
disabled={isLoading}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{itemBeingUpdated === item.id && onUpdate && (
|
||||
<div className="update-form">
|
||||
{renderComponent(onUpdate, { item })}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{data.length > 0 && (
|
||||
<div className="pagination-controls">
|
||||
<div className="items-per-page">
|
||||
<span>Items per page:</span>
|
||||
<select value={itemsPerPage} onChange={handleItemsPerPageChange}>
|
||||
{pageOptions.map(option => (
|
||||
<option key={option} value={option}>{option}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="pagination-buttons">
|
||||
<button
|
||||
onClick={() => handlePageChange(1)}
|
||||
disabled={currentPage === 1}
|
||||
className="pagination-button"
|
||||
>
|
||||
<<
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
className="pagination-button"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<span className="page-info">
|
||||
Page {currentPage} of {totalPages}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
disabled={currentPage === totalPages}
|
||||
className="pagination-button"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handlePageChange(totalPages)}
|
||||
disabled={currentPage === totalPages}
|
||||
className="pagination-button"
|
||||
>
|
||||
>>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="element-details">
|
||||
{selectedItem ? (
|
||||
<div className="details-content">
|
||||
<h3>Details</h3>
|
||||
{Object.entries(selectedItem)
|
||||
.filter(([key]) => !key.toLowerCase().includes('id'))
|
||||
.map(([key, value]) => (
|
||||
<div key={key} className="detail-row">
|
||||
<span className="detail-label">{key}:</span>
|
||||
<span className="detail-value">
|
||||
{key.toLowerCase() === 'progress' ? (
|
||||
value === 100 ? (
|
||||
<span className="completed-text">Completed</span>
|
||||
) : (
|
||||
<span>{value}%</span>
|
||||
)
|
||||
) : (
|
||||
value
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="no-selection">
|
||||
Select an item to view details
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
import React, { useEffect } from 'react'
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { renderCrudSelector } from '../../../redux/slices/renderCrudSlice'
|
||||
import renderCrudAsyncThunk from '../../../redux/asyncThunks/renderCrudAsyncThunk'
|
||||
|
||||
import { GeneralAddress } from '../../../redux/asyncThunks/abstracts/abstractAddress'
|
||||
|
||||
import FormGenerator from '../formGenerator'
|
||||
|
||||
|
||||
const RenderShowAndDownloadForm = () => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const { render_list } = useSelector( renderCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
let blocker = false
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if ( render_list.length === 0 && user.id > 0 && token !== '' && !blocker ) {
|
||||
dispatch( renderCrudAsyncThunk.fetchGetAllRenders( token ) )
|
||||
if ( render_list.length === 0 ) {
|
||||
blocker = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Download',
|
||||
endpint: 'model/download',
|
||||
button_value: 'Refresh Renders List'
|
||||
},
|
||||
{
|
||||
type: 'links-listing',
|
||||
name: 'Renders',
|
||||
values: render_list,
|
||||
link: GeneralAddress + '/render/',
|
||||
}
|
||||
]
|
||||
|
||||
const handleRefresh = () => {
|
||||
dispatch( renderCrudAsyncThunk.fetchGetAllRenders( token ) )
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='float_form_render_sync'>
|
||||
<FormGenerator
|
||||
inputList={ inputList }
|
||||
refList={ [] }
|
||||
action={ handleRefresh }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default RenderShowAndDownloadForm
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { modelCrudSelector } from '../../../redux/slices/modelCrudSlice'
|
||||
|
||||
import modelCrudAsyncThunk from '../../../redux/asyncThunks/modelCrudAsyncThunk'
|
||||
|
||||
import AbstractWebsocket from '../abstractWebsocket'
|
||||
|
||||
|
||||
const RenderAllForm = () => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const choiceListing = React.createRef()
|
||||
const rotateRange = React.createRef()
|
||||
const resolutionXRange = React.createRef()
|
||||
const resolutionYRange = React.createRef()
|
||||
|
||||
|
||||
const { models_list } = useSelector( modelCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
let refList = [
|
||||
choiceListing,
|
||||
rotateRange,
|
||||
resolutionXRange,
|
||||
resolutionYRange,
|
||||
]
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Async',
|
||||
endpint: 'render/async/all',
|
||||
button_value: 'Render All Sets'
|
||||
},
|
||||
{
|
||||
type: 'choice-listing',
|
||||
name: 'Models',
|
||||
values: models_list,
|
||||
ref: choiceListing
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Rotate',
|
||||
min: 1,
|
||||
max: 360,
|
||||
step: 1,
|
||||
unit: 'deg',
|
||||
ref: rotateRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution X',
|
||||
min: 600,
|
||||
max: 4096,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionXRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution Y',
|
||||
min: 300,
|
||||
max: 3112,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionYRange
|
||||
},
|
||||
]
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
||||
if ( models_list.length === 0 && user.id > 0 && token !== '' ) {
|
||||
dispatch( modelCrudAsyncThunk.fetchGetAllModels( token ) )
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
const bodyComparer = ( refs ) => {
|
||||
return {
|
||||
fileName: refs[0].current.value.replace('.blend', ''),
|
||||
// fileName: 'testHand',
|
||||
angle: refs[1].current.value / 62, // on backend 0.1 - 6.2 value
|
||||
resolutionX: refs[2].current.value,
|
||||
resolutionY: refs[3].current.value,
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AbstractWebsocket
|
||||
addressWS={ '/all/' }
|
||||
inputList={ inputList }
|
||||
refList={ refList }
|
||||
bodyComparer={ bodyComparer }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RenderAllForm
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { modelCrudSelector } from '../../../redux/slices/modelCrudSlice'
|
||||
|
||||
import modelCrudAsyncThunk from '../../../redux/asyncThunks/modelCrudAsyncThunk'
|
||||
|
||||
import AbstractWebsocket from '../abstractWebsocket'
|
||||
|
||||
|
||||
const RenderSingleImageForm = () => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const choiceListing = React.createRef()
|
||||
const setIdRange = React.createRef()
|
||||
const rotateRange = React.createRef()
|
||||
const cameraIdRange = React.createRef()
|
||||
const resolutionXRange = React.createRef()
|
||||
const resolutionYRange = React.createRef()
|
||||
|
||||
const { models_list } = useSelector( modelCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
let refList = [
|
||||
choiceListing,
|
||||
setIdRange,
|
||||
rotateRange,
|
||||
cameraIdRange,
|
||||
resolutionXRange,
|
||||
resolutionYRange
|
||||
]
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Async',
|
||||
endpint: 'render/async/single/image',
|
||||
button_value: 'Render Single Image'
|
||||
},
|
||||
{
|
||||
type: 'choice-listing',
|
||||
name: 'Models',
|
||||
values: models_list,
|
||||
ref: choiceListing
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Set ID',
|
||||
min: 0,
|
||||
max: 87,
|
||||
step: 1,
|
||||
unit: 'set',
|
||||
ref: setIdRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Rotate',
|
||||
min: 1,
|
||||
max: 360,
|
||||
step: 1,
|
||||
unit: 'deg',
|
||||
ref: rotateRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Camera ID',
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 1,
|
||||
unit: 'camera index',
|
||||
ref: cameraIdRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution X',
|
||||
min: 600,
|
||||
max: 4096,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionXRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution Y',
|
||||
min: 300,
|
||||
max: 3112,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionYRange
|
||||
},
|
||||
]
|
||||
|
||||
let blocker = false
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
||||
if ( models_list.length === 0 && user.id > 0 && token !== '' && blocker === false ) {
|
||||
dispatch( modelCrudAsyncThunk.fetchGetAllModels( token ) )
|
||||
if ( models_list.length === 0 ) {
|
||||
blocker = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
const bodyComparer = ( refs ) => {
|
||||
|
||||
return {
|
||||
fileName: refs[0].current.value.replace('.blend', ''),
|
||||
// fileName: 'testHand',
|
||||
setID: refs[1].current.value,
|
||||
rotate: refs[2].current.value / 62, // on backend 0.1 - 6.2 value
|
||||
cameraID: refs[3].current.value,
|
||||
nameSeries: 0,
|
||||
resolutionX: refs[4].current.value,
|
||||
resolutionY: refs[5].current.value
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AbstractWebsocket
|
||||
addressWS={ '/single/image/' }
|
||||
inputList={ inputList }
|
||||
refList={ refList }
|
||||
bodyComparer={ bodyComparer }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RenderSingleImageForm
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { modelCrudSelector } from '../../../redux/slices/modelCrudSlice'
|
||||
|
||||
import modelCrudAsyncThunk from '../../../redux/asyncThunks/modelCrudAsyncThunk'
|
||||
|
||||
import AbstractWebsocket from '../abstractWebsocket'
|
||||
|
||||
|
||||
const RenderSingleSetForm = () => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const choiceListing = React.createRef()
|
||||
const setIdRange = React.createRef()
|
||||
const cameraIdRange = React.createRef()
|
||||
const resolutionXRange = React.createRef()
|
||||
const resolutionYRange = React.createRef()
|
||||
const angleRange = React.createRef()
|
||||
|
||||
const { models_list } = useSelector( modelCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
let refList = [
|
||||
choiceListing,
|
||||
setIdRange,
|
||||
cameraIdRange,
|
||||
resolutionXRange,
|
||||
resolutionYRange,
|
||||
angleRange
|
||||
]
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Async',
|
||||
endpint: 'render/async/single/set',
|
||||
button_value: 'Render Single Set'
|
||||
},
|
||||
{
|
||||
type: 'choice-listing',
|
||||
name: 'Models',
|
||||
values: models_list,
|
||||
ref: choiceListing
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Set ID',
|
||||
min: 0,
|
||||
max: 87,
|
||||
step: 1,
|
||||
unit: 'set',
|
||||
ref: setIdRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Camera ID',
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 1,
|
||||
unit: 'camera index',
|
||||
ref: cameraIdRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution X',
|
||||
min: 600,
|
||||
max: 4096,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionXRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution Y',
|
||||
min: 300,
|
||||
max: 3112,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionYRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Rotate',
|
||||
min: 1,
|
||||
max: 360,
|
||||
step: 1,
|
||||
unit: 'deg',
|
||||
ref: angleRange
|
||||
},
|
||||
]
|
||||
|
||||
let blocker = false
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
||||
if ( models_list.length === 0 && user.id > 0 && token !== '' && blocker === false ) {
|
||||
dispatch( modelCrudAsyncThunk.fetchGetAllModels( token ) )
|
||||
if ( models_list.length === 0 ) {
|
||||
blocker = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
const bodyComparer = ( refs ) => {
|
||||
return {
|
||||
fileName: refs[0].current.value.replace('.blend', ''),
|
||||
// fileName: 'testHand',
|
||||
setID: refs[1].current.value,
|
||||
cameraID: refs[2].current.value,
|
||||
resolutionX: refs[3].current.value,
|
||||
resolutionY: refs[4].current.value,
|
||||
angle: refs[5].current.value / 62, // on backend 0.1 - 6.2 value
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AbstractWebsocket
|
||||
addressWS={ '/single/set/' }
|
||||
inputList={ inputList }
|
||||
refList={ refList }
|
||||
bodyComparer={ bodyComparer }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RenderSingleSetForm
|
||||
|
|
@ -1,445 +0,0 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { modelCrudSelector } from '../../../redux/slices/modelCrudSlice'
|
||||
|
||||
import modelCrudAsyncThunk from '../../../redux/asyncThunks/modelCrudAsyncThunk'
|
||||
|
||||
import AbstractWebsocket from '../abstractWebsocket'
|
||||
|
||||
|
||||
const refVectorGenerator = () => {
|
||||
return {
|
||||
x: React.createRef(),
|
||||
y: React.createRef(),
|
||||
z: React.createRef(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const RenderVectorSingleImageForm = () => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const choiceListing = React.createRef()
|
||||
const rotateRange = React.createRef()
|
||||
const cameraIdRange = React.createRef()
|
||||
const resolutionXRange = React.createRef()
|
||||
const resolutionYRange = React.createRef()
|
||||
|
||||
// const IK_nadgarstek = refVectorGenerator()
|
||||
// const IK_joint3_R = refVectorGenerator()
|
||||
// const IK_joint4_R = refVectorGenerator()
|
||||
// const IK_joint5_R = refVectorGenerator()
|
||||
// const IK_joint6_R = refVectorGenerator()
|
||||
const IK_maly_1_R = refVectorGenerator()
|
||||
const IK_maly_2_R = refVectorGenerator()
|
||||
const IK_maly_3_R = refVectorGenerator()
|
||||
const IK_serdeczny_1_R = refVectorGenerator()
|
||||
const IK_serdeczny_2_R = refVectorGenerator()
|
||||
const IK_serdeczny_3_R = refVectorGenerator()
|
||||
const IK_srodkowy_1_R = refVectorGenerator()
|
||||
const IK_srodkowy_2_R = refVectorGenerator()
|
||||
const IK_srodkowy_3_R = refVectorGenerator()
|
||||
const IK_wskazujacy_1_R = refVectorGenerator()
|
||||
const IK_wskazujacy_2_R = refVectorGenerator()
|
||||
const IK_wskazujacy_3_R = refVectorGenerator()
|
||||
const IK_kciuk_0_R = refVectorGenerator()
|
||||
const IK_kciuk_1_R = refVectorGenerator()
|
||||
const IK_kciuk_2_R = refVectorGenerator()
|
||||
|
||||
|
||||
const { models_list } = useSelector( modelCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
let refList = [
|
||||
choiceListing,
|
||||
rotateRange,
|
||||
cameraIdRange,
|
||||
resolutionXRange,
|
||||
resolutionYRange,
|
||||
{
|
||||
// IK_nadgarstek: IK_nadgarstek,
|
||||
// IK_joint3_R: IK_joint3_R,
|
||||
// IK_joint4_R: IK_joint4_R,
|
||||
// IK_joint5_R: IK_joint5_R,
|
||||
// IK_joint6_R: IK_joint6_R,
|
||||
IK_maly_1_R: IK_maly_1_R,
|
||||
IK_maly_2_R: IK_maly_2_R,
|
||||
IK_maly_3_R: IK_maly_3_R,
|
||||
IK_serdeczny_1_R: IK_serdeczny_1_R,
|
||||
IK_serdeczny_2_R: IK_serdeczny_2_R,
|
||||
IK_serdeczny_3_R: IK_serdeczny_3_R,
|
||||
IK_srodkowy_1_R: IK_srodkowy_1_R,
|
||||
IK_srodkowy_2_R: IK_srodkowy_2_R,
|
||||
IK_srodkowy_3_R: IK_srodkowy_3_R,
|
||||
IK_wskazujacy_1_R: IK_wskazujacy_1_R,
|
||||
IK_wskazujacy_2_R: IK_wskazujacy_2_R,
|
||||
IK_wskazujacy_3_R: IK_wskazujacy_3_R,
|
||||
IK_kciuk_0_R: IK_kciuk_0_R,
|
||||
IK_kciuk_1_R: IK_kciuk_1_R,
|
||||
IK_kciuk_2_R: IK_kciuk_2_R,
|
||||
}
|
||||
]
|
||||
|
||||
const fingers_max = {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
}
|
||||
const fingers_min = {
|
||||
x: -0.7,
|
||||
y: -0.7,
|
||||
z: -0.7
|
||||
}
|
||||
|
||||
const validate_fingers = {
|
||||
thumb: {
|
||||
max:{
|
||||
part_0: {
|
||||
y: 0.5,
|
||||
x: 0.5,
|
||||
z: 0.6
|
||||
},
|
||||
part_1: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0.7,
|
||||
},
|
||||
part_2: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0.7,
|
||||
}
|
||||
},
|
||||
min: {
|
||||
part_0: {
|
||||
y: -0.3,
|
||||
x: 0,
|
||||
z: -0.3,
|
||||
},
|
||||
part_1: {
|
||||
y: -0.1,
|
||||
x: 0,
|
||||
z: 0,
|
||||
},
|
||||
part_2: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: -0.4,
|
||||
}
|
||||
}
|
||||
},
|
||||
others: {
|
||||
max: {
|
||||
part_1: {
|
||||
y: 0.2,
|
||||
x: 0,
|
||||
z: 0.7
|
||||
},
|
||||
part_2: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 1.6
|
||||
},
|
||||
part_3: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0.7
|
||||
}
|
||||
},
|
||||
min: {
|
||||
part_1: {
|
||||
y: -0.3,
|
||||
x: 0,
|
||||
z: -0.3
|
||||
},
|
||||
part_2: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0
|
||||
},
|
||||
part_3: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Async',
|
||||
endpint: 'render/async/single/image',
|
||||
button_value: 'Render Single Image'
|
||||
},
|
||||
{
|
||||
type: 'choice-listing',
|
||||
name: 'Models',
|
||||
values: models_list,
|
||||
ref: choiceListing
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Rotate',
|
||||
min: 1,
|
||||
max: 360,
|
||||
step: 1,
|
||||
unit: 'deg',
|
||||
ref: rotateRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Camera ID',
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 1,
|
||||
unit: 'camera index',
|
||||
ref: cameraIdRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution X',
|
||||
min: 600,
|
||||
max: 4096,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionXRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution Y',
|
||||
min: 300,
|
||||
max: 3112,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionYRange
|
||||
},
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Wirst (experimental)',
|
||||
// native: 'Nadgarstek',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_nadgarstek,
|
||||
// },
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Litte Finger Joint 3 (experimental)',
|
||||
// native: 'Mały Palec Joint 3',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_joint3_R,
|
||||
// },
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Ring Finger Joint 4 (experimental)',
|
||||
// native: 'Serdeczny Palec Joint 4',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_joint4_R,
|
||||
// },
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Middle Finger Joint 5 (experimental)',
|
||||
// native: 'Środkowy Palec Joint 5',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_joint5_R,
|
||||
// },
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Index Finger Joint 6 (experimental)',
|
||||
// native: 'Wskazujący Palec Joint 6',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_joint6_R,
|
||||
// },
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Litte Finger 1',
|
||||
native: 'Mały 1',
|
||||
min: validate_fingers.others.min.part_1,
|
||||
max: validate_fingers.others.max.part_1,
|
||||
refDict: IK_maly_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Litte Finger 2',
|
||||
native: 'Mały 2',
|
||||
min: validate_fingers.others.min.part_2,
|
||||
max: validate_fingers.others.max.part_2,
|
||||
refDict: IK_maly_2_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Litte Finger 3',
|
||||
native: 'Mały 3',
|
||||
min: validate_fingers.others.min.part_3,
|
||||
max: validate_fingers.others.max.part_3,
|
||||
refDict: IK_maly_3_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Ring Finger 1',
|
||||
native: 'Serdeczny 1',
|
||||
min: validate_fingers.others.min.part_1,
|
||||
max: validate_fingers.others.max.part_1,
|
||||
refDict: IK_serdeczny_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Ring Finger 2',
|
||||
native: 'Serdeczny 2',
|
||||
min: validate_fingers.others.min.part_2,
|
||||
max: validate_fingers.others.max.part_2,
|
||||
refDict: IK_serdeczny_2_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Ring Finger 3',
|
||||
native: 'Serdeczny 3',
|
||||
min: validate_fingers.others.min.part_3,
|
||||
max: validate_fingers.others.max.part_3,
|
||||
refDict: IK_serdeczny_3_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Middle Finger 1',
|
||||
native: 'Środkowy 1',
|
||||
min: validate_fingers.others.min.part_1,
|
||||
max: validate_fingers.others.max.part_1,
|
||||
refDict: IK_srodkowy_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Middle Finger 2',
|
||||
native: 'Środkowy 2',
|
||||
min: validate_fingers.others.min.part_2,
|
||||
max: validate_fingers.others.max.part_2,
|
||||
refDict: IK_srodkowy_2_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Middle Finger 3',
|
||||
native: 'Środkowy 3',
|
||||
min: validate_fingers.others.min.part_3,
|
||||
max: validate_fingers.others.max.part_3,
|
||||
refDict: IK_srodkowy_3_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Index Finger 1',
|
||||
native: 'Wskazujący 1',
|
||||
min: validate_fingers.others.min.part_1,
|
||||
max: validate_fingers.others.max.part_1,
|
||||
refDict: IK_wskazujacy_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Index Finger 2',
|
||||
native: 'Wskazujący 2',
|
||||
min: validate_fingers.others.min.part_2,
|
||||
max: validate_fingers.others.max.part_2,
|
||||
refDict: IK_wskazujacy_2_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Index Finger 3',
|
||||
native: 'Wskazujący 3',
|
||||
min: validate_fingers.others.min.part_3,
|
||||
max: validate_fingers.others.max.part_3,
|
||||
refDict: IK_wskazujacy_3_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Thumb 0',
|
||||
native: 'Kciuk 0',
|
||||
min: validate_fingers.thumb.min.part_0,
|
||||
max: validate_fingers.thumb.max.part_0,
|
||||
refDict: IK_kciuk_0_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Thumb 1',
|
||||
native: 'Kciuk 1',
|
||||
min: validate_fingers.thumb.min.part_1,
|
||||
max: validate_fingers.thumb.max.part_1,
|
||||
refDict: IK_kciuk_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Thumb 2',
|
||||
native: 'Kciuk 2',
|
||||
min: validate_fingers.thumb.min.part_2,
|
||||
max: validate_fingers.thumb.max.part_2,
|
||||
refDict: IK_kciuk_2_R,
|
||||
},
|
||||
]
|
||||
|
||||
let blocker = false
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
||||
if ( models_list.length === 0 && user.id > 0 && token !== '' && blocker === false ) {
|
||||
dispatch( modelCrudAsyncThunk.fetchGetAllModels( token ) )
|
||||
if ( models_list.length === 0 ) {
|
||||
blocker = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
const bodyComparer = ( refs ) => {
|
||||
|
||||
let vectors = {}
|
||||
|
||||
console.log( refs )
|
||||
|
||||
Object.keys(refs[5]).map(
|
||||
( key ) => {
|
||||
let singleFinger = {
|
||||
scale: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
singleFinger['scale'] = 0.9
|
||||
singleFinger['x'] = parseFloat(refs[5][key]['x'].current.value)
|
||||
singleFinger['y'] = parseFloat(refs[5][key]['y'].current.value)
|
||||
singleFinger['z'] = parseFloat(refs[5][key]['z'].current.value)
|
||||
vectors[key] = singleFinger
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
fileName: refs[0].current.value.replace('.blend', ''),
|
||||
// fileName: 'testHand',
|
||||
rotate: refs[1].current.value / 62, // on backend 0.1 - 6.2 value
|
||||
cameraID: refs[2].current.value,
|
||||
nameSeries: 0,
|
||||
resolutionX: refs[3].current.value,
|
||||
resolutionY: refs[4].current.value,
|
||||
vectors: vectors
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AbstractWebsocket
|
||||
addressWS={ '/vector/single/image/' }
|
||||
inputList={ inputList }
|
||||
refList={ refList }
|
||||
bodyComparer={ bodyComparer }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RenderVectorSingleImageForm
|
||||
|
|
@ -1,444 +0,0 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { modelCrudSelector } from '../../../redux/slices/modelCrudSlice'
|
||||
|
||||
import modelCrudAsyncThunk from '../../../redux/asyncThunks/modelCrudAsyncThunk'
|
||||
|
||||
import AbstractWebsocket from '../abstractWebsocket'
|
||||
|
||||
|
||||
const refVectorGenerator = () => {
|
||||
return {
|
||||
x: React.createRef(),
|
||||
y: React.createRef(),
|
||||
z: React.createRef(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const RenderVectorSingleSetForm = () => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const choiceListing = React.createRef()
|
||||
const rotateRange = React.createRef()
|
||||
const cameraIdRange = React.createRef()
|
||||
const resolutionXRange = React.createRef()
|
||||
const resolutionYRange = React.createRef()
|
||||
|
||||
// const IK_nadgarstek = refVectorGenerator()
|
||||
// const IK_joint3_R = refVectorGenerator()
|
||||
// const IK_joint4_R = refVectorGenerator()
|
||||
// const IK_joint5_R = refVectorGenerator()
|
||||
// const IK_joint6_R = refVectorGenerator()
|
||||
const IK_maly_1_R = refVectorGenerator()
|
||||
const IK_maly_2_R = refVectorGenerator()
|
||||
const IK_maly_3_R = refVectorGenerator()
|
||||
const IK_serdeczny_1_R = refVectorGenerator()
|
||||
const IK_serdeczny_2_R = refVectorGenerator()
|
||||
const IK_serdeczny_3_R = refVectorGenerator()
|
||||
const IK_srodkowy_1_R = refVectorGenerator()
|
||||
const IK_srodkowy_2_R = refVectorGenerator()
|
||||
const IK_srodkowy_3_R = refVectorGenerator()
|
||||
const IK_wskazujacy_1_R = refVectorGenerator()
|
||||
const IK_wskazujacy_2_R = refVectorGenerator()
|
||||
const IK_wskazujacy_3_R = refVectorGenerator()
|
||||
const IK_kciuk_0_R = refVectorGenerator()
|
||||
const IK_kciuk_1_R = refVectorGenerator()
|
||||
const IK_kciuk_2_R = refVectorGenerator()
|
||||
|
||||
|
||||
const { models_list } = useSelector( modelCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
let refList = [
|
||||
choiceListing,
|
||||
rotateRange,
|
||||
cameraIdRange,
|
||||
resolutionXRange,
|
||||
resolutionYRange,
|
||||
{
|
||||
// IK_nadgarstek: IK_nadgarstek,
|
||||
// IK_joint3_R: IK_joint3_R,
|
||||
// IK_joint4_R: IK_joint4_R,
|
||||
// IK_joint5_R: IK_joint5_R,
|
||||
// IK_joint6_R: IK_joint6_R,
|
||||
IK_maly_1_R: IK_maly_1_R,
|
||||
IK_maly_2_R: IK_maly_2_R,
|
||||
IK_maly_3_R: IK_maly_3_R,
|
||||
IK_serdeczny_1_R: IK_serdeczny_1_R,
|
||||
IK_serdeczny_2_R: IK_serdeczny_2_R,
|
||||
IK_serdeczny_3_R: IK_serdeczny_3_R,
|
||||
IK_srodkowy_1_R: IK_srodkowy_1_R,
|
||||
IK_srodkowy_2_R: IK_srodkowy_2_R,
|
||||
IK_srodkowy_3_R: IK_srodkowy_3_R,
|
||||
IK_wskazujacy_1_R: IK_wskazujacy_1_R,
|
||||
IK_wskazujacy_2_R: IK_wskazujacy_2_R,
|
||||
IK_wskazujacy_3_R: IK_wskazujacy_3_R,
|
||||
IK_kciuk_0_R: IK_kciuk_0_R,
|
||||
IK_kciuk_1_R: IK_kciuk_1_R,
|
||||
IK_kciuk_2_R: IK_kciuk_2_R,
|
||||
}
|
||||
]
|
||||
|
||||
const fingers_max = {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
}
|
||||
const fingers_min = {
|
||||
x: -0.7,
|
||||
y: -0.7,
|
||||
z: -0.7
|
||||
}
|
||||
|
||||
const validate_fingers = {
|
||||
thumb: {
|
||||
max:{
|
||||
part_0: {
|
||||
y: 0.5,
|
||||
x: 0.5,
|
||||
z: 0.6
|
||||
},
|
||||
part_1: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0.7,
|
||||
},
|
||||
part_2: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0.7,
|
||||
}
|
||||
},
|
||||
min: {
|
||||
part_0: {
|
||||
y: -0.3,
|
||||
x: 0,
|
||||
z: -0.3,
|
||||
},
|
||||
part_1: {
|
||||
y: -0.1,
|
||||
x: 0,
|
||||
z: 0,
|
||||
},
|
||||
part_2: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: -0.4,
|
||||
}
|
||||
}
|
||||
},
|
||||
others: {
|
||||
max: {
|
||||
part_1: {
|
||||
y: 0.2,
|
||||
x: 0,
|
||||
z: 0.7
|
||||
},
|
||||
part_2: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 1.6
|
||||
},
|
||||
part_3: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0.7
|
||||
}
|
||||
},
|
||||
min: {
|
||||
part_1: {
|
||||
y: -0.3,
|
||||
x: 0,
|
||||
z: -0.3
|
||||
},
|
||||
part_2: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0
|
||||
},
|
||||
part_3: {
|
||||
y: 0,
|
||||
x: 0,
|
||||
z: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Async',
|
||||
endpint: 'render/async/single/set',
|
||||
button_value: 'Render Single Set'
|
||||
},
|
||||
{
|
||||
type: 'choice-listing',
|
||||
name: 'Models',
|
||||
values: models_list,
|
||||
ref: choiceListing
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Rotate',
|
||||
min: 1,
|
||||
max: 360,
|
||||
step: 1,
|
||||
unit: 'deg',
|
||||
ref: rotateRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Camera ID',
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 1,
|
||||
unit: 'camera index',
|
||||
ref: cameraIdRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution X',
|
||||
min: 600,
|
||||
max: 4096,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionXRange
|
||||
},
|
||||
{
|
||||
type: 'range',
|
||||
name: 'Resolution Y',
|
||||
min: 300,
|
||||
max: 3112,
|
||||
step: 10,
|
||||
unit: 'px',
|
||||
ref: resolutionYRange
|
||||
},
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Wirst (experimental)',
|
||||
// native: 'Nadgarstek',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_nadgarstek,
|
||||
// },
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Litte Finger Joint 3 (experimental)',
|
||||
// native: 'Mały Palec Joint 3',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_joint3_R,
|
||||
// },
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Ring Finger Joint 4 (experimental)',
|
||||
// native: 'Serdeczny Palec Joint 4',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_joint4_R,
|
||||
// },
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Middle Finger Joint 5 (experimental)',
|
||||
// native: 'Środkowy Palec Joint 5',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_joint5_R,
|
||||
// },
|
||||
// {
|
||||
// type: 'vector',
|
||||
// name: 'Index Finger Joint 6 (experimental)',
|
||||
// native: 'Wskazujący Palec Joint 6',
|
||||
// min: fingers_min,
|
||||
// max: fingers_max,
|
||||
// refDict: IK_joint6_R,
|
||||
// },
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Litte Finger 1',
|
||||
native: 'Mały 1',
|
||||
min: validate_fingers.others.min.part_1,
|
||||
max: validate_fingers.others.max.part_1,
|
||||
refDict: IK_maly_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Litte Finger 2',
|
||||
native: 'Mały 2',
|
||||
min: validate_fingers.others.min.part_2,
|
||||
max: validate_fingers.others.max.part_2,
|
||||
refDict: IK_maly_2_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Litte Finger 3',
|
||||
native: 'Mały 3',
|
||||
min: validate_fingers.others.min.part_3,
|
||||
max: validate_fingers.others.max.part_3,
|
||||
refDict: IK_maly_3_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Ring Finger 1',
|
||||
native: 'Serdeczny 1',
|
||||
min: validate_fingers.others.min.part_1,
|
||||
max: validate_fingers.others.max.part_1,
|
||||
refDict: IK_serdeczny_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Ring Finger 2',
|
||||
native: 'Serdeczny 2',
|
||||
min: validate_fingers.others.min.part_2,
|
||||
max: validate_fingers.others.max.part_2,
|
||||
refDict: IK_serdeczny_2_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Ring Finger 3',
|
||||
native: 'Serdeczny 3',
|
||||
min: validate_fingers.others.min.part_3,
|
||||
max: validate_fingers.others.max.part_3,
|
||||
refDict: IK_serdeczny_3_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Middle Finger 1',
|
||||
native: 'Środkowy 1',
|
||||
min: validate_fingers.others.min.part_1,
|
||||
max: validate_fingers.others.max.part_1,
|
||||
refDict: IK_srodkowy_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Middle Finger 2',
|
||||
native: 'Środkowy 2',
|
||||
min: validate_fingers.others.min.part_2,
|
||||
max: validate_fingers.others.max.part_2,
|
||||
refDict: IK_srodkowy_2_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Middle Finger 3',
|
||||
native: 'Środkowy 3',
|
||||
min: validate_fingers.others.min.part_3,
|
||||
max: validate_fingers.others.max.part_3,
|
||||
refDict: IK_srodkowy_3_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Index Finger 1',
|
||||
native: 'Wskazujący 1',
|
||||
min: validate_fingers.others.min.part_1,
|
||||
max: validate_fingers.others.max.part_1,
|
||||
refDict: IK_wskazujacy_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Index Finger 2',
|
||||
native: 'Wskazujący 2',
|
||||
min: validate_fingers.others.min.part_2,
|
||||
max: validate_fingers.others.max.part_2,
|
||||
refDict: IK_wskazujacy_2_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Index Finger 3',
|
||||
native: 'Wskazujący 3',
|
||||
min: validate_fingers.others.min.part_3,
|
||||
max: validate_fingers.others.max.part_3,
|
||||
refDict: IK_wskazujacy_3_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Thumb 0',
|
||||
native: 'Kciuk 0',
|
||||
min: validate_fingers.thumb.min.part_0,
|
||||
max: validate_fingers.thumb.max.part_0,
|
||||
refDict: IK_kciuk_0_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Thumb 1',
|
||||
native: 'Kciuk 1',
|
||||
min: validate_fingers.thumb.min.part_1,
|
||||
max: validate_fingers.thumb.max.part_1,
|
||||
refDict: IK_kciuk_1_R,
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'Thumb 2',
|
||||
native: 'Kciuk 2',
|
||||
min: validate_fingers.thumb.min.part_2,
|
||||
max: validate_fingers.thumb.max.part_2,
|
||||
refDict: IK_kciuk_2_R,
|
||||
},
|
||||
]
|
||||
|
||||
let blocker = false
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
||||
if ( models_list.length === 0 && user.id > 0 && token !== '' && blocker === false ) {
|
||||
dispatch( modelCrudAsyncThunk.fetchGetAllModels( token ) )
|
||||
if ( models_list.length === 0 ) {
|
||||
blocker = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
const bodyComparer = ( refs ) => {
|
||||
|
||||
let vectors = {}
|
||||
|
||||
console.log( refs )
|
||||
|
||||
Object.keys(refs[5]).map(
|
||||
( key ) => {
|
||||
let singleFinger = {
|
||||
scale: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
singleFinger['scale'] = 0.9
|
||||
singleFinger['x'] = parseFloat(refs[5][key]['x'].current.value)
|
||||
singleFinger['y'] = parseFloat(refs[5][key]['y'].current.value)
|
||||
singleFinger['z'] = parseFloat(refs[5][key]['z'].current.value)
|
||||
vectors[key] = singleFinger
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
fileName: refs[0].current.value.replace('.blend', ''),
|
||||
// fileName: 'testHand',
|
||||
angle: refs[1].current.value / 62, // on backend 0.1 - 6.2 value
|
||||
cameraID: refs[2].current.value,
|
||||
resolutionX: refs[3].current.value,
|
||||
resolutionY: refs[4].current.value,
|
||||
vectors: vectors
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AbstractWebsocket
|
||||
addressWS={ '/vector/single/set/' }
|
||||
inputList={ inputList }
|
||||
refList={ refList }
|
||||
bodyComparer={ bodyComparer }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RenderVectorSingleSetForm
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
import React, { useState, createRef, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { navigate } from 'gatsby';
|
||||
import { loginUser } from '../../../redux/asyncThunks/userAuthAsyncThunk';
|
||||
import { selectAuthError, selectAuthLoading, setError } from '../../../redux/slices/userAuthSlice';
|
||||
import FormGenerator from '../formGenerator';
|
||||
|
||||
const UserLogin = () => {
|
||||
const dispatch = useDispatch();
|
||||
const error = useSelector(selectAuthError);
|
||||
const loading = useSelector(selectAuthLoading);
|
||||
const [infoMessage, setInfoMessage] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const usernameInput = createRef();
|
||||
const passwordInput = createRef();
|
||||
|
||||
const [usernameValidationInfo, setUsernameValidationInfo] = useState("Empty");
|
||||
const [passwordValidationInfo, setPasswordValidationInfo] = useState("Empty");
|
||||
|
||||
const [allowButtonAction, setAllowButtonAction] = useState(false);
|
||||
|
||||
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,24}$/;
|
||||
|
||||
const usernameValidation = (event) => {
|
||||
if (event.target.value === "") {
|
||||
setUsernameValidationInfo("Login jest wymagany");
|
||||
} else {
|
||||
setUsernameValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
const passwordValidation = (event) => {
|
||||
if (event.target.value === "") {
|
||||
setPasswordValidationInfo("Hasło jest wymagane");
|
||||
} else if (!passwordRegex.test(event.target.value)) {
|
||||
setPasswordValidationInfo("Hasło nie spełnia wymagań tej witryny");
|
||||
} else {
|
||||
setPasswordValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAllowButtonAction(
|
||||
usernameValidationInfo === "Success" &&
|
||||
passwordValidationInfo === "Success"
|
||||
);
|
||||
}, [
|
||||
usernameValidationInfo,
|
||||
passwordValidationInfo,
|
||||
]);
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Login',
|
||||
endpoint: 'auth',
|
||||
button_value: loading ? 'LOGOWANIE...' : 'ZALOGUJ',
|
||||
allowButtonAction: allowButtonAction
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'LOGIN',
|
||||
ref: usernameInput,
|
||||
onChange: usernameValidation,
|
||||
validationInfo: usernameValidationInfo
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'HASŁO',
|
||||
ref: passwordInput,
|
||||
onChange: passwordValidation,
|
||||
validationInfo: passwordValidationInfo
|
||||
}
|
||||
];
|
||||
|
||||
const login = async (formData) => {
|
||||
try {
|
||||
const credentials = {
|
||||
username: formData.LOGIN,
|
||||
password: formData.HASŁO
|
||||
};
|
||||
|
||||
await dispatch(loginUser(credentials)).unwrap();
|
||||
setInfoMessage("Logowanie zakończone sukcesem!");
|
||||
navigate('/dashboard');
|
||||
} catch (error) {
|
||||
setErrorMessage("Wystąpił błąd podczas logowania (" + error.message + ")");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='form-container'>
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
action={login}
|
||||
/>
|
||||
<div className='form_info'>
|
||||
{infoMessage && <div className="success-message">{infoMessage}</div>}
|
||||
{errorMessage && <div className="error-message">{errorMessage}</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserLogin;
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
import React, { useState, useEffect, createRef } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { navigate } from 'gatsby';
|
||||
import { registerUser } from '../../../redux/asyncThunks/userAuthAsyncThunk';
|
||||
import { selectAuthError, selectAuthLoading } from '../../../redux/slices/userAuthSlice';
|
||||
import FormGenerator from '../formGenerator';
|
||||
|
||||
const UserRegister = () => {
|
||||
const dispatch = useDispatch();
|
||||
const error = useSelector(selectAuthError);
|
||||
const loading = useSelector(selectAuthLoading);
|
||||
const [infoMessage, setInfoMessage] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const usernameInput = createRef();
|
||||
const emailInput = createRef();
|
||||
const passwordInput = createRef();
|
||||
const confirmPasswordInput = createRef();
|
||||
|
||||
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,24}$/;
|
||||
|
||||
const [usernameValidationInfo, setUsernameValidationInfo] = useState("Empty");
|
||||
const [emailValidationInfo, setEmailValidationInfo] = useState("Empty");
|
||||
const [passwordValidationInfo, setPasswordValidationInfo] = useState("Empty");
|
||||
const [confirmPasswordValidationInfo, setConfirmPasswordValidationInfo] = useState("Empty");
|
||||
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [allowButtonAction, setAllowButtonAction] = useState(false);
|
||||
|
||||
const usernameValidation = (event) => {
|
||||
if (event.target.value === "") {
|
||||
setUsernameValidationInfo("Login jest wymagany");
|
||||
} else {
|
||||
setUsernameValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
const emailValidation = (event) => {
|
||||
if (event.target.value === "") {
|
||||
setEmailValidationInfo("Email jest wymagany");
|
||||
} else if (!emailRegex.test(event.target.value)) {
|
||||
setEmailValidationInfo("Nieprawidłowy format emaila");
|
||||
} else {
|
||||
setEmailValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
const passwordValidation = (event) => {
|
||||
setPassword(event.target.value);
|
||||
|
||||
if (event.target.value === "") {
|
||||
setPasswordValidationInfo("Hasło jest wymagane");
|
||||
} else if (!passwordRegex.test(event.target.value)) {
|
||||
setPasswordValidationInfo("Hasło musi zawierać:\n - Minimum 8 znaków\n - Maksimum 24 znaki\n - Minimum jedną wielką literę\n - Minimum jedną małą literę\n - Minimum jedną cyfrę\n - Minimum jeden znak specjalny");
|
||||
} else {
|
||||
setPasswordValidationInfo("Success");
|
||||
}
|
||||
|
||||
if (event.target.value !== confirmPassword) {
|
||||
setConfirmPasswordValidationInfo("Hasła nie są identyczne");
|
||||
} else {
|
||||
setConfirmPasswordValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
const confirmPasswordValidation = (event) => {
|
||||
setConfirmPassword(event.target.value);
|
||||
|
||||
if (event.target.value !== password) {
|
||||
setConfirmPasswordValidationInfo("Hasła nie są identyczne");
|
||||
} else {
|
||||
setConfirmPasswordValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAllowButtonAction(
|
||||
usernameValidationInfo === "Success" &&
|
||||
emailValidationInfo === "Success" &&
|
||||
passwordValidationInfo === "Success" &&
|
||||
confirmPasswordValidationInfo === "Success"
|
||||
);
|
||||
}, [
|
||||
usernameValidationInfo,
|
||||
emailValidationInfo,
|
||||
passwordValidationInfo,
|
||||
confirmPasswordValidationInfo
|
||||
]);
|
||||
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Register',
|
||||
endpoint: 'auth',
|
||||
button_value: loading ? 'REJESTRACJA...' : 'ZAREJESTRUJ',
|
||||
allowButtonAction: allowButtonAction
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'LOGIN',
|
||||
ref: usernameInput,
|
||||
onChange: usernameValidation,
|
||||
validationInfo: usernameValidationInfo
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'EMAIL',
|
||||
ref: emailInput,
|
||||
onChange: emailValidation,
|
||||
validationInfo: emailValidationInfo
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'HASŁO',
|
||||
ref: passwordInput,
|
||||
onChange: passwordValidation,
|
||||
validationInfo: passwordValidationInfo
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'POTWIERDŹ HASŁO',
|
||||
ref: confirmPasswordInput,
|
||||
onChange: confirmPasswordValidation,
|
||||
validationInfo: confirmPasswordValidationInfo
|
||||
}
|
||||
];
|
||||
|
||||
const register = async (formData) => {
|
||||
try {
|
||||
const userData = {
|
||||
username: formData.LOGIN,
|
||||
email: formData.EMAIL,
|
||||
password: formData.HASŁO
|
||||
};
|
||||
|
||||
await dispatch(registerUser(userData)).unwrap();
|
||||
setInfoMessage("Rejestracja zakończona sukcesem!");
|
||||
navigate('/dashboard');
|
||||
} catch (error) {
|
||||
setErrorMessage("Wystąpił błąd podczas rejestracji (" + error.massage + ")");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='form-container'>
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
action={register}
|
||||
/>
|
||||
<div className='form_info'>
|
||||
{infoMessage && <div className="success-message">{infoMessage}</div>}
|
||||
{errorMessage && <div className="error-message">{errorMessage}</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserRegister;
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import { userCrudSelector } from '../../../redux/slices/userCrudSlice'
|
||||
|
||||
import { deleteUser } from '../../../redux/slices/userAuthSlice'
|
||||
|
||||
import userCrudAsyncThunk from '../../../redux/asyncThunks/userCrudAsyncThunk'
|
||||
|
||||
import FormGenerator from '../formGenerator'
|
||||
|
||||
|
||||
const UserDeleteForm = () => {
|
||||
|
||||
const usernameInput = React.createRef()
|
||||
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const { info } = useSelector( userCrudSelector )
|
||||
|
||||
let refList = [
|
||||
usernameInput,
|
||||
]
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Create',
|
||||
endpint: 'user/crud/delete',
|
||||
button_value: 'Delete'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'Username',
|
||||
ref: usernameInput
|
||||
}
|
||||
]
|
||||
|
||||
const handleDelete = async ( refs ) => {
|
||||
if ( refs[0].current.value === user.username ) {
|
||||
let pass = {
|
||||
token: token,
|
||||
user_id: user.id
|
||||
}
|
||||
dispatch(
|
||||
deleteUser()
|
||||
)
|
||||
dispatch(
|
||||
userCrudAsyncThunk.fetchDeleteUser(
|
||||
pass
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="float_form_model"
|
||||
style={ { marginTop: '17%'} }
|
||||
>
|
||||
<FormGenerator
|
||||
inputList={ inputList }
|
||||
refList={ refList }
|
||||
action={ handleDelete }
|
||||
/>
|
||||
<div className='form_info'>
|
||||
{ info }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default UserDeleteForm
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
import { userCrudSelector } from '../../../redux/slices/userCrudSlice'
|
||||
import { userAuthSelector } from '../../../redux/slices/userAuthSlice'
|
||||
import userCrudAsyncThunk from '../../../redux/asyncThunks/userCrudAsyncThunk'
|
||||
|
||||
import FormGenerator from '../formGenerator'
|
||||
|
||||
|
||||
const UserUpdateForm = () => {
|
||||
|
||||
const usernameInput = React.createRef()
|
||||
const passwordInput = React.createRef()
|
||||
const emailInput = React.createRef()
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const { info } = useSelector( userCrudSelector )
|
||||
const { user, token } = useSelector( userAuthSelector )
|
||||
|
||||
let refList = [
|
||||
usernameInput,
|
||||
passwordInput,
|
||||
emailInput
|
||||
]
|
||||
|
||||
let inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Create',
|
||||
endpint: 'user/crud/update',
|
||||
button_value: 'Update User'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'Username',
|
||||
ref: usernameInput
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'Password',
|
||||
ref: passwordInput
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'Email',
|
||||
ref: emailInput
|
||||
}
|
||||
]
|
||||
|
||||
const update = async ( refs ) => {
|
||||
let pass = {
|
||||
user_id: user.id,
|
||||
token: token,
|
||||
user: {
|
||||
username: refs[0].current.value,
|
||||
password: refs[1].current.value,
|
||||
email: refs[2].current.value,
|
||||
}
|
||||
}
|
||||
dispatch(
|
||||
userCrudAsyncThunk.fetchUpdateUser(
|
||||
pass
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="float_form_model"
|
||||
style={ { marginTop: '10%'} }
|
||||
>
|
||||
<FormGenerator
|
||||
inputList={ inputList }
|
||||
refList={ refList }
|
||||
action={ update }
|
||||
/>
|
||||
<div className='form_info'>
|
||||
{ info }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default UserUpdateForm
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
import React, { useState, createRef, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { changePassword } from '../../../redux/asyncThunks/userAuthAsyncThunk';
|
||||
import { selectAuthError, selectAuthLoading } from '../../../redux/slices/userAuthSlice';
|
||||
import FormGenerator from '../formGenerator';
|
||||
|
||||
const UserChangePasswordForm = () => {
|
||||
const dispatch = useDispatch();
|
||||
const loading = useSelector(selectAuthLoading);
|
||||
const error = useSelector(selectAuthError);
|
||||
const [infoMessage, setInfoMessage] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const currentPasswordInput = createRef();
|
||||
const newPasswordInput = createRef();
|
||||
const confirmPasswordInput = createRef();
|
||||
|
||||
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,24}$/;
|
||||
|
||||
const [currentPasswordValidationInfo, setCurrentPasswordValidationInfo] = useState("Empty");
|
||||
const [newPasswordValidationInfo, setNewPasswordValidationInfo] = useState("Empty");
|
||||
const [confirmPasswordValidationInfo, setConfirmPasswordValidationInfo] = useState("Empty");
|
||||
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [allowButtonAction, setAllowButtonAction] = useState(false);
|
||||
|
||||
const currentPasswordValidation = (event) => {
|
||||
if (event.target.value === "") {
|
||||
setCurrentPasswordValidationInfo("Hasło jest wymagane");
|
||||
} else if (event.target.value === newPassword) {
|
||||
setCurrentPasswordValidationInfo("Nowe hasło nie może być takie samo jak aktualne");
|
||||
} else {
|
||||
setCurrentPasswordValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
const newPasswordValidation = (event) => {
|
||||
setNewPassword(event.target.value);
|
||||
|
||||
if (event.target.value === "") {
|
||||
setNewPasswordValidationInfo("Hasło jest wymagane");
|
||||
} else if (!passwordRegex.test(event.target.value)) {
|
||||
setNewPasswordValidationInfo("Hasło musi zawierać:\n - Minimum 8 znaków\n - Maksimum 24 znaki\n - Minimum jedną wielką literę\n - Minimum jedną małą literę\n - Minimum jedną cyfrę\n - Minimum jeden znak specjalny");
|
||||
} else {
|
||||
setNewPasswordValidationInfo("Success");
|
||||
}
|
||||
|
||||
if (event.target.value !== confirmPassword) {
|
||||
setConfirmPasswordValidationInfo("Hasła nie są identyczne");
|
||||
} else {
|
||||
setConfirmPasswordValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
const confirmPasswordValidation = (event) => {
|
||||
setConfirmPassword(event.target.value);
|
||||
|
||||
if (event.target.value !== newPassword) {
|
||||
setConfirmPasswordValidationInfo("Hasła nie są identyczne");
|
||||
} else {
|
||||
setConfirmPasswordValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAllowButtonAction(
|
||||
currentPasswordValidationInfo === "Success" &&
|
||||
newPasswordValidationInfo === "Success" &&
|
||||
confirmPasswordValidationInfo === "Success"
|
||||
);
|
||||
}, [
|
||||
currentPasswordValidationInfo,
|
||||
newPasswordValidationInfo,
|
||||
confirmPasswordValidationInfo
|
||||
]);
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Update',
|
||||
endpoint: 'User',
|
||||
button_value: loading ? 'AKTUALIZACJA...' : 'AKTUALIZUJ',
|
||||
allowButtonAction: allowButtonAction
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'AKTUALNE HASŁO',
|
||||
ref: currentPasswordInput,
|
||||
onChange: currentPasswordValidation,
|
||||
validationInfo: currentPasswordValidationInfo
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'NOWE HASŁO',
|
||||
ref: newPasswordInput,
|
||||
onChange: newPasswordValidation,
|
||||
validationInfo: newPasswordValidationInfo
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'POTWIERDŹ NOWE HASŁO',
|
||||
ref: confirmPasswordInput,
|
||||
onChange: confirmPasswordValidation,
|
||||
validationInfo: confirmPasswordValidationInfo
|
||||
}
|
||||
];
|
||||
|
||||
const update = async (formData) => {
|
||||
try {
|
||||
const userData = {
|
||||
currentPassword: formData['AKTUALNE HASŁO'],
|
||||
newPassword: formData['NOWE HASŁO']
|
||||
};
|
||||
|
||||
await dispatch(changePassword(userData)).unwrap();
|
||||
setInfoMessage("Hasło zostało zmienione!");
|
||||
} catch (error) {
|
||||
setErrorMessage("Wystąpił błąd podczas zmiany hasła (" + error.message + ")");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='form-container'>
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
action={update}
|
||||
/>
|
||||
<div className='form_info'>
|
||||
{infoMessage && <div className="success-message">{infoMessage}</div>}
|
||||
{errorMessage && <div className="error-message">{errorMessage}</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserChangePasswordForm;
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
import React, { useState, createRef, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { navigate } from 'gatsby';
|
||||
import { deleteAccount } from '../../../redux/asyncThunks/userAuthAsyncThunk';
|
||||
import { selectAuthError, selectAuthLoading } from '../../../redux/slices/userAuthSlice';
|
||||
import FormGenerator from '../formGenerator';
|
||||
|
||||
const UserDeleteAccountForm = () => {
|
||||
const dispatch = useDispatch();
|
||||
const loading = useSelector(selectAuthLoading);
|
||||
const error = useSelector(selectAuthError);
|
||||
const [infoMessage, setInfoMessage] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const deleteConfirmationInput = createRef();
|
||||
const [deleteConfirmationValidationInfo, setDeleteConfirmationValidationInfo] = useState("Empty");
|
||||
const [allowButtonAction, setAllowButtonAction] = useState(false);
|
||||
|
||||
const deleteConfirmationValidation = (event) => {
|
||||
if (event.target.value === "") {
|
||||
setDeleteConfirmationValidationInfo("Wpisz 'DELETE' aby potwierdzić usunięcie konta");
|
||||
} else if (event.target.value !== "DELETE") {
|
||||
setDeleteConfirmationValidationInfo("Wpisz dokładnie 'DELETE' aby potwierdzić usunięcie konta");
|
||||
} else {
|
||||
setDeleteConfirmationValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAllowButtonAction(deleteConfirmationValidationInfo === "Success");
|
||||
}, [deleteConfirmationValidationInfo]);
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Delete',
|
||||
endpoint: 'User',
|
||||
button_value: loading ? 'USUWANIE...' : 'USUŃ KONTO',
|
||||
allowButtonAction: allowButtonAction
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'POTWIERDŹ USUNIĘCIE',
|
||||
ref: deleteConfirmationInput,
|
||||
onChange: deleteConfirmationValidation,
|
||||
validationInfo: deleteConfirmationValidationInfo,
|
||||
placeholder: "Wpisz 'DELETE' aby potwierdzić usunięcie konta"
|
||||
}
|
||||
];
|
||||
|
||||
const deleteAccountHandler = async (formData) => {
|
||||
try {
|
||||
if (formData['POTWIERDŹ USUNIĘCIE'] === 'DELETE') {
|
||||
await dispatch(deleteAccount()).unwrap();
|
||||
setInfoMessage("Konto zostało usunięte!");
|
||||
setTimeout(() => {
|
||||
navigate('/');
|
||||
}, 2000);
|
||||
} else {
|
||||
setErrorMessage("Wpisz 'DELETE' aby potwierdzić usunięcie konta");
|
||||
}
|
||||
} catch (error) {
|
||||
setErrorMessage("Wystąpił błąd podczas usuwania konta (" + error.message + ")");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='form-container'>
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
action={deleteAccountHandler}
|
||||
/>
|
||||
<div className='form_info'>
|
||||
{infoMessage && <div className="success-message">{infoMessage}</div>}
|
||||
{errorMessage && <div className="error-message">{errorMessage}</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserDeleteAccountForm;
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
import React, { useState, createRef, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { updateProfile } from '../../../redux/asyncThunks/userAuthAsyncThunk';
|
||||
import { selectAuthError, selectAuthLoading, selectCurrentUser } from '../../../redux/slices/userAuthSlice';
|
||||
import FormGenerator from '../formGenerator';
|
||||
|
||||
const UserUpdateProfileForm = () => {
|
||||
const dispatch = useDispatch();
|
||||
const user = useSelector(selectCurrentUser);
|
||||
const loading = useSelector(selectAuthLoading);
|
||||
const error = useSelector(selectAuthError);
|
||||
const [infoMessage, setInfoMessage] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const emailInput = createRef();
|
||||
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
const [emailValidationInfo, setEmailValidationInfo] = useState("Empty");
|
||||
const [allowButtonAction, setAllowButtonAction] = useState(false);
|
||||
|
||||
const emailValidation = (event) => {
|
||||
if (event.target.value === "") {
|
||||
setEmailValidationInfo("Email jest wymagany");
|
||||
} else if (!emailRegex.test(event.target.value)) {
|
||||
setEmailValidationInfo("Nieprawidłowy format emaila");
|
||||
} else {
|
||||
setEmailValidationInfo("Success");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAllowButtonAction(emailValidationInfo === "Success");
|
||||
}, [emailValidationInfo]);
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: 'Update',
|
||||
endpoint: 'User',
|
||||
button_value: loading ? 'AKTUALIZACJA...' : 'AKTUALIZUJ',
|
||||
allowButtonAction: allowButtonAction
|
||||
},
|
||||
{
|
||||
type: 'label',
|
||||
name: 'LOGIN',
|
||||
value: user?.login || ''
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'EMAIL',
|
||||
ref: emailInput,
|
||||
onChange: emailValidation,
|
||||
validationInfo: emailValidationInfo,
|
||||
value: user?.email || ''
|
||||
}
|
||||
];
|
||||
|
||||
const update = async (formData) => {
|
||||
try {
|
||||
const userData = {
|
||||
email: formData.EMAIL
|
||||
};
|
||||
|
||||
await dispatch(updateProfile(userData)).unwrap();
|
||||
setInfoMessage("Profil został zaktualizowany!");
|
||||
} catch (error) {
|
||||
setErrorMessage("Wystąpił błąd podczas aktualizacji (" + error.message + ")");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='form-container'>
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
action={update}
|
||||
/>
|
||||
<div className='form_info'>
|
||||
{infoMessage && <div className="success-message">{infoMessage}</div>}
|
||||
{errorMessage && <div className="error-message">{errorMessage}</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserUpdateProfileForm;
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import '../styles/large.cube.scss';
|
||||
|
||||
|
||||
const LargeCubeComponent = () => {
|
||||
return (
|
||||
<div className="large-graphic-container">
|
||||
<div className="large-cube">
|
||||
<div className="front"></div>
|
||||
<div className="back"></div>
|
||||
<div className="right"></div>
|
||||
<div className="left"></div>
|
||||
<div className="top"></div>
|
||||
<div className="bottom"></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default LargeCubeComponent
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import '../styles/general.scss';
|
||||
|
||||
import SmallCubeComponent from './smallCube.js';
|
||||
|
||||
|
||||
const NavBarComponent = () => {
|
||||
return (
|
||||
<div className="navbar">
|
||||
<div className="content-container">
|
||||
<div className="content">
|
||||
<div className="section">
|
||||
<SmallCubeComponent />
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="content">
|
||||
<div className="section">
|
||||
<h4>Menu |||</h4>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default NavBarComponent
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import '../styles/small.cube.scss';
|
||||
|
||||
|
||||
const SmallCubeComponent = () => {
|
||||
return (
|
||||
<div className="graphic-container">
|
||||
<div className="cube">
|
||||
<div className="face-front"></div>
|
||||
<div className="face-back"></div>
|
||||
<div className="face-right"></div>
|
||||
<div className="face-left"></div>
|
||||
<div className="face-top"></div>
|
||||
<div className="face-bottom"></div>
|
||||
</div>
|
||||
<div className="title">
|
||||
XGPU
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default SmallCubeComponent
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// Konfiguracja API
|
||||
export const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:3001/api';
|
||||
|
||||
// Konfiguracja aplikacji
|
||||
export const APP_CONFIG = {
|
||||
name: 'Render App',
|
||||
version: '1.0.0',
|
||||
environment: process.env.NODE_ENV || 'development'
|
||||
};
|
||||
|
||||
// Konfiguracja autentykacji
|
||||
export const AUTH_CONFIG = {
|
||||
tokenExpiry: 7, // dni
|
||||
refreshTokenExpiry: 30, // dni
|
||||
cookieOptions: {
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
path: '/'
|
||||
}
|
||||
};
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB |
27
src/index.js
27
src/index.js
|
|
@ -1,27 +0,0 @@
|
|||
// import React from 'react';
|
||||
//
|
||||
// import LoginPage from './pages/user/login.js';
|
||||
// import LandingPage from './pages/Landing.js';
|
||||
//
|
||||
// const IndexPage = () => {
|
||||
// return (
|
||||
// <LandingPage />
|
||||
// )
|
||||
// }
|
||||
//
|
||||
//
|
||||
// export default IndexPage
|
||||
|
||||
// src/index.js
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
|
|
@ -1,20 +1,54 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import '../styles/general.scss';
|
||||
import * as React from "react"
|
||||
import { Link } from "gatsby"
|
||||
|
||||
// styles
|
||||
const pageStyles = {
|
||||
color: "#232129",
|
||||
padding: "96px",
|
||||
fontFamily: "-apple-system, Roboto, sans-serif, serif",
|
||||
}
|
||||
const headingStyles = {
|
||||
marginTop: 0,
|
||||
marginBottom: 64,
|
||||
maxWidth: 320,
|
||||
}
|
||||
|
||||
const paragraphStyles = {
|
||||
marginBottom: 48,
|
||||
}
|
||||
const codeStyles = {
|
||||
color: "#8A6534",
|
||||
padding: 4,
|
||||
backgroundColor: "#FFF4DB",
|
||||
fontSize: "1.25rem",
|
||||
borderRadius: 4,
|
||||
}
|
||||
|
||||
// markup
|
||||
const NotFoundPage = () => {
|
||||
return (
|
||||
<div className="landing-container">
|
||||
<div className="landing">
|
||||
<h1>404</h1>
|
||||
<h2>Strona nie została znaleziona</h2>
|
||||
<p>Przepraszamy, ale strona, której szukasz, nie istnieje.</p>
|
||||
<Link to="/">
|
||||
<button>Powrót do strony głównej</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<main style={pageStyles}>
|
||||
<title>Not found</title>
|
||||
<h1 style={headingStyles}>Page not found</h1>
|
||||
<p style={paragraphStyles}>
|
||||
Sorry{" "}
|
||||
<span role="img" aria-label="Pensive emoji">
|
||||
😔
|
||||
</span>{" "}
|
||||
we couldn’t find what you were looking for.
|
||||
<br />
|
||||
{process.env.NODE_ENV === "development" ? (
|
||||
<>
|
||||
<br />
|
||||
Try creating a page in <code style={codeStyles}>src/pages/</code>.
|
||||
<br />
|
||||
</>
|
||||
) : null}
|
||||
<br />
|
||||
<Link to="/">Go home</Link>.
|
||||
</p>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotFoundPage;
|
||||
export default NotFoundPage
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { getAiTasks, createAiTask, updateAiTask, deleteAiTask } from '../services/task.service';
|
||||
|
||||
function FormAi() {
|
||||
const [tasks, setTasks] = useState([]);
|
||||
|
||||
const fetchTasks = async () => {
|
||||
try {
|
||||
const data = await getAiTasks();
|
||||
setTasks(data || []);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Failed to load AI tasks');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchTasks();
|
||||
}, []);
|
||||
|
||||
const handleCreate = async () => {
|
||||
const userId = 123; // Example user ID
|
||||
try {
|
||||
await createAiTask(userId);
|
||||
alert('AI task created');
|
||||
fetchTasks();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Failed to create AI task');
|
||||
}
|
||||
};
|
||||
|
||||
const handleStop = async (taskId) => {
|
||||
try {
|
||||
await updateAiTask(taskId, 'stop');
|
||||
fetchTasks();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Failed to stop AI task');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (taskId) => {
|
||||
try {
|
||||
await deleteAiTask(taskId);
|
||||
fetchTasks();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Failed to delete AI task');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ margin: '1rem' }}>
|
||||
<h2>AI Tasks</h2>
|
||||
<button onClick={handleCreate}>Create AI Task</button>
|
||||
<ul>
|
||||
{tasks.map((t) => (
|
||||
<li key={t._id}>
|
||||
Task ID: {t._id}, Status: {t.status}
|
||||
<button onClick={() => handleStop(t._id)}>Stop</button>
|
||||
<button onClick={() => handleDelete(t._id)}>Delete</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormAi;
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
import authService from '../services/auth.service';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
function FormLogin() {
|
||||
const navigate = useNavigate();
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const handleLogin = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await authService.login(username, password);
|
||||
alert('Login successful!');
|
||||
navigate('/models');
|
||||
} catch (err) {
|
||||
alert('Login failed');
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '400px', margin: '0 auto' }}>
|
||||
<h2>Login</h2>
|
||||
<form onSubmit={handleLogin}>
|
||||
<div>
|
||||
<label>Username:</label>
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Password:</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormLogin;
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { getModels, createModel, deleteModel } from '../services/model.service';
|
||||
|
||||
function FormModels() {
|
||||
const [models, setModels] = useState([]);
|
||||
const [modelName, setModelName] = useState('');
|
||||
const [file, setFile] = useState(null);
|
||||
|
||||
const fetchModels = async () => {
|
||||
try {
|
||||
const data = await getModels();
|
||||
setModels(data.items || []);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert('Failed to load models');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchModels();
|
||||
}, []);
|
||||
|
||||
const handleCreateModel = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!modelName || !file) {
|
||||
alert('Please provide name and file');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await createModel(modelName, file);
|
||||
setModelName('');
|
||||
setFile(null);
|
||||
alert('Model created successfully');
|
||||
fetchModels();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Failed to create model');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (!window.confirm('Are you sure?')) return;
|
||||
try {
|
||||
await deleteModel(id);
|
||||
fetchModels();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert('Error deleting model');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ margin: '1rem' }}>
|
||||
<h2>Models</h2>
|
||||
<form onSubmit={handleCreateModel}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Model Name"
|
||||
value={modelName}
|
||||
onChange={(e) => setModelName(e.target.value)}
|
||||
/>
|
||||
<input type="file" onChange={(e) => setFile(e.target.files?.[0] || null)} />
|
||||
<button type="submit">Create Model</button>
|
||||
</form>
|
||||
|
||||
<ul>
|
||||
{models.map((m) => (
|
||||
<li key={m.id}>
|
||||
{m.name} (ID: {m.id}){' '}
|
||||
<button onClick={() => handleDelete(m.id)}>Delete</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormModels;
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
import authService from '../services/auth.service';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
function FormRegister() {
|
||||
const navigate = useNavigate();
|
||||
const [username, setUsername] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const handleRegister = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await authService.register(username, password, email);
|
||||
alert('Registration successful!');
|
||||
navigate('/login');
|
||||
} catch (err) {
|
||||
alert('Registration failed');
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '400px', margin: '0 auto' }}>
|
||||
<h2>Register</h2>
|
||||
<form onSubmit={handleRegister}>
|
||||
<div>
|
||||
<label>Username:</label>
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Email:</label>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Password:</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit">Register</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormRegister;
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import api from '../services/api'; // or a dedicated function
|
||||
|
||||
function FormRenders() {
|
||||
const [renders, setRenders] = useState([]);
|
||||
|
||||
const fetchRenders = async () => {
|
||||
try {
|
||||
// GET /renders? (Your backend might differ.)
|
||||
// Must handle pagination if needed
|
||||
const res = await api.get('/renders', { params: { page: 1, limit: 10 } });
|
||||
setRenders(res.data.items || []);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert('Failed to load renders');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchRenders();
|
||||
}, []);
|
||||
|
||||
const handleDownload = (s3Key) => {
|
||||
// If the backend returns a direct S3 link, just open it:
|
||||
// window.open(downloadUrl, '_blank');
|
||||
|
||||
// Otherwise, if you only have s3Key, you might need to build the S3 URL or call a backend endpoint that redirects.
|
||||
const s3Url = `https://YOUR_BUCKET.s3.amazonaws.com/${s3Key}`;
|
||||
window.open(s3Url, '_blank');
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ margin: '1rem' }}>
|
||||
<h2>Renders</h2>
|
||||
{renders.map((r) => (
|
||||
<div key={r.id} style={{ marginBottom: '0.5rem' }}>
|
||||
<strong>{r.name}</strong> (ID: {r.id})
|
||||
{r.s3Key && (
|
||||
<button onClick={() => handleDownload(r.s3Key)}>Download ZIP</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormRenders;
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import '../../styles/general.scss';
|
||||
|
||||
import LargeCubeComponent from '../../components/largeCube.js';
|
||||
import UserLoginForm from '../../components/forms/user_auth/userLogin.js';
|
||||
import FootComponent from '../../components/foot.js';
|
||||
import NavBarComponent from '../../components/navbar.js';
|
||||
|
||||
const LoginPage = () => {
|
||||
return (
|
||||
<>
|
||||
<NavBarComponent />
|
||||
<div className="login-container" style={{"display": "flex"}}>
|
||||
<main className="main-content">
|
||||
<div style={{"padding-left": "200px"}}>
|
||||
<UserLoginForm />
|
||||
</div>
|
||||
</main>
|
||||
<main className="main-content">
|
||||
<div style={{"padding-right": "200px"}}>
|
||||
<LargeCubeComponent />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<FootComponent />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default LoginPage
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import '../../styles/general.scss';
|
||||
|
||||
import LargeCubeComponent from '../../components/largeCube.js';
|
||||
import UserRegisterForm from '../../components/forms/user_auth/userRegister.js';
|
||||
import FootComponent from '../../components/foot.js';
|
||||
import NavBarComponent from '../../components/navbar.js';
|
||||
|
||||
const RegisterPage = () => {
|
||||
return (
|
||||
<>
|
||||
<NavBarComponent />
|
||||
<div className="register-container" style={{"display": "flex"}}>
|
||||
<main className="main-content">
|
||||
<div style={{"padding-left": "200px"}}>
|
||||
<UserRegisterForm />
|
||||
</div>
|
||||
</main>
|
||||
<main className="main-content">
|
||||
<div style={{"padding-right": "200px"}}>
|
||||
<LargeCubeComponent />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<FootComponent />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default RegisterPage
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { navigate } from 'gatsby';
|
||||
import { logoutUser, checkAuth } from '../redux/asyncThunks/userAuthAsyncThunk';
|
||||
import { selectIsAuthenticated, selectUserPermissions } from '../redux/slices/userAuthSlice';
|
||||
|
||||
import NavBarComponent from '../components/navbar.js';
|
||||
import FootComponent from '../components/foot.js';
|
||||
import AIModelsDashboard from './dashboards/ai.models.js';
|
||||
import AITasksDashboard from './dashboards/ai.tasks.js';
|
||||
import RendersDashboard from './dashboards/renders.js';
|
||||
import ServersDashboard from './dashboards/servers.js';
|
||||
import UserSettingsDashboard from './dashboards/user.js';
|
||||
import ThreeDModelsDashboard from './dashboards/3d-models.js';
|
||||
|
||||
const DashboardPage = () => {
|
||||
const dispatch = useDispatch();
|
||||
const isAuthenticated = useSelector(selectIsAuthenticated);
|
||||
const permissions = useSelector(selectUserPermissions);
|
||||
|
||||
const icons_size = "fa-2x";
|
||||
const [activeComponent, setActiveComponent] = useState('3d-models');
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
dispatch(checkAuth())
|
||||
.unwrap()
|
||||
.catch(() => {
|
||||
navigate('/auth/login');
|
||||
});
|
||||
}
|
||||
}, [dispatch, isAuthenticated]);
|
||||
|
||||
const handleNavigation = (path) => {
|
||||
setActiveComponent(path);
|
||||
};
|
||||
|
||||
const isActive = (path) => {
|
||||
return activeComponent === path;
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await dispatch(logoutUser()).unwrap();
|
||||
navigate('/auth/login');
|
||||
} catch (error) {
|
||||
console.error('Błąd podczas wylogowywania:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
switch (activeComponent) {
|
||||
case 'ai-models':
|
||||
return <AIModelsDashboard />;
|
||||
case 'ai-tasks':
|
||||
return <AITasksDashboard />;
|
||||
case 'renders':
|
||||
return <RendersDashboard />;
|
||||
case 'servers':
|
||||
return <ServersDashboard />;
|
||||
case 'settings':
|
||||
return <UserSettingsDashboard />;
|
||||
case '3d-models':
|
||||
default:
|
||||
return <ThreeDModelsDashboard />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavBarComponent />
|
||||
<div className="dashboard-container" style={{"display": "flex"}}>
|
||||
<main className="dashboard-content">
|
||||
{renderContent()}
|
||||
</main>
|
||||
<main className="large-menu-content">
|
||||
<p>Servers</p>
|
||||
<ol>
|
||||
<li
|
||||
className={isActive('servers') ? 'active' : ''}
|
||||
onClick={() => handleNavigation('servers')}
|
||||
>
|
||||
<i className={"fas " + icons_size + " fa-server"}></i>
|
||||
<p>GPU Instances</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>3D Stuff</p>
|
||||
<ol>
|
||||
<li
|
||||
className={isActive('3d-models') ? 'active' : ''}
|
||||
onClick={() => handleNavigation('3d-models')}
|
||||
>
|
||||
<i className={"fas " + icons_size + " fa-cube"}></i>
|
||||
<p>3D Models</p>
|
||||
</li>
|
||||
<li
|
||||
className={isActive('renders') ? 'active' : ''}
|
||||
onClick={() => handleNavigation('renders')}
|
||||
>
|
||||
<i className={"fas " + icons_size + " fa-paint-brush"}></i>
|
||||
<p>3D Rendering</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>AI Stuff</p>
|
||||
<ol>
|
||||
<li
|
||||
className={isActive('ai-models') ? 'active' : ''}
|
||||
onClick={() => handleNavigation('ai-models')}
|
||||
>
|
||||
<i className={"fas " + icons_size + " fa-robot"}></i>
|
||||
<p>AI Models</p>
|
||||
</li>
|
||||
<li
|
||||
className={isActive('ai-tasks') ? 'active' : ''}
|
||||
onClick={() => handleNavigation('ai-tasks')}
|
||||
>
|
||||
<i className={"fas " + icons_size + " fa-microchip"}></i>
|
||||
<p>AI Training</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>User</p>
|
||||
<ol>
|
||||
<li
|
||||
className={isActive('settings') ? 'active' : ''}
|
||||
onClick={() => handleNavigation('settings')}
|
||||
>
|
||||
<i className={"fas " + icons_size + " fa-user-cog"}></i>
|
||||
<p>Settings</p>
|
||||
</li>
|
||||
<li onClick={handleLogout}>
|
||||
<i className={"fas " + icons_size + " fa-sign-out-alt"}></i>
|
||||
<p>Log Out</p>
|
||||
</li>
|
||||
</ol>
|
||||
</main>
|
||||
</div>
|
||||
<FootComponent />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardPage;
|
||||
|
|
@ -1,408 +0,0 @@
|
|||
import React, { useState, useMemo } from 'react';
|
||||
import { ListGenerator } from '../../components/forms/listGenerator';
|
||||
import ModelUploadForm from '../../components/forms/3d_model_crud/threeDModelUpload';
|
||||
|
||||
const ThreeDModelsDashboard = () => {
|
||||
const [selectedModel, setSelectedModel] = useState(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [models, setModels] = useState([
|
||||
{
|
||||
id: 1,
|
||||
name: 'Dragon Model',
|
||||
type: 'Blender',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '2.1'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Medieval Castle',
|
||||
type: 'Maya',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-19',
|
||||
version: '1.5'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Sci-fi Weapon',
|
||||
type: '3ds Max',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '1.0'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Forest Scene',
|
||||
type: 'Blender',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-18',
|
||||
version: '3.2'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Robot Character',
|
||||
type: 'Maya',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-17',
|
||||
version: '2.0'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Space Ship',
|
||||
type: '3ds Max',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '1.8'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Ancient Temple',
|
||||
type: 'Blender',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-19',
|
||||
version: '2.4'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Fantasy Sword',
|
||||
type: 'Maya',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-16',
|
||||
version: '1.2'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'City Block',
|
||||
type: '3ds Max',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '2.7'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Warrior Character',
|
||||
type: 'Blender',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-18',
|
||||
version: '1.9'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Futuristic Car',
|
||||
type: 'Maya',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-15',
|
||||
version: '1.3'
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'Mountain Range',
|
||||
type: '3ds Max',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '2.2'
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: 'Alien Creature',
|
||||
type: 'Blender',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-19',
|
||||
version: '1.6'
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: 'Magic Staff',
|
||||
type: 'Maya',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '1.4'
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: 'Underground Cave',
|
||||
type: '3ds Max',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-17',
|
||||
version: '2.3'
|
||||
}
|
||||
]);
|
||||
const [isFormVisible, setIsFormVisible] = useState(false);
|
||||
const [formMode, setFormMode] = useState('create');
|
||||
const [message, setMessage] = useState({ type: '', text: '' });
|
||||
|
||||
const mockModels = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Dragon Model",
|
||||
type: "Character",
|
||||
status: "Completed",
|
||||
progress: 100
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Medieval Castle",
|
||||
type: "Environment",
|
||||
status: "In Progress",
|
||||
progress: 65
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Sci-fi Weapon",
|
||||
type: "Prop",
|
||||
status: "Queued",
|
||||
progress: 0
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Forest Scene",
|
||||
type: "Environment",
|
||||
status: "Completed",
|
||||
progress: 100
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Robot Character",
|
||||
type: "Character",
|
||||
status: "In Progress",
|
||||
progress: 45
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Space Ship",
|
||||
type: "Vehicle",
|
||||
status: "Completed",
|
||||
progress: 100
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Ancient Temple",
|
||||
type: "Environment",
|
||||
status: "In Progress",
|
||||
progress: 78
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Fantasy Sword",
|
||||
type: "Prop",
|
||||
status: "Queued",
|
||||
progress: 0
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "City Block",
|
||||
type: "Environment",
|
||||
status: "Completed",
|
||||
progress: 100
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Warrior Character",
|
||||
type: "Character",
|
||||
status: "In Progress",
|
||||
progress: 89
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Futuristic Car",
|
||||
type: "Vehicle",
|
||||
status: "Queued",
|
||||
progress: 0
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Mountain Range",
|
||||
type: "Environment",
|
||||
status: "Completed",
|
||||
progress: 100
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Alien Creature",
|
||||
type: "Character",
|
||||
status: "In Progress",
|
||||
progress: 34
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Magic Staff",
|
||||
type: "Prop",
|
||||
status: "Completed",
|
||||
progress: 100
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Underground Cave",
|
||||
type: "Environment",
|
||||
status: "In Progress",
|
||||
progress: 56
|
||||
}
|
||||
];
|
||||
|
||||
const handleModelSelect = (model) => {
|
||||
setSelectedModel(model);
|
||||
};
|
||||
|
||||
const handleModelAction = (action, model) => {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
handleDeleteModel(model.id);
|
||||
break;
|
||||
case 'edit':
|
||||
handleEditModel(model);
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown action:', action);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateModel = () => {
|
||||
setFormMode('create');
|
||||
setIsFormVisible(true);
|
||||
setSelectedModel(null);
|
||||
};
|
||||
|
||||
const handleEditModel = (model) => {
|
||||
setFormMode('edit');
|
||||
setIsFormVisible(true);
|
||||
setSelectedModel(model);
|
||||
};
|
||||
|
||||
const handleDeleteModel = (modelId) => {
|
||||
setModels(models.filter(model => model.id !== modelId));
|
||||
if (selectedModel?.id === modelId) {
|
||||
setSelectedModel(null);
|
||||
}
|
||||
setMessage({ type: 'success', text: '3D Model has been deleted' });
|
||||
};
|
||||
|
||||
const handleFormCancel = () => {
|
||||
setIsFormVisible(false);
|
||||
setSelectedModel(null);
|
||||
};
|
||||
|
||||
const getModelActions = (model) => {
|
||||
return [
|
||||
{
|
||||
label: 'Edit',
|
||||
action: 'edit',
|
||||
className: 'update-button'
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
action: 'delete',
|
||||
className: 'delete-button'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const filteredModels = useMemo(() => {
|
||||
return models.filter(model =>
|
||||
model.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
model.type.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
model.status.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
}, [models, searchQuery]);
|
||||
|
||||
return (
|
||||
<div className="list-container">
|
||||
<div className="dashboard-header">
|
||||
<h2>3D Models</h2>
|
||||
<div className="dashboard-controls">
|
||||
<div className="search-container">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Szukaj modeli..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="search-input"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
className="clear-search"
|
||||
onClick={() => setSearchQuery('')}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className="create-button"
|
||||
onClick={handleCreateModel}
|
||||
>
|
||||
<i className="fas fa-upload"></i>
|
||||
Upload 3D Model
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message.text && (
|
||||
<div className={`message ${message.type}`}>
|
||||
{message.text}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isFormVisible && (
|
||||
<div className="form-overlay">
|
||||
<div className="form-container">
|
||||
<ModelUploadForm/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ListGenerator
|
||||
data={filteredModels}
|
||||
selectedItem={selectedModel}
|
||||
onItemSelect={handleModelSelect}
|
||||
onItemAction={handleModelAction}
|
||||
getItemActions={getModelActions}
|
||||
renderItem={(model) => (
|
||||
<div className="list-row">
|
||||
<div className="item-info">
|
||||
<div className="item-name">{model.name}</div>
|
||||
<div className="item-type">{model.type}</div>
|
||||
<div className={`item-status ${model.status.toLowerCase()}`}>
|
||||
{model.status}
|
||||
</div>
|
||||
</div>
|
||||
<div className="item-details">
|
||||
<div>Version: {model.version}</div>
|
||||
<div>Last Modified: {model.lastModified}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
renderDetails={(model) => (
|
||||
<div className="details-panel">
|
||||
<h3>3D Model Details</h3>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">ID:</span>
|
||||
<span className="detail-value">{model.id}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Name:</span>
|
||||
<span className="detail-value">{model.name}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Type:</span>
|
||||
<span className="detail-value">{model.type}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Version:</span>
|
||||
<span className="detail-value">{model.version}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Status:</span>
|
||||
<span className="detail-value">{model.status}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Last Modified:</span>
|
||||
<span className="detail-value">{model.lastModified}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThreeDModelsDashboard;
|
||||
|
|
@ -1,385 +0,0 @@
|
|||
import React, { useState, useRef, useMemo } from 'react';
|
||||
import { ListGenerator } from '../../components/forms/listGenerator';
|
||||
import FormGenerator from '../../components/forms/formGenerator';
|
||||
import AIModelUploadForm from '../../components/forms/ai_model_crud/AIModelUpload';
|
||||
|
||||
const AIModelsDashboard = () => {
|
||||
const [selectedModel, setSelectedModel] = useState(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [models, setModels] = useState([
|
||||
{
|
||||
id: 1,
|
||||
name: 'stable-diffusion',
|
||||
type: 'text-to-image',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '2.1'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'gpt-4',
|
||||
type: 'text-generation',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-19',
|
||||
version: '1.0'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'yolo-v8',
|
||||
type: 'object-detection',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '1.2'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'wav2vec',
|
||||
type: 'speech-recognition',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-18',
|
||||
version: '3.0'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'bert-base',
|
||||
type: 'text-classification',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-17',
|
||||
version: '2.0'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'resnet-50',
|
||||
type: 'image-classification',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '1.8'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'detr',
|
||||
type: 'object-detection',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-19',
|
||||
version: '2.4'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'whisper',
|
||||
type: 'speech-recognition',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-16',
|
||||
version: '1.2'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'dalle-3',
|
||||
type: 'text-to-image',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '2.7'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'llama-2',
|
||||
type: 'text-generation',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-18',
|
||||
version: '1.9'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'mask-rcnn',
|
||||
type: 'image-segmentation',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-15',
|
||||
version: '1.3'
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'roberta',
|
||||
type: 'text-classification',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '2.2'
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: 'dino',
|
||||
type: 'image-classification',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-19',
|
||||
version: '1.6'
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: 'sam',
|
||||
type: 'image-segmentation',
|
||||
status: 'Active',
|
||||
lastModified: '2024-03-20',
|
||||
version: '1.4'
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: 'clip',
|
||||
type: 'image-text',
|
||||
status: 'Inactive',
|
||||
lastModified: '2024-03-17',
|
||||
version: '2.3'
|
||||
}
|
||||
]);
|
||||
const [isFormVisible, setIsFormVisible] = useState(false);
|
||||
const [formMode, setFormMode] = useState('create');
|
||||
const [message, setMessage] = useState({ type: '', text: '' });
|
||||
|
||||
const nameInput = React.createRef();
|
||||
const typeInput = React.createRef();
|
||||
const versionInput = React.createRef();
|
||||
const statusInput = React.createRef();
|
||||
|
||||
const formRefs = [
|
||||
nameInput,
|
||||
typeInput,
|
||||
versionInput,
|
||||
statusInput
|
||||
];
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: formMode === 'create' ? 'Create' : 'Update',
|
||||
endpoint: 'ai/models',
|
||||
button_value: formMode === 'create' ? '+ AI MODEL' : 'UPDATE',
|
||||
allowButtonAction: false
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'NAME',
|
||||
ref: nameInput,
|
||||
value: selectedModel?.name || '',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'TYPE',
|
||||
ref: typeInput,
|
||||
options: [
|
||||
{ value: 'text-to-image', label: 'Text to Image' },
|
||||
{ value: 'image-to-text', label: 'Image to Text' }
|
||||
],
|
||||
value: selectedModel?.type || 'text-to-image',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'VERSION',
|
||||
ref: versionInput,
|
||||
value: selectedModel?.version || '1.0',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'STATUS',
|
||||
ref: statusInput,
|
||||
options: [
|
||||
{ value: 'Active', label: 'Active' },
|
||||
{ value: 'Inactive', label: 'Inactive' }
|
||||
],
|
||||
value: selectedModel?.status || 'Active',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
}
|
||||
];
|
||||
|
||||
const handleModelSelect = (model) => {
|
||||
setSelectedModel(model);
|
||||
};
|
||||
|
||||
const handleModelAction = (action, model) => {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
handleDeleteModel(model.id);
|
||||
break;
|
||||
case 'edit':
|
||||
handleEditModel(model);
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown action:', action);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateModel = () => {
|
||||
setFormMode('create');
|
||||
setIsFormVisible(true);
|
||||
setSelectedModel(null);
|
||||
};
|
||||
|
||||
const handleEditModel = (model) => {
|
||||
setFormMode('edit');
|
||||
setIsFormVisible(true);
|
||||
setSelectedModel(model);
|
||||
};
|
||||
|
||||
const handleDeleteModel = (modelId) => {
|
||||
setModels(models.filter(model => model.id !== modelId));
|
||||
if (selectedModel?.id === modelId) {
|
||||
setSelectedModel(null);
|
||||
}
|
||||
setMessage({ type: 'success', text: 'AI Model has been deleted' });
|
||||
};
|
||||
|
||||
const handleFormSubmit = (formData) => {
|
||||
if (formMode === 'create') {
|
||||
const newModel = {
|
||||
id: models.length + 1,
|
||||
...formData,
|
||||
lastModified: new Date().toISOString().split('T')[0],
|
||||
status: 'Active'
|
||||
};
|
||||
setModels([...models, newModel]);
|
||||
setMessage({ type: 'success', text: 'AI Model has been created' });
|
||||
} else {
|
||||
setModels(models.map(model =>
|
||||
model.id === selectedModel.id
|
||||
? { ...model, ...formData, lastModified: new Date().toISOString().split('T')[0] }
|
||||
: model
|
||||
));
|
||||
setMessage({ type: 'success', text: 'AI Model has been updated' });
|
||||
}
|
||||
setIsFormVisible(false);
|
||||
};
|
||||
|
||||
const handleFormCancel = () => {
|
||||
setIsFormVisible(false);
|
||||
setSelectedModel(null);
|
||||
};
|
||||
|
||||
const getModelActions = (model) => {
|
||||
return [
|
||||
{
|
||||
label: 'Edit',
|
||||
action: 'edit',
|
||||
className: 'update-button'
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
action: 'delete',
|
||||
className: 'delete-button'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const filteredModels = useMemo(() => {
|
||||
return models.filter(model =>
|
||||
model.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
model.type.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
model.status.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
}, [models, searchQuery]);
|
||||
|
||||
return (
|
||||
<div className="list-container">
|
||||
<div className="dashboard-header">
|
||||
<h2>AI Models</h2>
|
||||
<div className="dashboard-controls">
|
||||
<div className="search-container">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Szukaj modeli..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="search-input"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
className="clear-search"
|
||||
onClick={() => setSearchQuery('')}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className="create-button"
|
||||
onClick={() => setIsFormVisible(true)}
|
||||
>
|
||||
<i className="fas fa-upload"></i>
|
||||
Upload AI Model
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message.text && (
|
||||
<div className={`message ${message.type}`}>
|
||||
{message.text}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isFormVisible && (
|
||||
<div className="form-overlay">
|
||||
<div className="form-container">
|
||||
<AIModelUploadForm />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ListGenerator
|
||||
data={filteredModels}
|
||||
selectedItem={selectedModel}
|
||||
onItemSelect={handleModelSelect}
|
||||
onItemAction={handleModelAction}
|
||||
getItemActions={getModelActions}
|
||||
renderItem={(model) => (
|
||||
<div className="list-row">
|
||||
<div className="item-info">
|
||||
<div className="item-name">{model.name}</div>
|
||||
<div className="item-type">{model.type}</div>
|
||||
<div className={`item-status ${model.status.toLowerCase()}`}>
|
||||
{model.status}
|
||||
</div>
|
||||
</div>
|
||||
<div className="item-details">
|
||||
<div>Version: {model.version}</div>
|
||||
<div>Last Modified: {model.lastModified}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
renderDetails={(model) => (
|
||||
<div className="details-panel">
|
||||
<h3>AI Model Details</h3>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">ID:</span>
|
||||
<span className="detail-value">{model.id}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Name:</span>
|
||||
<span className="detail-value">{model.name}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Type:</span>
|
||||
<span className="detail-value">{model.type}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Version:</span>
|
||||
<span className="detail-value">{model.version}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Status:</span>
|
||||
<span className="detail-value">{model.status}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Last Modified:</span>
|
||||
<span className="detail-value">{model.lastModified}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AIModelsDashboard;
|
||||
|
||||
|
|
@ -1,482 +0,0 @@
|
|||
import React, { useState, useRef, useMemo } from 'react';
|
||||
import { ListGenerator } from '../../components/forms/listGenerator';
|
||||
import FormGenerator from '../../components/forms/formGenerator';
|
||||
|
||||
const AITasksDashboard = () => {
|
||||
const [selectedTask, setSelectedTask] = useState(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [tasks, setTasks] = useState([
|
||||
{
|
||||
id: 1,
|
||||
name: "Model Training - CNN",
|
||||
type: "Training",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Object Detection v2",
|
||||
startTime: "2024-03-20 09:00:00",
|
||||
endTime: "2024-03-20 14:30:00"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "BERT Fine-tuning",
|
||||
type: "Fine-tuning",
|
||||
status: "In Progress",
|
||||
progress: 75,
|
||||
model: "Text Generator",
|
||||
startTime: "2024-03-20 10:15:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Model Evaluation",
|
||||
type: "Evaluation",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Style Transfer v1",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Performance Testing",
|
||||
type: "Testing",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Face Recognition",
|
||||
startTime: "2024-03-19 15:00:00",
|
||||
endTime: "2024-03-19 17:30:00"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Model Training - RNN",
|
||||
type: "Training",
|
||||
status: "In Progress",
|
||||
progress: 60,
|
||||
model: "Language Translator",
|
||||
startTime: "2024-03-20 08:45:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "GAN Training",
|
||||
type: "Training",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Image Generation",
|
||||
startTime: "2024-03-19 11:00:00",
|
||||
endTime: "2024-03-19 18:30:00"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Model Optimization",
|
||||
type: "Fine-tuning",
|
||||
status: "In Progress",
|
||||
progress: 82,
|
||||
model: "Voice Synthesis",
|
||||
startTime: "2024-03-20 09:30:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Accuracy Testing",
|
||||
type: "Testing",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Pose Estimation",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Transfer Learning",
|
||||
type: "Training",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Scene Understanding",
|
||||
startTime: "2024-03-19 13:15:00",
|
||||
endTime: "2024-03-19 16:45:00"
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Model Validation",
|
||||
type: "Evaluation",
|
||||
status: "In Progress",
|
||||
progress: 45,
|
||||
model: "Text Summarizer",
|
||||
startTime: "2024-03-20 11:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Hyperparameter Tuning",
|
||||
type: "Fine-tuning",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Speech Recognition",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Model Training - YOLO",
|
||||
type: "Training",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Object Tracking",
|
||||
startTime: "2024-03-19 09:00:00",
|
||||
endTime: "2024-03-19 15:30:00"
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Performance Optimization",
|
||||
type: "Fine-tuning",
|
||||
status: "In Progress",
|
||||
progress: 68,
|
||||
model: "Image Segmentation",
|
||||
startTime: "2024-03-20 10:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Model Deployment Test",
|
||||
type: "Testing",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Sentiment Analysis",
|
||||
startTime: "2024-03-19 14:00:00",
|
||||
endTime: "2024-03-19 16:00:00"
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Model Training - GPT",
|
||||
type: "Training",
|
||||
status: "In Progress",
|
||||
progress: 92,
|
||||
model: "Chatbot Model",
|
||||
startTime: "2024-03-20 07:30:00",
|
||||
endTime: null
|
||||
}
|
||||
]);
|
||||
const [isFormVisible, setIsFormVisible] = useState(false);
|
||||
const [formMode, setFormMode] = useState('create');
|
||||
const [message, setMessage] = useState({ type: '', text: '' });
|
||||
|
||||
const nameInput = React.createRef();
|
||||
const typeInput = React.createRef();
|
||||
const statusInput = React.createRef();
|
||||
const progressInput = React.createRef();
|
||||
|
||||
const formRefs = [
|
||||
nameInput,
|
||||
typeInput,
|
||||
statusInput,
|
||||
progressInput
|
||||
];
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: formMode === 'create' ? 'Create' : 'Update',
|
||||
endpoint: 'ai/tasks',
|
||||
button_value: formMode === 'create' ? '+ AI TASK' : 'UPDATE',
|
||||
allowButtonAction: false
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'NAME',
|
||||
ref: nameInput,
|
||||
value: selectedTask?.name || '',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'TYPE',
|
||||
ref: typeInput,
|
||||
options: [
|
||||
{ value: 'text-to-image', label: 'Text to Image' },
|
||||
{ value: 'image-to-text', label: 'Image to Text' }
|
||||
],
|
||||
value: selectedTask?.type || 'text-to-image',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'STATUS',
|
||||
ref: statusInput,
|
||||
options: [
|
||||
{ value: 'In Progress', label: 'In Progress' },
|
||||
{ value: 'Completed', label: 'Completed' },
|
||||
{ value: 'Failed', label: 'Failed' },
|
||||
{ value: 'Cancelled', label: 'Cancelled' }
|
||||
],
|
||||
value: selectedTask?.status || 'In Progress',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'PROGRESS',
|
||||
ref: progressInput,
|
||||
value: selectedTask?.progress || 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
}
|
||||
];
|
||||
|
||||
const handleTaskSelect = (task) => {
|
||||
setSelectedTask(task);
|
||||
};
|
||||
|
||||
const handleTaskAction = (action, task) => {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
handleDeleteTask(task.id);
|
||||
break;
|
||||
case 'edit':
|
||||
handleEditTask(task);
|
||||
break;
|
||||
case 'cancel':
|
||||
handleCancelTask(task.id);
|
||||
break;
|
||||
case 'restart':
|
||||
handleRestartTask(task.id);
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown action:', action);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateTask = () => {
|
||||
setFormMode('create');
|
||||
setIsFormVisible(true);
|
||||
setSelectedTask(null);
|
||||
};
|
||||
|
||||
const handleEditTask = (task) => {
|
||||
setFormMode('edit');
|
||||
setIsFormVisible(true);
|
||||
setSelectedTask(task);
|
||||
};
|
||||
|
||||
const handleDeleteTask = (taskId) => {
|
||||
setTasks(tasks.filter(task => task.id !== taskId));
|
||||
if (selectedTask?.id === taskId) {
|
||||
setSelectedTask(null);
|
||||
}
|
||||
setMessage({ type: 'success', text: 'Task has been deleted' });
|
||||
};
|
||||
|
||||
const handleCancelTask = (taskId) => {
|
||||
setTasks(tasks.map(task =>
|
||||
task.id === taskId
|
||||
? { ...task, status: 'Cancelled', progress: 0 }
|
||||
: task
|
||||
));
|
||||
setMessage({ type: 'success', text: 'Task has been cancelled' });
|
||||
};
|
||||
|
||||
const handleRestartTask = (taskId) => {
|
||||
setTasks(tasks.map(task =>
|
||||
task.id === taskId
|
||||
? { ...task, status: 'In Progress', progress: 0 }
|
||||
: task
|
||||
));
|
||||
setMessage({ type: 'success', text: 'Task has been restarted' });
|
||||
};
|
||||
|
||||
const handleFormSubmit = (formData) => {
|
||||
if (formMode === 'create') {
|
||||
const newTask = {
|
||||
id: tasks.length + 1,
|
||||
...formData,
|
||||
lastModified: new Date().toISOString().split('T')[0],
|
||||
status: 'In Progress',
|
||||
progress: 0
|
||||
};
|
||||
setTasks([...tasks, newTask]);
|
||||
setMessage({ type: 'success', text: 'Task has been created' });
|
||||
} else {
|
||||
setTasks(tasks.map(task =>
|
||||
task.id === selectedTask.id
|
||||
? { ...task, ...formData, lastModified: new Date().toISOString().split('T')[0] }
|
||||
: task
|
||||
));
|
||||
setMessage({ type: 'success', text: 'Task has been updated' });
|
||||
}
|
||||
setIsFormVisible(false);
|
||||
};
|
||||
|
||||
const handleFormCancel = () => {
|
||||
setIsFormVisible(false);
|
||||
setSelectedTask(null);
|
||||
};
|
||||
|
||||
const handleFormAction = (refs) => {
|
||||
const formData = {};
|
||||
refs.forEach((ref, index) => {
|
||||
formData[inputList[index].name] = ref.current.value;
|
||||
});
|
||||
handleFormSubmit(formData);
|
||||
};
|
||||
|
||||
const getTaskActions = (task) => {
|
||||
const actions = [];
|
||||
|
||||
if (task.status === 'In Progress') {
|
||||
actions.push({
|
||||
label: 'Cancel',
|
||||
action: 'cancel',
|
||||
className: 'cancel-button'
|
||||
});
|
||||
} else if (task.status === 'Completed' || task.status === 'Cancelled') {
|
||||
actions.push({
|
||||
label: 'Restart',
|
||||
action: 'restart',
|
||||
className: 'restart-button'
|
||||
});
|
||||
}
|
||||
|
||||
actions.push(
|
||||
{
|
||||
label: 'Edit',
|
||||
action: 'edit',
|
||||
className: 'update-button'
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
action: 'delete',
|
||||
className: 'delete-button'
|
||||
}
|
||||
);
|
||||
|
||||
return actions;
|
||||
};
|
||||
|
||||
const filteredTasks = useMemo(() => {
|
||||
return tasks.filter(task =>
|
||||
task.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
task.type.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
task.status.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
task.model.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
}, [tasks, searchQuery]);
|
||||
|
||||
return (
|
||||
<div className="list-container">
|
||||
<div className="dashboard-header">
|
||||
<h2>AI Training</h2>
|
||||
<div className="dashboard-controls">
|
||||
<div className="search-container">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Szukaj zadań..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="search-input"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
className="clear-search"
|
||||
onClick={() => setSearchQuery('')}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className="create-button"
|
||||
onClick={handleCreateTask}
|
||||
>
|
||||
<i className="fas fa-plus"></i>
|
||||
Create Task
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message.text && (
|
||||
<div className={`message ${message.type}`}>
|
||||
{message.text}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isFormVisible && (
|
||||
<div className="form-overlay">
|
||||
<div className="form-container">
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
formRefs={formRefs}
|
||||
onSubmit={handleFormSubmit}
|
||||
onCancel={handleFormCancel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ListGenerator
|
||||
data={filteredTasks}
|
||||
selectedItem={selectedTask}
|
||||
onItemSelect={handleTaskSelect}
|
||||
onItemAction={handleTaskAction}
|
||||
getItemActions={getTaskActions}
|
||||
renderItem={(task) => (
|
||||
<div className="list-row">
|
||||
<div className="item-info">
|
||||
<div className="item-name">{task.name}</div>
|
||||
<div className="item-type">{task.type}</div>
|
||||
<div className={`item-status ${task.status.toLowerCase().replace(' ', '-')}`}>
|
||||
{task.status}
|
||||
</div>
|
||||
<div className="item-progress">
|
||||
<div className="progress-bar">
|
||||
<div
|
||||
className="progress-fill"
|
||||
style={{ width: `${task.progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span>{task.progress}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item-details">
|
||||
<div>Last Modified: {task.lastModified}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
renderDetails={(task) => (
|
||||
<div className="details-panel">
|
||||
<h3>Task Details</h3>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">ID:</span>
|
||||
<span className="detail-value">{task.id}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Name:</span>
|
||||
<span className="detail-value">{task.name}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Type:</span>
|
||||
<span className="detail-value">{task.type}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Status:</span>
|
||||
<span className="detail-value">{task.status}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Progress:</span>
|
||||
<span className="detail-value">{task.progress}%</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Last Modified:</span>
|
||||
<span className="detail-value">{task.lastModified}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AITasksDashboard;
|
||||
|
|
@ -1,570 +0,0 @@
|
|||
import React, { useState, useRef, useMemo } from 'react';
|
||||
import { ListGenerator } from '../../components/forms/listGenerator';
|
||||
import FormGenerator from '../../components/forms/formGenerator';
|
||||
|
||||
const RendersDashboard = () => {
|
||||
const [selectedRender, setSelectedRender] = useState(null);
|
||||
const [renders, setRenders] = useState([
|
||||
{
|
||||
id: 1,
|
||||
name: "Character Animation",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Hero Character",
|
||||
startTime: "2024-03-20 09:00:00",
|
||||
endTime: "2024-03-20 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Environment Lighting",
|
||||
type: "Still",
|
||||
status: "In Progress",
|
||||
progress: 75,
|
||||
model: "Forest Scene",
|
||||
startTime: "2024-03-20 10:15:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Product Showcase",
|
||||
type: "360 View",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Sports Car",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Battle Scene",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Warriors",
|
||||
startTime: "2024-03-19 15:00:00",
|
||||
endTime: "2024-03-19 18:30:00"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Architectural Visualization",
|
||||
type: "Still",
|
||||
status: "In Progress",
|
||||
progress: 60,
|
||||
model: "Modern House",
|
||||
startTime: "2024-03-20 08:45:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Character Portrait",
|
||||
type: "Still",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Fantasy Character",
|
||||
startTime: "2024-03-19 11:00:00",
|
||||
endTime: "2024-03-19 12:30:00"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Vehicle Animation",
|
||||
type: "Animation",
|
||||
status: "In Progress",
|
||||
progress: 82,
|
||||
model: "Racing Car",
|
||||
startTime: "2024-03-20 09:30:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Product Display",
|
||||
type: "360 View",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Smartphone",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Nature Scene",
|
||||
type: "Still",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Mountain Landscape",
|
||||
startTime: "2024-03-19 13:15:00",
|
||||
endTime: "2024-03-19 15:45:00"
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Character Walk Cycle",
|
||||
type: "Animation",
|
||||
status: "In Progress",
|
||||
progress: 45,
|
||||
model: "Robot Character",
|
||||
startTime: "2024-03-20 11:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Jewelry Showcase",
|
||||
type: "360 View",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Diamond Ring",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "City Flythrough",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Future City",
|
||||
startTime: "2024-03-19 09:00:00",
|
||||
endTime: "2024-03-19 14:30:00"
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Interior Design",
|
||||
type: "Still",
|
||||
status: "In Progress",
|
||||
progress: 68,
|
||||
model: "Living Room",
|
||||
startTime: "2024-03-20 10:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Product Animation",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Gaming Console",
|
||||
startTime: "2024-03-19 14:00:00",
|
||||
endTime: "2024-03-19 16:00:00"
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Character Showcase",
|
||||
type: "360 View",
|
||||
status: "In Progress",
|
||||
progress: 92,
|
||||
model: "Superhero",
|
||||
startTime: "2024-03-20 07:30:00",
|
||||
endTime: null
|
||||
}
|
||||
]);
|
||||
const [isFormVisible, setIsFormVisible] = useState(false);
|
||||
const [formMode, setFormMode] = useState('create');
|
||||
const [message, setMessage] = useState({ type: '', text: '' });
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const nameInput = React.createRef();
|
||||
const typeInput = React.createRef();
|
||||
const resolutionInput = React.createRef();
|
||||
const threeDModelInput = React.createRef();
|
||||
|
||||
const formRefs = [
|
||||
nameInput,
|
||||
typeInput,
|
||||
resolutionInput,
|
||||
threeDModelInput
|
||||
];
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: formMode === 'create' ? 'Create' : 'Update',
|
||||
endpoint: 'renders',
|
||||
button_value: formMode === 'create' ? '+ RENDER' : 'UPDATE',
|
||||
allowButtonAction: false
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'Name',
|
||||
ref: nameInput,
|
||||
value: selectedRender?.name || '',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'Resolution',
|
||||
ref: resolutionInput,
|
||||
value: selectedRender?.resolution || '',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'choice-listing',
|
||||
name: '3D Model',
|
||||
ref: threeDModelInput,
|
||||
values: selectedRender?.threeDModel|| '',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
];
|
||||
|
||||
const handleRenderSelect = (render) => {
|
||||
setSelectedRender(render);
|
||||
};
|
||||
|
||||
const handleRenderAction = (action, render) => {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
handleDeleteRender(render.id);
|
||||
break;
|
||||
case 'edit':
|
||||
handleEditRender(render);
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown action:', action);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateRender = () => {
|
||||
setFormMode('create');
|
||||
setIsFormVisible(true);
|
||||
setSelectedRender(null);
|
||||
};
|
||||
|
||||
const handleEditRender = (render) => {
|
||||
setFormMode('edit');
|
||||
setIsFormVisible(true);
|
||||
setSelectedRender(render);
|
||||
};
|
||||
|
||||
const handleDeleteRender = (renderId) => {
|
||||
setRenders(renders.filter(render => render.id !== renderId));
|
||||
if (selectedRender?.id === renderId) {
|
||||
setSelectedRender(null);
|
||||
}
|
||||
setMessage({ type: 'success', text: 'Render został usunięty' });
|
||||
};
|
||||
|
||||
const handleFormSubmit = (formData) => {
|
||||
if (formMode === 'create') {
|
||||
const newRender = {
|
||||
id: renders.length + 1,
|
||||
...formData,
|
||||
lastModified: new Date().toISOString().split('T')[0],
|
||||
status: 'In Progress',
|
||||
progress: 0
|
||||
};
|
||||
setRenders([...renders, newRender]);
|
||||
setMessage({ type: 'success', text: 'Render został utworzony' });
|
||||
} else {
|
||||
setRenders(renders.map(render =>
|
||||
render.id === selectedRender.id
|
||||
? { ...render, ...formData, lastModified: new Date().toISOString().split('T')[0] }
|
||||
: render
|
||||
));
|
||||
setMessage({ type: 'success', text: 'Render został zaktualizowany' });
|
||||
}
|
||||
setIsFormVisible(false);
|
||||
};
|
||||
|
||||
const handleFormCancel = () => {
|
||||
setIsFormVisible(false);
|
||||
setSelectedRender(null);
|
||||
};
|
||||
|
||||
const getRenderActions = (render) => {
|
||||
return [
|
||||
{
|
||||
label: 'Edit',
|
||||
action: 'edit',
|
||||
className: 'update-button'
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
action: 'delete',
|
||||
className: 'delete-button'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const mockRenders = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Dragon Scene",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Dragon Model",
|
||||
startTime: "2024-03-01 10:00:00",
|
||||
endTime: "2024-03-01 12:30:00"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Castle Exterior",
|
||||
type: "Still",
|
||||
status: "In Progress",
|
||||
progress: 65,
|
||||
model: "Medieval Castle",
|
||||
startTime: "2024-03-02 09:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Weapon Showcase",
|
||||
type: "360 View",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Sci-fi Weapon",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Forest Flythrough",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Forest Scene",
|
||||
startTime: "2024-03-01 14:00:00",
|
||||
endTime: "2024-03-01 16:00:00"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Robot Animation",
|
||||
type: "Animation",
|
||||
status: "In Progress",
|
||||
progress: 45,
|
||||
model: "Robot Character",
|
||||
startTime: "2024-03-02 11:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Spaceship Launch",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Space Ship",
|
||||
startTime: "2024-03-01 13:00:00",
|
||||
endTime: "2024-03-01 15:30:00"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Temple Interior",
|
||||
type: "Still",
|
||||
status: "In Progress",
|
||||
progress: 78,
|
||||
model: "Ancient Temple",
|
||||
startTime: "2024-03-02 10:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Sword Display",
|
||||
type: "360 View",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Fantasy Sword",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "City Timelapse",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "City Block",
|
||||
startTime: "2024-03-01 09:00:00",
|
||||
endTime: "2024-03-01 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Warrior Battle",
|
||||
type: "Animation",
|
||||
status: "In Progress",
|
||||
progress: 89,
|
||||
model: "Warrior Character",
|
||||
startTime: "2024-03-02 13:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Car Showcase",
|
||||
type: "360 View",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
model: "Futuristic Car",
|
||||
startTime: null,
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Mountain Vista",
|
||||
type: "Still",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Mountain Range",
|
||||
startTime: "2024-03-01 15:00:00",
|
||||
endTime: "2024-03-01 16:30:00"
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Alien Movement",
|
||||
type: "Animation",
|
||||
status: "In Progress",
|
||||
progress: 34,
|
||||
model: "Alien Creature",
|
||||
startTime: "2024-03-02 14:00:00",
|
||||
endTime: null
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Staff Effects",
|
||||
type: "Animation",
|
||||
status: "Completed",
|
||||
progress: 100,
|
||||
model: "Magic Staff",
|
||||
startTime: "2024-03-01 16:00:00",
|
||||
endTime: "2024-03-01 18:30:00"
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Cave Exploration",
|
||||
type: "Animation",
|
||||
status: "In Progress",
|
||||
progress: 56,
|
||||
model: "Underground Cave",
|
||||
startTime: "2024-03-02 12:00:00",
|
||||
endTime: null
|
||||
}
|
||||
];
|
||||
|
||||
const filteredRenders = useMemo(() => {
|
||||
return renders.filter(render =>
|
||||
render.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
render.type.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
render.status.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
render.model.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
}, [renders, searchQuery]);
|
||||
|
||||
return (
|
||||
<div className="list-container">
|
||||
<div className="dashboard-header">
|
||||
<h2>3D Rendering</h2>
|
||||
<div className="dashboard-controls">
|
||||
<div className="search-container">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Szukaj renderów..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="search-input"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
className="clear-search"
|
||||
onClick={() => setSearchQuery('')}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className="create-button"
|
||||
onClick={handleCreateRender}
|
||||
>
|
||||
<i className="fas fa-plus"></i>
|
||||
Create Render
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message.text && (
|
||||
<div className={`message ${message.type}`}>
|
||||
{message.text}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isFormVisible && (
|
||||
<div className="form-overlay">
|
||||
<div className="form-container">
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
refList={formRefs}
|
||||
action={handleFormSubmit}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ListGenerator
|
||||
data={filteredRenders}
|
||||
selectedItem={selectedRender}
|
||||
onItemSelect={handleRenderSelect}
|
||||
onItemAction={handleRenderAction}
|
||||
getItemActions={getRenderActions}
|
||||
renderItem={(render) => (
|
||||
<div className="list-row">
|
||||
<div className="item-info">
|
||||
<div className="item-name">{render.name}</div>
|
||||
<div className="item-type">{render.type}</div>
|
||||
<div className={`item-status ${render.status.toLowerCase().replace(' ', '-')}`}>
|
||||
{render.status}
|
||||
</div>
|
||||
<div className="item-progress">
|
||||
<div className="progress-bar">
|
||||
<div
|
||||
className="progress-fill"
|
||||
style={{ width: `${render.progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span>{render.progress}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item-details">
|
||||
<div>Rozdzielczość: {render.resolution}</div>
|
||||
<div>Ostatnia modyfikacja: {render.lastModified}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
renderDetails={(render) => (
|
||||
<div className="details-panel">
|
||||
<h3>Szczegóły renderu</h3>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">ID:</span>
|
||||
<span className="detail-value">{render.id}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Nazwa:</span>
|
||||
<span className="detail-value">{render.name}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Typ:</span>
|
||||
<span className="detail-value">{render.type}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Rozdzielczość:</span>
|
||||
<span className="detail-value">{render.resolution}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Status:</span>
|
||||
<span className="detail-value">{render.status}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Postęp:</span>
|
||||
<span className="detail-value">{render.progress}%</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Ostatnia modyfikacja:</span>
|
||||
<span className="detail-value">{render.lastModified}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RendersDashboard;
|
||||
|
||||
|
|
@ -1,413 +0,0 @@
|
|||
import React, { useState, useRef, useMemo } from 'react';
|
||||
import { ListGenerator } from '../../components/forms/listGenerator';
|
||||
import FormGenerator from '../../components/forms/formGenerator';
|
||||
|
||||
const ServersDashboard = () => {
|
||||
const [selectedServer, setSelectedServer] = useState(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [servers, setServers] = useState([
|
||||
{
|
||||
id: 1,
|
||||
name: "Render Node 1",
|
||||
type: "Render Node",
|
||||
status: "Active",
|
||||
progress: 100,
|
||||
ip: "192.168.1.101",
|
||||
lastActive: "2024-03-20 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "AI Training Node 1",
|
||||
type: "AI Training Node",
|
||||
status: "In Progress",
|
||||
progress: 75,
|
||||
ip: "192.168.1.102",
|
||||
lastActive: "2024-03-20 11:29:00"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Storage Server 1",
|
||||
type: "Storage Server",
|
||||
status: "Active",
|
||||
progress: 100,
|
||||
ip: "192.168.1.103",
|
||||
lastActive: "2024-03-20 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Render Node 2",
|
||||
type: "Render Node",
|
||||
status: "Inactive",
|
||||
progress: 0,
|
||||
ip: "192.168.1.104",
|
||||
lastActive: "2024-03-20 10:15:00"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "AI Training Node 2",
|
||||
type: "AI Training Node",
|
||||
status: "Active",
|
||||
progress: 100,
|
||||
ip: "192.168.1.105",
|
||||
lastActive: "2024-03-20 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Storage Server 2",
|
||||
type: "Storage Server",
|
||||
status: "Active",
|
||||
progress: 100,
|
||||
ip: "192.168.1.106",
|
||||
lastActive: "2024-03-20 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Render Node 3",
|
||||
type: "Render Node",
|
||||
status: "In Progress",
|
||||
progress: 82,
|
||||
ip: "192.168.1.107",
|
||||
lastActive: "2024-03-20 11:29:00"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "AI Training Node 3",
|
||||
type: "AI Training Node",
|
||||
status: "Queued",
|
||||
progress: 0,
|
||||
ip: "192.168.1.108",
|
||||
lastActive: "2024-03-20 11:00:00"
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Storage Server 3",
|
||||
type: "Storage Server",
|
||||
status: "Active",
|
||||
progress: 100,
|
||||
ip: "192.168.1.109",
|
||||
lastActive: "2024-03-20 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Render Node 4",
|
||||
type: "Render Node",
|
||||
status: "In Progress",
|
||||
progress: 45,
|
||||
ip: "192.168.1.110",
|
||||
lastActive: "2024-03-20 11:29:00"
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "AI Training Node 4",
|
||||
type: "AI Training Node",
|
||||
status: "Active",
|
||||
progress: 100,
|
||||
ip: "192.168.1.111",
|
||||
lastActive: "2024-03-20 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Storage Server 4",
|
||||
type: "Storage Server",
|
||||
status: "Inactive",
|
||||
progress: 0,
|
||||
ip: "192.168.1.112",
|
||||
lastActive: "2024-03-20 10:45:00"
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Render Node 5",
|
||||
type: "Render Node",
|
||||
status: "Active",
|
||||
progress: 100,
|
||||
ip: "192.168.1.113",
|
||||
lastActive: "2024-03-20 11:30:00"
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "AI Training Node 5",
|
||||
type: "AI Training Node",
|
||||
status: "In Progress",
|
||||
progress: 68,
|
||||
ip: "192.168.1.114",
|
||||
lastActive: "2024-03-20 11:29:00"
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Storage Server 5",
|
||||
type: "Storage Server",
|
||||
status: "Active",
|
||||
progress: 100,
|
||||
ip: "192.168.1.115",
|
||||
lastActive: "2024-03-20 11:30:00"
|
||||
}
|
||||
]);
|
||||
const [isFormVisible, setIsFormVisible] = useState(false);
|
||||
const [formMode, setFormMode] = useState('create');
|
||||
const [message, setMessage] = useState({ type: '', text: '' });
|
||||
|
||||
const nameInput = React.createRef();
|
||||
const typeInput = React.createRef();
|
||||
const ipInput = React.createRef();
|
||||
const statusInput = React.createRef();
|
||||
|
||||
const formRefs = [
|
||||
nameInput,
|
||||
typeInput,
|
||||
ipInput,
|
||||
statusInput
|
||||
];
|
||||
|
||||
const inputList = [
|
||||
{
|
||||
type: 'info',
|
||||
action: formMode === 'create' ? 'Create' : 'Update',
|
||||
endpoint: 'servers',
|
||||
button_value: formMode === 'create' ? '+ SERVER' : 'UPDATE'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'NAME',
|
||||
ref: nameInput,
|
||||
value: selectedServer?.name || '',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'TYPE',
|
||||
ref: typeInput,
|
||||
options: [
|
||||
{ value: 'Render', label: 'Render' },
|
||||
{ value: 'AI', label: 'AI' },
|
||||
{ value: 'Storage', label: 'Storage' }
|
||||
],
|
||||
value: selectedServer?.type || 'Render',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'IP',
|
||||
ref: ipInput,
|
||||
value: selectedServer?.ip || '',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'STATUS',
|
||||
ref: statusInput,
|
||||
options: [
|
||||
{ value: 'Online', label: 'Online' },
|
||||
{ value: 'Offline', label: 'Offline' },
|
||||
{ value: 'Maintenance', label: 'Maintenance' }
|
||||
],
|
||||
value: selectedServer?.status || 'Offline',
|
||||
onChange: null,
|
||||
validationInfo: null
|
||||
}
|
||||
];
|
||||
|
||||
const handleServerSelect = (server) => {
|
||||
setSelectedServer(server);
|
||||
};
|
||||
|
||||
const handleServerAction = (action, server) => {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
handleDeleteServer(server.id);
|
||||
break;
|
||||
case 'edit':
|
||||
handleEditServer(server);
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown action:', action);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateServer = () => {
|
||||
setFormMode('create');
|
||||
setIsFormVisible(true);
|
||||
setSelectedServer(null);
|
||||
};
|
||||
|
||||
const handleEditServer = (server) => {
|
||||
setFormMode('edit');
|
||||
setIsFormVisible(true);
|
||||
setSelectedServer(server);
|
||||
};
|
||||
|
||||
const handleDeleteServer = (serverId) => {
|
||||
setServers(servers.filter(server => server.id !== serverId));
|
||||
if (selectedServer?.id === serverId) {
|
||||
setSelectedServer(null);
|
||||
}
|
||||
setMessage({ type: 'success', text: 'Serwer został usunięty' });
|
||||
};
|
||||
|
||||
const handleFormSubmit = (formData) => {
|
||||
if (formMode === 'create') {
|
||||
const newServer = {
|
||||
id: servers.length + 1,
|
||||
...formData,
|
||||
lastModified: new Date().toISOString().split('T')[0],
|
||||
status: 'Offline'
|
||||
};
|
||||
setServers([...servers, newServer]);
|
||||
setMessage({ type: 'success', text: 'Serwer został utworzony' });
|
||||
} else {
|
||||
setServers(servers.map(server =>
|
||||
server.id === selectedServer.id
|
||||
? { ...server, ...formData, lastModified: new Date().toISOString().split('T')[0] }
|
||||
: server
|
||||
));
|
||||
setMessage({ type: 'success', text: 'Serwer został zaktualizowany' });
|
||||
}
|
||||
setIsFormVisible(false);
|
||||
};
|
||||
|
||||
const handleFormCancel = () => {
|
||||
setIsFormVisible(false);
|
||||
setSelectedServer(null);
|
||||
};
|
||||
|
||||
const getServerActions = (server) => {
|
||||
return [
|
||||
{
|
||||
label: 'Edit',
|
||||
action: 'edit',
|
||||
className: 'update-button'
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
action: 'delete',
|
||||
className: 'delete-button'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const handleFormAction = (refs) => {
|
||||
const formData = {};
|
||||
refs.forEach((ref, index) => {
|
||||
formData[inputList[index].name] = ref.current.value;
|
||||
});
|
||||
handleFormSubmit(formData);
|
||||
};
|
||||
|
||||
const filteredServers = useMemo(() => {
|
||||
return servers.filter(server =>
|
||||
server.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
server.type.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
server.status.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
server.ip.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
}, [servers, searchQuery]);
|
||||
|
||||
return (
|
||||
<div className="list-container">
|
||||
<div className="dashboard-header">
|
||||
<h2>GPU Instances</h2>
|
||||
<div className="dashboard-controls">
|
||||
<div className="search-container">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Szukaj serwerów..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="search-input"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
className="clear-search"
|
||||
onClick={() => setSearchQuery('')}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className="create-button"
|
||||
onClick={handleCreateServer}
|
||||
>
|
||||
<i className="fas fa-plus"></i>
|
||||
Add Server
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message.text && (
|
||||
<div className={`message ${message.type}`}>
|
||||
{message.text}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isFormVisible && (
|
||||
<div className="form-overlay">
|
||||
<div className="form-container">
|
||||
<FormGenerator
|
||||
inputList={inputList}
|
||||
refList={formRefs}
|
||||
action={handleFormAction}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ListGenerator
|
||||
data={filteredServers}
|
||||
selectedItem={selectedServer}
|
||||
onItemSelect={handleServerSelect}
|
||||
onItemAction={handleServerAction}
|
||||
getItemActions={getServerActions}
|
||||
renderItem={(server) => (
|
||||
<div className="list-row">
|
||||
<div className="item-info">
|
||||
<div className="item-name">{server.name}</div>
|
||||
<div className="item-type">{server.type}</div>
|
||||
<div className={`item-status ${server.status.toLowerCase()}`}>
|
||||
{server.status}
|
||||
</div>
|
||||
</div>
|
||||
<div className="item-details">
|
||||
<div>IP: {server.ip}</div>
|
||||
<div>Ostatnia modyfikacja: {server.lastModified}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
renderDetails={(server) => (
|
||||
<div className="details-panel">
|
||||
<h3>Szczegóły serwera</h3>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">ID:</span>
|
||||
<span className="detail-value">{server.id}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Nazwa:</span>
|
||||
<span className="detail-value">{server.name}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Typ:</span>
|
||||
<span className="detail-value">{server.type}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">IP:</span>
|
||||
<span className="detail-value">{server.ip}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Status:</span>
|
||||
<span className="detail-value">{server.status}</span>
|
||||
</div>
|
||||
<div className="detail-row">
|
||||
<span className="detail-label">Ostatnia modyfikacja:</span>
|
||||
<span className="detail-value">{server.lastModified}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServersDashboard;
|
||||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectCurrentUser } from '../../redux/slices/userAuthSlice';
|
||||
import UserUpdateProfileForm from '../../components/forms/user_settings/userUpdateProfileForm';
|
||||
import UserChangePasswordForm from '../../components/forms/user_settings/userChangePasswordForm';
|
||||
import UserDeleteAccountForm from '../../components/forms/user_settings/userDeleteAccountForm';
|
||||
|
||||
const UserSettingsDashboard = () => {
|
||||
const user = useSelector(selectCurrentUser);
|
||||
|
||||
return (
|
||||
<div className="dashboard-container">
|
||||
<div className="dashboard-header">
|
||||
<h1>Panel Użytkownika</h1>
|
||||
<div className="user-info">
|
||||
<span>Zalogowany jako: {user?.login}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="dashboard-content">
|
||||
<div className="user-settings">
|
||||
<div className="settings-section">
|
||||
<h2>Aktualizacja profilu</h2>
|
||||
<UserUpdateProfileForm />
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<h2>Zmiana hasła</h2>
|
||||
<UserChangePasswordForm />
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<h2>Usuwanie konta</h2>
|
||||
<UserDeleteAccountForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserSettingsDashboard;
|
||||
|
|
@ -1,11 +1,150 @@
|
|||
import React from 'react';
|
||||
import * as React from "react"
|
||||
|
||||
// styles
|
||||
const pageStyles = {
|
||||
color: "#232129",
|
||||
padding: "96px",
|
||||
fontFamily: "-apple-system, Roboto, sans-serif, serif",
|
||||
}
|
||||
const headingStyles = {
|
||||
marginTop: 0,
|
||||
marginBottom: 64,
|
||||
maxWidth: 320,
|
||||
}
|
||||
const headingAccentStyles = {
|
||||
color: "#663399",
|
||||
}
|
||||
const paragraphStyles = {
|
||||
marginBottom: 48,
|
||||
}
|
||||
const codeStyles = {
|
||||
color: "#8A6534",
|
||||
padding: 4,
|
||||
backgroundColor: "#FFF4DB",
|
||||
fontSize: "1.25rem",
|
||||
borderRadius: 4,
|
||||
}
|
||||
const listStyles = {
|
||||
marginBottom: 96,
|
||||
paddingLeft: 0,
|
||||
}
|
||||
const listItemStyles = {
|
||||
fontWeight: "300",
|
||||
fontSize: "24px",
|
||||
maxWidth: "560px",
|
||||
}
|
||||
|
||||
const linkStyle = {
|
||||
color: "#8954A8",
|
||||
fontWeight: "bold",
|
||||
fontSize: "16px",
|
||||
verticalAlign: "5%",
|
||||
}
|
||||
|
||||
const docLinkStyle = {
|
||||
...linkStyle,
|
||||
listStyleType: "none",
|
||||
marginBottom: 24,
|
||||
}
|
||||
|
||||
const descriptionStyle = {
|
||||
color: "#232129",
|
||||
fontSize: "14px",
|
||||
}
|
||||
|
||||
const docLink = {
|
||||
text: "Documentation",
|
||||
url: "https://www.gatsbyjs.com/docs/",
|
||||
color: "#8954A8",
|
||||
}
|
||||
// data
|
||||
const links = [
|
||||
{
|
||||
text: "Tutorial",
|
||||
url: "https://www.gatsbyjs.com/docs/tutorial/",
|
||||
description:
|
||||
"A great place to get started if you're new to web development. Designed to guide you through setting up your first Gatsby site.",
|
||||
color: "#E95800",
|
||||
},
|
||||
{
|
||||
text: "How to Guides",
|
||||
url: "https://www.gatsbyjs.com/docs/how-to/",
|
||||
description:
|
||||
"Practical step-by-step guides to help you achieve a specific goal. Most useful when you're trying to get something done.",
|
||||
color: "#1099A8",
|
||||
},
|
||||
{
|
||||
text: "Reference Guides",
|
||||
url: "https://www.gatsbyjs.com/docs/reference/",
|
||||
description:
|
||||
"Nitty-gritty technical descriptions of how Gatsby works. Most useful when you need detailed information about Gatsby's APIs.",
|
||||
color: "#BC027F",
|
||||
},
|
||||
{
|
||||
text: "Conceptual Guides",
|
||||
url: "https://www.gatsbyjs.com/docs/conceptual/",
|
||||
description:
|
||||
"Big-picture explanations of higher-level Gatsby concepts. Most useful for building understanding of a particular topic.",
|
||||
color: "#0D96F2",
|
||||
},
|
||||
{
|
||||
text: "Plugin Library",
|
||||
url: "https://www.gatsbyjs.com/plugins",
|
||||
description:
|
||||
"Add functionality and customize your Gatsby site or app with thousands of plugins built by our amazing developer community.",
|
||||
color: "#000000",
|
||||
},
|
||||
]
|
||||
|
||||
// markup
|
||||
const IndexPage = () => {
|
||||
return (
|
||||
<div>
|
||||
{/* Reszta komponentów aplikacji */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<main style={pageStyles}>
|
||||
<title>Home Page</title>
|
||||
<h1 style={headingStyles}>
|
||||
Congratulations
|
||||
<br />
|
||||
<span style={headingAccentStyles}>— you just made a Gatsby site! </span>
|
||||
<span role="img" aria-label="Party popper emojis">
|
||||
🎉🎉🎉
|
||||
</span>
|
||||
</h1>
|
||||
<p style={paragraphStyles}>
|
||||
Edit <code style={codeStyles}>src/pages/index.js</code> to see this page
|
||||
update in real-time.{" "}
|
||||
<span role="img" aria-label="Sunglasses smiley emoji">
|
||||
😎
|
||||
</span>
|
||||
</p>
|
||||
<ul style={listStyles}>
|
||||
<li style={docLinkStyle}>
|
||||
<a
|
||||
style={linkStyle}
|
||||
href={`${docLink.url}?utm_source=starter&utm_medium=start-page&utm_campaign=minimal-starter`}
|
||||
>
|
||||
{docLink.text}
|
||||
</a>
|
||||
</li>
|
||||
{links.map(link => (
|
||||
<li key={link.url} style={{ ...listItemStyles, color: link.color }}>
|
||||
<span>
|
||||
<a
|
||||
style={linkStyle}
|
||||
href={`${link.url}?utm_source=starter&utm_medium=start-page&utm_campaign=minimal-starter`}
|
||||
>
|
||||
{link.text}
|
||||
</a>
|
||||
<p style={descriptionStyle}>{link.description}</p>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<img
|
||||
alt="Gatsby G Logo"
|
||||
src="data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 2a10 10 0 110 20 10 10 0 010-20zm0 2c-3.73 0-6.86 2.55-7.75 6L14 19.75c3.45-.89 6-4.02 6-7.75h-5.25v1.5h3.45a6.37 6.37 0 01-3.89 4.44L6.06 9.69C7 7.31 9.3 5.63 12 5.63c2.13 0 4 1.04 5.18 2.65l1.23-1.06A7.959 7.959 0 0012 4zm-8 8a8 8 0 008 8c.04 0 .09 0-8-8z' fill='%23639'/%3E%3C/svg%3E"
|
||||
/>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default IndexPage;
|
||||
export default IndexPage
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import '../styles/general.scss';
|
||||
|
||||
import UserRegisterForm from '../components/forms/user_auth/userRegister.js';
|
||||
import FootComponent from '../components/foot.js';
|
||||
import NavBarComponent from '../components/navbar.js';
|
||||
|
||||
|
||||
const LandingPage = () => {
|
||||
return (
|
||||
<>
|
||||
<NavBarComponent />
|
||||
<div className="landing-container">
|
||||
<header className="landing">
|
||||
<h2>Affordable. Efficient. Accessible.</h2>
|
||||
<h1>GUARANTED.</h1>
|
||||
<p>
|
||||
Minimize your expenses without compromising on quality.
|
||||
<br />
|
||||
Our platform is built for individuals who need top-tier rendering
|
||||
<br />
|
||||
<span className="span-white">
|
||||
without the top-tier investment.
|
||||
</span>
|
||||
</p>
|
||||
<button>
|
||||
GET A DEMO
|
||||
</button>
|
||||
<h4>
|
||||
GPU IS IMPORTANT...
|
||||
</h4>
|
||||
<p>
|
||||
...But <span className="span-white">GPU server can be very expensive.</span>
|
||||
<br />
|
||||
Configuration and administration<span className="span-white"> take too long time.</span>
|
||||
<br />
|
||||
<span className="span-white">
|
||||
Everything of that needs so much additional knowledge.
|
||||
</span>
|
||||
</p>
|
||||
<h2>
|
||||
We Offer Ready Solution.
|
||||
</h2>
|
||||
<p>
|
||||
Boost your GPU power for your 3D and AI models stuff
|
||||
<br />
|
||||
<span className="span-white">
|
||||
without overpay for servers...
|
||||
</span>
|
||||
<br />
|
||||
<span className="span-white">
|
||||
...without overtime for environment adjustments!
|
||||
</span>
|
||||
</p>
|
||||
<h2>
|
||||
"OK... So What Makes You So Different?"
|
||||
</h2>
|
||||
<h4>
|
||||
GUARANTED
|
||||
</h4>
|
||||
<p>
|
||||
We only win if you win. You won't carry all the risk, we'll share it
|
||||
</p>
|
||||
<h4>
|
||||
RESLUTS
|
||||
</h4>
|
||||
<p>
|
||||
Our first priority is to get you results.
|
||||
</p>
|
||||
<h4>
|
||||
LOCAL
|
||||
</h4>
|
||||
<p>
|
||||
</p>
|
||||
<h4>
|
||||
SPECIALIZED
|
||||
</h4>
|
||||
<p>
|
||||
</p>
|
||||
</header>
|
||||
<main className="secondary-content">
|
||||
<UserRegisterForm />
|
||||
</main>
|
||||
</div>
|
||||
<FootComponent />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default LandingPage
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import axios from 'axios';
|
||||
|
||||
const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
|
||||
|
||||
// Helper function to get auth header
|
||||
const getAuthHeader = (token) => ({
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
export const fetchAiModels = createAsyncThunk(
|
||||
'aiModelCrud/fetchModels',
|
||||
async (_, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.get(`${API_URL}/ai/models`, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchAiModel = createAsyncThunk(
|
||||
'aiModelCrud/fetchModel',
|
||||
async (modelId, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.get(`${API_URL}/ai/models/${modelId}`, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const createAiModel = createAsyncThunk(
|
||||
'aiModelCrud/createModel',
|
||||
async (modelData, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.post(`${API_URL}/ai/models`, modelData, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const updateAiModel = createAsyncThunk(
|
||||
'aiModelCrud/updateModel',
|
||||
async ({ modelId, updates }, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.put(`${API_URL}/ai/models/${modelId}`, updates, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteAiModel = createAsyncThunk(
|
||||
'aiModelCrud/deleteModel',
|
||||
async (modelId, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
await axios.delete(`${API_URL}/ai/models/${modelId}`, getAuthHeader(token));
|
||||
return modelId;
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchAiTasks = createAsyncThunk(
|
||||
'aiModelCrud/fetchTasks',
|
||||
async ({ status, page = 1, limit = 10 }, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const params = { page, limit };
|
||||
if (status) params.status = status;
|
||||
|
||||
const response = await axios.get(`${API_URL}/ai/tasks`, {
|
||||
...getAuthHeader(token),
|
||||
params
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const createAiTask = createAsyncThunk(
|
||||
'aiModelCrud/createTask',
|
||||
async (taskData, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.post(`${API_URL}/ai/tasks`, taskData, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const updateAiTask = createAsyncThunk(
|
||||
'aiModelCrud/updateTask',
|
||||
async ({ taskId, action }, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.put(
|
||||
`${API_URL}/ai/tasks/${taskId}`,
|
||||
{ action },
|
||||
getAuthHeader(token)
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteAiTask = createAsyncThunk(
|
||||
'aiModelCrud/deleteTask',
|
||||
async (taskId, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
await axios.delete(`${API_URL}/ai/tasks/${taskId}`, getAuthHeader(token));
|
||||
return taskId;
|
||||
}
|
||||
);
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import axios from 'axios';
|
||||
|
||||
const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
|
||||
|
||||
// Helper function to get auth header
|
||||
const getAuthHeader = (token) => ({
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
export const fetchRenders = createAsyncThunk(
|
||||
'renderCrud/fetchRenders',
|
||||
async ({ page = 1, limit = 10 }, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.get(`${API_URL}/renders`, {
|
||||
...getAuthHeader(token),
|
||||
params: { page, limit }
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchRender = createAsyncThunk(
|
||||
'renderCrud/fetchRender',
|
||||
async (renderId, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.get(`${API_URL}/renders/${renderId}`, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const updateRender = createAsyncThunk(
|
||||
'renderCrud/updateRender',
|
||||
async ({ renderId, updates }, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.put(`${API_URL}/renders/${renderId}`, updates, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteRender = createAsyncThunk(
|
||||
'renderCrud/deleteRender',
|
||||
async (renderId, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
await axios.delete(`${API_URL}/renders/${renderId}`, getAuthHeader(token));
|
||||
return renderId;
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchRenderTasks = createAsyncThunk(
|
||||
'renderCrud/fetchTasks',
|
||||
async ({ status, page = 1, limit = 10 }, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const params = { page, limit };
|
||||
if (status) params.status = status;
|
||||
|
||||
const response = await axios.get(`${API_URL}/renders/tasks`, {
|
||||
...getAuthHeader(token),
|
||||
params
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const createRenderTask = createAsyncThunk(
|
||||
'renderCrud/createTask',
|
||||
async (taskData, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.post(`${API_URL}/renders/tasks`, taskData, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const updateRenderTask = createAsyncThunk(
|
||||
'renderCrud/updateTask',
|
||||
async ({ taskId, action }, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.put(
|
||||
`${API_URL}/renders/tasks/${taskId}`,
|
||||
{ action },
|
||||
getAuthHeader(token)
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteRenderTask = createAsyncThunk(
|
||||
'renderCrud/deleteTask',
|
||||
async (taskId, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
await axios.delete(`${API_URL}/renders/tasks/${taskId}`, getAuthHeader(token));
|
||||
return taskId;
|
||||
}
|
||||
);
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import axios from 'axios';
|
||||
|
||||
const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
|
||||
|
||||
// Helper function to get auth header
|
||||
const getAuthHeader = (token) => ({
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
export const fetchModels = createAsyncThunk(
|
||||
'threeDModelCrud/fetchModels',
|
||||
async (_, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.get(`${API_URL}/models`, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchModel = createAsyncThunk(
|
||||
'threeDModelCrud/fetchModel',
|
||||
async (modelId, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.get(`${API_URL}/models/${modelId}`, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const createModel = createAsyncThunk(
|
||||
'threeDModelCrud/createModel',
|
||||
async (modelData, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.post(`${API_URL}/models`, modelData, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const updateModel = createAsyncThunk(
|
||||
'threeDModelCrud/updateModel',
|
||||
async ({ modelId, updates }, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
const response = await axios.put(`${API_URL}/models/${modelId}`, updates, getAuthHeader(token));
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteModel = createAsyncThunk(
|
||||
'threeDModelCrud/deleteModel',
|
||||
async (modelId, { getState }) => {
|
||||
const { token } = getState().userAuth;
|
||||
await axios.delete(`${API_URL}/models/${modelId}`, getAuthHeader(token));
|
||||
return modelId;
|
||||
}
|
||||
);
|
||||
|
||||
export const uploadModel = createAsyncThunk(
|
||||
'threeDModelCrud/uploadModel',
|
||||
async ({ user_id, file, token }) => {
|
||||
const formData = new FormData();
|
||||
formData.append('user_id', user_id);
|
||||
formData.append('file', file);
|
||||
formData.append('token', token);
|
||||
|
||||
const response = await axios.post(`${API_URL}/models/upload`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { setCredentials, setLoading, setError, logout } from '../slices/userAuthSlice';
|
||||
import { API_URL, AUTH_CONFIG } from '../../config';
|
||||
import { cookieService } from '../../services/cookieService';
|
||||
|
||||
export const loginUser = createAsyncThunk(
|
||||
'userAuth/login',
|
||||
async (credentials, { dispatch }) => {
|
||||
try {
|
||||
dispatch(setLoading(true));
|
||||
const response = await fetch(`${API_URL}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(credentials),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || 'Błąd logowania');
|
||||
}
|
||||
|
||||
dispatch(setCredentials({
|
||||
user: data.user,
|
||||
token: data.token,
|
||||
permissions: data.permissions
|
||||
}));
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
dispatch(setError(error.message));
|
||||
throw error;
|
||||
} finally {
|
||||
dispatch(setLoading(false));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const registerUser = createAsyncThunk(
|
||||
'userAuth/register',
|
||||
async (userData, { dispatch }) => {
|
||||
try {
|
||||
dispatch(setLoading(true));
|
||||
const response = await fetch(`${API_URL}/auth/register`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(userData),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || 'Błąd rejestracji');
|
||||
}
|
||||
|
||||
dispatch(setCredentials({
|
||||
user: data.user,
|
||||
token: data.token,
|
||||
permissions: data.permissions
|
||||
}));
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
dispatch(setError(error.message));
|
||||
throw error;
|
||||
} finally {
|
||||
dispatch(setLoading(false));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const logoutUser = createAsyncThunk(
|
||||
'userAuth/logout',
|
||||
async (_, { dispatch }) => {
|
||||
try {
|
||||
dispatch(setLoading(true));
|
||||
const token = cookieService.getToken();
|
||||
|
||||
if (token) {
|
||||
await fetch(`${API_URL}/auth/logout`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(logout());
|
||||
} catch (error) {
|
||||
console.error('Błąd podczas wylogowywania:', error);
|
||||
} finally {
|
||||
dispatch(setLoading(false));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const checkAuth = createAsyncThunk(
|
||||
'userAuth/checkAuth',
|
||||
async (_, { dispatch }) => {
|
||||
try {
|
||||
const token = cookieService.getToken();
|
||||
|
||||
if (!token) {
|
||||
throw new Error('Brak tokenu');
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_URL}/auth/verify`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Token nieprawidłowy');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
dispatch(logout());
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const changePassword = createAsyncThunk(
|
||||
'userAuth/changePassword',
|
||||
async (passwordData) => {
|
||||
const response = await fetch(`${API_URL}/change-password`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(passwordData),
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const updateProfile = createAsyncThunk(
|
||||
'userAuth/updateProfile',
|
||||
async (profileData, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/user/profile`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${cookieService.getToken()}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(profileData),
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error.response?.data);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteAccount = createAsyncThunk(
|
||||
'userAuth/deleteAccount',
|
||||
async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/user/delete`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${cookieService.getToken()}`,
|
||||
},
|
||||
});
|
||||
cookieService.clearAll();
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return rejectWithValue(error.response?.data);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import {
|
||||
fetchAiModels,
|
||||
fetchAiModel,
|
||||
createAiModel,
|
||||
updateAiModel,
|
||||
deleteAiModel,
|
||||
createAiTask,
|
||||
updateAiTask,
|
||||
deleteAiTask,
|
||||
fetchAiTasks
|
||||
} from '../asyncThunks/aiModelCrudAsyncThunk';
|
||||
|
||||
const initialState = {
|
||||
models: [],
|
||||
selectedModel: null,
|
||||
tasks: [],
|
||||
isLoading: false,
|
||||
error: null
|
||||
};
|
||||
|
||||
const aiModelCrudSlice = createSlice({
|
||||
name: 'aiModelCrud',
|
||||
initialState,
|
||||
reducers: {
|
||||
clearError: (state) => {
|
||||
state.error = null;
|
||||
},
|
||||
setSelectedModel: (state, action) => {
|
||||
state.selectedModel = action.payload;
|
||||
}
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
// Fetch Models
|
||||
builder.addCase(fetchAiModels.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchAiModels.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.models = action.payload;
|
||||
});
|
||||
builder.addCase(fetchAiModels.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Fetch Single Model
|
||||
builder.addCase(fetchAiModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchAiModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.selectedModel = action.payload;
|
||||
});
|
||||
builder.addCase(fetchAiModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Create Model
|
||||
builder.addCase(createAiModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(createAiModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.models.push(action.payload);
|
||||
});
|
||||
builder.addCase(createAiModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Update Model
|
||||
builder.addCase(updateAiModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(updateAiModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
const index = state.models.findIndex(model => model.id === action.payload.id);
|
||||
if (index !== -1) {
|
||||
state.models[index] = action.payload;
|
||||
}
|
||||
});
|
||||
builder.addCase(updateAiModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Delete Model
|
||||
builder.addCase(deleteAiModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(deleteAiModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.models = state.models.filter(model => model.id !== action.payload);
|
||||
});
|
||||
builder.addCase(deleteAiModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Fetch Tasks
|
||||
builder.addCase(fetchAiTasks.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchAiTasks.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.tasks = action.payload;
|
||||
});
|
||||
builder.addCase(fetchAiTasks.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Create Task
|
||||
builder.addCase(createAiTask.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(createAiTask.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.tasks.push(action.payload);
|
||||
});
|
||||
builder.addCase(createAiTask.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Update Task
|
||||
builder.addCase(updateAiTask.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(updateAiTask.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
const index = state.tasks.findIndex(task => task.id === action.payload.id);
|
||||
if (index !== -1) {
|
||||
state.tasks[index] = action.payload;
|
||||
}
|
||||
});
|
||||
builder.addCase(updateAiTask.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Delete Task
|
||||
builder.addCase(deleteAiTask.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(deleteAiTask.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.tasks = state.tasks.filter(task => task.id !== action.payload);
|
||||
});
|
||||
builder.addCase(deleteAiTask.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const { clearError, setSelectedModel } = aiModelCrudSlice.actions;
|
||||
export const aiModelCrudSelector = (state) => state.aiModelCrud;
|
||||
export default aiModelCrudSlice.reducer;
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import {
|
||||
fetchRenders,
|
||||
fetchRender,
|
||||
updateRender,
|
||||
deleteRender,
|
||||
fetchRenderTasks,
|
||||
createRenderTask,
|
||||
updateRenderTask,
|
||||
deleteRenderTask
|
||||
} from '../asyncThunks/renderCrudAsyncThunk';
|
||||
|
||||
const initialState = {
|
||||
renders: [],
|
||||
selectedRender: null,
|
||||
tasks: [],
|
||||
isLoading: false,
|
||||
error: null
|
||||
};
|
||||
|
||||
const renderCrudSlice = createSlice({
|
||||
name: 'renderCrud',
|
||||
initialState,
|
||||
reducers: {
|
||||
clearError: (state) => {
|
||||
state.error = null;
|
||||
},
|
||||
setSelectedRender: (state, action) => {
|
||||
state.selectedRender = action.payload;
|
||||
}
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
// Fetch Renders
|
||||
builder.addCase(fetchRenders.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchRenders.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.renders = action.payload;
|
||||
});
|
||||
builder.addCase(fetchRenders.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Fetch Single Render
|
||||
builder.addCase(fetchRender.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchRender.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.selectedRender = action.payload;
|
||||
});
|
||||
builder.addCase(fetchRender.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Update Render
|
||||
builder.addCase(updateRender.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(updateRender.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
const index = state.renders.findIndex(render => render.id === action.payload.id);
|
||||
if (index !== -1) {
|
||||
state.renders[index] = action.payload;
|
||||
}
|
||||
});
|
||||
builder.addCase(updateRender.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Delete Render
|
||||
builder.addCase(deleteRender.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(deleteRender.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.renders = state.renders.filter(render => render.id !== action.payload);
|
||||
});
|
||||
builder.addCase(deleteRender.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Fetch Tasks
|
||||
builder.addCase(fetchRenderTasks.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchRenderTasks.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.tasks = action.payload;
|
||||
});
|
||||
builder.addCase(fetchRenderTasks.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Create Task
|
||||
builder.addCase(createRenderTask.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(createRenderTask.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.tasks.push(action.payload);
|
||||
});
|
||||
builder.addCase(createRenderTask.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Update Task
|
||||
builder.addCase(updateRenderTask.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(updateRenderTask.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
const index = state.tasks.findIndex(task => task.id === action.payload.id);
|
||||
if (index !== -1) {
|
||||
state.tasks[index] = action.payload;
|
||||
}
|
||||
});
|
||||
builder.addCase(updateRenderTask.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Delete Task
|
||||
builder.addCase(deleteRenderTask.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(deleteRenderTask.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.tasks = state.tasks.filter(task => task.id !== action.payload);
|
||||
});
|
||||
builder.addCase(deleteRenderTask.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const { clearError, setSelectedRender } = renderCrudSlice.actions;
|
||||
export const renderCrudSelector = (state) => state.renderCrud;
|
||||
export default renderCrudSlice.reducer;
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import {
|
||||
fetchModels,
|
||||
fetchModel,
|
||||
createModel,
|
||||
updateModel,
|
||||
deleteModel,
|
||||
uploadModel
|
||||
} from '../asyncThunks/threeDModelCrudAsyncThunk';
|
||||
|
||||
const initialState = {
|
||||
models: [],
|
||||
selectedModel: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
upload_blend_file_status: ''
|
||||
};
|
||||
|
||||
const threeDModelCrudSlice = createSlice({
|
||||
name: 'threeDModelCrud',
|
||||
initialState,
|
||||
reducers: {
|
||||
clearError: (state) => {
|
||||
state.error = null;
|
||||
},
|
||||
setSelectedModel: (state, action) => {
|
||||
state.selectedModel = action.payload;
|
||||
}
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
// Fetch Models
|
||||
builder.addCase(fetchModels.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchModels.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.models = action.payload;
|
||||
});
|
||||
builder.addCase(fetchModels.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Fetch Single Model
|
||||
builder.addCase(fetchModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(fetchModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.selectedModel = action.payload;
|
||||
});
|
||||
builder.addCase(fetchModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Create Model
|
||||
builder.addCase(createModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(createModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.models.push(action.payload);
|
||||
});
|
||||
builder.addCase(createModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Update Model
|
||||
builder.addCase(updateModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(updateModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
const index = state.models.findIndex(model => model.id === action.payload.id);
|
||||
if (index !== -1) {
|
||||
state.models[index] = action.payload;
|
||||
}
|
||||
});
|
||||
builder.addCase(updateModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Delete Model
|
||||
builder.addCase(deleteModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
});
|
||||
builder.addCase(deleteModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.models = state.models.filter(model => model.id !== action.payload);
|
||||
});
|
||||
builder.addCase(deleteModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
});
|
||||
|
||||
// Upload Model
|
||||
builder.addCase(uploadModel.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
state.upload_blend_file_status = 'uploading';
|
||||
});
|
||||
builder.addCase(uploadModel.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.upload_blend_file_status = { info: 'Upload successful' };
|
||||
});
|
||||
builder.addCase(uploadModel.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.error.message;
|
||||
state.upload_blend_file_status = { info: 'Upload failed' };
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const { clearError, setSelectedModel } = threeDModelCrudSlice.actions;
|
||||
export const threeDModelCrudSelector = (state) => state.threeDModelCrud;
|
||||
export default threeDModelCrudSlice.reducer;
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { cookieService } from '../../services/cookieService';
|
||||
|
||||
const initialState = {
|
||||
user: cookieService.getUserData() || null,
|
||||
token: cookieService.getToken() || null,
|
||||
isAuthenticated: !!cookieService.getToken(),
|
||||
loading: false,
|
||||
error: null,
|
||||
permissions: []
|
||||
};
|
||||
|
||||
const userAuthSlice = createSlice({
|
||||
name: 'userAuth',
|
||||
initialState,
|
||||
reducers: {
|
||||
setCredentials: (state, { payload }) => {
|
||||
const { user, token, permissions } = payload;
|
||||
state.user = user;
|
||||
state.token = token;
|
||||
state.isAuthenticated = true;
|
||||
state.permissions = permissions;
|
||||
cookieService.setToken(token);
|
||||
cookieService.setUserData({ ...user, permissions });
|
||||
},
|
||||
logout: (state) => {
|
||||
state.user = null;
|
||||
state.token = null;
|
||||
state.isAuthenticated = false;
|
||||
state.permissions = [];
|
||||
cookieService.clearAll();
|
||||
},
|
||||
setLoading: (state, { payload }) => {
|
||||
state.loading = payload;
|
||||
},
|
||||
setError: (state, { payload }) => {
|
||||
state.error = payload;
|
||||
},
|
||||
clearError: (state) => {
|
||||
state.error = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const { setCredentials, logout, setLoading, setError, clearError } = userAuthSlice.actions;
|
||||
|
||||
export const selectCurrentUser = (state) => state.userAuth.user;
|
||||
export const selectCurrentToken = (state) => state.userAuth.token;
|
||||
export const selectIsAuthenticated = (state) => state.userAuth.isAuthenticated;
|
||||
export const selectUserPermissions = (state) => state.userAuth.permissions;
|
||||
export const selectAuthLoading = (state) => state.userAuth.loading;
|
||||
export const selectAuthError = (state) => state.userAuth.error;
|
||||
|
||||
export default userAuthSlice.reducer;
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import userAuthReducer from './slices/userAuthSlice';
|
||||
import threeDModelCrudReducer from './slices/threeDModelCrudSlice';
|
||||
import aiModelCrudReducer from './slices/aiModelCrudSlice';
|
||||
import renderCrudReducer from './slices/renderCrudSlice';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
userAuth: userAuthReducer,
|
||||
threeDModelCrud: threeDModelCrudReducer,
|
||||
aiModelCrud: aiModelCrudReducer,
|
||||
renderCrud: renderCrudReducer
|
||||
}
|
||||
});
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// src/services/api.js
|
||||
import axios from 'axios';
|
||||
import { getCookie } from './cookie';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: 'http://0.0.0.0:9090',
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
api.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = getCookie('access_token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default api;
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// src/services/auth.service.js
|
||||
import api from './api';
|
||||
import { setCookie, eraseCookie } from './cookie';
|
||||
|
||||
class AuthService {
|
||||
async login(username, password) {
|
||||
// The FastAPI /auth endpoint expects form data: username, password
|
||||
const formData = new FormData();
|
||||
formData.append('username', username);
|
||||
formData.append('password', password);
|
||||
|
||||
const response = await api.post('/auth', formData);
|
||||
const { access_token } = response.data;
|
||||
// Save to cookie
|
||||
setCookie('access_token', access_token, 1); // 1 day (adjust as needed)
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async register(username, password, email) {
|
||||
const payload = { username, password, email };
|
||||
return api.post('/register/', payload);
|
||||
}
|
||||
|
||||
logout() {
|
||||
// Remove token cookie
|
||||
eraseCookie('access_token');
|
||||
}
|
||||
|
||||
// Optionally, you can provide a check to see if cookie is present
|
||||
isLoggedIn() {
|
||||
return !!document.cookie.includes('access_token=');
|
||||
}
|
||||
}
|
||||
|
||||
export default new AuthService();
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
export function setCookie(name, value, days = 1) {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
|
||||
const expires = 'expires=' + date.toUTCString();
|
||||
document.cookie = `${name}=${value};${expires};path=/;SameSite=Lax`;
|
||||
}
|
||||
|
||||
export function getCookie(name) {
|
||||
const nameEQ = name + '=';
|
||||
const ca = document.cookie.split(';');
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) === ' ') c = c.substring(1);
|
||||
if (c.indexOf(nameEQ) === 0) {
|
||||
return c.substring(nameEQ.length, c.length);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function eraseCookie(name) {
|
||||
document.cookie = name + '=; Max-Age=-99999999;path=/;';
|
||||
}
|
||||
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import Cookies from 'js-cookie';
|
||||
|
||||
const TOKEN_COOKIE_NAME = 'auth_token';
|
||||
const USER_COOKIE_NAME = 'user_data';
|
||||
|
||||
export const cookieService = {
|
||||
setToken: (token) => {
|
||||
Cookies.set(TOKEN_COOKIE_NAME, token, {
|
||||
expires: 7, // 7 dni
|
||||
secure: true, // tylko HTTPS
|
||||
sameSite: 'strict', // ochrona przed CSRF
|
||||
path: '/'
|
||||
});
|
||||
},
|
||||
|
||||
getToken: () => {
|
||||
return Cookies.get(TOKEN_COOKIE_NAME);
|
||||
},
|
||||
|
||||
removeToken: () => {
|
||||
Cookies.remove(TOKEN_COOKIE_NAME, { path: '/' });
|
||||
},
|
||||
|
||||
setUserData: (userData) => {
|
||||
Cookies.set(USER_COOKIE_NAME, JSON.stringify(userData), {
|
||||
expires: 7,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
path: '/'
|
||||
});
|
||||
},
|
||||
|
||||
getUserData: () => {
|
||||
const userData = Cookies.get(USER_COOKIE_NAME);
|
||||
return userData ? JSON.parse(userData) : null;
|
||||
},
|
||||
|
||||
removeUserData: () => {
|
||||
Cookies.remove(USER_COOKIE_NAME, { path: '/' });
|
||||
},
|
||||
|
||||
clearAll: () => {
|
||||
this.removeToken();
|
||||
this.removeUserData();
|
||||
}
|
||||
};
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
// src/services/model.service.js
|
||||
import api from './api';
|
||||
|
||||
/**
|
||||
* Fetch a paginated list of models
|
||||
*/
|
||||
export async function getModels(page = 1, limit = 10) {
|
||||
const res = await api.get('/models', { params: { page, limit } });
|
||||
return res.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new model:
|
||||
* 1) Request a presigned URL from backend
|
||||
* 2) PUT the file to S3
|
||||
* 3) POST the model data with s3Key to backend
|
||||
*/
|
||||
export async function createModel(name, file) {
|
||||
// get presigned URL from your backend
|
||||
const presignRes = await api.post('/models/upload_url', { fileName: file.name });
|
||||
const { url, key } = presignRes.data; // e.g. { url, key }
|
||||
|
||||
// upload file to S3
|
||||
await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': file.type },
|
||||
body: file,
|
||||
});
|
||||
|
||||
// call actual model creation
|
||||
const createRes = await api.post('/models', {
|
||||
name,
|
||||
s3Key: key,
|
||||
});
|
||||
return createRes.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing model (similar approach)
|
||||
*/
|
||||
export async function updateModel(modelId, newName, newFile) {
|
||||
let s3Key;
|
||||
if (newFile) {
|
||||
// 1) get presigned URL
|
||||
const presignRes = await api.post('/models/upload_url', { fileName: newFile.name });
|
||||
const { url, key } = presignRes.data;
|
||||
|
||||
// 2) PUT file to S3
|
||||
await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': newFile.type },
|
||||
body: newFile,
|
||||
});
|
||||
s3Key = key;
|
||||
}
|
||||
|
||||
// 3) call PUT /models/{id} with updated fields
|
||||
const payload = {};
|
||||
if (newName) payload.name = newName;
|
||||
if (s3Key) payload.s3Key = s3Key;
|
||||
|
||||
const updateRes = await api.put(`/models/${modelId}`, payload);
|
||||
return updateRes.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete model
|
||||
*/
|
||||
export async function deleteModel(modelId) {
|
||||
const res = await api.delete(`/models/${modelId}`);
|
||||
return res.data;
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
// src/services/task.service.js
|
||||
import api from './api';
|
||||
|
||||
/**
|
||||
* RENDER tasks
|
||||
*/
|
||||
// Return a list of tasks from the backend
|
||||
export async function getRenderTasks(status, page = 1, limit = 10) {
|
||||
const params = { page, limit };
|
||||
if (status) params.status = status;
|
||||
|
||||
const res = await api.get('/renders/tasks', { params });
|
||||
return res.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a render task (Photo or Video).
|
||||
* The backend expects either `photo: {...}` or `video: {...}` in the request body
|
||||
*/
|
||||
export async function createRenderTaskPhoto(userId, modelId, renderName) {
|
||||
const body = {
|
||||
photo: { USER_ID: userId, MODEL_ID: modelId, RENDER_NAME: renderName },
|
||||
};
|
||||
const res = await api.post('/renders/tasks', body);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function createRenderTaskVideo(userId, modelId, renderName, frameStart, frameEnd) {
|
||||
const body = {
|
||||
video: {
|
||||
USER_ID: userId,
|
||||
MODEL_ID: modelId,
|
||||
RENDER_NAME: renderName,
|
||||
FRAME_START: frameStart,
|
||||
FRAME_END: frameEnd,
|
||||
},
|
||||
};
|
||||
const res = await api.post('/renders/tasks', body);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
// Stop or resume a render task
|
||||
export async function updateRenderTask(taskId, action) {
|
||||
const res = await api.put(`/renders/tasks/${taskId}`, { action });
|
||||
return res.data;
|
||||
}
|
||||
|
||||
// Delete (stop) a render task
|
||||
export async function deleteRenderTask(taskId) {
|
||||
const res = await api.delete(`/renders/tasks/${taskId}`);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* AI tasks
|
||||
*/
|
||||
export async function getAiTasks(status, page = 1, limit = 10) {
|
||||
const params = { page, limit };
|
||||
if (status) params.status = status;
|
||||
|
||||
const res = await api.get('/ai/tasks', { params });
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function createAiTask(userId) {
|
||||
// The backend expects { ai_model: { USER_ID: ... } }
|
||||
const body = {
|
||||
ai_model: { USER_ID: userId },
|
||||
};
|
||||
const res = await api.post('/ai/tasks', body);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function updateAiTask(taskId, action) {
|
||||
const res = await api.put(`/ai/tasks/${taskId}`, { action });
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function deleteAiTask(taskId) {
|
||||
const res = await api.delete(`/ai/tasks/${taskId}`);
|
||||
return res.data;
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
// Define common variables
|
||||
$background-base-start: rgba(0, 90, 25, 1);
|
||||
$background-base-end: rgb(51,54,51, 1);
|
||||
$header-background: 0;
|
||||
$form-background: rgba(0,0,0,0.5);
|
||||
$header-color: $form-background;
|
||||
$button-background: green;
|
||||
$border-color: green;
|
||||
$input-background: #222;
|
||||
$font-family-default: neue-haas-unica, sans-serif;
|
||||
$login-form-font-color: #a6a6a6;
|
||||
|
||||
body, html {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
font-family: $font-family-default;
|
||||
font-weight: 680;
|
||||
tab-size: 4;
|
||||
background: radial-gradient($background-base-start, $background-base-end 70%);
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: center; // Center horizontally
|
||||
align-items: center; // Center vertically
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background: $header-background;
|
||||
color: $header-color;
|
||||
z-index: 3; // Ensure the login form is above the cube
|
||||
position: relative; // Required for z-index to take effect
|
||||
}
|
||||
|
||||
.login-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 0%;
|
||||
border-radius: 5px;
|
||||
|
||||
.main-content {
|
||||
//flex: 1;
|
||||
//display: flex;
|
||||
margin-top: 0%;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
|
||||
.login-form {
|
||||
background: $form-background;
|
||||
padding: 50px;
|
||||
width: 350px;
|
||||
color: $login-form-font-color;
|
||||
// border: 2px solid $border-color;
|
||||
border-radius: 10px;
|
||||
|
||||
margin-top: -400px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
|
||||
z-index: 2; // Ensure the login form is above the cube
|
||||
position: relative; // Required for z-index to take effect
|
||||
|
||||
.form-field {
|
||||
margin-bottom: 15px;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 90%;
|
||||
padding: 5%;
|
||||
background: $input-background;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 5px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 5%;
|
||||
width: 100%;
|
||||
margin-top: 30px;
|
||||
border-radius: 5px;
|
||||
background: $button-background;
|
||||
font-family: $font-family-default;
|
||||
font-weight: 800;
|
||||
color: rgba(0,0,0,0.5);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.graphic-container {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
z-index: 1; // Lower z-index than the login form to place it behind
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,52 +0,0 @@
|
|||
$cube-size: 350px;
|
||||
$cube-color: rgba(0, 140, 0, 0.5);
|
||||
$perspective: 100000px;
|
||||
$animation-duration: 20s;
|
||||
|
||||
@mixin large-cube-face {
|
||||
position: absolute;
|
||||
width: $cube-size - 15px;
|
||||
height: $cube-size - 15px;
|
||||
background: $cube-color;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes rotateLargeCube {
|
||||
0% {
|
||||
transform: rotateX(0) rotateY(0) rotateZ(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.large-graphic-container {
|
||||
width: 100%;
|
||||
margin-top: 0%;
|
||||
//perspective: $perspective;
|
||||
//perspective-origin: 50% 100px; // Adjusted for better 3D effect
|
||||
//z-index: 1; // Lower z-index than the login form to place it behind
|
||||
}
|
||||
|
||||
.large-cube {
|
||||
width: $cube-size;
|
||||
height: $cube-size;
|
||||
//position: relative;
|
||||
margin: auto; // Centers the cube within the graphic container
|
||||
transform-style: preserve-3d;
|
||||
transform-origin: center center; // Rotates around the center of the cube
|
||||
animation: rotateLargeCube $animation-duration infinite linear;
|
||||
will-change: transform;
|
||||
|
||||
div {
|
||||
@include large-cube-face;
|
||||
}
|
||||
|
||||
.front { transform: translateZ($cube-size / 2); }
|
||||
.back { transform: translateZ(-$cube-size / 2) rotateY(180deg); }
|
||||
.right { transform: rotateY(90deg) translateZ($cube-size / 2); }
|
||||
.left { transform: rotateY(-90deg) translateZ($cube-size / 2); }
|
||||
.top { transform: rotateX(90deg) translateZ($cube-size / 2); }
|
||||
.bottom { transform: rotateX(-90deg) translateZ($cube-size / 2); }
|
||||
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
$cube-size: 30px;
|
||||
$cube-color: rgba(0, 140, 0, 0.5);
|
||||
$perspective: 10000px;
|
||||
$animation-duration: 20s;
|
||||
|
||||
@mixin cube-face {
|
||||
position: absolute;
|
||||
width: $cube-size - 4px;
|
||||
height: $cube-size - 4px;
|
||||
background: $cube-color;
|
||||
}
|
||||
|
||||
@keyframes rotateCube {
|
||||
0% {
|
||||
transform: rotateX(0) rotateY(0) rotateZ(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.graphic-container {
|
||||
display: flex;
|
||||
perspective: $perspective;
|
||||
perspective-origin: 50% 100px; // Adjusted for better 3D effect
|
||||
z-index: 1; // Lower z-index than the login form to place it behind
|
||||
|
||||
.cube {
|
||||
width: $cube-size;
|
||||
height: $cube-size;
|
||||
position: relative;
|
||||
margin: auto; // Centers the cube within the graphic container
|
||||
transform-style: preserve-3d;
|
||||
transform-origin: center center; // Rotates around the center of the cube
|
||||
animation: rotateCube $animation-duration infinite linear;
|
||||
|
||||
div {
|
||||
@include cube-face;
|
||||
}
|
||||
|
||||
.face-front { transform: translateZ($cube-size / 2); }
|
||||
.face-back { transform: translateZ(-$cube-size / 2) rotateY(180deg); }
|
||||
.face-right { transform: rotateY(90deg) translateZ($cube-size / 2); }
|
||||
.face-left { transform: rotateY(-90deg) translateZ($cube-size / 2); }
|
||||
.face-top { transform: rotateX(90deg) translateZ($cube-size / 2); }
|
||||
.face-bottom { transform: rotateX(-90deg) translateZ($cube-size / 2); }
|
||||
|
||||
}
|
||||
|
||||
.title {
|
||||
color: white;
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue