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