All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Fixes all 542 ESLint problems in the web package to achieve 0 errors and 0 warnings. Changes: - Fixed 144 issues: nullish coalescing, return types, unused variables - Fixed 118 issues: unnecessary conditions, type safety, template literals - Fixed 79 issues: non-null assertions, unsafe assignments, empty functions - Fixed 67 issues: explicit return types, promise handling, enum comparisons - Fixed 45 final warnings: missing return types, optional chains - Fixed 25 typecheck-related issues: async/await, type assertions, formatting - Fixed JSX.Element namespace errors across 90+ files All Quality Rails violations resolved. Lint and typecheck both pass with 0 problems. Files modified: 118 components, tests, hooks, and utilities Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
145 lines
4.3 KiB
TypeScript
145 lines
4.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import type { Team } from "@mosaic/shared";
|
|
import { Card, CardHeader, CardContent, CardFooter, Button, Input, Textarea } from "@mosaic/ui";
|
|
|
|
interface TeamSettingsProps {
|
|
team: Team;
|
|
onUpdate: (data: { name?: string; description?: string }) => Promise<void>;
|
|
onDelete: () => Promise<void>;
|
|
}
|
|
|
|
export function TeamSettings({ team, onUpdate, onDelete }: TeamSettingsProps): React.JSX.Element {
|
|
const [name, setName] = useState(team.name);
|
|
const [description, setDescription] = useState(team.description ?? "");
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
const [isSaving, setIsSaving] = useState(false);
|
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
|
|
const hasChanges = name !== team.name || description !== (team.description ?? "");
|
|
|
|
const handleSave = async (): Promise<void> => {
|
|
if (!hasChanges) return;
|
|
|
|
setIsSaving(true);
|
|
try {
|
|
const updates: { name?: string; description?: string } = {};
|
|
if (name !== team.name) {
|
|
updates.name = name;
|
|
}
|
|
if (description !== (team.description ?? "")) {
|
|
updates.description = description;
|
|
}
|
|
await onUpdate(updates);
|
|
setIsEditing(false);
|
|
} catch (error) {
|
|
console.error("Failed to update team:", error);
|
|
alert("Failed to update team. Please try again.");
|
|
} finally {
|
|
setIsSaving(false);
|
|
}
|
|
};
|
|
|
|
const handleCancel = (): void => {
|
|
setName(team.name);
|
|
setDescription(team.description ?? "");
|
|
setIsEditing(false);
|
|
};
|
|
|
|
const handleDelete = async (): Promise<void> => {
|
|
setIsDeleting(true);
|
|
try {
|
|
await onDelete();
|
|
} catch (error) {
|
|
console.error("Failed to delete team:", error);
|
|
alert("Failed to delete team. Please try again.");
|
|
setIsDeleting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<h2 className="text-xl font-semibold text-gray-900">Team Settings</h2>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
<Input
|
|
label="Team Name"
|
|
value={name}
|
|
onChange={(e) => {
|
|
setName(e.target.value);
|
|
setIsEditing(true);
|
|
}}
|
|
placeholder="Enter team name"
|
|
fullWidth
|
|
disabled={isSaving}
|
|
/>
|
|
<Textarea
|
|
label="Description"
|
|
value={description}
|
|
onChange={(e) => {
|
|
setDescription(e.target.value);
|
|
setIsEditing(true);
|
|
}}
|
|
placeholder="Enter team description (optional)"
|
|
fullWidth
|
|
disabled={isSaving}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<div className="flex items-center justify-between w-full">
|
|
<div className="flex gap-2">
|
|
{isEditing && (
|
|
<>
|
|
<Button
|
|
variant="primary"
|
|
onClick={handleSave}
|
|
disabled={!hasChanges || isSaving || !name.trim()}
|
|
>
|
|
{isSaving ? "Saving..." : "Save Changes"}
|
|
</Button>
|
|
<Button variant="ghost" onClick={handleCancel} disabled={isSaving}>
|
|
Cancel
|
|
</Button>
|
|
</>
|
|
)}
|
|
</div>
|
|
<div>
|
|
{!showDeleteConfirm ? (
|
|
<Button
|
|
variant="danger"
|
|
onClick={() => {
|
|
setShowDeleteConfirm(true);
|
|
}}
|
|
disabled={isSaving}
|
|
>
|
|
Delete Team
|
|
</Button>
|
|
) : (
|
|
<div className="flex gap-2">
|
|
<span className="text-sm text-gray-600 self-center">Are you sure?</span>
|
|
<Button variant="danger" onClick={handleDelete} disabled={isDeleting}>
|
|
{isDeleting ? "Deleting..." : "Confirm Delete"}
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => {
|
|
setShowDeleteConfirm(false);
|
|
}}
|
|
disabled={isDeleting}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</CardFooter>
|
|
</Card>
|
|
);
|
|
}
|