-
- {createVisible && (
-
- {renderComponent(create_component)}
-
+
+ {title &&
{title}
}
+
+ {onRefresh && (
+
)}
+ {onCreate && (
+
+ )}
+
+
+
+ {createVisible && onCreate && (
+
+ {renderComponent(onCreate)}
)}
- {
- columns.map(
- (column) => (
-
-
- {column.toUpperCase()}
-
-
- )
- )
- }
+ {columns.map((column) => (
+
+ {column.toUpperCase()}
+
+ ))}
+ {(onUpdate || onDelete) && (
+
Actions
+ )}
-
- {data.map((item) => (
-
-
-
- {
- Object.values(item).map((value) => (
-
- {value}
-
- )
- )
- }
-
- {/* UPDATE BUTTON & FORM */}
- {update_component && (
- <>
-
- {itemBeingUpdated === item.id && (
-
- {renderComponent(update_component, { item })}
-
- )}
- >
- )}
-
- {/* DELETE BUTTON */}
- {delete_action && (
-
- )}
+ {data.length === 0 ? (
+
+ No items found. {onCreate && `Click '+ ${title || 'Item'}' to add new items.`}
- ))}
+ ) : (
+ data.map((item) => (
+
handleItemClick(item)}
+ >
+ {Object.entries(item)
+ .filter(([key]) => !key.toLowerCase().includes('id'))
+ .map(([key, value], index) => (
+
+ {key.toLowerCase() === 'progress' ? (
+ value === 100 ? (
+
Completed
+ ) : (
+
+ )
+ ) : key.toLowerCase() === 'status' ? (
+
+ {value}
+
+ ) : (
+
{value}
+ )}
+
+ ))}
+ {(onUpdate || onDelete) && (
+
+ {onUpdate && (
+
+ )}
+ {onDelete && (
+
+ )}
+
+ )}
+ {itemBeingUpdated === item.id && onUpdate && (
+
+ {renderComponent(onUpdate, { item })}
+
+ )}
+
+ ))
+ )}
+
+ {selectedItem ? (
+
+
Details
+ {Object.entries(selectedItem)
+ .filter(([key]) => !key.toLowerCase().includes('id'))
+ .map(([key, value]) => (
+
+ {key}:
+
+ {key.toLowerCase() === 'progress' ? (
+ value === 100 ? (
+ Completed
+ ) : (
+ {value}%
+ )
+ ) : (
+ value
+ )}
+
+
+ ))}
+
+ ) : (
+
+ Select an item to view details
+
+ )}
+
);
};
diff --git a/src/pages/404.js b/src/pages/404.js
new file mode 100644
index 0000000..9c03863
--- /dev/null
+++ b/src/pages/404.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import '../styles/general.scss';
+
+const NotFoundPage = () => {
+ return (
+
+
+
404
+
Strona nie została znaleziona
+
Przepraszamy, ale strona, której szukasz, nie istnieje.
+
+
+
+
+
+ );
+};
+
+export default NotFoundPage;
\ No newline at end of file
diff --git a/src/pages/dashboard.js b/src/pages/dashboard.js
index 117d6c5..6fb5665 100644
--- a/src/pages/dashboard.js
+++ b/src/pages/dashboard.js
@@ -1,67 +1,124 @@
-import React from 'react';
-
+import React, { useState } from 'react';
import '../styles/general.scss';
import '@fortawesome/fontawesome-free/css/all.min.css';
import FootComponent from '../components/foot.js';
import NavBarComponent from '../components/navbar.js';
-import ModelsDashboard from './dashboards/models.js';
+import ThreeDModelsDashboard from './dashboards/3d-models.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 UserSettings from './dashboards/user.js';
const DashboardPage = () => {
+ const icons_size = "fa-2x";
+ const [activeComponent, setActiveComponent] = useState('3d-models');
- const icons_size = "fa-2x"
+ const handleNavigation = (path) => {
+ setActiveComponent(path);
+ };
+
+ const isActive = (path) => {
+ return activeComponent === path;
+ };
+
+ const handleLogout = () => {
+ // TODO: Implement proper logout logic (clear tokens, session, etc.)
+ console.log('Logging out...');
+ window.location.href = '/login';
+ };
+
+ const renderContent = () => {
+ switch (activeComponent) {
+ case 'ai-models':
+ return
;
+ case 'ai-tasks':
+ return
;
+ case 'renders':
+ return
;
+ case 'servers':
+ return
;
+ case 'settings':
+ return
;
+ case '3d-models':
+ default:
+ return
;
+ }
+ };
return (
- <>
-
-
-
-
-
-
- Servers
-
- -
- {/* fa-microchip */}
-
Dashboard
-
-
- Rendering
-
- -
-
-
3D Models
-
- -
-
-
Rendered Materials
-
-
- AI Training
-
- -
-
-
AI Models
-
-
- User
-
- -
-
-
Settings
-
- -
-
-
Log Out
-
-
-
-
-
- >
- )
-}
+ <>
+
+
+
+ {renderContent()}
+
+
+ Servers
+
+ - handleNavigation('servers')}
+ >
+
+
Dashboard
+
+
+ Rendering
+
+ - handleNavigation('3d-models')}
+ >
+
+
3D Models
+
+ - handleNavigation('renders')}
+ >
+
+
Rendered Materials
+
+
+ AI Training
+
+ - handleNavigation('ai-models')}
+ >
+
+
AI Models
+
+ - handleNavigation('ai-tasks')}
+ >
+
+
AI Training Tasks
+
+
+ User
+
+ - handleNavigation('settings')}
+ >
+
+
Settings
+
+ -
+
+
Log Out
+
+
+
+
+
+ >
+ );
+};
-
-export default DashboardPage
\ No newline at end of file
+export default DashboardPage;
\ No newline at end of file
diff --git a/src/pages/dashboards/3d-models.js b/src/pages/dashboards/3d-models.js
new file mode 100644
index 0000000..b6d5e57
--- /dev/null
+++ b/src/pages/dashboards/3d-models.js
@@ -0,0 +1,226 @@
+import React, { useState } from 'react';
+import { ListGenerator } from '../../components/forms/listGenerator';
+import { FormGenerator } from '../../components/forms/formGenerator';
+
+const ThreeDModelsDashboard = () => {
+ const [selectedModel, setSelectedModel] = useState(null);
+ const [models, setModels] = useState([
+ {
+ id: 1,
+ name: 'Model A',
+ type: '3D',
+ status: 'Active',
+ lastModified: '2024-03-20',
+ size: '2.5MB'
+ },
+ {
+ id: 2,
+ name: 'Model B',
+ type: '3D',
+ status: 'Inactive',
+ lastModified: '2024-03-19',
+ size: '1.8MB'
+ }
+ ]);
+ const [isFormVisible, setIsFormVisible] = useState(false);
+ const [formMode, setFormMode] = useState('create'); // 'create' lub 'edit'
+ const [message, setMessage] = useState({ type: '', text: '' });
+
+ 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: '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: '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: '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 formFields = [
+ {
+ name: 'name',
+ label: 'Model Name',
+ type: 'text',
+ required: true,
+ value: selectedModel?.name || ''
+ },
+ {
+ name: 'type',
+ label: 'Model Type',
+ type: 'select',
+ required: true,
+ options: [
+ { value: '3D', label: '3D' },
+ { value: '2D', label: '2D' }
+ ],
+ value: selectedModel?.type || '3D'
+ },
+ {
+ name: 'status',
+ label: 'Status',
+ type: 'select',
+ required: true,
+ options: [
+ { value: 'Active', label: 'Active' },
+ { value: 'Inactive', label: 'Inactive' }
+ ],
+ value: selectedModel?.status || 'Active'
+ }
+ ];
+
+ return (
+
+
+
3D Models
+
+
+
+ {message.text && (
+
+ {message.text}
+
+ )}
+
+ {isFormVisible && (
+
+ )}
+
+
(
+
+
+
{model.name}
+
{model.type}
+
+ {model.status}
+
+
+
+
Last Modified: {model.lastModified}
+
Size: {model.size}
+
+
+ )}
+ renderDetails={(model) => (
+
+
Model Details
+
+ ID:
+ {model.id}
+
+
+ Nazwa:
+ {model.name}
+
+
+ Typ:
+ {model.type}
+
+
+ Status:
+ {model.status}
+
+
+ Last Modified:
+ {model.lastModified}
+
+
+ Size:
+ {model.size}
+
+
+ )}
+ />
+
+ );
+};
+
+export default ThreeDModelsDashboard;
\ No newline at end of file
diff --git a/src/pages/dashboards/ai.models.js b/src/pages/dashboards/ai.models.js
index e69de29..d16caa6 100644
--- a/src/pages/dashboards/ai.models.js
+++ b/src/pages/dashboards/ai.models.js
@@ -0,0 +1,234 @@
+import React, { useState } from 'react';
+import { ListGenerator } from '../../components/forms/listGenerator';
+import FormGenerator from '../../components/forms/formGenerator';
+
+const AIModelsDashboard = () => {
+ const [selectedModel, setSelectedModel] = useState(null);
+ 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'
+ }
+ ]);
+ const [isFormVisible, setIsFormVisible] = useState(false);
+ const [formMode, setFormMode] = useState('create');
+ const [message, setMessage] = useState({ type: '', text: '' });
+
+ 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 formFields = [
+ {
+ name: 'name',
+ label: 'Model Name',
+ type: 'text',
+ required: true,
+ value: selectedModel?.name || ''
+ },
+ {
+ name: 'type',
+ label: 'Model Type',
+ type: 'select',
+ required: true,
+ options: [
+ { value: 'text-to-image', label: 'Text to Image' },
+ { value: 'image-to-text', label: 'Image to Text' }
+ ],
+ value: selectedModel?.type || 'text-to-image'
+ },
+ {
+ name: 'version',
+ label: 'Version',
+ type: 'text',
+ required: true,
+ value: selectedModel?.version || '1.0'
+ },
+ {
+ name: 'status',
+ label: 'Status',
+ type: 'select',
+ required: true,
+ options: [
+ { value: 'Active', label: 'Active' },
+ { value: 'Inactive', label: 'Inactive' }
+ ],
+ value: selectedModel?.status || 'Active'
+ }
+ ];
+
+ return (
+
+
+
AI Models
+
+
+
+ {message.text && (
+
+ {message.text}
+
+ )}
+
+ {isFormVisible && (
+
+ )}
+
+
(
+
+
+
{model.name}
+
{model.type}
+
+ {model.status}
+
+
+
+
Version: {model.version}
+
Last Modified: {model.lastModified}
+
+
+ )}
+ renderDetails={(model) => (
+
+
AI Model Details
+
+ ID:
+ {model.id}
+
+
+ Nazwa:
+ {model.name}
+
+
+ Typ:
+ {model.type}
+
+
+ Wersja:
+ {model.version}
+
+
+ Status:
+ {model.status}
+
+
+ Ostatnia modyfikacja:
+ {model.lastModified}
+
+
+ )}
+ />
+
+ );
+};
+
+export default AIModelsDashboard;
+
diff --git a/src/pages/dashboards/ai.tasks.js b/src/pages/dashboards/ai.tasks.js
new file mode 100644
index 0000000..3f0fb88
--- /dev/null
+++ b/src/pages/dashboards/ai.tasks.js
@@ -0,0 +1,280 @@
+import React, { useState } from 'react';
+import { ListGenerator } from '../../components/forms/listGenerator';
+import { FormGenerator } from '../../components/forms/formGenerator';
+
+const AITasksDashboard = () => {
+ const [selectedTask, setSelectedTask] = useState(null);
+ const [tasks, setTasks] = useState([
+ {
+ id: 1,
+ name: 'Task A',
+ type: 'Training',
+ status: 'In Progress',
+ lastModified: '2024-03-20',
+ progress: 45
+ },
+ {
+ id: 2,
+ name: 'Task B',
+ type: 'Inference',
+ status: 'Completed',
+ lastModified: '2024-03-19',
+ progress: 100
+ }
+ ]);
+ const [isFormVisible, setIsFormVisible] = useState(false);
+ const [formMode, setFormMode] = useState('create');
+ const [message, setMessage] = useState({ type: '', text: '' });
+
+ 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 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 formFields = [
+ {
+ name: 'name',
+ label: 'Task Name',
+ type: 'text',
+ required: true,
+ value: selectedTask?.name || ''
+ },
+ {
+ name: 'type',
+ label: 'Task Type',
+ type: 'select',
+ required: true,
+ options: [
+ { value: 'Training', label: 'Training' },
+ { value: 'Inference', label: 'Inference' },
+ { value: 'Evaluation', label: 'Evaluation' }
+ ],
+ value: selectedTask?.type || 'Training'
+ },
+ {
+ name: 'status',
+ label: 'Status',
+ type: 'select',
+ required: true,
+ options: [
+ { value: 'In Progress', label: 'In Progress' },
+ { value: 'Completed', label: 'Completed' },
+ { value: 'Failed', label: 'Failed' },
+ { value: 'Cancelled', label: 'Cancelled' }
+ ],
+ value: selectedTask?.status || 'In Progress'
+ }
+ ];
+
+ return (
+
+
+
AI Tasks
+
+
+
+ {message.text && (
+
+ {message.text}
+
+ )}
+
+ {isFormVisible && (
+
+ )}
+
+
(
+
+
+
{task.name}
+
{task.type}
+
+ {task.status}
+
+
+
+
+
Last Modified: {task.lastModified}
+
+
+ )}
+ renderDetails={(task) => (
+
+
Task Details
+
+ ID:
+ {task.id}
+
+
+ Name:
+ {task.name}
+
+
+ Type:
+ {task.type}
+
+
+ Status:
+ {task.status}
+
+
+ Progress:
+ {task.progress}%
+
+
+ Last Modified:
+ {task.lastModified}
+
+
+ )}
+ />
+
+ );
+};
+
+export default AITasksDashboard;
\ No newline at end of file
diff --git a/src/pages/dashboards/models.js b/src/pages/dashboards/models.js
deleted file mode 100644
index 24a9ffe..0000000
--- a/src/pages/dashboards/models.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React, { useState } from 'react';
-
-import { ListGenerator } from '../../components/forms/listGenerator';
-
-const ModelsDashboard = () => {
-
- const [items, setItems] = useState([
- { id: 0, name: 'orc.blend' },
- { id: 1, name: 'palladin.blend' },
- ]);
-
- return (
- <>
-
- >
- )
-}
-
-export default ModelsDashboard
\ No newline at end of file
diff --git a/src/pages/dashboards/renders.js b/src/pages/dashboards/renders.js
index e69de29..4677f23 100644
--- a/src/pages/dashboards/renders.js
+++ b/src/pages/dashboards/renders.js
@@ -0,0 +1,265 @@
+import React, { useState } 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: 'Render A',
+ type: '3D',
+ status: 'Completed',
+ lastModified: '2024-03-20',
+ resolution: '1920x1080',
+ progress: 100
+ },
+ {
+ id: 2,
+ name: 'Render B',
+ type: '2D',
+ status: 'In Progress',
+ lastModified: '2024-03-19',
+ resolution: '3840x2160',
+ progress: 45
+ }
+ ]);
+ const [isFormVisible, setIsFormVisible] = useState(false);
+ const [formMode, setFormMode] = useState('create');
+ const [message, setMessage] = useState({ type: '', text: '' });
+
+ 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 formFields = [
+ {
+ name: 'name',
+ label: 'Nazwa renderu',
+ type: 'text',
+ required: true,
+ value: selectedRender?.name || ''
+ },
+ {
+ name: 'type',
+ label: 'Typ renderu',
+ type: 'select',
+ required: true,
+ options: [
+ { value: '3D', label: '3D' },
+ { value: '2D', label: '2D' }
+ ],
+ value: selectedRender?.type || '3D'
+ },
+ {
+ name: 'resolution',
+ label: 'Rozdzielczość',
+ type: 'select',
+ required: true,
+ options: [
+ { value: '1920x1080', label: 'Full HD (1920x1080)' },
+ { value: '3840x2160', label: '4K (3840x2160)' },
+ { value: '7680x4320', label: '8K (7680x4320)' }
+ ],
+ value: selectedRender?.resolution || '1920x1080'
+ },
+ {
+ name: 'status',
+ label: 'Status',
+ type: 'select',
+ required: true,
+ options: [
+ { value: 'In Progress', label: 'W trakcie' },
+ { value: 'Completed', label: 'Zakończony' },
+ { value: 'Failed', label: 'Nieudany' }
+ ],
+ value: selectedRender?.status || 'In Progress'
+ },
+ {
+ name: 'progress',
+ label: 'Postęp',
+ type: 'number',
+ required: true,
+ min: 0,
+ max: 100,
+ value: selectedRender?.progress || 0
+ }
+ ];
+
+ return (
+
+
+
Rendered Materials
+
+
+
+ {message.text && (
+
+ {message.text}
+
+ )}
+
+ {isFormVisible && (
+
+ )}
+
+
(
+
+
+
{render.name}
+
{render.type}
+
+ {render.status}
+
+
+
+
+
Rozdzielczość: {render.resolution}
+
Ostatnia modyfikacja: {render.lastModified}
+
+
+ )}
+ renderDetails={(render) => (
+
+
Szczegóły renderu
+
+ ID:
+ {render.id}
+
+
+ Nazwa:
+ {render.name}
+
+
+ Typ:
+ {render.type}
+
+
+ Rozdzielczość:
+ {render.resolution}
+
+
+ Status:
+ {render.status}
+
+
+ Postęp:
+ {render.progress}%
+
+
+ Ostatnia modyfikacja:
+ {render.lastModified}
+
+
+ )}
+ />
+
+ );
+};
+
+export default RendersDashboard;
+
diff --git a/src/pages/dashboards/servers.js b/src/pages/dashboards/servers.js
index e69de29..e182c3e 100644
--- a/src/pages/dashboards/servers.js
+++ b/src/pages/dashboards/servers.js
@@ -0,0 +1,236 @@
+import React, { useState } from 'react';
+import { ListGenerator } from '../../components/forms/listGenerator';
+import FormGenerator from '../../components/forms/formGenerator';
+
+const ServersDashboard = () => {
+ const [selectedServer, setSelectedServer] = useState(null);
+ const [servers, setServers] = useState([
+ {
+ id: 1,
+ name: 'Server A',
+ type: 'Render',
+ status: 'Online',
+ lastModified: '2024-03-20',
+ ip: '192.168.1.100'
+ },
+ {
+ id: 2,
+ name: 'Server B',
+ type: 'AI',
+ status: 'Offline',
+ lastModified: '2024-03-19',
+ ip: '192.168.1.101'
+ }
+ ]);
+ const [isFormVisible, setIsFormVisible] = useState(false);
+ const [formMode, setFormMode] = useState('create');
+ const [message, setMessage] = useState({ type: '', text: '' });
+
+ 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 formFields = [
+ {
+ name: 'name',
+ label: 'Nazwa serwera',
+ type: 'text',
+ required: true,
+ value: selectedServer?.name || ''
+ },
+ {
+ name: 'type',
+ label: 'Typ serwera',
+ type: 'select',
+ required: true,
+ options: [
+ { value: 'Render', label: 'Render' },
+ { value: 'AI', label: 'AI' },
+ { value: 'Storage', label: 'Storage' }
+ ],
+ value: selectedServer?.type || 'Render'
+ },
+ {
+ name: 'ip',
+ label: 'Adres IP',
+ type: 'text',
+ required: true,
+ value: selectedServer?.ip || ''
+ },
+ {
+ name: 'status',
+ label: 'Status',
+ type: 'select',
+ required: true,
+ options: [
+ { value: 'Online', label: 'Online' },
+ { value: 'Offline', label: 'Offline' },
+ { value: 'Maintenance', label: 'Maintenance' }
+ ],
+ value: selectedServer?.status || 'Offline'
+ }
+ ];
+
+ return (
+
+
+
Servers
+
+
+
+ {message.text && (
+
+ {message.text}
+
+ )}
+
+ {isFormVisible && (
+
+ )}
+
+
(
+
+
+
{server.name}
+
{server.type}
+
+ {server.status}
+
+
+
+
IP: {server.ip}
+
Ostatnia modyfikacja: {server.lastModified}
+
+
+ )}
+ renderDetails={(server) => (
+
+
Szczegóły serwera
+
+ ID:
+ {server.id}
+
+
+ Nazwa:
+ {server.name}
+
+
+ Typ:
+ {server.type}
+
+
+ IP:
+ {server.ip}
+
+
+ Status:
+ {server.status}
+
+
+ Ostatnia modyfikacja:
+ {server.lastModified}
+
+
+ )}
+ />
+
+ );
+};
+
+export default ServersDashboard;
+
diff --git a/src/pages/dashboards/user.js b/src/pages/dashboards/user.js
index e69de29..ba07e78 100644
--- a/src/pages/dashboards/user.js
+++ b/src/pages/dashboards/user.js
@@ -0,0 +1,176 @@
+import React, { useState, useRef } from 'react';
+import FormGenerator from '../../components/forms/formGenerator';
+
+const UserSettings = () => {
+ const [isEditing, setIsEditing] = useState(false);
+ const [message, setMessage] = useState({ type: '', text: '' });
+
+ const usernameInput = useRef();
+ const emailInput = useRef();
+ const currentPasswordInput = useRef();
+ const newPasswordInput = useRef();
+ const confirmPasswordInput = useRef();
+
+ const [usernameValidationInfo, setUsernameValidationInfo] = useState("Empty");
+ const [emailValidationInfo, setEmailValidationInfo] = useState("Empty");
+ const [currentPasswordValidationInfo, setCurrentPasswordValidationInfo] = useState("Empty");
+ const [newPasswordValidationInfo, setNewPasswordValidationInfo] = useState("Empty");
+ const [confirmPasswordValidationInfo, setConfirmPasswordValidationInfo] = useState("Empty");
+
+ 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 usernameValidation = (event) => {
+ if (event.target.value === "") {
+ setUsernameValidationInfo("Username is required.");
+ } else {
+ setUsernameValidationInfo("Success");
+ }
+ };
+
+ const emailValidation = (event) => {
+ if (event.target.value === "") {
+ setEmailValidationInfo("Email is required.");
+ } else if (!emailRegex.test(event.target.value)) {
+ setEmailValidationInfo("Please provide correct email");
+ } else {
+ setEmailValidationInfo("Success");
+ }
+ };
+
+ const currentPasswordValidation = (event) => {
+ if (event.target.value === "") {
+ setCurrentPasswordValidationInfo("Current password is required.");
+ } else {
+ setCurrentPasswordValidationInfo("Success");
+ }
+ };
+
+ const newPasswordValidation = (event) => {
+ if (event.target.value === "") {
+ setNewPasswordValidationInfo("New password is required.");
+ } else if (!passwordRegex.test(event.target.value)) {
+ setNewPasswordValidationInfo("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 {
+ setNewPasswordValidationInfo("Success");
+ }
+ };
+
+ const confirmPasswordValidation = (event) => {
+ if (event.target.value === "") {
+ setConfirmPasswordValidationInfo("Please confirm your new password.");
+ } else if (event.target.value !== newPasswordInput.current.value) {
+ setConfirmPasswordValidationInfo("Passwords do not match.");
+ } else {
+ setConfirmPasswordValidationInfo("Success");
+ }
+ };
+
+ const handleSubmit = async (refs) => {
+ try {
+ // TODO: Implement API call to update user data
+ console.log('Updating user data:', {
+ username: refs[0].current.value,
+ email: refs[1].current.value,
+ currentPassword: refs[2].current.value,
+ newPassword: refs[3].current.value
+ });
+
+ setMessage({ type: 'success', text: 'Settings updated successfully!' });
+ setIsEditing(false);
+ } catch (error) {
+ setMessage({ type: 'error', text: 'Failed to update settings. Please try again.' });
+ }
+ };
+
+ const getInputList = () => {
+ const baseInputs = [
+ {
+ type: 'info',
+ action: 'Update',
+ endpoint: 'user/settings',
+ button_value: isEditing ? 'SAVE CHANGES' : ''
+ },
+ {
+ type: 'text',
+ name: 'USERNAME',
+ ref: usernameInput,
+ onChange: usernameValidation,
+ validationInfo: usernameValidationInfo,
+ disabled: !isEditing
+ },
+ {
+ type: 'text',
+ name: 'EMAIL',
+ ref: emailInput,
+ onChange: emailValidation,
+ validationInfo: emailValidationInfo,
+ disabled: !isEditing
+ }
+ ];
+
+ if (isEditing) {
+ baseInputs.push(
+ {
+ type: 'password',
+ name: 'CURRENT PASSWORD',
+ ref: currentPasswordInput,
+ onChange: currentPasswordValidation,
+ validationInfo: currentPasswordValidationInfo
+ },
+ {
+ type: 'password',
+ name: 'NEW PASSWORD',
+ ref: newPasswordInput,
+ onChange: newPasswordValidation,
+ validationInfo: newPasswordValidationInfo
+ },
+ {
+ type: 'password',
+ name: 'CONFIRM NEW PASSWORD',
+ ref: confirmPasswordInput,
+ onChange: confirmPasswordValidation,
+ validationInfo: confirmPasswordValidationInfo
+ }
+ );
+ }
+
+ return baseInputs;
+ };
+
+ return (
+
+
+
+
User Settings
+
+
+
+ {message.text && (
+
+ {message.text}
+
+ )}
+
+
+
+
+ );
+};
+
+export default UserSettings;
diff --git a/src/styles/general.scss b/src/styles/general.scss
index b82ca81..c7668a1 100644
--- a/src/styles/general.scss
+++ b/src/styles/general.scss
@@ -3,6 +3,10 @@
$first-color: rgba(0, 90, 25, 1);
$secondary-color: white;
$third-color: #242b2f;
+$error-color: #dc3545;
+$in-progress-color: #ffa500;
+$queued-color: #fbff00;
+$success-color: $first-color;
$title-color: white;
$subtitle-color: #a6a6a6;
@@ -86,9 +90,17 @@ body, html {
width: 100%;
padding-top: 15px;
padding-bottom: 15px;
- cursor:pointer;
+ cursor: pointer;
transition-duration: 0.2s;
+ &.active {
+ background-color: $first-color;
+
+ i {
+ color: $form-background;
+ }
+ }
+
p {
height: 13px;
padding-top: 7px;
@@ -113,7 +125,7 @@ body, html {
background-color: $first-color;
i {
- color: $form-background; // $secondary-color;
+ color: $form-background;
}
}
}
@@ -232,61 +244,421 @@ body, html {
}
.list-container {
- width: 95%;
- height: 100%;
- margin: 2.5%;
- background-color: $form-background;
- color: $secondary-color;
+ margin: 20px;
+ padding: 20px;
+ background: $form-background;
+ color: $subtitle-color;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
.list-generator-container {
- width: 100%;
- height: 50%;
-
- .items-columns {
- display: flex;
- align-items: center;
- margin-top: 10px;
- margin-bottom: 10px;
width: 100%;
- height: 10px;
- text-align: center;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
- .item-column-row {
- font-weight: bolder;
- height: 10px;
- margin-top: 5px;
- margin-bottom: 5px;
- }
- }
+ .table-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
- .items-list {
- .item-row {
- display: flex;
- align-items: center;
- margin-top: 10px;
- margin-bottom: 10px;
+ h2 {
+ margin: 0;
+ color: $title-color;
+ }
- .item-info {
- font-weight: bold;
- margin-top: 15px;
- margin-bottom: 15px;
- }
+ .header-actions {
+ display: flex;
+ gap: 10px;
+
+ button {
+ padding: 8px 16px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+ transition: background-color 0.2s;
+
+ &.refresh-button {
+ background-color: $secondary-color;
+ color: white;
+
+ &:hover {
+ background-color: darken($secondary-color, 10%);
+ }
+
+ &:disabled {
+ background-color: lighten($secondary-color, 20%);
+ cursor: not-allowed;
+ }
+ }
+
+ &.create-button {
+ background-color: $background-color;
+ color: white;
+
+ &:hover {
+ background-color: darken($background-color, 10%);
+ }
+ }
+ }
+ }
}
- .update-button {
+ .items-columns {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 10px;
+ padding: 10px;
+ background-color: rgba($background-color, 0.1);
+ border-radius: 4px;
+ .item-column-row {
+ font-weight: 600;
+ color: $title-color;
+ }
}
- .delete-button {
+ .items-list {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ .no-data {
+ text-align: center;
+ padding: 20px;
+ color: $subtitle-color;
+ background-color: rgba($subtitle-color, 0.1);
+ border-radius: 4px;
+ }
+
+ .item-row {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 10px;
+ padding: 15px;
+ background-color: $background-color;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all 0.2s;
+
+ &:hover {
+ background-color: rgba($form-background, 0.5);
+ }
+
+ &.selected {
+ background-color: $form-background;
+ border-left: 8px solid $first-color;
+ }
+
+ .item-info {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ .progress-bar {
+ width: 100%;
+ height: 8px;
+ background-color: rgba($subtitle-color, 0.1);
+ border-radius: 4px;
+ overflow: visible;
+ position: relative;
+
+ .progress-fill {
+ height: 100%;
+ border-radius: 4px;
+ background-color: $first-color;
+ transition: width 0.3s ease;
+ }
+
+ .progress-text {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: $subtitle-color;
+ font-size: 12px;
+ font-weight: 500;
+ text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
+ }
+ }
+
+ .completed-text {
+ color: $first-color;
+ font-weight: 700;
+ text-align: center;
+ width: 100%;
+ }
+
+ .status-text {
+ font-weight: 700;
+ width: 100%;
+
+ &.active {
+ color: $success-color;
+ }
+
+ &.inactive {
+ color: $subtitle-color;
+ }
+
+ &.in-progress {
+ color: $in-progress-color;
+ }
+
+ &.queued {
+ color: $queued-color;
+ }
+
+ &.completed {
+ color: $success-color;
+ }
+
+ &.failed {
+ color: $error-color;
+ }
+
+ &.cancelled {
+ color: $subtitle-color;
+ }
+ }
+ }
+
+ .action-buttons {
+ display: flex;
+ gap: 8px;
+ justify-content: flex-end;
+
+ button {
+ padding: 6px 12px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+ transition: background-color 0.2s;
+
+ &.update-button {
+ background-color: $secondary-color;
+ color: white;
+
+ &:hover {
+ background-color: darken($secondary-color, 10%);
+ }
+ }
+
+ &.delete-button {
+ background-color: $error-color;
+ color: white;
+
+ &:hover {
+ background-color: darken($error-color, 10%);
+ }
+
+ &:disabled {
+ background-color: lighten($error-color, 20%);
+ cursor: not-allowed;
+ }
+ }
+ }
+ }
+ }
}
- }
+ .element-details {
+ margin-top: 20px;
+ padding: 20px;
+ background-color: $form-background;
+ border-radius: 4px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+
+ .no-selection {
+ text-align: center;
+ color: $subtitle-color;
+ padding: 20px;
+ }
+
+ .details-content {
+ h3 {
+ margin: 0 0 15px 0;
+ color: $title-color;
+ }
+
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ padding: 8px 0;
+ border-bottom: 1px solid rgba($subtitle-color, 0.1);
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .detail-label {
+ font-weight: 600;
+ color: $title-color;
+ }
+
+ .detail-value {
+ color: $subtitle-color;
+ }
+ }
+ }
+ }
}
- .element-details {
- width: 100%;
- height: 50%;
+ .list-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px;
+ background-color: white;
+ border-radius: 4px;
+ margin-bottom: 10px;
+ cursor: pointer;
+ transition: all 0.2s;
+
+ &:hover {
+ background-color: rgba($background-color, 0.05);
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+
+ .item-name {
+ font-weight: 600;
+ color: $title-color;
+ }
+
+ .item-type {
+ color: $subtitle-color;
+ font-size: 0.9em;
+ }
+
+ .item-status {
+ display: inline-block;
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 0.8em;
+ font-weight: 500;
+
+ &.active {
+ background-color: rgba($success-color, 0.1);
+ color: $success-color;
+ }
+
+ &.inactive {
+ background-color: rgba($subtitle-color, 0.1);
+ color: $subtitle-color;
+ }
+
+ &.in-progress {
+ background-color: rgba($in-progress-color, 0.1);
+ color: $in-progress-color;
+ }
+
+ &.queued {
+ background-color: rgba($queued-color, 0.1);
+ color: $queued-color;
+ }
+
+ &.completed {
+ background-color: rgba($success-color, 0.1);
+ color: $success-color;
+ }
+
+ &.failed {
+ background-color: rgba($error-color, 0.1);
+ color: $error-color;
+ }
+
+ &.cancelled {
+ background-color: rgba($subtitle-color, 0.1);
+ color: $subtitle-color;
+ }
+ }
+
+ .item-progress {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ .progress-bar {
+ width: 100%;
+ height: 8px;
+ background-color: rgba($subtitle-color, 0.1);
+ border-radius: 4px;
+ overflow: visible;
+ position: relative;
+
+ .progress-fill {
+ height: 100%;
+ background-color: $first-color;
+ transition: width 0.3s ease;
+ }
+
+ .progress-text {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: $subtitle-color;
+ font-size: 12px;
+ font-weight: 500;
+ text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
+ }
+ }
+
+ .completed-text {
+ color: $first-color;
+ font-weight: 500;
+ }
+
+ span {
+ font-size: 0.9em;
+ color: $subtitle-color;
+ }
+ }
+ }
+
+ .item-details {
+ color: $subtitle-color;
+ font-size: 0.9em;
+ }
+ }
+
+ .details-panel {
+ margin-top: 20px;
+ padding: 20px;
+ background-color: white;
+ border-radius: 4px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+
+ h3 {
+ margin: 0 0 15px 0;
+ color: $title-color;
+ }
+
+ .detail-row {
+ display: flex;
+ justify-content: space-between;
+ padding: 8px 0;
+ border-bottom: 1px solid rgba($subtitle-color, 0.1);
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .detail-label {
+ font-weight: 600;
+ color: $title-color;
+ }
+
+ .detail-value {
+ color: $subtitle-color;
+ }
+ }
}
}
@@ -699,3 +1071,318 @@ body, html {
margin-left: 0px;
}
}
+
+.user-settings {
+ padding: 20px;
+ color: $secondary-color;
+
+ .settings-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+
+ h2 {
+ color: $title-color;
+ margin: 0;
+ font-size: 24px;
+ }
+
+ .edit-button {
+ background: $first-color;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 5px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.3s ease;
+
+ &.cancel {
+ background: #dc3545;
+ }
+
+ &:hover {
+ transform: translateY(-2px);
+ &.cancel {
+ background: darken(#dc3545, 10%);
+ }
+ &:not(.cancel) {
+ background: darken($first-color, 10%);
+ }
+ }
+ }
+ }
+
+ .message {
+ padding: 10px;
+ margin-bottom: 20px;
+ border-radius: 5px;
+ text-align: center;
+ font-weight: 500;
+
+ &.success {
+ background: rgba(0, 120, 0, 0.2);
+ color: #00ff00;
+ }
+
+ &.error {
+ background: rgba(220, 53, 69, 0.2);
+ color: #ff4444;
+ }
+ }
+
+ .settings-form {
+ max-width: 500px;
+ margin: 0 auto;
+
+ .form-group {
+ margin-bottom: 20px;
+
+ label {
+ display: block;
+ margin-bottom: 8px;
+ color: $title-color;
+ font-weight: 600;
+ }
+
+ input {
+ width: 100%;
+ padding: 10px;
+ background: rgba(0, 0, 0, 0.2);
+ border: 1px solid $border-color;
+ border-radius: 5px;
+ color: $secondary-color;
+ transition: all 0.3s ease;
+
+ &:disabled {
+ background: rgba(0, 0, 0, 0.1);
+ cursor: not-allowed;
+ }
+
+ &:focus {
+ outline: none;
+ border-color: $first-color;
+ box-shadow: 0 0 0 2px rgba($first-color, 0.2);
+ }
+ }
+ }
+
+ .save-button {
+ width: 100%;
+ padding: 12px;
+ background: $first-color;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background: darken($first-color, 10%);
+ transform: translateY(-2px);
+ }
+ }
+ }
+ }
+
+.task-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px;
+ background: rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ margin-bottom: 10px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.2);
+ transform: translateX(5px);
+ }
+
+ .task-info {
+ flex: 1;
+
+ .task-name {
+ font-size: 18px;
+ font-weight: 600;
+ color: $title-color;
+ margin-bottom: 5px;
+ }
+
+ .task-status {
+ font-size: 14px;
+ font-weight: 500;
+ margin-bottom: 10px;
+ }
+
+ .task-progress {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ .progress-bar {
+ flex: 1;
+ height: 8px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 4px;
+ overflow: hidden;
+
+ .progress-fill {
+ height: 100%;
+ background: $background-color;
+ transition: width 0.3s ease;
+ }
+ }
+
+ span {
+ min-width: 45px;
+ text-align: right;
+ color: $subtitle-color;
+ }
+ }
+ }
+
+ .task-details {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 10px;
+ color: $subtitle-color;
+ font-size: 14px;
+ margin-left: 20px;
+ }
+
+ .action-buttons {
+ display: flex;
+ gap: 8px;
+ margin-left: 20px;
+
+ button {
+ padding: 6px 12px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+ transition: all 0.3s ease;
+
+ &.cancel-button {
+ background: #ffc107;
+ color: #000;
+
+ &:hover {
+ background: darken(#ffc107, 10%);
+ }
+ }
+
+ &.restart-button {
+ background: #17a2b8;
+ color: white;
+
+ &:hover {
+ background: darken(#17a2b8, 10%);
+ }
+ }
+
+ &.delete-button {
+ background: #dc3545;
+ color: white;
+
+ &:hover {
+ background: darken(#dc3545, 10%);
+ }
+ }
+ }
+ }
+}
+
+.task-details-panel {
+ padding: 20px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 8px;
+
+ h3 {
+ color: $title-color;
+ margin-bottom: 20px;
+ font-size: 20px;
+ }
+
+ .detail-row {
+ display: flex;
+ margin-bottom: 10px;
+ padding: 8px;
+ background: rgba(0, 0, 0, 0.1);
+ border-radius: 4px;
+
+ .detail-label {
+ font-weight: 600;
+ color: $title-color;
+ margin-right: 10px;
+ min-width: 120px;
+ }
+
+ .detail-value {
+ color: $subtitle-color;
+ }
+ }
+}
+
+.dashboard-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px;
+
+ h2 {
+ color: $title-color;
+ margin: 0;
+ font-size: 24px;
+ }
+
+ .create-button {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 20px;
+ background: $first-color;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.3s ease;
+
+ i {
+ font-size: 16px;
+ }
+
+ &:hover {
+ background: darken($first-color, 10%);
+ transform: translateY(-2px);
+ }
+ }
+}
+
+.form-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.7);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+
+ .form-container {
+ background: $form-background;
+ padding: 30px;
+ border-radius: 10px;
+ width: 500px;
+ max-width: 90%;
+ max-height: 90vh;
+ overflow-y: auto;
+ }
+}