mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-10-31 20:13:50 +00:00
fix(frontend): A form label must be associated with an input
This commit is contained in:
@@ -31,7 +31,7 @@ import {
|
|||||||
Wifi,
|
Wifi,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useId, useState } from "react";
|
||||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||||
import InlineEdit from "../components/InlineEdit";
|
import InlineEdit from "../components/InlineEdit";
|
||||||
import {
|
import {
|
||||||
@@ -1001,6 +1001,8 @@ const HostDetail = () => {
|
|||||||
const CredentialsModal = ({ host, isOpen, onClose }) => {
|
const CredentialsModal = ({ host, isOpen, onClose }) => {
|
||||||
const [showApiKey, setShowApiKey] = useState(false);
|
const [showApiKey, setShowApiKey] = useState(false);
|
||||||
const [activeTab, setActiveTab] = useState("quick-install");
|
const [activeTab, setActiveTab] = useState("quick-install");
|
||||||
|
const apiIdInputId = useId();
|
||||||
|
const apiKeyInputId = useId();
|
||||||
|
|
||||||
const { data: serverUrlData } = useQuery({
|
const { data: serverUrlData } = useQuery({
|
||||||
queryKey: ["serverUrl"],
|
queryKey: ["serverUrl"],
|
||||||
@@ -1342,11 +1344,15 @@ echo " - View logs: tail -f /var/log/patchmon-agent.log"`;
|
|||||||
</h4>
|
</h4>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={apiIdInputId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
API ID
|
API ID
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
|
id={apiIdInputId}
|
||||||
type="text"
|
type="text"
|
||||||
value={host.api_id}
|
value={host.api_id}
|
||||||
readOnly
|
readOnly
|
||||||
@@ -1364,11 +1370,15 @@ echo " - View logs: tail -f /var/log/patchmon-agent.log"`;
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={apiKeyInputId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
API Key
|
API Key
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
|
id={apiKeyInputId}
|
||||||
type={showApiKey ? "text" : "password"}
|
type={showApiKey ? "text" : "password"}
|
||||||
value={host.api_key}
|
value={host.api_key}
|
||||||
readOnly
|
readOnly
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
Trash2,
|
Trash2,
|
||||||
Users,
|
Users,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useId, useState } from "react";
|
||||||
import { hostGroupsAPI } from "../utils/api";
|
import { hostGroupsAPI } from "../utils/api";
|
||||||
|
|
||||||
const HostGroups = () => {
|
const HostGroups = () => {
|
||||||
@@ -252,6 +252,9 @@ const HostGroups = () => {
|
|||||||
|
|
||||||
// Create Host Group Modal
|
// Create Host Group Modal
|
||||||
const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
||||||
|
const nameId = useId();
|
||||||
|
const descriptionId = useId();
|
||||||
|
const colorId = useId();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
@@ -279,11 +282,15 @@ const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={nameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Name *
|
Name *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
id={nameId}
|
||||||
name="name"
|
name="name"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -294,10 +301,14 @@ const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={descriptionId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Description
|
Description
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id={descriptionId}
|
||||||
name="description"
|
name="description"
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -308,12 +319,16 @@ const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={colorId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Color
|
Color
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="color"
|
||||||
|
id={colorId}
|
||||||
name="color"
|
name="color"
|
||||||
value={formData.color}
|
value={formData.color}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -350,6 +365,9 @@ const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
|||||||
|
|
||||||
// Edit Host Group Modal
|
// Edit Host Group Modal
|
||||||
const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
||||||
|
const editNameId = useId();
|
||||||
|
const editDescriptionId = useId();
|
||||||
|
const editColorId = useId();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description || "",
|
description: group.description || "",
|
||||||
@@ -377,11 +395,15 @@ const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editNameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Name *
|
Name *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
id={editNameId}
|
||||||
name="name"
|
name="name"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -392,10 +414,14 @@ const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editDescriptionId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Description
|
Description
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id={editDescriptionId}
|
||||||
name="description"
|
name="description"
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -406,12 +432,16 @@ const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editColorId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Color
|
Color
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="color"
|
||||||
|
id={editColorId}
|
||||||
name="color"
|
name="color"
|
||||||
value={formData.color}
|
value={formData.color}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
Users,
|
Users,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useId, useState } from "react";
|
||||||
import { Link, useNavigate, useSearchParams } from "react-router-dom";
|
import { Link, useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import InlineEdit from "../components/InlineEdit";
|
import InlineEdit from "../components/InlineEdit";
|
||||||
import InlineGroupEdit from "../components/InlineGroupEdit";
|
import InlineGroupEdit from "../components/InlineGroupEdit";
|
||||||
@@ -43,6 +43,8 @@ import { OSIcon } from "../utils/osIcons.jsx";
|
|||||||
|
|
||||||
// Add Host Modal Component
|
// Add Host Modal Component
|
||||||
const AddHostModal = ({ isOpen, onClose, onSuccess }) => {
|
const AddHostModal = ({ isOpen, onClose, onSuccess }) => {
|
||||||
|
const friendlyNameId = useId();
|
||||||
|
const hostGroupId = useId();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
friendly_name: "",
|
friendly_name: "",
|
||||||
hostGroupId: "",
|
hostGroupId: "",
|
||||||
@@ -113,11 +115,15 @@ const AddHostModal = ({ isOpen, onClose, onSuccess }) => {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<label
|
||||||
|
htmlFor={friendlyNameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2"
|
||||||
|
>
|
||||||
Friendly Name *
|
Friendly Name *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
id={friendlyNameId}
|
||||||
required
|
required
|
||||||
value={formData.friendly_name}
|
value={formData.friendly_name}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -133,9 +139,9 @@ const AddHostModal = ({ isOpen, onClose, onSuccess }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-3">
|
<span className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-3">
|
||||||
Host Group
|
Host Group
|
||||||
</label>
|
</span>
|
||||||
<div className="grid grid-cols-3 gap-2">
|
<div className="grid grid-cols-3 gap-2">
|
||||||
{/* No Group Option */}
|
{/* No Group Option */}
|
||||||
<button
|
<button
|
||||||
@@ -231,6 +237,8 @@ const AddHostModal = ({ isOpen, onClose, onSuccess }) => {
|
|||||||
|
|
||||||
// Credentials Modal Component
|
// Credentials Modal Component
|
||||||
const CredentialsModal = ({ host, isOpen, onClose }) => {
|
const CredentialsModal = ({ host, isOpen, onClose }) => {
|
||||||
|
const apiIdId = useId();
|
||||||
|
const apiKeyId = useId();
|
||||||
const [showApiKey, setShowApiKey] = useState(false);
|
const [showApiKey, setShowApiKey] = useState(false);
|
||||||
const [activeTab, setActiveTab] = useState(
|
const [activeTab, setActiveTab] = useState(
|
||||||
host?.isNewHost ? "quick" : "credentials",
|
host?.isNewHost ? "quick" : "credentials",
|
||||||
@@ -508,12 +516,16 @@ echo " - View logs: tail -f /var/log/patchmon-agent.log"`,
|
|||||||
{activeTab === "credentials" && (
|
{activeTab === "credentials" && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 mb-2">
|
<label
|
||||||
|
htmlFor={apiIdId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 mb-2"
|
||||||
|
>
|
||||||
API ID
|
API ID
|
||||||
</label>
|
</label>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
id={apiIdId}
|
||||||
readOnly
|
readOnly
|
||||||
value={host.apiId}
|
value={host.apiId}
|
||||||
className="flex-1 block w-full border-secondary-300 rounded-l-md shadow-sm bg-secondary-50"
|
className="flex-1 block w-full border-secondary-300 rounded-l-md shadow-sm bg-secondary-50"
|
||||||
@@ -529,12 +541,16 @@ echo " - View logs: tail -f /var/log/patchmon-agent.log"`,
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 mb-2">
|
<label
|
||||||
|
htmlFor={apiKeyId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 mb-2"
|
||||||
|
>
|
||||||
API Key
|
API Key
|
||||||
</label>
|
</label>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<input
|
<input
|
||||||
type={showApiKey ? "text" : "password"}
|
type={showApiKey ? "text" : "password"}
|
||||||
|
id={apiKeyId}
|
||||||
readOnly
|
readOnly
|
||||||
value={host.apiKey}
|
value={host.apiKey}
|
||||||
className="flex-1 block w-full border-secondary-300 rounded-l-md shadow-sm bg-secondary-50"
|
className="flex-1 block w-full border-secondary-300 rounded-l-md shadow-sm bg-secondary-50"
|
||||||
@@ -779,6 +795,10 @@ echo " - View logs: tail -f /var/log/patchmon-agent.log"`,
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Hosts = () => {
|
const Hosts = () => {
|
||||||
|
const hostGroupFilterId = useId();
|
||||||
|
const statusFilterId = useId();
|
||||||
|
const osFilterId = useId();
|
||||||
|
const bulkHostGroupId = useId();
|
||||||
const [showAddModal, setShowAddModal] = useState(false);
|
const [showAddModal, setShowAddModal] = useState(false);
|
||||||
const [selectedHosts, setSelectedHosts] = useState([]);
|
const [selectedHosts, setSelectedHosts] = useState([]);
|
||||||
const [showBulkAssignModal, setShowBulkAssignModal] = useState(false);
|
const [showBulkAssignModal, setShowBulkAssignModal] = useState(false);
|
||||||
@@ -1737,10 +1757,14 @@ const Hosts = () => {
|
|||||||
<div className="bg-secondary-50 dark:bg-secondary-700 p-4 rounded-lg border dark:border-secondary-600">
|
<div className="bg-secondary-50 dark:bg-secondary-700 p-4 rounded-lg border dark:border-secondary-600">
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={hostGroupFilterId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Host Group
|
Host Group
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id={hostGroupFilterId}
|
||||||
value={groupFilter}
|
value={groupFilter}
|
||||||
onChange={(e) => setGroupFilter(e.target.value)}
|
onChange={(e) => setGroupFilter(e.target.value)}
|
||||||
className="w-full border border-secondary-300 dark:border-secondary-600 rounded-lg px-3 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white dark:bg-secondary-800 text-secondary-900 dark:text-white"
|
className="w-full border border-secondary-300 dark:border-secondary-600 rounded-lg px-3 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white dark:bg-secondary-800 text-secondary-900 dark:text-white"
|
||||||
@@ -1755,10 +1779,14 @@ const Hosts = () => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={statusFilterId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Status
|
Status
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id={statusFilterId}
|
||||||
value={statusFilter}
|
value={statusFilter}
|
||||||
onChange={(e) => setStatusFilter(e.target.value)}
|
onChange={(e) => setStatusFilter(e.target.value)}
|
||||||
className="w-full border border-secondary-300 dark:border-secondary-600 rounded-lg px-3 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white dark:bg-secondary-800 text-secondary-900 dark:text-white"
|
className="w-full border border-secondary-300 dark:border-secondary-600 rounded-lg px-3 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white dark:bg-secondary-800 text-secondary-900 dark:text-white"
|
||||||
@@ -1771,10 +1799,14 @@ const Hosts = () => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={osFilterId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Operating System
|
Operating System
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id={osFilterId}
|
||||||
value={osFilter}
|
value={osFilter}
|
||||||
onChange={(e) => setOsFilter(e.target.value)}
|
onChange={(e) => setOsFilter(e.target.value)}
|
||||||
className="w-full border border-secondary-300 dark:border-secondary-600 rounded-lg px-3 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white dark:bg-secondary-800 text-secondary-900 dark:text-white"
|
className="w-full border border-secondary-300 dark:border-secondary-600 rounded-lg px-3 py-2 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white dark:bg-secondary-800 text-secondary-900 dark:text-white"
|
||||||
@@ -2116,10 +2148,14 @@ const BulkAssignModal = ({
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 mb-1">
|
<label
|
||||||
|
htmlFor={bulkHostGroupId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 mb-1"
|
||||||
|
>
|
||||||
Host Group
|
Host Group
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id={bulkHostGroupId}
|
||||||
value={selectedGroupId}
|
value={selectedGroupId}
|
||||||
onChange={(e) => setSelectedGroupId(e.target.value)}
|
onChange={(e) => setSelectedGroupId(e.target.value)}
|
||||||
className="w-full px-3 py-2 border border-secondary-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
|
className="w-full px-3 py-2 border border-secondary-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||||
@@ -2309,7 +2345,7 @@ const ColumnSettingsModal = ({
|
|||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDrop={(e) => handleDrop(e, index)}
|
onDrop={(e) => handleDrop(e, index)}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Focus handling for keyboard users
|
// Focus handling for keyboard users
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Trash2,
|
Trash2,
|
||||||
Users,
|
Users,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useId, useState } from "react";
|
||||||
import { hostGroupsAPI } from "../utils/api";
|
import { hostGroupsAPI } from "../utils/api";
|
||||||
|
|
||||||
const Options = () => {
|
const Options = () => {
|
||||||
@@ -332,6 +332,9 @@ const Options = () => {
|
|||||||
|
|
||||||
// Create Host Group Modal
|
// Create Host Group Modal
|
||||||
const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
||||||
|
const nameId = useId();
|
||||||
|
const descriptionId = useId();
|
||||||
|
const colorId = useId();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
@@ -359,11 +362,15 @@ const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={nameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Name *
|
Name *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
id={nameId}
|
||||||
name="name"
|
name="name"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -374,10 +381,14 @@ const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={descriptionId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Description
|
Description
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id={descriptionId}
|
||||||
name="description"
|
name="description"
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -388,12 +399,16 @@ const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={colorId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Color
|
Color
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="color"
|
||||||
|
id={colorId}
|
||||||
name="color"
|
name="color"
|
||||||
value={formData.color}
|
value={formData.color}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -430,6 +445,9 @@ const CreateHostGroupModal = ({ onClose, onSubmit, isLoading }) => {
|
|||||||
|
|
||||||
// Edit Host Group Modal
|
// Edit Host Group Modal
|
||||||
const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
||||||
|
const editNameId = useId();
|
||||||
|
const editDescriptionId = useId();
|
||||||
|
const editColorId = useId();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: group.name,
|
name: group.name,
|
||||||
description: group.description || "",
|
description: group.description || "",
|
||||||
@@ -457,11 +475,15 @@ const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editNameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Name *
|
Name *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
id={editNameId}
|
||||||
name="name"
|
name="name"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -472,10 +494,14 @@ const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editDescriptionId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Description
|
Description
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id={editDescriptionId}
|
||||||
name="description"
|
name="description"
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@@ -486,12 +512,16 @@ const EditHostGroupModal = ({ group, onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editColorId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Color
|
Color
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="color"
|
||||||
|
id={editColorId}
|
||||||
name="color"
|
name="color"
|
||||||
value={formData.color}
|
value={formData.color}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
Users,
|
Users,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useId, useState } from "react";
|
||||||
import { useAuth } from "../contexts/AuthContext";
|
import { useAuth } from "../contexts/AuthContext";
|
||||||
import { permissionsAPI } from "../utils/api";
|
import { permissionsAPI } from "../utils/api";
|
||||||
|
|
||||||
@@ -320,6 +320,7 @@ const RolePermissionsCard = ({
|
|||||||
<div key={field.key} className="flex items-start">
|
<div key={field.key} className="flex items-start">
|
||||||
<div className="flex items-center h-5">
|
<div className="flex items-center h-5">
|
||||||
<input
|
<input
|
||||||
|
id={`${role.role}-${field.key}`}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={isChecked}
|
checked={isChecked}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -335,7 +336,10 @@ const RolePermissionsCard = ({
|
|||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Icon className="h-4 w-4 text-secondary-400 mr-2" />
|
<Icon className="h-4 w-4 text-secondary-400 mr-2" />
|
||||||
<label className="text-sm font-medium text-secondary-900 dark:text-white">
|
<label
|
||||||
|
htmlFor={`${role.role}-${field.key}`}
|
||||||
|
className="text-sm font-medium text-secondary-900 dark:text-white"
|
||||||
|
>
|
||||||
{field.label}
|
{field.label}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -354,6 +358,7 @@ const RolePermissionsCard = ({
|
|||||||
|
|
||||||
// Add Role Modal Component
|
// Add Role Modal Component
|
||||||
const AddRoleModal = ({ isOpen, onClose, onSuccess }) => {
|
const AddRoleModal = ({ isOpen, onClose, onSuccess }) => {
|
||||||
|
const roleNameInputId = useId();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
role: "",
|
role: "",
|
||||||
can_view_dashboard: true,
|
can_view_dashboard: true,
|
||||||
@@ -404,10 +409,14 @@ const AddRoleModal = ({ isOpen, onClose, onSuccess }) => {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={roleNameInputId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Role Name
|
Role Name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={roleNameInputId}
|
||||||
type="text"
|
type="text"
|
||||||
name="role"
|
name="role"
|
||||||
required
|
required
|
||||||
@@ -439,13 +448,17 @@ const AddRoleModal = ({ isOpen, onClose, onSuccess }) => {
|
|||||||
].map((permission) => (
|
].map((permission) => (
|
||||||
<div key={permission.key} className="flex items-center">
|
<div key={permission.key} className="flex items-center">
|
||||||
<input
|
<input
|
||||||
|
id={`add-role-${permission.key}`}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name={permission.key}
|
name={permission.key}
|
||||||
checked={formData[permission.key]}
|
checked={formData[permission.key]}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-secondary-300 rounded"
|
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-secondary-300 rounded"
|
||||||
/>
|
/>
|
||||||
<label className="ml-2 block text-sm text-secondary-700 dark:text-secondary-200">
|
<label
|
||||||
|
htmlFor={`add-role-${permission.key}`}
|
||||||
|
className="ml-2 block text-sm text-secondary-700 dark:text-secondary-200"
|
||||||
|
>
|
||||||
{permission.label}
|
{permission.label}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ const Profile = () => {
|
|||||||
const currentPasswordId = useId();
|
const currentPasswordId = useId();
|
||||||
const newPasswordId = useId();
|
const newPasswordId = useId();
|
||||||
const confirmPasswordId = useId();
|
const confirmPasswordId = useId();
|
||||||
|
const verificationTokenId = useId();
|
||||||
|
const disablePasswordId = useId();
|
||||||
const { user, updateProfile, changePassword } = useAuth();
|
const { user, updateProfile, changePassword } = useAuth();
|
||||||
const { theme, toggleTheme, isDark } = useTheme();
|
const { theme, toggleTheme, isDark } = useTheme();
|
||||||
const [activeTab, setActiveTab] = useState("profile");
|
const [activeTab, setActiveTab] = useState("profile");
|
||||||
@@ -924,10 +926,14 @@ const TfaTab = () => {
|
|||||||
</p>
|
</p>
|
||||||
<form onSubmit={handleVerify} className="space-y-4">
|
<form onSubmit={handleVerify} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={verificationTokenId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Verification Code
|
Verification Code
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={verificationTokenId}
|
||||||
type="text"
|
type="text"
|
||||||
value={verificationToken}
|
value={verificationToken}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -1030,10 +1036,14 @@ const TfaTab = () => {
|
|||||||
</p>
|
</p>
|
||||||
<form onSubmit={handleDisable} className="space-y-4">
|
<form onSubmit={handleDisable} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={disablePasswordId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Password
|
Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={disablePasswordId}
|
||||||
type="password"
|
type="password"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ import { repositoryAPI } from "../utils/api";
|
|||||||
|
|
||||||
const RepositoryDetail = () => {
|
const RepositoryDetail = () => {
|
||||||
const isActiveId = useId();
|
const isActiveId = useId();
|
||||||
|
const repositoryNameId = useId();
|
||||||
|
const priorityId = useId();
|
||||||
|
const descriptionId = useId();
|
||||||
const { repositoryId } = useParams();
|
const { repositoryId } = useParams();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [editMode, setEditMode] = useState(false);
|
const [editMode, setEditMode] = useState(false);
|
||||||
@@ -201,11 +204,15 @@ const RepositoryDetail = () => {
|
|||||||
{editMode ? (
|
{editMode ? (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-1">
|
<label
|
||||||
|
htmlFor={repositoryNameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-1"
|
||||||
|
>
|
||||||
Repository Name
|
Repository Name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
id={repositoryNameId}
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, name: e.target.value })
|
setFormData({ ...formData, name: e.target.value })
|
||||||
@@ -214,11 +221,15 @@ const RepositoryDetail = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-1">
|
<label
|
||||||
|
htmlFor={priorityId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-1"
|
||||||
|
>
|
||||||
Priority
|
Priority
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
id={priorityId}
|
||||||
value={formData.priority}
|
value={formData.priority}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, priority: e.target.value })
|
setFormData({ ...formData, priority: e.target.value })
|
||||||
@@ -228,10 +239,14 @@ const RepositoryDetail = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-1">
|
<label
|
||||||
|
htmlFor={descriptionId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-300 mb-1"
|
||||||
|
>
|
||||||
Description
|
Description
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id={descriptionId}
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, description: e.target.value })
|
setFormData({ ...formData, description: e.target.value })
|
||||||
@@ -263,9 +278,9 @@ const RepositoryDetail = () => {
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
<span className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
||||||
URL
|
URL
|
||||||
</label>
|
</span>
|
||||||
<div className="flex items-center mt-1">
|
<div className="flex items-center mt-1">
|
||||||
<Globe className="h-4 w-4 text-secondary-400 mr-2" />
|
<Globe className="h-4 w-4 text-secondary-400 mr-2" />
|
||||||
<span className="text-secondary-900 dark:text-white">
|
<span className="text-secondary-900 dark:text-white">
|
||||||
@@ -274,25 +289,25 @@ const RepositoryDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
<span className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
||||||
Distribution
|
Distribution
|
||||||
</label>
|
</span>
|
||||||
<p className="text-secondary-900 dark:text-white mt-1">
|
<p className="text-secondary-900 dark:text-white mt-1">
|
||||||
{repository.distribution}
|
{repository.distribution}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
<span className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
||||||
Components
|
Components
|
||||||
</label>
|
</span>
|
||||||
<p className="text-secondary-900 dark:text-white mt-1">
|
<p className="text-secondary-900 dark:text-white mt-1">
|
||||||
{repository.components}
|
{repository.components}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
<span className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
||||||
Repository Type
|
Repository Type
|
||||||
</label>
|
</span>
|
||||||
<p className="text-secondary-900 dark:text-white mt-1">
|
<p className="text-secondary-900 dark:text-white mt-1">
|
||||||
{repository.repoType}
|
{repository.repoType}
|
||||||
</p>
|
</p>
|
||||||
@@ -300,9 +315,9 @@ const RepositoryDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
<span className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
||||||
Security
|
Security
|
||||||
</label>
|
</span>
|
||||||
<div className="flex items-center mt-1">
|
<div className="flex items-center mt-1">
|
||||||
{repository.isSecure ? (
|
{repository.isSecure ? (
|
||||||
<>
|
<>
|
||||||
@@ -319,9 +334,9 @@ const RepositoryDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
{repository.priority && (
|
{repository.priority && (
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
<span className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
||||||
Priority
|
Priority
|
||||||
</label>
|
</span>
|
||||||
<p className="text-secondary-900 dark:text-white mt-1">
|
<p className="text-secondary-900 dark:text-white mt-1">
|
||||||
{repository.priority}
|
{repository.priority}
|
||||||
</p>
|
</p>
|
||||||
@@ -329,18 +344,18 @@ const RepositoryDetail = () => {
|
|||||||
)}
|
)}
|
||||||
{repository.description && (
|
{repository.description && (
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
<span className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
||||||
Description
|
Description
|
||||||
</label>
|
</span>
|
||||||
<p className="text-secondary-900 dark:text-white mt-1">
|
<p className="text-secondary-900 dark:text-white mt-1">
|
||||||
{repository.description}
|
{repository.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
<span className="text-sm font-medium text-secondary-500 dark:text-secondary-400">
|
||||||
Created
|
Created
|
||||||
</label>
|
</span>
|
||||||
<div className="flex items-center mt-1">
|
<div className="flex items-center mt-1">
|
||||||
<Calendar className="h-4 w-4 text-secondary-400 mr-2" />
|
<Calendar className="h-4 w-4 text-secondary-400 mr-2" />
|
||||||
<span className="text-secondary-900 dark:text-white">
|
<span className="text-secondary-900 dark:text-white">
|
||||||
|
|||||||
@@ -30,6 +30,17 @@ const Settings = () => {
|
|||||||
const repoPrivateId = useId();
|
const repoPrivateId = useId();
|
||||||
const useCustomSshKeyId = useId();
|
const useCustomSshKeyId = useId();
|
||||||
const isDefaultId = useId();
|
const isDefaultId = useId();
|
||||||
|
const protocolId = useId();
|
||||||
|
const hostId = useId();
|
||||||
|
const portId = useId();
|
||||||
|
const updateIntervalId = useId();
|
||||||
|
const defaultRoleId = useId();
|
||||||
|
const repositoryTypeId = useId();
|
||||||
|
const githubRepoUrlId = useId();
|
||||||
|
const sshKeyPathId = useId();
|
||||||
|
const versionId = useId();
|
||||||
|
const releaseNotesId = useId();
|
||||||
|
const scriptContentId = useId();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
serverProtocol: "http",
|
serverProtocol: "http",
|
||||||
serverHost: "localhost",
|
serverHost: "localhost",
|
||||||
@@ -470,10 +481,14 @@ const Settings = () => {
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<label
|
||||||
|
htmlFor={protocolId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2"
|
||||||
|
>
|
||||||
Protocol
|
Protocol
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id={protocolId}
|
||||||
value={formData.serverProtocol}
|
value={formData.serverProtocol}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange("serverProtocol", e.target.value)
|
handleInputChange("serverProtocol", e.target.value)
|
||||||
@@ -486,10 +501,14 @@ const Settings = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<label
|
||||||
|
htmlFor={hostId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2"
|
||||||
|
>
|
||||||
Host *
|
Host *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={hostId}
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.serverHost}
|
value={formData.serverHost}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -510,10 +529,14 @@ const Settings = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<label
|
||||||
|
htmlFor={portId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2"
|
||||||
|
>
|
||||||
Port *
|
Port *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={portId}
|
||||||
type="number"
|
type="number"
|
||||||
value={formData.serverPort}
|
value={formData.serverPort}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -551,13 +574,17 @@ const Settings = () => {
|
|||||||
|
|
||||||
{/* Update Interval */}
|
{/* Update Interval */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<label
|
||||||
|
htmlFor={updateIntervalId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2"
|
||||||
|
>
|
||||||
Agent Update Interval (minutes)
|
Agent Update Interval (minutes)
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{/* Numeric input (concise width) */}
|
{/* Numeric input (concise width) */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
|
id={updateIntervalId}
|
||||||
type="number"
|
type="number"
|
||||||
min="5"
|
min="5"
|
||||||
max="1440"
|
max="1440"
|
||||||
@@ -687,10 +714,14 @@ const Settings = () => {
|
|||||||
{/* Default User Role Dropdown */}
|
{/* Default User Role Dropdown */}
|
||||||
{formData.signupEnabled && (
|
{formData.signupEnabled && (
|
||||||
<div className="mt-3 ml-6">
|
<div className="mt-3 ml-6">
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<label
|
||||||
|
htmlFor={defaultRoleId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2"
|
||||||
|
>
|
||||||
Default Role for New Users
|
Default Role for New Users
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id={defaultRoleId}
|
||||||
value={formData.defaultUserRole}
|
value={formData.defaultUserRole}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleInputChange("defaultUserRole", e.target.value)
|
handleInputChange("defaultUserRole", e.target.value)
|
||||||
@@ -989,10 +1020,10 @@ const Settings = () => {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<fieldset>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<legend className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
||||||
Repository Type
|
Repository Type
|
||||||
</label>
|
</legend>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
@@ -1038,13 +1069,17 @@ const Settings = () => {
|
|||||||
Choose whether your repository is public or private to
|
Choose whether your repository is public or private to
|
||||||
determine the appropriate access method.
|
determine the appropriate access method.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<label
|
||||||
|
htmlFor={githubRepoUrlId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2"
|
||||||
|
>
|
||||||
GitHub Repository URL
|
GitHub Repository URL
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={githubRepoUrlId}
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.githubRepoUrl || ""}
|
value={formData.githubRepoUrl || ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -1084,10 +1119,14 @@ const Settings = () => {
|
|||||||
|
|
||||||
{formData.useCustomSshKey && (
|
{formData.useCustomSshKey && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
<label
|
||||||
|
htmlFor={sshKeyPathId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2"
|
||||||
|
>
|
||||||
SSH Key Path
|
SSH Key Path
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={sshKeyPathId}
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.sshKeyPath || ""}
|
value={formData.sshKeyPath || ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -1384,10 +1423,14 @@ const AgentVersionModal = ({ isOpen, onClose, onSubmit, isLoading }) => {
|
|||||||
<form onSubmit={handleSubmit} className="px-6 py-4">
|
<form onSubmit={handleSubmit} className="px-6 py-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={versionId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Version *
|
Version *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={versionId}
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.version}
|
value={formData.version}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -1408,10 +1451,14 @@ const AgentVersionModal = ({ isOpen, onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={releaseNotesId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Release Notes
|
Release Notes
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id={releaseNotesId}
|
||||||
value={formData.releaseNotes}
|
value={formData.releaseNotes}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
@@ -1426,7 +1473,10 @@ const AgentVersionModal = ({ isOpen, onClose, onSubmit, isLoading }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={scriptContentId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Script Content *
|
Script Content *
|
||||||
</label>
|
</label>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -1437,6 +1487,7 @@ const AgentVersionModal = ({ isOpen, onClose, onSubmit, isLoading }) => {
|
|||||||
className="block w-full text-sm text-secondary-500 dark:text-secondary-400 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-medium file:bg-primary-50 file:text-primary-700 hover:file:bg-primary-100 dark:file:bg-primary-900 dark:file:text-primary-200"
|
className="block w-full text-sm text-secondary-500 dark:text-secondary-400 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-medium file:bg-primary-50 file:text-primary-700 hover:file:bg-primary-100 dark:file:bg-primary-900 dark:file:text-primary-200"
|
||||||
/>
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
|
id={scriptContentId}
|
||||||
value={formData.scriptContent}
|
value={formData.scriptContent}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
User,
|
User,
|
||||||
XCircle,
|
XCircle,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useId, useState } from "react";
|
||||||
import { useAuth } from "../contexts/AuthContext";
|
import { useAuth } from "../contexts/AuthContext";
|
||||||
import { adminUsersAPI, permissionsAPI } from "../utils/api";
|
import { adminUsersAPI, permissionsAPI } from "../utils/api";
|
||||||
|
|
||||||
@@ -290,6 +290,13 @@ const Users = () => {
|
|||||||
|
|
||||||
// Add User Modal Component
|
// Add User Modal Component
|
||||||
const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
||||||
|
const usernameId = useId();
|
||||||
|
const emailId = useId();
|
||||||
|
const firstNameId = useId();
|
||||||
|
const lastNameId = useId();
|
||||||
|
const passwordId = useId();
|
||||||
|
const roleId = useId();
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
username: "",
|
username: "",
|
||||||
email: "",
|
email: "",
|
||||||
@@ -343,10 +350,14 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={usernameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Username
|
Username
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={usernameId}
|
||||||
type="text"
|
type="text"
|
||||||
name="username"
|
name="username"
|
||||||
required
|
required
|
||||||
@@ -357,10 +368,14 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={emailId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Email
|
Email
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={emailId}
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
required
|
required
|
||||||
@@ -372,10 +387,14 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
|||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={firstNameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
First Name
|
First Name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={firstNameId}
|
||||||
type="text"
|
type="text"
|
||||||
name="first_name"
|
name="first_name"
|
||||||
value={formData.first_name}
|
value={formData.first_name}
|
||||||
@@ -384,10 +403,14 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={lastNameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Last Name
|
Last Name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={lastNameId}
|
||||||
type="text"
|
type="text"
|
||||||
name="last_name"
|
name="last_name"
|
||||||
value={formData.last_name}
|
value={formData.last_name}
|
||||||
@@ -398,10 +421,14 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={passwordId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Password
|
Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={passwordId}
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
required
|
required
|
||||||
@@ -416,10 +443,14 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={roleId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Role
|
Role
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id={roleId}
|
||||||
name="role"
|
name="role"
|
||||||
value={formData.role}
|
value={formData.role}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
@@ -473,6 +504,13 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
|
|||||||
|
|
||||||
// Edit User Modal Component
|
// Edit User Modal Component
|
||||||
const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
|
const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
|
||||||
|
const editUsernameId = useId();
|
||||||
|
const editEmailId = useId();
|
||||||
|
const editFirstNameId = useId();
|
||||||
|
const editLastNameId = useId();
|
||||||
|
const editRoleId = useId();
|
||||||
|
const editActiveId = useId();
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
username: user?.username || "",
|
username: user?.username || "",
|
||||||
email: user?.email || "",
|
email: user?.email || "",
|
||||||
@@ -518,10 +556,14 @@ const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editUsernameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Username
|
Username
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={editUsernameId}
|
||||||
type="text"
|
type="text"
|
||||||
name="username"
|
name="username"
|
||||||
required
|
required
|
||||||
@@ -532,10 +574,14 @@ const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editEmailId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Email
|
Email
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={editEmailId}
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
required
|
required
|
||||||
@@ -547,10 +593,14 @@ const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
|
|||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editFirstNameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
First Name
|
First Name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={editFirstNameId}
|
||||||
type="text"
|
type="text"
|
||||||
name="first_name"
|
name="first_name"
|
||||||
value={formData.first_name}
|
value={formData.first_name}
|
||||||
@@ -559,10 +609,14 @@ const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editLastNameId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Last Name
|
Last Name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={editLastNameId}
|
||||||
type="text"
|
type="text"
|
||||||
name="last_name"
|
name="last_name"
|
||||||
value={formData.last_name}
|
value={formData.last_name}
|
||||||
@@ -573,10 +627,14 @@ const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={editRoleId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Role
|
Role
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id={editRoleId}
|
||||||
name="role"
|
name="role"
|
||||||
value={formData.role}
|
value={formData.role}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
@@ -600,13 +658,17 @@ const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
|
|||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
|
id={editActiveId}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="is_active"
|
name="is_active"
|
||||||
checked={formData.is_active}
|
checked={formData.is_active}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-secondary-300 rounded"
|
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-secondary-300 rounded"
|
||||||
/>
|
/>
|
||||||
<label className="ml-2 block text-sm text-secondary-700 dark:text-secondary-200">
|
<label
|
||||||
|
htmlFor={editActiveId}
|
||||||
|
className="ml-2 block text-sm text-secondary-700 dark:text-secondary-200"
|
||||||
|
>
|
||||||
Active user
|
Active user
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -649,6 +711,8 @@ const ResetPasswordModal = ({
|
|||||||
onPasswordReset,
|
onPasswordReset,
|
||||||
isLoading,
|
isLoading,
|
||||||
}) => {
|
}) => {
|
||||||
|
const newPasswordId = useId();
|
||||||
|
const confirmPasswordId = useId();
|
||||||
const [newPassword, setNewPassword] = useState("");
|
const [newPassword, setNewPassword] = useState("");
|
||||||
const [confirmPassword, setConfirmPassword] = useState("");
|
const [confirmPassword, setConfirmPassword] = useState("");
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
@@ -696,10 +760,14 @@ const ResetPasswordModal = ({
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={newPasswordId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
New Password
|
New Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={newPasswordId}
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
minLength={6}
|
minLength={6}
|
||||||
@@ -711,10 +779,14 @@ const ResetPasswordModal = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1">
|
<label
|
||||||
|
htmlFor={confirmPasswordId}
|
||||||
|
className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-1"
|
||||||
|
>
|
||||||
Confirm Password
|
Confirm Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
id={confirmPasswordId}
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
|
|||||||
Reference in New Issue
Block a user