070e960c05
- Extract use-projects hook for state management - Extract ProjectCard and ProjectDialog components - Slim ProjectsPage from 433 to ~100 lines Quality gates: tsc --noEmit passes, npm run build passes
72 lines
1.9 KiB
TypeScript
72 lines
1.9 KiB
TypeScript
import { Icon } from "../../icon";
|
|
|
|
interface Props {
|
|
mode: "create" | "edit";
|
|
name: string;
|
|
description: string;
|
|
error: string | null;
|
|
onNameChange: (name: string) => void;
|
|
onDescriptionChange: (desc: string) => void;
|
|
onSubmit: (e: React.FormEvent) => void;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
export const ProjectDialog = ({
|
|
mode,
|
|
name,
|
|
description,
|
|
error,
|
|
onNameChange,
|
|
onDescriptionChange,
|
|
onSubmit,
|
|
onCancel,
|
|
}: Props) => {
|
|
return (
|
|
<div className="dialog-overlay" role="dialog" aria-modal="true">
|
|
<div className="dialog">
|
|
<h2>{mode === "create" ? "Create Project" : "Edit Project"}</h2>
|
|
<form onSubmit={onSubmit} className="stack">
|
|
<label className="form-field">
|
|
Name
|
|
<input
|
|
type="text"
|
|
value={name}
|
|
onChange={(e) => onNameChange(e.target.value)}
|
|
placeholder="Project name"
|
|
/>
|
|
</label>
|
|
<label className="form-field">
|
|
Description
|
|
<textarea
|
|
value={description}
|
|
onChange={(e) => onDescriptionChange(e.target.value)}
|
|
placeholder="Optional description"
|
|
rows={3}
|
|
/>
|
|
</label>
|
|
{error && <p className="error-text">{error}</p>}
|
|
<div className="dialog-actions">
|
|
<button className="secondary-button" onClick={onCancel} type="button">
|
|
<Icon name="cancel" size="sm" />
|
|
Cancel
|
|
</button>
|
|
<button className="primary-button" type="submit">
|
|
{mode === "create" ? (
|
|
<>
|
|
<Icon name="add" size="sm" />
|
|
Create
|
|
</>
|
|
) : (
|
|
<>
|
|
<Icon name="save" size="sm" />
|
|
Save
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|