Compare commits

...

90 Commits

Author SHA1 Message Date
TBS093A 0c08a4d942 fix(dashboards): remove useless files
-
2025-03-06 14:32:57 +01:00
TBS093A afe0b88808 feat(forms & pages & services): add a lot of stuff
improve login & register (integrate that with redux and api) + improve user settings dashboard
(divide forms to particular files as form components) + improve formGenerator (catching refs and
etc) + more
2025-03-06 14:30:33 +01:00
TBS093A ae781652de feat(dashboard): add search functionality and polish translations
- Add search functionality to all dashboard components (servers, renders, AI tasks)- Implement
real-time filtering by name, type, status and other relevant fields- Update search input styles to
match form-field design- Add clear search button functionality- Polish UI translations for headers,
buttons and placeholders- Ensure consistent styling across all dashboard components
2025-03-05 15:28:16 +01:00
TBS093A e8c234ce51 feat(all): write new functionalities with cursor AI
add all dashboards + add routing + change styles + prepare listGenerator component which works
correctly + add progress bars + add colors for objects statuses + add mocked data + etc
2025-03-04 14:58:26 +01:00
TBS093A 96606b2231 feat(styles & scripts & fixes): add and style new components (menu + listGenerator + etc)
-
2025-02-07 20:53:17 +01:00
TBS093A 45a3415e52 feat(forms & services): prepare by chatgpt changes in application + own adjustments
improve login form & add more simpler axios services + cookie usage
2025-02-06 20:32:58 +01:00
TBS093A fe05313ce2 feat(forms & navbar): upgrade forms generator & add navbar
add universal validation for XSS & SQL Injection issues omit. add Navbar Component
2024-05-09 19:39:29 +02:00
TBS093A 24f22240d1 feat(forms): add password hide / visible button
-
2024-05-07 18:44:11 +02:00
TBS093A 9da564e2e6 feat(improvements): improve landing page & add new components + forms validation
improve landing page, improve register component (add proffesional validation, change styles + etc),
add footer component (with small rotated cube as logo), improve formGenerator
2024-05-05 23:51:55 +02:00
TBS093A b91896e6a5 feat(refactor): combine old useful func to new stuff + other
refactor LoginPage, add LandingPage, add Components for easy re-use, combine scss files / etc.
2024-04-30 00:49:00 +02:00
TBS093A 91da004a7e feat(dockerignore): trying to fix dockerignore
-
2024-04-12 20:00:24 +02:00
TBS093A 9fd42ebbec feat(dockerignore): remove public/ dir from ignored files
-
2024-04-12 19:41:02 +02:00
TBS093A 093fec96bf feat(dockerfile): remove npm update command & add --force flag for solve dependencies issues
-
2024-04-12 19:23:51 +02:00
TBS093A 1a63be25fc feat(dockerfile): remove global flag from npm update
-
2024-04-12 19:20:43 +02:00
TBS093A 07464431af feat(dockerfile): add npm upgrade command in Dockerfile nodejs
-
2024-04-12 16:13:33 +02:00
TBS093A 4076ee4fdd feat(dockerfiles): add Dockerfiles for build app (nodejs) and share builded app (nginx)
-
2024-04-11 12:04:07 +02:00
TBS093A 65eb29de9b feat(login page): improve gradient & change header color
-
2024-04-09 18:56:57 +02:00
TBS093A 44815beaa1 feat(loading page): add gradients
-
2024-04-09 18:47:30 +02:00
TBS093A 81fbd07b12 feat(login page): remove old page / add new
remove old components like redux / react / etc; add new / first component LoginPage
2024-04-09 18:19:47 +02:00
TBS093A a21957003a Fixes 2021-09-24 16:17:45 +02:00
TBS093A 0ac81dc892 All -> huge changes 2021-09-24 11:48:59 +02:00
TBS093A 932fb4d48a DFixes -> fixes and upgrades all functionality 2021-08-22 17:17:10 +02:00
TBS093A c36180ffd0 Docker -> fix all bugs 2021-05-17 22:20:08 +02:00
TBS093A 87c9ec632d Docker -> use old storage statements 2021-05-17 21:32:22 +02:00
TBS093A 1ba3a8117b Docker -> fix conflicts 2021-05-17 21:23:23 +02:00
TBS093A 03f8d7292e Old changes 2021-05-17 21:20:22 +02:00
TBS093A 9e2efb8d06 Docker -> ready to work on remote 2021-05-17 21:18:27 +02:00
TBS093A 37f7f6448e Deploy -> fix conflicts 2021-05-17 12:30:18 +02:00
TBS093A 7f07a8178f Deploy -> fix conflicts 2021-05-17 12:29:48 +02:00
TBS093A dccb479c80 Deploy -> fix conflicts 2021-05-17 12:29:00 +02:00
TBS093A 7ae2ab3753 React -> add select option divs for choice model & simple fixes / upgrades 2021-05-17 12:28:37 +02:00
TBS093A ce838b61f0 React -> add other forms && complete general tasks 2021-05-17 12:28:37 +02:00
TBS093A 28f755a0a2 React -> add all styles && upgrade 2021-05-17 12:28:37 +02:00
TBS093A d5abcd0dbe React -> big upgrades && big fixes 2021-05-17 12:28:37 +02:00
TBS093A 0615309c19 React -> create websocket abstract 2021-05-17 12:28:37 +02:00
TBS093A f0cdf5823b React -> upgrade websocket & fix redux (delete websocket from redux because its crashed as shit) 2021-05-17 12:28:37 +02:00
TBS093A 4088161363 React -> upgrade websockets 2021-05-17 12:28:37 +02:00
TBS093A 28492e2f2a React / Froms / Websocket -> fixes & upgrades 2021-05-17 12:28:37 +02:00
TBS093A 9b066d6d2f React / Forms -> compleate uploader form 2021-05-17 12:28:37 +02:00
TBS093A 96f1d508fb React -> upgrade forms & fixes 2021-05-17 12:28:37 +02:00
TBS093A 36a2598129 React -> upgrade forms & init next views 2021-05-17 12:28:37 +02:00
TBS093A ba4489f0ed React -> upgrade forms / add register & login & logout operations 2021-05-17 12:28:37 +02:00
TBS093A 9e5fe8790b React -> Upgrade forms & simple fixes 2021-05-17 12:28:37 +02:00
TBS093A d7bc10f938 React -> Fix files namespaces & general fix 2021-05-17 12:28:37 +02:00
TBS093A 520471c0f4 Redux / React -> fixes & upgrades 2021-05-17 12:28:37 +02:00
TBS093A f951ead34b React / Redux -> simple fixes & upgrade movements statement 2021-05-17 12:28:37 +02:00
TBS093A e9e3e04060 React -> complete NavigateBar 2021-05-17 12:28:37 +02:00
TBS093A 09f638d3aa Redux / React -> add movements 2021-05-17 12:28:37 +02:00
TBS093A 098270c434 Redux / React -> integrate views & pages 2021-05-17 12:28:37 +02:00
TBS093A 77a6fd801e Redux / React -> implement redux localstorage 2021-05-17 12:28:37 +02:00
TBS093A f5747c51b0 Redux -> add localstorage backup & state loader functionality 2021-05-17 12:28:37 +02:00
TBS093A 08102edbd0 Redux -> simple fixes 2021-05-17 12:28:37 +02:00
TBS093A e1cd04420f Redux -> install node components 2021-05-17 12:28:37 +02:00
TBS093A 42667b9297 Redux -> add async thunk & slicer for websocket usage 2021-05-17 12:28:37 +02:00
TBS093A e660ccce6f Redux -> simple fixes & upgrade slices 2021-05-17 12:28:37 +02:00
TBS093A 39c796eb1a Redux -> simple fixes in async thunks & add model & render crud slicers 2021-05-17 12:28:37 +02:00
TBS093A 7078b4f4f2 Redux -> Fixes 2021-05-17 12:28:37 +02:00
TBS093A cd746fbb6b Redux -> add other endpoint operations in AsyncThunks 2021-05-17 12:28:37 +02:00
TBS093A bea0db148c Redux -> add all Slices / AsyncThunks / storage elements + create code in abstractServica & modelCrudAT 2021-05-17 12:28:37 +02:00
TBS093A 10ba48fd08 Docker -> add docker files 2021-05-17 12:24:47 +02:00
TBS093A 74a68fb6b4 Dockerize a gatsby instance 2021-02-22 23:33:25 +01:00
TBS093A 4a0a6d088c React -> add select option divs for choice model & simple fixes / upgrades 2021-02-22 15:18:13 +01:00
TBS093A c63663b38e React -> add other forms && complete general tasks 2021-02-21 00:23:45 +01:00
TBS093A 24c2ae5c20 React -> add all styles && upgrade 2021-02-20 23:03:00 +01:00
TBS093A 24d5490b96 React -> big upgrades && big fixes 2021-02-19 21:34:41 +01:00
TBS093A fa6999947b React -> create websocket abstract 2021-02-19 10:51:12 +01:00
TBS093A 89f047a39a React -> upgrade websocket & fix redux (delete websocket from redux because its crashed as shit) 2021-02-19 02:54:34 +01:00
TBS093A 0c14883489 React -> upgrade websockets 2021-02-19 00:19:06 +01:00
TBS093A 38def5f503 React / Froms / Websocket -> fixes & upgrades 2021-02-18 23:21:59 +01:00
TBS093A 49b0aa505e React / Forms -> compleate uploader form 2021-02-18 20:38:17 +01:00
TBS093A 0026e41481 React -> upgrade forms & fixes 2021-02-18 19:12:32 +01:00
TBS093A 4e4b5fee3f React -> upgrade forms & init next views 2021-02-18 13:55:08 +01:00
TBS093A 4ee3e84fb4 React -> upgrade forms / add register & login & logout operations 2021-02-17 23:48:24 +01:00
TBS093A b2ecdfb807 React -> Upgrade forms & simple fixes 2021-02-17 23:13:39 +01:00
TBS093A b2565641e5 React -> Fix files namespaces & general fix 2021-02-17 21:07:09 +01:00
TBS093A cb5ea53d42 Redux / React -> fixes & upgrades 2021-02-17 20:05:48 +01:00
TBS093A 7e0f7d16ff React / Redux -> simple fixes & upgrade movements statement 2021-02-17 19:27:57 +01:00
TBS093A c0c164638b React -> complete NavigateBar 2021-02-17 18:45:04 +01:00
TBS093A a75dcf97a9 Redux / React -> add movements 2021-02-17 16:29:15 +01:00
TBS093A 675e8b3452 Redux / React -> integrate views & pages 2021-02-17 15:56:48 +01:00
TBS093A dcd885146a Redux / React -> implement redux localstorage 2021-02-17 15:29:24 +01:00
TBS093A fbc2632490 Redux -> add localstorage backup & state loader functionality 2021-02-17 14:58:14 +01:00
TBS093A 9f90fa0ba2 Redux -> simple fixes 2021-02-17 14:50:33 +01:00
TBS093A 74de7cf0c8 Redux -> install node components 2021-02-17 13:40:11 +01:00
TBS093A 39dad010c6 Redux -> add async thunk & slicer for websocket usage 2021-02-17 13:35:20 +01:00
TBS093A e357719578 Redux -> simple fixes & upgrade slices 2021-02-17 11:53:26 +01:00
TBS093A a8f55c540d Redux -> simple fixes in async thunks & add model & render crud slicers 2021-02-17 10:58:17 +01:00
TBS093A 0131d75f8b Redux -> Fixes 2021-02-17 10:00:09 +01:00
TBS093A d2c3a9d272 Redux -> add other endpoint operations in AsyncThunks 2021-02-17 09:55:24 +01:00
TBS093A 6f3e8f7e4d Redux -> add all Slices / AsyncThunks / storage elements + create code in abstractServica & modelCrudAT 2021-02-17 09:15:23 +01:00
75 changed files with 35808 additions and 12424 deletions

3
.dockerignore 100644
View File

@ -0,0 +1,3 @@
node_modules/
.cache/

1
.env 100644
View File

@ -0,0 +1 @@
REACT_APP_API_URL=http://localhost:8000

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
node_modules/
.cache/
public
public/

11
Dockerfile.nginx 100644
View File

@ -0,0 +1,11 @@
FROM nginx:latest
WORKDIR /
RUN rm /usr/share/nginx/html/*
COPY ./public ./usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -0,0 +1,9 @@
FROM node:18.19.1-alpine
WORKDIR /app
COPY . ./
RUN npm install --silent --force
CMD ["npm", "run", "build"]

13
default.conf 100644
View File

@ -0,0 +1,13 @@
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;
}
}

View File

@ -0,0 +1,9 @@
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './src/redux/store';
export const wrapRootElement = ({ element }) => (
<Provider store={store}>
{element}
</Provider>
);

9
gatsby-ssr.js 100644
View File

@ -0,0 +1,9 @@
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './src/redux/store';
export const wrapRootElement = ({ element }) => (
<Provider store={store}>
{element}
</Provider>
);

38166
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,17 +8,24 @@
"gatsby"
],
"scripts": {
"develop": "gatsby develop",
"start": "gatsby develop",
"develop": "gatsby develop --host=0.0.0.0",
"start": "gatsby develop --host=0.0.0.0",
"build": "gatsby build",
"serve": "gatsby serve",
"clean": "gatsby clean"
},
"dependencies": {
"gatsby": "^2.32.3",
"gatsby-plugin-sass": "^3.2.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"@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",
"sass": "^1.32.7"
}
}

59
src/App.js 100644
View File

@ -0,0 +1,59 @@
// 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;

View File

@ -0,0 +1,91 @@
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>
&copy; {new Date().getFullYear()} FehuDev. All rights reserved.
</p>
</div>
</footer>
)
}
export default FootComponent

View File

@ -0,0 +1,68 @@
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

View File

@ -0,0 +1,69 @@
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

View File

@ -0,0 +1,265 @@
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

View File

@ -0,0 +1,71 @@
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

View File

@ -0,0 +1,674 @@
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

View File

@ -0,0 +1,308 @@
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"
>
&lt;&lt;
</button>
<button
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
className="pagination-button"
>
&lt;
</button>
<span className="page-info">
Page {currentPage} of {totalPages}
</span>
<button
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
className="pagination-button"
>
&gt;
</button>
<button
onClick={() => handlePageChange(totalPages)}
disabled={currentPage === totalPages}
className="pagination-button"
>
&gt;&gt;
</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>
);
};

View File

@ -0,0 +1,65 @@
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

View File

@ -0,0 +1,106 @@
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

View File

@ -0,0 +1,136 @@
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

View File

@ -0,0 +1,134 @@
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

View File

@ -0,0 +1,445 @@
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

View File

@ -0,0 +1,444 @@
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

View File

@ -0,0 +1,106 @@
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;

View File

@ -0,0 +1,161 @@
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;

View File

@ -0,0 +1,77 @@
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

View File

@ -0,0 +1,87 @@
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

View File

@ -0,0 +1,137 @@
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;

View File

@ -0,0 +1,81 @@
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;

View File

@ -0,0 +1,84 @@
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;

View File

@ -0,0 +1,22 @@
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

View File

@ -0,0 +1,28 @@
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

View File

@ -0,0 +1,25 @@
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

20
src/config.js 100644
View File

@ -0,0 +1,20 @@
// 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: '/'
}
};

BIN
src/images/logo.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

27
src/index.js 100644
View File

@ -0,0 +1,27 @@
// 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>
);

View File

@ -1,54 +1,20 @@
import * as React from "react"
import { Link } from "gatsby"
import React from 'react';
import { Link } from 'react-router-dom';
import '../styles/general.scss';
// 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 (
<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 couldnt 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>
)
}
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>
);
};
export default NotFoundPage
export default NotFoundPage;

View File

@ -0,0 +1,70 @@
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}&nbsp;
<button onClick={() => handleStop(t._id)}>Stop</button>
<button onClick={() => handleDelete(t._id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
export default FormAi;

View File

@ -0,0 +1,52 @@
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;

View File

@ -0,0 +1,79 @@
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;

View File

@ -0,0 +1,63 @@
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;

View File

@ -0,0 +1,47 @@
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}) &nbsp;
{r.s3Key && (
<button onClick={() => handleDownload(r.s3Key)}>Download ZIP</button>
)}
</div>
))}
</div>
);
}
export default FormRenders;

View File

@ -0,0 +1,32 @@
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

View File

@ -0,0 +1,32 @@
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

View File

@ -0,0 +1,142 @@
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;

View File

@ -0,0 +1,408 @@
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;

View File

@ -0,0 +1,385 @@
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;

View File

@ -0,0 +1,482 @@
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;

View File

@ -0,0 +1,570 @@
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;

View File

@ -0,0 +1,413 @@
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;

View File

@ -0,0 +1,42 @@
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;

View File

@ -1,150 +1,11 @@
import * as React from "react"
import 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 (
<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>
)
}
return (
<div>
{/* Reszta komponentów aplikacji */}
</div>
);
};
export default IndexPage
export default IndexPage;

View File

@ -0,0 +1,92 @@
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

View File

@ -0,0 +1,100 @@
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;
}
);

View File

@ -0,0 +1,94 @@
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;
}
);

View File

@ -0,0 +1,72 @@
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;
}
);

View File

@ -0,0 +1,179 @@
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);
}
}
);

View File

@ -0,0 +1,170 @@
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;

View File

@ -0,0 +1,155 @@
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;

View File

@ -0,0 +1,124 @@
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;

View File

@ -0,0 +1,54 @@
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;

14
src/redux/store.js 100644
View File

@ -0,0 +1,14 @@
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
}
});

View File

@ -0,0 +1,23 @@
// 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;

View File

@ -0,0 +1,35 @@
// 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();

View File

@ -0,0 +1,24 @@
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=/;';
}

View File

@ -0,0 +1,46 @@
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();
}
};

View File

@ -0,0 +1,72 @@
// 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;
}

View File

@ -0,0 +1,82 @@
// 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;
}

View File

@ -0,0 +1,104 @@
// 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

View File

@ -0,0 +1,52 @@
$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); }
}

View File

@ -0,0 +1,56 @@
$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;
}
}