Merge pull request 'fix-add-app' (#14) from fix-add-app into master

Reviewed-on: #14
This commit is contained in:
dvirlabs 2025-06-04 01:14:12 +00:00
commit a6b8d502e7
4 changed files with 79 additions and 9 deletions

View File

@ -8,6 +8,10 @@ sections:
icon: prometheus.svg
name: Prometheus
url: https://prometheus.dvirlabs.com
- description: Kibana logs server
icon: kibana.svg
name: Kibana
url: https://kibana.dvirlabs.com
name: Monitoring
- apps:
- description: Git server
@ -22,4 +26,12 @@ sections:
icon: woodpecker-ci.svg
name: Woodpecker
url: https://woodpecker.dvirlabs.com
name: Dev-tools
- description: CD tool
icon: argocd.svg
name: ArgoCD
url: https://argocd.dvirlabs.com
- description: Hashicorp vault
icon: vault.svg
name: Vault
url: https://vault.dvirlabs.com
name: Devtools

View File

@ -1,5 +1,5 @@
import { useState } from 'react';
import { addAppToSection } from '../services/api';
import { useEffect, useState } from 'react';
import { addAppToSection, fetchSections } from '../services/api';
import '../style/AddAppModal.css';
import { IoIosAdd } from "react-icons/io";
@ -7,11 +7,16 @@ function AddAppModal({ onAdded }) {
const [open, setOpen] = useState(false);
const [section, setSection] = useState('');
const [customSection, setCustomSection] = useState(false);
const [name, setName] = useState('');
const [icon, setIcon] = useState('');
const [description, setDescription] = useState('');
const [url, setUrl] = useState('');
const [sections, setSections] = useState([]);
useEffect(() => {
fetchSections().then(data => setSections(data.sections || []));
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
@ -23,6 +28,7 @@ function AddAppModal({ onAdded }) {
setIcon('');
setDescription('');
setUrl('');
setCustomSection(false);
if (onAdded) onAdded();
} catch (err) {
alert('Failed to add app');
@ -31,17 +37,44 @@ function AddAppModal({ onAdded }) {
return (
<>
<IoIosAdd
className="add-button"
onClick={() => setOpen(true)}
/>
<IoIosAdd className="add-button" onClick={() => setOpen(true)} />
{open && (
<div className="modal-overlay" onClick={() => setOpen(false)}>
<div className="modal" onClick={(e) => e.stopPropagation()}>
<h2>Add New App</h2>
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Section" value={section} onChange={e => setSection(e.target.value)} required />
<select
value={customSection ? '__new__' : section}
onChange={(e) => {
const val = e.target.value;
if (val === '__new__') {
setSection('');
setCustomSection(true);
} else {
setSection(val);
setCustomSection(false);
}
}}
required={!customSection}
>
<option value="" disabled>Select a section</option>
{sections.map(s => (
<option key={s.name} value={s.name}>{s.name}</option>
))}
<option value="__new__"> Create new section...</option>
</select>
{customSection && (
<input
type="text"
placeholder="New section name"
value={section}
onChange={e => setSection(e.target.value)}
required
/>
)}
<input type="text" placeholder="App name" value={name} onChange={e => setName(e.target.value)} required />
<input type="text" placeholder="Icon URL" value={icon} onChange={e => setIcon(e.target.value)} />
<input type="text" placeholder="Description" value={description} onChange={e => setDescription(e.target.value)} />

View File

@ -60,6 +60,31 @@
animation: fadeInUp 0.3s ease-out;
}
.modal select {
width: 95%;
height: 40px;
padding: 0.6rem;
margin-bottom: 1rem;
background-color: #1f1f1f;
border: 1px solid #2c2c2c;
color: #fff;
border-radius: 8px;
transition: border 0.3s;
font-family: inherit;
font-size: 1rem;
}
.modal select:focus {
border: 1px solid #00f0ff;
outline: none;
box-shadow: 0 0 6px #00f0ff40;
}
.modal option {
background-color: #1f1f1f;
color: #fff;
}
.modal h2 {
color: #fff;
font-size: 1.8rem;

View File

@ -1,7 +1,7 @@
.app-card {
background-color: #1e1e1e;
color: #fff;
padding: 1.3rem;
padding: 0.9rem;
border-radius: 12px;
text-align: center;
text-decoration: none;