feat(forms & services): prepare by chatgpt changes in application + own adjustments
improve login form & add more simpler axios services + cookie usagefeat/x_gpu/chat_gpt_new_version
parent
fe05313ce2
commit
45a3415e52
File diff suppressed because it is too large
Load Diff
|
|
@ -15,11 +15,13 @@
|
|||
"clean": "gatsby clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"gatsby": "^5.13.3",
|
||||
"gatsby-plugin-sass": "^6.13.1",
|
||||
"gatsby-plugin-sass": "^3.2.0",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-particles-js": "^3.4.1",
|
||||
"react-router-dom": "^7.1.5",
|
||||
"react-tsparticles": "^2.12.2",
|
||||
"sass": "^1.32.7"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
// src/App.js
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
|
||||
|
||||
import LoginPage from './pages/user/login';
|
||||
|
||||
import FormLogin from './pages/FormLogin';
|
||||
import FormRegister from './pages/FormRegister';
|
||||
import LandingPage from './pages/Landing';
|
||||
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="/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={<FormLogin />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/register" element={<FormRegister />} />
|
||||
<Route path="/landing" element={<LandingPage />} />
|
||||
<Route path="/models" element={<FormModels />} />
|
||||
<Route path="/renders" element={<FormRenders />} />
|
||||
<Route path="/ai-tasks" element={<FormAi />} />
|
||||
<Route path="*" element={<div>Not Found</div>} />
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
|
@ -13,6 +13,52 @@ const UserLoginForm = () => {
|
|||
const usernameInput = React.createRef()
|
||||
const passwordInput = React.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,}$/
|
||||
|
||||
const [usernameValidationInfo, setUsernameValidationInfo] = useState("Empty")
|
||||
const [passwordValidationInfo, setPasswordValidationInfo] = useState("Empty")
|
||||
|
||||
const [password, setPassword] = useState("")
|
||||
|
||||
const [allowButtonAction, setAllowButtonAction] = useState(false)
|
||||
|
||||
const usernameValidation = (event) => {
|
||||
|
||||
if (event.target.value === "") {
|
||||
setUsernameValidationInfo("Email is required.")
|
||||
} else if(!emailRegex.test(event.target.value)) {
|
||||
setUsernameValidationInfo("Please provide correct email")
|
||||
} else {
|
||||
setUsernameValidationInfo("Success")
|
||||
}
|
||||
}
|
||||
|
||||
const passwordValidation = (event) => {
|
||||
|
||||
setPassword(event.target.value)
|
||||
|
||||
if (event.target.value === "") {
|
||||
setPasswordValidationInfo("Password is required.")
|
||||
} else if(!passwordRegex.test(event.target.value)) {
|
||||
setPasswordValidationInfo("Password require:\n - At least 8 characters,\n - At least one uppercase letter,\n - At least one lowercase letter,\n - At least one digit,\n - At least one special character.")
|
||||
} else {
|
||||
setPasswordValidationInfo("Success")
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setAllowButtonAction(
|
||||
usernameValidationInfo === "Success"
|
||||
&& passwordValidationInfo === "Success"
|
||||
)
|
||||
}, [
|
||||
allowButtonAction,
|
||||
usernameValidationInfo,
|
||||
passwordValidationInfo
|
||||
]
|
||||
)
|
||||
|
||||
// const dispatch = useDispatch()
|
||||
// const { info } = useSelector( userAuthSelector )
|
||||
const info = "" // if redux is integrated - delete this line
|
||||
|
|
@ -27,17 +73,21 @@ const UserLoginForm = () => {
|
|||
type: 'info',
|
||||
action: 'Create',
|
||||
endpint: 'user/auth',
|
||||
button_value: 'CONTINUE'
|
||||
button_value: 'SIGN IN'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'EMAIL',
|
||||
ref: usernameInput
|
||||
ref: usernameInput,
|
||||
onChange: usernameValidation,
|
||||
validationInfo: usernameValidationInfo
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'PASSWORD',
|
||||
ref: passwordInput
|
||||
ref: passwordInput,
|
||||
onChange: passwordValidation,
|
||||
validationInfo: passwordValidationInfo
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import '../styles/large.cube.scss';
|
|||
|
||||
const LargeCubeComponent = () => {
|
||||
return (
|
||||
<div className="graphic-container">
|
||||
<div className="cube">
|
||||
<div className="large-graphic-container">
|
||||
<div className="large-cube">
|
||||
<div className="front"></div>
|
||||
<div className="back"></div>
|
||||
<div className="right"></div>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
||||
|
|
@ -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}
|
||||
<button onClick={() => handleStop(t._id)}>Stop</button>
|
||||
<button onClick={() => handleDelete(t._id)}>Delete</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormAi;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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})
|
||||
{r.s3Key && (
|
||||
<button onClick={() => handleDownload(r.s3Key)}>Download ZIP</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormRenders;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import LoginPage from './user/login.js';
|
||||
import LandingPage from './landing.js';
|
||||
|
||||
const IndexPage = () => {
|
||||
return (
|
||||
<LandingPage />
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default IndexPage
|
||||
|
|
@ -4,19 +4,27 @@ 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 (
|
||||
<div className="login-container">
|
||||
<header className="header">
|
||||
<h1>XGPU</h1>
|
||||
</header>
|
||||
<>
|
||||
<NavBarComponent />
|
||||
<div className="login-container" style={{"display": "flex"}}>
|
||||
<main className="main-content">
|
||||
<LargeCubeComponent />
|
||||
<UserLoginForm />
|
||||
<div style={{"padding-left": "200px"}}>
|
||||
<UserLoginForm />
|
||||
</div>
|
||||
</main>
|
||||
<main className="main-content">
|
||||
<div style={{"padding-right": "200px"}}>
|
||||
<LargeCubeComponent />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<FootComponent />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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();
|
||||
|
|
@ -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=/;';
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -130,7 +130,9 @@ body, html {
|
|||
}
|
||||
|
||||
.main-content {
|
||||
margin-top: 0%;
|
||||
padding-top: 15%;
|
||||
padding-bottom: 12%;
|
||||
width: 50%;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
|
@ -153,7 +155,6 @@ body, html {
|
|||
// border: 2px solid $border-color;
|
||||
border-radius: 10px;
|
||||
|
||||
margin-top: -400px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
$cube-size: 400px;
|
||||
$cube-size: 350px;
|
||||
$cube-color: rgba(0, 140, 0, 0.5);
|
||||
$perspective: 10000px;
|
||||
$perspective: 100000px;
|
||||
$animation-duration: 20s;
|
||||
|
||||
@mixin cube-face {
|
||||
@mixin large-cube-face {
|
||||
position: absolute;
|
||||
width: $cube-size - 15px;
|
||||
height: $cube-size - 15px;
|
||||
|
|
@ -11,7 +11,7 @@ $animation-duration: 20s;
|
|||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes rotateCube {
|
||||
@keyframes rotateLargeCube {
|
||||
0% {
|
||||
transform: rotateX(0) rotateY(0) rotateZ(0);
|
||||
}
|
||||
|
|
@ -20,24 +20,26 @@ $animation-duration: 20s;
|
|||
}
|
||||
}
|
||||
|
||||
.graphic-container {
|
||||
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-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
|
||||
}
|
||||
|
||||
.cube {
|
||||
.large-cube {
|
||||
width: $cube-size;
|
||||
height: $cube-size;
|
||||
position: relative;
|
||||
//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;
|
||||
animation: rotateLargeCube $animation-duration infinite linear;
|
||||
will-change: transform;
|
||||
|
||||
div {
|
||||
@include cube-face;
|
||||
@include large-cube-face;
|
||||
}
|
||||
|
||||
.front { transform: translateZ($cube-size / 2); }
|
||||
|
|
|
|||
Loading…
Reference in New Issue