Files
AetherForge/server/web/src/components/Fleet/CreateGroupModal.tsx

100 lines
3.2 KiB
TypeScript

import { useEffect, useState } from 'react';
import { useModalAmbientDuck } from '../../context/AmbientMusicContext';
import { FLEET_GROUP_COLORS, normalizeGroupColor } from '../../help/fleetGroups';
import './CreateGroupModal.css';
interface Props {
open: boolean;
agentCount: number;
onClose: () => void;
onCreate: (name: string, color: string) => void;
}
export default function CreateGroupModal({ open, agentCount, onClose, onCreate }: Props) {
useModalAmbientDuck(open);
const [name, setName] = useState('');
const [color, setColor] = useState<string>(FLEET_GROUP_COLORS[0]);
useEffect(() => {
if (open) {
setName('');
setColor(FLEET_GROUP_COLORS[groupsColorIndex(agentCount) % FLEET_GROUP_COLORS.length]);
}
}, [open, agentCount]);
if (!open || agentCount < 1) return null;
const submit = (e: React.FormEvent) => {
e.preventDefault();
const trimmed = name.trim();
if (!trimmed) return;
onCreate(trimmed, normalizeGroupColor(color));
onClose();
};
return (
<div className="fleet-group-modal-backdrop" role="presentation" onClick={onClose}>
<div
className="fleet-group-modal card"
role="dialog"
aria-labelledby="fleet-group-modal-title"
onClick={(e) => e.stopPropagation()}
>
<h2 id="fleet-group-modal-title" className="font-display">Create group</h2>
<p className="form-hint">
Saves {agentCount} selected machine{agentCount === 1 ? '' : 's'} usable in Crucible for bulk commands.
</p>
<form onSubmit={submit}>
<label className="label" htmlFor="fleet-group-name">Group name</label>
<input
id="fleet-group-name"
className="input"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g. Living room PCs"
autoFocus
maxLength={64}
/>
<p className="label" style={{ marginTop: '1rem' }}>Group color</p>
<div className="fleet-group-color-grid">
{FLEET_GROUP_COLORS.map((c) => (
<button
key={c}
type="button"
className={`fleet-group-swatch ${color === c ? 'selected' : ''}`}
style={{ background: c }}
title={c}
aria-label={`Color ${c}`}
onClick={() => setColor(c)}
/>
))}
</div>
<div className="fleet-group-custom-color">
<input
type="color"
value={normalizeGroupColor(color)}
onChange={(e) => setColor(e.target.value)}
aria-label="Custom color"
/>
<span className="font-tech">{normalizeGroupColor(color)}</span>
</div>
<div className="fleet-group-modal-actions">
<button type="button" className="btn btn-outline" onClick={onClose}>
Cancel
</button>
<button type="submit" className="btn btn-primary" disabled={!name.trim()}>
Create group
</button>
</div>
</form>
</div>
</div>
);
}
function groupsColorIndex(n: number): number {
return Math.abs(n) % FLEET_GROUP_COLORS.length;
}