import React, { useState, useEffect } from "react";
import { useNavigate, useParams, Link } from "react-router-dom";
import { tasksApi, Task, TaskCreateRequest } from "@/services/tasksApi";
import { issuesApi } from "@/lib/api";
import { projectService } from "@/lib/api";
import { IssueApi } from "@/types";
import { ProjectApi } from "@/types";
import { toast } from "sonner";
import {
ChevronRight,
Home,
Save,
Bug,
ArrowLeft,
AlertTriangle,
Check,
ChevronDown,
User,
Lock
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/components/ui/tabs";
import Layout from "../components/layout/layout";
import { employeeService } from "@/lib/api";
import { Employee } from "@/types";
import { Separator } from "@/components/ui/separator";
import { format } from "date-fns";
import { Calendar } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
import { Badge } from "@/components/ui/badge";
import { useAuth } from "@/lib/auth-context";
// Custom dropdown component
interface CustomDropdownProps {
value: string;
onChange: (value: string) => void;
options: { value: string; label: string }[];
placeholder: string;
}
function CustomDropdown({ value, onChange, options, placeholder }: CustomDropdownProps) {
const [open, setOpen] = useState(false);
console.log("CustomDropdown render with value:", value, typeof value);
const getLabel = () => {
const option = options.find(option => option.value === value);
console.log("Finding option for value:", value, "Found:", option);
return option?.label || placeholder;
};
const handleButtonClick = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setOpen(!open);
};
const handleOptionClick = (optionValue: string) => {
console.log("Option clicked:", optionValue);
onChange(optionValue);
setOpen(false);
};
// Close dropdown when clicking outside
useEffect(() => {
if (!open) return;
const handleOutsideClick = (e: MouseEvent) => {
// @ts-ignore
if (e.target && !e.target.closest('.dropdown-container')) {
setOpen(false);
}
};
document.addEventListener('mousedown', handleOutsideClick);
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, [open]);
return (
{getLabel()}
{open && (
{options.map((option) => (
{
e.preventDefault();
e.stopPropagation();
handleOptionClick(option.value);
}}
>
{option.label}
{value === option.value && }
))}
)}
);
}
// Custom Employee Dropdown component
interface CustomEmployeeDropdownProps {
value: string | null;
onChange: (value: string) => void;
employees: Employee[];
placeholder: string;
disabled?: boolean;
}
function CustomEmployeeDropdown({ value, onChange, employees, placeholder, disabled = false }: CustomEmployeeDropdownProps) {
const [open, setOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
console.log("CustomEmployeeDropdown render with value:", value, typeof value);
const getEmployeeName = (id: string | null) => {
console.log("Getting employee name for ID:", id, typeof id);
if (!id || id === "unassigned") return placeholder;
const employee = employees.find(emp => emp.id.toString() === id);
return employee ? `${employee.firstName} ${employee.lastName}` : `Employee ${id}`;
};
const getInitials = (id: string) => {
const employee = employees.find(emp => emp.id.toString() === id);
if (!employee) return "??";
return (employee.firstName.charAt(0) + employee.lastName.charAt(0)).toUpperCase();
};
const handleButtonClick = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
// Don't open if disabled
if (disabled) return;
setOpen(!open);
if (!open) {
setSearchTerm(""); // Reset search when opening
}
};
const handleOptionClick = (employeeId: string) => {
console.log("Employee option clicked:", employeeId);
onChange(employeeId);
setOpen(false);
setSearchTerm(""); // Reset search when selecting
};
// Helper functions for checking selection state
const isUnassigned = () => {
return value === null || value === "unassigned" || value === undefined;
};
const isEmployeeSelected = (empId: string) => {
return value === empId;
};
// Filter employees based on search term
const filteredEmployees = employees.filter(employee => {
if (!searchTerm) return true;
const fullName = `${employee.firstName} ${employee.lastName}`.toLowerCase();
return fullName.includes(searchTerm.toLowerCase());
});
// Close dropdown when clicking outside
useEffect(() => {
if (!open) return;
const handleOutsideClick = (e: MouseEvent) => {
// @ts-ignore
if (e.target && !e.target.closest('.employee-dropdown-container')) {
setOpen(false);
setSearchTerm(""); // Reset search when closing
}
};
document.addEventListener('mousedown', handleOutsideClick);
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, [open]);
return (
{value && value !== "unassigned" ? (
{getInitials(value)}
) : (
)}
{getEmployeeName(value)}
{!disabled &&
}
{disabled &&
}
{open && (
{/* Search Input */}
setSearchTerm(e.target.value)}
className="h-8 text-sm"
autoFocus
onClick={(e) => e.stopPropagation()}
/>
{/* Options Container */}
{/* Unassigned Option */}
{(!searchTerm || "unassigned".includes(searchTerm.toLowerCase())) && (
{
e.preventDefault();
e.stopPropagation();
handleOptionClick("unassigned");
}}
>
Unassigned
{isUnassigned() && (
)}
)}
{/* Employee Options */}
{filteredEmployees.length > 0 ? (
filteredEmployees.map((employee) => (
{
e.preventDefault();
e.stopPropagation();
handleOptionClick(employee.id.toString());
}}
>
{(employee.firstName.charAt(0) + employee.lastName.charAt(0)).toUpperCase()}
{employee.firstName} {employee.lastName}
{isEmployeeSelected(employee.id.toString()) && (
)}
))
) : (
searchTerm && (
No employees found matching "{searchTerm}"
)
)}
)}
);
}
// Add ProjectDropdown component
interface CustomProjectDropdownProps {
value: number | null;
onChange: (value: string) => void;
projects: ProjectApi[];
placeholder: string;
}
function CustomProjectDropdown({ value, onChange, projects, placeholder }: CustomProjectDropdownProps) {
const [open, setOpen] = useState(false);
const getProjectName = (id: number | null) => {
if (id === null) return placeholder;
const project = projects.find(project => project.id === id);
return project ? project.projectName : `Project #${id}`;
};
const handleButtonClick = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setOpen(!open);
};
const handleOptionClick = (projectId: string) => {
if (projectId === "all") {
onChange("all");
} else {
onChange(projectId);
}
setOpen(false);
};
// Close dropdown when clicking outside
useEffect(() => {
if (!open) return;
const handleOutsideClick = (e: MouseEvent) => {
// @ts-ignore
if (e.target && !e.target.closest('.project-dropdown-container')) {
setOpen(false);
}
};
document.addEventListener('mousedown', handleOutsideClick);
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, [open]);
// Helper functions
const isProjectSelected = (projectId: number) => {
return value === projectId;
};
const isAllProjectsSelected = () => {
return value === null;
};
return (
{value !== null ? getProjectName(value) : placeholder}
{open && (
{
e.preventDefault();
e.stopPropagation();
handleOptionClick("all");
}}
>
All Projects
{isAllProjectsSelected() && }
{projects.map((project) => (
{
e.preventDefault();
e.stopPropagation();
handleOptionClick(project.id.toString());
}}
>
{project.projectName}
Code: {project.projectCode}
{isProjectSelected(project.id) &&
}
))}
)}
);
}
// Custom Issue Dropdown component
interface CustomIssueDropdownProps {
value: number | null;
onChange: (value: string) => void;
issues: IssueApi[];
placeholder: string;
required?: boolean;
isError?: boolean;
}
function CustomIssueDropdown({ value, onChange, issues, placeholder, required, isError = false }: CustomIssueDropdownProps) {
const [open, setOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
console.log("CustomIssueDropdown render with value:", value, typeof value);
const getIssueName = (id: number | null) => {
console.log("Getting issue name for ID:", id, typeof id);
if (id === null || id === undefined) {
return placeholder;
}
const issue = issues.find(issue => issue.id === id);
return issue ? issue.title : `Issue #${id}`;
};
const handleButtonClick = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setOpen(!open);
};
const handleOptionClick = (issueId: string) => {
console.log("Issue option clicked:", issueId, typeof issueId);
if (issueId === "null") {
console.log("Setting issue to null");
onChange("null");
} else {
console.log("Setting issue to:", parseInt(issueId, 10));
onChange(issueId);
}
setOpen(false);
setSearchTerm("");
};
// Filter issues based on search term
const filteredIssues = searchTerm
? issues.filter(issue =>
issue.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
issue.id.toString().includes(searchTerm)
)
: issues;
// Close dropdown when clicking outside
useEffect(() => {
if (!open) return;
const handleOutsideClick = (e: MouseEvent) => {
// @ts-ignore
if (e.target && !e.target.closest('.issue-dropdown-container')) {
setOpen(false);
}
};
document.addEventListener('mousedown', handleOutsideClick);
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, [open]);
// Helper function to check if an issue is selected
const isIssueSelected = (issueId: number) => {
return value === issueId;
}
// Helper to check if "No linked issue" is selected
const isNoIssueSelected = () => {
return value === null;
}
// Check if the field is invalid (required but not filled)
const isInvalid = required && value === null;
const showError = isInvalid || isError;
return (
{value !== null ? getIssueName(value) : placeholder}
{showError && * }
{showError && !open && (
This field is required
)}
{open && (
setSearchTerm(e.target.value)}
onClick={(e) => e.stopPropagation()}
/>
{
e.preventDefault();
e.stopPropagation();
handleOptionClick("null");
}}
>
No linked issue
{isNoIssueSelected() && }
{filteredIssues.length === 0 ? (
No issues found in your assigned projects
) : (
filteredIssues.map((issue) => (
{
e.preventDefault();
e.stopPropagation();
handleOptionClick(issue.id.toString());
}}
>
{issue.title}
#{issue.id} • {issue.status}
{isIssueSelected(issue.id) &&
}
))
)}
)}
);
}
type BugFormProps = {
isEditMode?: boolean;
defaultType?: string;
};
const BugForm: React.FC = ({ isEditMode = false, defaultType }) => {
const navigate = useNavigate();
const { id } = useParams();
const { userData } = useAuth();
const [isLoading, setIsLoading] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [employees, setEmployees] = useState([]);
const [issues, setIssues] = useState([]);
const [allIssues, setAllIssues] = useState([]);
const [projects, setProjects] = useState([]);
const [selectedProject, setSelectedProject] = useState(null);
const [activeTab, setActiveTab] = useState("details");
const [validationErrors, setValidationErrors] = useState([]);
const [bug, setBug] = useState({
id: 0,
type: defaultType === "Production Bug" ? "Production Bug" : "Bug",
priority: "Medium",
severity: "Minor",
title: "",
description: "",
sprintName: null,
estimates: "",
esUnit: 0,
stateDate: new Date().toISOString(),
endDate: null,
assignedTo: null,
remainingHr: 0,
status: "Open",
issuesId: null,
taskCode: "", // Auto-generated by API, not shown in UI
version: "",
url: "",
stepsToReproduce: "",
inputs: "",
environment: defaultType === "Production Bug" ? "Production" : "Staging",
expectedBehavior: "",
actualBehavior: "",
resolution: "",
rootCause: ""
});
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(undefined);
useEffect(() => {
fetchEmployees();
fetchProjects();
if (isEditMode && id) {
fetchBug(Number(id));
}
}, [isEditMode, id]);
// Add a new effect to fetch issues after projects are loaded
useEffect(() => {
// Only fetch issues when projects change and we have at least one project
if (projects.length > 0 && !isLoading) {
// Check if we already have the issues for these projects to avoid unnecessary refreshes
const projectIds = projects.map(project => project.id);
const currentIssueProjectIds = new Set(issues.map(issue => issue.projectId));
// Only fetch if we have new projects or no issues loaded yet
const needsFetch = issues.length === 0 ||
projectIds.some(id => !currentIssueProjectIds.has(id));
if (needsFetch) {
console.log("Projects changed, refreshing issues");
fetchIssues();
}
}
}, [projects, issues, isLoading]);
const fetchEmployees = async () => {
try {
const data = await employeeService.getAll();
setEmployees(data);
} catch (error) {
console.error("Error fetching employees:", error);
toast.error("Error loading employee data");
}
};
const fetchProjects = async () => {
try {
let projectsData: ProjectApi[] = [];
// Fetch only user's projects if user data is available
if (userData && userData.employeeId) {
projectsData = await projectService.getByEmployeeId(userData.employeeId);
} else {
projectsData = await projectService.getAll();
}
setProjects(projectsData);
} catch (error) {
console.error("Error fetching projects:", error);
toast.error("Error loading projects data");
}
};
const fetchIssues = async () => {
try {
console.log("Fetching issues...");
// Prevent duplicate API calls if already loading
if (isLoading) {
console.log("Already loading, skipping fetchIssues call");
return;
}
// Get all issues first
const allIssuesData = await issuesApi.getAll();
console.log("All issues fetched:", allIssuesData);
setAllIssues(allIssuesData);
// If user has assigned projects, filter the issues to only show those projects
if (projects.length > 0) {
const projectIds = projects.map(project => project.id);
console.log("Filtering issues for user's assigned projects:", projectIds);
// Filter issues to only include those from user's assigned projects
const filteredIssues = allIssuesData.filter(issue =>
projectIds.includes(issue.projectId)
);
console.log("Filtered issues:", filteredIssues.length);
setIssues(filteredIssues);
} else {
// If no projects assigned yet or still loading, show all issues
setIssues(allIssuesData);
}
} catch (error) {
console.error("Error fetching issues:", error);
toast.error("Error loading issues data");
}
};
const fetchBug = async (bugId: number) => {
setIsLoading(true);
try {
const data = await tasksApi.getById(bugId);
if (data) {
// Ensure it's a bug type (regular Bug or Production Bug)
if (data.type !== "Bug" && data.type !== "Production Bug") {
toast.error("The requested item is not a bug");
// Redirect to the appropriate list based on the defaultType
if (defaultType === "Production Bug") {
navigate("/production-bugs");
} else {
navigate("/bugs");
}
return;
}
console.log("Bug data loaded from API:", {
status: data.status,
type: data.type,
priority: data.priority,
severity: data.severity,
environment: data.environment
});
// Convert dates to Date objects if they exist, handling timezone issues
let startDateObj = data.stateDate ? new Date(data.stateDate) : new Date();
let endDateObj = data.endDate ? new Date(data.endDate) : undefined;
// Adjust for timezone differences to show local date
if (startDateObj) {
startDateObj = new Date(startDateObj.getTime() + startDateObj.getTimezoneOffset() * 60000);
}
if (endDateObj) {
endDateObj = new Date(endDateObj.getTime() + endDateObj.getTimezoneOffset() * 60000);
}
setStartDate(startDateObj);
setEndDate(endDateObj);
// Update the form state with the fetched data, preserving the original bug type
setBug({
...data,
type: data.type, // Explicitly keep the original bug type
stateDate: data.stateDate || new Date().toISOString(),
endDate: data.endDate || null
});
} else {
toast.error("Bug not found");
// Check if we're editing a Production Bug and redirect accordingly
if (defaultType === "Production Bug") {
navigate("/production-bugs");
} else {
navigate("/bugs");
}
}
} catch (error) {
console.error("Error fetching bug:", error);
toast.error("Error loading bug data");
// Check if we're editing a Production Bug and redirect accordingly
if (defaultType === "Production Bug") {
navigate("/production-bugs");
} else {
navigate("/bugs");
}
} finally {
setIsLoading(false);
}
};
const handleInputChange = (e: React.ChangeEvent) => {
const { name, value } = e.target;
// Clear validation error for this field when it's changed
if (validationErrors.includes(name)) {
setValidationErrors(prev => prev.filter(err => err !== name));
}
setBug(prev => ({ ...prev, [name]: value }));
};
const handleSelectChange = (field: string, value: string) => {
console.log(`Changing field ${field} to value:`, value, typeof value);
// Clear validation error for this field when it's changed
if (validationErrors.includes(field)) {
setValidationErrors(prev => prev.filter(err => err !== field));
}
if (field === 'assignedTo') {
const newValue = value === "unassigned" ? null : parseInt(value, 10);
console.log(`Setting assignedTo from ${bug.assignedTo} to:`, newValue, typeof newValue);
// Force a re-render by making a completely new object
setBug({
...bug,
assignedTo: newValue
});
} else if (field === 'project') {
// Handle project selection
const newValue = value === "all" ? null : parseInt(value, 10);
setSelectedProject(newValue);
// Filter issues based on selected project
if (newValue === null) {
setIssues(allIssues);
} else {
const filteredIssues = allIssues.filter(issue => issue.projectId === newValue);
setIssues(filteredIssues);
// Clear selected issue if it's not in the filtered list
if (bug.issuesId !== null && !filteredIssues.some(issue => issue.id === bug.issuesId)) {
setBug({
...bug,
issuesId: null
});
}
}
} else if (field === 'issuesId') {
const newValue = value === "null" ? null : parseInt(value, 10);
console.log(`Setting issuesId from ${bug.issuesId} to:`, newValue, typeof newValue);
// Force a re-render by making a completely new object
setBug({
...bug,
issuesId: newValue
});
// If issue is selected, also set the project to match
if (newValue !== null) {
const selectedIssue = allIssues.find(issue => issue.id === newValue);
if (selectedIssue && selectedProject !== selectedIssue.projectId) {
setSelectedProject(selectedIssue.projectId);
}
}
} else if (['status', 'type', 'priority', 'severity', 'environment'].includes(field)) {
// For dropdown fields, ensure we create a new object to trigger re-render
console.log(`Setting ${field} from "${bug[field as keyof typeof bug]}" to "${value}"`);
setBug({
...bug,
[field]: value
});
} else {
// For other fields
setBug(prev => ({ ...prev, [field]: value }));
}
};
const formatDateForApi = (date: Date | undefined) => {
if (!date) return null;
// Ensure we're sending a valid ISO string
try {
return new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString();
} catch (error) {
console.error("Error formatting date:", error);
return null;
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Reset validation errors
const errors: string[] = [];
// Validate required fields
if (bug.issuesId === null) {
errors.push("issuesId");
toast.error("Linked Feature is required");
}
if (!bug.title.trim()) {
errors.push("title");
toast.error("Bug Title is required");
}
if (!bug.description.trim()) {
errors.push("description");
toast.error("Bug Description is required");
}
if (errors.length > 0) {
setValidationErrors(errors);
setActiveTab("details"); // Switch to details tab where most required fields are
return;
}
setIsSaving(true);
try {
// Ensure bug type is preserved - for production bugs this is critical
const isProductionBug = bug.type === "Production Bug" || defaultType === "Production Bug";
// Format dates for API
const formattedBug = {
...bug,
// Ensure type is set correctly for production bugs
type: isProductionBug ? "Production Bug" : bug.type,
// For client users, always use current date for stateDate and clear resolution fields
stateDate: userData?.roleName?.toLowerCase() === "client"
? new Date().toISOString()
: (formatDateForApi(startDate) || new Date().toISOString()),
endDate: formatDateForApi(endDate),
// Clear resolution fields for client users
rootCause: userData?.roleName?.toLowerCase() === "client" ? "" : bug.rootCause,
resolution: userData?.roleName?.toLowerCase() === "client" ? "" : bug.resolution,
// Ensure issuesId is properly set to null or an integer
issuesId: bug.issuesId === null ? null : (typeof bug.issuesId === 'number' ? bug.issuesId : null),
// Ensure assignedTo is properly set to null or an integer
assignedTo: bug.assignedTo === null ? null : (typeof bug.assignedTo === 'number' ? bug.assignedTo : null)
};
console.log("Submitting bug with data:", formattedBug);
console.log("Issue ID value:", formattedBug.issuesId, typeof formattedBug.issuesId);
console.log("Assigned To value:", formattedBug.assignedTo, typeof formattedBug.assignedTo);
let result;
if (isEditMode && id) {
// Ensure id is properly set for update
const updateData = {
...formattedBug,
id: Number(id)
};
console.log("Updating bug with data:", updateData);
result = await tasksApi.update(Number(id), updateData);
toast.success("Bug updated successfully");
} else {
console.log("Creating new bug with data:", formattedBug);
result = await tasksApi.create(formattedBug);
toast.success("Bug created successfully");
}
// Check if the bug is a Production Bug and redirect accordingly
if (isProductionBug) {
navigate(`/production-bugs/${result.id}`);
} else {
navigate(`/bugs/${result.id}`);
}
} catch (error) {
console.error("Error saving bug:", error);
toast.error("Failed to save bug. Please try again.");
} finally {
setIsSaving(false);
}
};
const handleCancel = () => {
// Check if this is a production bug based on either current type or default type
const isProductionBug = bug.type === "Production Bug" || defaultType === "Production Bug";
if (isEditMode && id) {
if (isProductionBug) {
navigate(`/production-bugs/${id}`);
} else {
navigate(`/bugs/${id}`);
}
} else {
if (isProductionBug) {
navigate("/production-bugs");
} else {
navigate("/bugs");
}
}
};
useEffect(() => {
console.log("Render: Issues available:", issues);
}, [issues]);
useEffect(() => {
console.log("Bug issuesId changed:", bug.issuesId, typeof bug.issuesId);
// If issuesId is not null and not a number, fix it
if (bug.issuesId !== null && typeof bug.issuesId !== 'number') {
console.log("Correcting issuesId type");
try {
// Try to convert to a number if possible
const numValue = Number(bug.issuesId);
if (!isNaN(numValue)) {
console.log(`Converting issuesId from ${typeof bug.issuesId} to number: ${numValue}`);
setBug(prev => ({...prev, issuesId: numValue}));
} else {
// If conversion fails, set to null
console.log("Failed to convert issuesId to number, setting to null");
setBug(prev => ({...prev, issuesId: null}));
}
} catch (error) {
console.error("Error converting issuesId:", error);
setBug(prev => ({...prev, issuesId: null}));
}
}
}, [bug.issuesId]);
useEffect(() => {
console.log("Bug assignedTo changed:", bug.assignedTo, typeof bug.assignedTo);
// If assignedTo is not null and not a number, fix it
if (bug.assignedTo !== null && typeof bug.assignedTo !== 'number') {
console.log("Correcting assignedTo type");
try {
// Try to convert to a number if possible
const numValue = Number(bug.assignedTo);
if (!isNaN(numValue)) {
console.log(`Converting assignedTo from ${typeof bug.assignedTo} to number: ${numValue}`);
setBug(prev => ({...prev, assignedTo: numValue}));
} else {
// If conversion fails, set to null
console.log("Failed to convert assignedTo to number, setting to null");
setBug(prev => ({...prev, assignedTo: null}));
}
} catch (error) {
console.error("Error converting assignedTo:", error);
setBug(prev => ({...prev, assignedTo: null}));
}
}
}, [bug.assignedTo]);
useEffect(() => {
console.log("Bug state updated:", {
status: bug.status,
type: bug.type,
priority: bug.priority,
severity: bug.severity,
environment: bug.environment
});
}, [bug.status, bug.type, bug.priority, bug.severity, bug.environment]);
// Set default assignee for client users when component initializes
useEffect(() => {
if (userData?.roleName?.toLowerCase() === "client" && !isEditMode && bug.assignedTo === null) {
// For new bugs, if the user is a client, set assignedTo to null initially
// This will be handled by the backend/admin assignment later
setBug(prev => ({
...prev,
assignedTo: null
}));
}
}, [userData, isEditMode]); // Run once when component mounts and userData is available
return (
{/* Breadcrumb Navigation */}
Home
{bug.type === "Production Bug" ? (
Production Bugs
) : (
Bugs
)}
{isEditMode ? "Edit Bug" : "New Bug"}
{isEditMode ?
`Edit ${bug.type === "Production Bug" ? "Production Bug" : "Bug"}` :
`New ${bug.type === "Production Bug" ? "Production Bug" : "Bug"}`}
{isEditMode
? `Update the details of an existing ${bug.type === "Production Bug" ? "production bug" : "bug"}`
: `Create a new ${bug.type === "Production Bug" ? "production bug" : "bug or defect"} to track`}
Back
{isLoading ? (
) : (
)}
);
};
export default BugForm;