Files
livedash-node/app/dashboard/company/page.tsx
Kaj Kowalski 93fbb44eec feat: comprehensive Biome linting fixes and code quality improvements
Major code quality overhaul addressing 58% of all linting issues:

• Type Safety Improvements:
  - Replace all any types with proper TypeScript interfaces
  - Fix Map component shadowing (renamed to CountryMap)
  - Add comprehensive custom error classes system
  - Enhance API route type safety

• Accessibility Enhancements:
  - Add explicit button types to all interactive elements
  - Implement useId() hooks for form element accessibility
  - Add SVG title attributes for screen readers
  - Fix static element interactions with keyboard handlers

• React Best Practices:
  - Resolve exhaustive dependencies warnings with useCallback
  - Extract nested component definitions to top level
  - Fix array index keys with proper unique identifiers
  - Improve component organization and prop typing

• Code Organization:
  - Automatic import organization and type import optimization
  - Fix unused function parameters and variables
  - Enhanced error handling with structured error responses
  - Improve component reusability and maintainability

Results: 248 → 104 total issues (58% reduction)
- Fixed all critical type safety and security issues
- Enhanced accessibility compliance significantly
- Improved code maintainability and performance
2025-06-29 07:35:45 +02:00

214 lines
6.9 KiB
TypeScript

"use client";
import { Database, Save, Settings, ShieldX } from "lucide-react";
import { useSession } from "next-auth/react";
import { useEffect, useId, useState } from "react";
import { Alert, AlertDescription } from "@/components/ui/alert";
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 type { Company } from "../../../lib/types";
export default function CompanySettingsPage() {
const csvUrlId = useId();
const csvUsernameId = useId();
const csvPasswordId = useId();
const { data: session, status } = useSession();
// We store the full company object for future use and updates after save operations
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
const [_company, setCompany] = useState<Company | null>(null);
const [csvUrl, setCsvUrl] = useState<string>("");
const [csvUsername, setCsvUsername] = useState<string>("");
const [csvPassword, setCsvPassword] = useState<string>("");
const [message, setMessage] = useState<string>("");
const [loading, setLoading] = useState(true);
useEffect(() => {
if (status === "authenticated") {
const fetchCompany = async () => {
setLoading(true);
try {
const res = await fetch("/api/dashboard/config");
const data = await res.json();
setCompany(data.company);
setCsvUrl(data.company.csvUrl || "");
setCsvUsername(data.company.csvUsername || "");
if (data.company.csvPassword) {
setCsvPassword(data.company.csvPassword);
}
} catch (error) {
console.error("Failed to fetch company settings:", error);
setMessage("Failed to load company settings.");
} finally {
setLoading(false);
}
};
fetchCompany();
}
}, [status]);
async function handleSave() {
setMessage("");
try {
const res = await fetch("/api/dashboard/settings", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
csvUrl,
csvUsername,
csvPassword,
}),
});
if (res.ok) {
setMessage("Settings saved successfully!");
// Update local state if needed
const data = await res.json();
setCompany(data.company);
} else {
const error = await res.json();
setMessage(
`Failed to save settings: ${error.message || "Unknown error"}`
);
}
} catch (error) {
setMessage("Failed to save settings. Please try again.");
console.error("Error saving settings:", error);
}
}
// Loading state
if (loading) {
return (
<div className="space-y-6">
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Settings className="h-6 w-6" />
<CardTitle>Company Settings</CardTitle>
</div>
</CardHeader>
<CardContent>
<div className="text-center py-8 text-muted-foreground">
Loading settings...
</div>
</CardContent>
</Card>
</div>
);
}
// Check for ADMIN access
if (session?.user?.role !== "ADMIN") {
return (
<div className="space-y-6">
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<ShieldX className="h-6 w-6 text-destructive" />
<CardTitle className="text-destructive">Access Denied</CardTitle>
</div>
</CardHeader>
<CardContent>
<div className="text-center py-8">
<p className="text-muted-foreground">
You don&apos;t have permission to view company settings.
</p>
</div>
</CardContent>
</Card>
</div>
);
}
return (
<div className="space-y-6">
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Settings className="h-6 w-6" />
<CardTitle>Company Settings</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-6">
{message && (
<Alert
variant={message.includes("Failed") ? "destructive" : "default"}
>
<AlertDescription>{message}</AlertDescription>
</Alert>
)}
<form
className="space-y-6"
onSubmit={(e) => {
e.preventDefault();
handleSave();
}}
autoComplete="off"
>
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Database className="h-5 w-5" />
<CardTitle className="text-lg">
Data Source Configuration
</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor={csvUrlId}>CSV Data Source URL</Label>
<Input
id={csvUrlId}
type="text"
value={csvUrl}
onChange={(e) => setCsvUrl(e.target.value)}
placeholder="https://example.com/data.csv"
autoComplete="off"
/>
</div>
<div className="space-y-2">
<Label htmlFor={csvUsernameId}>CSV Username</Label>
<Input
id={csvUsernameId}
type="text"
value={csvUsername}
onChange={(e) => setCsvUsername(e.target.value)}
placeholder="Username for CSV access (if needed)"
autoComplete="off"
/>
</div>
<div className="space-y-2">
<Label htmlFor={csvPasswordId}>CSV Password</Label>
<Input
id={csvPasswordId}
type="password"
value={csvPassword}
onChange={(e) => setCsvPassword(e.target.value)}
placeholder="Password will be updated only if provided"
autoComplete="new-password"
/>
<p className="text-sm text-muted-foreground">
Leave blank to keep current password
</p>
</div>
</CardContent>
</Card>
<div className="flex justify-end">
<Button type="submit" className="gap-2">
<Save className="h-4 w-4" />
Save Settings
</Button>
</div>
</form>
</CardContent>
</Card>
</div>
);
}