mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 03:10:38 +09:00
143 lines
4.4 KiB
TypeScript
143 lines
4.4 KiB
TypeScript
|
|
import { useState } from "react";
|
||
|
|
import { useDialog } from "../context/DialogContext";
|
||
|
|
import { useCompany } from "../context/CompanyContext";
|
||
|
|
import { issuesApi } from "../api/issues";
|
||
|
|
import {
|
||
|
|
Dialog,
|
||
|
|
DialogContent,
|
||
|
|
DialogHeader,
|
||
|
|
DialogTitle,
|
||
|
|
DialogFooter,
|
||
|
|
} from "@/components/ui/dialog";
|
||
|
|
import { Input } from "@/components/ui/input";
|
||
|
|
import { Textarea } from "@/components/ui/textarea";
|
||
|
|
import { Button } from "@/components/ui/button";
|
||
|
|
import { Label } from "@/components/ui/label";
|
||
|
|
import {
|
||
|
|
Select,
|
||
|
|
SelectContent,
|
||
|
|
SelectItem,
|
||
|
|
SelectTrigger,
|
||
|
|
SelectValue,
|
||
|
|
} from "@/components/ui/select";
|
||
|
|
|
||
|
|
interface NewIssueDialogProps {
|
||
|
|
onCreated?: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function NewIssueDialog({ onCreated }: NewIssueDialogProps) {
|
||
|
|
const { newIssueOpen, newIssueDefaults, closeNewIssue } = useDialog();
|
||
|
|
const { selectedCompanyId } = useCompany();
|
||
|
|
const [title, setTitle] = useState("");
|
||
|
|
const [description, setDescription] = useState("");
|
||
|
|
const [status, setStatus] = useState(newIssueDefaults.status ?? "todo");
|
||
|
|
const [priority, setPriority] = useState(newIssueDefaults.priority ?? "medium");
|
||
|
|
const [submitting, setSubmitting] = useState(false);
|
||
|
|
|
||
|
|
function reset() {
|
||
|
|
setTitle("");
|
||
|
|
setDescription("");
|
||
|
|
setStatus("todo");
|
||
|
|
setPriority("medium");
|
||
|
|
}
|
||
|
|
|
||
|
|
async function handleSubmit(e: React.FormEvent) {
|
||
|
|
e.preventDefault();
|
||
|
|
if (!selectedCompanyId || !title.trim()) return;
|
||
|
|
|
||
|
|
setSubmitting(true);
|
||
|
|
try {
|
||
|
|
await issuesApi.create(selectedCompanyId, {
|
||
|
|
title: title.trim(),
|
||
|
|
description: description.trim() || undefined,
|
||
|
|
status,
|
||
|
|
priority,
|
||
|
|
});
|
||
|
|
reset();
|
||
|
|
closeNewIssue();
|
||
|
|
onCreated?.();
|
||
|
|
} finally {
|
||
|
|
setSubmitting(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Dialog
|
||
|
|
open={newIssueOpen}
|
||
|
|
onOpenChange={(open) => {
|
||
|
|
if (!open) {
|
||
|
|
reset();
|
||
|
|
closeNewIssue();
|
||
|
|
}
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<DialogContent className="sm:max-w-lg">
|
||
|
|
<DialogHeader>
|
||
|
|
<DialogTitle>New Issue</DialogTitle>
|
||
|
|
</DialogHeader>
|
||
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="issue-title">Title</Label>
|
||
|
|
<Input
|
||
|
|
id="issue-title"
|
||
|
|
placeholder="Issue title"
|
||
|
|
value={title}
|
||
|
|
onChange={(e) => setTitle(e.target.value)}
|
||
|
|
autoFocus
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div className="space-y-2">
|
||
|
|
<Label htmlFor="issue-desc">Description</Label>
|
||
|
|
<Textarea
|
||
|
|
id="issue-desc"
|
||
|
|
placeholder="Add a description..."
|
||
|
|
value={description}
|
||
|
|
onChange={(e) => setDescription(e.target.value)}
|
||
|
|
rows={3}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div className="flex gap-4">
|
||
|
|
<div className="space-y-2 flex-1">
|
||
|
|
<Label>Status</Label>
|
||
|
|
<Select value={status} onValueChange={setStatus}>
|
||
|
|
<SelectTrigger>
|
||
|
|
<SelectValue />
|
||
|
|
</SelectTrigger>
|
||
|
|
<SelectContent>
|
||
|
|
<SelectItem value="backlog">Backlog</SelectItem>
|
||
|
|
<SelectItem value="todo">Todo</SelectItem>
|
||
|
|
<SelectItem value="in_progress">In Progress</SelectItem>
|
||
|
|
<SelectItem value="in_review">In Review</SelectItem>
|
||
|
|
<SelectItem value="done">Done</SelectItem>
|
||
|
|
</SelectContent>
|
||
|
|
</Select>
|
||
|
|
</div>
|
||
|
|
<div className="space-y-2 flex-1">
|
||
|
|
<Label>Priority</Label>
|
||
|
|
<Select value={priority} onValueChange={setPriority}>
|
||
|
|
<SelectTrigger>
|
||
|
|
<SelectValue />
|
||
|
|
</SelectTrigger>
|
||
|
|
<SelectContent>
|
||
|
|
<SelectItem value="critical">Critical</SelectItem>
|
||
|
|
<SelectItem value="high">High</SelectItem>
|
||
|
|
<SelectItem value="medium">Medium</SelectItem>
|
||
|
|
<SelectItem value="low">Low</SelectItem>
|
||
|
|
</SelectContent>
|
||
|
|
</Select>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<DialogFooter>
|
||
|
|
<Button type="button" variant="outline" onClick={closeNewIssue}>
|
||
|
|
Cancel
|
||
|
|
</Button>
|
||
|
|
<Button type="submit" disabled={!title.trim() || submitting}>
|
||
|
|
{submitting ? "Creating..." : "Create Issue"}
|
||
|
|
</Button>
|
||
|
|
</DialogFooter>
|
||
|
|
</form>
|
||
|
|
</DialogContent>
|
||
|
|
</Dialog>
|
||
|
|
);
|
||
|
|
}
|