Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ import {
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { useQuery } from '@tanstack/react-query';
import { userService } from '@/services/userService';
import { serviceService } from '@/services/serviceService';
import { Badge } from '@/components/ui/badge';
import { X, Users } from 'lucide-react';
import { Button } from '@/components/ui/button';
Expand Down Expand Up @@ -50,6 +58,13 @@ export const IncidentBasicFields: React.FC = () => {
staleTime: 300000 // Cache for 5 minutes
});

// Fetch uptime services for the affected-service dropdown
const { data: services = [] } = useQuery({
queryKey: ['services'],
queryFn: serviceService.getServices,
staleTime: 300000,
});

// Add user to assigned_to
const addUser = (userId: string) => {
// For now, we're using a single user assignment
Expand Down Expand Up @@ -119,9 +134,27 @@ export const IncidentBasicFields: React.FC = () => {
render={({ field }) => (
<FormItem>
<FormLabel>{t('serviceId')}</FormLabel>
<FormControl>
<Input placeholder={t('enterServiceId')} {...field} />
</FormControl>
<Select value={field.value || undefined} onValueChange={field.onChange}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={t('selectUptimeService')} />
</SelectTrigger>
</FormControl>
<SelectContent>
{services.map((service) => (
<SelectItem key={service.id} value={service.id}>
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${
service.status === 'up' ? 'bg-green-500' :
service.status === 'down' ? 'bg-red-500' :
'bg-yellow-500'
}`} />
{service.name}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,94 @@ import {
FormControl,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { X } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { serviceService } from '@/services/serviceService';

// Affected services are stored as a comma-separated list of service names in
// the `affected` text field. This control lets the user pick them from the
// uptime services instead of typing names by hand, while keeping that format.
const splitServices = (value: string): string[] =>
value
? value.split(',').map((s) => s.trim()).filter(Boolean)
: [];

export const MaintenanceAffectedFields: React.FC = () => {
const { t } = useLanguage();
const { control } = useFormContext<MaintenanceFormValues>();

const { data: services = [] } = useQuery({
queryKey: ['services'],
queryFn: serviceService.getServices,
staleTime: 300000,
});

return (
<FormField
control={control}
name="affected"
render={({ field }) => (
<FormItem>
<FormLabel>{t('affectedServices')}</FormLabel>
<FormControl>
<Input placeholder={t('enterAffectedServices')} {...field} />
</FormControl>
<FormMessage />
<p className="text-sm text-muted-foreground">{t('separateServicesWithComma')}</p>
</FormItem>
)}
render={({ field }) => {
const selected = splitServices(field.value);

const addService = (name: string) => {
if (name && !selected.includes(name)) {
field.onChange([...selected, name].join(', '));
}
};

const removeService = (name: string) => {
field.onChange(selected.filter((s) => s !== name).join(', '));
};

return (
<FormItem>
<FormLabel>{t('affectedServices')}</FormLabel>
<FormControl>
<select
className="w-full h-10 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
value=""
onChange={(e) => addService(e.target.value)}
>
<option value="">{t('selectUptimeService')}</option>
{services
.filter((service) => !selected.includes(service.name))
.map((service) => (
<option key={service.id} value={service.name}>
{service.name}
</option>
))}
</select>
</FormControl>

{selected.length > 0 && (
<div className="flex flex-wrap gap-2 mt-2">
{selected.map((name) => (
<Badge
key={name}
variant="secondary"
className="flex items-center gap-1 py-1 px-2"
>
<span>{name}</span>
<Button
variant="ghost"
size="icon"
className="h-4 w-4 p-0 ml-1 hover:bg-transparent hover:opacity-70"
onClick={() => removeService(name)}
type="button"
>
<X className="h-3 w-3" />
<span className="sr-only">{t('remove')}</span>
</Button>
</Badge>
))}
</div>
)}
<FormMessage />
</FormItem>
);
}}
/>
);
};